What goes in Rails lib/

clay shentrup
5 min readNov 29, 2017

--

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 rails orframework, and 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 ActiveRecord::Base or ActionController::Base.

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/shipping.rb and 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/services and 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/last_attempt_handler.rb and 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.

Conclusion

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.

UPDATE

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/presenters, or app/services, etc., they are added to autoload paths.

The array of autoload paths can be extended by mutating config.autoload_paths, in 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 app/helpers/users_helper.rb should define UsersHelper and the file app/controllers/admin/payments_controller.rb should define Admin::PaymentsController.

Given this, I prefer app/lib to schemes like app/presenters or 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.

--

--

clay shentrup
clay shentrup

Written by clay shentrup

advocate of score voting and approval voting. software engineer.

Responses (2)