Skip to main content

Pattern Misuse and Cargo-Culting Warning Signs

What This Concept Is

Cargo-culting is applying a pattern because it has a name, not because it is solving a real force. The code keeps the ceremony of the pattern (interfaces, factories, layers of wrappers) without the payoff (flexibility, testability, evolvability).

Famous warning signs:

  • a Factory class with one product and one concrete factory
  • a Singleton used for things that are actually per-request or per-test scoped
  • an AbstractBaseThing with exactly one subclass
  • Decorator chains three layers deep with no runtime configuration choice
  • an "Adapter" that also imports the domain model and orchestrates it
  • an interface named IFoo that has exactly one implementation called Foo
  • a language-specific nouning smell where every verb becomes a class: OrderCreator, OrderValidator, OrderSaver with no state each -- Steve Yegge's Execution in the Kingdom of Nouns.

Why It Matters Here

Cargo-culted patterns are expensive. They add files, indirection, and cognitive load without absorbing any real change. They also signal to readers that the team is confused, which makes future change harder.

The real design skill is not "I can apply Decorator"; it is "I can tell when adding Decorator will cost more than it saves, and I can argue the position out loud."

Concrete Example

Before (cargo-culted):

public interface UserService { User findById(long id); }
public class UserServiceImpl implements UserService { ... }
public interface UserServiceFactory { UserService create(); }
public class UserServiceFactoryImpl implements UserServiceFactory {
public UserService create() { return new UserServiceImpl(); }
}
// called once, from one place, returning a type with one implementation.

After (real design):

public final class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) { this.repo = repo; }
public User findById(long id) { return repo.find(id); }
}

The "after" is three files smaller, depends only on a real seam (the repository), and loses nothing. Tests and alternate implementations come through the composition root, not through an unused factory.

Diagnostic questions when you find a pattern in review:

  • Does removing this pattern force a change anywhere else?
  • Does this pattern have more than one concrete implementation that is exercised?
  • Does the name of the pattern match the intent in the surrounding code, or was it chosen by shape?

Common Confusion / Misconception

  • "We might need it later" is not a justification. Needed later is a forecasting claim; patterns should respond to present pressure or genuine near-term variation.
  • "Everyone uses this pattern at this layer" is a tradition, not a design reason. Traditions are sometimes right, but only for reasons you should be able to state.
  • An interface for every class is not abstraction; it is duplication in a hat. Add an interface when you have -- or will imminently have -- a second implementation or a test double that matters.
  • Removing a pattern is a valid refactor. "Inline Factory," "Inline Interface," and "Replace Subclass with Delegate" are legitimate moves.

How To Use It

A practical smell-check routine:

  1. For every interface in a PR, ask: how many non-test implementations? If one, demand a reason.
  2. For every Factory, ask: what is the second product, and does it exist in this repo yet?
  3. For every Singleton, ask: is "exactly one" an invariant, or just a convenience? If convenience, replace with DI.
  4. For every Decorator chain, ask: does anyone actually turn these layers on and off, or are they always composed the same way?
  5. For every "Manager", "Handler", or "Processor" with no state, ask: could this be a function?

Check Yourself

  1. What is the minimum evidence that a Factory is earning its keep?
  2. Why does "we might need it later" usually fail as a defense in code review?
  3. What is the difference between pattern fluency and pattern fetish?

Mini Drill or Application

Find a real codebase you have access to (yours or open source). Do three things:

  1. Identify at least two cargo-culted patterns using the checklist above.
  2. Sketch the deletion/inline refactor in pseudocode.
  3. Write a short review-style comment you would leave on the PR that introduced them. Keep it non-mocking: name the force the pattern is not absorbing, not the author.

Read This Only If Stuck