Refactoring preserves external behavior
Refactoring changes the internal organization of code, but not its external contract. Its goals are readability, less duplication, clear responsibilities, easier testing, and lower coupling.
If the result for a client changes, this is already a bugfix, feature, migration, or another type of work. Do not disguise such a change as structural cleanup.
| Observable behavior | Examples |
|---|---|
| API | HTTP status, JSON, headers, and public signatures |
| Messages | Error codes and texts |
| State | Database records, events, emails, and webhooks |
| Order | Sequence of business checks and side effects |
Invariants: what must remain unchanged
- the same relevant inputs and outputs;
- the same exceptions and messages;
- the same order of checks when it affects the result;
- the same records, events, and side effects;
- no changes to the public API.
Safe candidates and dangerous substitutions
| Can be refactoring | Is not pure refactoring |
|---|---|
| Extract validation into a private helper | Fix an incorrect result |
| Rename a private method | Add a new user scenario |
| Simplify conditions without changing logic | Update a framework or dependencies |
| Remove duplication | Rewrite the whole module or change the API |
Ask Claude for analysis first
- Find calls, tests, dependencies, and module boundaries.
- Ask Claude to name local structural candidates.
- For each candidate, require the benefit, risk, and affected files.
- Choose one structural problem.
- Record the goal, non-goals, and invariants.
- Only then delegate one small step.
The one-refactoring-PR rule
One PR should solve one structural problem. Its description should explain what changed, what was deliberately not changed, and how preservation of behavior is checked.
If the goal cannot be stated without adding “and one more thing,” the changes most likely need to be split.
Canonical lesson source · JavaRush
Open JavaRush material