PR yang Sama, Dua Review yang Berbeda

Seorang developer di jaringan saya menyiapkan Claude Code sebagai code reviewer di CI miliknya. “Minta saja Claude mengecek PR-nya,” katanya. “Dia menangkap hal-hal yang mungkin saya lewatkan.” Saya memintanya menjalankan PR yang sama melalui Claude dua kali.

Review pertama mengatakan error handling-nya sudah komprehensif. Review kedua menandainya sebagai tidak memadai dan menyarankan tiga perubahan. Dia menatap layarnya selama lima menit, mencoba memahami Claude yang mana yang benar.

Tidak ada yang benar. Tidak ada yang salah. LLM itu sedang membuat tebakan probabilistik, bukan menerapkan rule yang deterministic. Dan di situlah masalah fundamental saat memakai AI untuk verifikasi: verifikasi membutuhkan dua sifat yang tidak dimiliki LLM. Determinism - input yang sama selalu menghasilkan output yang sama. Completeness - semua failure mode tertangkap, setiap saat.

Masalah Determinism

Jalankan kode yang sama melalui Claude Code dua kali dan Anda bisa mendapat dua review yang berbeda. Temperature, context window, phrasing prompt - semuanya menambah variansi. Untuk brainstorming, variansi adalah fitur. Untuk verifikasi, variansi adalah bug. Anda tidak bisa membangun software yang andal di atas quality gate yang probabilistik.

Deterministic checks tidak punya masalah ini. tsc --noEmit selalu menghasilkan error yang sama. ESLint dengan config yang sama selalu menandai issue yang sama. Tools ini menerapkan rules, bukan probabilitas. Mereka membosankan. Itulah alasan mereka bekerja.

Stack Guard O(1)

Inilah bentuk stack pre-commit kami. Setiap check deterministic. Setiap check berjalan dalam hitungan milidetik. Tidak ada satu pun yang peduli apakah kode itu ditulis manusia atau Claude Code:

# Pre-commit (< 1 second total)
TypeScript strict mode      # tsc --noEmit
ESLint boundary rules       # module isolation
Import restrictions         # dependency direction
dependency-cruiser          # graph analysis
Unit tests                  # contract verification

# CI (< 30 seconds)
Full test suite
Bundle size checks
Fresh clone launch test

Metrik kuncinya adalah O(1) relatif terhadap ukuran codebase. Type checking bersifat incremental. Linting bersifat per-file. dependency-cruiser menganalisis import graph secara linear. Tidak ada yang melambat saat aplikasi hasil vibe coding Anda tumbuh. Claude Code review justru makin lambat dan makin tidak akurat saat codebase Anda melebihi context window.

Apa yang Sebenarnya Ditangkap Deterministic Checks

Minggu lalu, dependency-cruiser menangkap sebuah PR di mana screen component mengimpor langsung dari implementasi service, bukan dari barrel. PR itu compile tanpa masalah. Test lulus. Cursor menghasilkan import itu secara otomatis. Tapi ia melanggar rule arsitektur kami: hanya composition root yang boleh mengimpor implementasi.

LLM review mungkin bisa menangkap ini. Atau mungkin melewatkannya karena PR-nya besar dan import tersebut terkubur di tengah file. dependency-cruiser tidak pernah melewatkannya karena tool ini tidak sedang membaca kode - ia sedang menganalisis graph:

// .dependency-cruiser.cjs
// 8 lines. 100ms. Catches this every time, forever.
{
  name: 'no-deep-service-imports',
  comment: 'Only service barrels are public.',
  severity: 'error',
  from: { pathNot: '^src/services/' },
  to: {
    path: '^src/services/[^/]+/',
    pathNot: '^src/services/[^/]+/index\.ts$',
  }
}

Delapan baris. Di bawah seratus milidetik. Setiap PR. Selamanya. Claude Code review menghabiskan token, butuh beberapa detik, dan mungkin tetap melewatkannya. Perhitungannya tidak dekat sama sekali.

Di Mana AI Sebenarnya Cocok

Saya bukan anti-AI. Saya memakai Cursor dan Claude Code setiap hari - untuk drafting code, mengeksplorasi pendekatan, menulis test, dan debugging. Tapi saya tidak menaruh mereka pada peran verifikasi. Verifikasi harus deterministic. Harus membosankan. Harus sama, setiap saat.

Guardrails seharusnya tidak terlihat tapi tidak terhindarkan. Mereka harus menangkap masalah tanpa siapa pun perlu ingat untuk menjalankannya. Itulah yang diberikan deterministic checks: jaring pengaman yang tidak pernah tidur, tidak pernah lelah, dan tidak pernah kehilangan konteks saat codebase Anda tumbuh.

Autotomy Expo Starter Pack sudah menyertakan TypeScript strict mode, ESLint boundary rules, konfigurasi dependency-cruiser, dan pre-commit hooks - semuanya sudah dikonfigurasi. Anda mendapat kecepatan Cursor untuk generasi kode. Anda mendapat presisi mesin untuk verifikasi. Begitulah cara melakukan vibe coding tanpa merusak production.