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

Divergent Change

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

Closest to 'only careful code review or runtime testing' (d7). While detection_hints lists phpstan and phpmd, automated detection is explicitly marked 'no' — these tools can flag class complexity metrics but cannot reliably detect that a class changes for multiple unrelated reasons. Identifying divergent change requires understanding the semantic reasons behind changes over time, which demands careful code review or historical analysis of commit patterns.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix indicates splitting a class along responsibility lines, which means extracting new classes, creating interfaces, updating all call sites within the component, and adjusting dependency injection. This is a significant refactor but typically contained to one area of the codebase rather than cross-cutting.

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

Closest to 'strong gravitational pull' (b7). Divergent change applies across all contexts (web, cli, queue-worker) and represents an SRP violation that shapes how changes propagate through the system. Once a class becomes a multi-responsibility hub, every feature touching any of those concerns must navigate that class. The common_mistakes note that it's 'easier to split early than after it is heavily depended upon' — the burden compounds as the class accumulates dependencies.

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

Closest to 'notable trap' (t5). The misconception clearly states that developers confuse divergent change with shotgun surgery, thinking they are opposites when they are actually complementary smells. This is a documented gotcha that most developers eventually learn through experience with refactoring literature, but the initial confusion about the direction of the smell (one class, many reasons vs. one change, many classes) catches intermediate developers.

About DEBT scoring →

Also Known As

divergent change smell multiple change reasons

TL;DR

One class changes for many different reasons — a sign it has too many responsibilities.

Explanation

Divergent change is the opposite of Shotgun Surgery. It occurs when a single class is modified every time a different kind of change is needed — you change it for database updates, then again for UI changes, then again for business rule changes. This indicates the class handles multiple distinct concerns and should be split. It is a violation of the Single Responsibility Principle. The refactoring is to extract the different concerns into separate classes.

Common Misconception

Divergent change and shotgun surgery are opposites. They are complementary smells — divergent change is when one class changes for many different reasons (violates SRP); shotgun surgery is when one change requires edits across many classes.

Why It Matters

When a single class must be modified for multiple unrelated reasons, it has too many responsibilities — every change carries the risk of breaking unrelated behaviour in the same class.

Common Mistakes

  • A service class that handles both business logic and persistence — database changes and rule changes both require editing it.
  • Controllers that contain validation, business logic, and response formatting — three reasons to change.
  • Not applying Single Responsibility Principle when a class first becomes large — easier to split early than after it is heavily depended upon.
  • Treating divergent change as an acceptable trade-off — it always increases defect rates over time.

Code Examples

✗ Vulnerable
// One class changes for multiple unrelated reasons
class OrderProcessor {
    public function processPayment(): void {}  // changes with payment logic
    public function sendConfirmation(): void {} // changes with email templates
    public function saveToDatabase(): void {}   // changes with DB schema
}
✓ Fixed
// Split by reason to change (Single Responsibility)
class PaymentProcessor  { public function charge(Order \$o): void {} }
class OrderMailer        { public function sendConfirmation(Order \$o): void {} }
class OrderRepository    { public function save(Order \$o): void {} }

class OrderService { // thin orchestrator
    public function place(Order \$o): void {
        \$this->payment->charge(\$o);
        \$this->repository->save(\$o);
        \$this->mailer->sendConfirmation(\$o);
    }
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 35
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 1 ping T 1 ping W 0 pings T 1 ping F
No pings yesterday
Ahrefs 8 Amazonbot 7 Perplexity 4 Google 3 Unknown AI 3 SEMrush 2 Majestic 1 ChatGPT 1 Bing 1
crawler 28 crawler_json 1 pre-tracking 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
If changing one concern (e.g. pricing logic) requires touching a class, but changing another concern (e.g. storage) also requires touching the same class — split it along those responsibility lines
📦 Applies To
any web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Class that changes both when business rules change AND when database schema changes AND when UI requirements change
Auto-detectable: ✗ No phpstan phpmd
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: High ✗ Manual fix Fix: High Context: Class Tests: Update

✓ schema.org compliant