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.
The Rails guide says:
app/ Contains the controllers, models, views, helpers, mailers and assets for your application.
lib/ Extended modules for your application.
In other words,
app/ is for “configuring Rails”, and
lib/ is code that would make sense even if your application was ported to the console or an Android app for instance. Here’s a pristine exemplar of a well organized
lib/ from an application I worked on for reference.
One could thus argue that
app would have been better named something like
lib could have been named
app. But regardless of the naming, the docs are pretty clear on the intent. This distinction was revisited by Corey Haines in tweet which he subsequently quoted in a 2011 blog post.
“Rails is not your application. It might be your views and data source, but it’s not your application. Put your app in a Gem or under lib/.”
Taking it literally
I’m all for breaking with the conventions of a framework when there’s a very good reason. But there has been an (IMO) unfortunate trend of taking the name “lib” a little to literally, and thinking of its contents as “library code”, or “stuff that isn’t core to your domain logic”, roughly like a staging ground for code that could conceivably be extracted to a gem for use by external entities. For instance, in a 2012 post on the Code Climate blog, Bryan Helmkamp says:
I recommend any code that is not specific to the domain of the application goes in lib/.
He gives a sample litmus test:
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?
This is a prelude to some other examples he provides:
- An OnboardingReport class goes in app/. This depends on the specific steps the users must go through to get started with the application.
- A SSHTunnel class 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.
- A ShippingRatesImporter goes in app/ if it imports a data format the company developed internally. On the other hand, a FedExRateImporter would probably go in lib/.
- A GPGEncrypter class goes in lib/. Turtles need privacy too.
The problem with this kind of distinction in my view, is that such examples are even necessary. A good rule should be unambiguous. The default distinction provided by Rails is crystal clear—we know if a class inherits from
Whether a piece of code is unique to our application is less cut and dried. I would argue that the answer lies on a spectrum. For instance, in the sample
lib/ directory above, we had code that called out to the Socrata Open Data API. This is certainly less generic than the concept of user logins, and absolutely core to that application’s business domain. But a multitude of health care applications use Socrata, so it’s general purpose functionality within that space. In principle, we could have open sourced this and at least a few people would have used it. So is this generic enough to go in
lib/? I don’t know! And I don’t want a large diverse team to have inconsistencies and confusion about where to put things due to fuzzy ambiguous rules.
To revisit one of Helmkamp’s litmus tests:
A ShippingRatesImporter goes in app/ if it imports a data format the company developed internally. On the other hand, a FedExRateImporter would probably go in lib/.
Note the word “probably”. That’s an indicator of ambiguity. If we just think of
lib/ as “non-Rails code”, then the two importers would make sense named something like
lib/rate_importers/fedex.rb. The two entities are semantically related, and shouldn’t be “physically” separated because of their level of generality.
Where in /app?
Another problem with putting non-Rails code in
app is, where do you put it? For instance, one article from EngineYard demonstrated custom code separated into
app/decorators. This is confusing on multiple levels. We generally want code to be organized semantically. E.g. in our health care app, we had
lib/sessions/password_expiration_presenter.rb. Organizing around our session construct was clear. These classes conceptually relate to each other and would change for similar reasons at similar times. We wouldn’t want to have to move the latter file because it changed from a presenter to some other design pattern.
And we’re free to pick semantic groupings that maximize mutual exclusivity, so there’s a more obvious One Right Place for a given class to go. Separating code by its design pattern is confusing because patterns aren’t mutually exclusive. E.g. a “service object” can use the method object patten and decorate its arguments.
Rails solved this problem for us a long time ago, with a few pithy sentences in the docs. They are practical and unambiguous. Let’s keep it simple and use the defaults here.
Rails looks to be moving away from using
/lib, largely in the sense that it doesn’t get the autoload functionality.
Autoload paths automatically pick any custom directories under
app. For example, if your application has
app/services, etc., they are added to autoload paths.
The array of autoload paths can be extended by mutating
config/application.rb, but nowadays this is discouraged.
But if you use such directories, be mindful of the naming conventions that you should follow:
In a Rails application file names have to match the constants they define, with directories acting as namespaces.
For example, the file
UsersHelperand the file
Given this, I prefer
app/lib to schemes like
app/services. I prefer to organize non-Rails PORO’s semantically based on their domain rather than breaking them up by (somewhat subjective) style, like “presenters” or “services”. For instance, here’s a directory full of hospital-related code from a defunct startup I once worked at. It. includes things that one might call “services”, and presenters, and even models (on the general sense, not ActiveRecord sense). But I think they are better organized based on their semantic/domain relationships than whether they fit a design pattern.