What Code Goes in the Lib/ Directory?


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 lib/?” question.

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 lib/

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:

  • An OnboardingReport class goes in app/. This depends on the specific steps the users must go through to get started with the application.
  • 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.
  • ShippingRatesImporter goes in app/ if it imports a data format the company developed internally. On the other hand, a FedExRateImporterwould probably go in lib/.
  • GPGEncrypter class goes in lib/. Turtles need privacy too.

Some developers find it distasteful to have classes like OnboardingReport and 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 Reports and Importers to group these classes which avoids them cluttering up the root of the app/models/ directory.

I’m sure there are other patterns for dividing code between lib/ and app/. What rule of thumb does your team follow? What are the pros and cons?

Actionable metrics for engineering leaders.

Try Velocity Free right_arrow_white

Start your 15-day free trial today.

See what Velocity reveals about your productivity and discover
opportunities to improve your processes, people and code.