A common question teams run into while building a Rails app is “What code should go in the
lib/ directory?”. The generated Rails README proclaims:
lib – Application specific libraries. Basically, any kind of custom code that doesn’t belong under controllers, models, or helpers. This directory is in the load path.
That’s not particularly helpful. By using the word “libraries” in the definition, it’s recursive. And what does “application specific” mean? Isn’t all the code in my Rails app “application specific” by definition?
So teams are left to work out their own conventions. If you quizzed the members of most teams, I’d bet they’d give different answers to the “What goes in
Antipattern: Anything that’s not an ActiveRecord
As we know from the secret to Rails object oriented design, well crafted applications grow many plain old Ruby object that don’t inherit from anything (and certainly not the framework). However, the most common antipattern is pushing any classes that are not ActiveRecord models into
lib/. This tremendously harmful to fostering a well designed application.
Treat non-ActiveRecord classes as first class citizens. Requiring these classes be stored in a
lib/ junk drawer away from the rest of the domain model creates enough friction that they tend not be created at all.
Pattern: Store code that is not domain specific in
As an alternative, I recommend any code that is not specific to the domain of the application goes in
lib/. Now the issue is how to define “specific to the domain”. I apply a litmus test that almost always provides a clear answer:
If instead of my app, I were building a social networking site for pet turtles (let’s call it MyTurtleFaceSpace) is there a chance I would use this code?
Now things get easier. Let’s look at some examples:
OnboardingReportclass goes in
app/. This depends on the specific steps the users must go through to get started with the application.
SSHTunnelclass used for deployment goes in
lib/. MyTurtleFaceSpace needs to be deployed too.
- Custom Resque extensions go in
lib/. Any site may use a background processing system.
app/if it imports a data format the company developed internally. On the other hand, a
FedExRateImporterwould probably go in
GPGEncrypterclass goes in
lib/. Turtles need privacy too.
Some developers find it distasteful to have classes like
ShippingRatesImporter living in
app/models/. This doesn’t bother me as I consider them to be part of the broadly defined “domain model”, but I’ll often introduce modules like
Importers to group these classes which avoids them cluttering up the root of the
I’m sure there are other patterns for dividing code between
app/. What rule of thumb does your team follow? What are the pros and cons?
Subscribe to our newsletter for more engineering intelligence.