Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
27 Jul 2026 · 27 mnt baca ·Artikel 9 / 208
Go

Spesifikasi Non-Fungsional Golang: Performance, Security, Scalability untuk SDD

Cara menulis Non-Functional Requirements (NFR) yang efektif untuk proyek Golang. Performance targets, security spec, dan scalability requirements yang measurable untuk SDD.

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

Spesifikasi Non-Fungsional: Performance, Security, Scalability

Menulis spesifikasi non-fungsional golang yang benar adalah bagian spec yang paling sering dilewati — padahal justru di sinilah letak perbedaan antara sistem yang “jalan di laptop” dan sistem yang “layak production”. Developer fokus pada “apa yang harus dilakukan sistem” (functional), tapi sering lupa mendefinisikan “bagaimana sistem harus berperilaku” (non-functional).

Akibatnya? Fitur yang “bekerja dengan benar” tapi timeout dalam 10 detik. API yang functional tapi tidak punya rate limiting sehingga rentan abuse. Service yang berjalan lancar di 100 request/detik tapi collapse di 1000.

Di artikel ini, kita bahas cara menulis NFR yang benar-benar berguna — specific, measurable, dan bisa jadi acceptance criteria untuk testing.


09.1 NFR vs Functional Requirements: Perbedaan yang Sering Kabur

Functional requirements menjawab: “Apa yang dilakukan sistem?” Non-functional requirements menjawab: “Bagaimana sistem berperilaku?” Perbandingan berdampingan berikut memperjelas garis pemisah yang sering kabur itu lewat tiga contoh konkret.

text
1Functional: "System menerima POST /orders dan membuat order"
2Non-functional: "System merespons POST /orders dalam < 500ms untuk p95"
3
4Functional: "System memvalidasi JWT token"
5Non-functional: "System menolak 100% request tanpa valid JWT, dengan latency overhead < 5ms"
6
7Functional: "System mengurangi stok saat order dibuat"
8Non-functional: "Pengurangan stok harus atomic — tidak ada partial update meskipun server restart"

Perhatikan bahwa setiap NFR menempel pada functional-nya dengan angka konkret. Kedua-duanya sama pentingnya untuk sistem production, tapi NFR yang buruk (atau tidak ada) adalah penyebab yang lebih sering untuk production incidents daripada functional bugs.


09.2 Tujuh Kategori NFR untuk API Golang

Agar tidak ada aspek yang terlewat, kita kelompokkan NFR ke tujuh kategori. Setiap kategori di bawah punya template spec yang bisa langsung kamu adaptasi.

Kategori 1: Performance

Kategori pertama mendefinisikan kecepatan dan throughput yang diharapkan. Template berikut memecahnya menjadi latency, throughput, dan resource dengan angka yang bisa langsung diverifikasi.

markdown
 1## Performance NFR — Create Order Endpoint
 2
 3### Latency
 4- NFR-P1: Response time < 200ms untuk p50 (median) di normal load
 5- NFR-P2: Response time < 500ms untuk p95 di normal load
 6- NFR-P3: Response time < 2000ms untuk p99 di peak load (flash sale)
 7- NFR-P4: Tidak ada request yang melebihi 10 detik (hard timeout)
 8
 9### Throughput
10- NFR-P5: Endpoint harus mampu menangani 200 req/detik sustained
11- NFR-P6: Burst capacity: 500 req/detik selama maksimum 30 detik
12
13### Resource
14- NFR-P7: Memory per instance: maksimum 512MB
15- NFR-P8: CPU throttle: tidak boleh melebihi 80% CPU selama > 5 menit sustained

Dengan memisahkan latency per persentil (p50/p95/p99), kamu menghindari jebakan “rata-rata” yang menyembunyikan pengalaman buruk di ekor distribusi.

Kategori 2: Availability dan Reliability

Kategori kedua menetapkan seberapa sering sistem harus tersedia dan seberapa cepat pulih. Template berikut menerjemahkan janji “reliable” menjadi angka uptime, RTO, dan RPO yang bisa diukur.

markdown
1## Availability NFR
2
3- NFR-A1: Uptime: 99.9% (downtime < 8.76 jam/tahun)
4- NFR-A2: Deployment downtime: 0 (zero-downtime deployment wajib)
5- NFR-A3: Recovery time objective (RTO): < 5 menit
6- NFR-A4: Recovery point objective (RPO): < 1 menit (no data loss)
7- NFR-A5: Graceful degradation: jika Kafka down, order creation tetap berhasil
8           (event akan dipublish saat Kafka kembali online)

NFR-A5 adalah yang paling sering dilupakan: mendefinisikan behavior saat dependency down sama pentingnya dengan mendefinisikan behavior saat semuanya sehat.

Kategori 3: Security

Kategori security paling luas cakupannya. Template berikut membaginya menjadi authentication, input validation, rate limiting, dan data protection agar tidak ada celah yang lolos.

markdown
 1## Security NFR
 2
 3### Authentication & Authorization
 4- NFR-S1: Semua endpoint (kecuali /health, /metrics) HARUS authenticated
 5- NFR-S2: JWT validation: cek expiry, signature, dan issuer
 6- NFR-S3: Token max age: 1 jam untuk access token
 7- NFR-S4: Authorization check: user hanya bisa akses resource miliknya sendiri
 8
 9### Input Validation
10- NFR-S5: Semua string input di-sanitize sebelum disimpan ke DB
11- NFR-S6: SQL injection prevention: selalu gunakan parameterized query
12- NFR-S7: Max request body size: 1MB
13
14### Rate Limiting
15- NFR-S8: Per-user rate limit: 100 request/menit untuk write operations
16- NFR-S9: Per-IP rate limit: 1000 request/menit (DDoS protection)
17- NFR-S10: Rate limit headers harus di-include di setiap response:
18           X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
19
20### Data Protection
21- NFR-S11: Sensitive data (payment info) tidak boleh muncul di logs
22- NFR-S12: PII (nama, email, phone) di-mask di logs
23- NFR-S13: Semua komunikasi antar service harus melalui TLS 1.2+

Setiap item security di atas measurable dan bisa jadi test — inilah yang membedakannya dari NFR generik “sistem harus aman” yang tidak berguna.

Kategori 4: Scalability

Kategori keempat memastikan sistem bisa tumbuh mengikuti beban. Template berikut menegaskan syarat stateless dan batas koneksi yang menjadi fondasi horizontal scaling.

markdown
1## Scalability NFR
2
3- NFR-SC1: Horizontal scaling: service harus stateless, bisa scale dari 1 ke 20 instance
4- NFR-SC2: Database connections: max 10 connections per instance (total 200 untuk 20 instance)
5- NFR-SC3: Auto-scaling trigger: CPU > 70% selama 2 menit → scale up 1 instance
6- NFR-SC4: Scale-in delay: tunggu 5 menit setelah load turun sebelum scale-in
7- NFR-SC5: Session affinity: tidak diperlukan (stateless)

NFR-SC1 (stateless) bukan sekadar requirement — ia adalah driver arsitektur yang memaksa kamu menyimpan session di Redis, bukan di memory application.

Kategori 5: Observability

Kategori kelima memastikan kamu bisa melihat apa yang terjadi di production. Template berikut mewajibkan metrics, logging, tracing, dan alerting secara eksplisit.

markdown
 1## Observability NFR
 2
 3### Metrics (WAJIB)
 4- NFR-O1: Expose metrics di /metrics (format Prometheus)
 5- NFR-O2: Metrics yang wajib ada:
 6  - http_requests_total{method, path, status_code}
 7  - http_request_duration_seconds{method, path} (histogram)
 8  - orders_created_total{status}
 9  - orders_cancelled_total{status, reason}
10  - db_query_duration_seconds{query_type} (histogram)
11  - kafka_publish_total{topic, status}
12
13### Logging (WAJIB)
14- NFR-O3: Structured logging dalam format JSON
15- NFR-O4: Setiap request harus di-log dengan: method, path, status_code,
16           duration_ms, user_id (jika authenticated), request_id
17- NFR-O5: Log level: ERROR untuk 5xx, WARN untuk expected errors (4xx),
18           INFO untuk successful operations
19- NFR-O6: Sensitive fields harus di-redact dari logs (password, credit_card, dll)
20
21### Tracing (WAJIB)
22- NFR-O7: Distributed tracing dengan OpenTelemetry
23- NFR-O8: Setiap request harus punya trace_id yang propagate ke semua downstream calls
24- NFR-O9: Span harus ada untuk: HTTP request, DB query, Kafka publish
25
26### Alerting
27- NFR-O10: Alert jika error rate (5xx) > 1% selama 5 menit
28- NFR-O11: Alert jika p95 latency > 1 detik selama 5 menit
29- NFR-O12: Alert jika disk usage > 80%

Tanpa observability yang wajib seperti ini, kamu baru tahu ada masalah ketika user sudah mengeluh — dan itu terlambat.

Kategori 6: Data Integrity

Kategori keenam menjaga data tetap konsisten meski di bawah beban konkuren. Template berikut menetapkan transaction, idempotency, dan audit trail sebagai syarat wajib.

markdown
1## Data Integrity NFR
2
3- NFR-D1: Semua write operations harus dalam database transaction
4- NFR-D2: Idempotency: operasi yang sama boleh dilakukan berulang, hasilnya sama
5- NFR-D3: Optimistic locking untuk update yang concurrent
6- NFR-D4: Audit trail: setiap perubahan status order harus tercatat di order_history
7- NFR-D5: Soft delete: tidak ada physical delete untuk business data

Data integrity adalah kategori yang paling mahal jika dilanggar — bug availability bisa di-recover, tapi data yang corrupt sering permanen.

Kategori 7: Testability

Kategori terakhir memastikan semua NFR di atas benar-benar bisa diverifikasi. Template berikut menetapkan target coverage dan disiplin race detection.

markdown
1## Testability NFR
2
3- NFR-T1: Unit test coverage: minimum 80% untuk usecase layer
4- NFR-T2: Integration test: semua happy path dan critical edge cases
5- NFR-T3: All tests harus pass dengan go test -race (race condition detection)
6- NFR-T4: Test execution time: semua unit test selesai dalam < 30 detik
7- NFR-T5: Mock semua external dependencies (DB, Kafka, external API)

Testability adalah NFR yang membuat NFR lain menjadi nyata: tanpa cara verifikasi, target performa dan security hanya angan-angan di dokumen.


09.3 Cara Mendefinisikan Performance Baseline

NFR performance yang baik perlu baseline yang realistic, bukan angka yang diarang. Tiga langkah berikut menunjukkan cara mendapatkan angka itu dari data nyata.

Step 1: Profiling endpoint yang sudah ada

Langkah pertama adalah menyalakan profiling untuk endpoint yang mirip agar tahu di mana waktu dihabiskan. Cuplikan berikut mengaktifkan pprof lalu menjalankan benchmark ber-profile.

go
1// Tambahkan profiling untuk endpoint yang mirip
2import _ "net/http/pprof"
3
4// Jalankan profiling:
5// go test -bench=BenchmarkCreateOrder -benchmem -cpuprofile=cpu.prof ./internal/usecase/order/...
6// go tool pprof -http=:6060 cpu.prof

Hasil profiling ini memberitahu apakah bottleneck ada di DB, serialisasi, atau logika bisnis — informasi yang membuat target NFR-mu berdasar, bukan tebakan.

Step 2: Load test untuk menentukan capacity

Setelah tahu profil per-request, langkah berikutnya adalah mengukur kapasitas agregat. Perintah berikut memakai k6 atau hey untuk menekan endpoint dengan beban konkuren.

bash
1# Gunakan k6 atau hey untuk load testing
2k6 run --vus 100 --duration 60s scripts/load-test-create-order.js
3
4# Atau dengan hey
5hey -n 10000 -c 100 \
6    -H "Authorization: Bearer $TOKEN" \
7    -m POST \
8    -d '{"cart_id":"...", "shipping_address_id":"..."}' \
9    http://localhost:8080/v1/orders

Angka p50, p95, dan max throughput dari load test inilah yang jadi titik acuan untuk menetapkan target dengan headroom yang masuk akal.

Step 3: Definisikan NFR berdasarkan data

Dengan baseline di tangan, langkah terakhir adalah menuliskan NFR yang menyisakan ruang di atas angka terukur. Contoh berikut menurunkan target dari hasil pengukuran, bukan dari harapan.

markdown
 1## Cara Mendefinisikan NFR Performance yang Realistic
 2
 3Baseline measurements (dari load test):
 4- p50 latency saat ini: 120ms
 5- p95 latency saat ini: 280ms
 6- Max stable throughput: 150 req/s
 7
 8NFR yang realistis:
 9- NFR-P1: Response time < 200ms untuk p50 (headroom 67% dari baseline)
10- NFR-P2: Response time < 500ms untuk p95 (headroom 78% dari baseline)
11- NFR-P5: Throughput minimum 200 req/s (target 33% improvement)

Pola ini mengubah NFR performance dari aspirasi menjadi kontrak: setiap angka bisa ditelusuri ke measurement dan headroom yang eksplisit.


09.4 Security NFR: Template yang Bisa Langsung Dipakai

Security NFR sering terlewat padahal kritis. Checklist berikut merangkum item yang paling sering hilang dan bisa kamu tempel langsung ke setiap spec endpoint.

markdown
 1# Security Checklist — Semua API Endpoint Golang
 2
 3## Authentication
 4- [ ] JWT validation: signature, expiry, issuer
 5- [ ] Token tidak disimpan di log
 6- [ ] Refresh token mechanism (jika applicable)
 7
 8## Authorization
 9- [ ] User hanya bisa akses resource miliknya
10- [ ] Admin role check sebelum admin operations
11- [ ] Principle of least privilege untuk service accounts
12
13## Input Validation
14- [ ] Max request size di-enforce (default 1MB)
15- [ ] Semua string di-validate length
16- [ ] UUID format di-validate sebelum DB query
17- [ ] JSON schema validation sebelum business logic
18
19## SQL Injection Prevention
20- [ ] Semua query menggunakan parameterized queries
21- [ ] TIDAK ADA string concatenation untuk SQL
22- [ ] ORM juga perlu dicek (bisa ada raw query yang rentan)
23
24## Rate Limiting
25- [ ] Per-user: write operations (10 req/min untuk kritis, 100 req/min untuk normal)
26- [ ] Per-IP: global (1000 req/min, configurable)
27- [ ] Response headers: X-RateLimit-* headers selalu ada
28
29## Error Handling
30- [ ] Error message tidak bocorkan informasi internal (stack trace, query, dll)
31- [ ] 5xx errors hanya expose generic message ke client
32- [ ] Internal error details hanya di log, tidak di response
33
34## Sensitive Data
35- [ ] Daftar sensitive fields yang tidak boleh di-log: [password, credit_card, cvv, otp]
36- [ ] Daftar PII yang di-mask di log: [email, phone, address, name]
37- [ ] HTTPS enforced (tidak boleh ada HTTP untuk production)
38
39## Headers
40- [ ] Security headers: Strict-Transport-Security, X-Content-Type-Options
41- [ ] CORS: whitelist domain yang diizinkan, jangan wildcard di production
42- [ ] Content-Security-Policy jika ada web UI

Perlakukan checklist ini sebagai definition of done keamanan: setiap kotak yang belum dicentang adalah risiko yang kamu terima secara sadar, bukan celah yang tidak sengaja lolos.


09.5 NFR untuk Background Jobs dan Workers

Background jobs punya NFR yang berbeda dari HTTP endpoints — tidak ada user yang menunggu, tapi ada database yang bisa kewalahan. Template berikut menyesuaikan timing, resource, dan reliability untuk konteks worker.

markdown
 1## NFR — Cleanup Expired Carts Background Job
 2
 3### Timing
 4- NFR-J1: Job harus selesai dalam maksimum 5 menit per run
 5- NFR-J2: Jika satu run > 5 menit, log sebagai anomaly dan alert
 6
 7### Resource
 8- NFR-J3: DB query tidak boleh lock table — gunakan batch dengan small transactions
 9- NFR-J4: Batch size: maksimum 500 records per batch
10- NFR-J5: Jeda antara batch: 100ms untuk mencegah DB overload
11
12### Reliability
13- NFR-J6: Idempotency: run ulang harus aman (tidak double-delete)
14- NFR-J7: Jika ada error saat batch, skip batch tersebut dan lanjut
15          (jangan hentikan seluruh job)
16
17### Observability
18- NFR-J8: Log jumlah records yang diproses per run
19- NFR-J9: Metric: job_duration_seconds, records_processed_total, errors_total
20- NFR-J10: Alert jika job gagal 3x berturut-turut

Perhatikan penekanan pada batching dan jeda antar-batch: worker yang tidak mengatur ritmenya bisa menjadi sumber DB overload yang justru merusak endpoint user-facing.


09.6 Implementasi NFR di Go: Contoh Konkret

NFR di dokumen hanya berarti jika tercermin di kode. Empat contoh berikut menerjemahkan NFR security dan observability menjadi Go idiomatik.

Rate Limiting (NFR-S8, NFR-S9)

Untuk memenuhi batas per-user dan per-IP, kita bangun middleware yang mengecek kedua limit sebelum request diteruskan. Implementasi berikut memakai golang.org/x/time/rate dengan backing Redis.

go
 1// internal/delivery/http/middleware/rate_limit.go
 2package middleware
 3
 4import (
 5    "net/http"
 6    "golang.org/x/time/rate"
 7    "github.com/labstack/echo/v4"
 8    goredis "github.com/redis/go-redis/v9"
 9)
10
11type RateLimiter struct {
12    redis      *goredis.Client
13    userLimit  rate.Limit  // 100 req/min = 100/60 per second
14    ipLimit    rate.Limit  // 1000 req/min
15}
16
17func NewRateLimiter(redis *goredis.Client) *RateLimiter {
18    return &RateLimiter{
19        redis:     redis,
20        userLimit: rate.Every(time.Minute / 100), // NFR-S8: 100 req/min
21        ipLimit:   rate.Every(time.Minute / 1000), // NFR-S9: 1000 req/min
22    }
23}
24
25func (rl *RateLimiter) Middleware(next echo.HandlerFunc) echo.HandlerFunc {
26    return func(c echo.Context) error {
27        userID := getUserID(c)
28        clientIP := c.RealIP()
29
30        // Check per-user limit (NFR-S8)
31        if !rl.checkLimit(c.Request().Context(), "user:"+userID.String(), rl.userLimit) {
32            c.Response().Header().Set("X-RateLimit-Limit", "100") // NFR-S10
33            return c.JSON(http.StatusTooManyRequests, map[string]string{
34                "error_code": "RATE_LIMIT_EXCEEDED",
35                "message":    "rate limit exceeded, please try again later",
36            })
37        }
38
39        // Check per-IP limit (NFR-S9)
40        if !rl.checkLimit(c.Request().Context(), "ip:"+clientIP, rl.ipLimit) {
41            return c.JSON(http.StatusTooManyRequests, map[string]string{
42                "error_code": "IP_RATE_LIMIT_EXCEEDED",
43                "message":    "too many requests from this IP",
44            })
45        }
46
47        return next(c)
48    }
49}

Komentar // NFR-S8 dan seterusnya membuat mapping antara requirement dan kode eksplisit — reviewer bisa menelusuri setiap NFR ke baris implementasinya.

Request Timeout (NFR-P4)

NFR-P4 menuntut tidak ada request yang menggantung lebih dari batas keras. Middleware berikut membungkus handler dengan context timeout dan mengembalikan 504 jika lewat.

go
 1// Implementasi hard timeout di middleware
 2func TimeoutMiddleware(timeout time.Duration) echo.MiddlewareFunc {
 3    return func(next echo.HandlerFunc) echo.HandlerFunc {
 4        return func(c echo.Context) error {
 5            // NFR-P4: tidak ada request melebihi 10 detik
 6            ctx, cancel := context.WithTimeout(c.Request().Context(), timeout)
 7            defer cancel()
 8
 9            c.SetRequest(c.Request().WithContext(ctx))
10
11            done := make(chan error, 1)
12            go func() {
13                done <- next(c)
14            }()
15
16            select {
17            case err := <-done:
18                return err
19            case <-ctx.Done():
20                return c.JSON(http.StatusGatewayTimeout, map[string]string{
21                    "error_code": "REQUEST_TIMEOUT",
22                    "message":    "request timeout",
23                })
24            }
25        }
26    }
27}

Pola select antara channel done dan ctx.Done() inilah yang mengubah “batas waktu” dari harapan menjadi jaminan yang benar-benar dipaksakan runtime.

Structured Logging dengan Sensitive Data Masking (NFR-O4, NFR-O6)

NFR-O6 melarang data sensitif bocor ke log. Helper berikut menyaring field berdasarkan nama sebelum apa pun ditulis ke output.

go
 1// internal/pkg/logger/logger.go
 2package logger
 3
 4import (
 5    "log/slog"
 6    "strings"
 7)
 8
 9// sensitiveFields yang tidak boleh di-log
10var sensitiveFields = map[string]bool{
11    "password": true, "credit_card": true, "cvv": true, "otp": true,
12}
13
14// piiFields yang di-mask
15var piiFields = map[string]bool{
16    "email": true, "phone": true, "address": true,
17}
18
19// MaskSensitive masks sensitive values in structured log
20func MaskSensitive(key string, value any) (string, any) {
21    keyLower := strings.ToLower(key)
22
23    // NFR-O6: sensitive fields → tidak di-log sama sekali
24    if sensitiveFields[keyLower] {
25        return key, "[REDACTED]"
26    }
27
28    // NFR-S12: PII → di-mask
29    if piiFields[keyLower] {
30        if str, ok := value.(string); ok && len(str) > 4 {
31            return key, str[:2] + strings.Repeat("*", len(str)-4) + str[len(str)-2:]
32        }
33    }
34
35    return key, value
36}

Dengan memusatkan aturan masking di satu fungsi, kamu menutup kebocoran PII di seluruh codebase tanpa mengandalkan disiplin manual di setiap slog.Info.

Prometheus Metrics (NFR-O2)

NFR-O2 mewajibkan sekumpulan metric tertentu selalu ada. Deklarasi berikut mendaftarkannya sekali di init sehingga tidak mungkin lupa di-register.

go
 1// internal/pkg/metrics/metrics.go
 2package metrics
 3
 4import "github.com/prometheus/client_golang/prometheus"
 5
 6var (
 7    // NFR-O2: Wajib ada
 8    HTTPRequestsTotal = prometheus.NewCounterVec(
 9        prometheus.CounterOpts{
10            Name: "http_requests_total",
11            Help: "Total HTTP requests",
12        },
13        []string{"method", "path", "status_code"},
14    )
15
16    HTTPRequestDuration = prometheus.NewHistogramVec(
17        prometheus.HistogramOpts{
18            Name:    "http_request_duration_seconds",
19            Help:    "HTTP request duration",
20            Buckets: []float64{0.01, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0},
21        },
22        []string{"method", "path"},
23    )
24
25    OrdersCreatedTotal = prometheus.NewCounterVec(
26        prometheus.CounterOpts{
27            Name: "orders_created_total",
28            Help: "Total orders created",
29        },
30        []string{"status"},
31    )
32
33    OrdersCancelledTotal = prometheus.NewCounterVec(
34        prometheus.CounterOpts{
35            Name: "orders_cancelled_total",
36            Help: "Total orders cancelled",
37        },
38        []string{"status", "reason"},
39    )
40)
41
42func init() {
43    prometheus.MustRegister(
44        HTTPRequestsTotal,
45        HTTPRequestDuration,
46        OrdersCreatedTotal,
47        OrdersCancelledTotal,
48    )
49}

Bucket histogram yang dipilih (0.01 hingga 10.0 detik) langsung selaras dengan target latency di NFR-P1 sampai NFR-P4, sehingga dashboard-mu bisa memverifikasi SLA tanpa konfigurasi tambahan.


09.7 Verifikasi NFR Performance dengan Benchmark Test

NFR performance harus bisa di-verify dengan automated test, bukan hanya manual testing. Test berikut mengukur p95 latency dan gagal secara eksplisit jika melewati batas NFR-P2.

go
 1// internal/usecase/order/cancel_order_bench_test.go
 2package order_test
 3
 4import (
 5    "context"
 6    "testing"
 7    // imports...
 8)
 9
10// BenchmarkCancelOrder verifies NFR-P2: p95 < 500ms under load
11func BenchmarkCancelOrder(b *testing.B) {
12    // Setup
13    uc := setupCancelOrderUseCase(b)
14    ctx := context.Background()
15
16    b.ResetTimer()
17    b.RunParallel(func(pb *testing.PB) {
18        for pb.Next() {
19            orderID := createTestOrder(b)
20            input := cancelorder.CancelOrderInput{
21                OrderID: orderID,
22                UserID:  testUserID,
23            }
24            if err := uc.Execute(ctx, input); err != nil {
25                b.Errorf("cancel order failed: %v", err)
26            }
27        }
28    })
29}
30
31// TestNFR_CancelOrder_MaxLatency verifies that p95 latency meets NFR-P2
32func TestNFR_CancelOrder_MaxLatency(t *testing.T) {
33    if testing.Short() {
34        t.Skip("skipping NFR test in short mode")
35    }
36
37    uc := setupCancelOrderUseCase(t)
38    const iterations = 1000
39    const p95Limit = 500 * time.Millisecond
40
41    durations := make([]time.Duration, 0, iterations)
42    for i := 0; i < iterations; i++ {
43        orderID := createTestOrder(t)
44        start := time.Now()
45        uc.Execute(context.Background(), cancelorder.CancelOrderInput{
46            OrderID: orderID,
47            UserID:  testUserID,
48        })
49        durations = append(durations, time.Since(start))
50    }
51
52    sort.Slice(durations, func(i, j int) bool {
53        return durations[i] < durations[j]
54    })
55
56    p95Index := int(float64(iterations) * 0.95)
57    p95 := durations[p95Index]
58
59    if p95 > p95Limit {
60        t.Errorf("NFR-P2 violated: p95 latency %v exceeds limit %v", p95, p95Limit)
61    }
62    t.Logf("p95 latency: %v (limit: %v)", p95, p95Limit)
63}

Dengan menuliskan NFR-P2 violated langsung di pesan error, pelanggaran NFR menjadi bug yang gagal di CI — bukan angka yang harus seseorang ingat untuk dicek manual.


09.8 NFR sebagai Checklist di CI/CD

NFR harus bisa di-verify secara automated di pipeline, bukan hanya di mesin developer. Workflow berikut menjalankan pengecekan performance, security, dan coverage sebagai gate.

yaml
 1# .github/workflows/nfr-check.yml
 2name: NFR Verification
 3
 4on: [push, pull_request]
 5
 6jobs:
 7  performance-nfr:
 8    name: Performance NFR Check
 9    runs-on: ubuntu-latest
10    steps:
11      - name: Run benchmark tests
12        run: go test -bench=BenchmarkCancelOrder -benchtime=10s ./internal/usecase/order/...
13
14      - name: Run NFR latency tests
15        run: go test -run TestNFR_ -timeout 120s ./...
16
17  security-nfr:
18    name: Security NFR Check
19    steps:
20      - name: Run security scanner
21        uses: securego/gosec@master
22        with:
23          args: ./...
24
25      - name: Check for sensitive data in logs test
26        run: go test -run TestLog_NoSensitiveData ./...
27
28  coverage-nfr:
29    name: Coverage NFR Check (NFR-T1)
30    steps:
31      - name: Run tests with coverage
32        run: |
33          go test -coverprofile=coverage.out ./internal/usecase/...
34          go tool cover -func=coverage.out | tail -1 | awk '{print $3}' | \
35            awk -F'%' '{if ($1 < 80) exit 1}'  # NFR-T1: minimum 80%

Ketika gate NFR ini berjalan di setiap push, tim tidak bisa lagi men-degradasi performa atau coverage tanpa sengaja — pipeline yang menolaknya, bukan reviewer yang harus jeli.


09.9 Prompting Claude untuk Generate NFR yang Lebih Baik

Claude bisa membantu menyusun NFR, tapi hanya jika kamu memberi konteks sistem yang cukup. Prompt berikut menyediakan kerangka yang memaksa output specific dan measurable.

text
 1Saya membangun endpoint CREATE ORDER untuk Santekno Shop.
 2
 3Konteks sistem:
 4- Platform e-commerce B2C Indonesia
 5- Peak traffic: flash sale event, 10x normal load
 6- Normal load: 100 req/s untuk seluruh platform
 7- Infrastructure: Kubernetes on AWS (3 AZ), RDS PostgreSQL, ElastiCache Redis
 8- SLA yang dijanjikan ke merchant: 99.9% uptime
 9
10Bantu saya mendefinisikan NFR yang specific dan measurable untuk:
111. Performance (latency dan throughput)
122. Availability (uptime, RTO, RPO)
133. Security (authentication, rate limiting, data protection)
144. Scalability (horizontal scaling, DB connections)
155. Observability (metrics, logging, alerting thresholds)
16
17Untuk setiap NFR:
18- Berikan angka yang specific (bukan "fast" tapi "< 500ms")
19- Jelaskan alasan di balik angka tersebut
20- Tunjukkan cara mengukur / verifikasinya
21
22Hindari NFR yang tidak bisa di-test atau di-verify.

Kunci prompt ini ada pada konteks beban dan infrastruktur: tanpa “flash sale 10x” dan “RDS PostgreSQL”, Claude hanya bisa menebak angka generik yang tidak cocok dengan realita sistemmu.


09.10 Integrasi NFR dengan Feature Spec

NFR terbaik adalah yang terintegrasi langsung ke feature spec — bukan dokumen terpisah yang mudah dilupakan. Contoh berikut menempelkan NFR beserta cara verifikasinya persis di bawah acceptance criteria fungsional.

markdown
 1# specs/order/create-order.md v2.0
 2
 3## User Story
 4[...]
 5
 6## Acceptance Criteria (Functional)
 7[AC1-AC14 seperti sebelumnya]
 8
 9## Acceptance Criteria (Non-Functional)
10
11### Performance
12- NFR1: Response time < 500ms untuk p95 di 200 concurrent users
13  Verification: load test script di scripts/load-test-create-order.js
14
15### Security
16- NFR2: Rate limit 20 order creation per user per jam
17  Verification: test/integration/rate_limit_test.go
18- NFR3: Semua string input di-sanitize (HTML chars escaped)
19  Verification: unit test TestCreateOrder_HTMLInjection
20
21### Reliability
22- NFR4: Operasi stok decrement harus atomic
23  Verification: TestCreateOrder_ConcurrentRequests_NoOversell
24- NFR5: Graceful degradation: jika Kafka down, order tetap berhasil
25  Verification: TestCreateOrder_KafkaDown_StillCreates
26
27### Observability
28- NFR6: Metric order_created_total harus di-increment untuk setiap create
29  Verification: TestCreateOrder_MetricIncrement

Setiap NFR di atas menyebut file verifikasinya, sehingga developer membaca requirement dan test-nya di tempat yang sama saat implementasi — inilah yang mencegah NFR terlupakan.


09.11 NFR untuk Microservice Boundaries

Ketika service berkomunikasi satu sama lain, NFR juga perlu mendefinisikan SLA antar service. Template berikut menetapkan latency, retry, dan behavior fallback di batas Order Service ke Product Service.

markdown
 1## NFR untuk Order Service → Product Service Communication
 2
 3### Latency SLA
 4- NFR-MS1: Order Service mengharapkan Product Service merespons dalam < 100ms
 5           untuk get product by ID
 6- NFR-MS2: Jika Product Service timeout (> 100ms), Order Service
 7           retry 2x dengan exponential backoff (100ms, 200ms)
 8- NFR-MS3: Setelah 3 kali timeout, circuit breaker opens selama 30 detik
 9
10### Availability SLA
11- NFR-MS4: Order Service harus bisa berjalan meskipun Product Service down
12           (jika ada cache valid)
13- NFR-MS5: Cache TTL untuk product data: 5 menit
14
15### Data Consistency
16- NFR-MS6: Stok yang di-tampilkan di Order saat order dibuat = stok saat itu
17           (eventual consistency acceptable, eventual timeout: 1 detik)

NFR antar service inilah yang mencegah cascade failure: tanpa circuit breaker dan cache fallback, satu service yang lambat bisa menjatuhkan seluruh rantai request.


09.12 Common NFR Anti-patterns

Sebelum menutup, penting mengenali pola NFR yang tampak benar tapi tidak berguna. Empat anti-pattern berikut adalah yang paling sering muncul di review.

Anti-pattern 1: NFR yang tidak measurable

Contoh berikut menyandingkan versi yang tidak bisa diuji dengan versi yang punya angka konkret.

markdown
1❌ "Sistem harus cepat dan reliable"
2✅ "Response time < 500ms untuk p95, uptime 99.9%"

Tanpa angka, tidak ada cara objektif menyatakan NFR terpenuhi atau dilanggar — dan itu berarti ia tidak pernah benar-benar diuji.

Anti-pattern 2: NFR yang terlalu aspirasional tanpa dasar

Contoh berikut menunjukkan target yang mustahil dicapai dengan constraint teknis nyata.

markdown
1❌ "Response time < 10ms untuk semua operation"
2(mungkin tidak achievable dengan DB call yang diperlukan)
3 "Response time < 50ms untuk cached reads, < 300ms untuk DB reads"

NFR yang mengabaikan biaya DB call akan selalu dilanggar, dan NFR yang selalu dilanggar cepat diabaikan tim.

Anti-pattern 3: Security NFR yang terlalu generic

Contoh berikut mempertegas bedanya “aman” yang kabur dengan syarat validasi yang spesifik.

markdown
1❌ "Sistem harus secure"
2✅ "JWT validation mencakup: signature verification, expiry check, issuer validation.
3    Token yang expired harus return 401 dalam < 5ms."

Security yang tidak menyebut mekanisme konkret tidak bisa di-test, dan keamanan yang tidak di-test adalah keamanan yang diasumsikan — asumsi paling berbahaya di production.

Anti-pattern 4: NFR yang tidak ada test-nya

NFR yang tidak bisa di-verify adalah NFR yang akan dilanggar — mungkin tanpa disadari. Setiap NFR harus punya cara verifikasi yang jelas, atau ia hanya jadi hiasan dokumen.


09.13 NFR Template untuk Santekno Shop

Agar tim tidak menulis NFR dari nol setiap fitur, kita siapkan template standar. Kerangka berikut memandu pengisian angka untuk setiap kategori inti.

markdown
 1## Non-Functional Requirements Template
 2
 3### Performance
 4- [ ] p50 latency target: [X]ms di [Y] concurrent users
 5- [ ] p95 latency target: [X]ms di [Y] concurrent users
 6- [ ] Throughput target: [X] req/s sustained
 7- [ ] Hard timeout: [X] detik (tidak ada request melebihi ini)
 8- [ ] Verification: [load test script atau benchmark test]
 9
10### Availability
11- [ ] Uptime SLA: [X]%
12- [ ] Zero-downtime deployment: [ya/tidak]
13- [ ] RTO: [X] menit
14- [ ] RPO: [X] menit
15- [ ] Graceful degradation: [kondisi dan behavior]
16
17### Security
18- [ ] Authentication: [mechanism dan requirement]
19- [ ] Authorization: [yang bisa akses resource ini]
20- [ ] Rate limiting: [per-user dan per-IP]
21- [ ] Input validation: [fields dan rules]
22- [ ] Sensitive data handling: [list fields dan treatment]
23
24### Scalability
25- [ ] Horizontal scaling: [stateless? ya/tidak]
26- [ ] DB connections: [max per instance]
27- [ ] External connections: [max per instance]
28
29### Observability
30- [ ] Required metrics: [list metric names]
31- [ ] Log level rules: [INFO/WARN/ERROR untuk kondisi apa]
32- [ ] Alert thresholds: [kondisi alert dan severity]
33- [ ] Tracing: [ya/tidak, granularity]
34
35### Data Integrity
36- [ ] Transaction requirement: [single/distributed]
37- [ ] Idempotency: [ya/tidak, mechanism]
38- [ ] Audit trail: [ya/tidak, retention]

Dengan template ini sebagai titik awal, diskusi NFR bergeser dari “apakah kita butuh NFR” menjadi “berapa angka yang tepat” — pergeseran yang membuat NFR jadi kebiasaan, bukan beban.


09.14 Menggunakan NFR untuk Capacity Planning

NFR juga berguna untuk planning infrastruktur — angka throughput dan latency bisa langsung diterjemahkan ke jumlah instance. Prompt berikut memberi Claude data yang cukup untuk menghitung kapasitas.

text
 1Berdasarkan NFR berikut, bantu saya lakukan capacity planning
 2untuk Order Service di Santekno Shop:
 3
 4NFR:
 5- 200 req/s sustained throughput untuk create order
 6- 500 req/s burst capacity
 7- Response time < 500ms untuk p95
 8- Maximum 10 DB connections per instance
 9
10Pertanyaan untuk dijawab:
111. Berapa instance minimum yang dibutuhkan untuk handle load ini?
122. Berapa ukuran connection pool di PostgreSQL?
133. Berapa memory yang dibutuhkan per instance?
144. Berapa storage IOPS yang diperlukan?
15
16Asumsi:
17- Setiap request membutuhkan 1 DB read + 2 DB writes (average)
18- DB read = 5ms, DB write = 10ms
19- Application processing time (non-DB) = 50ms

Karena setiap pertanyaan berpijak pada NFR yang measurable, jawaban capacity planning-nya bisa diaudit — bukan tebakan “sepertinya butuh 5 instance”.


09.15 Testing NFR: Dari Unit ke Load Test

NFR diverifikasi di beberapa level yang saling melengkapi. Piramida berikut memetakan jenis test dari unit hingga load test.

text
 1NFR Testing Pyramid:
 2
 3                    /\
 4                   /  \  Performance Test
 5                  /    \  (k6, JMeter, Gatling)
 6                 /------\
 7                /        \ Integration NFR Test
 8               /          \ (race detector, timeout test)
 9              /------------\
10             /              \ Unit NFR Test
11            /                \ (benchmark, metric check)
12           /------------------\

Piramida ini mengingatkan bahwa load test yang mahal hanya efektif jika fondasi unit dan integration NFR test sudah menangkap masalah lebih awal dan lebih murah.


09.16 Dokumentasi NFR untuk Stakeholder Non-Teknikal

NFR perlu dikomunikasikan ke stakeholder non-teknikal dalam bahasa yang bisa dipahami. Template ringkasan berikut menerjemahkan angka teknis menjadi janji bisnis yang jelas.

markdown
 1## NFR Summary untuk Product Team
 2
 3**Kecepatan:**
 4"API checkout akan merespons dalam kurang dari setengah detik untuk 95% kasus.
 5Selama flash sale, mungkin sedikit lebih lambat tapi tidak lebih dari 2 detik."
 6
 7**Keandalan:**
 8"Layanan ini akan tersedia 99.9% waktu — artinya downtime kurang dari 9 jam per tahun.
 9Jika terjadi masalah, sistem akan pulih dalam 5 menit."
10
11**Keamanan:**
12"Setiap customer hanya bisa akses order miliknya sendiri.
13Ada proteksi terhadap spam: maksimum 20 order per jam per user."
14
15**Notifikasi masalah:**
16"Tim engineering akan mendapat alert otomatis jika ada masalah performa
17lebih dari 1% dari semua request, atau jika sistem lambat untuk lebih dari 5 menit."

Menerjemahkan “p95 < 500ms” menjadi “setengah detik untuk 95% kasus” membuat product team bisa ikut memutuskan trade-off — sesuatu yang tidak mungkin jika NFR hanya berbentuk jargon.


09.17 NFR Evolution: Dari MVP ke Scale

NFR yang tepat untuk MVP mungkin berbeda dari yang diperlukan saat scale. Rencana berikut menunjukkan bagaimana target mengetat seiring pertumbuhan volume.

markdown
 1## NFR Evolution Plan — Create Order
 2
 3### Phase 1: MVP (0-10K orders/hari)
 4- Performance: p95 < 1000ms
 5- Rate limit: 50 order/user/jam
 6- Monitoring: basic alerting
 7- Availability: 99.5%
 8
 9### Phase 2: Growth (10K-100K orders/hari)
10- Performance: p95 < 500ms (tighten)
11- Rate limit: 20 order/user/jam (tighten)
12- Monitoring: full observability dengan alerting
13- Availability: 99.9%
14
15### Phase 3: Scale (100K+ orders/hari)
16- Performance: p95 < 200ms
17- Rate limit: adaptive (based on user history)
18- Monitoring: full tracing + anomaly detection
19- Availability: 99.95%
20- Add: global rate limiting per IP, DDoS protection

Dengan memetakan NFR per fase, kamu menghindari over-engineering di MVP sekaligus punya peta jelas kapan harus mengetatkan target seiring beban naik.


09.18 Tips & Gotchas

💡 Tip 1: Setiap NFR harus punya cara verifikasi yang jelas — “Response time < 500ms” yang tidak ada test-nya tidak berguna. Selalu tambahkan: “Verification: [test file atau script]”.

💡 Tip 2: Mulai dengan NFR yang paling business-critical — tidak semua NFR sama pentingnya. Prioritaskan: security (always), performance yang langsung dirasakan user, data integrity.

💡 Tip 3: Review NFR setiap kuartal — NFR yang tepat untuk 100 user mungkin tidak cukup untuk 100.000 user. Jadwalkan NFR review setiap kuartal seiring growth.

💡 Tip 4: Gunakan NFR untuk drive architecture decisions — “Service harus stateless (NFR-SC1)” bukan hanya requirement, ini adalah driver untuk menghindari session state di application layer dan menggunakan Redis untuk cache.

⚠️ Gotcha 1: NFR yang terlalu ketat bisa jadi premature optimization — performance NFR “< 10ms untuk semua operation” di MVP stage mungkin menghabiskan engineering effort yang lebih baik diinvestasikan di tempat lain.

⚠️ Gotcha 2: Security NFR membutuhkan pengetahuan domain — NFR security yang generik (“sistem harus secure”) tidak berguna. Butuh pengetahuan tentang threat model spesifik bisnis kamu.

⚠️ Gotcha 3: Jangan konflik antara NFR dan budget infrastruktur — NFR “99.999% uptime” butuh multi-region setup yang mahal. Pastikan NFR sejalan dengan budget yang tersedia.

⚠️ Gotcha 4: NFR observability sering di-skip karena dianggap non-critical — padahal ini adalah NFR yang paling penting untuk production operations. Tanpa metrics dan alerting yang baik, tidak bisa detect masalah sebelum user mengeluh.


09.19 NFR dalam Konteks AI-Powered Features

Fitur yang menggunakan AI punya NFR yang unik — ada dimensi cost dan quality yang tidak muncul di endpoint biasa. Template berikut menambahkan kategori khusus untuk fitur berbasis LLM.

markdown
 1## NFR untuk AI Product Description Generator
 2
 3### Performance
 4- NFR-AI1: Streaming response harus mulai dalam < 2 detik (Time to First Token)
 5- NFR-AI2: Total response selesai dalam < 30 detik
 6- NFR-AI3: Timeout LLM API call: 35 detik (5 detik buffer)
 7
 8### Reliability
 9- NFR-AI4: Jika LLM API down  503 dengan message "AI service unavailable"
10           JANGAN return placeholder text sebagai "AI generated"
11- NFR-AI5: Circuit breaker: setelah 5 consecutive LLM failures, open circuit
12           selama 60 detik
13
14### Cost Control
15- NFR-AI6: Max token input: 2000 token per request
16- NFR-AI7: Max token output: 1000 token per request
17- NFR-AI8: Rate limit per user: 10 AI requests per jam
18- NFR-AI9: Monthly budget cap: alert di $500, hard stop di $1000
19
20### Quality
21- NFR-AI10: Moderation check: semua output di-filter untuk inappropriate content
22- NFR-AI11: Description harus dalam bahasa yang sama dengan produk
23- NFR-AI12: Tidak boleh ada PII tentang user lain dalam generated content
24
25### Observability
26- NFR-AI13: Log: input token count, output token count, latency, model version
27- NFR-AI14: Alert: cost per day > $50

Kategori Cost Control dan Quality inilah yang membedakan NFR fitur AI: tanpa budget cap dan moderation, satu fitur bisa menghabiskan anggaran atau menghasilkan konten berbahaya tanpa terdeteksi.


09.20 Ringkasan

Non-Functional Requirements bukan “nice to have” — mereka adalah perbedaan antara sistem yang “bekerja” dan sistem yang “layak production”.

Tujuh kategori NFR yang wajib dipertimbangkan: Performance, Availability, Security, Scalability, Observability, Data Integrity, dan Testability.

NFR yang baik harus: Specific (ada angka konkret), Measurable (ada cara verifikasi), Achievable (realistic dengan constraints), Relevant (sesuai business context), dan Time-bound (valid untuk phase tertentu).

Integrasi dengan feature spec: NFR paling efektif ketika masuk langsung ke feature spec, bukan dokumen terpisah. Ini memastikan developer membacanya saat implementasi.

Evolusi seiring growth: NFR untuk MVP berbeda dari scale. Jadwalkan review rutin dan rencanakan NFR evolution seiring business grows.

Di artikel berikutnya, kita masuk ke Part 3 seri ini — dari spec ke kode. Artikel pertama: Plan Mode di Claude Code — bagaimana menggunakan planning phase untuk menghasilkan rencana implementasi yang solid sebelum baris kode pertama ditulis.

Artikel Terkait

💬 Komentar