Studi Kasus SDD Golang: Order Service E-commerce dari Spec ke Merge
Studi kasus lengkap Specification-Driven Development di Golang: bagaimana cancel order feature Santekno Shop dibuat dari nol menggunakan SDD dengan Claude Code dari spec hingga merge ke production.
Studi Kasus: Order Service E-commerce dari Spec ke Merge
Kita sudah membahas setiap komponen SDD golang secara terpisah. Sekarang saatnya melihat semuanya bekerja bersama dalam satu studi kasus SDD golang order service yang nyata — end-to-end, dari spec pertama yang ditulis hingga PR di-merge ke production.
Studi kasus ini mengilustrasikan implementasi fitur Cancel Order di Santekno Shop. Semua langkah, semua prompt, semua keputusan, semuanya transparan sehingga bisa kamu reproduksi di proyek sendiri.
Timeline: 1,5 hari kerja. Developer: @andi (mid-level Go developer dengan 2 tahun pengalaman).
19.1 Situasi Awal
Senin, 08:30 WIB
Semuanya bermula dari sebuah Jira ticket yang mendarat di board @andi. Isinya sengaja dibiarkan apa adanya agar kamu bisa merasakan titik mula yang realistis — business requirement jelas, tapi spec teknis belum ada.
1SHOP-456: [Order] Implementasikan fitur cancel order untuk customer
2
3Priority: P1 (High)
4Sprint: 23
5
6Business Requirements:
7- Customer bisa cancel order mereka sendiri
8- Stok produk dikembalikan setelah cancel
9- Customer mendapat notifikasi email
10
11Technical Notes:
12- Spec belum ditulis
13- Estimate: 1-2 hari
14
15Assignee: @andiPerhatikan baris “Spec belum ditulis” — ini pemicu keseluruhan alur SDD. Sebelum menyentuh kode, @andi mengecek aturan proyek yang tersimpan di CLAUDE.md untuk memastikan langkah pertama yang benar.
1$ cat CLAUDE.md | head -30
2# CLAUDE.md — Santekno Shop
3
4## Spec Process
5Every new feature MUST:
61. Have spec at specs/domain/[feature].md
72. Be reviewed (label: spec-review on PR)
83. Get approval BEFORE implementation starts
9...Karena CLAUDE.md secara eksplisit mewajibkan spec sebelum implementasi, keputusannya sudah jelas: @andi harus tulis spec dulu sebelum coding. Inilah fungsi CLAUDE.md sebagai gerbang disiplin yang berlaku otomatis.
19.2 Fase 1 — Menulis Spec
Senin, 09:00 WIB
Langkah pertama bukan mengetik kode, melainkan membuka Claude Code dan memberikan konteks yang lengkap sebelum meminta apa pun. Prompt berikut menunjukkan cara @andi menyiapkan latar belakang bisnis, state saat ini, dan meminta pertanyaan klarifikasi lebih dulu.
1Saya mau implement SHOP-456: cancel order feature untuk Santekno Shop.
2Sebelum implementasi, saya perlu menulis spec.
3
4Context:
5- Santekno Shop adalah e-commerce B2C Indonesia
6- Order ada status: PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
7- Saat ini tidak ada mekanisme cancel
8
9Business requirements dari ticket:
10- Customer bisa cancel order mereka sendiri
11- Stok produk dikembalikan setelah cancel
12- Customer mendapat notifikasi email (via Notification Service via Kafka)
13
14Bantu saya draft spec untuk fitur ini.
15Gunakan template di specs/_templates/feature-spec.md.
16Ajukan pertanyaan klarifikasi yang perlu dijawab sebelum spec bisa ditulis.Kunci prompt ini adalah instruksi terakhir: minta klarifikasi dulu, jangan langsung menulis. Dengan begitu AI tidak mengisi ambiguitas dengan asumsi. Berikut pertanyaan yang diajukan Claude sebagai respons.
1Sebelum draft spec, ada beberapa hal yang perlu dikonfirmasi:
2
31. Cancel window: apakah ada batas waktu untuk cancel? Contoh: hanya bisa
4 cancel dalam 15 menit setelah order dibuat?
5
62. Status restriction: apakah hanya PENDING yang bisa di-cancel, atau
7 CONFIRMED juga bisa di-cancel dalam kondisi tertentu?
8
93. Refund: apakah ada proses refund terkait cancel? Atau hanya stock restore?
10
114. Concurrent cancel: jika dua request cancel masuk bersamaan, bagaimana
12 yang seharusnya terjadi?
13
145. Admin cancel: apakah admin juga bisa cancel order customer?
15 (beda scope dari customer cancel)Kelima pertanyaan ini persis area yang paling sering menimbulkan bug jika dibiarkan implisit. @andi tidak menjawab sendiri — ia membawa pertanyaan ini ke PM dan tech lead, lalu kembali dengan jawaban resmi berikut.
1Jawaban setelah diskusi dengan @budi dan @maya:
2
31. Cancel window: 30 menit dari order created (updated dari awal 15 menit
4 berdasarkan user research yang ada)
5
62. Status: hanya PENDING yang bisa di-cancel. CONFIRMED sudah dalam
7 proses fulfillment.
8
93. Refund: tidak ada refund di scope ini (order belum ada payment flow).
10 Hanya stock restore.
11
124. Concurrent: hanya satu yang berhasil, yang lain error.
13
145. Admin cancel: out of scope untuk ticket ini. Beda ticket.Perhatikan poin 1: angka berubah dari 15 menjadi 30 menit — sebuah keputusan bisnis yang baru muncul justru karena ada proses klarifikasi. Dengan jawaban lengkap ini, @andi kembali ke Claude Code untuk meminta draft spec final.
1Terima kasih. Dengan konfirmasi tersebut, draft spec untuk cancel order:
2
3- Cancel window: 30 menit dari order created
4- Hanya status PENDING
5- Tidak ada refund, hanya stock restore
6- Concurrent: hanya satu berhasil
7- Notification via Kafka event
8- Customer cancel saja (bukan admin)
9
10Format sesuai specs/_templates/feature-spec.mdClaude menghasilkan draft spec yang kemudian di-review dan di-edit @andi. Setelah draft rampung, spec di-commit dan dibuat menjadi PR agar bisa direview seperti kode biasa. Perintah git berikut merekam commit spec beserta ringkasan keputusan.
1git checkout -b spec/SHOP-456-cancel-order
2git add specs/order/cancel-order.md
3git commit -m "docs(spec): add cancel order spec SHOP-456
4
5Spec for customer cancel order feature:
6- Cancel window: 30 minutes from order creation
7- PENDING status only
8- Stock restore on cancel
9- ORDER_CANCELLED Kafka event
10- Concurrent cancel: first wins
11
12Status: DRAFT → pending review"
13
14git push origin spec/SHOP-456-cancel-orderDengan spec masuk ke PR berlabel spec-review, dokumen kebutuhan kini punya jejak versi dan bisa diberi komentar per baris — persis seperti kode. Senin, 10:30 WIB, PR spec siap direview.
19.3 Fase 2 — Spec Review
Senin, 11:00 WIB
Spec bukan dokumen yang langsung dipakai; ia harus melewati review. @budi (tech lead) dan @maya (PM) memberi feedback yang menyentuh detail teknis maupun keputusan bisnis. Berikut catatan review yang mereka tinggalkan.
1Review dari @budi:
2
31. AC5 perlu lebih explicit: "stock restore" harus atomic
4 Tambahkan: "dalam satu database transaction dengan status update"
5
62. EC1 (concurrent cancel) perlu dijelaskan implementation expectation:
7 "Gunakan SELECT FOR UPDATE untuk prevent race condition"
8
93. NFR-P2: latency target terlalu longgar (500ms).
10 Saat ini kita sudah di 120ms untuk create order.
11 Suggest: p95 < 300ms untuk cancel (simpler operation)
12
13Review dari @maya:
14
154. AC di spec bilang "30 menit" tapi ticket original bilang "15 menit".
16 Konfirmasi yang dipakai adalah 30 menit (per user research terbaru)?Feedback ini membuktikan spec review bukan formalitas: @budi menegaskan atomicity dan mekanisme locking, sementara @maya menutup celah inkonsistensi angka. @andi merespons dengan ringkas dan langsung push perubahan.
1@budi: Updated AC5, EC1, dan NFR-P2 — changes di-push.
2@maya: Ya, 30 menit per konfirmasi hari ini. Ticket akan di-update.Setelah revisi diterima, spec di-approve dan di-merge sebagai baseline yang mengikat implementasi. Senin, 13:00 WIB, status spec berpindah dari DRAFT ke APPROVED.
1# Spec PR di-merge ke main
2# Spec status: DRAFT → APPROVEDSejak titik ini, spec cancel-order.md v1.0 menjadi sumber kebenaran tunggal — setiap keputusan implementasi harus bisa dilacak balik ke sini.
19.4 Fase 3 — Plan Mode
Senin, 13:30 WIB
Dengan spec yang sudah approved, godaan untuk langsung menulis kode besar. Tapi SDD menyisipkan satu langkah lagi: minta rencana implementasi lebih dulu. Prompt berikut secara eksplisit melarang penulisan kode dan hanya meminta plan.
1Spec cancel-order.md v1.0 sudah di-approve.
2
3SEBELUM menulis kode, buat implementation plan untuk:
4- File yang dibuat/dimodifikasi
5- SQL queries untuk CancelWithStockRestore
6- DB migration yang diperlukan
7- Dependency order
8- Risks berdasarkan EC1-EC3 di spec
9
10Referensi pola: internal/usecase/order/create_order.go
11
12Jangan tulis kode. Hanya plan.Plan dihasilkan dalam lima menit, dan justru di sinilah nilai Plan Mode terbukti: @andi menyadari ada dependency arsitektur yang belum jelas dan menanyakannya sebelum menulis apa pun.
1Di plan, kamu menyebut perlu interface baru EventPublisher.
2Apakah interface ini sudah ada di codebase?
3
4Cek di internal/domain/ dan internal/kafka/ apakah ada yang bisa dipakai.Claude mengecek dan menemukan internal/kafka/publisher.go sudah punya concrete struct tapi belum ada interface, sehingga @andi memutuskan membuat interface baru. Senin, 14:00 WIB, plan di-approve secara internal dan siap dipecah menjadi task. Pelajarannya: Plan Mode menangkap kebutuhan arsitektur sebelum ia menjadi rework di tengah implementasi.
19.5 Fase 4 — Task Breakdown
Dari plan yang sudah disepakati, langkah berikutnya adalah memecahnya menjadi task-task kecil yang bisa dieksekusi dan di-commit terpisah. Prompt berikut meminta breakdown dengan estimasi waktu dan Definition of Done.
1Berdasarkan plan, buat task breakdown dengan:
2- Estimasi waktu per task
3- Definition of Done untuk setiap task
4- Dependency order
5
6Target: setiap task 30-90 menit.Hasilnya adalah daftar sebelas task berurutan dengan estimasi total 6,5 jam, di mana setiap task punya cakupan yang cukup kecil untuk direview dengan teliti.
1Task 1: Entity update [30 min] → entity.go: StatusCancelled + CanBeCancelled(30 min window)
2Task 2: EventPublisher interface [20 min] → internal/domain/event/publisher.go
3Task 3: Repository interface [20 min] → domain/order/repository.go: 2 new methods
4Task 4: Mock regeneration [10 min] → mockgen untuk updated interface
5Task 5: DB migration [15 min] → index: idx_orders_user_id_status
6Task 6: Repository implementation [90 min] → SQL + transaction + SELECT FOR UPDATE
7Task 7: UseCase implementation [60 min] → cancel_order.go dengan full business logic
8Task 8: UseCase tests [75 min] → 7 test cases per AC/EC mapping
9Task 9: HTTP handler [45 min] → CancelOrder handler di order_handler.go
10Task 10: Route registration [10 min] → router.go
11Task 11: Smoke test [20 min] → curl manual test
12
13Total estimate: ~6.5 jamBreakdown ini bukan sekadar to-do list: urutannya menghormati dependency (entity → interface → mock → implementasi → handler), sehingga @andi bisa maju tanpa mundur. Ia mencatat daftar ini di Jira dan mulai implementasi.
19.6 Fase 5 — Implementasi Incremental
Senin, 14:30 WIB
Task 1: Entity Update
Implementasi dimulai dari lapisan paling dalam: domain entity. Prompt berikut mereferensikan AC spesifik agar kode yang dihasilkan bisa langsung ditelusuri ke spec.
1Implementasikan Task 1: tambah ke internal/domain/order/entity.go:
21. StatusCancelled Status = "CANCELLED"
32. Method CanBeCancelled() bool
4 - return true jika Status == StatusPending
5 AND time.Since(CreatedAt) < 30*time.Minute (sesuai spec v1.0)
6
7Spec reference: cancel-order.md v1.0 AC3, AC4Setelah output diterima dan di-review, @andi langsung menjalankan test dan meng-commit dengan pesan yang mereferensikan AC. Perintah berikut merekam siklus test-lalu-commit itu.
1go test ./internal/domain/order/...
2# OK, 100% pass
3git commit -m "feat(order): add StatusCancelled and CanBeCancelled method
4
5Implements:
6- cancel-order.md v1.0 AC3: PENDING status check
7- cancel-order.md v1.0 AC4: 30-minute cancellation window"Commit yang menyebut AC3 dan AC4 secara eksplisit membuat git log menjadi jejak traceability: setiap baris kode punya alasan yang tertaut ke spec.
Task 2-4: Interface dan Mock
Tiga task berikutnya dikelompokkan karena saling bergantung — interface, penambahan method repository, lalu regenerasi mock. Prompt berikut merangkum ketiganya.
1Implementasikan Tasks 2-4:
21. Interface EventPublisher di internal/domain/event/publisher.go
3 Method: PublishOrderCancelled(ctx, orderID, userID uuid.UUID) error
4
52. Tambah ke OrderRepository interface:
6 - GetByIDAndUserID(ctx, orderID, userID uuid.UUID) (*Order, error)
7 - CancelWithStockRestore(ctx, orderID uuid.UUID) error
8
93. Regenerate mocks dengan mockgenKarena interface dibuat sebelum implementasi konkret, unit test di lapisan usecase nanti bisa memakai mock tanpa menunggu repository selesai. Senin, 15:30 WIB, Tasks 1-4 selesai dengan tiga commit.
Task 5: DB Migration
Sebelum menulis query, index pendukung disiapkan lewat migration. Skrip berikut membuat index secara CONCURRENTLY agar aman untuk zero-downtime deployment.
1# migration dibuat manual
2cat > migrations/20250701001_cancel_order_index.sql << 'EOF'
3-- Cancel Order: add index for GetByIDAndUserID query
4-- Spec: cancel-order.md v1.0 NFR performance
5CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_orders_user_id_status
6ON orders(user_id, status) WHERE deleted_at IS NULL;
7EOF
8
9# Test di local
10psql $DATABASE_URL -f migrations/20250701001_cancel_order_index.sql
11# \d orders → verify index exists
12
13git commit -m "feat(order): add index for cancel order query
14
15Supports GetByIDAndUserID performance
16Index: idx_orders_user_id_status on (user_id, status)"Menaruh index lebih awal memastikan target NFR performance bukan pikiran belakangan, melainkan bagian dari desain query sejak baris pertama.
Task 6: Repository Implementation
Senin, 16:00 WIB
Inilah task paling berisiko karena menyentuh transaksi dan locking. Prompt berikut mengikat setiap method ke AC dan EC yang relevan, serta menunjuk pola yang sudah ada sebagai referensi.
1Implementasikan dua method di internal/repository/postgres/order_repository.go:
2
31. GetByIDAndUserID — return nil, nil jika tidak ditemukan atau bukan owner
4 Sesuai spec AC7, AC8: keduanya return error yang sama (security)
5
62. CancelWithStockRestore — transaction dengan SELECT FOR UPDATE
7 Sesuai spec AC5: atomic, EC1: concurrent protection
8
9Referensi pola: GetByID method yang sudah ada untuk scan pattern.
10Referensi SQL: dari plan yang sudah kita buat.@andi tidak menerima output mentah-mentah — ia mereview SQL dengan teliti dan menuliskan penalarannya sebagai komentar untuk memverifikasi bahwa EC1 benar-benar tercakup.
1// Verify EC1 implementation:
2// SELECT id FROM orders WHERE id = $1 FOR UPDATE
3// → lock diperoleh, concurrent request akan wait
4// → setelah 3 detik timeout, lock expire dan request gagalDari review itu muncul satu keputusan judgment yang tidak diusulkan AI: menambahkan context timeout untuk pengambilan lock, agar request yang menunggu tidak menggantung selamanya.
1// @andi manual addition: 3s timeout for lock acquisition (EC1 protection)
2cancelCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
3defer cancel()
4err = tx.QueryRow(cancelCtx, `SELECT id FROM orders WHERE id = $1 FOR UPDATE`, orderID).Scan(&id)Penambahan timeout tiga detik ini adalah contoh AI sebagai accelerator, bukan pengganti judgment: keputusan proteksi race condition tetap datang dari developer. Setelah itu, integration test dijalankan dan hasilnya di-commit.
1# Integration test dengan test DB
2go test -tags=integration ./internal/repository/postgres/...
3# 3 tests pass
4
5git commit -m "feat(order): implement cancel order repository methods
6
7- GetByIDAndUserID: returns nil,nil for not found OR not owner (security)
8- CancelWithStockRestore: atomic with SELECT FOR UPDATE (EC1)
9
10Implements: cancel-order.md v1.0 AC2, AC5, EC1"Dengan repository lulus integration test, fondasi data untuk cancel order sudah solid dan aman terhadap concurrent access.
Task 7: UseCase Implementation
Senin, 16:45 WIB
Lapisan usecase mengorkestrasi semua komponen di bawahnya. Prompt berikut menuliskan flow, tipe error, dan mapping ke AC/EC secara eksplisit agar tidak ada logika bisnis yang mengambang.
1Implementasikan CancelOrderUseCase di internal/usecase/order/cancel_order.go.
2
3Sesuai spec cancel-order.md v1.0:
4- Input: CancelOrderInput { OrderID, UserID uuid.UUID }
5- Dependencies: OrderRepository, EventPublisher
6- Flow: GetByIDAndUserID → CanBeCancelled check → CancelWithStockRestore → Publish
7
8Error types:
9- ErrOrderNotFound (AC7, AC8)
10- OrderNotCancellableError { CurrentStatus } (AC9)
11- CancelWindowExpiredError (AC10)
12
13EC2: Kafka failure = log warning, tidak rollback cancel
14
15Pattern dari: internal/usecase/order/create_order.goKarena flow dan setiap tipe error sudah dipetakan ke AC/EC di prompt, kode yang dihasilkan langsung dapat diverifikasi terhadap spec. Senin, 17:15 WIB, usecase selesai dan @andi memutuskan melanjutkan ke test selagi masih ada waktu.
Task 8: UseCase Tests
Test ditulis bukan asal-asalan, melainkan sebagai pemetaan langsung dari AC/EC ke skenario. Prompt berikut menamai setiap test sesuai item spec yang di-cover-nya.
1Implementasikan unit tests untuk CancelOrderUseCase.
2
3Tests yang diperlukan (sesuai mapping spec → test):
41. TestCancelOrder_PendingOrder_Success → AC1-AC6
52. TestCancelOrder_OrderNotFound → AC7
63. TestCancelOrder_OrderNotOwnedByUser → AC8
74. TestCancelOrder_ConfirmedOrder → AC9
85. TestCancelOrder_WindowExpired → AC10
96. TestCancelOrder_ConcurrentCancel_SecondFails → EC1 (via mock)
107. TestCancelOrder_KafkaFails_StillSucceeds → EC2
11
12Gunakan testify/suite + gomock
13Package: package order_test
14Reference: internal/usecase/order/create_order_test.goPenamaan test yang mereferensikan AC/EC membuat hasil test menjadi laporan compliance yang bisa dibaca. Senin, 17:55 WIB, @andi menjalankan seluruh suite dengan race detector.
1go test -race -v -cover ./internal/usecase/order/...
2
3=== RUN TestCancelOrderUseCaseSuite
4--- PASS: TestCancelOrder_PendingOrder_Success (0.001s)
5--- PASS: TestCancelOrder_OrderNotFound (0.000s)
6--- PASS: TestCancelOrder_OrderNotOwnedByUser (0.000s)
7--- PASS: TestCancelOrder_ConfirmedOrder (0.001s)
8--- PASS: TestCancelOrder_WindowExpired (0.001s)
9--- PASS: TestCancelOrder_ConcurrentCancel_SecondFails (0.001s)
10--- PASS: TestCancelOrder_KafkaFails_StillSucceeds (0.001s)
11PASS
12coverage: 93.7% of statements in ./internal/usecase/order/...Tujuh test hijau dengan coverage 93,7% berarti setiap AC/EC utama sudah terkunci oleh test — akhir hari Senin ditutup dengan Tasks 1-8 selesai dan enam commit.
19.7 Fase 6 — Handler dan Integrasi
Selasa, 09:00 WIB
Task 9: HTTP Handler
Lapisan handler menerjemahkan error domain menjadi HTTP status dan error code. Prompt berikut memetakan setiap tipe error ke status code sesuai spec agar respons API konsisten.
1Implementasikan CancelOrder method di internal/delivery/http/handler/order_handler.go
2untuk Echo v4.
3
4Error mapping sesuai spec:
5- ErrOrderNotFound → 404, error_code: "ORDER_NOT_FOUND"
6- *OrderNotCancellableError → 409, error_code: dari error.ErrorCode()
7- *CancelWindowExpiredError → 409, error_code: dari error.ErrorCode()
8- Error lain → 500, log ErrorContext
9
10Path param: :id (UUID)
11Auth: userID dari c.Get("userID").(uuid.UUID)
12Success: 204 No Content (spec AC6)Untuk mengunci kontrak error code agar tidak diam-diam bergeser, @andi menambahkan handler test yang meng-assert string error code secara persis.
1// Verify spec AC9 error code exact match
2func (s *OrderHandlerSuite) TestCancelOrder_ConfirmedOrder_Returns409WithCorrectCode() {
3 mockUC.EXPECT().Execute(gomock.Any(), gomock.Any()).
4 Return(&cancelorder.OrderNotCancellableError{CurrentStatus: "CONFIRMED"})
5
6 // ... execute ...
7
8 var body map[string]interface{}
9 json.Unmarshal(rec.Body.Bytes(), &body)
10
11 // Spec compliance assertion — prevents spec drift
12 s.Equal("ORDER_NOT_CANCELLABLE", body["error_code"],
13 "error_code must match spec cancel-order.md AC9 exactly")
14}Assertion string eksak seperti ini adalah pagar anti spec drift: jika suatu hari error code diubah, test langsung gagal di CI sebelum sampai production.
Task 10: Route Registration
Setelah handler ada, ia perlu didaftarkan ke router dengan middleware auth. Cuplikan berikut menandai perubahan minimal di router.
1# Di internal/delivery/http/router/router.go
2# Tambah: DELETE /api/v1/orders/:id → handler.CancelOrder (dengan auth middleware)Route DELETE yang dilindungi auth middleware memastikan endpoint cancel hanya bisa diakses user terautentikasi, sejalan dengan AC keamanan di spec.
Task 11: Smoke Test
Sebelum percaya pada unit test saja, @andi menjalankan smoke test manual terhadap server lokal untuk memverifikasi perilaku end-to-end. Serangkaian curl berikut menguji happy path hingga kasus tanpa auth.
1# Start local server
2go run cmd/server/main.go &
3
4# Test 1: Happy path
5curl -X DELETE http://localhost:8080/api/v1/orders/$(PENDING_ORDER_ID) \
6 -H "Authorization: Bearer $(TEST_JWT)"
7# Expected: 204 No Content ✅
8
9# Test 2: Order not found
10curl -X DELETE http://localhost:8080/api/v1/orders/00000000-0000-0000-0000-000000000000 \
11 -H "Authorization: Bearer $(TEST_JWT)"
12# Expected: {"error_code":"ORDER_NOT_FOUND","message":"order not found"} ✅
13
14# Test 3: Already confirmed
15curl -X DELETE http://localhost:8080/api/v1/orders/$(CONFIRMED_ORDER_ID) \
16 -H "Authorization: Bearer $(TEST_JWT)"
17# Expected: {"error_code":"ORDER_NOT_CANCELLABLE","message":"order cannot be cancelled: status is CONFIRMED"} ✅
18
19# Test 4: No auth
20curl -X DELETE http://localhost:8080/api/v1/orders/$(PENDING_ORDER_ID)
21# Expected: {"error_code":"UNAUTHORIZED","message":"authentication required"} ✅Keempat smoke test menghasilkan respons persis seperti yang dispesifikasikan, memberi keyakinan bahwa integrasi handler-router-usecase bekerja utuh sebelum masuk ke tahap PR.
19.8 Fase 7 — Spec Compliance Final Check
Selasa, 11:00 WIB
Sebelum membuka PR, @andi menjalankan compliance check menyeluruh — meminta AI memetakan setiap AC/EC ke lokasi implementasi dan test-nya. Prompt berikut menstrukturkan permintaan itu.
1Lakukan spec compliance final check.
2
3Spec: specs/order/cancel-order.md v1.0
4
5Files yang diimplementasikan:
6- internal/domain/order/entity.go (CanBeCancelled)
7- internal/usecase/order/cancel_order.go (full usecase)
8- internal/repository/postgres/order_repository.go (2 new methods)
9- internal/delivery/http/handler/order_handler.go (CancelOrder)
10- internal/delivery/http/router/router.go (new route)
11
12Untuk setiap AC (AC1-AC10) dan EC (EC1-EC3):
13- Di mana terimplementasikan?
14- Apakah ada test yang cover?
15- Apakah ada gap?Output-nya berupa tabel yang mengubah pertanyaan subjektif “apakah sudah sesuai spec?” menjadi laporan terstruktur per item.
| Item | Status | File | Test? |
|---|---|---|---|
| AC1 | ✅ | handler.go:87 | ✅ |
| AC2 | ✅ | repo.go:134 | ✅ |
| AC3 | ✅ | entity.go:42 | ✅ |
| AC4 | ✅ | entity.go:43 | ✅ |
| AC5 | ✅ | repo.go:156 | ✅ |
| AC6 | ✅ | handler.go:112 | ✅ |
| AC7 | ✅ | usecase.go:48 | ✅ |
| AC8 | ✅ | repo.go:134 | ✅ |
| AC9 | ✅ | handler.go:94 | ✅ |
| AC10 | ✅ | handler.go:100 | ✅ |
| EC1 | ✅ | repo.go:156 | ✅ (via mock) |
| EC2 | ✅ | usecase.go:68 | ✅ |
| EC3 | ⚠️ | - | Context timeout dari middleware covers this. JIRA-789. |
Tabel ini menutup laporan dengan angka jelas: 12 dari 13 item fully covered dan satu gap yang diakui secara terbuka (EC3). Karena tidak ada gap tersembunyi, @andi puas dan siap membuat PR.
19.9 Fase 8 — Pull Request
Selasa, 11:30 WIB
PR yang baik menceritakan seluruh perjalanan, bukan hanya diff. Deskripsi PR berikut menautkan spec, tabel compliance, coverage, daftar perubahan, dan checklist verifikasi.
1## Cancel Order Feature — SHOP-456
2
3### Summary
4Implementasi cancel order endpoint untuk customer.
5Customer bisa cancel order yang masih dalam status PENDING
6dalam 30 menit setelah order dibuat.
7
8### Spec Reference
9- **Spec file:** specs/order/cancel-order.md
10- **Spec version:** v1.0
11- **Spec status:** APPROVED (by @budi + @maya, 2025-07-01)
12- **PR:** #231 (spec PR)
13
14### Spec Compliance
15Semua AC (AC1-AC10) ter-implement. 2/3 EC fully covered.
16
17| AC/EC | Status | Notes |
18|-------|--------|-------|
19| AC1-AC6 (happy path) | ✅ | Full implementation |
20| AC7, AC8 | ✅ | Security: same error for not-found and not-owner |
21| AC9 | ✅ | OrderNotCancellableError → 409 |
22| AC10 | ✅ | CancelWindowExpiredError → 409 |
23| EC1 | ✅ | SELECT FOR UPDATE in transaction |
24| EC2 | ✅ | Best effort Kafka + log warning |
25| EC3 | ⚠️ | Context from HTTP middleware covers this. Tracked: JIRA-789 |
26
27### Test Coverage
28coverage: 93.7% of statements in ./internal/usecase/order/...
29
30### Changes
31- internal/domain/order/entity.go → StatusCancelled, CanBeCancelled()
32- internal/domain/event/publisher.go → EventPublisher interface (new)
33- internal/domain/order/repository.go → 2 new interface methods
34- internal/repository/postgres/order_repository.go → 2 new implementations
35- internal/usecase/order/cancel_order.go → full usecase (new)
36- internal/usecase/order/cancel_order_test.go → 7 test cases (new)
37- internal/delivery/http/handler/order_handler.go → CancelOrder method
38- internal/delivery/http/router/router.go → route registration
39- migrations/20250701001_cancel_order_index.sql → performance index
40
41### Checklist
42- [x] Spec compliance self-review done
43- [x] All high-priority AC/EC have tests (EC3 tracked separately)
44- [x] Error codes match spec exactly
45- [x] HTTP status codes match spec
46- [x] No sensitive data in logs
47- [x] Migration added with CONCURRENTLY for zero-downtimeDeskripsi selengkap ini membuat reviewer bisa memverifikasi klaim tanpa menebak — setiap item punya bukti yang bisa dicek. PR pun masuk ke antrean review dengan konteks penuh.
19.10 Fase 9 — Code Review
Selasa, 13:00 WIB
Karena spec sudah jelas, review bisa fokus ke kualitas teknis alih-alih menebak perilaku. Feedback @budi berikut memisahkan pujian yang wajar dari catatan yang perlu ditindaklanjuti.
1@budi review — General:
2✅ Spec compliance sangat baik. Semua AC ter-cover.
3✅ Error codes match spec exactly (cek AC9 dan AC10).
4✅ Test coverage 93.7% melewati target 85%.
5
6@budi review — Technical:
71. ⚠️ Line 87 di order_handler.go: userID extraction tidak ada nil check
8 jika middleware tidak set "userID" key.
9
10 Current:
11 userID, ok := c.Get("userID").(uuid.UUID)
12 if !ok {
13 return c.JSON(http.StatusInternalServerError, ...)
14 }
15
16 Ini OK untuk sekarang karena middleware wajib, tapi tambahkan comment
17 tentang assumption ini.
18
192. ✅ SELECT FOR UPDATE implementation di repo — sangat bagus.
20 Sama persis dengan pola yang direkomendasikan di ADR-003.
21
223. 💬 Suggestion (non-blocking): di cancel_order.go baris 68,
23 bisa extract context key "userID" ke constant untuk avoid magic string.Feedback ini bertipe “polish”, bukan “salah paham fitur” — bukti bahwa spec yang matang menggeser fokus review ke hal yang benar-benar bernilai. @andi menanggapi dan langsung menindaklanjuti.
1@budi:
21. Updated — ditambahkan comment tentang middleware assumption
32. Thanks!
43. Good point, extracted ke OrderHandlerContextKey constant. Update pushed.Selasa, 14:30 WIB, @budi approve dan @andi merge. Siklus review yang mulus ini adalah dividen dari investasi spec di awal.
19.11 Fase 10 — Post-Merge
Selasa, 15:00 WIB
Merge bukan garis akhir; ada housekeeping yang menjaga jejak keputusan tetap rapi. Rangkaian langkah berikut menutup ticket, memperbarui status spec, dan memberi tahu stakeholder.
Pertama, ticket Jira ditutup dengan catatan yang menautkan spec dan gap yang diketahui.
1Status: SHOP-456 → Done
2Note: Implementation merged to main branch.
3Spec: specs/order/cancel-order.md updated to ACTIVE status.
4Migration will run automatically via deployment pipeline.
5Known gap: EC3 tracked in JIRA-789Kedua, status spec dinaikkan ke ACTIVE sehingga dokumen mencerminkan realita production.
1# specs/order/cancel-order.md
2
3## Status: ACTIVE
4**Implemented in:** PR #232
5**Merged:** 2025-07-01
6**Deploy:** Staging (auto), Production (next release cycle)Terakhir, stakeholder diberi tahu lewat Slack agar tim punya visibilitas dan trade-off yang diketahui tetap transparan.
1Slack #engineering:
2"🚀 SHOP-456 merged: Cancel Order feature is now in main.
3Spec: cancel-order.md v1.0
4Coverage: 93.7%
5Will deploy to staging via CD pipeline.
6Known gap: EC3 (DB timeout) tracked in JIRA-789"Ketiga langkah post-merge ini memastikan pengetahuan tidak menguap: siapa pun yang membuka fitur ini enam bulan lagi bisa menemukan spec, PR, dan gap yang diketahui dalam hitungan menit.
19.12 Metrics dari Studi Kasus Ini
Angka bicara lebih jujur daripada kesan. Rincian waktu berikut memecah 12,5 jam kerja ke setiap fase sehingga kamu bisa melihat ke mana waktu benar-benar mengalir.
1Fase 1 — Menulis Spec: 2 jam (termasuk klarifikasi dengan PM)
2Fase 2 — Spec Review: 2 jam (review + revisi + approval)
3Fase 3 — Plan Mode: 30 menit
4Fase 4 — Task Breakdown: 20 menit
5Fase 5-6 — Implementasi: 5 jam
6Fase 7 — Final Compliance Check: 20 menit
7Fase 8 — PR: 30 menit
8Fase 9 — Code Review: 1.5 jam (review + revisi + approval)
9Fase 10 — Post-merge: 20 menit
10
11Total: ~12.5 jam (sekitar 1.5 hari kerja)Yang menarik dari distribusi ini: spec dan implementasi masing-masing menyerap 40% waktu. Rangkuman berikut menyoroti proporsi dan hasil konkret yang didapat.
- Spec-related: 5 jam (40%)
- Implementation: 5 jam (40%)
- Review: 2 jam (16%)
- Admin/other: 30 menit (4%)
Hasil akhirnya: coverage 93,7%, nol issue di staging, nol pertanyaan klarifikasi dari reviewer tentang behavior, semua AC ter-trace ke test, dan 12/13 spec item fully covered. Angka-angka ini adalah bukti bahwa waktu di spec berbunga di fase-fase berikutnya.
19.13 Pelajaran dari Studi Kasus
Pelajaran 1: Waktu di Spec Menghemat Waktu di Implementasi
2 jam di spec = tidak ada revisi besar selama implementasi. @andi tidak perlu kembali ke PM untuk tanya “apakah 15 atau 30 menit?” karena sudah ada di spec yang di-approve.
Pelajaran 2: Plan Mode Mencegah Dead End
Tanpa Plan Mode, @andi mungkin tidak melihat bahwa perlu buat EventPublisher interface sebelum mulai usecase. Plan Mode catch dependency ini sebelum coding dimulai.
Pelajaran 3: Test dari Spec Memberikan Confidence
Ketika @budi review, tidak ada pertanyaan “apakah kasus X sudah di-handle?” Jawabnya ada di test yang explicitly reference EC/AC dari spec.
Pelajaran 4: Spec Drift Dicegah dari Hari Pertama
Test yang assert exact string "ORDER_NOT_CANCELLABLE" memastikan error code tidak bisa berubah tanpa test fail di CI.
Pelajaran 5: AI sebagai Accelerator, Bukan Pengganti Judgment
@andi yang memutuskan menambahkan context timeout untuk SELECT FOR UPDATE — bukan Claude. @andi yang mendeteksi spec mengatakan 30 menit bukan 15. AI mengakselerasi eksekusi keputusan yang dibuat @andi.
19.14 Apa yang Tidak Dimasukkan (dan Kenapa)
Sama pentingnya dengan apa yang dibangun adalah apa yang sengaja ditunda — dan alasannya harus eksplisit. Daftar berikut merangkum keputusan scope beserta cara memutuskannya.
Yang sengaja di-skip:
- Idempotency key: scope belum diputuskan (JIRA-790)
- Rate limiting: juga di middleware, beda PR (JIRA-791)
- EC3 explicit timeout: context dari middleware sudah cover
Cara memutuskan apa yang di-skip:
- Apakah ada spec-nya? Jika tidak ada, tidak implement
- Jika ada di spec tapi out of scope ticket, buat tracking ticket
- Tidak ada “implementasi inisiatif” di luar spec
Aturan sederhana ini — tidak ada kode tanpa spec, tidak ada scope tersembunyi — mencegah PR membengkak dan menjaga setiap keputusan tetap dapat dilacak.
19.15 Alternative: Jika Tidak Ada Spec Dari Awal
Untuk mengukur nilai SDD, bandingkan dengan skenario tandingannya: langsung coding tanpa spec. Estimasi realistis berikut menunjukkan bagaimana back-and-forth justru menggerus waktu yang seolah dihemat di awal.
1Tanpa SDD:
2- Implementasi awal: 3 jam
3- PR #1: "implement cancel order"
4- Review comments:
5 "Apa yang terjadi jika cancel setelah 30 menit?" → revisi
6 "Kenapa timeout 15 menit bukan 30?" → revisi, diskusi dengan PM
7 "EC1 concurrent cancel tidak di-handle" → revisi
8 "Test coverage hanya 45%" → tambah test
9- PR #2 (revisi): 2 jam lagi
10- Review #2: "Error code 'NOT_CANCELLABLE' harusnya 'ORDER_NOT_CANCELLABLE'" → revisi lagi
11- PR #3: 1 jam
12
13Total: ~6 jam + lebih banyak back-and-forth
14Coverage: 45-60% (common untuk code-first approach)
15Keputusan bisnis (30 menit window): mungkin di-discover di review, bukan di awalPerhatikan bahwa jalur tanpa SDD terlihat lebih cepat di atas kertas (6 jam vs 12,5 jam), tapi menghasilkan coverage rendah, kode rapuh, dan keputusan bisnis yang baru muncul di review. SDD menukar waktu di depan dengan spec yang tetap berharga jauh setelah PR di-merge.
19.16 Reproduksi di Project Kamu
Studi kasus ini bukan cerita sekali pakai — polanya bisa kamu terapkan besok pagi. Empat langkah berikut merangkum cara mereproduksinya di proyek sendiri.
1Step 1: Setup
2- Buat atau update CLAUDE.md dengan spec rules
3- Buat template spec di specs/_templates/feature-spec.md
4- Setup PR template dengan spec reference section
5
6Step 2: Pilih fitur kecil sebagai pilot
7- Pilih fitur yang belum di-implement tapi sudah clear business requirements
8- Jangan mulai dengan fitur yang paling complex
9
10Step 3: Follow the workflow
111. Klarifikasi dengan stakeholder
122. Tulis spec dengan Claude Code
133. Spec review (minimal tech lead)
144. Plan Mode → task breakdown
155. Implementasi incremental
166. Test dari spec
177. Spec compliance final check
188. PR dengan spec reference
199. Code review (spec + code)
20
21Step 4: Retrospective setelah pilot
22- Apa yang berjalan baik?
23- Apa yang perlu disesuaikan untuk tim kamu?
24- Update CLAUDE.md dan templateKunci dari keempat langkah ini adalah memulai kecil: satu fitur pilot yang tidak terlalu kompleks cukup untuk membuktikan alur SDD di konteks tim kamu sendiri sebelum di-scale.
19.17 Tools yang Digunakan dalam Studi Kasus
Agar reproduksi tidak menebak-nebak, berikut daftar lengkap stack yang dipakai sepanjang studi kasus ini — dari AI hingga database.
1AI:
2- Claude Code (claude-sonnet-4-20250514)
3
4Testing:
5- go test -race -cover
6- testify/suite v1.9
7- gomock v1.6
8
9Quality:
10- golangci-lint (standard config)
11
12Database:
13- PostgreSQL 15 (local via Docker)
14
15Version Control:
16- Git dengan conventional commits
17- Branch per spec + per implementation
18
19Documentation:
20- Markdown untuk spec
21- PR template di GitHubTidak ada tool eksotis di daftar ini — justru itu poinnya: SDD berjalan di atas stack Go standar, sehingga hambatan adopsinya rendah untuk tim mana pun.
19.18 Tips & Gotchas dari Pengalaman Langsung
💡 Tip 1: Mulai spec dengan pertanyaan klarifikasi, bukan asumsi
@andi bisa langsung nulis spec tapi dengan klarifikasi ke PM, menemukan window 30 menit (bukan 15 yang ada di awal expectation). Ini menghemat revisi.
💡 Tip 2: Plan Mode adalah “architectural sanity check” gratis
Tanpa Plan Mode, @andi mungkin tidak sadar perlu buat EventPublisher interface sampai di tengah usecase implementation.
💡 Tip 3: Spec compliance check sebelum PR lebih cepat dari setelah review
Self-check sebelum PR bisa catch issues dalam 20 menit. Fix setelah code review bisa butuh 1-2 jam (context switch + revisi + re-review).
💡 Tip 4: Acknowledged gaps > secret gaps
EC3 yang di-track di JIRA lebih baik dari EC3 yang ada di kode tapi tidak didokumentasikan. Reviewer bisa melihat dan menyetujui trade-off yang explicit.
⚠️ Gotcha 1: Spec approval bukan rubber stamp
@budi menemukan issue dengan latency target saat review spec. Spec review yang meaningful, bukan formalitas.
⚠️ Gotcha 2: Context window Claude bisa penuh dalam session panjang
@andi mulai session baru untuk handler setelah session usecase selesai. Better to have clean context per task.
⚠️ Gotcha 3: 1.5 hari terasa lama untuk fitur “sederhana”
Tanpa SDD mungkin 6-8 jam, tapi hasilnya lebih fragile dan coverage lebih rendah. Invest time di awal, save time di production.
19.19 Apa Selanjutnya untuk Cancel Order
Fitur yang di-merge hari ini bukan produk jadi selamanya — ia punya roadmap. Rencana sprint berikut menunjukkan bagaimana gap yang diakui tadi berubah menjadi pekerjaan terjadwal dengan spec masing-masing.
1Setelah PR #232 di-merge, roadmap selanjutnya untuk fitur ini:
2
3Sprint 24:
4- JIRA-789: EC3 explicit DB timeout implementation
5- JIRA-790: Idempotency key untuk retry-safe cancel
6
7Sprint 25:
8- JIRA-791: Rate limiting untuk cancel (5 per user per jam)
9- Load test untuk verify NFR-P2 (p95 < 300ms)
10
11Sprint 26:
12- Admin cancel order (separate spec, separate implementation)
13- Analytics dashboard update untuk cancel rateSetiap item roadmap ini akan menempuh workflow yang sama persis — bukti bahwa SDD bukan ritual sekali jalan, melainkan pola berkelanjutan yang menjaga kualitas seiring fitur bertumbuh.
19.20 Ringkasan
Studi kasus ini menunjukkan SDD bukan proses yang linear dan sempurna — ada feedback loop, ada revisi spec, ada keputusan di pertengahan implementasi. Yang membuat SDD efektif bukan kesempurnaan prosesnya, tapi struktur yang memberikan confidence di setiap langkah.
Empat komponen kunci yang bekerja bersama: Spec yang di-approve sebelum coding, Plan Mode untuk architectural thinking, test yang trace ke spec, dan spec compliance check sebelum PR.
SDD sebagai investasi: 12,5 jam untuk implement, tapi menghasilkan spec yang bisa di-reuse untuk onboarding, test yang akan catch regression, dan dokumentasi keputusan yang berguna bulan atau tahun kemudian.
AI sebagai accelerator: Claude Code menghemat waktu secara signifikan — generate spec draft, plan, kode, dan test. Tapi @andi yang membuat keputusan di setiap langkah. Itulah perbedaan antara SDD dan vibe coding.
Di artikel terakhir topik ini, kita bicara tentang apa yang ada setelah SDD dikuasai: tools yang lebih dalam, evolusi selanjutnya, dan bridge ke ekosistem yang lebih luas.