Zum Hauptinhalt springen
50 % Rabatt alle Pläne, begrenzte Zeit. Ab $2.48/mo
14 min left
Developer Tools und DevOps

So richten Sie einen Linter für KI-generierten Code ein

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

KI-Agenten können Code erzeugen, der kompiliert.

Das ist trotzdem nicht der Maßstab, den Sie wollen. Der Maßstab ist Code, der nichts importiert, was er nie verwendet, der sich nicht per any-Cast aus dem Typsystem stiehlt, der keine Fehlerrückgaben ignoriert und der keine Zugangsdaten hartcodiert, die gosec vor dem Review abfangen sollte.

KI-Modelle trainieren auf alten Stack-Overflow-Antworten und liefern die Muster aus, die sie dort gelernt haben, darunter veraltete APIs, fehlende Typannotationen und Funktionen, die technisch korrekt, aber zu groß sind, um sie sicher zu reviewen. Sie brauchen einen Linter im Loop. Nicht als Vorschlag, sondern als Gate.

Dieser Leitfaden behandelt die Konfiguration für drei Ökosysteme (Python mit Ruff, TypeScript/JavaScript mit der ESLint-v10-Flat-Config und Go mit golangci-lint) mit Regeln, die speziell auf die Fehlermuster abgestimmt sind, die KI einführt. Anschließend zeigt er, wie Sie das Gate deutlich schwerer umgehbar machen, sodass der Agent die lokalen Hooks nicht einfach mit --no-verify überspringen kann, ohne dass eine weitere Schicht es abfängt.

Das Setup ist gestaffelt: Linting auf IDE-Ebene fängt Probleme inline ab, während der Agent schreibt, Pre-Commit-Hooks fangen alles ab, was bis zu einem Commit-Versuch gelangt, und CI fängt alles ab, was lokal durchgeht. Jede Schicht ist eigenständig, und Sie können die Sprachabschnitte auswählen, die auf Ihren Stack zutreffen. Die Durchsetzungsschicht funktioniert gleich, unabhängig davon, welche Sprache Sie linten.

Kurzfassung

  • Ruff (Python), ESLint v10 (TS/JS) und golangci-lint (Go) haben jeweils spezifische Regeln, die die häufigsten Fehler von KI abfangen
  • Die Konfigurationen unten sind kommentiert; jede Regel hat einen Grund
  • Lefthook übernimmt das Pre-Commit-Gate; der afterFileEdit-Hook von Cursor führt Lint inline aus
  • Vier Durchsetzungsschichten machen es für KI-Agenten deutlich schwerer, das Gate mit --no-verify zu überspringen
  • CI ist das letzte Sicherheitsnetz: Agenten können --no-verify nicht an GitHub Actions übergeben

So konfigurieren Sie Ruff für Python-KI-Code

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

Ruff ist der richtige Python-Linter für KI-unterstützte Codebasen. Er ist schnell genug, um bei jedem Speichern einer Datei zu laufen, ohne etwas zu blockieren, er deckt sowohl Stil als auch echte Logikfehler ab und liefert das Formatter-Verhalten (als Ersatz für Black) im selben Binary. Die Regeln unten zielen auf die spezifischen Fehlermuster ab, die KI-Modelle in Python am häufigsten einführen.

Ruff installieren

pip install ruff

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

Das war's. Kein Plugin-Ökosystem, kein Aushandeln von Peer-Dependencies.

Die pyproject.toml-Konfiguration

[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

Die F-Regeln zahlen sich sofort aus. KI-Code erzeugt Import-Anweisungen für Pakete, die er am Ende nicht verwendet, und F401 (unbenutzter Import) fängt jeden einzelnen ab. Die UP-Regeln fangen Aufrufe veralteter API-Muster ab, die KI aus Python-Antworten vor Version 3.10 gelernt hat; allein UP006 und UP007 markieren Dutzende unnötiger Typprüfungsmuster. Die S-Regeln (Bandit) fangen die Sicherheitsfehler ab: hartcodierte Strings, die wie Zugangsdaten aussehen (S105/S106), Shell-Injection über subprocess(shell=True) (S603/S607), schwache Kryptografie-Entscheidungen (S324).

Das ignore = [] ist Absicht. Jede Ausnahme, die Sie dieser Liste hinzufügen, ist eine Klasse von KI-Fehlern, die Sie zuzulassen entschieden haben.

Ruff ausführen

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 wendet standardmäßig die sicheren Fixes von Ruff an, etwa das Entfernen unbenutzter Imports oder das Anwenden einfacher Formatierungs- und Lint-Korrekturen. Ruff hat auch unsichere Fixes, aber die erfordern ein ausdrückliches Opt-in und sollten sorgfältiger geprüft werden. Prüfen Sie alles, was Ruff nicht sicher beheben kann, manuell.

So konfigurieren Sie ESLint v10 für TypeScript- und JavaScript-KI-Code

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

ESLint v10 hat das alte .eslintrc.*-Konfigurationsformat fallen gelassen. Alles ist jetzt Flat-Config in eslint.config.mjs. Wenn Sie ein Tutorial finden, das .eslintrc.json oder .eslintrc.js verwendet, zielt es auf ESLint v8 oder v9 ab, wo die Syntax anders ist. Verwenden Sie, was unten steht.

Die @typescript-eslint-Regeln in dieser Konfiguration zielen auf die spezifischen Fehlermodi ab, die in KI-generiertem TypeScript immer wieder auftauchen: die any-Notausstiegsluke, monolithische Funktionen, die schwer zu reviewen sind, und hartcodierte Werte, die Konstanten sein sollten.

ESLint v10 mit TypeScript-Unterstützung installieren

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 ist Stand Juni 2026 aktuell. Die @typescript-eslint-Pakete sollten zu Ihrer TypeScript-Version passen; prüfen Sie deren README für die Kompatibilitätsmatrix.

Die eslint.config.mjs-Konfiguration

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',
    },
  },
];

Die Regel max-lines-per-function: 50 ist das Aggressivste in dieser Konfiguration. Beim ersten Durchlauf in einer KI-unterstützten Codebasis werden Sie ständig daran anstoßen. Das ist der Punkt. Sie sollten daran anstoßen. Funktionen, die 50 Zeilen überschreiten, sind das Erste, was unmöglich zu durchschauen wird, wenn Sie KI-Output in großem Umfang reviewen.

Die Regel max-params: 2 erzwingt Zerlegung. KI-Modelle lernen aus Codebasen, in denen Funktionen mit fünf Argumenten normal sind; die Regel wirkt dem entgegen, indem sie den Agenten zwingt, ein Options-Objekt zu verwenden, was ein besseres Design und leichter zu lesen ist.

ESLint ausführen

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

Verwenden Sie --max-warnings 0 in Ihrem CI-Schritt. Es stuft no-console-Warnungen von „technisch vermerkt" zu „tatsächlich blockierend" hoch.

Optional: Strengere Regeln für KI-generierte Dateien

Wenn Ihr Team eine Dateibenennungskonvention verwendet, um KI-generierten Code zu kennzeichnen (*.ai.ts, *-generated.ts oder ähnlich), können Sie speziell auf diese Dateien strengere Regeln anwenden:

// 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
  },
}

So konfigurieren Sie golangci-lint für Go-KI-Code

golangci-lint ist der Standard-Runner für mehrere Linter in Go. Es liefert gosec, errcheck, staticcheck und über 40 weitere Linter, die sich aus einer einzigen YAML-Datei konfigurieren lassen. Für KI-generierten Go-Code sind die kritischen Regeln die Prüfung von Fehlerrückgaben und die Erkennung von Sicherheitsmustern: die beiden Fehlerkategorien, die KI-Modelle in Go am konstantesten übersehen.

golangci-lint installieren

# 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

Die .golangci.yml-Konfiguration

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

Gos Fehlerbehandlungsmuster ist von Grund auf explizit: Jede Funktion, die fehlschlagen kann, gibt einen Fehlerwert zurück. KI-Modelle verstehen das, gewichten es aber zu gering; sie lassen Fehlerprüfungen in unkritischen Codepfaden weg. errcheck macht dieses Weglassen zu einem Lint-Fehler.

gosec ist der Sicherheits-Linter. Bei KI-Code fängt er die Muster ab, die KI aus Go-Tutorials vor 2020 übernommen hat: unsichere Zufallszahlengenerierung (G404), unsichere Hash-Funktionen (G401), Probleme mit Dateiberechtigungen (G306). Das sind die Fehler, die Sie im Code-Review nicht entdecken, weil sie syntaktisch normal aussehen.

golangci-lint ausführen

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

So binden Sie den Linter in Pre-Commit-Hooks ein

Ein Pre-Commit-Hook führt Lint vor jedem git commit aus und blockiert den Commit, wenn Lint fehlschlägt. Das bedeutet, der KI-Agent kann keinen Code committen, der Ihre konfigurierten Regeln verletzt. Er muss die Verstöße zuerst beheben.

Lefthook ist die empfohlene Option. Es ist plattformübergreifend, schnell und hat ein Konfigurationsmuster, das speziell mit der Durchsetzung gegenüber KI-Agenten funktioniert (im nächsten Abschnitt behandelt).

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.

Die fail_text-Nachricht wird von KI-Agenten gelesen, wenn ein Commit fehlschlägt. Das Muster ist dokumentiert in Liam Bigelows Beitrag zur Lefthook-Lint-Durchsetzung für Claude Code. Das allein hält einen entschlossenen Agenten nicht auf, aber es gibt ihm die richtige nächste Anweisung („Lint-Verstöße beheben"), statt es ihm zu überlassen, einen Workaround zu erschließen.

Alternative: pre-commit (Setups nur für Python)

Wenn Sie auf einem reinen Python-Stack sind und das pre-commit-Framework bevorzugen:

# .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 über den afterFileEdit-Hook

Wenn Sie Cursor verwenden, können Sie Lint sofort auslösen, wenn die KI eine Datei ändert, bevor sie überhaupt einen Commit versucht. Legen Sie .cursor/hooks.json im Stammverzeichnis Ihres Projekts an:

{
  "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}"
      }
    ]
  }
}

Das wird jedes Mal ausgelöst, wenn die KI von Cursor eine Datei ändert. Der Agent erhält Lint-Feedback inline, bevor er die Aufgabe als abgeschlossen behandelt, sodass die meisten Verstöße behoben werden, bevor sie das Pre-Commit-Gate erreichen.

So machen Sie das Gate für KI-Agenten schwerer umgehbar

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

Vier Durchsetzungsschichten machen es für einen KI-Agenten deutlich schwerer, das Pre-Commit-Gate mit --no-verify zu überspringen. Jede zielt auf eine andere Angriffsfläche: Richtlinie in CLAUDE.md, eine Deny-Regel von Claude Code, ein PreToolUse-Hook und ein CI-Sicherheitsnetz. Behandeln Sie sie als gestaffeltes Setup, nicht als vier unabhängige Garantien.

KI-Agenten übergeben manchmal --no-verify an git commit, um Pre-Commit-Hooks zu überspringen, wenn Lint fehlschlägt und der Agent entschieden hat, dass die Fehler „nichts mit seinen Änderungen zu tun haben". Die Entscheidung ist nicht immer falsch, aber Sie sollten den Agenten sie nicht im Alleingang treffen lassen. Der ganze Sinn des Lint-Gates ist, dass ein Mensch die Richtlinie festgelegt hat; die Aufgabe des Agenten ist es, sie zu erfüllen, nicht sie zu umgehen.

Hier sind die einzelnen Schichten und die Angriffsfläche, die sie abdecken.

Schicht 1: Die Richtlinie in CLAUDE.md dokumentieren

## 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 liest CLAUDE.md beim Sitzungsstart. Das ist keine Durchsetzung; der Agent kann die Umgehung weiterhin versuchen. Aber es entfernt den „Ich wusste es nicht"-Pfad und legt eine klare Richtlinie fest, gegen die der Agent aktiv verstoßen müsste, was er seltener tut, als aus Schweigen einen Workaround zu erschließen.

Schicht 2: Claude-Code-Deny-Regel

Zu .claude/settings.json hinzufügen:

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

Das blockiert den expliziten Aufruf. Eine Einschränkung: Die Deny-Regel verwendet Präfix-Matching, fängt also --no-verify nur direkt nach commit ab. Ein hinreichend kreativer Agent könnte den Aufruf anders strukturieren. Verlassen Sie sich nicht allein darauf.

Schicht 3: PreToolUse-Hook

Installieren Sie das block-no-verify-Paket:

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

Konfigurieren Sie es dann als PreToolUse-Hook in den Einstellungen von Claude Code. Das wird vor jedem Tool-Aufruf ausgelöst und untersucht die Argumente auf --no-verify über sechs git-Unterbefehle hinweg, nicht nur commit. Es beendet sich mit einem Exit-Code ungleich null, um den Aufruf vor der Ausführung zu blockieren.

Das Handbuch unter pydevtools.com ist unmissverständlich: „Die Hook-Schicht ist die einzige, die die Regel zuverlässig durchsetzt." Verwenden Sie sie zusammen mit den anderen Schichten; behandeln Sie keinen lokalen Hook als die gesamte Garantie.

Schicht 4: CI-Sicherheitsnetz

CI läuft auf dem Server, wo der Agent keine Shell hat, um Flags hineinzureichen:

# .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

KI-Agenten können --no-verify nicht an CI übergeben. GitHub Actions läuft unabhängig davon, was der Agent lokal getan hat. Wenn ein Commit mit fehlschlagendem Lint irgendwie durchgekommen ist, fängt CI ihn ab, bevor er gemergt wird.

Das ist das letzte Sicherheitsnetz.

Bonus: ESLint-MCP-Server

Wenn Sie Claude Code verwenden, gibt es eine proaktive Schicht, die reduziert, wie oft Sie überhaupt an das Gate stoßen.

Der ESLint-MCP-Server (@eslint/mcp) integriert ESLint direkt in den Tool-Loop des Agenten. Der Agent kann ESLint während der Aufgabe abfragen, bevor er einen Commit versucht. Installieren Sie ihn global:

npm install -g @eslint/mcp@latest

Zu .claude/settings.json hinzufügen:

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

Mit dieser Konfiguration kann der Agent ESLint während der Aufgabe abfragen. Der Agent erhält Lint-Feedback inline, und manche Verstöße können behoben werden, bevor sie den Hook erreichen. Das ersetzt das Gate nicht, es reduziert das Rauschen am Gate.

Häufig gestellte Fragen

Muss ich alle drei Linter konfigurieren, wenn ich nur in einer Sprache arbeite?

Nein. Richten Sie den Linter für Ihre Hauptsprache und die Durchsetzungsschicht ein. Wenn Ihr Stack nur TypeScript ist, konfigurieren Sie ESLint und überspringen Sie Ruff und golangci-lint. Der Abschnitt zur Verhinderung von Agenten-Umgehungen gilt unabhängig davon, welche Sprache Sie linten.

Werden diese Konfigurationen meine bestehende Codebasis beim ersten Durchlauf kaputtmachen?

Mit ziemlicher Sicherheit, und mit Absicht. Führen Sie zuerst ruff check --fix . oder npx eslint . --fix aus, um die sicheren Verstöße automatisch zu korrigieren. Was nach dem Auto-Fix übrig bleibt, ist die Liste für die manuelle Prüfung: no-explicit-any-Casts, Funktionen, die 50 Zeilen überschreiten, fehlende Fehlerbehandlung. Arbeiten Sie diese nach und nach ab. Fügen Sie keine ignore-Regeln hinzu, um sich nicht damit befassen zu müssen.

Ist Biome an diesem Punkt ein Ersatz für ESLint?

Biome v2.5.0 (veröffentlicht im Juni 2026) ist bei Formatierung und grundlegenden Lint-Regeln konkurrenzfähig. Es ist schneller als ESLint und hat null Konfigurationsaufwand, wenn Sie es über bun x ultracite@latest init installieren. Für Teams, die ein einziges Tool wollen und nicht die volle Regeltiefe von @typescript-eslint brauchen, ist Biome eine vernünftige Wahl. Für die KI-spezifischen Regeln in diesem Leitfaden (max-params, no-magic-numbers, max-lines-per-function mit auf KI abgestimmten Schwellenwerten) hat ESLint mit @typescript-eslint immer noch mehr Abdeckung. Sie können beide nutzen: Biome für Formatierung und grundlegendes Lint, ESLint für die KI-spezifischen Regeln.

Was, wenn mein KI-Agent immer wieder dieselben Lint-Verstöße neu erzeugt?

Der Agent umgeht die Regel, statt das zugrunde liegende Problem zu beheben. Bei no-explicit-any bedeutet das, eine Typzusicherung hinzuzufügen, statt den tatsächlichen Typ zu definieren. Bei max-lines-per-function bedeutet es, eine Hilfsfunktion zu extrahieren, die nichts Nützliches tut, aber die Zeilenzahl unter den Schwellenwert bringt. Keine der beiden Lösungen besteht das Code-Review. Die Lint-Regel hat das Symptom abgefangen; die Grundursache ist der Prompt des Agenten. Verfeinern Sie den Prompt, um die Typbeschränkung oder die erwartete Zerlegung anzugeben; der Agent folgt expliziter Strukturanleitung zuverlässiger als impliziten Regeln. Wenn Sie ein aufwendigeres Agenten-Setup betreiben, hält das Eingrenzen der Arbeit auf einen dedizierten Subagenten mit in seinen Anweisungen verankerten Beschränkungen tendenziell besser als ein einzelner breiter Prompt.

Ersetzt der ESLint-MCP-Server das Pre-Commit-Gate?

Nein. Er reduziert, wie oft der Agent Code erzeugt, der am Gate scheitert. Das Gate läuft weiterhin bei jedem Commit. Die Inline-Prüfung des MCP-Servers und die Durchsetzung durch den Pre-Commit-Hook ergänzen sich, also entfernen Sie keines von beiden.

Share

Mehr aus dem Blog

Weiterlesen.

Bereit zum Deployen? Ab 2,48 $/Monat.

Unabhängige Cloud, seit 2008. AMD EPYC, NVMe, 40 Gbps. 14 Tage Geld-zurück-Garantie.