Benjamin Stark/February 16, 2026Engineering

How we use Knip and Ona Automations to keep our codebase clean

Unused code doesn't just clutter your codebase. It wastes agent tokens and degrades AI performance. Here's how we turned a manual cleanup tool into a daily Ona Automation.

The problem

Every codebase accumulates dead code, and in a large monorepo where AI agents work alongside humans, that dead code is more than a hygiene issue. Every unused file or export is context the agent has to process, which means wasted tokens, a noisier context window, and lower quality output.

One of our frontend engineers brought Knip to our attention, a tool that scans your project for unused dependencies, exports, and files. We started running it manually and it worked well, but the results were hard to act on. You don't want to open a massive PR removing dozens of files because each removal still needs verification and tests need updating. Big cleanup PRs are risky, hard to review, and tend to go stale. This is work that would never be prioritized otherwise, so we automated it.

The solution

We built an Ona Automation that runs Knip daily, picks one cleanup task, validates it, and opens a small PR with auto-merge enabled. Engineers start their day by approving a couple of tiny PRs, and the codebase gets cleaner without anyone scheduling time for it.

How the automation works

The automation is configured as a multi-step pipeline in Ona:

1. Run Knip via shell script. After dependencies install, the automation runs Knip against the target project and outputs results as JSON:

cd frontend/dashboard && yarn --silent knip --no-exit-code --no-config-hints --reporter json --exclude unlisted,unresolved,dependencies | jq .

2. Feed the output to the agent. The agent receives the JSON output along with context about what Knip is and how to interpret its findings. It also checks existing open PRs to avoid creating duplicates for issues that are already being addressed.

3. Create a small PR with auto-merge. The agent selects one issue, makes the fix, validates it by running builds and lints, and opens a PR with auto-merge enabled. As soon as someone on the team approves, it merges automatically.

Why one issue at a time

We deliberately constrain the automation to one fix per run. A PR that removes a single unused file is trivial to verify and safe to approve immediately. A PR that removes 50 files is hard to review, risky to merge, and likely to conflict with other in-flight work.

Running daily, the automation chips away at dead code steadily. Every morning when we start work, there's a small PR to review. Approve it, it merges, and the codebase is a little cleaner. This work would never be prioritized in a sprint, but it happens every day now without anyone thinking about it.

The result

Dead code gets removed from our monorepo continuously without cleanup sprints, backlog tickets, or anyone scheduling time for it. The downstream effect on agent performance is the more interesting outcome: a cleaner codebase means a tighter context window, which means better agent output across every other task.

This pattern also extends well beyond Knip. Running a deterministic tool via shell script, feeding structured output to an agent, and having it take incremental action works for any cleanup or migration task: JavaScript to TypeScript conversions, test coverage improvements, dependency updates, or any other work that's important, automatable, and never gets prioritized.

For more examples to try today, check out the Ona template library.

Explore Ona Automations → | Get a demo →

Join 440K engineers getting biweekly insights on building AI organizations and practices

This website uses cookies to enhance the user experience. Read our cookie policy for more info.