A practical decision tree you can actually use in real code
Most people learn design patterns like vocabulary flash cards.
- “Singleton = one instance”
- “Strategy = interchangeable algorithms”
- “Observer = event subscription”
And then real life happens:
You open a codebase. The requirements changed. Two services now need to coordinate. Your “simple” conditional became a hydra.
You don’t need more memorization.
You need a way to diagnose the problem you’re facing — and only then choose a pattern.
This post gives you exactly that: a decision flow, concrete examples, and a few “pattern vs pattern” forks that trip people up.
The idea: start with the symptom
When developers pick the wrong pattern, it’s usually because they started with the tool (“Let’s use Factory!”) instead of the pain (“Creation logic is scattered and brittle.”).
So here’s the rule:
Symptom → Constraint → Shape → Pattern
Patterns are just names for recurring shapes.
The decision tree (high-level)

Branch 1: Creation problems (instantiation is the mess)
Symptoms you’ll recognize
- constructors with 8–12 parameters
- new scattered throughout business logic
- if/else ladders choosing classes
- you need different configurations per environment/client/tenant

Concrete example: payments (Factory) vs configuration (Builder)
Factory-ish problem: you choose implementation by input:
- card / UPI / PayPal / crypto
That’s “select a class based on a condition” → Factory Method / Simple Factory.
Builder problem: you assemble a large object with optional parts:
- a HttpClient with retries, timeouts, proxy, TLS settings
That’s “step-by-step assembly” → Builder.
Tiny Java example (Factory-ish) :
interface PaymentProcessor { void pay(long amount); }
class CardPayment implements PaymentProcessor { public void pay(long amount) {} }
class UpiPayment implements PaymentProcessor { public void pay(long amount) {} }
class PaymentFactory {
static PaymentProcessor create(String type) {
return switch (type) {
case "card" -> new CardPayment();
case "upi" -> new UpiPayment();
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}
}Branch 2: Structural problems (composition is the mess)
Symptoms you’ll recognize
- you want to add behavior without changing existing classes
- you need to integrate incompatible interfaces
- you want to wrap something without the caller knowing
- you’re duplicating code across similar object combinations

The big confusion fork: Decorator vs Proxy vs Adapter
Use this mental model:
- Adapter changes the shape (interface) so two things can connect.
- Decorator keeps the same interface but adds responsibilities (logging, caching, metrics).
- Proxy keeps the same interface but controls access (lazy init, permissions, remote calls).
Branch 3: Behavioral problems (flow, communication, state is the mess)
Symptoms you’ll recognize
- huge if/else chains
- logic changes depending on current state
- you need to notify many consumers without tight coupling
- you want to swap algorithms at runtime

Another confusion fork: Strategy vs State
They look similar in UML diagrams. The difference is in who decides:
- Strategy: the caller picks an algorithm (“use pricing strategy A for this request”).
- State: the object transitions itself (“after PAID, next is SHIPPED”).
A “real-world” mini case study (ties it together)
Problem: discount logic is exploding
You start with:
- new users: 10% off
- festival sale: 15% off
- premium customers: 20% off
- coupon codes: variable
Soon you have:
- nested conditionals
- impossible-to-test combinations
- constant regressions
Diagnosis: behavioral variation by business rule → Strategy
Solution sketch
- DiscountStrategy interface
- implementations per rule
- a selector that chooses which strategy applies based on request context
This gives:
- testable units,
- easy rollout of new rules,
- no “edit the god function” every sprint.
A checklist to stop pattern overuse (important for credibility)
Before adding any pattern, ask:
- Is there real pain today? (not hypothetical “future scale”)
- Will a simpler refactor solve it? (extract method, split class, rename, remove duplication)
- Will this make onboarding easier — or harder?
- Can you explain the choice without naming the pattern?
If not, you might be pattern-shopping.
This is how you avoid “pattern cosplay.”
Happy Coding!
Also visit my Gumroad stor for quick cheatsheets and other coding stuff : https://pushpak48.gumroad.com/
✔️ If you like my blog, you can Buy Me a Coffee here. ✔️ Connect with me on Linkedin. ✔️ Press and hold the 👏 button to give up to 50 claps to this article!
Thanks for reading!
Design Patterns Aren’t a Checklist. They’re a Diagnosis. was originally published in Javarevisited on Medium, where people are continuing the conversation by highlighting and responding to this story.
This post first appeared on Read More