Shotgun Surgery in Dart: Examples and Fixes

One feature request, five files changed, two bugs introduced. That scatter is the shotgun surgery code smell — here's a Dart example, the three-move fix, and how to keep it out.

Your "clean" architecture with dozens of tiny classes might be the problem. If every small feature request sends you on a treasure hunt across five files — changing the same rule in each one, missing one, shipping a bug — you don&#x27;t have a testing problem. You have the shotgun surgery code smell: one logical change that forces edits scattered across your codebase. Here&#x27;s the shotgun surgery Dart example that makes it concrete, the three-move refactor that kills it, and the decision table for choosing the right move. What you&#x27;ll learn What shotgun surgery is and how to detect it in your own git history A full shotgun surgery Dart example: the smelly before-state and the clean after-state How it differs from its mirror image, divergent change The three-step refactor: Move Method, Extract Class, verify with dart analyze A decision table for picking extract method vs. move method vs. new class Four mistakes teams make when "fixing" the smell (and what to do instead) Shotgun surgery Dart example: one rule, five files A notification system. Every channel — push, email, SMS, in-app, digest — must respect quiet hours. The rule is duplicated in every channel: // ❌ BEFORE: the same rule copy-pasted into five classes (Dart 3.13) class PushService { void send(String userId, String body, UserPrefs prefs) { final now = DateTime.now(); if (prefs.quietStart != null && prefs.quietEnd != null) { final s = prefs.quietStart!; final e = prefs.quietEnd!; if (now.hour >= s && now.hour < e) return; // respect quiet hours } _deliver(userId, body); } } class EmailService { void send(String userId, String body, UserPrefs prefs) { final now = DateTime.now(); if (prefs.quietStart != null && prefs.quietEnd != null) { final s = prefs.quietStart!; final e = prefs.quietEnd!; if (now.hour…

Explore