Linters and formatters for AI-heavy code that stays clean
Linters and formatters for AI-heavy code help teams stop style churn, keep pull requests readable, and make AI output easier to review.

Why AI-written code drifts
AI tools copy whatever style is nearby. If one file uses single quotes, another uses double quotes, and a third mixes both, the model learns that all of those are fine. It usually mirrors the pattern closest to the code it is changing.
That sounds minor. It is not. Two correct answers can still differ in spacing, import order, line wrapping, and function layout. When those differences show up in every suggestion, the codebase starts to wobble.
Reviewers feel it first. A small pull request turns into a scan of quote changes, moved blank lines, and imports that got reordered for no reason. Review should focus on logic, edge cases, and safety. Style noise steals that time.
Cosmetic edits also make logic changes harder to spot. A bug fix might be three lines, but if the file gets rewrapped and regrouped at the same time, a reviewer has to inspect forty changed lines to feel safe.
The problem grows when a team uses AI all day. One person accepts trailing commas, another does not. One prompt rewrites a helper because a nearby file uses a different pattern. By Friday, people argue about taste instead of behavior.
That is why formatting and linting matter early. Without a fixed style, every prompt becomes a tiny vote on how the code should look. A small enforced rule set keeps the code predictable so reviews can stay focused on what might actually break production.
What a small enforced rule set should do
A good rule set stops the same argument from showing up in every pull request. If people still debate quote marks, import order, or line breaks by hand, the rules are too loose. Tooling should settle those details automatically.
Keep the rules short. A new teammate should be able to read them in a few minutes and understand why each one exists. If a rule needs a long defense, it probably does not belong in enforcement.
Give the formatter and the linter different jobs. The formatter owns appearance: spacing, indentation, line width, trailing commas, and similar choices. The linter should catch code that is broken, risky, or likely to confuse people later, like unused variables, shadowed names, unsafe async patterns, or stray debug output. When those jobs blur, teams get noise instead of clarity.
A simple setup usually works best:
- Put style choices in the formatter.
- Keep lint rules for bugs, risky patterns, and a few shared habits.
- Auto-fix what you can before review.
- Block only the rules the team is willing to follow every time.
Write down exceptions before rollout. Most teams already know where the defaults get in the way: test files, generated code, and migration scripts. If you wait until the first blocked pull request, the rules will feel random and annoying.
With AI in the loop, a narrow target helps. Models copy consistency fast, but they also copy inconsistency fast. A short rule set reduces noisy diffs, cosmetic comments, and repeated style arguments. If the setup works, nobody talks about it much after the first week.
Pick one formatter and stick with it
When every person and every AI tool formats code a little differently, pull requests fill with noise. One formatter across the repo cuts that down fast.
Pick the formatter that fits your main stack, then stop debating style. Let it decide spacing, quotes, trailing commas, and line breaks. Those choices do not need human review.
This matters even more with AI-assisted coding. An assistant often copies whatever file it saw last, even if that file uses older patterns. A formatter resets the output to the same house style before review.
A practical formatter should cover most of the files your team edits, run from the command line, work inside editors with little setup, and produce the same result in CI.
Consistency matters more than finding the perfect tool. If your JavaScript team uses Prettier, use it everywhere that makes sense. If your Go code uses gofmt, do not pile personal preferences on top. The best formatter is the one nobody thinks about after week one.
Run it the same way on every machine. Pin the version, add one project command, and use that exact command in CI. That removes the classic problem where code looks fine on a laptop but changes again after push.
Do not add manual style rules that fight the formatter. If it wraps a long line, reviewers should not ask someone to unwrap it. If it prefers double quotes, nobody should request single quotes by hand. Those comments waste time and teach AI tools mixed signals.
Save manual review for things the formatter cannot judge well: confusing names, risky logic, dead code, and missing tests. The formatter should own visual consistency. People should own code quality.
Add linter rules that catch real problems
AI-written code often runs, but it still leaves small mistakes behind. You see stale imports, unused helpers, renamed functions that no longer match, and variables that never get touched again. A linter should catch that before reviewers spend time cleaning it up by hand.
Start with rules that pay for themselves every week. Good first picks are unresolved imports, unused imports, unused variables, shadowed names, simple async mistakes, and basic syntax or type issues your language can detect. These checks are plain and practical. They stop bugs, and they rarely start long arguments.
A short list like this is usually enough:
- Broken or unresolved imports
- Unused imports and variables
- Duplicate or shadowed names
- Simple syntax, async, or logic mistakes your language can detect
Avoid rules that mostly reflect personal taste. If a rule starts the same debate in every pull request, leave it out of the first version of your setup. The formatter already covers most style choices. The linter should focus on code that is wrong, risky, or clearly dead.
A small example shows why this matters. An AI assistant rewrites fetchUser() to getUser(), updates half the file, and forgets the old import at the top. The code may still look fine in a quick skim. A linter catches the unused import and the missing reference in seconds. That saves a reviewer from hunting through the file line by line.
Do not make every new rule an error on day one. Start with warnings for a week and see what happens in real pull requests. If a rule keeps catching actual mistakes, keep it and tighten it later. If people keep adding disable comments or running into false alarms, drop it. Generated code often hits odd edge cases, so the calm rule set beats the clever one.
Roll out changes in steps
Rollouts fail when teams change too much at once. If you want this setup to stick, keep the first pass boring and strict.
Start with the files where AI writes most often. On most teams, that means API handlers, React components, tests, scripts, and small utility files. Do not begin with every corner of the repo. Start where pull requests change code every day.
A clean rollout usually looks like this:
- Agree that new code in the busiest folders follows the formatter and lint rules.
- Run the formatter across the repo once in a single commit.
- Turn on a small lint set next.
- Clean the current errors before CI blocks new violations.
That one repo-wide formatting commit matters more than it seems. If you skip it, every later change mixes real work with spacing, quotes, and line-wrap noise. Reviewers get tired fast, and blame history gets messy.
Keep the lint set small on purpose. A giant preset creates arguments, local overrides, and random exceptions. A short rule set is easier to remember, and AI tools adapt to it faster.
A small team can do this in a week. Day one, format the repo. Day two, fix lint errors in the busiest folders. After that, let CI check changed code until the rest of the repo catches up.
If you have a fractional CTO or an engineering lead, let one person own the cleanup commit and the CI switch. Committees drag this out, and style drift returns while people debate tabs, quotes, and commas.
What this looks like on a small team
A three-person product team shipped a small API quickly. One person built handlers, one wrote tests, and the founder used AI to draft both when the queue got busy. They moved fast, but every pull request looked a little different.
The code usually worked. The trouble sat in the details. One AI draft grouped imports by package name, another mixed local and external imports, and a third left unused imports behind. Some handlers used userId, others used user_id, and test names swung between short labels and full sentences. None of it broke production, but it slowed every review.
After two weeks, the team changed one habit. They picked one formatter and ran it on every commit. That ended the arguments about quotes, spacing, commas, and line breaks in one step. Then they added six lint rules and kept them narrow enough that everyone could remember them.
Their rules covered import order, unused imports, one naming pattern for variables and functions, shadowed names, forgotten promises, and debug logs in pull requests. That small set did most of the work. They did not try to catch every possible issue.
The next batch of pull requests felt different. The AI still wrote first drafts, but the output arrived in the same shape every time. Review comments stopped circling around style. People started talking about whether the handler returned the right status code, whether the test covered an edge case, and whether the error message made sense.
That is the point. These tools do not make the code smarter. They make it consistent enough that humans can pay attention to logic.
The team noticed the change quickly:
- Pull requests got shorter because formatting-only changes disappeared.
- Review time dropped from about 25 minutes to 10.
- New contributors copied the same patterns with little coaching.
- Test files became easier to scan during bug fixes.
A small enforced rule set did not slow them down. It gave the AI less room to drift and gave the team its attention back.
Mistakes that create more churn
The fastest way to make this setup unpopular is to turn it into a cleanup project instead of a guardrail. When every pull request rewrites half the file, people stop trusting the tools and start fighting them.
A common mistake is importing a giant preset on day one. Big presets look safe, but they usually bring opinions your team never agreed to. Then AI adds code in one style, the preset demands another, and every review turns into a taste fight.
Mixing two formatters in one repo is worse. One tool changes quotes, the other changes line breaks, and both keep touching the same files. You save a file, commit it, and the next check changes it again. That loop wastes time and teaches people to ignore formatting noise.
Build failures need the same restraint. If CI suddenly fails on 700 old warnings, nobody sees the real issue. A new bug sits next to a wall of old complaints, and the team either adds blanket ignores or turns the rule off completely.
A better rollout stays dull on purpose. Start with one formatter and a short lint set for real issues like unresolved imports, accidental shadowing, obvious dead code, simple async mistakes, and unsafe any if your team cares about it. Fix old files in batches, or enforce rules only on changed lines and new files.
Bad rules also linger too long. If people disable the same check in every third pull request, the rule is not helping. Remove it, narrow it, or change the threshold.
One small example makes the problem obvious. A team adds a popular ESLint preset, Prettier, and another formatter because one developer likes its output. Their next AI-assisted pull request touches 18 files, but only two contain real logic changes. Reviewers spend 20 minutes sorting formatting edits from actual code. After a week, developers start pasting disable comments just to get work merged.
That kind of churn is self-inflicted. Keep the rule set small, keep one formatter in charge, and make CI strict only where the team can actually follow through.
A short merge checklist
A pull request should tell a simple story. Reviewers should notice the behavior change first, not spend their time on spaces, quotes, or import order.
Keep the merge check short enough that people actually use it:
- The formatter already ran locally or in CI. Nobody should fix spacing, line breaks, or commas by hand right before merge.
- The diff mostly shows behavior changes. If half the pull request is style noise, split formatting into its own commit or rerun the formatter on a smaller scope.
- The linter reports no unused imports, dead variables, or obvious mistakes.
- Every enforced rule has a plain-English reason. If a teammate cannot explain a rule in one sentence, it probably does not belong in the default set.
The second item matters more than teams think. AI assistants often rewrite nearby lines while touching one function. The code may still work, but the review gets slower because people have to scan a noisy diff to find the real change.
The fourth item keeps the rule set healthy. Small teams get into trouble when they keep adding rules after every annoying pull request. A month later, nobody remembers why a warning exists, and people start adding ignore comments just to get a green check.
A good merge habit is boring on purpose. The formatter runs. The linter stays quiet. The diff is small enough to read in one pass. Then the reviewer can spend attention on naming, edge cases, and whether the code should exist at all.
If your team keeps seeing style churn in pull requests, do not add five more rules. Cut the list down, enforce the basics, and make the diff readable again.
What to do next
Start by cutting rules, not adding them. Most teams already have enough checks to keep code readable. The bigger mess usually comes from old rules that create noise, argue over taste, or force tiny edits in every pull request.
Open your current config and review it line by line. Ask one simple question about each rule: does this stop bugs, or does it start style debates? If a rule mostly creates comments about spacing, wrapping, or naming taste, remove it. When AI touches many files at once, a short lint set is often better than a long one.
For most teams, this setup should feel boring. It fixes style automatically, catches real mistakes, and stays out of the way the rest of the time.
Test it on one active project before you roll it out wider. Pick a repo with regular pull requests, not a side project nobody touches. After a week or two, check what changed: fewer style-only comments, fewer CI reruns, and less back-and-forth before merge.
Write a short team note for AI-assisted coding habits. One page is enough if it answers four things: which formatter runs automatically, which lint rules block a merge, when AI-generated code needs a human cleanup pass, and what reviewers should ignore because tooling already covers it.
That note matters more than people expect. AI tools copy local patterns fast. If your repo, CI checks, and review habits all say the same thing, style churn drops quickly.
If you want an outside review, Oleg Sotnikov at oleg.is works with startups and small teams on AI-assisted development workflows, CI/CD, and Fractional CTO support. A quick pass on your tooling and review flow is often enough to trim a bloated rule set down to something people will actually follow.
Keep the first pass small. Delete noise, test one project, write the team note, and watch what still causes friction. Once the clutter is gone, the remaining problems are usually easy to see.
Frequently Asked Questions
Why does AI keep changing quotes, imports, and spacing?
Because the model copies the code around it. If nearby files mix quotes, import groups, or naming styles, the tool learns that all of them are acceptable and mirrors whatever it saw last.
Do I need both a formatter and a linter?
Yes. Let the formatter handle appearance, and let the linter catch mistakes. That split keeps review focused on logic instead of style comments.
How many rules should we start with?
Start small. Pick only the rules your team will follow every time. For many teams, a formatter plus a few lint checks for unused imports, unused variables, shadowed names, broken imports, and simple async mistakes is enough.
What should block a pull request first?
Block checks that stop real bugs or obvious churn. Good first choices include unresolved imports, unused imports, dead variables, shadowed names, stray debug logs, and simple promise mistakes.
Should I format the whole repo before I enforce rules?
Yes. Run one repo-wide formatting commit before you turn on strict checks. That keeps later pull requests from mixing real work with quote, spacing, and line-wrap noise.
What should we do with old files that already fail lint?
Do not try to fix everything in one week. Clean the busiest folders first, then let CI enforce rules on changed files or new code until the rest catches up.
Can we use more than one formatter in the same repo?
No. One formatter should own the repo, or at least each language area with a clear boundary. Two tools that rewrite the same files will keep fighting each other and create noisy diffs.
How do we keep AI-generated pull requests small and readable?
Pin the formatter version, run the same command locally and in CI, and ask people to format before review. If the diff still looks noisy, split cosmetic edits from behavior changes into separate commits.
Which files should we treat as exceptions?
Most teams should exempt generated code, some migration scripts, and a few test patterns. Write those exceptions down early so nobody gets surprised by a blocked merge.
When should a small team ask for outside help with this setup?
Bring in help when reviews drag, CI fails for noisy reasons, or the team keeps arguing about style. A fractional CTO or engineering lead can trim the rule set, clean up the rollout, and make the checks boring again.