← All categories

Bug Hunt

A support ticket with a design flaw behind it. Fix the cause, not just the symptom.

Bug Hunt problems start from a real-looking support ticket. The quick fix is usually obvious: add a check, copy a list, reset a flag. That patch makes the reported case pass, but the same flaw can still be reached by other paths.

Grading reflects this. Hidden regression tests replay the ticket, and hidden sibling tests try the other routes to the same root cause. Only a fix to the underlying design, such as immutable values, explicit states or proper equality, passes both.

You'll recognise it when

  • Two objects share a mutable list or object without anyone deciding they should.
  • A getter changes state, so reading a value twice gives two answers.
  • equals is overridden but hashCode is not.
  • Side effects happen before a transaction is known to succeed.

Example

Ticket: "I added a tag to one photo and it appeared on every photo in the album."

Before
class Photo {
    private static final List<String> NO_TAGS = new ArrayList<>();
    private List<String> tags = NO_TAGS;          // every new photo shares this list
    void tag(String t) { tags.add(t); }
}

// Quick patch: tag() copies first. Ticket fixed...
// ...but tags() still hands out the shared list to anyone who asks.
After
class Photo {
    private final List<String> tags = new ArrayList<>();   // each photo owns its tags
    void tag(String t) { tags.add(t); }
    List<String> tags() { return List.copyOf(tags); }       // callers can't change it
}

Why it's better: The root cause was shared mutable state, not the tag() method. Giving each photo its own list and never handing it out closes every path to the bug, including the ones nobody has reported yet.

Ideas you'll practise

Shared mutable stateDefensive copiesCommand-query separationExplicit statesequals and hashCodeTime zonesAfter-commit side effects

Problems 15