Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Ruby For Startups

4,415 views

Published on

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

Published in: Technology, Education
  • Be the first to comment

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

×