Discover Before You Refactor
There is a specific feeling when you open a codebase intending to clean it up. Almost smug. You remember writing this thing, or you've read it before, and you can already see the problems from the doorway. The function names are too long. The abstractions leaked in the wrong direction. There's that one class that does eleven things and everyone's afraid to touch.
You open a file and start typing.
Three hours later you're debugging something that has nothing to do with what you set out to fix. A test is failing that wasn't failing before. Two modules that "obviously" weren't connected are connected in a way that only makes sense if you know the history. And the refactor that was supposed to take the afternoon is now a week-long project with unclear scope.
I've been there more times than I want to admit.
The Problem Isn't the Refactor
The problem isn't that refactoring is hard. The problem is that the thing you were refactoring wasn't the thing you thought it was.
When you dive into a codebase with intent, you're not actually reading it. You're confirming your existing mental model. You see what you expect to see. You skip the comments that seem obvious. You skip the edge-case handling that looks like paranoia. You skim the test file because you already know what the function does.
Then you make a change that invalidates three assumptions you didn't know you were making.
I had a collision like this with a production refactor. I had a clear picture of what needed to change. I'd looked at the code before. I had context. What I didn't have was an accurate map of what was actually there versus what I remembered being there. The two things are not the same. Codebases drift. Requirements drift. The mental model you built six months ago is six months stale.
What I'd skipped was the audit. The actual discovery pass.
Not "reading the code." Discovery. There's a difference.
Reading the code is what you do when you're looking for something specific. Discovery is what you do when you're forcing yourself to be surprised. You read the git log to see what changed and why. You read the build manifest to understand what the project actually depends on. You check whether there are deviation documents, audit findings, anything that captures where the codebase departs from what the README says it does. You trace the actual data flow from input to output, not the flow you think is there.
You write down what you find. Not in your head. On the page. Writing forces honesty in a way that thinking never does.
What Discovery Actually Costs
The discovery pass costs time. That's real. When you're under pressure to ship, sitting down to do a structured audit feels like the wrong direction. The code is right there. The change is obvious. Just do it.
But the cost of skipping discovery isn't zero. It gets paid later, with interest, usually at the worst possible moment. You find out that the "obvious" refactor had a dependency you didn't see. You find out the test suite was testing something slightly different from what you assumed. You find out a pattern you thought was broken was actually working around a constraint in a downstream system.
The discovery pass also changes what you build. This is the part that's hard to internalize until you've felt it a few times.
When you force yourself to map what's actually there before you plan what to change, the plan gets better. Not marginally better. Fundamentally better. You stop solving the problem in your head and start solving the problem in reality. Sometimes the refactor you were planning turns out to be unnecessary. The code was already doing the right thing in a way you hadn't seen. Sometimes the refactor was too small and the real issue was upstream.
Discovery doesn't slow the work down. It redirects energy to the right problem.
I built a discipline around this after the collision I mentioned. A set of questions, answered in writing, before any implementation plan gets drafted. What does the actual directory structure look like? What does git say happened in the last few weeks? What do the project docs say about what's done and what's not? Are there deviation documents, audit findings, decisions that aren't in the code but absolutely constrain it?
The question that stings the most: does the mental model I walked in with actually match the codebase I'm looking at? If there's a gap, surface it before writing a single line of plan. Not after. Before.
The gap always exists. The question is whether you know about it.
The Same Pattern at Every Scale
This isn't a code problem. It's a thinking problem. It shows up everywhere.
When I started building AI systems that could dispatch agents and orchestrate work, the pattern came back immediately. The first instinct was to just build. Spin up a pipeline, wire things together, see what happens.
The better move was discovery first. What capabilities already exist? What's already registered? What's already wired? The answer was almost always: more than I thought. Which meant the work was smaller than I'd planned. Which meant I could ship something real instead of spending three weeks rebuilding something already in the garage.
The discipline translates: audit the existing system before you design the new one. Whether the system is a codebase, an agent fleet, an API surface, or a product strategy. You're always arriving with a model that's partially stale. Discovery is how you refresh it.
The temptation is always the same: skip it because you think you already know. The cost is always the same too.
The Principle
Discover before you refactor. Map before you move. Read before you write.
Not as ceremony, not as overhead. As the actual first step of the work.
The best change I've made to a system was usually the one I didn't make, because I found the existing solution during the audit. The second best was the one I made correctly the first time, because I understood what I was changing before I touched it.
There's a specific feeling when discovery works the way it should. You do the audit, and somewhere in the process you find the thing you would have broken. You find the assumption that was wrong. You find the edge case that would have cost you a week of debugging.
And then you don't break it. Because you found it first.
That's the win. No pull request, no green build, no deploy. Just the quiet satisfaction of knowing exactly what you're dealing with before you deal with it.
That's the discipline. That's what makes the refactor clean.
Top comments (0)