Five software Anti-Patterns worth designing out

Tangled control flow, god objects, copy-paste logic, weak version control and premature optimisation — why each persists and what removes it for good.

6 min read Updated 28 September 2026
Five software Anti-Patterns worth designing out

Anti-patterns are usually presented as mistakes. They are better understood as reasonable local decisions with bad global consequences — which is why telling people to avoid them does not work, and why the fix is structural rather than educational.

Each of the five below persists because it was the fastest option at the time. What removes it is changing what the fastest option is.

1. Tangled control flow

The pattern: logic that cannot be followed without holding the whole file in your head. Conditions nested five deep, a function that does six things, state modified in places the name does not suggest.

Why it persists. It rarely arrives at once. Each addition was a small, sensible change to code that was already slightly tangled. Nobody introduced the mess; it accreted.

The business consequence. Change becomes unpredictable — the same request takes an hour in one area and three days in another, with no way to tell in advance. That unpredictability is what erodes trust in engineering estimates.

What removes it: a size limit that forces the conversation. Not a style rule, a review question — “can a new joiner follow this?” And crucially, refactoring budgeted as part of feature work rather than as a project that never gets approved.

2. God objects

The pattern: one class or module that knows about everything. Every feature touches it, so every change risks everything.

Why it persists. Adding to the thing that already has the context is always faster than creating a new boundary and wiring it in. That is true on every individual occasion, and false in aggregate.

The business consequence. It becomes the bottleneck for parallel work. Two people cannot safely change it at once, so the team serialises around a single file — which looks like a people problem and is an architecture problem.

What removes it: cutting by business capability rather than technical layer, the same principle that governs legacy modernisation in seven principles of legacy modernisation without downtime. A capability can be changed and reverted independently; a god object cannot.

3. Copy-paste logic

The pattern: the same rule implemented in four places, because copying was faster than finding the right abstraction.

Why it persists. It genuinely is faster, and the cost lands on whoever changes the rule later — usually a different person, often a different year.

The business consequence. This is the one that produces wrong answers rather than slow ones. The rule changes, three copies are updated, the fourth is missed, and two parts of the system now disagree. In a regulated context that is not a bug; it is a finding.

What removes it: knowing where business rules live. If a developer cannot answer “where is the discount rule implemented?” in under a minute, copies will keep appearing regardless of discipline.

4. Weak version control practice

The pattern is rarely no version control at all any more. It is version control used badly: commits that bundle six unrelated changes, branches that live for months, messages that say “fixes”, history nobody can read.

Why it persists. Discipline here has no immediate payoff. It costs a little now and saves a lot later, which is the worst possible incentive shape.

The business consequence. You lose the ability to answer “when did this break, and what changed?” — which is the first question in every incident. And for anything under revDSG/nDSG, GDPR, IEC 62304 or FINMA expectations, the change history is part of the evidence you have to produce.

What removes it: short-lived branches, small commits, and a message convention that survives review. The regulatory framing helps here — it converts a preference into a requirement.

5. Premature optimisation

The pattern: complexity added for performance that was never measured to be a problem. Caches that create staleness bugs, denormalised data that drifts, concurrency where sequential would have been fine.

Why it persists. It feels like professionalism. Anticipating scale is what a senior engineer is supposed to do.

The business consequence. You pay the complexity cost immediately and permanently, for a benefit that may never arrive. Worse, the added complexity often makes the real bottleneck harder to find when it does appear — and it usually appears somewhere nobody predicted.

What removes it: measurement before optimisation, without exception. And the inverse discipline — which matters more in practice — is knowing which performance decisions genuinely are architectural and cannot be deferred: data model shape, synchronous versus asynchronous boundaries, anything that would require a rewrite to change.

How to act on this

Reading a list of anti-patterns changes nothing on its own. Three interventions do:

Make the healthy path the fast path. If adding a new module takes twenty minutes of scaffolding and adding to the god object takes two, the god object wins every time regardless of what anyone believes.

Budget refactoring inside feature work. A cleanup project competing against features loses permanently. A feature that includes leaving its area better does not.

Watch the trend, not the snapshot. The signal that matters is whether the share of time spent on maintenance is rising. That shows up in finance data before it shows up in code, as covered in maximising engineering ROI: the CFO’s guide.

If the estimates in one part of your system are unpredictable and you want to know why, book a call. No obligation.

Code Quality Anti-Patterns Technical Debt Refactoring Engineering

Let's talk about your project.

Tell us about your project. We will assemble the right team, define a clear scope and get going, usually within two weeks.

Book a call