AI Context Engineering untuk Monolith Multi-Module
19 min read

AI Context Engineering untuk Monolith Multi-Module

Monolith multi-module sering dianggap lebih sederhana untuk AI agent dibanding microservices karena semuanya ada dalam satu repo — tidak ada masalah akses lintas repo, tidak perlu pointer file eksternal. Tapi anggapan ini keliru begitu monolith-nya sudah berisi belasan module dengan domain yang berbeda-beda: agent yang mengerjakan module payment tetap bisa kebanjiran context dari module catalog, shipping, atau notification kalau tidak ada strukturisasi yang jelas. Artikel ini membahas context engineering di dalam satu repo secara konkret, memakai studi kasus e-commerce monolith dengan beberapa module utama, lengkap dengan contoh isi AGENTS.md dan CLAUDE.md di tiap layer serta best practice menjaga context tetap relevan dan sinkron.

Studi Kasus: Struktur E-Commerce Monolith Multi-Module

Supaya pembahasan konkret, kita pakai satu contoh struktur yang representatif untuk aplikasi e-commerce monolith bergaya modular — pola yang umum dipakai baik di Go (modular monolith dengan package per domain), Java/Spring (multi-module Maven/Gradle), maupun Node/TypeScript (workspace-based monorepo internal).

ecommerce-monolith/
  ├── AGENTS.md
  ├── CLAUDE.md
  ├── docs/
  │   └── architecture.md
  ├── modules/
  │   ├── catalog/
  │   │   ├── AGENTS.md
  │   │   ├── CLAUDE.md
  │   │   ├── domain/
  │   │   ├── application/
  │   │   └── infrastructure/
  │   ├── cart/
  │   │   ├── AGENTS.md
  │   │   ├── CLAUDE.md
  │   │   └── ...
  │   ├── checkout/
  │   │   ├── AGENTS.md
  │   │   ├── CLAUDE.md
  │   │   └── ...
  │   ├── payment/
  │   │   ├── AGENTS.md
  │   │   ├── CLAUDE.md
  │   │   ├── gateway/
  │   │   │   └── AGENTS.md
  │   │   └── ...
  │   ├── inventory/
  │   │   └── ...
  │   ├── shipping/
  │   │   └── ...
  │   ├── user/
  │   │   └── ...
  │   └── notification/
  │       └── ...
  └── shared/
      ├── domain/
      └── AGENTS.md

Tujuh module utama: catalog (produk), cart, checkout, payment, inventory, shipping, user, dan notification. Ada juga folder shared/ untuk domain model yang dipakai lintas module — ini sengaja jadi contoh kasus khusus karena sering jadi sumber ambiguitas context.


Kenapa Monolith Multi-Module Tetap Butuh Context Engineering

Tiga alasan struktur ini tetap butuh perhatian context yang sama seriusnya dengan multi-service, meski secara teknis semua kode ada dalam satu akses:

Context window tetap terbatas. Agent yang mengerjakan bug di payment/gateway/ tidak butuh tahu detail implementasi notification/email-templates/. Akses teknis penuh tidak menghilangkan kebutuhan filtering — token tetap mahal dan noise tetap menurunkan kualitas jawaban.

Domain knowledge tetap terpisah. Konvensi module payment (harus idempotent, harus audit trail, harus terhubung ke payment gateway eksternal) sangat berbeda dari konvensi module catalog (fokus ke search performance, caching produk). Menyatukan semua ini di satu file besar membuat agent harus menyaring sendiri mana yang relevan — pekerjaan yang seharusnya sudah diselesaikan oleh strukturisasi context file.

Module boundary sering dilanggar tanpa disadari. Salah satu masalah paling umum di monolith modular adalah agent (atau developer) tanpa sadar mengimpor langsung dari internal module lain, melanggar boundary yang seharusnya dijaga lewat interface publik. Context file yang menjelaskan boundary ini secara eksplisit mengurangi risiko itu.

flowchart TD
    A[Agent kerja di modules/payment/] --> B{Baca context apa?}
    B -- Tanpa strukturisasi --> C[Satu AGENTS.md raksasa berisi semua module]
    C --> D[Noise tinggi, konvensi campur aduk, token boros]
    B -- Dengan strukturisasi --> E[Root AGENTS.md: konvensi global]
    E --> F[modules/payment/AGENTS.md: konvensi domain payment]
    F --> G[Context relevan, ringkas, boundary jelas]

Bagaimana Context Sebenarnya Bekerja di Berbagai AI Agent

Sebelum masuk ke pola tiga layer, penting digarisbawahi: struktur root/module/local yang dibahas di artikel ini adalah pola rekomendasi berdasarkan prinsip context engineering umum — bukan mekanisme universal yang berlaku sama persis di semua AI coding agent. Tiap tool punya cara berbeda menentukan file instruksi mana yang dibaca, kapan, dan bagaimana urutannya. Memahami perbedaan ini penting supaya pola yang kamu terapkan benar-benar cocok dengan tool yang dipakai tim.

Claude Code — Lazy-Load Berbasis Lokasi Folder

Dokumentasi resmi Claude Code menjelaskan bahwa file CLAUDE.md di root (beserta level enterprise dan user di atasnya) dimuat otomatis ke context begitu sesi dimulai. Untuk CLAUDE.md yang ada di subdirectory, mekanismenya berbeda — file itu baru ikut dimuat saat Claude membaca file dari direktori tersebut, bukan sekaligus di awal sesi. Kalau agent belum pernah menyentuh file di suatu folder, CLAUDE.md di folder itu juga belum ter-load ke context, dan instruksi di dalamnya bisa tampak seperti “diabaikan” padahal sebenarnya belum sempat dibaca. Ini yang membuat perintah /memory berguna untuk mengecek file instruksi mana saja yang sudah benar-benar masuk ke context pada suatu titik dalam sesi.

Referensi: docs.anthropic.com/en/docs/claude-code/memory dan code.claude.com/docs/en/memory.

OpenAI Codex CLI — Cascading AGENTS.md dari Global sampai Direktori Kerja

Codex CLI, yang menjadi salah satu pendorong awal format AGENTS.md sebagai standar terbuka, mendukung nested AGENTS.md secara native. Perbedaan pentingnya dengan Claude Code: Codex menyusun context-nya dari beberapa layer sekaligus — level global (~/.codex/AGENTS.md), level root repo, dan level direktori kerja saat ini — lalu menggabungkannya, dengan file yang lebih lokal memenangkan konflik terhadap yang lebih general. Repo Codex milik OpenAI sendiri dilaporkan memakai puluhan AGENTS.md terpisah tersebar di seluruh direktori proyek sebagai contoh nyata pola ini diterapkan dalam skala besar.

Referensi: spesifikasi resmi di agents.md, dan dokumentasi developer OpenAI soal custom instructions dengan AGENTS.md.

Cursor — Bukan Berbasis Lokasi Folder, tapi Glob Pattern dan Aktivasi Semantik

Cursor punya pendekatan yang paling berbeda dari dua tool di atas. Alih-alih mekanisme “file terdekat ke lokasi kerja menang” seperti Claude Code dan Codex, Cursor memakai direktori .cursor/rules/ berisi file .mdc dengan empat mode aktivasi yang didefinisikan lewat YAML frontmatter:

Mode AktivasiTrigger
Always ApplySelalu dimuat di setiap request, terlepas dari lokasi file
Auto AttachedDimuat kalau file yang sedang disentuh cocok dengan pattern di field globs
Agent RequestedAI sendiri yang memutuskan apakah rule ini relevan, berdasarkan field description
ManualHanya dimuat kalau developer secara eksplisit mengetik @nama-rule di chat

Field globs di Cursor tidak terikat pada lokasi folder tempat rule itu disimpan — sebuah rule di .cursor/rules/backend.mdc bisa saja diaktifkan berdasarkan pattern services/api/**, meski file .mdc-nya sendiri fisiknya ada di lokasi terpusat, bukan tersebar mengikuti struktur folder kode. Ini kebalikan dari pola nested AGENTS.md/CLAUDE.md yang mengandalkan lokasi fisik file sebagai penentu kapan dia dimuat.

Referensi: dokumentasi resmi Cursor soal Rules dan format frontmatter .mdc.

Implikasi untuk Pola Tiga Layer di Artikel Ini

Karena perbedaan ini, penerapan pola root/module/local yang dibahas di artikel ini perlu disesuaikan tergantung tool:

  • Claude Code — pola nested AGENTS.md + CLAUDE.md jembatan (seperti dibahas di bagian sebelumnya) langsung relevan, karena mekanisme lazy-load berbasis lokasi folder memang cara kerja aslinya.
  • Codex CLI — nested AGENTS.md per module juga langsung didukung native, tanpa perlu file jembatan tambahan seperti di Claude Code.
  • Cursor — pola nested per-folder kurang pas diterapkan apa adanya. Yang lebih sesuai adalah menerjemahkan batas module jadi globs pattern di file .mdc terpusat, atau memakai AGENTS.md yang tetap dibaca Cursor sebagai source of truth lintas tool, sambil menambahkan .mdc khusus untuk kebutuhan aktivasi semantik yang memang jadi kekuatan Cursor.
  • Detail mekanisme ini berubah cukup cepat mengikuti update tiap tool — kalau tim kamu serius menerapkan pola ini, cek ulang dokumentasi resmi tool yang dipakai sebelum implementasi final, jangan hanya mengandalkan artikel ini sebagai satu-satunya sumber.
  • Kalau tim memakai lebih dari satu tool, AGENTS.md sebagai source of truth tetap masuk akal karena didukung secara luas — tapi mekanisme bagaimana dia dimuat ke context tetap berbeda-beda per tool, dan itu yang menentukan seberapa efektif strukturisasi nested-mu di masing-masing tool.

Layer Konteks: Root, Module, dan Local

Pola tiga layer ini jadi kerangka utama untuk seluruh contoh konkret di artikel ini.

LayerLokasiIsiFrekuensi Berubah
Root/AGENTS.mdKonvensi global, tech stack, cara run/test/build, arsitektur keseluruhanJarang
Module/modules/<nama>/AGENTS.mdKonvensi domain spesifik, boundary module, invariant bisnisSedang
Local/modules/<nama>/<sub-folder>/AGENTS.mdDetail sangat spesifik — misalnya integrasi payment gateway tertentuSering

Prinsipnya: semakin dalam levelnya, semakin spesifik dan semakin sering berubah isinya. Root file harus stabil — kalau root sering diubah, itu tanda ada konten yang seharusnya dipindah ke level module.


Skenario Konkret: Agent Memperbaiki Bug Idempotency di Payment

Untuk melihat manfaat strukturisasi ini secara nyata, bayangkan task berikut: ada bug di mana refund kadang terpotong dua kali kalau webhook dari payment gateway datang duplikat. Agent ditugaskan memperbaikinya di modules/payment/gateway/.

Tanpa strukturisasi (satu AGENTS.md raksasa di root berisi semua module), agent harus memproses seluruh konvensi catalog, cart, shipping, user, dan notification sebelum sampai ke informasi yang relevan — meski task ini sama sekali tidak menyentuh module-module itu. Selain boros token, ada risiko lebih halus: agent bisa saja “meniru” pola dari module lain yang sebenarnya tidak cocok untuk konteks payment, misalnya menganggap semua module boleh update status langsung padahal payment punya aturan state machine yang ketat.

Dengan strukturisasi tiga layer, urutan context yang ter-load justru mengikuti kebutuhan task secara alami:

  1. Root AGENTS.md ter-load di awal sesi — agent tahu aturan umum cross-module dan lokasi module payment.
  2. Begitu agent mulai membaca file di modules/payment/, modules/payment/AGENTS.md ikut ter-load — agent tahu invariant idempotency dan aturan state machine sebelum menyentuh kode.
  3. Begitu agent masuk ke modules/payment/gateway/, file lokal di situ ikut ter-load — agent langsung tahu bahwa webhook Midtrans memang bisa duplikat, dan validasi harus lewat transaction_id + status, bukan asumsi idempotency dari sisi provider.

Poin ketiga ini penting: tanpa file lokal itu, agent kemungkinan besar akan menghabiskan waktu menyimpulkan sendiri dari kode kenapa webhook bisa duplikat — atau lebih buruk, membuat asumsi salah dan menambah state check yang keliru. Gotcha spesifik seperti ini adalah jenis informasi yang paling bernilai ditulis eksplisit di context file lokal, karena sulit disimpulkan hanya dari membaca kode.

PendekatanContext yang ter-loadRelevansi dengan task
Satu AGENTS.md raksasaSemua 7 module + shared, sekaligus di awalRendah — sebagian besar tidak relevan
Tiga layer (root + module + local)Root, lalu payment, lalu gateway — bertahap sesuai kebutuhanTinggi — setiap layer relevan dengan langkah task saat itu

Context file terstruktur menyelesaikan masalah apa konvensi yang berlaku, tapi tidak menyelesaikan masalah di mana kode yang relevan berada ketika repo sudah sangat besar. Untuk monolith dengan puluhan ribu baris kode per module, agent yang harus membaca banyak file satu-satu untuk menemukan definisi function atau pemanggilnya akan boros context dengan cara yang berbeda — bukan dari context file, tapi dari eksplorasi kode itu sendiri.

Beberapa alat yang membantu di titik ini, saling melengkapi dengan context file:

  • ripgrep/grep cepat — untuk mencari string atau pattern spesifik lintas module tanpa membuka tiap file satu-satu. Banyak agent coding modern sudah menggunakan ini secara otomatis lewat tool bawaan.
  • ctags/tree-sitter symbol index — memungkinkan agent “lompat” ke definisi function/struct langsung, alih-alih membaca seluruh file untuk menemukan satu definisi.
  • Codebase indexing/embedding search — beberapa tool (built-in di sebagian agent, atau eksternal seperti Sourcegraph) mengindeks seluruh repo secara semantik, sehingga query seperti “di mana logika retry payment gateway” bisa langsung mengarahkan ke file yang tepat tanpa agent harus menyusuri struktur folder secara manual.

Tooling ini tidak menggantikan context file — dia melengkapi. Context file menjawab “apa aturan dan gotcha di sini”, sementara symbol index/search menjawab “di mana kode yang relevan berada”. Untuk monolith seukuran studi kasus e-commerce ini (tujuh module, kemungkinan puluhan ribu baris tiap module), kombinasi keduanya jauh lebih efektif dibanding mengandalkan agent membaca seluruh direktori secara manual setiap kali mulai task baru.


Konkret: AGENTS.md Root untuk E-Commerce Monolith

Root AGENTS.md berisi hal yang relevan untuk semua module — bukan detail satu domain tertentu.

# AGENTS.md — ecommerce-monolith

## Stack
- Go 1.23, modular monolith (bukan clean architecture penuh — lihat docs/architecture.md)
- PostgreSQL per module (schema terpisah, bukan shared table lintas module)
- Setiap module wajib expose public API lewat package `application/`,
  jangan pernah import langsung dari `domain/` atau `infrastructure/` module lain.

## Struktur Module
- catalog/    → manajemen produk, kategori, search
- cart/       → keranjang belanja, berlaku per-session
- checkout/   → orkestrasi proses order dari cart ke payment
- payment/    → integrasi payment gateway, state machine transaksi
- inventory/  → stok, reservasi, sinkronisasi dengan warehouse
- shipping/   → kalkulasi ongkir, tracking pengiriman
- user/       → autentikasi, profil, alamat
- notification/ → email, push notification, SMS

Tiap module punya AGENTS.md sendiri di modules/<nama>/ — BACA file itu
sebelum mengerjakan perubahan di module tersebut. Jangan asumsikan
konvensi satu module berlaku sama di module lain.

## Build & Test
- `make test-module MODULE=<nama>` untuk test satu module saja
- `make test-all` untuk seluruh monolith (lambat, hindari kecuali perlu)
- Semua migration lewat `make migrate MODULE=<nama>`, jangan edit skema manual

## Cross-Module Rule
- Komunikasi antar module HARUS lewat interface di application/, tidak
  pernah lewat akses langsung ke database module lain.
- Domain model yang dipakai lintas module (Product, Order, User) ada
  di shared/domain/ — lihat shared/AGENTS.md sebelum mengubahnya.

Perhatikan: root file ini tidak menjelaskan detail bisnis payment atau algoritma search catalog — itu urusan file di level module.


Konkret: AGENTS.md per Module

Contoh — modules/payment/AGENTS.md

# AGENTS.md — payment module

## Tanggung Jawab
Module ini menangani seluruh siklus transaksi pembayaran: charge,
refund, dan rekonsiliasi dengan payment gateway eksternal.

## Invariant yang TIDAK BOLEH Dilanggar
- Setiap operasi charge/refund HARUS idempotent — gunakan idempotency_key
  yang sudah ada di tabel transactions, jangan buat mekanisme baru.
- State transaksi HANYA boleh berubah lewat state machine di
  domain/transaction_state.go — jangan update status secara langsung.
- Setiap perubahan status wajib menulis audit log di audit_trail table
  sebagai bagian dari transaksi database yang sama (bukan best-effort
  terpisah).

## Dependency ke Module Lain
- Membaca data order dari checkout module lewat application/OrderReader
  interface — TIDAK mengakses tabel orders milik checkout secara langsung.
- Mempublish event payment.completed dan payment.failed yang di-consume
  oleh checkout dan notification module.

## Payment Gateway
Integrasi gateway eksternal ada di gateway/ sub-folder — lihat
gateway/AGENTS.md untuk detail spesifik provider yang dipakai.

## Test
- Gunakan mock gateway di testing/mock_gateway.go untuk unit test.
- Test idempotency WAJIB ada di setiap PR yang menyentuh charge/refund flow.

Contoh — modules/catalog/AGENTS.md

# AGENTS.md — catalog module

## Tanggung Jawab
Manajemen produk, kategori, atribut varian, dan search index.

## Konvensi Spesifik Module Ini
- Setiap perubahan skema produk WAJIB disertai reindex ke Elasticsearch
  (lihat infrastructure/search_indexer.go) — perubahan yang tidak
  memicu reindex akan membuat data search basi.
- Harga dan stok TIDAK disimpan di module ini — harga ada di pricing
  service eksternal (lihat shared/AGENTS.md), stok ada di inventory module.

## Dependency ke Module Lain
- Membaca stok dari inventory module lewat application/StockReader,
  jangan pernah query tabel inventory langsung.

## Performance
- Endpoint search punya SLA p99 < 200ms  perubahan apapun di query
  path harus disertai benchmark sebelum merge.

Perhatikan perbedaan gaya isi antara payment dan catalog — masing-masing fokus ke invariant dan gotcha yang benar-benar spesifik ke domainnya, bukan mengulang konvensi umum yang sudah ada di root.

Contoh — modules/payment/gateway/AGENTS.md (Local, Paling Spesifik)

# AGENTS.md — payment gateway integration

## Provider
Integrasi saat ini ke Midtrans (bukan Stripe/Xendit — sudah pernah
dicoba migrasi, lihat docs/architecture.md#adr-012 untuk alasan batal).

## Gotcha Penting
- Webhook Midtrans bisa datang duplikat — validasi selalu lewat
  transaction_id + status, jangan asumsikan webhook idempotent
  dari sisi mereka.
- Sandbox Midtrans punya rate limit ketat (10 req/menit) — jangan
  jalankan integration test tanpa mock kecuali benar-benar perlu.

## Kredensial
Kredensial ada di vault, bukan file config — lihat
infrastructure/README.md untuk cara akses lokal.

File local seperti ini idealnya hanya dibaca ketika agent benar-benar menyentuh integrasi gateway — bukan setiap kali kerja di module payment secara umum. Ini yang membuat pola nested (bukan satu file besar) penting.


CLAUDE.md Root dan Bridge ke Setiap Module

Karena Claude Code secara native melakukan lazy-load pada file bernama CLAUDE.md di subdirectory — bukan AGENTS.md — kamu perlu file jembatan di tiap level yang juga punya AGENTS.md, supaya mekanisme discovery otomatis itu ikut terpicu.

# CLAUDE.md — root
@AGENTS.md
# modules/payment/CLAUDE.md
@AGENTS.md
# modules/payment/gateway/CLAUDE.md
@AGENTS.md

Tiap CLAUDE.md cukup satu baris import. Ini menghindari duplikasi isi — AGENTS.md tetap jadi satu-satunya source of truth yang juga bisa dibaca tool lain (Cursor, Codex CLI, Windsurf), sementara CLAUDE.md cuma jadi trigger supaya Claude Code ikut men-discover file itu secara otomatis saat bekerja di folder tersebut.

Kalau kamu tidak ingin maintain file duplikat sama sekali, alternatifnya pakai symlink:

cd modules/payment && ln -s AGENTS.md CLAUDE.md
cd gateway && ln -s AGENTS.md CLAUDE.md
sequenceDiagram
    participant Agent
    participant CC as Claude Code
    participant Root as /CLAUDE.md
    participant Mod as modules/payment/CLAUDE.md

    Agent->>CC: Mulai sesi di root
    CC->>Root: Load @AGENTS.md root (startup)
    Agent->>CC: Task: perbaiki bug di modules/payment/
    CC->>Mod: Baca file di subtree ini
    Mod-->>CC: Lazy-load modules/payment/CLAUDE.md -> @AGENTS.md
    CC-->>Agent: Context gabungan: root + payment module
  • Root CLAUDE.md/AGENTS.md di-load di awal sesi. File nested hanya di-load saat Claude membaca file di subtree itu — bukan otomatis semua sekaligus di awal.
  • Kalau setelah kerja di suatu folder instruksi nested-nya sepertinya belum terbaca, jalankan /memory untuk cek file mana saja yang sudah ter-load.

Menentukan Batas Module: Kapan Perlu AGENTS.md Sendiri

Tidak semua sub-folder butuh AGENTS.md sendiri. Aturan praktis:

BUTUH AGENTS.md sendiri jika folder tersebut:
  ✓ Punya domain/business rule yang berbeda dari parent-nya
  ✓ Sering jadi tempat kerja agent secara terpisah dari folder lain
  ✓ Punya gotcha/invariant spesifik yang tidak berlaku di tempat lain
  ✓ Merupakan boundary kepemilikan tim (meski satu repo, tim beda)

TIDAK BUTUH jika:
  ✗ Isinya hanya kumpulan file generik (misalnya folder utils/ kecil)
  ✗ Konvensi di dalamnya sama persis dengan parent
  ✗ Jarang sekali disentuh, dan kalaupun disentuh cukup 1-2 baris tambahan
    di file parent

Untuk studi kasus e-commerce di atas, payment/gateway/ layak punya file sendiri karena provider eksternal, gotcha webhook, dan rate limit sandbox adalah hal yang sangat spesifik dan berbahaya kalau diabaikan. Sebaliknya, folder seperti payment/testing/ yang isinya cuma helper mock tidak perlu file terpisah — cukup disebut satu baris di payment/AGENTS.md.

Pertanyaan yang lebih sulit biasanya muncul di folder yang berada di tengah-tengah — tidak jelas-jelas butuh, tidak jelas-jelas tidak butuh. Untuk kasus ambigu semacam ini, heuristik yang berguna adalah bertanya: “kalau developer baru ditugaskan khusus mengerjakan folder ini selama seminggu, informasi apa yang akan mereka tanyakan berulang kali di hari pertama?” Kalau jawabannya cukup banyak dan spesifik, itu sinyal folder tersebut layak punya context file sendiri. Kalau jawabannya “sama saja dengan yang sudah ada di parent”, cukup gabungkan ke file yang sudah ada.

Penting juga diingat bahwa keputusan ini bukan sekali jadi. Module yang awalnya kecil dan cukup dijelaskan satu baris di parent bisa berkembang jadi kompleks seiring waktu — misalnya inventory/reservation/ yang tadinya sederhana bisa jadi rumit begitu ditambah logika reservasi multi-warehouse. Review context file secara berkala (idealnya bersamaan dengan review arsitektur module) membantu menangkap momen ketika sebuah sub-folder sudah waktunya “naik kelas” menjadi context file tersendiri.


Directory-Purpose Lines vs Full File Tree

Kesalahan umum saat menulis context file untuk repo besar adalah mencoba mencantumkan seluruh file tree secara detail. Ini boros token dan cepat basi begitu ada file baru.

// ANTI-PATTERN — daftar file lengkap, gampang basi
modules/payment/
  ├── domain/
  │   ├── transaction.go
  │   ├── transaction_state.go
  │   ├── refund.go
  │   ├── charge.go
  │   └── transaction_test.go
  ├── application/
  │   ├── payment_service.go
  │   ├── order_reader.go
  │   └── ...
  [30 baris lagi]

// BENAR — directory-purpose lines, stabil terhadap perubahan file individual
modules/payment/
  domain/          → entity & state machine transaksi (lihat invariant di atas)
  application/      → use case & interface ke module lain
  infrastructure/    → implementasi gateway, repository DB
  gateway/           → integrasi provider eksternal (AGENTS.md sendiri)
  testing/            → mock & fixture untuk unit test

Directory-purpose lines menjelaskan fungsi tiap folder dalam satu baris, bukan isi lengkapnya. Kalau ada file baru ditambahkan di dalam domain/, tidak ada yang perlu diupdate di context file — fungsinya tetap sama.


Mengelola Shared Domain Model Lintas Module

Folder shared/domain/ adalah sumber ambiguitas paling umum di monolith modular — entity seperti Product atau Order dipakai banyak module, tapi siapa yang “memiliki” perubahan terhadapnya?

# shared/AGENTS.md

## Kepemilikan Entity
- Product: dimiliki catalog module. Module lain HANYA membaca lewat
  shared/domain/product.go, tidak boleh menambah field tanpa approval
  dari perubahan di catalog/AGENTS.md juga.
- Order: dimiliki checkout module.
- User: dimiliki user module.

## Aturan Perubahan
Setiap perubahan struct di folder ini WAJIB:
1. Update module AGENTS.md pemilik entity terkait
2. Cek seluruh module consumer (lihat komentar "// used by" di tiap
   struct) untuk breaking change
3. Jalankan make test-all (bukan test-module) karena perubahan di
   sini berdampak lintas module

Pola “kepemilikan eksplisit” ini penting supaya agent tidak asal mengubah shared entity dari module manapun tanpa menyadari dampaknya ke module lain — masalah yang sama seperti kontrak API di microservices, hanya sekarang objeknya struct dalam satu repo yang sama.

  • Shared domain model yang tidak punya kepemilikan eksplisit adalah sumber breaking change paling umum di monolith modular — siapa saja bisa mengubahnya, tidak ada yang merasa bertanggung jawab memvalidasi dampak lintas module.
  • Jangan taruh business logic di shared/domain/ — folder ini seharusnya cuma struct/interface, logic tetap tinggal di module pemiliknya.

Sinkronisasi Context File Saat Module Berubah

Sama seperti kontrak API di multi-service, context file di monolith juga bisa basi. Bedanya, karena semua dalam satu repo, mekanisme deteksinya bisa lebih ketat karena CI punya akses penuh ke seluruh perubahan.

CI Check untuk Deteksi Perubahan Tanpa Update Context

flowchart TD
    A[PR mengubah modules/payment/domain/] --> B{AGENTS.md payment ikut berubah?}
    B -- Ya --> C[Lanjut review normal]
    B -- Tidak, tapi perubahan signifikan* --> D[CI warning: review apakah AGENTS.md perlu update]
    D --> E[Reviewer putuskan: perlu update atau memang tidak relevan]

*“Signifikan” di sini bisa didefinisikan lewat heuristik sederhana — misalnya perubahan pada file yang namanya mengandung state, invariant, atau interface publik di application/.

# contoh step CI sederhana (pseudo)
- name: Check context file freshness
  run: |
    if git diff --name-only origin/main | grep -q "modules/payment/application/"; then
      if ! git diff --name-only origin/main | grep -q "modules/payment/AGENTS.md"; then
        echo "::warning::modules/payment/application/ changed but AGENTS.md was not updated. Please review."
      fi
    fi    

Ini sengaja berupa warning, bukan hard block — karena tidak semua perubahan di folder itu otomatis butuh update context file. Tapi warning ini cukup untuk memicu reviewer (manusia atau agent) mengecek secara sadar.

Instruksi ke Agent di Root AGENTS.md

## Menjaga Context File Tetap Akurat
Jika perubahanmu menambah/menghapus interface publik di application/,
mengubah invariant bisnis, atau menambah dependency baru ke module
lain, update AGENTS.md module terkait sebagai bagian dari task —
sebelum menandai pekerjaan selesai.

Kombinasi instruksi eksplisit (advisory) dan CI warning (safety net) memberi jaminan berlapis — mirip pola yang sama dipakai untuk sinkronisasi kontrak API di multi-service, hanya sekarang dieksekusi dalam satu repo yang sama.


Anti-Pattern yang Harus Dihindari

// ✗ Satu AGENTS.md raksasa di root berisi semua detail semua module
//    -- setiap sesi agent membawa noise dari module yang tidak relevan
// ✓ Root untuk konvensi global saja, detail domain di file per-module

// ✗ Context file mendaftar seluruh file tree secara manual
//    -- cepat basi begitu ada file baru/dihapus
// ✓ Directory-purpose lines yang stabil terhadap perubahan file individual

// ✗ Shared domain model tanpa kepemilikan eksplisit
//    -- siapa saja ubah struct tanpa sadar dampak lintas module
// ✓ Setiap shared entity punya "dimiliki oleh module X" yang jelas

// ✗ CLAUDE.md berisi salinan penuh AGENTS.md (dua sumber kebenaran)
//    -- gampang out of sync begitu salah satu diupdate
// ✓ CLAUDE.md cuma satu baris @AGENTS.md, atau symlink

Checklist Review Context File Monolith

STRUKTUR:
  □ Root AGENTS.md hanya berisi konvensi global, bukan detail per-domain
  □ Tiap module dengan domain rule berbeda punya AGENTS.md sendiri
  □ Folder generik/kecil TIDAK punya AGENTS.md terpisah yang tidak perlu
  □ CLAUDE.md di tiap level cuma import (@AGENTS.md) atau symlink

ISI:
  □ Invariant bisnis dan gotcha spesifik ada di level module, bukan root
  □ Directory-purpose lines dipakai, bukan full file tree manual
  □ Shared domain model punya kepemilikan eksplisit per entity
  □ Tidak ada duplikasi isi antara root dan module

SINKRONISASI:
  □ Ada instruksi eksplisit ke agent soal update context saat task selesai
  □ Ada CI check/warning untuk mendeteksi perubahan signifikan tanpa
    update context file
  □ Root file di-review berkala — apakah masih ringkas atau mulai
    membengkak

Ringkasan

  • Monolith multi-module tetap butuh context engineering — akses teknis penuh ke semua kode tidak menghilangkan masalah context window dan noise antar domain.
  • Tiga layer: root (konvensi global, jarang berubah), module (domain rule spesifik), local (detail sangat spesifik seperti integrasi gateway eksternal).
  • Root AGENTS.md untuk stack, struktur module secara umum, dan aturan cross-module — bukan detail bisnis satu domain.
  • Module AGENTS.md untuk invariant, boundary, dan dependency spesifik domain itu — beda gaya isi antar module (payment fokus idempotency, catalog fokus performance).
  • CLAUDE.md di tiap level cukup satu baris @AGENTS.md atau symlink — supaya mekanisme lazy-load native Claude Code ikut terpicu tanpa duplikasi source of truth.
  • Directory-purpose lines, bukan full file tree — jelaskan fungsi folder dalam satu baris, tahan terhadap perubahan file individual.
  • Shared domain model wajib punya kepemilikan eksplisit per entity — sumber breaking change paling umum kalau diabaikan.
  • Sinkronisasi butuh kombinasi instruksi eksplisit ke agent dan CI warning sebagai safety net, bukan mengandalkan salah satu saja.

Portofolio