At some point, every fast-growing codebase becomes messy.
In our case, things changed quickly. Features were evolving, old logic was being refactored or replaced, and more than 30 engineers were pushing new code daily. It didn’t take long before we realized: we were sitting on a mountain of dead code.
For sure, during the refactoring we cleaned up a bunch of code, but not every line can be found by a human. In outcome, I decided to clean it up automatically.
Step One: The Easy Wins
My first instinct was to start with the low-hanging fruit aka unused files. A couple of years back, I integrated unimported, a sweet little tool that analyzes your project and tells you what files aren’t being imported anywhere.
{
"entry": ["./src/index.ts"],
"rootDir": "./src",
"aliases": {
"atoms/*": ["./atoms/*"]
},
"ignorePatterns": [
"**/node_modules/**"
],
"ignoreUnimported": [
"src/file-to-be-ignored.js",
],
"ignoreUnused": ["npm-package-to-ignore"]
}
It’s fast, customizable, and worked like a spell.
Additionally, we heavily used WebStorm; its dead code highlighting is honestly the best I’ve ever seen in an IDE. But then came the Cursor era.
We started migrating to Cursor (based on VS Code), and suddenly our best tool in the war against unused code was gone. As much as I love VS Code’s extensibility, its native dead code detection just did not work out for us.
Also, turning on the eslint rules ahead of time turned out to be helpful. @typescript-eslint/no-unused-vars and no-unreachable saved us from keeping disorder that would otherwise go unnoticed. Static analysis is our first line of defense against dead code.
So, I went hunting for options.
When ts-prune Wasn’t Enough
I revisited ts-prune, an old favorite that identifies unused exports and code in TS projects. While it still works well, it’s officially in maintenance mode and I’m not a fan of building tooling workflows around deprecated packages.
That’s when I fell on something better: Knip.
Meet Knip: The Dead Code Terminator
Knip didn’t just impress me, it blew me away.
It’s more than a dead code detector. Knip is a comprehensive code hygiene tool with support for:
- Unused files and exports;
- Unnecessary dependencies;
- Missing dependencies;
- Monorepos and workspaces support;
- Unused class members, enum members, duplicate exports and so on.
What truly makes Knip powerful is its plugin-based architecture. It understands a wide variety of frameworks, libraries, test runners, and build tools out of the box. So instead of spending hours writing a config file, you can usually just run it and get instant insights.
Here’s the configuration sample:
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"project": [
"src/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}!"
],
"entry": [
"src/index.ts"
],
"typescript": true, // TS plugin is enabled
"ignore": [
"src/file-to-be-ignored.js"
],
"rules": {
"exports": "error",
"files": "error",
"unlisted": "warn",
"dependencies": "off",
"types": "error",
"binaries": "off",
"enumMembers": "warn"
},
"tags": ["-knipignore"]
}
After integrating Knip and hitting zero dead code (removed around 3.5k lines of code at once), I immediately added it to our CI pipeline. I was really impressed by how fast it is. Even with our huge code base, it only takes a few seconds to run through all the rules and checks with Knip.
Why You Should Care
If you’re wondering why any of this matters, here’s the TL;DR:
Dead code slows you down. Every unused file, dependency, or export increases your build time, and makes onboarding harder.
Dead dependencies are a liability. If you’re importing code from a dependency of a dependency, and you’re not declaring it directly, your code might break without warning when transitive dependencies change.
Cleaner code means fewer bugs. And fewer bugs mean happier developers.
Psyhological satisfaction. Deleting code is just so amazing! 😋
Final Thoughts
Now Knip runs with every pull request and protects our codebase from regressing into spaghetti.
If your project is more than a few months old, you’ve probably got a few things hanging around that you just don’t need anymore, right?
Knip won’t just help you clean it up, it’ll keep it that way, it’s a process already.
Give it a shot. Thanks for reading.
Support Original Article: https://medium.com/javascript-in-plain-english/how-i-cleaned-up-our-codebase-with-knip-and-why-you-should-too-1aa9d5152659
Top comments (4)
Love that you called out the psychological satisfaction of deleting code - totally underrated. Have you noticed any impact on PR review speed since adding Knip to CI?
I’m not really sure if the speed of PR reviews has picked up, but I can definitely say that the quality of the code has gotten better, and it’s a lot easier on the developers now.
this is extremely impressive, especially clearing 3.5k lines in one go
you think codebase health reflects the maturity of the engineering team
Absolutely! Code really reflects the hard work of engineers, and keeping it well-organized shows just how mature and professional they are.