Organizing large Rails projects

Do you have a Rails app that has gotten so big it is getting hard to know what is going on? When you have a new Rails app it is easy to tell what the app does by looking at the files in the models’ folder, but as the app gets bigger you often lose that. I prefer working on an app if I can first get a sense of it by looking at the model files.

A common solution

Namespacing your files in a Rails app is a common solution proposed to this problem of clutter and too many files. Using namespacing as an organizational concept isn’t terribly difficult. You find classes that really only exist to serve another class and you throw all those supporting classes into a folder that is named like the important class. Then you just need to make sure to rename the moved classes to reflect the move (along with adjusting tests, helpers, and anything else that calls that class).

The visual appeal of this is clear: a model/ folder with possibly hundreds of classes that can be organized into, perhaps a dozen upper classes, and the corresponding folders that contain the supporting classes. You are forced to make decisions that reveal the top level of your model domain (which was already there but is now explicit).

In defense of namespacing

Namespacing can offer more clarity at a glance into an app’s workings. Having a models folder full of 50-100 (or more) files do get quite confusing to sort through. Moving these files into namespaces and folders let you easily see the relationships and lets you highlight the most important models in your app. Namespacing also doesn’t limit how many models you can have overall and starting with namespacing the models folder can give you an easy pattern to follow for helpers and many tests.

A few problems with namespacing

  • The table names get longer.
  • Reorganizing is a lot of work and doesn’t create any user benefits.
  • Active record associations will have to be redefined.
  • Calls to the model will have to be rewritten anywhere they are called in the app (Foo.bar will become Namespace::Foo.bar)
  • As soon as you namespace a model you will have to fix its test file.

Small Nail, Big Hammer

So the problem that I see with all of this push to organize your models folder (or even the whole app) with namespacing is that to me it feels like hitting a small nail with a big hammer. Overall namespacing comes off as a lot of work just to organize the app for programmer understanding. The app may not be understandable to the programmer at a glace anymore (which is a little problem) but namespacing the whole models folder actually seems like too big of a fix. Namespacing a models folder with a hundred files in it (and changing tests, where they are called helpers, database table names, etc.) is a huge amount of work to just ‘organize’ something.

There is a better way. Imagine for a second that you were told to do all the work of namespacing but only to leave out the part where you move the newly renamed models to folders.

## Eg imagine if you were told to do this: (Namespaced without folders)
## models/
project.rb
project_item.rb    # => class Project::Item
project_contact.rb # => class Project::Contact
project_record.rb  # => class Project::Record

## Instead of doing this: (Namespaced with folder)
## models/
project.rb
project/item.rb    # => class Project::Item
project/contact.rb # => class Project::Contact
project/record.rb  # => class Project::Record

I think it is pretty clear that the namespacing without the folders doesn’t clear up the project much at all. Maybe a little bit, but not much in my estimation. The real clarity comes from putting all of the less important model files into folders that show off which more important model they support (or into a shared folder if they are less important but support multiple models).

So back to the small nail and big hammer problem. The small nail is that the app is getting disorganized and at a glance, readability has diminished as the app has grown. The big hammer is not only organizing all the models into different folders but also renaming them (and their database tables, tests, and helpers, etc.) so that they fit perfectly into this new organization scheme.

The right-sized hammer. Let’s throw out the namespacing/renaming everything idea and just organize the supporting models into folders named after the models they support without changing their class or wrapping them in a module (or a folder named ‘shared’). For example:

## Do this: (Folders without namespacing)
## models/
project.rb
project/item.rb    # => class Item
project/contact.rb # => class Contact
project/record.rb  # => class Record

We get all the organizational benefit with just a little bit of work.

How to do it

Create the folder (cd app/models then mkdir project) and move the files into it (mv item.rb project/item.rb). All we have to do then is make sure that Rails is loading the files from the folder. Rails will load all subfolders in models if you add config.autoload_paths += Dir[Rails.root.join("app", "models", "{*/}")] to config/application.rb.

You can test this by moving the model files into the subfolder, running your tests (they should fail now because they can’t find the files), and then adding that line and running the tests again (they should pass now).

So with a one-line change to application.rb and a couple of mkdir and mv commands we can organize the whole models folder without the whole bother of going full namespacing on our app.


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *