Skip to content

Archive

Refactoring

58 articles
Software Engineering 04 Sep 2026 8 min read

Reducing Change Amplification in Code

A requirement can sound small and still produce a surprisingly large patch. Add one order state, rename one business concept, or change one validation rule, and suddenly five modules, several tests, and a serializer all need coordinated edits. The problem is not simply that the codebase is large. It is change amplification: one conceptual change causes many implementation changes because knowledge about that concept is spread across the system. This article shows how to recognize change amplification, trace it back to duplicated knowledge, and reduce it without forcing unrelated code into one giant abstraction.

Software Engineering 04 Sep 2026 9 min read

Keeping Behavior Close to the Data It Needs

A method can belong to one module while doing most of its thinking with data owned by another. When that happens repeatedly, a small change to the data often forces changes in distant code as well. This design smell is commonly called feature envy: behavior appears more interested in another object or module than in the one that contains it. The useful lesson is broader than the name. When a piece of behavior depends heavily on particular data and the rules around that data, keeping them close usually makes those rules easier to find, protect, and change together.

Software Engineering 04 Sep 2026 9 min read

Creating Seams for Safe Legacy Changes

Legacy code is often difficult to change for a reason that has little to do with the business rule you need to edit. The code may create its own database client, read the clock directly, call a remote service in the middle of a calculation, or depend on another component that is expensive or unreliable in tests. You may understand the desired change and still be unable to test it in isolation.

Software Engineering 03 Sep 2026 7 min read

Replacing Legacy Systems with the Strangler Pattern

Replacing a large legacy system in one release is attractive on a diagram and dangerous in practice. The old system usually contains years of behaviour, undocumented edge cases, operational knowledge, and integrations that are difficult to reproduce all at once. The strangler pattern takes a different approach: place a boundary in front of the existing system, move one capability at a time to a new implementation, and gradually reduce the responsibilities of the old system until it can be retired.

Software Engineering 03 Sep 2026 6 min read

Refactoring Legacy Code with Characterization Tests

Legacy code is difficult to change when its important behaviour is poorly understood and existing tests do not provide enough confidence. The risk is not only that a refactoring introduces a bug. The deeper problem is that developers may not know which behaviours are intentional, accidental, or relied on by other parts of the system. Characterization tests help reduce that uncertainty. Instead of beginning with a specification of what the code should do, they capture what the code does today. That behavioural baseline can make structural improvement safer while the team learns the system.

Software Engineering 03 Sep 2026 8 min read

Creating Seams for Safer Code Changes

Some code is difficult to change for reasons that have little to do with the change itself. A function may read the clock directly, create its own network client, access a global configuration object, or write to a file in the middle of business logic. The requested change may be small, but verifying it safely becomes difficult because the code is tightly connected to things that are slow, unpredictable, or hard to reproduce.

Software Engineering 03 Sep 2026 8 min read

Characterization Tests for Safe Legacy Changes

Changing old code is difficult when nobody can say with confidence which behaviours are intentional and which are accidents. Documentation may be incomplete, the original authors may be unavailable, and existing tests may cover only a small part of the system. In that situation, writing tests for the design you wish the code had can be risky. Before improving the design, you first need evidence about what the software actually does today.

Software Engineering 03 Sep 2026 8 min read

Changing Interfaces Safely with Parallel Change

Changing a shared interface often looks simple in the code that owns it. Rename a method, replace a parameter, or return a richer result, then update the callers. The difficulty appears when many callers cannot change at the same moment. They may live in different modules, be maintained by different teams, or be deployed independently. A change that is correct in isolation can then create a period where old callers and new code cannot work together.

Software Engineering 03 Sep 2026 6 min read

Branch by Abstraction for Incremental Change

Large replacements are tempting because they promise a clean boundary between the old design and the new one. In practice, a long-running replacement branch accumulates integration risk while the main codebase continues to change. Branch by abstraction offers a different approach. Instead of separating the work primarily with a source-control branch, engineers introduce an abstraction around the behaviour being replaced. The old implementation remains usable while a new implementation is developed and adopted behind the same boundary.

Software Engineering 02 Sep 2026 5 min read

Parallel Change for Safe Interface Refactoring

Changing a shared interface is risky when many callers depend on it. A large “update everything at once” patch can work in a small codebase, but it becomes harder to review, deploy, and roll back as dependencies spread across modules or services. Parallel change is a refactoring technique that keeps old and new interfaces working side by side for a limited period. The migration happens in three stages: expand, migrate, and contract.