- Requests hitting the same load balancer can't handle HTTP and HTTPS at the same time because of SSL issues
- If you are trying to access an outside resource behind a firewall, you have to tell them every time you get a new webserver so they can whitelist the new IP
- If you know the IPs of your web servers and database servers, you can have requests hit these directly and bypass the load balancers.
- Could buy your own data center, but then you have to buy a load balancing router. Those are fucking expensive (tens or hundreds of thousands of dollars)
- Private network in the cloud vís a vís Amazon
- A public subnet with an elastic load balancer, which hands requests off to private subnets for web servers and database servers
- Servers on the subnets only have private IP addresses!
- But what if you need to directly access web servers or database servers for maintenance? Have a bastion server you can SSH into and it will hand you off to the private subnets
- Outbound connections can be routed through a single NAT server with one public IP so that whitelists only need that one IP
- Available since Rails 0.14.3 as a plugin for Rails
- Rails 2.3 implements engines natively
- Rails 3.1 greatly improves rails engines by supporting the asset pipeline and making it easy to copy migrations from the engine to the core app
- Namespacing issue: if both the engine and the core app have a Product model, core app's Product model takes precedence and the engine will break
rails plugin new test-plugin --full --mountable
- All plugins in Rails are gems by default
- Add dependencies to the gemspec, NOT Gemfile
- A
Rails::Application
actually inherits fromRails::Engine
but is responsible for coordinating the boot process - Generating a model now places it in
app/models/engine_name/model.rb
so that it is namespaced to avoid collision.EngineName::Model
- Commonisms
- Modifying classes/methods at runtime (a la core_ext)
- Sending a method to an object (separation of the method from the object)
- Blocks
- Good lord this talk was just mostly incomprehensible.
- Relevant to my interests, but just incomprehensible
- Ruby applications used to have no good way of defining gem dependencies. Often, devs would just list the gems in their README
- .gems file:
cat .gems
and iterate through the list of gems, install them - Rails tried to solve this problem with the
config.gem
list inconfig/environment.rb
- DHH submitted a patch to bundler 1.1 so you can do
:git => "rails/rails"
bundle outdated
: get a list of outdated gems without having to runbundle upgrade
- Orphaned gem files
bundle clean
- Self-loading bundles
bundle install --standalone
- Creates a
.bundle/bundler/setup.rb
file that you can require; it sets up load paths for all the bundled gems
- Bundler 1.1 improves speed of installing one gem (sinatra) from 18s to 3s
- Uses API endpoints to fetch each gem's metadata instead of fetching the entire source index to quickly find each gem
- Falls back to fetching the full source index if the RubyGems API endpoints are, for some reason, unavailable
- Proposed features for bundler 1.2
- Ruby version check
- a
:local
option that falls back to:git
. Takes a local path to a gem you are developing locally (local gems no longer get put in Gemfile.lock) - NO BUNDLE EXEC (checks that the gem is in your Gemfile and uses that bundled version. if it's not, use the
gem install
'd version)
- This talk was just fucking awesome.
- tower.js
- Feature-by-feature clone of what most people associate with Rails, client-side with Node.js and CoffeeScript
- DHH: "Flexibility is overrated, constraints are liberating"
- Is Rails obsolete?
- No: the core principles that make it Rails are still controversial
- A simple web service in node.js
- Journey: JSON-only HTTP request router
- Cradle: CouchDB ORM
- ...
- Most people don't agree with everything in Rails, so Rails is always evolving
- Javascript web frameworks claim "convention over configuration", but then say things like "you can do X instead of Y" right off the bat. Rails gives a default option for everything and the configuration is an afterthought, mostly given by community members
- Points of failures in Rails
- Every command you copy and paste out of a blog post for Rails has a "10% chance of failure"
- Installation
$ sudo gem install rails
$ rails new app
$ cd app
$ rails server
- Caveat: this doesn't work in Rails 3.2, which should be considered a huge bug. Why doesn't this work?
- Default Gemfile now has
gem 'json'
which requires native C extensions to be built. - Another caveat: Rails 4 drops support for Ruby 1.8.7, which is still the default system Ruby for OSX.
- How to fix this? Binary Ruby, Binary Gems. Treat OSX users like Windows users and ship compiled Rubies/Gems
- Asset pipeline is too slow
- Booting Rails is too slow
bundle exec
should be run by default and just show a warning instead ofrake
dying- Traditional Rails
- Model => Controller => `@post = Post.find(1) => Template
- Rails with JS
- Model => Controller => { 'post' => { ... } } => In-browser Template
keys
is an O(n) operation that returns the number of keys. If you have a huge data set, this will probably time out. Resque uses this by default!?
class Post < ActiveRecord::Base
def after_save
Resque.enqueue(NotifyPeople, self.id)
end
end
- This causes a race condition because of transactions.
- Resque/Redis might start processing the job before the transaction is over, so the worker may not see the updated record if it starts quickly enough.
- Solution: Use
after_commit
instead. - What have they learned?
- Start with
expireat
. Do we need this forever? - Start with
slaveof
. Keep a replica or two around. - Upgrade to 2.4.6 made hset/hgetall performant.
- Resque can't be entirely trusted (see fork).
- Redis Cluster is still a ways off, so PRESHARD.
- Single-purpose instances over one multi-purpose.
- resque-jobs-per-fork has a nasty bug (fixed).
- Use
fakeredis
(work in progress) for testing
- Start with
- Put your models' logic in modules, mixin just at the right time (User::Validations, etc.)?
- Instead of monkey patching, extend the module and inject hooks/dependencies
- Law of Demeter (don't tightly couple your code)
- Don't write long, horizontal lines of code. Style your code.
- Try to stick to REST. Let your controllers be boring and predictable (CRUD) and don't define random controller actions that force you to match a bunch of shit.
- Give your join tables better names (TeamMembership instead of UserTeam)
- Wrote a hockey trivia rails app as a kid, made new questions by copypasting old ones and changing the words
- It should be easy and fast to add new features to an application. Your app shouldn't need to be born again.
- Conventions over configuration means everyone is on the same page.
- Which conventions?
- Do I follow the Rails convention?
- Do I follow the Ruby convention?
- Do I follow the OO convention?
- Do I follow the team's convention?
- These can all conflict; you have to decide.
- Avoid violating conventions; they're called best practices for a reason.
- Don't generate code. When you generate code, you now have to pay for code you didn't write and may not understand.
- Delete code. Every line of code has cost and you should straight up delete shit you don't use.
- Technical debt: "I know I'm making a mistake here, and I'm making it deliberately to easily achieve a goal."
- Boy Scout Rule: every module you check out, check it back in cleaner than you found it.
- Listen to your tests
- If your tests are slow, your software is slow.
- If your tests fail randomly, your software fails randomly.
- If your tests have timezone issues, your software has timezone issues.
- Write good tests and listen to them.
- Design patterns
- Get everyone speaking the same language
- If you follow design patterns where they're appropriate, you can explain the architectures of entire systems within minutes
- "Hey, we use this pattern." Done.
- Manage your dependencies
- Tight coupling sucks with dependencies
- Every time you type a constant, you've created a hard dependency
- If you are typing any raw SQL, you're tightly coupled to a specifically structured database
- Object-Oriented Design
- Encourages less coupling
- Gives you the vocabulary to talk about the ways your software can be improved
- Commits/comments should give the WHY of what you did
- SOLID principles
- Don't stub third-party libraries. Write a proxy to the library and stub that to avoid tight coupling
- If you're stubbing a class method (i.e.
Post.stub(:find).and_return(@post)
), you're clearly tightly coupled to ActiveRecord and you're probably Doing It Wrong™.
- MVC frameworks
- Delegate the V to the client using Javascript MVC
- Server-side views are expensive
- There's a catch...
- Frameworks are opinionated
- Testing is hard
- Code duplication (rewriting some server logic on the client)
- Longer iterations
- IE support is expensive
- Unexpected upsides
- You need an API anyway, so build it now
- Wean yourself off of the monolith
- Better subjectives (faster apparent loading, browser is native-ish)
- Denormalize your data into one large response
- Bootstrap common assets
- Cacheable layout (depersonalize, asset package, app shell)
- Common domain objects (US States, Categories)
- Bootstrap your data? (check Backbone.js documentation)
- Respond to every route with HTML
- Probably the most widely-used client-side framework right now
- Very flexible
- Lightweight
- Few dependencies
- Arbitrary collections
- Rendering is up to you
- Views: Rendering, event handling
- Models: key-value stores, change management, persistent
- Collections: Array proxies, enumerable via Underscore.js
- Events: General-purpose abstraction, lightweight coupling across layers, Collections/Models/Properties/Custom
- Inspired by Backbone.js
- Sugar for common practices
- Never wait for the server
- Manage everything
- Flexible rendering
- Prototypical
- Controllers: Rendering, event handling, application hierarchy, routing
- Models: key-value stores, weak collection management, Active-Record Style, opinionated
- Stacks: extends Controller, view state machine, manage visibility, hierarchy building block
- Events: General-purpose abstraction, Lightweight coupling across layers, Models/Custom, Questionable Implementation
- "Eliminate Boilerplate"
- Highly opinionated
- Don't manage the DOM yourself
- Property Bindings: auto-sync data across layers, declarative
- Computed Properties: Treat
function()
like property, can have dependent properties, composition across layers - Auto-updating views: views react to data changes, no calls to
render()
, per-property minimizes reflow, only Handlebars.js, DOM madness - Everything else: Object, View, ArrayController, Set, ...
-
Systems get exponentially more complex
-
Try not to screw it up
-
Level 0: Oblivious system
- Systems go down, dunno why
-
Level 1: Informed system
- Systems go down, we know about it (alerts)
-
Level 2: Defined system
- Systems go down, we know about it, and how to fix it
- Documentation for fixes
-
Level 3: Managed system
- Systems go down, we know about it and why, and the system has a plan to recover itself
-
Level 4: Optimized system
- Systems go down and magically come up
- Don't try to fix it, replace the component that's broken with a new component that works
-
Alerts
- Has to be actionable
- "Boy Who Cried Wolf" (alerts that are trivial or are about things you have no control over)
- No silent failures
- Alert with context, including a chain of evidence (full stack trace)
- Alert with recipe
-
Tolerance & Recovery
- Expect failure and design systems with failures in mind to minimize impact
- Isolate failures and degrade gracefully. Show a page with less functionality instead of no page at all
- Recover quickly from failures
- Health monitoring: sending a ping out to your severs every few seconds to see if they're responding
- Roll back (checkpointing). If a deployment fails, you can roll back to the previous deployment
- Roll forward for eventual compliance
- When a system fails, nuke it and create a new one in its place
- Look for opportunities for replication, sharding
-
CAP Theorem
- "It is impossible for a distributed computer system to simultaneously provide all three of Consistency, Availability and Partition tolerance."
- Partition tolerance isn't actually a choice
- This leaves a choice between consistency and availability
-
Testing & Releasing
- Use shipping systems (systems that are already in production somewhere)
- Ship often
- Deliver incrementally
- Full-system development: make sure you're developing against a local version of the entire system
- Hire a chaos monkey (have someone who wears a hat and is willing to go into your system to break it for fun)
- Don't design a Model to be coupled to an API
- Create a Service that talks to both your Model and the API
- You have services, stateless objects that talk to the application
- You have records, objects that talk to the database
- And you have Wrappers, a thin class for a narrow interface to an API
- Services can also talk to other Services
- SRP Rule: A class should have one, and only one, reason to change.
- Controllers tend to authenticate, authorize, wrap HTTP, manipulate models, manipulate databases (reload), query the database, present models, contain view content, create response content, route, choose content type, contain model logic...
- "If you have a big thing, and you put it into a press to squeeze it out into a lot of little things, you'll have a lot of great little things that are better than the big thing.
- Split the controller into various singular responsibilities.
- Threads are awesome
- But they suck!
- Hard to develop
- Hard to test
- Never, ever create a Thread in Ruby
- "Don't communicate by sharing data, share data by communicating"
gem install celluloid
- Sidekiq is a peer to resque and delayed_job
- Uses redis for storage
- Massively concurrent, using a default of 25 workers for process
- Instead of 25 200MB processes, one 300MB process
- Rails 3 native
- Async extensions for AR and AM
- Same metrics as resque, resque-web works!
- Capistrano integration
- Heroku
- Airbrake/Exceptional
- User-defined middleware
- 160 resque dynos ==> 10 sidekiq dynos
- $60k/yr savings