Notes on shipping a feature you inherited
What it takes to deliver something you didn't design, in a codebase you didn't build.
The feature already existed. Someone had spent weeks on it, made decisions I wasn't around for, and then moved on, or left, or handed it off, before it was done. My job was to finish it. Not redesign it. Not improve it. Finish it.
That constraint is harder than it sounds. Every instinct you develop as a developer points toward doing things the way you'd have done them. Inherited work fights that instinct at every step.
Find the intention, not just the code
The first thing I learned to do is find the ticket, the design file, the Slack thread, the PR description, whatever exists that explains what the original author was trying to build. Code tells you what was done. It doesn't tell you what was meant. Those are different things, and you need both.
Once I found a half-built onboarding flow with a database migration that had already run in production. The code stopped midway through a wizard. No notes, no comments. What I needed to know was: did they intend to add more steps, or was the wizard always supposed to be this short? Thirty minutes in the commit history answered it. The original plan had seven steps; they'd shipped four. I finished the remaining three.
Separate what's wrong from what's different
Some of what you inherit is genuinely wrong: a bug, a missing edge case, a decision that didn't age well. Some of it is just different from what you'd have chosen. These feel the same when you're reading unfamiliar code, but they aren't. The first deserves a fix. The second doesn't deserve anything. Leave it alone.
I keep a running list while I'm reading: bugs go in one column, style differences go in another. By the time I start writing code, I know which one I'm addressing. If it's a style difference, I write it down and keep moving. Occasionally, if the pattern is worth changing, I raise it separately as a refactor ticket, not a fix buried in the feature work.
Resist the urge to start over
At some point the thought will arrive: it would be faster to rewrite this. It almost never is. The existing code has run. It has survived integration with the rest of the system. It has edge cases baked in that you don't know about yet. A rewrite inherits none of that. You're not starting from zero. You're starting below zero.
The existing code has survived integration with the rest of the system. A rewrite inherits none of that.
There are cases where a rewrite is right. The structure is so tangled that finishing it would create worse debt than starting clean. But that bar should be high, and the decision should be explicit, not something that happens because you were uncomfortable with someone else's naming conventions.
Ship it, then improve it
The goal of inherited work is to finish it. Once it ships, it's yours. After that, if there's a better structure, a cleaner abstraction, a pattern worth introducing, you have standing to propose it. The codebase trusts you now because you delivered something. That's a different conversation than the one you'd have had before the feature was done.
Shipping inherited work taught me more about humility than almost anything else in this job. Someone made tradeoffs under constraints I don't fully understand. The least I can do is finish what they started before I decide I know better.