Java Omnibus
OOAD & Architecture / Anti-patterns / Shotgun Surgery and Divergent Change in Java
Anti-Patterns Java Order Management
Anti-Patterns

When one business change forces many unrelated edits, the design is telling you that boundaries are wrong.

Shotgun surgery and divergent change are two closely related failure patterns. Shotgun surgery means one conceptual change requires edits in many places. Divergent change means one module keeps changing for many unrelated reasons. Together, they reveal a design where coupling is high and cohesion is weak.

Why This Topic Matters

Why it matters in real Java systems

If a new payment rule forces changes in controllers, services, repositories, DTO mappers, and notification code, the design is not merely inconvenient. It is structurally amplifying change. That is one of the clearest signs that responsibility and dependency direction need work.

Core Explanation

The failure shape behind this anti-pattern

These patterns make change expensive in opposite but related ways. Shotgun surgery spreads one concept across too many modules. Divergent change concentrates too many concepts inside one module. Both indicate poor responsibility shaping.

In Java teams, these failures often appear when features grew incrementally without revisiting the model. New code was added where it fit tactically, not where it belonged structurally. Over time, the result is a codebase where change scope is hard to predict.

Refactoring here often involves re-centralizing one business concern into a better boundary while splitting overgrown classes or packages into narrower responsibilities.

Concept Breakdown

Key signals to watch for

Signal

One change, many files

Shotgun surgery signals that the same business concern is spread too widely.

Signal

One module, many reasons to change

Divergent change signals that a class or package has become too broad in responsibility.

Signal

Change pain is a design diagnostic

These anti-patterns are among the clearest indicators that refactoring has real business value.

Java Example

Java example in the Order Management domain

Java example: one policy leaking across several places
package org.javaomnibus.ecommerce.antipatterns;

public final class OrderController {
    public void applyDiscount(CheckoutRequest request) {
        // validates discount input
    }
}

public final class PricingService {
    public Money totalFor(Order order) {
        // applies discount policy
        return Money.zero("USD");
    }
}

public final class NotificationTemplateBuilder {
    public String discountMessage(Order order) {
        // builds text for discount-related emails
        return "discount applied";
    }
}
Code Walkthrough

Step by step

  1. A single pricing-policy change may now require edits in web, pricing, and notification layers.
  2. That is shotgun surgery pressure: one concept spread too broadly.
  3. If PricingService also handles tax, shipping, refunds, and promotions, divergent change pressure is also present.
  4. The design problem is not local syntax. It is boundary placement.
Real-World Usage

Where this shows up in practice

  • Feature work where every change unexpectedly grows in scope
  • Legacy Java systems with weak modular boundaries
  • Teams trying to stabilize release risk by reducing change surface area
Trade-Offs

How to respond proportionately

Sometimes a concern legitimately touches several areas, so not every multi-file change is pathological. The anti-pattern appears when the spread feels accidental and repeatedly unpredictable.

Common Mistakes

Frequent mistakes to watch for

  • Treating symptoms separately instead of tracing the shared concern
  • Splitting files without reducing conceptual spread
  • Ignoring package-level cohesion while fixing individual classes
  • Accepting recurring change amplification as normal project complexity
Related Pages

Related concepts


What To Read Next