New software smells great. Like a new car. It evokes images of a lush green field full of fragrant flowers, with butterflies and chirping birds. Developers usually like to build software from scratch or rewrite other people’s code. They view enhancing existing software as menial labor - beneath their dignity. Like tilling an ugly brown field full of rocks, that smells of manure. Ranting about the quality of legacy code is mandatory for acceptance in the community. I’ve been there before.
But legacy software is not always worthy of such disdain. In many cases, it is an asset that has been making money for the business, a source of truth for business rules and process knowledge that has been through the fires of production usage, and will continue to be functional for many more years. It might not need to be completely rewritten or upgraded to the latest and greatest technology platform yet. Maybe parts of it could be opportunistically redone if there is a business need or risk from using outdated technology.
Enhancements and bug fixes would certainly be needed to support the business as it evolves. These might be more challenging to make than in greenfield systems. Brownfield systems have usually accreted organically over time. Different developers have over the years brought their individual philosophies to bear on the codebase with the code itself being the only communication medium between them. Documentation is often scarce or non-existent. This is what makes system wide changes particularly challenging to make. Modifying code without breaking existing code can feel like a game of Jenga at times. It is sobering to remember that brownfield systems started life as greenfield systems.
According to Bob Martin (aka Uncle Bob), a system’s design can rot over time if not tended to. This rot manifests in the form of various code smells:
This is where knowledge of SOLID principles, refactoring and unit testing can be invaluable. The SOLID principles are a set of Object Oriented design principles proposed by Uncle Bob to address the code smells mentioned above. The key idea tying the principles together is to avoid coupling classes to each other. That is, eliminating dependencies between classes and keeping them as lean, single purpose, loosely coupled units with few or no close friends, that interact with other classes through well defined contracts. That can be developed, modified and tested in isolation and are fungible - i.e. can be snapped in and out easily like lego pieces as long as the shapes fit. This allows us to create software architectures that are antifragile, that is, not only resilient to stressors, but actually benefiting from them. Not all the principles are equally relevant or applicable to all environments. For example, the Liskov and Open-Closed principles may not really be relevant to functional programming. On the other hand, Single responsibility and dependency inversion could certainly be.
SOLID itself is one of those manufactured acronyms that the programming landscape is littered with. Baffling to the uninitiated, yet valuable as mnemonic devices. The principles enumerated are:
In future posts, we will examine each principle in more detail with examples.