Speculative Generality
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5), because detection_hints.tools lists phpstan, phpmd, and php-dead-code — specialist static analysis tools that can flag abstract classes with single implementations, unused parameters, and dead code patterns. Standard linters won't catch these automatically; targeted SAST/dead-code tools are required.
Closest to 'simple parameterised fix' (e3), grounded in quick_fix which says to delete the abstract base class, plugin system, or unused configuration. Each removal is typically a focused deletion within one component — not a one-liner but also not cross-cutting; removing a speculative abstraction touches one area and its callsites but doesn't require sweeping multi-file architectural changes.
Closest to 'persistent productivity tax' (b5), because speculative abstractions apply across web, cli, and queue-worker contexts and impose ongoing cognitive overhead on every maintainer who encounters them. They slow down multiple work streams by forcing devs to navigate layers of indirection with no current value, but they don't fully define the system's shape — removal is painful but feasible without rewriting everything.
Closest to 'serious trap' (t7), grounded in the misconception field: 'Adding flexibility upfront saves refactoring time later.' This directly contradicts the well-established YAGNI principle and the real-world observation that speculative abstractions almost always need redesign anyway when real requirements arrive. A competent developer may sincerely believe they are doing the right thing by future-proofing, making this a habitual and hard-to-unlearn mistake that contradicts sound software engineering practice.
Also Known As
TL;DR
Explanation
Speculative generality is the code smell version of over-engineering: creating abstract classes with one subclass, adding unused parameters for flexibility, or writing hook points for requirements that don't exist yet. It violates YAGNI and adds cognitive overhead for every reader. Remove unused abstractions, parameters, and delegate methods that serve no current purpose — refactoring tools make re-introducing them straightforward if the need genuinely arises.
Common Misconception
Why It Matters
Common Mistakes
- Adding abstract base classes 'in case we need another implementation later' with only one concrete class.
- Plugin architectures for internal code that never needs to be pluggable.
- Configuration files for behaviour that has never varied and shows no sign of varying.
- Not applying YAGNI — the cost of adding abstraction when needed is almost always less than carrying premature abstraction.
Code Examples
// Built for flexibility nobody asked for yet
interface AbstractDataProviderFactoryInterface {
public function create(string $type): DataProviderInterface;
}
class ConcreteDataProviderFactory implements AbstractDataProviderFactoryInterface {
public function create(string $type): DataProviderInterface {
return new DatabaseDataProvider(); // only one type ever exists
}
}
// YAGNI — build what's needed today
class DatabaseDataProvider {
public function getData(): array { return $this->db->fetchAll(); }
}
// Add the interface/factory if and when a second provider materialises
Tags
Edits history 1 edit
- refs PF Media Bot Claude Opus 4.5 · 28 Apr 2026