RFC #

Setiap engineer pernah menghadapi situasi ini: sistem sudah dibangun, sudah berjalan di production, tapi tidak ada yang bisa menjelaskan mengapa keputusan arsitektur tertentu dibuat. Atau lebih buruk — sebuah perubahan besar diimplementasikan tanpa dikomunikasikan ke tim lain, dan dampaknya baru terasa berminggu-minggu kemudian dalam bentuk production incident. RFC (Request for Comments) hadir sebagai jawaban atas masalah itu. Ini bukan dokumen birokrasi — ini adalah alat komunikasi dan pengambilan keputusan teknis yang memaksa engineer untuk berpikir lebih dalam sebelum menulis satu baris kode pun.

Apa Itu RFC? #

RFC dalam konteks software engineering adalah dokumen formal yang digunakan untuk mengusulkan, mendiskusikan, dan mendokumentasikan perubahan teknis yang signifikan sebelum perubahan tersebut diimplementasikan.

Istilah RFC awalnya populer dari dunia internet standards (IETF) — hampir seluruh protokol internet yang kita gunakan hari ini lahir dari proses RFC. Di dunia engineering modern, perusahaan seperti Google, Meta, Netflix, dan Stripe mengadaptasinya sebagai engineering decision record yang terstruktur untuk keputusan internal.

RFC bukan hanya dokumentasi akhir dari keputusan yang sudah dibuat — ia adalah alat untuk membuat keputusan yang lebih baik. Perbedaannya penting:

// ✗ RFC sebagai dokumentasi retroaktif
Keputusan dibuat diam-diam → implementasi dimulai → RFC ditulis "untuk arsip"
→ Tidak ada review yang bermakna
→ RFC hanya menjadi formalitas

// ✓ RFC sebagai alat pengambilan keputusan
Ide muncul → RFC ditulis → diskusi terbuka → keputusan dibuat bersama → implementasi dimulai
→ Review terjadi di tahap yang tepat, sebelum biaya sunk cost membuat keputusan sulit diubah
→ Semua stakeholder punya konteks yang sama

RFC bisa mencakup berbagai jenis perubahan teknis signifikan: perubahan arsitektur sistem, penambahan atau penghapusan layanan, perubahan API contract, perubahan database schema yang berdampak luas, hingga keputusan pemilihan teknologi atau library baru.


Mengapa RFC Penting? #

Perubahan Teknis Selalu Punya Dampak yang Lebih Luas dari yang Terlihat #

Engineer yang menulis kode cenderung melihat dampak dari sudut pandang mereka sendiri. Perubahan yang terlihat kecil dari satu titik bisa berdampak signifikan ke sistem lain:

flowchart TD
    A[Perubahan: ganti format response API] --> B[Service A — butuh update parser]
    A --> C[Service B — butuh update model]
    A --> D[Mobile App — butuh update contract]
    A --> E[Monitoring — alert rule berubah]
    A --> F[Documentation — perlu diperbarui]
    A --> G[QA Automation — test case perlu disesuaikan]

RFC memaksa engineer berpikir end-to-end tentang dampak perubahan — bukan hanya dari perspektif service yang sedang dikerjakan.

Menghindari Hero Engineering #

Hero engineering adalah pola di mana satu engineer (biasanya yang paling senior atau paling berpengalaman) membuat keputusan teknis besar secara sepihak, mengimplementasikan, lalu meminta review setelah kode sudah jadi.

// ✗ Pola Hero Engineering
Senior Engineer membuat keputusan arsitektur sendiri
→ Langsung implementasi tanpa diskusi
→ PR dibuka setelah 2 minggu coding
→ "Review ya, mau merge besok karena deadline"
→ Tim lain tidak punya konteks → review hanya formalitas
→ Keputusan tidak bisa diubah karena sudah terlalu mahal

// ✓ Pola RFC-driven
Engineer menulis RFC — bahkan junior pun bisa
→ RFC dibuka untuk komentar dari seluruh tim
→ Diskusi terjadi di tahap ide, bukan di tahap kode
→ Keputusan final lebih robust karena sudah melalui berbagai sudut pandang
→ Semua orang yang relevan punya konteks yang sama

RFC meratakan lapangan — engineer junior bisa memberikan feedback bermakna pada keputusan arsitektur, dan engineer senior tidak bisa lagi membuat keputusan besar tanpa scrutiny.

Mengurangi Biaya Rework #

Semakin terlambat masalah ditemukan, semakin mahal biaya memperbaikinya. RFC memindahkan diskusi ke fase paling awal — sebelum ada kode yang ditulis.

flowchart LR
    A[Fase Ide\nRFC] --> B[Fase Design]
    B --> C[Fase Implementasi]
    C --> D[Fase Testing]
    D --> E[Production]

    A -.->|"Biaya perubahan: rendah\n(edit dokumen)"| A
    C -.->|"Biaya perubahan: tinggi\n(refactor kode)"| C
    E -.->|"Biaya perubahan: sangat tinggi\n(incident + hotfix + migration)"| E

Lebih murah membuang ide di fase dokumen daripada membuang kode setelah production.

Menjadi Memori Institusional #

Enam bulan setelah implementasi, tidak ada yang ingat mengapa keputusan tertentu dibuat. Dengan RFC yang tersimpan dengan baik, pertanyaan “kenapa sistem ini dibangun seperti ini?” selalu bisa dijawab:

Tanpa RFC:
  Engineer baru: "Kenapa kita pakai Redis untuk session, bukan database?"
  Senior: "Hmm... saya lupa, mungkin karena waktu itu ada masalah performa?"
  → Keputusan tidak bisa dievaluasi secara objektif

Dengan RFC:
  Engineer baru: "Kenapa kita pakai Redis untuk session, bukan database?"
  → RFC-007: Session Storage Migration (2023-Q2)
  → Latar belakang: database session menyebabkan query spike saat traffic tinggi
  → Alternatif yang dipertimbangkan: sticky session, JWT, Redis
  → Keputusan: Redis dipilih karena TTL native dan cluster support
  → Keputusan bisa dievaluasi: masih relevan? kondisi berubah?

Kapan Harus Membuat RFC? #

Tidak semua perubahan butuh RFC. Membuat RFC untuk setiap perubahan kecil akan menghasilkan overhead yang kontraproduktif. Gunakan panduan berikut:

flowchart TD
    A{Apakah perubahan ini\nberdampak ke lebih\ndari satu service?} -- Ya --> RFC
    A -- Tidak --> B{Apakah mengubah\nAPI contract atau\ndata model?}
    B -- Ya --> RFC
    B -- Tidak --> C{Apakah sulit\ndi-rollback jika\ngagal?}
    C -- Ya --> RFC
    C -- Tidak --> D{Apakah berdampak\npada performa,\ncost, atau security?}
    D -- Ya --> RFC
    D -- Tidak --> E[PR + komentar\ndi code review cukup]

    RFC[Buat RFC]

Aturan praktis yang sederhana: jika perubahan tidak bisa dijelaskan secara lengkap dalam 5 menit sinkronisasi, kemungkinan besar butuh RFC.

Butuh RFCTidak Butuh RFC
Migrasi dari monolith ke microserviceRefactor nama variabel atau fungsi
Penambahan message broker baruPenambahan endpoint CRUD sederhana
Perubahan strategi autentikasiPerbaikan bug yang terisolasi
Penggantian database engineUpdate dependency minor
Desain sistem caching terpusatPenambahan field di response yang tidak breaking
Perubahan deployment strategyPerubahan log format internal

Lifecycle RFC #

RFC bukan dokumen statis — ia punya siklus hidup yang harus diikuti agar efektif:

stateDiagram-v2
    [*] --> Draft : Engineer membuat RFC
    Draft --> InReview : RFC siap untuk didiskusikan
    InReview --> Draft : Perlu revisi signifikan
    InReview --> Accepted : Konsensus tercapai
    InReview --> Rejected : Tidak layak dilanjutkan
    Accepted --> Implemented : Implementasi selesai
    Rejected --> [*]
    Implemented --> [*]

Setiap status punya implikasi yang berbeda:

StatusArtinyaTindakan yang Diperlukan
DraftMasih ditulis, belum siap direviewAuthor melengkapi dokumen
In ReviewTerbuka untuk komentar dan diskusiReviewer memberikan feedback dalam tenggat waktu
AcceptedKeputusan dibuat, implementasi boleh dimulaiAuthor memulai implementasi
RejectedTidak akan dilanjutkan, dengan alasan yang jelasDokumentasikan alasan penolakan
ImplementedImplementasi selesai, RFC menjadi referensi historisUpdate RFC dengan catatan hasil aktual

Anatomi RFC yang Baik #

RFC yang baik bukan sekadar proposal — ia adalah dokumen yang memungkinkan reviewer membuat keputusan berdasarkan informasi yang lengkap. Berikut adalah bagian-bagian yang harus ada:

Header RFC #

# RFC-[NOMOR]: [Judul Singkat dan Deskriptif]

**Status:** Draft | In Review | Accepted | Rejected | Implemented
**Author:** [Nama / Tim]
**Reviewer:** [Nama reviewer yang diminta]
**Tanggal Dibuat:** YYYY-MM-DD
**Tanggal Diperbarui:** YYYY-MM-DD
**Target Keputusan:** YYYY-MM-DD

Nomor RFC penting untuk referensi — “seperti yang diputuskan di RFC-012” jauh lebih tertelusuri daripada “seperti yang kita diskusikan bulan lalu”.

Latar Belakang #

Jelaskan kondisi sistem saat ini dan konteks yang dibutuhkan reviewer untuk memahami permasalahan. Reviewer yang tidak familiar dengan domain harus bisa memahami situasinya dari bagian ini.

Contoh latar belakang yang baik:
"Saat ini proses upload file Excel untuk import produk diproses secara synchronous
di endpoint POST /api/products/import. Data menunjukkan rata-rata waktu pemrosesan
3–5 menit untuk file berisi 1.000 baris, dan 15–20 menit untuk file 10.000 baris.
Dalam 30 hari terakhir, terdapat 47 timeout error yang dilaporkan user (rate 12%)."

// ✗ Latar belakang yang tidak berguna
"Sistem kita punya masalah performa yang perlu diperbaiki."

Problem Statement #

Tuliskan masalah secara eksplisit dan terukur. Problem statement yang baik menjawab tiga pertanyaan:

1. Apa yang salah atau tidak optimal?
   "Endpoint import produk timeout untuk file >5.000 baris"

2. Dampaknya apa dan seberapa besar?
   "12% request gagal, user harus upload ulang, CS menerima ~15 tiket/minggu"

3. Siapa yang terdampak?
   "Merchant dengan katalog besar (>1.000 SKU) — segmen yang paling berharga"

Tujuan dan Non-Tujuan #

Bagian ini sering dilewatkan tapi sangat penting untuk mencegah scope creep selama diskusi.

**Tujuan:**
- Menghilangkan timeout pada proses import produk untuk semua ukuran file
- Memberikan feedback progress kepada user selama proses berlangsung
- Memastikan import bisa dilanjutkan jika ada kegagalan parsial

**Non-Tujuan (di luar scope RFC ini):**
- Optimasi performa database untuk query produk (akan dibahas di RFC terpisah)
- Validasi format file yang lebih advanced (backlog item terpisah)
- Real-time notification via WebSocket (bisa menjadi enhancement berikutnya)

Proposal / Solusi yang Diusulkan #

Jelaskan solusi yang diusulkan di level arsitektur — bukan detail implementasi kode. Fokus pada bagaimana sistem bekerja, bukan bagaimana kode ditulis.

sequenceDiagram
    participant Client
    participant API as API Gateway
    participant Worker as Background Worker
    participant Queue as Message Queue
    participant DB

    Client->>API: POST /import (file)
    API->>Queue: Enqueue job
    API-->>Client: 202 Accepted + job_id
    Queue->>Worker: Dequeue job
    Worker->>DB: Process & save rows
    Worker->>DB: Update job status
    Client->>API: GET /import/{job_id}/status
    API->>DB: Query job status
    API-->>Client: {status, progress, errors}

Alternatif yang Dipertimbangkan #

Ini adalah bagian yang paling sering dilewatkan dan paling menunjukkan kualitas sebuah RFC. Menuliskan alternatif — dan alasan mengapa tidak dipilih — membuktikan bahwa keputusan dibuat secara sadar, bukan karena keterbatasan pengetahuan.

Alternatif 1: Chunked Upload + Synchronous Processing
  Kelebihan: Implementasi lebih sederhana, tidak butuh infrastruktur baru
  Kekurangan: Masih ada batas waktu per chunk, tidak cocok untuk file sangat besar
  Alasan tidak dipilih: Hanya memindahkan masalah timeout, bukan menyelesaikannya

Alternatif 2: Streaming Processing via WebSocket
  Kelebihan: Real-time feedback, UX lebih baik
  Kekurangan: Kompleksitas tinggi, butuh perubahan besar di client
  Alasan tidak dipilih: Over-engineering untuk kebutuhan saat ini; bisa menjadi
  enhancement di masa depan setelah async processing stabil

Alternatif 3: Background Job dengan Polling (yang Diusulkan)
  Kelebihan: Sederhana, proven pattern, tidak butuh koneksi persisten
  Kekurangan: Slight delay dalam update status (polling interval)
  Alasan dipilih: Trade-off yang paling baik antara kompleksitas dan value

Dampak dan Risiko #

Bagian ini harus ditulis dengan jujur — termasuk dampak negatif dan skenario kegagalan.

Dampak Positif:
  ✓ Timeout error dihilangkan sepenuhnya
  ✓ Kapasitas file tidak terbatas (dalam batas storage)
  ✓ Server tidak lagi di-block selama proses import

Dampak yang Perlu Diperhatikan:
  ! Penambahan infrastruktur: butuh message queue (Redis/RabbitMQ)
  ! Kompleksitas operasional meningkat (monitoring worker)
  ! User experience berubah: dari synchronous ke polling

Skenario Kegagalan:
  ✗ Worker crash di tengah proses: partial import tersimpan di DB
     Mitigasi: Implementasi idempotency key per baris, import bisa di-resume
  ✗ Message queue penuh: request baru ditolak sementara
     Mitigasi: Queue size monitoring + alerting + graceful rejection dengan pesan jelas
  ✗ Job terlalu lama di queue: user menunggu terlalu lama
     Mitigasi: Priority queue untuk file kecil, deadline per job

Migration dan Rollback Plan #

Bagian yang paling sering dilupakan tapi paling krusial untuk keputusan yang mempengaruhi sistem yang sudah berjalan.

Migration Plan:
  1. Deploy infrastruktur message queue (tidak mempengaruhi sistem saat ini)
  2. Deploy worker service dalam mode "shadow" — memproses tapi tidak menyimpan
  3. Jalankan paralel: endpoint lama tetap aktif, endpoint baru /v2/import tersedia
  4. Gradual rollout: 10% → 50% → 100% traffic ke endpoint baru
  5. Monitor error rate dan processing time selama 1 minggu
  6. Deprecate endpoint lama setelah 2 sprint

Rollback Plan:
  Jika terjadi masalah setelah rollout:
  - Traffic bisa dikembalikan ke endpoint lama dengan feature flag (< 5 menit)
  - Worker bisa di-shutdown tanpa mempengaruhi endpoint lama
  - Jobs yang sudah masuk queue bisa di-drain atau di-discard
  - Database tidak ada perubahan schema yang tidak backward-compatible

Open Questions #

RFC yang baik tidak harus sempurna di awal. Menuliskan pertanyaan yang belum terjawab justru membantu reviewer untuk fokus pada area yang butuh masukan:

1. Apakah kita perlu menyimpan history import untuk audit trail?
   → Perlu input dari Product dan Compliance

2. Berapa lama job result harus disimpan sebelum di-cleanup?
   → Tradeoff antara storage cost dan kebutuhan user untuk re-download hasil

3. Apakah worker perlu autoscaling berdasarkan queue depth?
   → Perlu diskusi dengan DevOps tentang cost implication

4. Bagaimana handling jika row data invalid — stop seluruh import atau skip dan lanjut?
   → Perlu keputusan dari Product Owner

Best Practice Menulis RFC #

Tulis RFC Lebih Awal dari yang Kamu Kira Perlu #

RFC paling efektif ditulis ketika ide masih mentah — sebelum kamu terlalu terlibat dengan solusi tertentu. RFC yang ditulis setelah 2 minggu implementasi bukan RFC, itu post-hoc rationalization.

// ✗ Terlalu terlambat
"Saya sudah selesai implementasi async processing. Saya tulis RFC-nya sekarang."
→ Review tidak bermakna karena biaya perubahan sudah tinggi

// ✓ Timing yang tepat
"Saya punya ide untuk async processing. Sebelum mulai, saya tulis RFC dulu."
→ Review bisa mengubah pendekatan sebelum ada kode yang ditulis

Fokus pada “Mengapa”, Bukan “Bagaimana” #

Detail implementasi akan berevolusi selama development. Tapi alasan di balik keputusan arsitektur jarang berubah. RFC yang terlalu fokus pada detail kode akan menjadi usang dengan cepat.

// ✗ Terlalu fokus pada implementasi
"Worker akan menggunakan goroutine dengan channel buffer size 100 dan
timeout context 30 detik per batch..."

// ✓ Fokus pada keputusan arsitektur
"Proses import akan dipisahkan dari request lifecycle menggunakan background
job pattern, memungkinkan server merespons langsung tanpa menunggu proses selesai.
Detail implementasi (goroutine vs thread pool, queue library) akan diputuskan
selama implementasi berdasarkan karakteristik load aktual."

Jaga Panjang RFC dalam Batas yang Wajar #

RFC yang terlalu panjang tidak akan dibaca dengan cermat. RFC yang terlalu pendek tidak memberikan cukup konteks untuk keputusan yang baik.

Target panjang RFC:
  ✓ 2–6 halaman untuk RFC standar
  ✓ Bisa dibaca dan dipahami dalam 15–30 menit
  ✓ Diagram diutamakan daripada paragraf panjang

Tanda RFC terlalu panjang:
  ✗ Lebih dari 10 halaman untuk perubahan yang tidak revolusioner
  ✗ Menjelaskan hal-hal yang seharusnya sudah dipahami audiens
  ✗ Memasukkan detail kode yang seharusnya ada di PR

Tetapkan Tenggat Waktu Review #

RFC tanpa deadline cenderung tidak mendapat review tepat waktu — semua orang menunda karena merasa “masih ada waktu”.

// ✓ Praktik yang baik
RFC-012 dibuka untuk review
Target keputusan: 2026-06-20 (7 hari kerja)
Reviewer yang diminta: @developer-a, @tech-lead-b, @platform-team
Jika tidak ada objeksi signifikan sampai deadline, RFC dianggap Accepted

// ✗ Tanpa struktur yang jelas
"Tolong direview ya kalau ada waktu"
→ Tidak ada yang merasa bertanggung jawab
→ RFC terbengkalai berminggu-minggu

Perbarui RFC Setelah Implementasi #

RFC yang sudah diimplementasikan tapi tidak diperbarui kehilangan fungsinya sebagai memori institusional. Tambahkan catatan hasil aktual — apa yang berjalan sesuai rencana, apa yang berbeda, dan apa yang dipelajari.

## Catatan Post-Implementation (ditambahkan setelah RFC diimplementasikan)

**Tanggal Implementasi:** 2026-05-15
**Status Akhir:** Implemented

**Hasil Aktual:**
- Timeout error turun dari 12% menjadi 0.1% (target: 0%)
- Processing time untuk file 10.000 baris: rata-rata 45 detik (estimasi awal: 60 detik)
- Queue depth tertinggi selama peak: 23 jobs (tidak ada masalah)

**Yang Berbeda dari Rencana:**
- Polling interval dinaikkan dari 5 detik ke 10 detik berdasarkan feedback UX
- Redis dipilih sebagai queue (bukan RabbitMQ) karena sudah ada di infrastruktur

**Pelajaran:**
- Idempotency key per baris terbukti kritis — ada 3 kasus partial import yang berhasil di-resume
- Worker autoscaling tidak diperlukan di load saat ini, tapi perlu dipertimbangkan
  jika merchant base tumbuh 5x

Anti-Pattern RFC yang Harus Dihindari #

// ✗ RFC sebagai dokumen satu arah
RFC ditulis, dikirim, langsung diimplementasikan tanpa menunggu feedback
→ RFC hanya formalitas, bukan alat keputusan

// ✓ RFC harus menunggu feedback sebelum implementasi dimulai
Tentukan tenggat review dan patuhi prosesnya

// ✗ RFC yang terlalu teknis dan detail
Menjelaskan setiap baris kode dalam dokumen RFC
→ Reviewer tenggelam dalam detail, kehilangan gambaran besar
// ✓ RFC fokus pada arsitektur dan keputusan, bukan implementasi

// ✗ RFC yang tidak punya problem statement yang jelas
"Kita perlu meningkatkan sistem kita"
→ Reviewer tidak tahu apa yang sedang diselesaikan
// ✓ Problem statement harus spesifik, terukur, dan menjelaskan dampak

// ✗ Tidak menuliskan alternatif yang dipertimbangkan
"Ini satu-satunya solusi yang mungkin"
→ Menunjukkan analisis yang tidak lengkap
// ✓ Selalu pertimbangkan minimal 2 alternatif, bahkan jika "tidak melakukan apa-apa"

// ✗ RFC tanpa rollback plan
"Kita bisa rollback kalau ada masalah"
→ Bagaimana caranya? Berapa lama? Apa dampaknya?
// ✓ Rollback plan harus konkret: langkah-langkah, estimasi waktu, dampak terhadap data

Checklist Sebelum RFC Dipublikasikan #

KELENGKAPAN DOKUMEN:
  □ Nomor dan judul RFC sudah ditetapkan
  □ Status diset ke "Draft" atau "In Review"
  □ Author dan daftar reviewer sudah diisi
  □ Target tanggal keputusan sudah ditetapkan

KONTEN:
  □ Latar belakang memberikan konteks yang cukup bagi reviewer yang tidak familiar
  □ Problem statement spesifik dan terukur
  □ Tujuan dan Non-Tujuan jelas (mencegah scope creep)
  □ Proposal dijelaskan di level arsitektur, bukan detail kode
  □ Diagram disertakan untuk menjelaskan alur atau arsitektur
  □ Minimal 2 alternatif dituliskan beserta alasan tidak dipilih
  □ Dampak positif DAN negatif ditulis dengan jujur
  □ Skenario kegagalan dan mitigasinya sudah dipikirkan
  □ Migration plan konkret dan bertahap
  □ Rollback plan spesifik (langkah, waktu, dampak)
  □ Open questions ditulis untuk pertanyaan yang belum terjawab

PROSES:
  □ RFC dibagikan ke reviewer yang tepat (yang punya konteks dan authority)
  □ Deadline review dikomunikasikan dengan jelas
  □ Ada channel yang jelas untuk diskusi (komentar di dokumen, Slack thread, dll)

Ringkasan #

  • RFC bukan birokrasi — ia adalah alat keputusan — tujuannya adalah menghasilkan keputusan yang lebih baik, bukan menghasilkan dokumen.
  • Tulis RFC sebelum menulis kode — semakin awal feedback diterima, semakin murah biaya perubahan. RFC yang ditulis setelah implementasi bukan RFC, itu post-hoc rationalization.
  • Fokus pada “mengapa”, bukan “bagaimana” — detail implementasi berubah, alasan keputusan tidak.
  • Alternatif yang dipertimbangkan adalah bagian terpenting — RFC tanpa alternatif menunjukkan analisis yang tidak lengkap.
  • Rollback plan harus konkret — “bisa rollback” bukan rollback plan. Tuliskan langkah, waktu, dan dampaknya.
  • Tetapkan deadline review — RFC tanpa deadline tidak akan mendapat perhatian tepat waktu.
  • Perbarui RFC setelah implementasi — tambahkan hasil aktual agar RFC tetap berguna sebagai referensi historis.
  • RFC adalah memori institusional — ia menjawab pertanyaan “mengapa sistem ini dibangun seperti ini?” untuk engineer yang bergabung tahun depan.
  • Tidak semua perubahan butuh RFC — gunakan decision tree: jika berdampak ke banyak service, mengubah contract, atau sulit di-rollback, tulis RFC.

← Sebelumnya: Workflow   Berikutnya: Release Document →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact