Gå til hovedindhold
50% rabat alle planer, tidsbegrænset. Fra $2.48/mo
14 min left
Udviklerværktøjer og DevOps

Sådan opsætter du en linter til AI-genereret kode

S By Sherwin 14 min read
ESLint output showing AI-specific lint violations in a TypeScript file

AI-agenter kan generere kode, der kompilerer.

Det er stadig ikke den standard, du ønsker. Standarden er kode, der ikke importerer ting, den aldrig bruger, ikke any-caster sig ud af typesystemet, ikke ignorerer fejlreturneringer og ikke hardkoder credentials, som gosec burde fange før review.

AI-modeller trænes på gamle Stack Overflow-svar og leverer de mønstre, de lærte der, herunder forældede API'er, manglende type-annoteringer og funktioner, der teknisk set er korrekte, men for store til at kunne reviewes sikkert. Du har brug for en linter i loopet. Ikke som et forslag, men som en gate.

Denne guide dækker konfigurationen for tre økosystemer (Python med Ruff, TypeScript/JavaScript med ESLint v10 flat config og Go med golangci-lint) med regler tilpasset specifikt til de fejlmønstre, AI introducerer. Derefter dækker den, hvordan du gør gaten meget sværere at omgå, så agenten ikke bare kan springe lokale hooks over med --no-verify uden at et andet lag fanger det.

Opsætningen er lagdelt: linting på IDE-niveau fanger problemer inline, mens agenten skriver, pre-commit-hooks fanger alt, der når frem til et commit-forsøg, og CI fanger alt, der slipper igennem lokalt. Hvert lag er uafhængigt, og du kan vælge de sprogafsnit, der passer til din stack. Håndhævelseslaget fungerer ens, uanset hvilket sprog du linter.

Kort sagt

  • Ruff (Python), ESLint v10 (TS/JS) og golangci-lint (Go) har hver især specifikke regler, der fanger AI's mest almindelige fejl
  • Konfigurationerne nedenfor er kommenterede; hver regel er der af en grund
  • Lefthook håndterer pre-commit-gaten; Cursors afterFileEdit-hook kører lint inline
  • Fire håndhævelseslag gør det meget sværere for AI-agenter at springe gaten over med --no-verify
  • CI er den sidste sikring: agenter kan ikke sende --no-verify til GitHub Actions

Sådan konfigurerer du Ruff til Python-AI-kode

Terminal output from Ruff flagging unused imports and a missing return type annotation in AI-generated Python code

Ruff er den rette Python-linter til AI-assisterede kodebaser. Den er hurtig nok til at køre ved hver filgemning uden at blokere noget, den dækker både stil og reelle logikfejl, og den leverer formatter-adfærd (som erstatning for Black) i samme binær. Reglerne nedenfor er rettet mod de specifikke fejlmønstre, AI-modeller oftest introducerer i Python.

Installation af Ruff

pip install ruff

# or via uv (faster for new projects):
uv add --dev ruff

Det var det. Intet plugin-økosystem, ingen forhandling om peer dependencies.

pyproject.toml-konfigurationen

[tool.ruff]
line-length = 88
target-version = "py311"

[tool.ruff.lint]
select = [
  "E",   # pycodestyle — style consistency
  "F",   # pyflakes — catches unused imports (the most common AI artifact)
  "I",   # isort — import ordering (AI frequently reorders imports incorrectly)
  "N",   # pep8-naming — naming conventions
  "UP",  # pyupgrade — flags deprecated APIs (AI trains on old Stack Overflow answers)
  "S",   # flake8-bandit — security rules: subprocess.shell=True, eval(), hardcoded creds
  "ANN", # type annotation enforcement (AI frequently omits return type annotations)
]
ignore = []  # intentional: do not add sweeping ignores in AI-assisted codebases

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"]  # allow assert in tests

F-reglerne tjener sig hjem med det samme. AI-kode genererer import-sætninger for pakker, den ender med ikke at bruge, og F401 (ubrugt import) fanger hver eneste. UP-reglerne fanger kald til forældede API-mønstre, som AI har lært fra Python-svar fra før 3.10; UP006 og UP007 alene markerer dusinvis af unødvendige type-checking-mønstre. S-reglerne (Bandit) fanger sikkerhedsfejlene: hardkodede strings, der ligner credentials (S105/S106), shell-injektion via subprocess(shell=True) (S603/S607), svage kryptografivalg (S324).

ignore = [] er bevidst. Hver undtagelse, du tilføjer til denne liste, er en klasse af AI-fejl, du har besluttet at tillade.

Kørsel af Ruff

ruff check .           # lint only — see what's wrong
ruff check --fix .     # auto-fix safe violations (imports, formatting)
ruff format .          # format the codebase (replaces Black)

--fix anvender Ruffs sikre rettelser som standard, såsom fjernelse af ubrugte imports eller anvendelse af ligetil formatering og lint-korrektioner. Ruff har også usikre rettelser, men de kræver eksplicit opt-in og bør reviewes mere omhyggeligt. Gennemgå manuelt alt, hvad Ruff ikke kan rette sikkert.

Sådan konfigurerer du ESLint v10 til TypeScript- og JavaScript-AI-kode

ESLint v10 flat config reporting no-explicit-any and max-lines-per-function violations in an AI-generated TypeScript file

ESLint v10 droppede det gamle .eslintrc.*-konfigurationsformat. Alt er nu flat config i eslint.config.mjs. Hvis du finder en tutorial, der bruger .eslintrc.json eller .eslintrc.js, er den rettet mod ESLint v8 eller v9, hvor syntaksen er anderledes. Brug det, der står nedenfor.

@typescript-eslint-reglerne i denne konfiguration er rettet mod de specifikke fejltyper, der dukker op gentagne gange i AI-genereret TypeScript: any-nødudgangen, monolitiske funktioner, der er svære at reviewe, og hardkodede værdier, der burde være konstanter.

Installation af ESLint v10 med TypeScript-understøttelse

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

# or with pnpm:
pnpm add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

ESLint v10.5.0 er aktuel pr. juni 2026. @typescript-eslint-pakkerne bør matche din TypeScript-version; tjek deres README for kompatibilitetsmatrixen.

eslint.config.mjs-konfigurationen

import tseslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';

export default [
  {
    files: ['**/*.ts', '**/*.tsx'],
    languageOptions: {
      parser: tsParser,
      parserOptions: { project: './tsconfig.json' },
    },
    plugins: { '@typescript-eslint': tseslint },
    rules: {
      // AI defaults to `any` to bypass the type system — this blocks it
      '@typescript-eslint/no-explicit-any': 'error',

      // AI generates variables it declares but never uses
      '@typescript-eslint/no-unused-vars': 'error',

      // AI writes functions that work but are too long to review safely
      'max-lines-per-function': ['error', { max: 50 }],

      // AI overloads function signatures; this forces decomposition
      'max-params': ['error', 2],

      // AI hardcodes values that should be named constants
      'no-magic-numbers': ['error', { ignore: [0, 1, -1] }],

      // AI writes files that are too large to reason about in a single review
      'max-lines': ['error', { max: 250 }],

      // AI leaves console.log debugging in production code
      'no-console': 'warn',
    },
  },
];

Reglen max-lines-per-function: 50 er det mest aggressive i denne konfiguration. Du vil ramme den konstant ved første kørsel i en AI-assisteret kodebase. Det er meningen. Du bør ramme den. Funktioner, der overstiger 50 linjer, er det første, der bliver umuligt at ræsonnere om, når du reviewer AI-output i stor skala.

Reglen max-params: 2 fremtvinger dekomponering. AI-modeller lærer fra kodebaser, hvor funktioner med fem argumenter er normalt; reglen skubber tilbage ved at kræve, at agenten bruger et options-objekt, hvilket er bedre design og lettere at læse.

Kørsel af ESLint

npx eslint .                       # lint
npx eslint . --fix                 # auto-fix safe issues
npx eslint . --max-warnings 0      # CI mode — treats warnings as errors

Brug --max-warnings 0 i dit CI-trin. Det forfremmer no-console-advarsler fra "teknisk noteret" til "reelt blokerende."

Valgfrit: strengere regler for AI-genererede filer

Hvis dit team bruger en filnavngivningskonvention til at markere AI-genereret kode (*.ai.ts, *-generated.ts eller lignende), kan du anvende strammere regler specifikt på de filer:

// Add to eslint.config.mjs after the main config object
{
  files: ['**/*.ai.ts', '**/*.ai.tsx', '**/*-generated.ts'],
  rules: {
    'max-lines': ['error', { max: 100 }],  // tighter file ceiling
    'complexity': ['error', 5],             // McCabe complexity limit
  },
}

Sådan konfigurerer du golangci-lint til Go-AI-kode

golangci-lint er den standardiserede multi-linter-runner til Go. Den leveres med gosec, errcheck, staticcheck og over 40 andre lintere, der kan konfigureres fra én enkelt YAML-fil. For AI-genereret Go-kode er de kritiske regler kontrol af fejlreturneringer og detektion af sikkerhedsmønstre: de to fejlkategorier, AI-modeller mest konsekvent overser i Go.

Installation af golangci-lint

# Official binary installer:
curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b "$(go env GOPATH)/bin" v2.12.2

# or via Homebrew:
brew install golangci-lint

.golangci.yml-konfigurationen

version: "2"

linters:
  enable:
    - gosec        # security: flags hardcoded creds (G101), file path injection (G304), weak crypto (G401)
    - unused       # flags unused vars and functions — a common AI artifact in Go
    - errcheck     # AI frequently ignores error returns — this blocks it
    - govet        # catches subtle correctness bugs AI introduces
    - staticcheck  # comprehensive static analysis
    - revive       # style: catches non-idiomatic Go patterns AI writes
    - misspell     # AI occasionally misspells in comments and string literals

  settings:
    gosec:
      severity: medium
      confidence: medium
    errcheck:
      check-type-assertions: true  # check `val, ok := x.(Type)` patterns
      check-blank: true            # catch `_ = someErr` error suppression

run:
  timeout: 3m
  issues-exit-code: 1

Go's fejlhåndteringsmønster er eksplicit af design: enhver funktion, der kan fejle, returnerer en error-værdi. AI-modeller forstår dette, men nedvægter det; de udelader fejlkontrol i ikke-kritiske kodestier. errcheck gør den udeladelse til en lint-fejl.

gosec er sikkerhedslinteren. For AI-kode fanger den de mønstre, AI har opsamlet fra Go-tutorials fra før 2020: usikker generering af tilfældige tal (G404), usikre hash-funktioner (G401), problemer med filrettigheder (G306). Det er de fejl, du ikke fanger i code review, fordi de ser syntaktisk normale ud.

Kørsel af golangci-lint

golangci-lint run ./...            # lint all packages
golangci-lint run --fix ./...      # auto-fix where possible

Sådan kobler du linteren ind i pre-commit-hooks

En pre-commit-hook kører lint før hvert git commit og blokerer committen, hvis lint fejler. Det betyder, at AI-agenten ikke kan committe kode, der fejler dine konfigurerede regler. Den er nødt til at rette overtrædelserne først.

Lefthook er den anbefalede mulighed. Den er på tværs af platforme, hurtig og har et konfigurationsmønster, der fungerer specifikt med håndhævelse over for AI-agenter (dækkes i næste afsnit).

Lefthook

npm install --save-dev lefthook
npx lefthook install

lefthook.yml:

pre-commit:
  parallel: true
  commands:
    lint-python:
      glob: "*.py"
      run: ruff check {staged_files} --fix
    lint-js-ts:
      glob: "*.{js,ts,tsx}"
      run: npx eslint {staged_files} --fix
    lint-go:
      glob: "*.go"
      run: golangci-lint run {staged_files}
  fail_text: |
    Lint failed. For AI Agents: fix all lint violations before committing.
    Do not use --no-verify to bypass this gate.

fail_text-beskeden læses af AI-agenter, når en commit fejler. Mønsteret er dokumenteret i Liam Bigelows gennemgang af Lefthook lint-håndhævelse til Claude Code. Dette alene stopper ikke en målrettet agent, men det giver den den korrekte næste instruktion ("ret lint-overtrædelser") i stedet for at lade den udlede en omvej.

Alternativ: pre-commit (Python-kun-opsætninger)

Hvis du er på en ren Python-stack og foretrækker pre-commit-frameworket:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.17
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format

Cursor: inline-linting via afterFileEdit-hooket

Hvis du bruger Cursor, kan du udløse lint med det samme, når AI'en ændrer en fil, før den overhovedet forsøger en commit. Opret .cursor/hooks.json i din projektrod:

{
  "hooks": {
    "afterFileEdit": [
      {
        "match": "*.py",
        "run": "ruff check {file} --fix"
      },
      {
        "match": "*.{ts,tsx,js}",
        "run": "npx eslint {file} --fix"
      },
      {
        "match": "*.go",
        "run": "golangci-lint run {file}"
      }
    ]
  }
}

Dette udløses hver gang Cursors AI ændrer en fil. Agenten får lint-feedback inline, før den betragter opgaven som færdig, så de fleste overtrædelser bliver rettet, før de når frem til pre-commit-gaten.

Sådan gør du gaten sværere at omgå for AI-agenter

Diagram of four enforcement layers (CLAUDE.md policy, Claude Code deny rule, PreToolUse hook, and CI backstop) blocking an AI agent from skipping the lint gate with --no-verify

Fire håndhævelseslag gør det meget sværere for en AI-agent at springe pre-commit-gaten over med --no-verify. Hvert lag er rettet mod en forskellig angrebsflade: politik i CLAUDE.md, en deny-regel i Claude Code, et PreToolUse-hook og en CI-sikring. Behandl dem som en lagdelt opsætning, ikke som fire uafhængige garantier.

AI-agenter sender nogle gange --no-verify til git commit for at springe pre-commit-hooks over, når lint fejler, og agenten har besluttet, at fejlene er "uden relation til dens ændringer." Beslutningen er ikke altid forkert, men du bør ikke lade agenten træffe den ensidigt. Hele pointen med lint-gaten er, at et menneske har sat politikken; agentens opgave er at opfylde den, ikke at gå uden om den.

Her er hvert lag og den angrebsflade, det dækker.

Lag 1: dokumentér politikken i CLAUDE.md

## Linting Policy

NEVER use `git commit --no-verify`. All commits must pass pre-commit hooks.
Pre-commit hooks run lint. Fix lint violations before committing. Do not treat
lint failures as unrelated to your changes — they may not be, and you don't get
to decide that.

Claude Code læser CLAUDE.md ved sessionsstart. Dette er ikke håndhævelse; agenten kan stadig forsøge at omgå den. Men det fjerner "jeg vidste det ikke"-vejen og sætter en klar politik, som agenten aktivt skal vælge at overtræde, hvilket den er mindre tilbøjelig til end at udlede en omvej fra tavshed.

Lag 2: deny-regel i Claude Code

Tilføj til .claude/settings.json:

{
  "permissions": {
    "deny": [
      "Bash(git commit --no-verify*)"
    ]
  }
}

Dette blokerer den eksplicitte invokering. En begrænsning: deny-reglen bruger præfiks-matching, så den fanger kun --no-verify umiddelbart efter commit. En tilstrækkeligt kreativ agent kunne strukturere kaldet anderledes. Stol ikke på dette alene.

Lag 3: PreToolUse-hook

Installér block-no-verify-pakken:

npm install --save-dev block-no-verify

Konfigurér den derefter som et PreToolUse-hook i Claude Codes settings. Dette udløses før hvert tool-kald og undersøger argumenterne for --no-verify på tværs af seks git-subkommandoer, ikke kun commit. Den afslutter med en værdi forskellig fra nul for at blokere kaldet, før det udføres.

Håndbogen på pydevtools.com er ligefrem om det: "hook-laget er det eneste, der pålideligt håndhæver reglen." Brug dette sammen med de andre lag; behandl ikke noget lokalt hook som hele garantien.

Lag 4: CI-sikring

CI kører på serveren, hvor agenten ikke har nogen shell til at sende flag ind i:

# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Lint Python
        run: |
          pip install ruff
          ruff check . --output-format github

      - name: Lint JS/TS
        run: npx eslint . --max-warnings 0

      - name: Lint Go
        uses: golangci/golangci-lint-action@v9
        with:
          version: v2.12.2

AI-agenter kan ikke sende --no-verify til CI. GitHub Actions kører uafhængigt af, hvad agenten gjorde lokalt. Hvis en commit på en eller anden måde er sluppet igennem med fejlende lint, fanger CI den, før den merges.

Dette er den sidste sikring.

Bonus: ESLint MCP-server

Hvis du bruger Claude Code, findes der et proaktivt lag, der reducerer, hvor ofte du overhovedet rammer gaten.

ESLint MCP-serveren (@eslint/mcp) integrerer ESLint direkte i agentens tool-loop. Agenten kan forespørge ESLint under opgaven, før den forsøger en commit. Installér den globalt:

npm install -g @eslint/mcp@latest

Tilføj til .claude/settings.json:

{
  "mcpServers": {
    "eslint": {
      "command": "npx",
      "args": ["@eslint/mcp@latest"]
    }
  }
}

Med dette konfigureret kan agenten forespørge ESLint under opgaven. Agenten får lint-feedback inline, og nogle overtrædelser kan rettes, før de når frem til hooket. Dette erstatter ikke gaten, det reducerer støjen på gaten.

Ofte stillede spørgsmål

Skal jeg konfigurere alle tre lintere, hvis jeg kun arbejder i ét sprog?

Nej. Sæt linteren op for dit primære sprog og håndhævelseslaget. Hvis din stack kun er TypeScript, så konfigurér ESLint og spring Ruff og golangci-lint over. Afsnittet om forebyggelse af agent-omgåelse gælder uanset hvilket sprog du linter.

Vil disse konfigurationer ødelægge min eksisterende kodebase ved første kørsel?

Næsten med sikkerhed, og med vilje. Kør ruff check --fix . eller npx eslint . --fix for først at autorette de sikre overtrædelser. Det, der er tilbage efter auto-fix, er listen til manuel review: no-explicit-any-casts, funktioner der overstiger 50 linjer, manglende fejlhåndtering. Arbejd dig gradvist igennem dem. Tilføj ikke ignore-regler for at undgå at tage fat på dem.

Er Biome en erstatning for ESLint på nuværende tidspunkt?

Biome v2.5.0 (udgivet juni 2026) er konkurrencedygtig på formatering og grundlæggende lint-regler. Den er hurtigere end ESLint og har nul konfigurations-overhead, hvis du installerer den via bun x ultracite@latest init. For teams, der vil have ét enkelt værktøj og ikke har brug for hele dybden af @typescript-eslint-regler, er Biome et fornuftigt valg. For de AI-specifikke regler i denne guide (max-params, no-magic-numbers, max-lines-per-function med AI-rettede tærskler) har ESLint med @typescript-eslint stadig mere dækning. Du kan køre begge: Biome til formatering og grundlæggende lint, ESLint til de AI-specifikke regler.

Hvad hvis min AI-agent bliver ved med at regenerere de samme lint-overtrædelser?

Agenten arbejder uden om reglen i stedet for at rette det underliggende problem. For no-explicit-any betyder det at tilføje en type-assertion i stedet for at definere den faktiske type. For max-lines-per-function betyder det at udtrække en hjælpefunktion, der ikke gør noget nyttigt, men får linjeantallet under tærsklen. Ingen af løsningerne består code review. Lint-reglen fangede symptomet; årsagen er agentens prompt. Forfin prompten, så den specificerer typebegrænsningen eller den forventede dekomponering; agenten følger eksplicit struktur-vejledning mere pålideligt end implicitte regler. Hvis du kører en mere kompleks agent-opsætning, holder det typisk bedre at indkredse arbejdet til en dedikeret subagent med begrænsningerne bagt ind i dens instruktioner end en enkelt bred prompt.

Erstatter ESLint MCP-serveren pre-commit-gaten?

Nej. Den reducerer, hvor ofte agenten genererer kode, der fejler gaten. Gaten kører stadig ved hver commit. Inline-kontrol via MCP-serveren og håndhævelse via pre-commit-hooket supplerer hinanden, så fjern ikke nogen af dem.

Share

Mere fra bloggen

Læs videre.

Klar til at udrulle? Fra 2,48 $/md.

Uafhængig cloud siden 2008. AMD EPYC, NVMe, 40 Gbps. 14 dages pengene-tilbage-garanti.