Sometime we start building projects avoiding architecture and following framework conventions but diverging from that helped managing a Rails project growing for about 3 years.

Some Rails shortcoming have been fixed since then and I will link to the latest techniques but recognizing when to diverge from the conventional approach is still challenging and important to prevent an unmaintainable application. Do it too early and you will over engineer too late and the refactoring will be expensive.

The initial development went on for about 10 months with a team of two backend developers, two frontend plus designers and a project manager. After launch the application was constantly extended and maintained for the 24 months I was on the team.

The project

We were about to rebuild a multinational brand site dedicated to pregnancy and newborn content with social interactions delegated to a forum–the new version was adding custom social functionalities within the content–the original baby name finder for example was browsing a list of name meanings but its new version was also allowing members to shortlist, vote names, share with family, generate weekly popularity graphs.

We decided against a gradual deliver and instead rebuild all the existing vertical areas dedicated to specific subjects: baby names, promotions, kids activities, kids recipes each served within a unique segment: /baby-names, /promotions, /kids-activities, /kids-recipes–after that the stakeholder intended to invest in even more verticals.

Initial approach

Before starting development we agreed that keeping code from different verticals in the same controllers and models directory would obfuscate their boundaries and we decided to use subdirectories mapped to verticals ie. /app/models/babynames_models, /app/controllers/babynames, /app/views/babynames containing all code specific to baby names–this allowed us to quickly find vertical related code and reduce merge conflicts while developing in parallel.

$ ls -l app/models
ask_an_expert_models            contact_us                      loyalty_models                  recipe_finder_models            utils
babynames_models                kids_activities_models          membership_models               sharing
babynames_utils                 landing_page_header             promos_and_samples_models       story_models
$ ls -l app/controllers
admin                           ask_an_expert                   contact_us_controller.rb        membership                      promos_and_samples
admin_controller.rb             babynames                       kids_activities                 my_project                      recipe_finder
application_controller.rb       contact_us                      loyalty                         my_project_controller.rb        stories

This approach was sufficient during the initial development phase but after we went live and started adding more verticals the growing number of subdirectories made jumping between models/controllers/views time consuming–ideally I wanted to find and work on all the babynames files within a single directory. The application route file /config/routes.rb was a 300 lines mix from all verticals hard to understand and painful to maintain.

A self contained approach

To address these problems I introduced the room decorator vertical as a plugin (precursors of engines) not to reuse its code but to encapsulate its models, controllers, views, helpers, routes–now only minor changes would affect a few models in the main application through decoration.

The main application routes would pick up the plugin routes from PLUGIN/config/routes.rb and stop growing.

We were also able to have unit tests inside the plugin.

To make verticals plugins stand out from the vendor plugins we had a naming convention to append a static string in front of plugin name.

$ ls -l vendor/plugins/ | grep project
drwxr-xr-x   7 agenteo  84396665  238 14 Nov 19:55 project_api
drwxr-xr-x   7 agenteo  84396665  238 14 Nov 20:06 project_baby_room_gallery
drwxr-xr-x   9 agenteo  84396665  306 14 Nov 20:08 project_blog
drwxr-xr-x  14 agenteo  84396665  476 14 Nov 20:08 project_mums_tips
drwxr-xr-x   6 agenteo  84396665  204 14 Nov 20:11 project_voting_tool

What didn’t go well

Like the subdirectory structure the plugins were setting a weak boundary meaning classes in one vertical could depend on other verticals or the main application creating dependencies impossible to track without reading the code.

If a developer must consider the implementation of a component in order to use it, the value of encapsulation is lost.

Eric Evans

It was still hard to pinpoint which of the over 200 migrations belonged to a plugin and how its vertical was affecting the database schema.

I started thinking how much code should be in the main application for the plugins to use? Back then I could only chase good balance but with current Rails I can achieve that by moving all the functionality away from the main application in to small engine components creating a solid dependency structure. I explain how in A component based Rails architecture primer.

This plugin approach I used in 2008 was far from perfect but it reduced entropy and it was better then having all models and controllers in a fragile directory structure or even worst introducing service oriented architecture for code organization. I disagree that after a few years large applications must be rebuilt if you build them incrementally using a supple design they can be maintained and shaped in to a new form.

comments powered by Disqus

Enrico Teotti

developer and agile mentor with 15 years experience in the IT industry across Europe, Australia and the US.

Back to Overview