Vibe Coding dengan Test-Driven Rigor: Mengendalikan Claude 3.7 Sonnet Agar Tidak Menghasilkan Kode Halusinasi
Ada ilusi memabukkan yang menyebar cepat di kalangan developer belakangan ini. Seseorang duduk di depan editor, mengetikkan beberapa kalimat santai dalam bahasa manusia ke terminal, menyeruput kopi, dan menyaksikan ribuan baris kode terurai sendiri seperti sihir. Andrej Karpathy menyebut fenomena ini sebagai vibe coding—sebuah kondisi ketika programmer tidak lagi bergulat dengan sintaksis mikro, melainkan membiarkan insting dan model bahasa besar (LLM) mengambil alih roda kemudi.
Namun, siapa pun yang pernah menerjunkan hasil pure vibe coding ke server produksi tahu persis bagaimana babak keduanya berakhir. Pukul dua pagi, sebuah silent bug meledak di lapisan edge case: race condition pada database transaksi, payload JSON bersarang yang tiba-tiba mengembalikan null, atau pemanggilan pustaka usang yang tampak meyakinkan namun sebenarnya fiktif belaka.
Kehadiran Claude 3.7 Sonnet dengan arsitektur hybrid reasoning mempertajam pisau ini dari dua sisi. Di satu sisi, kemampuan extended thinking-nya mampu membedah logika rumit yang sebelumnya gagal dipahami model generasi pendahulu. Di sisi lain, ketika model secerdas ini berhalusinasi, halusinasinya tidak lagi tampak konyol; ia terdengar akademis, terstruktur, dan sangat persuasif.
Jika kita ingin mempertahankan kecepatan luar biasa dari vibe coding tanpa mengorbankan integritas sistem, kita membutuhkan jangkar. Jangkar tersebut adalah disiplin rekayasa perangkat lunak paling teruji yang ironisnya sering diabaikan: Test-Driven Rigor.
Anatomi Halusinasi Tingkat Tinggi pada Model Penalaran
Model penalaran modern tidak lagi sekadar memprediksi kata berikutnya secara serampangan. Claude 3.7 Sonnet mengalokasikan ribuan thinking tokens untuk menimbang arsitektur sebelum memuntahkan baris kode pertama. Namun, mengapa kode yang dihasilkan tetap bisa meleset dari spesifikasi bisnis?
[ ALUR NAIF: VIBE CODING TANPA JANGKAR ]
Prompt Abstrak ──> Claude 3.7 Sonnet ──> 500 Baris Kode Boilerplate
│
▼
Debug Manual di Runtime(Membingungkan)
[ ALUR RIGOROUS: SPEC-FIRST AGENTIC LOOP ]
Spesifikasi Ketat ──> Unit Test Assertion ──> Claude 3.7 ──> Test Runner
▲ │
│ (Auto-Correction Loop) │
└───────────────────────────────────────────────────────────┴── PASSED!Akar masalahnya terletak pada ambiguitas semantik. Ketika Anda memberi instruksi: "Buat endpoint webhook untuk memproses callback pembayaran dengan aman," model harus menebak ratusan parameter implisit:
Apa skema enkripsi signature-nya? (HMAC-SHA256, RSA?)
Bagaimana strategi mitigasi replay attack?
Apakah status mutasi database bersifat idempoten?
Tanpa batasan deterministik, model akan memilih jalur probabilitas tertinggi berdasarkan data pelatihannya. Di sinilah letak jebakannya. Model mungkin menghasilkan kode elegan yang lolos inspeksi visual sepintas, tetapi gagal total saat berhadapan dengan kegagalan jaringan riil.
Paradigma Spec-First: Memasang Rel Sebelum Menjalankan Kereta
Solusi untuk menjinakkan keliaran LLM bukan dengan menulis prompt sepanjang novel. Solusinya adalah membalik urutan kerja: Tuliskan kontrak pengujian (test assertion) terlebih dahulu, baru biarkan LLM mengimplementasikan kodenya.
Dalam alur spec-first agentic coding, peran manusia bergeser dari sekadar "pengetik kode" menjadi "pemberi batasan sistem" (system boundary designer). Kita mendefinisikan apa yang boleh dan tidak boleh terjadi lewat assertion matematis yang kaku.
Ketika unit test dijalankan dan gagal (fase Red), output kegagalan test tersebut—lengkap dengan stack trace dan diff nilai yang diharapkan vs yang didapat—diumpankan kembali ke Claude 3.7 Sonnet. Model penalaran tidak lagi menebak apa yang salah; ia memiliki target matematis objektif yang harus diubah menjadi hijau (fase Green).
Komparasi Paradigma: Wild Vibe Coding vs. Spec-Driven Rigor
| Parameter Evaluasi | Wild Vibe Coding (Prompt-and-Pray) | Spec-Driven Agentic Rigor |
|---|---|---|
| Peran Utama Engineer | Korektor sintaksis pasif | Perancang arsitektur & contract assertion |
| Validasi Kebenaran | Manual click testing / Cek log visual | Deterministic Test Suite Runner (Automated) |
| Resistensi Edge-Case | Sangat rentan halusinasi tersembunyi | Tinggi; dibatasi oleh skema & fuzzing |
| Efisiensi Token | Boros karena iterasi debugging bolak-balik | Terukur; Claude fokus menyelesaikan error trace |
| Skalabilitas Basis Kode | Menjadi spaghetti code setelah 3-4 iterasi | Modular, terisolasi, dan mudah direfaktor |
Panduan Praktis: Membangun Handler Webhook Idempoten
Mari bedah studi kasus nyata. Kita akan membangun webhook handler untuk sistem pembayaran yang harus memenuhi tiga kriteria ketat: validasi tanda tangan HMAC, penolakan timestamp kadaluarsa (anti-replay), dan eksekusi mutasi saldo yang idempoten.
Langkah 1: Menulis Spesifikasi Kontrak dan Test Assertion
Sebelum menyentuh berkas implementasi paymentHandler.ts, kita buat terlebih dahulu berkas pengujian paymentHandler.spec.ts. Kita menggunakan TypeScript dan Vitest.
// tests/paymentHandler.spec.ts
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { processPaymentWebhook } from '../src/paymentHandler';
import { DatabaseClient } from '../src/db';
describe('Payment Webhook Handler - Contract Rigor', () => {
let mockDb: DatabaseClient;
const SECRET_KEY = 'whsec_test_secret_abc123';
beforeEach(() => {
mockDb = {
hasProcessed: vi.fn(),
saveTransaction: vi.fn(),
updateUserBalance: vi.fn(),
} as unknown as DatabaseClient;
});
it('harus menolak request jika signature HMAC-SHA256 tidak valid', async () => {
const payload = JSON.stringify({ eventId: 'evt_01', amount: 500000 });
const invalidSignature = 'sha256=invalid_hash';
const timestamp = Math.floor(Date.now() / 1000).toString();
const response = await processPaymentWebhook({
payload,
signature: invalidSignature,
timestamp,
secret: SECRET_KEY,
db: mockDb,
});
expect(response.status).toBe(401);
expect(response.message).toMatch(/invalid signature/i);
expect(mockDb.updateUserBalance).not.toHaveBeenCalled();
});
it('harus menolak request replay yang berumur lebih dari 300 detik', async () => {
const payload = JSON.stringify({ eventId: 'evt_02', amount: 150000 });
const expiredTimestamp = (Math.floor(Date.now() / 1000) - 301).toString();
const validSignature = 'sha256=mock_valid_signature';
const response = await processPaymentWebhook({
payload,
signature: validSignature,
timestamp: expiredTimestamp,
secret: SECRET_KEY,
db: mockDb,
});
expect(response.status).toBe(400);
expect(response.message).toMatch(/timestamp expired/i);
});
it('harus bersifat idempoten: mengembalikan 200 tanpa mutasi ulang jika eventId sudah pernah diproses', async () => {
const payload = JSON.stringify({ eventId: 'evt_duplicate_99', amount: 200000, userId: 'usr_1' });
const timestamp = Math.floor(Date.now() / 1000).toString();
// Simulasikan eventId sudah tercatat di database
vi.spyOn(mockDb, 'hasProcessed').mockResolvedValue(true);
const response = await processPaymentWebhook({
payload,
signature: 'VALID_COMPUTED_SIG',
timestamp,
secret: SECRET_KEY,
db: mockDb,
});
expect(response.status).toBe(200);
expect(response.idempotentSkip).toBe(true);
expect(mockDb.updateUserBalance).not.toHaveBeenCalled();
});
});Langkah 2: Mengonstruksi Agentic Prompt Berbasis Assertion
Kini kita memiliki tes yang pasti gagal (fase Red). Langkah berikutnya adalah memberikan kode tes ini dan deskripsi antarmuka (interface) ke Claude 3.7 Sonnet.
Prompt yang kita gunakan tidak lagi berbentuk bujuk rayu deskriptif yang panjang, melainkan instruksi operasional yang presisi:
SYSTEM:
Anda adalah backend core engineer spesialis sistem transaksi berkeandalan tinggi.
Tugas Anda: Tulis implementasi TypeScript murni untuk fungsi `processPaymentWebhook` di file `src/paymentHandler.ts`.
ATURAN REKAYASA:
1. Kode HARUS membuat seluruh unit test di `tests/paymentHandler.spec.ts` lulus tanpa memodifikasi file test.
2. Gunakan crypto module bawaan Node.js untuk verifikasi HMAC-SHA256(`timingSafeEqual` wajib digunakan untuk menghindari timing attack).
3. Jangan tambahkan library eksternal di luar apa yang dibutuhkan kontrak.
4. Lakukan verifikasi signature, pengecekan toleransi waktu(+- 300 detik), lalu cek idempotensi database secara berurutan.
BERKAS TEST:
[Salin kode paymentHandler.spec.ts di sini]Langkah 3: Mengamati Kode Hasil Implementasi
Claude 3.7 Sonnet, dipandu oleh thinking tokens, akan menelusuri setiap kegagalan logika yang mungkin timbul dan menghasilkan implementasi yang bersih:
// src/paymentHandler.ts
import crypto from 'node:crypto';
import { DatabaseClient } from './db';
interface WebhookInput {
payload: string;
signature: string;
timestamp: string;
secret: string;
db: DatabaseClient;
}
interface WebhookResponse {
status: number;
message: string;
idempotentSkip?: boolean;
}
export async function processPaymentWebhook(input: WebhookInput): Promise<WebhookResponse> {
const { payload, signature, timestamp, secret, db } = input;
// 1. Verifikasi Toleransi Waktu (Anti-Replay Attack)
const currentTimestamp = Math.floor(Date.now() / 1000);
const requestTimestamp = Number.parseInt(timestamp, 10);
if (Number.isNaN(requestTimestamp) || Math.abs(currentTimestamp - requestTimestamp) > 300) {
return { status: 400, message: 'Timestamp expired or invalid' };
}
// 2. Verifikasi HMAC-SHA256 dengan Timing-Safe Comparison
const signedPayload = `${timestamp}.${payload}`;
const computedHash = `sha256=${crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex')}`;
const expectedSigBuffer = Buffer.from(computedHash, 'utf8');
const actualSigBuffer = Buffer.from(signature, 'utf8');
if (
expectedSigBuffer.length !== actualSigBuffer.length ||
!crypto.timingSafeEqual(expectedSigBuffer, actualSigBuffer)
) {
return { status: 401, message: 'Invalid signature verification failed' };
}
// 3. Parsing Data & Pengecekan Idempotensi
const data = JSON.parse(payload);
const alreadyProcessed = await db.hasProcessed(data.eventId);
if (alreadyProcessed) {
return { status: 200, message: 'Event already processed', idempotentSkip: true };
}
// 4. Eksekusi Mutasi Bisnis
await db.saveTransaction(data.eventId, data.amount);
await db.updateUserBalance(data.userId, data.amount);
return { status: 200, message: 'Payment processed successfully' };
}Ketika test suite dijalankan ulang, Vitest langsung menampilkan lampu hijau menyala:
✓ tests/paymentHandler.spec.ts(3 tests) 14ms
✓ Payment Webhook Handler - Contract Rigor > harus menolak request jika signature HMAC-SHA256 tidak valid
✓ Payment Webhook Handler - Contract Rigor > harus menolak request replay yang berumur lebih dari 300 detik
✓ Payment Webhook Handler - Contract Rigor > harus bersifat idempoten: mengembalikan 200 tanpa mutasi ulang jika eventId sudah pernah diproses
Test Files 1 passed(1)
Tests 3 passed(3)
Duration 218msJika terjadi kegagalan—misalnya Claude lupa memformat string signedPayload sesuai konvensi—kita cukup melempar log kegagalan terminal kembali ke agent. Model akan memperbaiki kodenya secara otonom dalam hitungan detik.
Orkestrasi Tanpa Friksi Melalui AiStudio.id API Gateway
Mengoperasikan alur agentic coding yang mengandalkan siklus eksekusi berulang membutuhkan infrastruktur perutean LLM yang tangguh. Ketika satu proses pengembangan melibatkan puluhan iterasi perbaikan kode secara otomatis, hambatan latensi dan kerumitan manajemen kuota token dapat memperlambat ritme kerja secara signifikan.
Dalam skenario inilah kehadiran AiStudio.id API Gateway menjadi solusi praktis bagi para developer dan tim rekayasa perangkat lunak.
[ Local IDE / Agent CLI ]
│
▼
[ AiStudio.id Unified API Gateway ]
├── Smart Load Balancing & Lowest Latency Routing
├── Unified OpenAI/Anthropic Compatible Schema
└── Dynamic Model Fallback(Claude 3.7 Sonnet / DeepSeek / Gemini)
│
▼
[ Frontier LLM Inference Engines ]Integrasi ekosistem AiStudio.id menyederhanakan alur kerja ini melalui beberapa nilai tambah mendasar:
- Akses Terpadu Multi-Model: Tanpa perlu mengubah arsitektur integrasi di lingkungan lokal atau skrip agent CLI, pengembang dapat berganti antara Claude 3.7 Sonnet untuk penalaran tingkat tinggi atau model lain saat hanya butuh autokompleksi sederhana.
- Kestabilan dan Latensi Teroptimasi: Infrastruktur gateway memastikan permintaan inferensi dirutekan secara efisien, meminimalkan cold-start saat agent menjalankan siklus self-healing berulang kali.
- Efisiensi Anggaran Pengembangan: Alih-alih mengelola banyak akun penagihan lintas penyedia AI global yang terfragmentasi, gateway menyatukan seluruh konsumsi token ke dalam satu sistem kendali yang transparan dan mudah dipantau.
Menggunakan antarmuka API yang kompatibel, menyematkan Claude 3.7 Sonnet ke dalam automated test watcher lokal menjadi sesederhana mengarahkan endpoint konfigurasi dasar:
# Integrasi sederhana pada environment testing lokal
export ANTHROPIC_BASE_URL="https://api.aistudio.id/v1"
export ANTHROPIC_API_KEY="sk-aistudio-direct-access-token"Paradoks Produktivitas: Mengapa Batasan Justru Melipatgandakan Kecepatan
Ada sebuah paradoks menarik dalam rekayasa perangkat lunak: kebebasan tanpa batas hampir selalu menghasilkan kelambatan jangka panjang. Ketika kita meminta LLM menghasilkan aplikasi lengkap dari nol hanya dengan satu instruksi teks bebas, kita menghemat lima menit di awal, namun membuang lima jam berikutnya untuk menelusuri bug yang tidak terlihat.
Menulis unit test di awal terasa seperti beban tambahan bagi mereka yang terbiasa dengan gaya vibe coding murni. Namun data empiris menunjukkan sebaliknya:
Eliminasi Keraguan Kontekstual: LLM bekerja jauh lebih terarah ketika ruang pencarian solusinya dipersempit oleh batasan tipe data dan assertion kegagalan.
Refactoring Tanpa Rasa Cemas: Ketika arsitektur database atau pustaka backend perlu ditingkatkan, test suite memastikan tidak ada logika lama yang patah saat Claude mengompilasi ulang codebase.
Dokumentasi Hidup: Test suite berfungsi ganda sebagai dokumentasi sistem yang tidak pernah usang, baik bagi rekan tim manusia maupun bagi agen AI berikutnya yang membaca repositori Anda.
Menatap Ulang Profesi Software Engineer
Kemampuan Claude 3.7 Sonnet mengurai persoalan algoritmik kompleks membuktikan bahwa aktivitas mekanis mengetik kode (syntax writing) sedang bergeser menjadi komoditas murah. Namun, rekayasa perangkat lunak tidak pernah sekadar tentang mengetik sintaks.
Inti dari software engineering adalah merumuskan kejelasan dari ambiguitas dunia nyata:
Menentukan apa yang terjadi ketika koneksi bank terputus di tengah transaksi.
Memastikan hak akses data terisolasi rapat antar pengguna.
Menyusun jaring pengaman agar sistem pulih secara anggun ketika terjadi anomali.
Vibe coding* bukanlah ancaman bagi standar rekayasa yang disiplin; ia adalah akselerator terbaik jika dikawinkan dengan ketelitian Test-Driven Development. Dengan membiarkan Test Suite menjadi kompas dan Claude 3.7 Sonnet sebagai mesin penggeraknya—didukung stabilitas routing performa tinggi dari AiStudio.id API Gateway—kita tidak lagi sekadar menebak-nebak masa depan software development. Kita sedang membangunnya dengan kecepatan tinggi, presisi mutlak, dan ketenangan pikiran.
Catatan Penulis

> Sandra menulis seputar rekayasa prompt, efisiensi arsitektur AI, dan produk digital di AiStudio.id.