TL;DR Version

Anything application specific goes in app/, anything not specific to this application goes in lib/. Most importantly, anything in lib/ by definition, should not know anything about files in app/. This is a one-way street: app/ can know about lib/ but lib/ should not know about app/.

Read the Manual

According to the README.rdoc of a fresh Rails install, app/:

Holds all the code that’s specific to this particular application.

Somewhat confusingly the README.rdoc goes on to explain the purpose of 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.

Application specific libraries may be a bit obtuse in this gem based world. Perhaps more appropriate it could say application specific customizations of external gems/classes.

But what really goes in app/ vs. lib/

Do you have an extending module for the String class? Do that in lib/. In this era of config/initializers some of the initial purposes of lib/ is obviated. (And I imagine this aligns with David Heinemeier Hansson’s desire to organize junk drawers.

One hangup that I’ve had to get over is that the directories of app/ are not sacrosanct. The “a-ha” moment was during the introduction to the asset pipeline, which brought about a new directory: app/assets/. This change opened the gates for me, and others, to begin adding custom directories to app/.

If you need application specific services, make an app/services/ directory. Or maybe you want active_model_serializers to handle the ugly details of serialization. Or simple_form custom inputs, you’re going to do that in app/inputs, then maybe just maybe move them to lib/.

And for heavens sake, don’t worry about your app/models/ directory having non-ActiveRecord::Base class definitions. That directory is your Domain Models directory. Though there is nothing to say that you can’t have an app/domain_models/ directory.

Ultimately, I would recommend viewing, and using, lib/ as a staging ground for gem extraction. Start with your code in app/ and if you find that code useful for another project, move the relevant code to lib/ (ensuring that you don’t depend on anything in app/). Then begin your gem creation.

And I’ll reiterate this again, any code that you put in lib/ should be independent of app/. Or, phrased another way, lib/ code should be perfectly functional if you removed the app/ directory.

Additional Material