Package boundaries are a big deal

Classes and objects are useful tools for working with low-level abstractions. They can express high-level abstractions, too, but it's easy to suppose a given class is more abstract or general than it is.

Instead we can compose software from services and modules (packages—in the Ruby world these are gems) and reason about the interaction between those.

A Facebook developer who works on the React project shared some insights about package boundaries in a conversation about dependency injection. They isolate their tests at package boundaries rather than at a finer level. This matches the reality of code interactions better than pretending a test is always a first-class client of the code under test.
In my experience, DI is useful but has a cost in terms of code complexity. So we should only use it where appropriate. I think that for stuff that actually has multiple implementations (think a plugin system) it should be used (in fact, we use explicit DI in the React core for our pluggable rendering backends.

But with testing I think DI is overkill. Whitebox testing, where you're testing the interface as well as the internals, tends to catch more bugs, but when you change the internals the test tends to change as well. So DI doesn't make much sense here, since part of the point of DI is to isolate parts of the implementation that don't change. So if you're doing it just for testing, you're effectively just over-engineering everything.

Instead, we basically mock at a per-module level (sometimes called "service locator"). So for a given test run you can say that require('moduleA') should actually load moduleA-mock (dig around mock-modules to see what I mean).

This has the advantage of not requiring refactoring of the production code just for testing. I've found that the "refactoring to support DI makes your code better" is a bunch of baloney in practice when you're trying to ship products (at least based on my experience at Facebook, where we DI'd all of our PHP tests starting in late 2011 and are now trying to dig ourselves out of the pit).

Diversity vs. monoculture in systems…Linux systems

Rachel Kroll explains potential pitfalls of oversimplifying when we automate. She advocates letting a few different solutions coexist and compete. There's some amount of waste that results; there's also a sizable amount of risk mitigation in a depth that would be truly hard to implement "on purpose" in a fully central plan.

Diversity is healthy in any system—this applies equally to agriculture, education, and software teams as well as Linux system administration.

Load path for a per-app namespace à la Corey Haines

Corey Haines, an exponent of dependency-free testing in Rails apps, has suggested that all custom app logic be moved into its own namespace, and that namespace get its own directory under app/. So a custom class Baz in an app called foobar would be namespaced as Foobar::Baz, and the code would live in app/foobar/baz.rb.

A slightly non-obvious consequence of this is that while Rails picks up all subdirectories of app/ in its class loading, it's expecting that any namespacing will occur inside those directories, not be expressed by those directory names themselves. If we just throw code in app/foobar/baz.rb, it will expect a top-level Baz class to be there, not Foobar::Baz.

One solution is to add a second foobar subdirectory, so the code would now live in app/foobar/foobar/baz.rb. That seems suboptimal.

Instead we can add app/ itself to config.autoload_paths in config/application.rb:

config.autoload_paths += %W( #{config.root}/app )

Then app/foobar becomes eligible to house code in the Foobar namespace.

Maxim Chernyak has a concise cheatsheet on Rails load paths that walks through the various gotchas lurking there.