Do not derive a full rewrite directly from "the architecture looks messy." Use experiments to locate complexity that yields no benefit while preserving valuable safeguards.
Establish the baseline and comparison metrics
Clearly write down the original user tasks, behavioral/data contracts, and failure protections that must be preserved. Choose the normal paths of representative tasks and the related error, recovery, boundary, or low-frequency scenarios.
Collect baselines relevant to the current problem: latency, model calls/cost, memory, dependencies, duplicate implementation maintenance points, failures, or false blocks. Lines of code are used to find candidates, not as a quality metric on their own.
Change only one mechanism at a time
Prioritize checking: multiple implementations of the same rule, multiple sources of the same data, expensive review layers, retained legacy entry points, configuration with no actual consumers, repeatedly converted data, and dependency relationships that prevent real tests from covering things.
In an isolated working copy or a rollback-capable experiment, remove, bypass, or replace one mechanism at a time, and rerun the same inputs and acceptance. Record what the experiment changed, which results held, which results degraded, and how costs changed. When the task only requires evaluation, the experiment must not turn into a permanent refactor of the production source code.
Safety, data integrity, and recovery guardrails must have corresponding failure or attack scenarios. No difference on normal samples is not enough to prove these guardrails are redundant. Mark candidates whose benefits are hard to observe as pending verification; do not delete them based on their names.
Decide what to keep based on the results
| Candidate mechanism | Original responsibility | Experimental change | Behavioral evidence | Cost change | Conclusion |
|---|
Conclusions can be keep, delete, merge, replace, or pending verification. Before merging duplicate implementations, check platform, interface, and behavioral differences; unifying code cannot be achieved by deleting necessary differences.
Choose the smallest combination that can solve the current user problem, and explain the benefits and migration costs. Do not include every existing problem, and do not introduce a new framework that is not needed just to reduce the current number of layers.
Implementation and retirement
When the task has already authorized implementation, continue and complete the concrete changes; when it only requires analysis, deliver a reviewable plan. When changes touch production data/migrations or external operations, follow the current authorization; do not derive extra permissions from "refactoring."
After the new path is launched, check old functions, routing/registration, configuration, tests, dependencies, and public documentation. Retire old paths with no consumers based on evidence; when compatibility entry points are temporarily retained, explain their responsibility and verifiable retirement conditions to avoid permanent dual tracks.
Rerun baseline tasks and related regressions, and measure actual costs. Necessary invariants are protected by regression tests; do not write tests that only match directory structure, naming, or implementation text.
Delivery
Explain what user outcomes were preserved, what the experiments support deleting, how costs changed, and whether old paths were retired. Do not write unmeasured benefits as numbers. One successful task cannot claim that all historical behavior is equivalent; clearly list uncovered platforms and low-frequency conditions.
When there are many candidates or multiple rounds of experiments are needed, you can use Ablation Experiment Record to save baselines, single-variable changes, trade-offs, and retirement evidence.