Skip to main content
BlogERP

Spec-Driven Development untuk Requirement ERP dengan AI Agent

Requirement ERP datang sebagai edge case, bukan spec. Cara saya mengubahnya jadi acceptance criteria yang bisa diuji, tabel contoh, dan ambiguity log.

Matthews Wong

Software Engineer

September 202612 menit baca
Spec-Driven Development untuk Requirement ERP dengan AI Agent

Foto oleh Internet Archive Book Images via Wikimedia Commons (No restrictions)

Ringkasan Utama

Requirement ERP datang sebagai kumpulan edge case, bukan spesifikasi. Sebelum menulis prompt untuk agent, tulis satu acceptance criteria yang bisa diuji untuk tiap aturan, satu tabel contoh berisi baris boundary yang paling diperdebatkan finance, dan satu ambiguity log yang menyebut siapa pemilik tiap pertanyaan yang belum terjawab. Spec hidup lebih lama daripada prompt, jadi spec itulah yang direview.

Requirement itu datang sebagai tiga hal dalam satu sore: satu spreadsheet berisi purchase request yang salah rute approval, satu thread WhatsApp tempat branch manager menjelaskan mana yang sebenarnya tidak masalah, dan satu meeting saat finance bilang limitnya lima juta rupiah — kecuali di akhir bulan. Tidak satu pun dari itu adalah spesifikasi, dan prompt sebagus apa pun tidak akan mengubahnya menjadi spesifikasi.

Tulisan ini soal bagian dari spec-driven development yang terjadi sebelum agent dilibatkan sama sekali: mengubah bahan tadi menjadi acceptance criteria yang bisa membuat test gagal, tabel contoh yang mau ditandatangani finance, dan catatan pertanyaan yang tidak bisa dijawab oleh spec itu sendiri. Contohnya adalah aturan ERP yang paling sering jadi bahan debat saat review — hierarki approval dengan jalur delegasi, penguncian posting period, pembulatan pajak, dan alokasi landed cost.

Bentuk requirement ERP saat pertama kali datang

Requirement ERP jarang tertulis, karena bisnisnya merasa sudah tahu. Aturannya hidup di dalam pengecualian, dan pengecualian baru muncul ke permukaan ketika ada dokumen yang salah rute. Yang sampai ke meja Anda hampir selalu salah satu dari empat bentuk berikut, dan tidak ada satu pun yang berupa aturan:

  • Spreadsheet berisi edge case — baris yang dikumpulkan seseorang karena sistem lama menanganinya salah, tanpa satu kalimat pun tentang seperti apa hasil yang benar.
  • Thread chat, tempat keputusannya nyata tetapi alasannya tersebar di sepanjang scrollback dan dua orang di dalamnya berbeda pendapat tanpa menyadarinya.
  • Kalimat yang mengandung kata kecuali. Approve semua sampai lima juta, kecuali di akhir bulan, sebenarnya dua aturan terpisah plus satu boundary yang tanggalnya belum ditentukan siapa pun.
  • Screenshot sistem lama yang disodorkan sebagai spesifikasi, lengkap dengan bug yang sudah diam-diam diakali tim selama bertahun-tahun.

Keempatnya punya cacat yang sama: menjelaskan perilaku lewat contoh tanpa menyebut contoh mana yang jadi aturan dan mana yang pengecualian. Serahkan salah satunya ke agent, hasilnya akan terdengar masuk akal, karena masuk akal adalah satu-satunya hal yang bisa dihasilkan dari input seperti itu. Pekerjaannya bukan menulis prompt yang lebih baik, melainkan memutuskan secara tertulis, sebelum session dimulai, baris mana yang berlaku sebagai hukum.

Aturan yang tidak bisa dibuatkan failing test belum jadi aturan

Hanya ini filter yang saya pakai untuk setiap baris spec ERP: bisakah saya menulis test yang hari ini gagal dan nanti lulus ketika aturannya diimplementasikan? Kalau tidak bisa, baris itu adalah preferensi, harapan, atau ringkasan. Simpan di bagian background dan berhenti menyebutnya requirement. ISO/IEC/IEEE 29148, standar yang menggantikan IEEE 830 pada 2011, meminta hal yang sama dengan kosakatanya sendiri — tiap requirement harus necessary, appropriate, dan unambiguous.

# specs/purchasing/pr-approval.feature

# Wrong: nothing in this line can fail, so it is not a rule yet.
#   Then high-value requests should be approved faster

# Right: one rule, one assertion per line, real people, a dated delegation.
Rule: A purchase request above the branch limit needs finance approval

  Background:
    Given branch JKT-01 has an approval limit of 5000000 IDR
    And Rina is the finance head for JKT-01
    And Rina has delegated approval to Budi from 2026-09-14 to 2026-09-21

  Scenario Outline: Routing by amount, delegation and self-approval
    Given a purchase request for <amount> IDR raised by <requester>
    When the department manager approves it
    Then the next approver is <next_approver>
    And the audit trail records <trail>

    Examples:
      |   amount | requester | next_approver    | trail                    |
      |  4500000 | Sari      | none             | manager only             |
      |  5000000 | Sari      | none             | manager only, inclusive  |
      | 40000000 | Sari      | Budi             | delegated from Rina      |
      | 40000000 | Budi      | Finance Director | delegate raised it       |

Gherkin bukan intinya di sini; bentuknya yang penting. Satu aturan per blok, satu assertion per baris, dan nama orang, bukan nama jabatan. Menulis bahwa Rina mendelegasikan approval ke Budi dari 14 sampai 21 September memaksa jendela delegasi masuk ke dalam spec, dan justru jendela itulah yang paling sering dilupakan implementasi: delegasi tanpa tanggal berakhir berarti satu approver tambahan yang permanen dan tidak pernah diaudit sampai ada auditor bertanya siapa yang menandatangani.

Tulis assertion-nya dulu, baru teks aturannya. Kalau assertion itu tidak bisa dinyatakan dalam satu klausa, berarti aturannya ada dua — pecah. Limit approval dan pembekuan akhir bulan terdengar seperti satu kalimat di ruang meeting, padahal di dalam spec keduanya aturan terpisah dengan dua pemilik keputusan yang berbeda.

Tabel contoh menyelesaikan lebih banyak debat daripada prosanya

Yang saya bawa ke review adalah tabelnya, bukan prosanya. Paragraf tentang limit approval cuma diangguki; satu baris yang menyatakan request senilai persis 5.000.000 disetujui manager sendirian pasti diperdebatkan, dan debat itulah hasil kerjanya. Example Mapping dari Cucumber menyebut baris seperti ini green card, contoh konkret yang duduk di bawah tiap blue card berisi aturan, dan klaimnya cocok dengan yang terjadi di review ERP: contoh memunculkan ketidaksepakatan yang justru disembunyikan oleh kalimat aturan.

InputHasil yang diharapkanAlasan baris ini ada di tabel
PR senilai 4.500.000 IDR, limit approval cabang 5.000.000Manager departemen approve, tanpa langkah financeJalur normal, supaya regresi routing langsung terlihat
PR senilai persis 5.000.000 IDRManager approve sendirian — limitnya bersifat inklusifFinance bilang sampai lima juta; implementasi pertama membacanya sebagai di bawah lima juta
PR senilai 40.000.000 IDR, finance head mendelegasikan ke controller selama satu mingguController approve; audit trail mencatat kedua nama beserta jendela waktunyaDelegasi tanpa tanggal berakhir berubah jadi approver kedua yang permanen
PR senilai 40.000.000 IDR yang diajukan oleh delegasi itu sendiriNaik satu level di atasnya, tidak pernah kembali ke delegasiLarangan self-approval adalah kontrol, bukan preferensi, jadi butuh barisnya sendiri
Dua baris invoice senilai 1.000.005 IDR dengan tarif 11 persen di item masterPajak 220.002 IDR, dibulatkan half up di tiap barisMembulatkan total invoice sekali saja menghasilkan 220.001, dan selisihnya tidak pernah cocok
Invoice supplier bertanggal 30 September, period sudah terkunciPosting ditolak; masuk ke Oktober dengan tanggal dokumen tetap dipertahankanBoundary yang selalu ditanyakan finance paling awal, di setiap review

Setiap baris di tabel itu ada karena pernah ada yang salah menanganinya. Baris boundary-lah yang bekerja: nominal yang persis menyentuh limit, approver yang ternyata pemohonnya sendiri, invoice bertanggal sama dengan hari period ditutup. Tabel berisi baris tengah yang aman tidak membuktikan apa pun dan selesai direview dalam tiga puluh detik, dan justru itu sebabnya membuatnya terasa produktif.

Empat band bertumpuk yang menunjukkan satu kalimat requirement kabur berubah menjadi acceptance criteria, lalu contoh terisi, lalu failing test, dengan tag pemilik di tiap band
Kepemilikan berpindah di setiap langkah, dan itu bagian yang perlu diperhatikan: kalimatnya milik finance, criteria-nya milik penulis spec, contoh terisi kembali ke finance untuk disetujui, dan hanya failing test yang jadi target agent. Melewati band ketiga adalah cara paling umum menghasilkan implementasi yang benar untuk aturan yang salah.

Ambiguity log adalah deliverable yang benar-benar dibaca stakeholder

Setiap spec modul ERP yang saya tulis menghasilkan lebih banyak pertanyaan daripada aturan pada putaran pertama, dan pertanyaannya ternyata bagian yang paling berharga. Example Mapping melakukan hal serupa lewat red card — seperti ditulis Cucumber, Anda baru saja mengubah unknown unknown menjadi known unknown. Penyesuaian khas ERP-nya adalah tiap pertanyaan diberi pemilik, tanggal, dan default.

# specs/purchasing/pr-approval.ambiguities.yaml
# Every question the worked-example table could not settle. Read in review.

- id: AMB-01
  question: 'Does up to 5,000,000 include exactly 5,000,000?'
  owner: Rina, Finance Manager     # a person, never a department
  asked: 2026-09-02
  due: 2026-09-05
  default: 'inclusive - the highest amount a manager may approve alone'
  blocks: false                    # the default ships, and the test asserts it

- id: AMB-02
  question: 'When the named delegate raises the request, who approves it?'
  owner: Rina, Finance Manager
  asked: 2026-09-02
  due: 2026-09-05
  default: none                    # no safe default exists for this one
  blocks: true                     # self-approval is a control, not a preference

- id: AMB-03
  question: 'Does the month-end freeze stop approvals, or only posting?'
  owner: Andri, Controller
  asked: 2026-09-02
  due: 2026-09-08
  default: 'posting only - approvals continue through close'
  blocks: false

Dua field membuat dokumen ini hidup, bukan sekadar daftar keluhan. Field owner menyebut nama orang, karena pertanyaan yang dimiliki finance sebenarnya tidak dimiliki siapa-siapa. Field default menyatakan apa yang akan dilakukan kode kalau jawabannya tidak pernah datang, sehingga pertanyaan terbuka berubah jadi perilaku yang bisa dirilis plus risiko yang tercatat — dan test menegaskan default itu, jadi begitu jawaban sebenarnya tiba, test yang gagal menunjukkan persis apa yang harus diubah.

Kegagalan paling umum adalah spec yang ditulis setelah kodenya jadi. Bacanya seperti spesifikasi, lolos review, padahal isinya dokumentasi dari apa pun yang terlanjur dibangun, termasuk bagian yang tidak pernah diputuskan siapa pun. Cirinya adalah baris boundary yang hilang: spec yang ditulis belakangan tidak punya baris itu, karena implementasinya sudah menjawab semua pertanyaan boundary secara diam-diam.

Boundary yang tidak bisa ditebak agent: lock period dan pembulatan

Dua kelas aturan menyumbang sebagian besar perdebatan setelah go-live, dan keduanya tidak bisa ditebak dari kode di sekitarnya. Yang pertama adalah batas posting period. Odoo, sebagai contoh yang terdokumentasi, punya dua: Lock Everything date yang mencegah perubahan entry yang sudah diposting sekaligus mencegah posting baru pada tanggal itu atau sebelumnya, dan Hard Lock date yang bersifat irreversible dan, dalam kalimat Odoo sendiri, tidak bisa diubah atau ditimpa terlepas dari hak akses. Aturan Anda mengacu ke yang mana adalah keputusan bisnis, dan kalimat periodenya sudah ditutup tidak menjawabnya.

Yang kedua adalah pembulatan, tempat sebuah spesifikasi benar-benar membayar dirinya sendiri, karena hasil aritmetikanya ditentukan oleh di mana Anda membulatkan, bukan bagaimana. Dua baris invoice senilai Rp 1.000.005 dengan tarif 11 persen yang dikonfigurasi di item master menghasilkan pajak 220.002 rupiah bila tiap baris dibulatkan half up, dan 220.001 bila total invoice yang dibulatkan sekali. Satu rupiah, di setiap invoice, dan satu task rekonsiliasi yang tidak pernah selesai. Alokasi landed cost punya pola yang sama dengan arah selisih terbalik.

// specs/purchasing/landed-cost.spec.ts - written before the module existed.
//
// Rule LC-03: import freight is apportioned across receipt lines by line
// value, rounded half up to the rupiah, and the rounding residual lands on
// the highest-value line so the apportioned total equals the freight exactly.

const halfUp = (n: number) => Math.floor(n + 0.5);

function apportion(freight: number, lineValues: number[]): number[] {
  const total = lineValues.reduce((a, b) => a + b, 0);
  const shares = lineValues.map((v) => halfUp((freight * v) / total));

  // Naive rounding invents money: three equal lines of a 2,000,000 freight
  // each round to 666,667, which sums to 2,000,001. The extra rupiah has to
  // be taken back from one named line, decided once, in the spec.
  const drift = freight - shares.reduce((a, b) => a + b, 0);
  shares[lineValues.indexOf(Math.max(...lineValues))] += drift;
  return shares;
}

test("LC-03 puts the residual on one named line, not on every line", () => {
  const shares = apportion(2_000_000, [1_000_000, 1_000_000, 1_000_000]);
  expect(shares).toEqual([666_666, 666_667, 666_667]);
  expect(shares.reduce((a, b) => a + b, 0)).toBe(2_000_000);
});

Komentar di dalam test itulah spesifikasinya. Pembulatan naif menciptakan satu rupiah ongkos angkut yang tidak ada, jadi sisa pembulatan harus mendarat di suatu tempat secara sengaja. Baris mana yang menerimanya memang arbitrer — nilai tertinggi, baris pertama, atau baris terakhir sama-sama bisa dibela — tetapi harus tertulis, karena kalau tidak, tiga developer akan memilih tiga jawaban arbitrer yang berbeda dan valuasi inventory berhenti cocok dengan invoice supplier.

Kenapa spec, bukan prompt, yang layak direview

Prompt dibuang begitu session berakhir. Spec dibaca orang berikutnya, dan di modul ERP orang berikutnya biasanya sedang mengaudit angka yang salah pada bulan yang sudah lama Anda lupakan. Spec Kit dari GitHub membawa gagasan ini ke ujungnya, menggambarkan proses saat spesifikasi menjadi executable dan langsung menghasilkan implementasi yang berjalan, bukan sekadar memandu, dengan specify, plan, tasks, dan implement sebagai command terpisah. Yang saya ambil lebih sempit: unit yang direview adalah file spec di dalam repository, bersebelahan dengan kode yang diaturnya.

Praktiknya, spec tinggal di folder specs bersama modulnya, ambiguity log duduk di sebelahnya, dan perubahan aturan menjadi diff yang bisa disodorkan ke orang finance. Yang saya review adalah diff dari spec, bukan transkrip session yang mengimplementasikannya. Transkrip memberi tahu apa yang disampaikan ke agent; spec memberi tahu apa yang diputuskan bisnis, dan hanya salah satunya yang masih benar kuartal depan.

Tiga panel yang membandingkan requirement yang sudah terjawab, requirement ambigu dengan pemilik bernama, dan requirement ambigu tanpa pemilik, beserta yang dilakukan agent pada tiap keadaan
Panel tengah adalah keadaan yang jadi target, bukan panel kiri — default tertulis plus pemilik bernama sudah cukup untuk mulai dibangun. Hanya panel kanan yang merilis keputusan tanpa review, dan itu baru ketahuan saat tutup buku akhir bulan, bukan saat code review.

Urutan yang menjaga spec ERP tetap jujur

Urutannya lebih penting daripada formatnya. Ditulis dengan urutan ini, spec tetap jadi spesifikasi; ditulis dengan urutan lain, pelan-pelan ia berubah jadi deskripsi kode.

  1. Kumpulkan edge case apa adanya begitu datang, lalu beri label aturan atau pengecualian pada masing-masing. Jangan dirapikan dulu — kekacauannya adalah bukti.
  2. Tulis satu acceptance criteria per aturan, masing-masing dengan assertion yang bisa gagal. Pecah apa pun yang mengandung kata kecuali.
  3. Isi tabel contoh, mulai dari baris boundary: persis di limit, persis di lock date, persis di batas pembulatan, dan pada kasus self-approval.
  4. Catat setiap pertanyaan yang tidak terjawab oleh tabel, lengkap dengan pemilik, tanggal, dan default yang akan dirilis kalau jawabannya tidak datang.
  5. Baru setelah itu jalankan agent, dan arahkan ke file spec-nya, bukan menempelkan requirement ke dalam prompt.

Langkah yang paling sering dilewati saat dikejar deadline adalah langkah keempat, dan melewatinya adalah seluruh perbedaan antara modul dengan tiga keputusan terbuka yang tercatat dan modul dengan tiga keputusan terbuka yang tidak tercatat. Keduanya tetap rilis di hari yang sama. Hanya satu yang bisa diaudit setelahnya.

Spec-driven development di ERP bukan teknik menulis prompt. Ini disiplin requirement lama — satu criteria yang bisa diuji per aturan, contoh mengalahkan prosa, pertanyaan dicatat atas nama pemilik tertentu — dikerjakan sebelum agent boleh menulis apa pun, karena agent dengan senang hati mengimplementasikan ambiguitas alih-alih melaporkannya. Tulis spec sampai failing test-nya lahir duluan, dan implementasi berhenti menjadi bagian yang sulit.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa itu spec-driven development untuk requirement ERP?

Artinya menuliskan requirement sebagai aturan yang bisa diuji sebelum AI agent menulis kode, bukan menjelaskan requirement di dalam prompt. Untuk ERP, spec itu punya tiga bagian: satu acceptance criteria per aturan bisnis, satu tabel contoh berisi input nyata dan hasil yang diharapkan, serta catatan pertanyaan yang belum terjawab. Agent membangun mengikuti spec, dan spec itulah yang direview dan disimpan.

Bagaimana menulis acceptance criteria untuk aturan approval ERP?

Tulis sedemikian rupa sehingga sebuah test bisa gagal hari ini dan lulus setelah aturannya diimplementasikan. Sebut nama orang, bukan nama jabatan, satu assertion per baris, dan sertakan jendela delegasi lengkap dengan tanggal mulai dan berakhir. Kalau assertion-nya tidak bisa dinyatakan dalam satu klausa, berarti itu dua aturan dan harus dipecah.

Kenapa spec ERP butuh tabel contoh?

Karena paragraf tentang limit approval biasanya cuma diangguki saat review, sedangkan satu baris konkret pasti diperdebatkan, dan debat itulah tujuannya. Isi baris boundary lebih dulu: nominal yang persis menyentuh limit, approver yang ternyata pemohonnya sendiri, invoice bertanggal sama dengan hari period dikunci. Tabel berisi contoh tengah yang aman selesai direview cepat dan tidak membuktikan apa pun.

Apa isi ambiguity log dan siapa pemiliknya?

Tiap entri memuat pertanyaannya, nama orang yang bisa menjawab, tanggal pertanyaan diajukan, dan perilaku default yang akan dirilis kode kalau jawabannya tidak datang. Menyebut nama departemen alih-alih nama orang berarti tidak ada yang memilikinya. Bagian default paling penting, karena mengubah pertanyaan terbuka menjadi perilaku yang bisa dirilis plus risiko yang tercatat, dan test menegaskan default itu sehingga perubahannya jelas saat jawaban sebenarnya tiba.

Saat agent membangun modul ERP, yang direview prompt atau spec?

Review spec-nya. Prompt dibuang begitu session berakhir, sedangkan spec dibaca berbulan-bulan kemudian oleh orang yang sedang mengaudit angka yang salah. Simpan file spec di dalam repository bersebelahan dengan modul yang diaturnya, supaya perubahan aturan bisnis muncul sebagai diff yang bisa Anda sodorkan ke orang finance.