Turn taste into lint rules
Review comments don't scale. A repo with 14 custom lint rules shows what does.
- Problem
The same review comments get written again and again, and still don't stick.
- Idea
The second time you write a comment, turn it into a lint rule with tests, exceptions, and suppressions that must explain themselves.
When most of your contributors are AI agents, a review comment is the least scalable thing you have. You write it, the agent fixes it, and the next agent makes the same mistake tomorrow. This repo encodes its taste as a custom lint plugin instead: fourteen rules, each with its own test file.
Each rule encodes a real failure
| Rule | The failure it prevents |
|---|---|
no-unscoped-has | A broad CSS :has() variant makes every DOM change restyle the whole page |
no-inline-schema-compile | Compiling a validation schema on every call instead of once |
no-hermes-unsupported-apis | Shared code using web APIs the mobile JS engine can't run |
no-rpc-permission-bypass | Client mutations skipping the permission-checked command path |
no-raw-mcp-registration | Agent tools registered without declaring who may call them |
prefer-catch-tags | Catch-all error handling that hides which typed error happened |
no-test-in-loop | Tests declared in a for loop instead of it.each |
no-native-title-tooltip | Native title="" tooltips instead of the styled component |
require-suppression-reason | An eslint-disable or @ts-ignore with no explanation |
Half of these are performance, portability or security bugs, not style. A lint rule is the cheapest place to stop a bug class: it runs before tests and review, on every change, for every contributor.
Good rules know their exceptions
The tooltip rule bans title as a tooltip on regular elements, but still allows it on iframe, embed, object, frame and math, where the attribute is the element's accessible name, not a tooltip. A blunt rule would have broken accessibility. Each exception is spelled out in the rule and covered by its tests.
Silencing a tool should cost something
- A reason is mandatoryEvery suppression needs a
-- reasonsuffix or a comment on the line above. Lint enforces it. - A human has to lookAny PR that adds or broadens a suppression can't be auto-approved.
Design systems need enforcement too
shadcn/no-restyle stops you restyling a shared <Button> with className. You pick a variant or size, or you add a variant to the component. Layout classes (width, margin, position) go on the parent. That's how a design system stays consistent with hundreds of contributors.
Dead code fails CI
knip runs in CI, and an export nobody imports fails the build. No "might need this later" exports, refactors actually delete old files, and there are no re-export shims.
Steal this
Each time you write the same review comment a second time, write a lint rule instead, with tests and with the exceptions spelled out. Make suppressions explain themselves and need a human to approve.
Use it in your code
A prompt for your coding agent
Run it from the root of your repo. It checks whether the idea fits before it changes anything.
Turn repeated review feedback in this codebase into lint rules.
The idea: when you write the same review comment a second time, encode it as a lint rule with tests and documented exceptions. Suppressions must explain themselves.
1. Find candidates: rules mentioned in the style guide, agent guide or contributing docs, recurring fixes in the git history ("use X instead of Y"), and inconsistencies across the code. List the top three with examples.
2. For each, check whether the existing linter already has a rule or option for it. Prefer configuring an existing rule over writing a custom one.
3. Implement the best one: the rule or config, tests or fixtures showing what it flags and what it allows, and its exceptions spelled out.
4. If the linter supports it, require a reason on every suppression comment.
Fix the existing violations, or list them if there are too many. Keep the change small.Read the source
Where this comes from
T3 Code (pingdotgg/t3code), the open-source GUI for coding agents. The files behind this lesson: