Ruby For Startups

4,290 views

Published on

My experiences using Ruby and Rails to build OtherInbox, a startup software company.

Published in: Technology, Education
0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,290
On SlideShare
0
From Embeds
0
Number of Embeds
519
Actions
Shares
0
Downloads
71
Comments
0
Likes
12
Embeds 0
No embeds

No notes for slide
  • Ruby For Startups

    1. 1. Startup Ruby Separate things that change from things that stay the same Program to an interface, not an implementation Prefer composition over inheritance Delegate, delegate, delegate You Ain't Gonna Need It (YAGNI)
    2. 2. I’m Mike Subelsky @subelsky
    3. 3. I’m Mike Subelsky @subelsky This is what I have learned while building
    4. 4. What’s unique about startup programming? Facing unknown problems & unknown solutions With scarce time and resources
    5. 5. “Ferocious customer-centric rapid iteration” @ericries startuplessonslearned.com
    6. 6. What advice would I give myself to thrive in these conditions?
    7. 7. Design thoughtfully before implementing Don’t make these specific mistakes
    8. 8. Separate things that change from things that stay the same Program to interfaces, not implementations Prefer composition over inheritance Delegate, delegate, delegate You Ain't Gonna Need It (YAGNI)
    9. 9. Design for Change "Separate code for general functionality from code for specialized functionality" Isolate design decisions in their own modules
    10. 10. First Use of SQS SQS = RightAws::SqsGen2.new(access_key_i d, secret_access_key], { :multi_thread => true }) queue = SQS.queue("#{RAILS_ENV}_#{queue_name }")
    11. 11. First Use of SQS SQS = RightAws::SqsGen2.new(access_key_i d, secret_access_key], { :multi_thread => true }) queue = SQS.queue("#{RAILS_ENV}_#{queue_name }") Then both of these changed
    12. 12. Designed for Change queue = QueueFetcher.fetch(queue_name) queue.send_message({ :user_id => user_id }.to_yaml Isolates how we connect to the messaging system Isolates naming convention for the messaging system
    13. 13. class QueueFetcher def self.fetch(queue_name) SQS2.queue("#{queue_env}_#{queue_name}") end private def self.queue_env APP_CONFIG['queue_context'] end end
    14. 14. class QueueFetcher def self.fetch(queue_name) SQS2.queue("#{queue_env}_#{queue_name}") end private def self.queue_env APP_CONFIG['queue_context'] end end More isolation
    15. 15. I don’t like this class QueueFetcher def self.fetch(queue_name) SQS2.queue("#{queue_env}_#{queue_name}") end private def self.queue_env APP_CONFIG['queue_context'] end end More isolation
    16. 16. Business Logic class Envelope def deliver_first_message new_mailbox_name = Mailbox.new_mailbox_name(recipient) mailbox = Mailbox.create!(:user_id => user_id, :name => new_mailbox_name) prepare_delivery_for_spam prepare_delivery_for_forwarding(mailbox) self.first_message = true return deliver(mailbox) end
    17. 17. Program to interfaces, not implementations Program to general types "Don’t call it a car if you can get away with calling it a vehicle"
    18. 18. Program to interfaces, not implementations Easy for us to do with duck-typing
    19. 19. class Message < ActiveRecord::Base end class ArticleMessage < Message end class SmtpMessage < Message end class SentMessage < Message end
    20. 20. class Message < ActiveRecord::Base end ? class ArticleMessage < Message end class SmtpMessage < Message end class SentMessage < Message end
    21. 21. Prefer composition over inheritance Equip objects with references to other objects which implement common behavior
    22. 22. module S3MessageContent private def head_from_s3(filename) S3.head(APP_CONFIG['message_bucket_name'],filename) end end
    23. 23. class Attachment < ActiveRecord::Base include S3MessageContent belongs_to :message before_create :put_attachment_on_s3 before_destroy :remove_from_s3 end
    24. 24. Delegate, delegate, delegate Objects express certain outward behavior but actually delegate responsibility for that behavior to another object
    25. 25. Delegate, delegate, delegate Really good for ActiveRecord relationships
    26. 26. Delegate, delegate, delegate Really good for ActiveRecord relationships class ExternalEmailAccount < ActiveRecord::Base belongs_to :external_email_server delegate :server_info, :mail_server, :to => :external_email_server
    27. 27. Delegate, delegate, delegate Forwardable and Delegate modules also make this easy
    28. 28. YAGNI
    29. 29. YAGNI We love solving cool, new problems with cool, new toys
    30. 30. YAGNI We love solving cool, new problems with cool, new toys So sometimes we look into the future for opportunities
    31. 31. YAGNI This is a fatal instinct in startups
    32. 32. YAGNI I’ve built things to be super-scalable that turned out not to be core to the product
    33. 33. YAGNI I’ve built things to be super-scalable that turned out not to be core to the product
    34. 34. YAGNI In the early days, focus on learning not performance*
    35. 35. YAGNI In the early days, focus on learning not performance* Concentrate on 80% solutions
    36. 36. YAGNI In the early days, focus on learning not performance* Concentrate on 80% solutions *startuplessonslearned.com
    37. 37. YAGNI Be a duct tape programmer
    38. 38. YAGNI Be a duct tape programmer “...any kind of coding technique that’s even slightly complicated is going to doom your project.” http://www.joelonsoftware.com/items/ 2009/09/23.html
    39. 39. Don’t make these specific mistakes
    40. 40. Plan to move everything out of the web request
    41. 41. Plan to move everything out of the web request ar_mailer, delayed_job, EventMachine, SQS, beanstalkd, etc.
    42. 42. Plan to move everything out of the web request But remember YAGNI
    43. 43. Make careful use of concurrency
    44. 44. Make careful use of concurrency Prefer processes communicating via message bus (SQS, Starling, delayed_job, Rabbit MQ, etc.)
    45. 45. Make careful use of concurrency Check out Unicorn http://tomayko.com/writings/unicorn-is-unix
    46. 46. Make careful use of concurrency Threading: EventMachine is your friend
    47. 47. Make careful use of concurrency Threading: EventMachine is your friend EMH.safe_defer do begin UserMailer.deliver_verification_email(@user, @email) rescue StandardError logger.warn("Unable to deliver signup verification to #{@user.login} due to #{$!.message}") end end
    48. 48. Consider your RDBMS relationship
    49. 49. Consider your RDBMS relationship Avoid touching the DB when storing non-critical data
    50. 50. Consider your RDBMS relationship Avoid touching the DB when storing non-critical data Don’t use an RDBMS for things it’s not good at
    51. 51. Consider your RDBMS relationship Avoid touching the DB when storing non-critical data Don’t use an RDBMS for things it’s not good at We rely heavily on AWS
    52. 52. Consider your RDBMS relationship Storing large text blobs (S3) Messaging system (SQS) Logging events (SimpleDB) Caching dynamic text (S3)
    53. 53. Consider your RDBMS relationship We use data_fabric gem to make master-slave transparent
    54. 54. Consider your RDBMS relationship We also just used it to shard our DB
    55. 55. default: &default adapter: 'mysql' username: otherinbox_mysql database: otherinbox_production shard_0: &shard_0 password: --------- <<: *controller encoding: utf8 pool: 15 shard_0_slave: &shard_0_slave <<: *controller_slave controller: &controller <<: *default <% 1.upto(10) do |n| %> host: ##### <%= "shard_#{n}: &shard_#{n}" %> <%= " <<: *default" %> controller_slave: &controller_slave <%= " host: shard#{n}.####" %> <<: *default <%= %> username: otherinbox_ro <%= "shard_#{n}_slave: &shard_#{n}_slave" %> host: #### <%= " <<: *shard_#{n}" %> <% end %> # production! production: <<: *controller <% 0.upto(10) do |n| %> <%= "shard_#{n}_production:" %> <%= " <<: *shard_#{n}" %> <% end %>
    56. 56. Great DB Scaling Videos Scaling Your DB Part 1 and 2: http://railslab.newrelic.com
    57. 57. Reconsider Virtualization
    58. 58. DB indexes degrade over time Everyone blogs about EXPLAIN but what about ANALYZE and OPTIMIZE?
    59. 59. Know Your Query Planner One of our biggest speedups came from upgrading to the latest minor MySQL version
    60. 60. Organize your code nicely We have too much code in lib/*
    61. 61. Organize your code nicely A lot of this stuff is plumbing and could be extracted as plugins or gems
    62. 62. Organize your code nicely A lot of this stuff is plumbing and could be extracted as plugins or gems And released open source!
    63. 63. Organize your code nicely ActiveMerchant has a great layout
    64. 64. Organize your con g variables Dangerous / difficult to change Rarely changing Changeable at runtime
    65. 65. Dangerous / difficult to change INBOX_MESSAGE = 1 ARCHIVED_MESSAGE = 2 DELETED_MESSAGE = 3 SENT_MESSAGE = 4 REJECTED_MESSAGE = 5 Nearly immutable, identical in all situations
    66. 66. Rarely changing default: &default full_host_name: 'my.otherinbox.com' tech_support_address: 'support@otherinbox.com' max_subdomains_per_user: 2 max_alternate_domain_name_results: 10 domain_registration_api_timeout: 10 Dump into a YAML file
    67. 67. Rarely changing development: <<: *default full_host_name: 'oib.local' domain_registration_api_timeout: 1 You want it under version control
    68. 68. Changeable at Runtime >> Configuration.default_blocked_addresses => "admin,support,help,info,sales,jobs,webmaster" >> Configuration.default_blocked_addresses += ",oib" => "admin,support,help,info,sales,jobs,webmaster,oib"
    69. 69. Changeable at Runtime >> Configuration.default_blocked_addresses => "admin,support,help,info,sales,jobs,webmaster" >> Configuration.default_blocked_addresses += ",oib" => "admin,support,help,info,sales,jobs,webmaster,oib" http://beautifulpixel.com/svn/plugins/settings
    70. 70. Changeable at Runtime >> Configuration.default_blocked_addresses => "admin,support,help,info,sales,jobs,webmaster" >> Configuration.default_blocked_addresses += ",oib" => "admin,support,help,info,sales,jobs,webmaster,oib" http://beautifulpixel.com/svn/plugins/settings http://toolmantim.com/articles/ consolidating_your_apps_constants
    71. 71. Avoid Boolean Columns Often want to know what time something changed Or you later end up needing more than 2 states
    72. 72. Bundle complex view logic into Presenters
    73. 73. Bundle complex view logic into Presenters class RefreshController < ApplicationController before_filter :signin_required def index render :text => JSON.generate(AdvancedRefresher.new(params).to_hash) end end
    74. 74. Maybe don't test all the time, at the beginning?
    75. 75. Maybe don't test all the time, at the beginning?
    76. 76. Maybe don't test all the time, at the beginning? Can slow down exploratory programming
    77. 77. Maybe don't test all the time, at the beginning? You’ll probably throw away half the stuff you write at the beginning anyway
    78. 78. Maybe don't test all the time, at the beginning? You’ll definitely change the names of things!
    79. 79. Maybe don't test all the time, at the beginning? On the other hand, tests can be a design tool (as with BDD) I wrote our SMTP code this way
    80. 80. Maybe don't test all the time, at the beginning? Would be interesting to see how many Rails Rumble teams use tests
    81. 81. Afford regular access to designers
    82. 82. Questions? @subelsky mike@oib.com
    83. 83. Thank you! Slides posted at subelsky.com

    ×