Dead Code Detection
debt(d5/e3/b3/t5)
Closest to 'specialist tool catches it' (d5). Dead code is detected by specialist static analysis tools like PHPStan, Psalm, and Rector as listed in detection_hints.tools. IDEs catch some cases but miss dynamic dispatch and reflection-based calls. These are not default linters but dedicated analysis tools requiring configuration at higher levels.
Closest to 'simple parameterised fix' (e3). The quick_fix indicates running PHPStan level 6+ with dead code extension, and Rector can auto-remove many flagged items. However, common_mistakes notes the need to verify reflection/dynamic calls before deletion, making it slightly more than a one-line patch but still a localized fix within specific files.
Closest to 'localised tax' (b3). Dead code detection is a tooling choice that applies across web/cli/queue-worker contexts per applies_to, but it's a quality practice that doesn't fundamentally shape the codebase architecture. Not running detection accumulates technical debt locally in affected components but doesn't impose system-wide structural constraints.
Closest to 'notable trap' (t5). The misconception explicitly states that developers wrongly believe IDE warnings are sufficient to catch all dead code, when in fact IDEs miss code behind dynamic dispatch, reflection, and configuration-based exclusions. This is a documented gotcha that most developers eventually learn through experience with large codebases.
Also Known As
TL;DR
Explanation
Dead code accumulates over time: unreachable branches after a return or throw, conditions that are always true or false due to type constraints, methods never called from anywhere, and variables written but never read. It increases cognitive load (readers wonder why it exists), masks real logic, and can hide bugs (the 'dead' branch may have been reached and mattered at some point). Detection tools: PHPStan level 4+ reports always-true/false conditions and unreachable code; Psalm's dead-code analysis finds uncalled methods with --find-dead-code; code coverage reports (Xdebug + PHPUnit) highlight never-executed lines. Remove dead code promptly — git history preserves it if needed. Never comment it out: commented code is worse than deleted code.
Common Misconception
Why It Matters
Common Mistakes
- Relying solely on manual code review to find dead code in large codebases — it scales poorly.
- Not running PHPStan or Psalm at maximum level — lower levels skip unused method and property checks.
- Deleting flagged dead code without checking if it's used via Reflection or dynamic calls like call_user_func.
- Treating dead code warnings as low priority — they often indicate structural problems worth investigating.
Code Examples
class UserService {
public function getUser(int $id): User { /* used */ }
public function legacyFetch(int $id): User { /* never called — dead */ }
private function internalHelper(): void { /* unreferenced — dead */ }
}
// PHP tools that find dead code:
// PHPStan — flags unreachable code and unused methods:
$ vendor/bin/phpstan analyse --level=6 src/
// 'Call to an always-true condition'
// 'Return type is never used'
// Psalm — taint analysis also finds dead paths:
$ vendor/bin/psalm --find-dead-code
// php-dead-code-detector:
$ composer require --dev povils/phpmnd
// IDE support:
// PhpStorm highlights unreachable code, unused private methods in grey
// Git approach — blame + coverage:
// Lines never covered by tests AND never changed in 12 months = candidates
$ vendor/bin/phpunit --coverage-text | grep -v ' 100%'
// Remove dead code — don't comment it out; version control preserves history