Lewati ke konten utama
diskon 50% semua paket, waktu terbatas. Mulai dari $2.48/mo
14 min left
Developer Tools dan DevOps

Cara Menyiapkan Linter untuk Kode yang Dihasilkan AI

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

Agen AI bisa menghasilkan kode yang ter-compile.

Itu masih belum menjadi standar yang Anda inginkan. Standarnya adalah kode yang tidak meng-import hal-hal yang tidak pernah dipakai, tidak meng-any-cast untuk keluar dari sistem tipe, tidak mengabaikan return error, dan tidak melakukan hardcode kredensial yang seharusnya ditangkap gosec sebelum review.

Model AI dilatih dari jawaban Stack Overflow lama dan mengeluarkan pola yang dipelajari di sana, termasuk API yang sudah usang, anotasi tipe yang hilang, dan fungsi yang secara teknis benar tetapi terlalu besar untuk di-review dengan aman. Anda membutuhkan linter dalam loop. Bukan sebagai saran, tetapi sebagai gerbang.

Panduan ini mencakup konfigurasi untuk tiga ekosistem (Python dengan Ruff, TypeScript/JavaScript dengan ESLint v10 flat config, dan Go dengan golangci-lint) dengan aturan yang disetel khusus untuk pola kegagalan yang diperkenalkan AI. Lalu panduan ini membahas cara membuat gerbang jauh lebih sulit dilewati, sehingga agen tidak bisa begitu saja melewati hook lokal dengan --no-verify tanpa ada lapisan lain yang menangkapnya.

Setup-nya berlapis: linting di tingkat IDE menangkap masalah secara inline saat agen menulis, hook pre-commit menangkap apa pun yang sampai ke percobaan commit, dan CI menangkap apa pun yang lolos secara lokal. Setiap lapisan bersifat independen, dan Anda bisa memilih bagian bahasa yang sesuai dengan stack Anda. Lapisan penegakannya bekerja sama saja terlepas dari bahasa mana yang Anda lint.

TL;DR

  • Ruff (Python), ESLint v10 (TS/JS), dan golangci-lint (Go) masing-masing memiliki aturan spesifik yang menangkap kegagalan paling umum dari AI
  • Konfigurasi di bawah ini diberi anotasi; setiap aturan ada karena suatu alasan
  • Lefthook menangani gerbang pre-commit; hook afterFileEdit milik Cursor menjalankan lint secara inline
  • Empat lapisan penegakan membuatnya jauh lebih sulit bagi agen AI untuk melewati gerbang dengan --no-verify
  • CI adalah jaring pengaman terakhir: agen tidak bisa meneruskan --no-verify ke GitHub Actions

Cara Mengonfigurasi Ruff untuk Kode AI Python

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

Ruff adalah linter Python yang tepat untuk basis kode yang dibantu AI. Ia cukup cepat untuk dijalankan setiap kali file disimpan tanpa memblokir apa pun, mencakup baik gaya maupun error logika yang nyata, dan menyertakan perilaku formatter (menggantikan Black) dalam satu binary yang sama. Aturan di bawah ini menargetkan pola kegagalan spesifik yang paling sering diperkenalkan model AI di Python.

Menginstal Ruff

pip install ruff

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

Hanya itu. Tidak ada ekosistem plugin, tidak ada negosiasi peer dependency.

Konfigurasi pyproject.toml

[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

Aturan F langsung membayar dirinya sendiri. Kode AI menghasilkan pernyataan import untuk paket yang akhirnya tidak dipakai, dan F401 (import yang tidak terpakai) menangkap setiap satunya. Aturan UP menangkap panggilan ke pola API usang yang dipelajari AI dari jawaban Python sebelum 3.10; UP006 dan UP007 saja menandai puluhan pola pemeriksaan tipe yang tidak perlu. Aturan S (Bandit) menangkap kegagalan keamanan: string yang di-hardcode yang terlihat seperti kredensial (S105/S106), shell injection lewat subprocess(shell=True) (S603/S607), pilihan kriptografi yang lemah (S324).

ignore = [] memang disengaja. Setiap pengecualian yang Anda tambahkan ke daftar ini adalah satu kelas kegagalan AI yang Anda putuskan untuk diizinkan.

Menjalankan 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 menerapkan perbaikan aman Ruff secara default, seperti menghapus import yang tidak terpakai atau menerapkan pemformatan dan koreksi lint yang sederhana. Ruff juga memiliki perbaikan tidak aman, tetapi itu memerlukan opt-in eksplisit dan harus di-review lebih hati-hati. Review secara manual apa pun yang tidak bisa diperbaiki Ruff dengan aman.

Cara Mengonfigurasi ESLint v10 untuk Kode AI TypeScript dan JavaScript

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

ESLint v10 menghapus format konfigurasi .eslintrc.* yang lama. Semuanya kini menjadi flat config di eslint.config.mjs. Jika Anda menemukan tutorial yang memakai .eslintrc.json atau .eslintrc.js, tutorial itu menargetkan ESLint v8 atau v9, di mana sintaksnya berbeda. Gunakan yang ada di bawah ini.

Aturan @typescript-eslint dalam konfigurasi ini menargetkan mode kegagalan spesifik yang muncul berulang kali dalam TypeScript yang dihasilkan AI: jalan keluar any, fungsi monolitik yang sulit di-review, dan nilai yang di-hardcode yang seharusnya berupa konstanta.

Menginstal ESLint v10 dengan Dukungan TypeScript

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 adalah versi terkini per Juni 2026. Paket @typescript-eslint harus cocok dengan versi TypeScript Anda; periksa README mereka untuk matriks kompatibilitasnya.

Konfigurasi eslint.config.mjs

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

Aturan max-lines-per-function: 50 adalah hal paling agresif dalam konfigurasi ini. Anda akan terus terkena aturan ini pada run pertama di basis kode yang dibantu AI. Itulah maksudnya. Anda memang seharusnya terkena. Fungsi yang melebihi 50 baris adalah hal pertama yang menjadi mustahil dipahami ketika Anda me-review output AI dalam jumlah besar.

Aturan max-params: 2 memaksa dekomposisi. Model AI belajar dari basis kode di mana fungsi berargumen lima itu normal; aturan ini mendorong balik dengan mengharuskan agen memakai objek opsi, yang merupakan desain lebih baik dan lebih mudah dibaca.

Menjalankan ESLint

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

Gunakan --max-warnings 0 dalam langkah CI Anda. Ini menaikkan peringatan no-console dari "sekadar dicatat" menjadi "benar-benar memblokir."

Opsional: Aturan Lebih Ketat untuk File yang Dihasilkan AI

Jika tim Anda memakai konvensi penamaan file untuk menandai kode yang dihasilkan AI (*.ai.ts, *-generated.ts, atau sejenisnya), Anda bisa menerapkan aturan lebih ketat khusus pada file-file tersebut:

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

Cara Mengonfigurasi golangci-lint untuk Kode AI Go

golangci-lint adalah runner multi-linter standar untuk Go. Ia menyertakan gosec, errcheck, staticcheck, dan 40+ linter lain yang bisa dikonfigurasi dari satu file YAML. Untuk kode Go yang dihasilkan AI, aturan paling kritis adalah pemeriksaan return error dan deteksi pola keamanan: dua kategori kegagalan yang paling konsisten dilewatkan model AI di Go.

Menginstal 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

Konfigurasi .golangci.yml

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

Pola penanganan error Go bersifat eksplisit by design: setiap fungsi yang bisa gagal mengembalikan nilai error. Model AI memahami ini tetapi kurang menimbangnya; mereka akan menghilangkan pemeriksaan error di jalur kode yang tidak kritis. errcheck menjadikan kelalaian itu sebagai kegagalan lint.

gosec adalah linter keamanan. Untuk kode AI, ia menangkap pola yang dipungut AI dari tutorial Go sebelum 2020: pembangkitan angka acak yang tidak aman (G404), fungsi hash yang tidak aman (G401), masalah izin file (G306). Inilah kesalahan yang tidak Anda tangkap dalam code review karena terlihat normal secara sintaksis.

Menjalankan golangci-lint

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

Cara Menyambungkan Linter ke Hook Pre-commit

Hook pre-commit menjalankan lint sebelum setiap git commit dan memblokir commit jika lint gagal. Ini berarti agen AI tidak bisa meng-commit kode yang gagal pada aturan yang Anda konfigurasi. Ia harus memperbaiki pelanggarannya terlebih dahulu.

Lefthook adalah opsi yang direkomendasikan. Ia lintas platform, cepat, dan memiliki pola konfigurasi yang bekerja khusus dengan penegakan agen AI (dibahas di bagian berikutnya).

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.

Pesan fail_text dibaca oleh agen AI ketika sebuah commit gagal. Polanya didokumentasikan di tulisan Liam Bigelow tentang penegakan lint Lefthook untuk Claude Code. Ini saja tidak akan menghentikan agen yang bertekad, tetapi memberinya instruksi berikutnya yang benar ("perbaiki pelanggaran lint") alih-alih membiarkannya menyimpulkan jalan pintas.

Alternatif: pre-commit (setup khusus Python)

Jika Anda berada di stack khusus Python dan lebih suka framework pre-commit:

# .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: Linting Inline lewat Hook afterFileEdit

Jika Anda memakai Cursor, Anda bisa memicu lint segera saat AI memodifikasi sebuah file, sebelum ia bahkan mencoba commit. Buat .cursor/hooks.json di root proyek Anda:

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

Ini terpicu setiap kali AI Cursor memodifikasi sebuah file. Agen mendapat umpan balik lint secara inline, sebelum ia menganggap tugasnya selesai, sehingga sebagian besar pelanggaran sudah diperbaiki sebelum mencapai gerbang pre-commit.

Cara Membuat Gerbang Lebih Sulit Dilewati oleh Agen AI

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

Empat lapisan penegakan membuatnya jauh lebih sulit bagi agen AI untuk melewati gerbang pre-commit dengan --no-verify. Masing-masing menargetkan permukaan serangan yang berbeda: kebijakan di CLAUDE.md, aturan deny Claude Code, hook PreToolUse, dan jaring pengaman CI. Perlakukan keempatnya sebagai setup berlapis, bukan sebagai empat jaminan yang independen.

Agen AI terkadang meneruskan --no-verify ke git commit untuk melewati hook pre-commit ketika lint gagal dan agen telah memutuskan bahwa kegagalan itu "tidak terkait dengan perubahannya." Keputusan itu tidak selalu salah, tetapi Anda tidak boleh membiarkan agen membuatnya secara sepihak. Inti dari gerbang lint adalah bahwa seorang manusia menetapkan kebijakannya; tugas agen adalah memenuhinya, bukan memutarinya.

Berikut setiap lapisan dan permukaan serangan yang dicakupnya.

Lapisan 1: Dokumentasikan Kebijakan di 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 membaca CLAUDE.md saat sesi dimulai. Ini bukan penegakan; agen masih bisa mencoba melewatinya. Tetapi ini menghilangkan jalan "saya tidak tahu" dan menetapkan kebijakan yang jelas yang harus dilanggar agen secara aktif, yang lebih kecil kemungkinannya dilakukan daripada menyimpulkan jalan pintas dari kebisuan.

Lapisan 2: Aturan Deny Claude Code

Tambahkan ke .claude/settings.json:

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

Ini memblokir pemanggilan eksplisit. Satu keterbatasan: aturan deny memakai pencocokan prefiks, sehingga ia hanya menangkap --no-verify yang langsung berada setelah commit. Agen yang cukup kreatif bisa menyusun panggilannya secara berbeda. Jangan mengandalkan ini saja.

Lapisan 3: Hook PreToolUse

Instal paket block-no-verify:

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

Lalu konfigurasikan sebagai hook PreToolUse di pengaturan Claude Code. Ini terpicu sebelum setiap panggilan tool dan memeriksa argumennya untuk --no-verify di enam subperintah git, bukan hanya commit. Ia keluar dengan kode non-nol untuk memblokir panggilan sebelum dieksekusi.

Buku panduan di pydevtools.com berkata tegas: "lapisan hook adalah satu-satunya yang menegakkan aturan secara andal." Gunakan ini bersama lapisan lain; jangan perlakukan hook lokal mana pun sebagai keseluruhan jaminan.

Lapisan 4: Jaring Pengaman CI

CI berjalan di server, di mana agen tidak memiliki shell untuk meneruskan flag ke dalamnya:

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

Agen AI tidak bisa meneruskan --no-verify ke CI. GitHub Actions berjalan independen dari apa pun yang dilakukan agen secara lokal. Jika sebuah commit entah bagaimana lolos dengan lint yang gagal, CI menangkapnya sebelum di-merge.

Ini adalah jaring pengaman terakhir.

Bonus: Server MCP ESLint

Jika Anda memakai Claude Code, ada lapisan proaktif yang mengurangi seberapa sering Anda terkena gerbang sama sekali.

Server MCP ESLint (@eslint/mcp) mengintegrasikan ESLint langsung ke dalam loop tool agen. Agen bisa meng-query ESLint selama tugas berlangsung, sebelum mencoba commit. Instal secara global:

npm install -g @eslint/mcp@latest

Tambahkan ke .claude/settings.json:

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

Dengan ini dikonfigurasi, agen bisa meng-query ESLint selama tugas berlangsung. Agen mendapat umpan balik lint secara inline, dan sebagian pelanggaran bisa dikoreksi sebelum mencapai hook. Ini tidak menggantikan gerbang, ini mengurangi gangguan pada gerbang.

Pertanyaan yang Sering Diajukan

Apakah saya perlu mengonfigurasi ketiga linter jika saya hanya bekerja di satu bahasa?

Tidak. Siapkan linter untuk bahasa utama Anda dan lapisan penegakannya. Jika stack Anda hanya TypeScript, konfigurasikan ESLint dan lewati Ruff serta golangci-lint. Bagian pencegahan bypass agen berlaku terlepas dari bahasa mana yang Anda lint.

Apakah konfigurasi ini akan merusak basis kode saya yang sudah ada pada run pertama?

Hampir pasti, dan memang disengaja. Jalankan ruff check --fix . atau npx eslint . --fix untuk mengoreksi otomatis pelanggaran yang aman terlebih dahulu. Yang tersisa setelah perbaikan otomatis adalah daftar review manual: cast no-explicit-any, fungsi yang melebihi 50 baris, penanganan error yang hilang. Kerjakan itu secara bertahap. Jangan menambahkan aturan ignore untuk menghindari menanganinya.

Apakah Biome merupakan pengganti ESLint saat ini?

Biome v2.5.0 (dirilis Juni 2026) kompetitif dalam pemformatan dan aturan lint dasar. Ia lebih cepat dari ESLint dan tidak memiliki overhead konfigurasi jika Anda menginstalnya lewat bun x ultracite@latest init. Untuk tim yang menginginkan satu alat dan tidak membutuhkan kedalaman aturan @typescript-eslint penuh, Biome adalah pilihan yang masuk akal. Untuk aturan spesifik AI dalam panduan ini (max-params, no-magic-numbers, max-lines-per-function dengan ambang yang ditargetkan untuk AI), ESLint dengan @typescript-eslint masih memiliki cakupan lebih banyak. Anda bisa menjalankan keduanya: Biome untuk pemformatan dan lint dasar, ESLint untuk aturan spesifik AI.

Bagaimana jika agen AI saya terus menghasilkan ulang pelanggaran lint yang sama?

Agen sedang memutari aturan alih-alih memperbaiki masalah yang mendasarinya. Untuk no-explicit-any, ini berarti menambahkan type assertion alih-alih mendefinisikan tipe yang sebenarnya. Untuk max-lines-per-function, ini berarti mengekstrak fungsi helper yang tidak melakukan hal berguna tetapi membuat jumlah baris di bawah ambang. Tidak satu pun solusi itu lolos code review. Aturan lint menangkap gejalanya; akar masalahnya adalah prompt agen. Perbaiki prompt untuk menentukan batasan tipe atau dekomposisi yang diharapkan; agen akan mengikuti panduan struktur eksplisit lebih andal daripada aturan implisit. Jika Anda menjalankan setup agen yang lebih rumit, membatasi pekerjaan ke subagen khusus dengan batasan yang ditanamkan ke dalam instruksinya cenderung bertahan lebih baik daripada satu prompt luas.

Apakah server MCP ESLint menggantikan gerbang pre-commit?

Tidak. Ia mengurangi seberapa sering agen menghasilkan kode yang gagal di gerbang. Gerbang tetap berjalan pada setiap commit. Pemeriksaan inline server MCP dan penegakan hook pre-commit saling melengkapi, jadi jangan menghapus salah satunya.

Share

Lebih banyak dari blog

Lanjutkan membaca.

Siap deploy? Mulai $2,48/bln.

Cloud independen, sejak 2008. AMD EPYC, NVMe, 40 Gbps. Garansi uang kembali 14 hari.