DEV Community

Cover image for ever-better: 13 commands that make a codebase only able to get better
Isamu Arimoto
Isamu Arimoto

Posted on

ever-better: 13 commands that make a codebase only able to get better

Adding a strict linter to an existing repository produces four thousand errors and gets reverted. The usual workaround — set everything to warn — enforces nothing, and the count grows quietly.

ever-better is the way around that. It records every violation that exists today as a ceiling: old code is grandfathered, new code is held to the whole rule set, and the ceiling can fall but never rise.

Below is every command, what it does, and the problem it exists for.

npx ever-better <command>
Enter fullscreen mode Exit fullscreen mode

MIT, zero runtime dependencies, Node 20.11+.


diagnose — what is missing, and what each gap costs

ever-better diagnose            # read-only
ever-better diagnose --write    # also writes QUALITY.md
Enter fullscreen mode Exit fullscreen mode

What it does. Surveys the repository — package manager, language, framework, runtime, ESLint setup, formatter, test runner, dead-code and duplication scans, agent instructions, CI, file sizes — and lists the gaps with the phase each belongs to.

Why. "Add linting" is not a task; it is six of them in an order that matters. Formatting has to land before linting or the first cleanup PR is a diff nobody can read. Type-aware rules cannot run without TypeScript. This prints that order instead of leaving you to discover it.


bootstrap — install the tooling, generate the configs

ever-better bootstrap
ever-better bootstrap --dry-run
Enter fullscreen mode Exit fullscreen mode

What it does. Adds the dependencies, writes eslint.config.mjs, Prettier config, .gitattributes, knip.json, the package scripts CI needs, workflows for three platforms, dependabot.yml, and a secret-scan workflow. Prices each TypeScript strictness flag by enabling it in a temporary config and counting the errors — turning on the free ones, reporting what the rest would cost.

Why. Every rule it writes is an error, not a warning. That is only survivable because of the next command.


freeze — pin today's violations as the ceiling

ever-better freeze
Enter fullscreen mode Exit fullscreen mode

What it does. Runs eslint --suppress-all, which records how many violations each rule has in each file, then stores the totals in .ever-better/state.json.

Baseline pinned: 4,942 violations across 72 rules are now grandfathered.
Enter fullscreen mode Exit fullscreen mode

Why. From this commit on, the 4,942 are silent and violation 4,943 fails the build. You do not have to fix anything today to start enforcing everything tomorrow.

The ratchet itself is ESLint's own bulk-suppressions feature. ever-better is the part around it: knowing which rules to add, keeping the ledger, and failing CI when a number goes up.

It refuses to run twice. A second freeze would grandfather everything added since — which is the one thing the baseline exists to prevent.


check — the gate

ever-better check
Enter fullscreen mode Exit fullscreen mode

What it does. Compares current counts against the ceiling and exits non-zero if anything rose.

8 unsuppressed error(s) — these are new since the baseline:
  2  id-length
  1  prettier/prettier
Enter fullscreen mode Exit fullscreen mode

Why. A baseline is only a ratchet if something rejects a regression. Without CI it is a note.

Do not pipe it. ever-better check | tail exits with tail's status and reports a failing gate as success.


prune — lower the ceiling after a fix

ever-better prune
Enter fullscreen mode Exit fullscreen mode

What it does. Removes suppressions for violations that no longer exist, so the recorded ceiling matches reality.

Why. Without it, fixing 100 violations changes nothing: the ceiling still permits them, and they can come back. This is what makes the number monotonic.


next — what to drain first

ever-better next
ever-better next --fan-in
Enter fullscreen mode Exit fullscreen mode

What it does. Ranks the backlog by which edit enforces the most, not by which rule is biggest.

take these first — one or two edits, and the rule is enforced in that file for good:
    1  src/browser.js  @typescript-eslint/no-require-imports
    1  src/common.js   no-param-reassign
Enter fullscreen mode Exit fullscreen mode

Why. The ratchet is per file per rule. A file with no suppression left for a rule fails on its next violation, whatever that rule's total is elsewhere — so two violations in one file buy more enforcement than twenty spread across twenty files.

--fan-in adds how many files import each one, because a fix in a widely imported module lands errors in files the diff never opened.


report — what the debt actually looks like

ever-better report
ever-better report --json
Enter fullscreen mode Exit fullscreen mode

What it does. Prints the backlog as a rule × area table, and appends it to $GITHUB_STEP_SUMMARY when that is set.

Why. next answers "which edit enforces the most". This answers "what shape is this repository's debt". And because the generated workflow runs it after check with if: always(), the run where the gate just failed is the run where the backlog is visible — no one has to edit a workflow to get a report.


secrets — scan the history for committed credentials

ever-better secrets
Enter fullscreen mode Exit fullscreen mode

What it does. Runs gitleaks over both the history and the working tree, failing on any finding.

Why both. Either alone passes a repository that is holding a secret. A history scan misses the key you pasted an hour ago and have not committed. A working-tree scan misses the key that was committed and then deleted — which is still in every clone.

This is the one check with no baseline. A leaked key is not debt you ratchet down.


migrate — JavaScript to TypeScript

ever-better migrate            # show the plan
ever-better migrate --all      # rename everything in one pass
ever-better migrate --file src/foo.js
Enter fullscreen mode Exit fullscreen mode

What it does. Renames with git mv so history follows, and lets the lint fallout land in the ratchet instead of blocking the migration.

Why. Types are the cheapest rule set there is, and the tier that finds the most real bugs cannot run without them. On a 13-year-old JavaScript project, renaming 49 files and changing no logic produced 2,641 type errors — every one of them a question nobody had answered.


emit-diff — prove a refactor changed nothing

ever-better emit-diff --against main
Enter fullscreen mode Exit fullscreen mode

What it does. Compiles the working tree and a git ref, then compares the emitted JavaScript.

Why. Types erase at compile time, so a change that only moves types must produce byte-identical output. That is a proof, in seconds. No amount of test coverage states it as strongly, because tests only cover the paths someone thought of.


catalog — the helpers that already exist

ever-better catalog
Enter fullscreen mode Exit fullscreen mode

What it does. Writes docs/shared-helpers.md: every exported function, grouped by directory, with the first sentence of its doc comment. Point your CLAUDE.md at it.

Why. A linter sees inside one file. Duplication detection only notices copies once they are textually similar, and two independent implementations of the same idea rarely are. Nothing else reports the same function written a sixth time under a sixth name.


status — where you are

ever-better status
Enter fullscreen mode Exit fullscreen mode

What it does. Prints the phase, the frozen date, the current backlog, which rules improved or regressed, and the smallest remaining backlogs.

STALE      100 commits since the diagnosis; re-run diagnose before trusting it
phase      drain
backlog    3532
Enter fullscreen mode Exit fullscreen mode

Why. That STALE line matters more than it looks. A diagnosis from a hundred commits ago is a description of a repository that no longer exists.


log — why, not how many

ever-better log --kind drained  --rule max-depth "6 violations, 1 real bug"
ever-better log --kind deferred --rule max-lines "router.ts is 1400 lines; its own project"
ever-better log --kind issue    --rule no-floating-promises "opened #42 — product decision"
Enter fullscreen mode Exit fullscreen mode

What it does. Records what happened against the current commit — the only command that writes the Work log in QUALITY.md. Everything else records counts.

Why. deferred is the one that earns its keep. It renders as a Carried over checklist stamped with the commit it was seen at, because "router.ts needs splitting" is useless four hundred commits later unless a reader can tell when it was true.


The whole loop, in five commands

The other eight are for when you need them. This is the shape:

npx ever-better diagnose     # what is missing
npx ever-better bootstrap    # install it
npx ever-better freeze       # pin today's count
npx ever-better check        # gate CI on it
npx ever-better prune        # lower the ceiling as you fix
Enter fullscreen mode Exit fullscreen mode

Or hand the repository to an agent

The commands are the deterministic half — count, record, gate, refuse. The judgement half is eight Claude Code skills that ship with it: which violation is a real bug, when to stop and ask, what deserves an issue rather than a fix.

/plugin marketplace add isamu/ever-better
/plugin install ever-better
Enter fullscreen mode Exit fullscreen mode

Then say clean this repo up. It formats, installs, freezes, and works the backlog down one rule per pull request — fixing violations, extracting the pure functions that make the fixes testable, writing the tests, and lowering the ceiling as it goes.

It is not unattended. Anything it can decide from the code, it decides. Anything it cannot — ambiguous behaviour, a public API change, a refactor that is its own project — becomes an issue with the options written out, and it moves on.


Try it on something you did not write

That is the honest test. Pick a repository you have never read:

git clone --depth 1 https://github.com/debug-js/debug.git
cd debug
npx ever-better diagnose
npx ever-better bootstrap
npx prettier --write .
npx ever-better freeze
Enter fullscreen mode Exit fullscreen mode

Then add a file with a violation and run npx ever-better check. The 72 existing violations stay silent; the new ones fail.

On this particular repository you will also see five (parse error) entries — /* eslint-env */
comments and stale eslint-disable directives that flat config rejects. Those are real, they have
no rule id, and --suppress-all cannot record them, so freeze names the file and line for each.
It is a fair first impression of what the tool does: it tells you where, and it does not pretend the
number is smaller than it is.

Results welcomedocs/RESULTS.md collects what people froze and how much of it turned out to be real defects. It has two rows and both are mine, which is enough to notice a pattern and nowhere near enough to claim one. A run that found zero real bugs is the most useful row that table could get.

github.com/isamu/ever-better (MIT)

Top comments (0)