tarek.kharsa

home / lessons / t3 code · lesson 5

DX & ToolingT3 Code · lesson 5 of 81 min read

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

RuleThe failure it prevents
no-unscoped-hasA broad CSS :has() variant makes every DOM change restyle the whole page
no-inline-schema-compileCompiling a validation schema on every call instead of once
no-hermes-unsupported-apisShared code using web APIs the mobile JS engine can't run
no-rpc-permission-bypassClient mutations skipping the permission-checked command path
no-raw-mcp-registrationAgent tools registered without declaring who may call them
prefer-catch-tagsCatch-all error handling that hides which typed error happened
no-test-in-loopTests declared in a for loop instead of it.each
no-native-title-tooltipNative title="" tooltips instead of the styled component
require-suppression-reasonAn 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

  1. A reason is mandatoryEvery suppression needs a -- reason suffix or a comment on the line above. Lint enforces it.
  2. 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: