Skip to content

Archive

Code Quality

63 articles
Software Engineering 11 Sep 2026 8 min read

Shotgun Surgery: When One Change Touches Many Files

Shotgun Surgery: When One Change Touches Many Files A pricing rule changes from “free shipping above 50” to “free shipping above 75.” The rule is simple, but the pull request touches checkout, order previews, invoice generation, customer notifications, and tests in several modules. Miss one place and customers see contradictory behavior. That pattern is called shotgun surgery: one logical change requires many small edits scattered through the codebase. The problem isn’t the number of files by itself. The problem is that one decision has many owners.

Software Engineering 11 Sep 2026 10 min read

Shotgun Surgery: Reduce Scattered Change

Shotgun Surgery: Reduce Scattered Change A small requirement can produce a surprisingly large patch. Adding one order state means editing validation, formatting, notification, audit, and reporting code in different modules. Each edit may be simple, yet missing one can leave the system inconsistent. This recurring shape is often called shotgun surgery: one conceptual change forces small edits across many places. The practical problem isn’t the number of files by itself. It is that a single responsibility has been scattered, so developers must reconstruct the full change surface each time that responsibility evolves.

Software Engineering 11 Sep 2026 7 min read

Replacing Type Branches with Polymorphism

Replacing Type Branches with Polymorphism A type-based switch can be perfectly clear. Trouble starts when the same cases appear across several operations. Adding one new type then means editing pricing, validation, formatting, scheduling, and other branches in separate places. The type list has become a change axis, but the code still represents it as scattered conditionals. Replacing type branches with polymorphism moves behavior for each variant behind a shared contract. Callers ask for an operation without selecting the implementation themselves. This can concentrate related rules and reduce repeated branching, but it also introduces more types and indirection. The refactoring pays off only when that trade is useful.

Software Engineering 11 Sep 2026 8 min read

Replace Nested Conditionals with Guard Clauses

Replace Nested Conditionals with Guard Clauses A method often starts simple and becomes deeply nested one condition at a time. A null check wraps a permission check. That wraps a state check. The actual operation ends up several indentation levels from the method boundary, even though it is the main path a reader cares about. Guard clauses handle exceptional or disqualifying conditions near the top of a method and exit immediately. The remaining code can then describe the normal path with less structural noise.

Software Engineering 11 Sep 2026 8 min read

Replace Magic Numbers with Named Domain Values

Replace Magic Numbers with Named Domain Values A condition such as attempts >= 5 is easy to execute and surprisingly hard to review. Why five? Is it a security policy, a technical limit, a temporary experiment, or just an arbitrary value copied from somewhere else? The code contains the number but not the reason it exists. A magic number is a numeric literal whose meaning isn’t clear from its context. Replacing one well means more than moving it into a constant. The goal is to preserve the value’s meaning, units, and ownership so a future change has an obvious place to happen.

Software Engineering 11 Sep 2026 8 min read

Replace Data Clumps with Parameter Objects

Replace Data Clumps with Parameter Objects A method takes startDate, endDate, and timezone. Another method takes the same three values. A third passes them unchanged to a lower layer. Soon, changing what “reporting period” means requires editing signatures across the codebase. This is a common design smell called a data clump: several values repeatedly appear together because they are really parts of one concept, but the code still treats them as unrelated pieces. A parameter object gives that concept a name and a boundary.

Software Engineering 11 Sep 2026 9 min read

Mutation Testing: Check Whether Tests Detect Broken Behavior

Mutation Testing: Check Whether Tests Detect Broken Behavior A test suite can execute every line of a function and still miss a defect. The tests may call the right code but make weak assertions, cover only one outcome, or never check a boundary condition. Mutation testing probes that gap by making small changes to production code and running the relevant tests against each changed version. If a test fails, the change is said to be killed. If all tests still pass, the mutation survives and deserves inspection.

Software Engineering 11 Sep 2026 7 min read

Middle Man Code Smell: Remove Delegation That Adds No Value

Middle Man Code Smell: Remove Delegation That Adds No Value Delegation is useful when one object asks another object to do work. It can separate responsibilities and keep implementation details behind a boundary. But delegation can become ceremony when an object does little more than forward most calls to another object. That situation is commonly called the Middle Man code smell. The extra layer adds names, files, navigation, and maintenance work without owning a meaningful decision.

Software Engineering 11 Sep 2026 9 min read

Feature Envy: Move Behavior Closer to the Data It Uses

Feature Envy: Move Behavior Closer to the Data It Uses A method can live in one class while spending most of its time inspecting another. It asks that other object for several values, combines them according to rules about that object, and perhaps repeats the same pattern elsewhere. The code works, but changing the data often means hunting down behavior in unrelated places. This is the design smell commonly called feature envy. The name matters less than the question behind it: does this behavior belong closer to the data and rules it depends on?

Software Engineering 11 Sep 2026 7 min read

Command-Query Separation for Predictable Methods

Command-Query Separation for Predictable Methods A method named getBalance() looks harmless. A caller expects it to report a value. If calling it also recalculates fees, updates an account, and writes an audit record, that caller has to understand much more than the name suggests. Command-query separation is a design principle for avoiding this kind of surprise. A command asks the system to change state. A query asks for information and does not change observable state. Keeping those responsibilities separate makes call sites easier to reason about and gives method contracts a clearer shape.

Software Engineering 10 Sep 2026 8 min read

Tell, Don’t Ask: Keep Decisions with the Data

Tell, Don’t Ask: Keep Decisions with the Data A checkout service reads an order’s status, total, and payment state, decides whether cancellation is allowed, and then changes the order. Later, a support tool needs the same operation and copies most of that decision logic. The two callers eventually disagree about one rule. Tell, Don’t Ask is a software design principle that helps prevent this kind of drift. Instead of asking an object for internal state so another object can make a decision about it, prefer telling the object what outcome you want and letting the object enforce the rules that belong to its state.

Software Engineering 10 Sep 2026 9 min read

Split Phase Refactoring to Separate Computation Stages

Split Phase Refactoring to Separate Computation Stages A function starts by interpreting input, then gradually accumulates validation, business rules, formatting, and output logic. None of those steps is necessarily complicated. The difficulty comes from having them interleaved: changing how input is interpreted can unexpectedly affect code that should only care about the interpreted result. Split Phase is a refactoring that separates one computation into distinct stages. The first phase produces an explicit intermediate result. The next phase consumes that result without needing to know how it was produced.

Software Engineering 10 Sep 2026 10 min read

Specification Pattern for Composable Business Rules

Specification Pattern for Composable Business Rules Business rules often begin as a few readable conditions. Then the same decisions appear in validation, eligibility checks, filtering, and workflow code. Small differences creep in: one path checks account status but forgets the credit limit; another copies the whole expression and changes only one threshold. The Specification pattern gives a business rule a name and an interface, then allows rules to be combined into larger decisions. It is useful when the same rule matters in several places or when complex policy is easier to understand as a composition of smaller concepts.

Software Engineering 10 Sep 2026 8 min read

Replace Conditional with Polymorphism When Branches Represent Types

Replace Conditional with Polymorphism When Branches Represent Types A conditional isn’t a design problem just because it has several branches. Sometimes if or switch is the clearest way to express a decision. Trouble starts when the same type-based decision appears in several places and every new variant requires editing all of them. Replace Conditional with Polymorphism is a refactoring that moves variant-specific behavior behind a common operation. Instead of asking an object what kind it is and then deciding what to do, callers ask it to perform the behavior directly.

Software Engineering 10 Sep 2026 10 min read

Design by Contract: Make Assumptions Explicit

Design by Contract: Make Assumptions Explicit A function often depends on rules that its type signature doesn’t fully express. A withdrawal amount must be positive. A completed operation must leave the balance consistent. An object may require its reserved quantity to stay between zero and the quantity on hand. When those rules live only in developers’ heads, failures appear far from their cause. Design by Contract gives the rules names and assigns responsibility for them. The useful mental model is simple: the caller promises to meet the operation’s entry conditions, and the operation promises a valid result while preserving the object’s valid state.

Software Engineering 10 Sep 2026 8 min read

Composed Method: Keep Code at One Level of Abstraction

Composed Method: Keep Code at One Level of Abstraction A method can be only thirty lines long and still be difficult to read. The problem often isn’t its length. It is that the method keeps changing altitude: one line describes a business step, the next manipulates a collection, then another formats a storage key, and then the code returns to business logic. The Composed Method pattern addresses that problem by making a method read as a sequence of operations at roughly the same level of abstraction. The top-level method explains what happens. Smaller methods hold the details of how each step happens.

Software Engineering 09 Sep 2026 8 min read

Reducing Positional Coupling in Function Calls

A function can be perfectly implemented and still be easy to call incorrectly. One common cause is a parameter list in which several values have the same representation and their meaning depends mainly on position. Consider a reporting function that accepts three dates. At the call site, the values may all look valid even when two of them are accidentally reversed. A compiler or type checker often cannot help because each argument still has the expected type.

Software Engineering 09 Sep 2026 8 min read

Reducing Coupling with the Law of Demeter

A small change to one class should not routinely force edits in code that is several objects away. Yet this happens when callers navigate through collaborators to reach deeper objects: order.customer().address().country().code() The line is compact, but the caller now knows that an order exposes a customer, a customer exposes an address, an address exposes a country, and a country exposes a code. If that structure changes, the caller may need to change even when the business question it asks stays the same.

Software Engineering 09 Sep 2026 8 min read

Null Object Pattern for Optional Behavior

Optional behavior often begins with one harmless-looking condition. A component may send notifications only when a notifier is configured, record metrics only when metrics are enabled, or write audit events only in some deployments. As the code grows, the same absence check can spread across many call sites: if notifier != null: notifier.send(message) The condition is simple, but repetition creates a maintenance problem. Every caller must remember that the collaborator may be absent and must know what absence means.

Software Engineering 09 Sep 2026 9 min read

Moving Behavior Toward the Data It Uses

A method can live in one class while doing most of its work with another class’s data. At first this may seem harmless: the code runs, the names are clear, and the calculation is short. Over time, however, the method often becomes a second place that knows how the other object works. That design smell is commonly called feature envy. A piece of behavior appears to “envy” another object’s features because it reads that object’s state or calls its methods much more than it uses its own.

Software Engineering 09 Sep 2026 9 min read

Decision Tables for Complex Business Rules

Business rules often start as a few harmless conditions. Then another exception arrives, followed by a special customer type, a threshold, and a fallback. The code still runs, but reviewing it becomes difficult because the real question is no longer “what does this if statement do?” It is “have we handled every meaningful combination of conditions, and do any rules disagree?” A decision table makes those combinations explicit before they are buried in branching code. It lists the conditions that matter, the relevant combinations of those conditions, and the outcome for each combination.

Software Engineering 09 Sep 2026 9 min read

Command-Query Separation for Predictable Operations

A method that returns a value can look harmless even when calling it changes the system. That mismatch creates bugs that are difficult to see at the call site: remaining = cart.removeItem(itemId) Does removeItem only remove the item? Does it return the removed item, the number of remaining items, or a success flag? More importantly, can code safely call it while merely trying to inspect the cart?

Software Engineering 08 Sep 2026 9 min read

Testing Test Suites with Mutation Testing

A test suite can execute every line of an important function and still fail to notice that the function is wrong. Coverage tells you which code ran during tests. It does not tell you whether the tests would detect a meaningful mistake in that code. Mutation testing examines that missing question. A mutation testing tool makes small changes to production code, one change at a time, and runs the relevant tests. If the tests fail, they detected the changed behavior. If they still pass, the altered code has exposed something worth investigating.

Software Engineering 08 Sep 2026 7 min read

Testing Complex Rules with Decision Tables

A rule can be easy to understand one condition at a time and still be difficult to test correctly when several conditions interact. Consider a refund policy. A refund depends on whether the order is within 30 days, whether the item is damaged, and whether it was marked final sale. Writing a few examples from memory can miss an important combination. Adding every possible combination can create noisy tests that repeat the same reasoning.