Incremental refactoring is a series of controlled slices

A legacy module becomes safer through a sequence of small changes with a clear purpose, not through one large rewrite. Each slice has bounded scope, non-goals, a safety net, a focused check, and a clear rollback.

If a bug fix, business-rule change, or new public contract is discovered during structural refactoring, stop and move it into a separate task.

One slice — one structural hypothesis

ElementExample question
GoalWhat one structural problem are we removing?
Allowed scopeWhich files, symbols, or boundaries may be touched?
Non-goalsWhich APIs, dependencies, and business rules are not changing?
Safety netWhich observable behavior do we compare before and after?
Stop conditionWhen must we return to the baseline or request a decision?

Small-step cycle

  1. State the structural goal and list the forbidden changes.
  2. Check usages, tests, the current baseline, and dependency direction.
  3. Make the smallest change that tests the hypothesis.
  4. Run a focused check and compare the observable output.
  5. Read the complete diff and check the list of touched files.
  6. Create a logical checkpoint or commit only after review.
  7. Update the modernization record with the actual result and open risks.

What is not incremental refactoring

  • mass renaming together with a behavior change;
  • updating the framework or dependencies without a separate compatibility plan;
  • moving code across dozens of modules without a safety net;
  • adding a feature while cleaning up a legacy class;
  • weakening a test to hide a regression.

Prompt for a controlled step

Bounded refactoring request
Goal: extract one internal responsibility
Allowed: <one module> and focused tests
Preserve: public API, outputs, errors, side effects
Forbidden: dependency updates, feature work, broad formatting
Check: <focused command>
Stop if: behavior or scope is unclear

Canonical lesson source · JavaRush

Open JavaRush material