Skip to content
Sponsor: NimblePros Software architecture, modernization, and training for high-performing development teams. Work with us.
Speculative Generality Code Smell

Speculative Generality Code Smell

Speculative Generality is the code smell of building abstractions, hooks, or flexibility into code for hypothetical future requirements that may never materialize. It is the technical expression of “just in case”: an extra parameter added to support a use case that is not yet needed, an interface created for a single implementation in case a second one is added someday, a configuration option introduced to allow behavior that is never actually varied. The code is more complex than the current problem requires, and that complexity serves no one today.

The You Aren’t Gonna Need It (YAGNI) principle is the direct remedy: do not add capability until there is a concrete, immediate need for it. Speculative code has a real cost — it must be read, understood, tested, and maintained — while its speculative benefit is uncertain and often never realized.

Common Manifestations

Unused Abstract Classes and Interfaces

An abstract base class or interface with only one concrete implementation — where no second implementation is planned or in progress — is often speculative. The abstraction adds a layer of indirection without supporting any actual variation.

public interface IGreetingFormatter
{
    string Format(string name);
}

// The only implementation, and no others exist or are planned
public class FormalGreetingFormatter : IGreetingFormatter
{
    public string Format(string name) => $"Good day, {name}.";
}

If there is no concrete reason to have multiple formatters, the interface is speculative. A plain class suffices.

Parameters That Are Never Varied

A method takes a parameter that is always passed the same value, or a flag parameter that is always true or always false. The parameter exists to support a variation that never occurs:

// includeArchived is always passed as false in every call site
public IEnumerable<Order> GetOrders(bool includeArchived)
{
    return includeArchived
        ? _repository.GetAll()
        : _repository.GetAll().Where(o => !o.IsArchived);
}

If includeArchived is never passed as true, the parameter and the archived-retrieval path are speculative generality.

Hooks and Extension Points Built for Unknown Future Use

Framework code, base classes, or service layers that expose overridable methods, plugin hooks, or configurable behaviors for reasons that are not yet needed. Each extension point is a contract that must be maintained even if it is never used.

Overly Generic Names and Structures

Names like Manager, Processor, Handler, Helper, or Utility are sometimes signals of speculative generality — structures built to absorb any future requirement rather than to solve a specific, named problem.

Dead Code from Abandoned Speculation

Speculative features that were never used and never removed become dead code. The speculation not only added complexity during development but continues to add cognitive overhead indefinitely.

Why Speculative Generality Is Tempting

Developers naturally want to build flexible, resilient systems that can accommodate change. This instinct is good, but premature flexibility can backfire:

  • The speculation is often wrong. The future requirement imagined is not the one that actually arrives. The generalization built for it does not fit the real need.
  • The added complexity makes the real change harder. Layers of abstraction built for the wrong future add friction when the actual future arrives.
  • It violates YAGNI and KISS. Code should be as simple as it can be while satisfying current requirements. Complexity introduced now for future benefit is a bet that may not pay off.

Addressing Speculative Generality

  • Remove unused parameters. If a parameter is always passed the same value, eliminate it and simplify the method.
  • Collapse single-implementation abstractions. If an interface or abstract class has only one implementation and no concrete plan for a second, replace it with the concrete class directly.
  • Delete unused hooks and extension points. If an overridable method, event, or plugin point has no current use, remove it. It can be added when the need arises, and the design will probably be better for it.
  • Inline the abstraction. The Inline Class and Inline Method refactorings are the primary tools for reversing speculative generality.

The right approach is to build for today’s needs with code that is easy to change, rather than building for imagined future needs with code that is complex today.

References