Question

Is there an anti pattern that describes a historically grown software system where multiple developers just added new features to the system but no one really kept an eye on the overall architecture nor were refactorings ever done?

I think this happens when management/customer asks for new feature constantly and no one ever refactors anything but just adds on to what other developers haven done before.

A reason could also be that the developer is just overwhelmed with the software system and does not really understand how it currently works and then just adds/glues his code at the very end (instead of refactoring the code and changing it.)

So over time it's getting harder and harder to maintain the system.

(I wonder if there are pictures for these kind of anti pattern to make this more clear to none programming folks - like a car that was built by just adding more and more features without thinking of the overall design. Like someone has the need to tow trailers while riding backwards and then an engineer just welds a tow bar to the front of the car. Job done. But now the cowl doesn't open anymore.)

Was it helpful?

Solution

You refer to technical debt.

We all accrue technical debt in the products we develop over time; refactoring is one of the very common and effective ways of reducing this technical debt, though many companies never pay down their technical debt. These companies tend to find their software extremely unstable years down the road, and the technical debt becomes so gruesome that you can't pay it down incrementally, because it would take too long to pay it down that way.

Technical debt has the term, because it follows the same behaviours of debt. You get the debt, and as long as you continue spending (creating features) and not paying down that debt, it'll only grow. Much like debt, when it get's too large you get to points where you may wish to shed it entirely with dangerous tasks like full-out rewrites. Also like real debt, as it accrues to a certain point, it hinders your ability to spend (creating features) altogether.

Just to throw another term in the mix, cohesion refers to how well a system, micro to the line level, or macro to the system level, fits together. A highly cohesive system will have all of its pieces fit together very well and look like one engineer wrote all of it. Your reference above to somebody just gluing their code to the end would be violating the cohesion of that system.


Managing Technical Debt

There are a lot of ways to manage technical debt, though like real debt the best approach is to pay it down frequently. Unfortunately like real debt it's occasionally a better idea to accrue more for a short period, where for instance time to market for a feature may double or triple your revenue. The tricky part is weighing these competing priorities as well as identifying when the debts ROI isn't worth it for the given feature vs. when it is.

So sometimes it is worth it to accrue the debt for a short period, but that's rarely the case, and as with all debt, the shorter the period the better. So eventually (preferably quickly) after you accrue technical debt, you have to pay it down, these are common approaches:

  • Refactoring
    • This allows you to take bits of code that were only realized to be misplaced partway through or after implementation was complete, and put them in their correct place (or a more correct one anyway).
  • Rewrite
    • This is like a bankruptcy. It wipes the slate clean, but you start with nothing and have every opportunity to make the same mistakes, or even bigger ones. High risk high reward approach to technical debt, but sometimes it's your only option. Though that's more rarely the case than many will tell you.
  • Architecture Overview
    • This is more of an active technical debt pay-down approach. This is done by having someone with authority over the technical details to halt an implementation regardless of project plans and schedules to ensure it accrues less technical debt.
  • Code Freeze
    • Freezing the code of changes can allow you breathing room where your debt doesn't go up or down. This gives you time to plan your approach to reducing the technical debt with hopes of having the highest ROI on your approach.
  • Modularization
    • This is like a tier-2 approach only available when you employ either Architecture Overview to have a modular system already, or Refactoring to move towards one. When you have a modular system, you can then pay down debt in whole pieces of the system in an isolated way. This allows you to do partial re-writes, partial refactoring, as well as minimizing the rate technical debt accrues because the isolation keeps the debt only in those areas where the features went in, as opposed to spread around the system.
  • Automated Tests
    • Automated testing can aid in managing your technical debt, because they can help you identify trouble spots in the system, hopefully before the debt in those areas has grown very large, but even after the fact they can still make engineers aware of those dangerous areas they may not have already realized. Furthermore, once you've got automated tests, you can more freely refactor things without concern for breaking too much. Not because developers won't break things, but because they'll find out when they do break things, relying on manual testers in highly indebted systems tends to have a poor track record for finding issues.

OTHER TIPS

Your description fits Foote and Yoder's Big Ball of Mud:

Over the last several years, a number of authors... have presented patterns that characterize high-level software architectures... In an ideal world, every system would be an exemplar of one or more such high-level patterns. Yet, this is not so. The architecture that actually predominates in practice has yet to be discussed: the BIG BALL OF MUD.

A BIG BALL OF MUD is haphazardly structured, sprawling, sloppy, duct-tape and bailing wire, spaghetti code jungle. We’ve all seen them. These systems show unmistakable signs of unregulated growth, and repeated, expedient repair. Information is shared promiscuously among distant elements of the system, often to the point where nearly all the important information becomes global or duplicated. The overall structure of the system may never have been well defined. If it was, it may have eroded beyond recognition. Programmers with a shred of architectural sensibility shun these quagmires. Only those who are unconcerned about architecture, and, perhaps, are comfortable with the inertia of the day-to-day chore of patching the holes in these failing dikes, are content to work on such systems...

Why does a system become a BIG BALL OF MUD? Sometimes, big, ugly systems emerge from THROWAWAY CODE. THROWAWAY CODE is quick-and-dirty code that was intended to be used only once and then discarded. However, such code often takes on a life of its own, despite casual structure and poor or non-existent documentation. It works, so why fix it? When a related problem arises, the quickest way to address it might be to expediently modify this working code, rather than design a proper, general program from the ground up. Over time, a simple throwaway program begets a BIG BALL OF MUD.

Even systems with well-defined architectures are prone to structural erosion. The relentless onslaught of changing requirements that any successful system attracts can gradually undermine its structure. Systems that were once tidy become overgrown as PIECEMEAL GROWTH gradually allows elements of the system to sprawl in an uncontrolled fashion.

If such sprawl continues unabated, the structure of the system can become so badly compromised that it must be abandoned. As with a decaying neighborhood, a downward spiral ensues. Since the system becomes harder and harder to understand, maintenance becomes more expensive, and more difficult. Good programmers refuse to work there. Investors withdraw their capital. And yet, as with neighborhoods, there are ways to avoid, and even reverse, this sort of decline. As with anything else in the universe, counteracting entropic forces requires an investment of energy. Software gentrification is no exception. The way to arrest entropy in software is to refactor it. A sustained commitment to refactoring can keep a system from subsiding into a BIG BALL OF MUD...

  • ...One of mud's most effective enemies is sunshine. Subjecting convoluted code to scrutiny arrow can set the stage for its refactoring, repair, and rehabilitation. Code reviews are one mechanism one can use to expose code to daylight.

http://www.laputan.org/images/pictures/Mir-Mud.gif

Licensed under: CC-BY-SA with attribution
scroll top