← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Shotgun Surgery

quality Intermediate
debt(d7/e7/b7/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints note automated=no and the code_pattern is 'PR touching 10+ files for a single logical change'. While phpstan is listed, it doesn't directly detect scattered responsibilities — this requires a human reviewer noticing the spread of changes across a PR. It's invisible until someone reviews the diff.

e7 Effort Remediation debt — work required to fix once spotted

Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix says to consolidate scattered responsibility into one place, but the common_mistakes (DTO, validator, controller, view, test; feature toggles in 20 places; ORM+query builder+raw SQL) all describe changes that are inherently spread across many files and architectural layers. Fixing it means identifying the scattered concern and refactoring all call sites to route through a new centralised abstraction.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). The smell applies_to web, cli, and queue-worker contexts, meaning the scattered responsibility affects all work streams. Every future change to that scattered concern pays the same tax — developers must remember all the places to update, which shapes how every change is made. It doesn't quite reach b9 (no full rewrite needed) but it imposes a strong ongoing burden.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly states that developers confuse shotgun surgery with divergent change — treating them as the same smell when they are opposites. This is a well-documented gotcha in refactoring literature that most developers eventually learn, but it's not catastrophic — it's a naming/taxonomy confusion rather than a behaviour-inversion.

About DEBT scoring →

Also Known As

shotgun surgery smell scattered change change fragmentation

TL;DR

A single change requires making many small edits across many different classes — a sign of poor cohesion.

Explanation

Shotgun surgery is the opposite of Divergent Change. It occurs when one logical change (e.g. adding a new payment method) forces you to modify many unrelated classes. This indicates that related responsibility is scattered across the codebase rather than centralised. The refactoring is to Move Method and Move Field to bring scattered behaviour together into a cohesive class. High shotgun surgery cost is a strong signal to refactor before adding new features.

Common Misconception

Shotgun surgery is the same as divergent change. They are opposite smells — divergent change is one class that changes for many reasons; shotgun surgery is one change that forces edits across many classes.

Why It Matters

A change that requires edits in many unrelated classes indicates that a responsibility is scattered — one logical change should ideally touch one place.

Common Mistakes

  • Adding a new field to a data model and having to update DTO, validator, controller, view, and test separately.
  • Not using a single source of truth for configuration that is used in many places.
  • Feature toggles checked in 20 different places — centralise in a FeatureFlag service.
  • Database column renames that cascade through ORM, query builder, and raw SQL across the codebase.

Code Examples

💡 Note
Shotgun surgery is a DRY violation spread across many files. Centralise the concept so changing it touches exactly one place.
✗ Vulnerable
// Changing VAT rate requires editing 8 different files:
// OrderController.php:  \$total * 0.20
// InvoiceService.php:   \$price * 0.20
// ReportGenerator.php:  \$amount * 0.20
// CartCalculator.php:   \$subtotal * 0.20 ...
✓ Fixed
// Single source of truth — one class, one change
class TaxCalculator {
    private const RATE = 0.20;
    public function apply(float \$amount): float { return \$amount * self::RATE; }
}

// All callers use TaxCalculator::apply() — rate change propagates everywhere

Added 15 Mar 2026
Edited 22 Mar 2026
Views 29
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 1 ping W 2 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Amazonbot 7 Perplexity 5 ChatGPT 3 Google 2 Unknown AI 2 SEMrush 2 Ahrefs 1
crawler 19 crawler_json 2 pre-tracking 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: High
⚡ Quick Fix
When one change requires editing many files, the responsibility is scattered — consolidate it into one place that other code calls
📦 Applies To
any web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
PR touching 10+ files for a single logical change; feature flag scattered across multiple classes and config files
Auto-detectable: ✗ No phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: High Context: File Tests: Update

✓ schema.org compliant