How to refactor code you didn't write without breaking it
refactoring vs rewriting, small safe steps, test-first refactoring, rename โ extract โ move sequence, preserving behavior, when not to refactor
Refactor Means Change Structure, Not Behavior
Refactoring has a strict definition: restructure code without changing observable behavior. If you change behavior while refactoring, you've introduced a bug and a refactor simultaneously โ and you won't know which caused a failure.
The Safe Refactoring Sequence
// Step 1: Ensure tests exist BEFORE touching anything
// If no tests exist, write characterization tests first
// Step 2: Rename โ safest change with IDE refactoring tools
// Before: function usr_val(u) { ... }
// After: function validateUser(user) { ... }
// Use IDE rename, not find-replace โ it handles imports automatically
// Step 3: Extract โ pull a block into a named function
// Before: long function doing 5 things
function processUser(data) {
// 20 lines of validation...
// 15 lines of normalization...
// 10 lines of persistence...
}
// After: extracted functions with clear names
function processUser(data) {
const validated = validateUserData(data);
const normalized = normalizeUserFields(validated);
return persistUser(normalized);
}When Not to Refactor
Don't refactor code you don't understand yet โ you'll break behavior you didn't know existed. Don't refactor across a large blast radius in one PR โ break it into multiple small PRs. Don't refactor to your personal style preference if the team hasn't agreed on the change. The question is always: does this make the code more correct, readable, or maintainable for the team โ or just more comfortable for me?
