A Big Ball of Mud
We recently went through the process of rethinking a large part of the architecture of our Rails app, which taught us many things. One of the things we learned was that there is an existing concept to describe what our codebase was — a big ball of mud.
According to Wikipedia, “a big ball of mud is a software system that lacks a perceivable architecture. Although undesirable from a software engineering point of view, such systems are common in practice due to business pressures, developer turnover, and code entropy”. Unless you are working in Lisp, where in this case it is used to describe the malleability of a Lisp system, it is not a good thing.
It is also worth noting that a big ball of mud is distinct from, yet perhaps orthogonal to, an increasingly common metaphor referring to eventual consequences of design choices in a codebase, known as technical debt.
So we know that a big ball of mud is bad, but chances are if you are reading this article, you have either firsthand experience inheriting a codebase that would qualify as a big ball of mud, or perhaps you have even written one yourself. The following is an attempt to provide developers with some guidance based on our experiences and the way we think about developing software, which will hopefully help someone facing either of these circumstances.
HIRE EXCEPTIONAL ENGINEERS
The first, best, and most question-begging thing you can do to avoid a big ball of mud, is to hire good engineers. This is one of those pieces of advice that is as annoying as it is true, because it begs the obvious question as to how does one hire good engineers. While I will save the answer to that particular question for a future blog post1, I want to discuss why in the specific context of avoiding a muddy codebase this is important. According to Intercom, one of the traits of exceptional engineers is that they habitually simplify.
They make the code they touch simpler, clearer, easier to work with and faster to change. They do this out of habit, opportunistically and continuously.
This is an amazingly valuable insight. The natural path of software over time is that it becomes more complex. Having engineers who truly understand this, internalize this, help their team around them learn this, and use it to influence their decision making on a day-to-day basis, is something of which the importance can not be overstated. In certain cases, it comes down to the difference between the technology being able to move with the business or not.
HAVE A LONG-TERM VISION
Another trait good engineers possess is the ability to develop a long-term vision for where the technology they own can and should evolve towards. The most concrete example of this I can give is an anecdote from our experiences. We noticed as we were developing our new API that quite a few of our ActiveRecord models were “swimming out of their lane”. Another way of saying this is that there were clear violations of the Single Responsibility Principle (SRP) at this particular area of our Rails app. Having both an understanding of the backwards compatibility that would have to be maintained in other parts of our application, as well as a vision for what types of responsibilities each of our models should have in an ideal world, allowed us to make prudent decisions about what should be migrated now, what should simply be made to appear as if it were migrated, and what was the subset of the existing functionality that was good and should be maintained.
REFACTOR CONTINUOUSLY, IN SMALL STEPS
With a solid engineering staff that has a place in it’s collective mind of what the architecture ought to look like, you can move in a better direction at whatever pace is appropriate given the constraints and goals of the business. This is an extremely powerful ability. Organizations that lack the ability to refactor continuously and opportunistically are ones where hearing phrases like “we would like to do [insert desirable engineering activity here], but we simply do not have the time” are all too commonplace. Similar to releasing widespread changes to your product in one fell swoop, unconstrained refactoring has a lower chance of being successful compared to iterative refactoring in service of a vision the entire team is excited about.
Quite simply, organizations that are serious about code quality measure it. Similar to automated testing, the tools you use to measure code quality are far less important than the fact that you do measure code quality somehow. We personally use Code Climate, which we can recommend. Code Climate will list potential style or security violations in your codebase, as well as callout areas with high cyclomatic complexity. It displays in list form opportunities for improving the overall quality of your applications. Other good tools for Ruby include Reek, RuboCop, and RubyLint. Brigade also released a Git hook manager called overcommit, which can run all of these linters (and many more) around your Git events.
The best advice anyone can give for learning and improving one’s code is to read. Practice, practice, practice. And read. One of the best things about software development is that it is one of those few skills that one can improve dramatically at without getting paid to do so. The same cannot be said for being a doctor2 or for being an investment banker. Both Refactoring: Improving the Design of Existing Code by Martin Fowler and Practical Object-Oriented Design in Ruby by Sandi Metz are excellent. These are kind of like books that teach you how to write well. They are the programming equivalent to Elements of Style. And if the written word is not your preferred style of learning, Adam Cuppy gave an excellent talk at RailsConf titled What If Shakespeare Wrote Ruby? and Mike Moore gave another at Los Angeles Ruby Conf called Build to Last, which talks about writing Rails applications that don’t need to be rewritten in a way that is highly congruent with the way that we think about building software.
If this resonates with you, I’d love to talk to you about joining our exceptional team :). Please get in touch.