10 Decide when to reuse refactor or replace

Existing code deserves neither automatic preservation nor automatic suspicion. It represents working behavior, accumulated knowledge, and compatibility obligations. It may also encode assumptions that no longer fit the product.

The decision to reuse, refactor, or replace should begin with a present problem. What behavior is missing? What change is unusually difficult? What failure recurs? What information must a developer understand that should have remained irrelevant?

Without that problem, a redesign can become a preference disguised as necessity.

Reuse requires semantic fit

Two functions with similar names are not necessarily interchangeable. Reusing an API because it already exists can preserve the wrong readiness boundary or ownership rule. Conversely, writing a replacement because the old code is unfamiliar can discard a correct solution without evidence.

For acquisition, inspect whether the existing operation preserves the old version during preparation and what its success response means. If it already satisfies the needed behavior, reuse may be the best outcome. If it almost fits, identify the missing promise before introducing another subsystem.

Reuse also has an integration cost. Adapters, configuration, error translation, and lifecycle alignment can make a nominally free component expensive. Include those costs in the comparison rather than measuring only the implementation you avoid writing.

Refactor to improve a named property

A refactor should make a relevant change easier to understand or safer to perform while preserving required behavior. State that property before editing.

“Separate provider transfer from publication so a new provider cannot alter readiness semantics” is a testable rationale. “Clean up the architecture” is too broad to guide a review. It does not tell a reviewer which complexity should disappear or what must remain stable.

Research at Microsoft combined engineer reports with repository analysis and found varied benefits and costs around refactoring. A later study across 103 Java projects showed how mixed changes and attribution methods complicate claims about refactoring-related bugs. These studies do not make refactoring either harmless or inherently dangerous. They support evaluating the specific objective and separating observed association from cause. Refactoring Challenges and Benefits; Differentiated Replication.

For a practical change, preserve characterization tests where they capture important behavior. Add a test for the actual property the refactor is meant to improve when that property is executable. Some benefits, such as comprehensibility, need a different assessment: a reviewer should be able to explain the new ownership path without reconstructing unrelated decisions.

Replacement needs a migration story

A replacement can be justified when the old model makes the required behavior persistently awkward or unsafe. It still needs a path from current use to the new design.

Who depends on the old contract? Can both versions coexist? How is stored data migrated? What makes rollback possible? Which compatibility behavior is genuinely required, and which exists only because nobody has removed it?

A clean new component with an unexplained transition is not a complete solution. Migration can dominate the work. It can also reveal that a smaller refactor would have been sufficient.

Avoid preserving compatibility indefinitely by default. Compatibility itself is code, testing, and conceptual overhead. But removing it should be an explicit decision grounded in actual consumers, rather than an assumption that no one needs it.

Defer a capability without abandoning changeability

Fowler's account of YAGNI distinguishes speculative features from practices that keep software changeable, such as refactoring and testing. His design-stamina discussion is explicitly a hypothesis, not a measured curve guaranteeing a payoff after a particular number of weeks. These distinctions help keep both extremes in view: premature infrastructure and avoidable decay. Yagni; Design Stamina Hypothesis.

For our example, resumable transfer may be a speculative capability. A clear publication boundary may already be necessary for today's cancellation behavior. Deferring the former does not justify entangling the latter.

Make the decision reviewable

Write a short note containing the current problem, evidence that the problem exists, the selected change, the behavior that must remain stable, migration costs, and the result that would show the work was worthwhile.

Then include one credible alternative. It may be reuse with a small adapter, a narrower refactor, or deliberate deferral. If you cannot explain why that alternative is insufficient, the replacement proposal may not yet be ready.

After the work, revisit the original claim. Did the change actually reduce the coupling, failure rate, or maintenance burden it targeted? If the answer is unknown, say so. A successful merge proves that a change entered the repository, not that its architectural promise was fulfilled.

Search the book

Type to find a passage.