10 practices for writing maintainable Ruby on Rails applications

Ruby on Rails is a mature platform with five major version releases so far, and a sixth on the horizon. There are a lot of running applications out there which need maintaining. As developers, there are some better practices that, if followed, will make your future life more comfortable as you continue to develop the application.

What follows are ten practices that help to keep the codebase maintainable for the long term. A well-maintained codebase helps to keep the cost of development lower because changes are more straightforward and therefore quicker.

There’s no silver bullet, it can be hard work, but the pay-off is worth it from both the business perspective and the developer.

Design Patterns

Familiarise yourself with design patterns. Think of them as templates for how to solve common problems that are applicable in different scenarios. The benefit is that patterns are a known best practice which others can understand and reference. I highly recommend that each team member reads Patterns of Enterprise Application Architecture and the “Gang of Four” book, _ Design Patterns: Elements of Reusable Object-Oriented Software_.


Closely following this, refactor, refactor, refactor. Do it as part of the feature development. Don’t wait for, or ask for, specific time to refactor. It’s rarely granted. Refactoring in isolation is also rather pointless. Refactor when there is a concrete need to do so. Usually, when a change needs to be introduced, and if the code was refactored, makes the change easy.

For each desired change, make the change easy, then make the easy change Kent Beck

Ensure your team knows how to refactor effectively. I recommend everyone reads Refactoring. (Even better, read the version written specifically for Ruby, Refactoring Ruby.)

SOLID principles

As a codebase grows in complexity, it is not uncommon to find the speed that you’re able to make changes slows. SOLID _(Single Responsibility, Open/Closed, Liskov’s Substitution, I_nterface Segregation, and Dependency Inversion) is a set of five principles, which when followed, guide you to make better decisions which help to keep the rate of long term development more constant through better design.

Test Driven Development (TDD)

Closely linked with each of the above practices is TDD. It may take a little longer to develop a feature using TDD but it ensures you understand what the outcome needs to be, and it guides you to only the code required to solve the problem. The small amount of extra up-front time is paid back in spades when updates and upgrades are applied. You’ll have much more confidence knowing what’s broken when you have extensive test coverage across the codebase.

Prefer clarity over brevity

Writing some clever one-liner might make you feel smart in the moment, but future-you and other members of your team will feel otherwise when they have to unravel it.

Related, be wary of adding comments to the code. If you do, you must make sure they stay maintained as part of the team’s practice. I can’t tell you how much time we’ve wasted when taking over new projects with comments that are outdated and incorrect. Better to make the code readable in the first place, and use the commit messages to explain more because this history stays with the code changes.

Document decisions

Use tools like ADR records to record decisions made with their rationale, what else you considered and any pros or cons. Used consistently they provide crucial information as the codebase evolves and embeds knowledge into the codebase as your team changes instead of losing it in the mists of time.

Explain Why

As I’ve discussed before, using version control is essential, but if the commit messages aren’t written to be helpful, then you’re losing a lot of their value. Well written commit messages save vast amounts of time over the lifetime of the application. They make tracking down bugs faster, it can save a lot of wasted time making a change to existing code if you know why it was written the way it was. There can be many times where, in a complex codebase, it looks like it could be simplified and refactored, but a good commit message will tell you immediately that it could be to satisfy an odd business requirement, or to fix an underlying bug in a dependency, and so on. They are worth their weight in gold.

Always keep in mind: If you were looking at this 6 months from now, is this abundantly clear or is there some nuance that you would need to remember.

Code style

Define a common style at the outset of a new project to make browsing code easier — after all, you read code far more than you write it. It also means there aren’t commits for “tidying” code because there are competing styles, which then get mixed in with actual feature changes, making finding why code was changed more difficult.

Code Reviews

Build code reviews into your development workflows. Having another colleague review your changes before they’re included in the codebase provides several benefits. A separate pair of eyes can indicate where the code is unclear, or documentation is lacking clarity. It also has the added bonus of spreading knowledge amongst the team. Automated tests and lint checkers also ensure that broken code isn’t checked in, and code style conventions are maintained. These also allow human reviewers to concentrate on more important matters, and not waste time bikeshedding.


Require your development team to have stints supporting the end-users to help them learn what sort of things crop up from real users. They can use that experience to develop better solutions in the future. There’s nothing like a painful support experience to teach you how you could have coded a feature more robustly. Instrumenting the application to make support easier with good logging of errors and exceptions to make it quicker and easier for the team to solve when things go wrong.

Our team at Foxsoft specialises in supporting and maintaining Ruby on Rails applications for clients who want to make sure their bespoke remains an asset to their organisation. If you need help bringing these practices to your team, or you’d prefer to make it someone else’s problem, let’s have a chat and see if we’d be a good fit for you.