Brian's Blog Homepage
finger with a band aid sticking plaster

You found a bug. Good. Now the real work starts. 

Many developers think fixing a bug means making the error message disappear or getting the expected output to appear again. That is not fixing a bug. That is silencing a symptom. Real bug fixing requires understanding what changed, why it changed, and what that change affected.

There is a simple discipline you should follow every single time. It is not complicated. But it does require patience and honesty.

Start With the Obvious Question

Is the bug in new code?

If it is, then fix it properly. Do not wrap the problem in a defensive if statement. Do not add another condition that simply avoids triggering the failure. Correct the logic. Understand why your implementation fails and address the cause directly. If you start patching immediately, you are already introducing fragility into the system.

If the bug is in older code, the next question is more uncomfortable: did this ever work?

If it never worked, then you are dealing with a defect that survived unnoticed. Fix it properly and make sure there is test coverage so it cannot silently fail again in the future.

If it used to work, then something changed. That change is more important than the symptom you are currently staring at.

This is where discipline matters. Go back through the commit history until you find the exact change that introduced the breakage. Not the moment someone reported it. The moment it actually stopped working. When you identify that change, you move from guessing to knowing. You are no longer reacting. You are analysing.

When the Breaking Change Was Fixing Another Bug

Very often, the change that introduced the regression was itself a bug fix. Someone adjusted the logic to handle a specific case, and in doing so altered behaviour somewhere else.

This is the critical moment. You now have two legitimate behaviours to satisfy: the original functionality that used to work and the scenario that required the earlier fix. Simply re-patching the regression on top of the previous change is not engineering. It is stacking conditions until the logic becomes unreadable.

If one fix breaks another use case, it usually means the original approach was conceptually incomplete. The solution is not another layer of conditionals. The solution is to rethink the structure so that both cases are handled deliberately and cleanly. That often requires stepping back and reconsidering assumptions about the underlying design.

When the Breaking Change Was Adding a Feature

Sometimes the break appears after a new feature is introduced. The feature works, but something that previously behaved correctly now fails.That's a regression.

This is not bad luck. It is impact. The new feature changed the system in ways that were not fully understood.

In this situation, you are balancing existing behaviour with new requirements. Bolting on compatibility logic as an afterthought might appear to solve the immediate issue, but it increases complexity and reduces clarity. Instead, the feature needs to be integrated properly into the existing flow so that both the old and new behaviours are intentional outcomes of the design, not accidental side effects.

The Temptation to Patch Over It

The worst thing you can do is simply add another fix on top of the previous fix and move on.

If a change broke something and no one noticed at the time, there is a real possibility that it has also broke something else that has not yet been discovered. Every blind patch makes the system more fragile. Every added conditional increases cognitive load. Over time, the code becomes a maze of defensive checks that no one fully understands, including the people who wrote it.

That is how software rots. Not because developers are careless, but because they are in a hurry.

Regression Bugs Are Signals

A regression bug is not just an inconvenience. It is a signal that your mental model of the system was incomplete. Something behaved differently than expected, which means there is a gap in understanding somewhere.

Good engineers do not silence that signal. They trace it back to its origin. They understand what changed and why. They correct the cause rather than masking the symptom. They verify that all affected behaviours work as intended, not just the one currently visible.

This is the difference between firefighting and engineering. One approach creates an endless cycle of fixes. The other builds systems that remain stable release after release.

If you want software that survives more than a few iterations, stop stacking fixes. Slow down. Understand the change. Fix the problem properly.

J o o m l a !

Brian Teeman

Brian Teeman

Who is Brian?

As a co-founder of Joomla! and OpenSourceMatters Inc I've never been known to be lacking an opinion or being too afraid to express it.

Despite what some people might think I'm a shy and modest man who doesn't like to blow his own trumpet or boast about achievements.

Where is Brian?