Refactoring, as Martin Fowler defines it in the book of the same name, has a specific and narrower meaning than its everyday use suggests: a disciplined technique for restructuring existing code without changing its observable behavior. The emphasis on “without changing behavior” is the entire point — a change that improves the code's structure but also fixes a bug, or adds a feature, isn't a refactor by this definition, it's two different changes bundled together, and bundling them is exactly what makes the combined change harder to review, harder to test, and harder to safely undo if something goes wrong. Further resources on the practice are collected at the Refactoring home page.
Why the narrow definition is the useful part
Keeping refactoring and behavior change strictly separate has a practical payoff that's easy to underestimate: a pure refactor, because it doesn't change behavior, can be verified by the code's existing tests passing unchanged, before and after. A change that mixes restructuring with new functionality can't be verified that cleanly — you're now testing whether the new behavior is correct and whether the restructuring preserved everything else, at the same time, which makes it much harder to isolate the source of a problem if one shows up. The hidden cost of changing mental contexts is explained in https://www.monitask.com/business-glossary/task-switching-cost/.
This is also why the book insists on small, individually verifiable steps rather than large rewrites: each small refactor can be checked against the existing tests immediately, which means a mistake is caught within one small step rather than discovered somewhere in a much larger, harder-to-unwind change.
The catalog, and why it mattered
A large part of the book's lasting contribution is a catalog of named, specific refactoring techniques — Extract Function, Rename Variable, Replace Conditional with Polymorphism, and dozens more — each with a precise definition and a step-by-step mechanic. Before this catalog existed, developers did versions of these transformations constantly, but informally and without a shared name, which made it hard to discuss a proposed change precisely (“I think we should clean this up”) or to trust that a specific transformation was actually safe (does this particular restructuring risk changing behavior, or is it provably safe by construction).
- A true refactor changes structure only — if it also changes behavior, even to fix something, treat it as a separate, clearly labeled change.
- Small, individually testable steps are safer than one large restructuring, even when the large restructuring is well understood — each small step is independently verifiable.
- Refactoring depends on a reasonably trustworthy test suite; refactoring code with poor test coverage is a fundamentally different, riskier activity, closer to the legacy-code problem discussed elsewhere on this shelf.
- Naming a specific refactoring technique (“I'm going to Extract Function here”) communicates the scope and intent of a change far more precisely than a general description of cleaning up.
Where refactoring gets confused with rewriting
The everyday, looser use of “refactor” — to mean any significant restructuring, including ones that touch behavior — has muddied the term's usefulness somewhat, and teams that use it loosely sometimes lose the specific safety guarantee the disciplined version provides. A large rewrite framed informally as “refactoring” carries a very different risk profile than a genuine, behavior-preserving refactor, and conflating the two in planning or in a pull-request description tends to produce mismatched expectations about how carefully the change needs to be reviewed and tested.
Understood this way, refactoring isn't a separate activity you schedule occasionally — it's a disciplined mode you move in and out of constantly while doing other work, made possible specifically by keeping the definition narrow enough to verify.