Eight years of product iterations leaves marks. Our Express API had grown to 45,000 lines of code across hundreds of endpoints — features that shipped, features that didn't, integrations that were replaced, experiments that were abandoned. Nobody knew exactly what was still being used.
Static analysis wasn't the answer. Tools like ESLint can tell you what's unreachable at the code level, but they can't tell you whether an endpoint is receiving live traffic. An endpoint can be perfectly reachable in code and completely dead in production. We needed a different signal.
Starting manually
We started with access logs and did the first pass by hand. Cross-referencing log data against registered Express routes, we identified candidates — endpoints with zero traffic over several months — then manually verified each one before touching anything.
That process confirmed the approach worked. We removed 12 endpoints manually. But doing it by hand didn't scale. The verification was slow, the cross-referencing was tedious, and we still had hundreds of endpoints to work through.
Automating the detection
We built a detector that automated the log analysis step. It:
- Ingests access logs
- Extracts every route that received traffic over a configurable observation window
- Maps that against every route registered in the Express app
- Outputs a ranked list of candidates with metadata: last seen date, call volume over time, and which services or clients were calling them before they went dark
The detection is fast. The verification still requires a human — some endpoints that look dead turn out to be called by quarterly jobs, legacy mobile clients, or internal scripts that don't show up in the main traffic logs. But the automated output makes verification dramatically faster because you're reviewing evidence rather than hunting for it.
The results
| Dead endpoints removed | 50 |
| Lines of code deleted | 16,000 |
| Original codebase size | 45,000 lines |
| Reduction | ~35% |
The codebase is easier to navigate, faster to onboard into, and less frightening to refactor.
The thing nobody tells you
The hardest part wasn't the detection. It was the fear of being wrong.
Every engineer who's worked in a legacy codebase knows the feeling — the endpoint looks dead, the logs confirm it, but what if something calls it once a year during a process nobody remembers?
The answer is a longer observation window and a verification checklist, not paralysis. If an endpoint has had zero traffic across production, staging, and canary for 12 months, with no reference in any scheduled job, script, or runbook — it's dead. Remove it.
If this sounds familiar
We're exploring building this as a standalone tool for Node.js/Express codebases.
If your team is sitting on a legacy API with the same problem, I'd genuinely like to hear how you're handling it — or not handling it.
→ Leave your email here if you'd want early access or just want to compare notes.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.