Code Triage: A No-Nonsense Framework for Deciding What to Fix, What to Leave, and What to Nuke
Every developer has been there. You open a file you haven't touched in six months, squint at what you wrote, and feel a complicated cocktail of shame, curiosity, and the urge to immediately rewrite everything from scratch. But here's the thing — that instinct will absolutely wreck your sprint velocity if you let it run unchecked.
Refactoring is one of those topics where everyone agrees it matters and almost nobody agrees on when to do it. Some teams treat it like a sacred ritual. Others defer it until the codebase looks like a haunted house. Most land somewhere in the messy middle, which is honestly where the real decisions get made.
Let's talk about how to actually triage your code — not with some abstract philosophy, but with a practical lens you can apply the next time you're staring down a function that makes you uncomfortable.
The Core Problem: Refactoring Has an Opportunity Cost
Before we get into the framework, it's worth naming the thing most teams avoid saying out loud: refactoring isn't free. Every hour you spend cleaning up code is an hour you're not shipping features, fixing bugs, or building the thing your users actually care about.
That's not an argument against refactoring. It's an argument for being deliberate about it. The teams that ship well aren't the ones who never refactor — they're the ones who know which code deserves the effort and which code is fine to leave alone.
The goal isn't a perfect codebase. The goal is a codebase that doesn't slow you down.
Zone 1: The "Touch It and Fix It" Rule
The most practical refactoring philosophy I've seen working teams adopt is dead simple: if you're already in a file making a change, clean up what you notice while you're there. Don't schedule a separate ticket. Don't block the PR. Just tidy as you go.
This approach works because it keeps refactoring connected to actual work. You're not cleaning code in the abstract — you're improving the specific area you're actively navigating. That context matters. You understand why something is structured the way it is, which makes you better equipped to improve it without breaking something downstream.
The limit here is scope. If the cleanup you're eyeing would take longer than the original task, that's a signal to pause and evaluate rather than dive in.
Zone 2: The "Leave It Alone" Assessment
Here's the question most developers forget to ask: does this code actually cause problems?
Ugly code that works reliably, lives in a low-traffic corner of your system, and hasn't needed to change in two years is not a crisis. It might be a mild aesthetic offense, but aesthetic offenses don't show up in your incident reports.
Before you schedule a refactor, run through this quick gut check:
- Is it causing bugs? If not, the urgency is low.
- Is it slowing down other development? If engineers can work around it without much friction, it's probably fine.
- Does anyone need to touch it soon? If the answer is no, you're solving a problem that doesn't exist yet.
- Would a new engineer struggle to understand it? This one matters more than people admit — confusion is a form of drag on your team.
If you answered "no" to most of those, you've got a candidate for Zone 2: acknowledged, documented if needed, and left alone until the calculus changes.
There's no shame in this. The engineers who understand when not to refactor are just as valuable as the ones who know how.
Zone 3: The Rage-Delete Scenario (and When It's Actually Justified)
Sometimes the right answer isn't to clean something up — it's to remove it entirely. And this is where a lot of teams lose their nerve.
Dead code is sneaky. It sits in your repo looking like it might be important, surrounded by comments that say things like "DO NOT DELETE — not sure if still used" and a git blame that points to someone who left the company in 2021. Nobody deletes it because nobody wants to be the person who broke something obscure.
But dead code has real costs. It creates confusion for new engineers, inflates your mental model of how the system works, and occasionally gets accidentally resurrected in ways that cause actual problems.
The signal for deletion is usually one of three things:
- The feature it supported no longer exists. If the product removed the thing this code powered, the code should follow it out the door.
- It's been commented out for more than a release cycle. Commented-out code is just noise. If it was important, it'd be running.
- It's a wrapper around a wrapper around a wrapper. Over-engineered abstraction that nobody uses is a gift to future maintainers — specifically the gift of confusion. Delete it.
A good test: if you can't find a single call site, a single test, or a single reference to this code in your documentation or tickets, it's almost certainly safe to go. Version control exists for a reason. You can always bring it back.
Building the Habit: Triage as a Team Practice
Individual frameworks are useful, but they work better when your team operates from shared norms. A few things that help:
Make refactoring visible in your workflow. Whether you use a label in GitHub, a column on your board, or just a recurring slot in your planning cycle — give technical cleanup a place to live so it doesn't get perpetually bumped.
Separate "this bothers me" from "this blocks us." The first is a preference. The second is a work item. Both are valid, but they shouldn't compete for the same priority.
Establish a deletion culture. Normalize the PR that only removes things. Celebrate it. Some of the most valuable commits in a codebase's history are the ones that make it smaller.
Write down the decisions you're not making. If you look at something and consciously decide to leave it alone, a quick comment explaining why is worth its weight in future debugging sessions.
The Actual Goal
The best codebases aren't the cleanest ones — they're the most navigable ones. They're built by teams who've gotten good at distinguishing between code that deserves attention and code that's just taking up mental real estate.
Refactoring when something needs it, leaving things alone when they don't, and having the confidence to delete what's genuinely dead — that's the cycle. Build it into your workflow, not as a separate initiative, but as a natural part of how you move through code.
Ship the feature. Clean what you touch. Delete what's dead. Repeat.