The debt a linter can't see
Most tech-debt tooling looks at files one at a time — a lint rule flags a line, a coverage report flags a function. Two of the most common problems we find don't show up that way at all, because they're not about what's in a file. They're about how the codebase is shaped.
Organized by type, not by feature
Open a lot of React codebases and the top level looks the same: a folder of components, a folder of reducers, a folder of actions, a folder of hooks. Anything related to "orders" is split across four folders that have nothing else in common except that a feature named "orders" happens to touch all of them.
This isn't a style preference — it has a real cost. When a query hook and the reducer it duplicates data into live in different top-level folders, nothing about the file tree makes that duplication visible. You'd have to already know to go looking. Feature-based structure — everything about "orders" living together, regardless of whether it's a hook, a component, or a type — doesn't just look tidier. It makes exactly this kind of overlap visible by proximity, before it needs a code review to catch it.
Restructuring a large codebase around this isn't a weekend project, and it doesn't need to be — the fix that actually matters is applying it to new work and letting new feature folders be the reviewable evidence that the old type-based ones are legacy, not the standard.
Dead code standing behind an old flag
The second one is quieter: a feature flag that shipped, fully rolled out or fully killed months ago, still sitting in the code — the conditional, the fallback branch, sometimes the entire old implementation it was meant to replace.
Nobody's using the old path. That's exactly the problem. It's not exercised by real traffic, often not covered by tests that anyone still runs, and it's one accidental flag flip away from becoming live again — untested, unreviewed, and invisible until it breaks something. Cleaning it up isn't just tidiness; it's removing a code path that nobody would actually vouch for if you asked them today.
Why these two belong together
Neither of these shows up in a diff review, a lint pass, or a coverage number — they're properties of the codebase's shape and history, not any single file. That's exactly why they last so long: nothing in a normal review cycle is looking for them. Finding this kind of debt takes actually reading the codebase like someone unfamiliar with its history would, which is a different exercise than reviewing what changed this week.
That's also why it's worth someone doing on purpose, on a schedule, instead of hoping it surfaces on its own.