In "Big Ball of Mud", Brian Foote and Joseph Yoder propose that the default (and most common) software architecture in use is the "Big Ball of Mud" pattern and go on to discuss six additional patterns and activities that it gives rise to: "Throwaway Code", "Piecemeal Growth", "Keep it Working", "Shearing Layers", "Sweep it Under the Rug" and "Reconstruction".
Their original article is located here and can be downloaded in PDF form here: Big Ball of Mud by Brian Foote and Joseph Yoder [PDF download]
I have picked out what I think are the highlights of their article ...
Big Ball of Mud ... alias Shantytown or Spaghetti Code
Shantytowns are usually built from common, cheap materials with simple tools and using unskilled labor. The construction and maintenance of the shantytown is labor intensive, and there is little or no labor specialization - each builder must be a jack of all trades. There's no overall planning, or regulation of future growth.
Too many of our software systems are, architecturally, little more than shantytowns. Investment in tools and infrastructure is often inadequate and the tools that are used are primitive. Parts of the system grow unchecked, and the lack of architecture and planning allows problems in one part of the system to erode and pollute adjacent portions. Deadlines loom like monsoons, and architectural elegance seems unattainable.
The time and money to chase perfection are seldom available and there is a survival at all costs attitude, to do what it takes to get the software working and out the door on time. The biggest cost borne by the Big Ball of Mud development is the lack of a decent architecture.
Common features of Big Ball of Mud Code
- Data structures are haphazardly constructed, or non-existent.
- Everything talks to everything else.
- Important state data is global.
- State data is passed around though Byzantine back channels that circumvent the system's original structure.
- Variable and function names are uninformative and misleading.
- Functions use global variables extensively, as well as long lists of poorly defined parameters.
- Functions themselves are lengthy and convoluted, and perform several unrelated tasks.
- Code duplication.
- The flow of control is hard to understand, and difficult to follow.
- The programmer’s intent is next to impossible to discern.
- The code is simply unreadable, and borders on indecipherable.
- The code exhibits the unmistakable signs of patch after patch at the hands of multiple maintainers, each of whom barely understood the consequences of what he or she was doing.
- Did we mention documentation? What documentation?
Some software engineers come to regard life with the Big Ball of Mud as normal and become skilled at learning to navigate these quagmires, and guiding others through them. Over time, this symbiosis between architecture and skills can change the character of the organization itself, as swamp guides become more valuable than architects.
As per Conway's Law, architects depart in futility, while engineers who have mastered the muddy details of the system they have built, prevail. The code becomes a personal fiefdom, since the author care barely understand it anymore, and no one else can come close. Once simple repairs become all day affairs, as the code turns to mud.
Throwaway Code ... alias Quick Hack or Protoduction
While prototyping a system, you're normally unconcerned with how elegant or efficient your code is. You plan that you will only use it to prove a concept and once the prototype is done, the code will be thrown away and written properly. As the time nears to demonstrate the prototype, the temptation to load it with impressive but utterly inefficient realizations of the system’s expected eventual functionality can be hard to resist. Sometimes, this strategy can be a bit too successful. The client, rather than funding the next phase of the project, may slate the prototype itself for release.
This quick-and-dirty coding is often rationalized as being a stopgap measure. More often than not, the time is never found for this follow up work. The code languishes, while the product flourishes. It becomes a protoduction - a prototype that gets used in production.
Once it becomes evident that the throwaway code is going to be around for a while, you can turn your attention to improving its structure, either through an iterative process of Piecemeal Growth, or via a fresh draft, as discussed in the Reconstruction pattern below.
Piecemeal Growth ... alias Refactoring
Successful software attracts a wider audience, which can, in turn, place a broader range of requirements on it.
When designers are faced with a choice between building something elegant from the ground up, or undermining the architecture of the existing system to quickly address a problem, architecture usually loses.
In the software world, we deploy our most skilled, experienced people early in the lifecycle. Later on, maintenance is often relegated to junior staff, and resources can be scarce. The so-called maintenance phase is the part of the lifecycle in which the price of the fiction of master planning is really paid. It is maintenance programmers who are called upon to bear the burden of coping with the ever widening divergence between fixed designs and a continuously changing world.
Piecemeal growth can be undertaken in an opportunistic fashion, starting with the existing, living, breathing system, and working outward, a step at a time, in such a way as to not undermine the system’s viability. You enhance the program as you use it. Massive system-wide changes are avoided - instead, change is broken down into small, manageable chunks.
Keep It Working ... alias Continuous Integration
Businesses become critically dependent on their software and computing infrastructures. There may be times where taking a system down for a major overhaul can be justified, but usually, doing so is fraught with peril. Therefore, do what it takes to maintain the software and keep it going. Keep it working.
This approach can be used for both minor and major modifications. Large new subsystems might be constructed off to the side, perhaps by separate teams, and integrated with the running system in such a way as to minimize disruption.
A development build of each product can be performed at regular intervals, such as daily or even more often via an automated build tool. Another vital factor in ensuring a system's continued vitality is a commitment to continuous testing, which can be integrated into the automated build process.
Software never stands still. It is often called upon to bear the brunt of changing requirements, because, being as that it is made of bits, it can change.
Over time, the software's frameworks, abstract classes, and components come to embody what we've learned about the structure of the domains for which they are built. More enduring insights gravitate towards the primary structural elements of these systems and change rarely. Parts which find themselves in flux are spun out into the data, where users can interact with them. Software evolution becomes like a centrifuge stirred by change. The layers that result, over time, can come to a much truer accommodation with the forces that shaped them than any top-down planning could have devised.
Sweeping It Under The Rug
At first glance, a Big Ball of Mud can inspire terror and despair in the hearts of those who would try to tame it. The first step on the road to architectural integrity can be to identify the disordered parts of the system, and isolate them from the rest of it.
Overgrown, tangled, haphazard spaghetti code is hard to comprehend, repair, or extend, and tends to grow even worse if it is not somehow brought under control. If you can’t easily make a mess go away, at least cordon it off. This restricts the disorder to a fixed area, keeps it out of sight, and can set the stage for additional refactoring.
Reconstruction ... alias Total Rewrite
One reason to start again might be that the previous system was written by people who are long gone. Doing a rewrite provides new personnel with a way to reestablish contact between the architecture and the implementation. Sometimes the only way to understand a system it is to write it yourself. Doing a fresh draft is a way to overcome neglect. Issues are revisited. A fresh draft adds vigor. You draw back to leap. The quagmire vanishes. The swamp is drained.
When a system becomes a Big Ball of Mud, its relative incomprehensibility may hasten its demise, by making it difficult for it to adapt. It can persist, since it resists change, but cannot evolve, for the same reason. Instead, its inscrutability, even when it is to its short-term benefit, sows the seeds of its ultimate demise.
The above are highlights from the original article, which is located here ... Big Ball of Mud by Brian Foote and Joseph Yoder [PDF download]
Some further reading on Programmers.StackExchange ...
I've inherited 200K lines of spaghetti code — what now?
How to convince my boss that quality is a good thing to have in code?
How to keep a big and complex software product maintainable over the years?
Techniques to re-factor garbage and maintain sanity?
When is code “legacy”?
What is negative code?
Some related books ...
Working Effectively With Legacy Code ... by Michael Feathers [Amazon]
Refactoring: Improving the Design of Existing Code ... by M. Fowler, K. Beck, et al. [Amazon]
Design Patterns: Elements of Reusable Object-Oriented Software ... by the Gang of Four [Amazon]
Patterns of Enterprise Application Architecture ... by Martin Fowler [Amazon]
Domain Driven Design: Tackling Complexity in the Heart of Software ... by Eric Evans [Amazon]
Head First Design Patterns by E. Freeman, E. Freeman, et al. [Amazon]