Catching the same bug twice: a practical look at bug pattern detection
Here's a pattern you've lived through. A bug gets reported. Someone fixes the immediate symptom. It's closed. Three months later a different person reports what is functionally the same bug in a slightly different place, fixes that symptom, closes it. Six months after that, a third person, a third symptom, a third fix.
Each fix took an afternoon. Nobody spent the two days it would have taken to fix the underlying cause, because from any single person's seat, this was a one-off. The recurrence is only visible if you look across a year of tickets at once, and nobody does that.
What "pattern detection" means here
It's less sophisticated than it sounds. We take the corpus of issues — titles, descriptions, comments, labels, the components they touch, the errors they mention — and group ones that are talking about the same thing even when they use different words.
"Checkout fails intermittently," "payment button does nothing sometimes," and "order stuck in pending" can all be the same race condition. A human reading all three back to back sees it immediately. The value is in surfacing those three next to each other in the first place, when they were filed months apart by different people.
Alongside the grouping, a few plain counts:
- how many issues touch each component, over time
- which errors or phrases show up repeatedly across otherwise-unrelated tickets
- how often a "fixed" issue is followed by a similar one in the same area
What the patterns actually tell you
Some areas are load-bearing and underfunded. When one component shows up in 40% of your bug reports across a year, that's not bad luck. It's usually a piece of the system that grew faster than its tests, or that three teams touch and none owns. The pattern doesn't fix it, but it makes the case for prioritising it concrete instead of vibes-based.
Some "fixes" are actually deferrals. If every fix in an area is a small patch and the reports keep coming, the pattern is telling you the real bug hasn't been found yet. That's a prompt to stop patching and spend the two days.
Some recurrences are process, not code. A cluster of "config was wrong in production" issues isn't a code bug. It's a deployment gap. The grouping surfaces it; what you do about it is a different conversation.
What it does not tell you
It doesn't rank severity. A pattern of ten cosmetic issues and a pattern of two data-loss issues look similar to the grouping. You still have to read them and apply judgment about which cluster matters.
It doesn't know your roadmap. It might loudly flag an area that you're already three weeks from replacing entirely. The pattern is real; acting on it would be a waste. Context you have and the tool doesn't.
It can over-group. Sometimes two issues share vocabulary — "timeout," "queue," "retry" — without being related at all. We tuned toward showing you a slightly-too-broad cluster rather than missing a real one, on the theory that a human skimming ten tickets can discard the two that don't belong faster than they can find a connection that wasn't surfaced.
How we use it
Once a sprint, someone looks at the top clusters and asks one question of each: is this a thing we should fix at the root, or is it noise. Most are noise. The ones that aren't go on the backlog with the cluster attached as evidence, which makes them much harder to deprioritise, because "here are eleven tickets about this over the past year" is a stronger argument than "I have a feeling this area is flaky."
The race condition from the top of this post — checkout failing intermittently — was a real cluster for us. Seven tickets over ten months, seven small fixes. The eighth time it came up, we had the cluster in front of us, spent three days on the actual cause, and haven't seen it since. The first seven fixes cost more, combined, than the real one would have if we'd done it first. We just couldn't see that until the tickets were sitting in a pile.
See your own clusters — connect your issue tracker and look at what's been quietly recurring.