Back to News/Refactoring & Compounding Tech Debt

Refactoring & Compounding Tech Debt

Faisal Affan
3/1/2026
Refactoring & Compounding Tech Debt — 1 / 4
1 / 4

Refactoring & Compounding Tech Debt

"Move fast and break things. Unless you are breaking things faster than you can fix them." — Adapted from Mark Zuckerberg's later reflection

Konteks Artikel Ini

Artikel ini bukan teori akademis tentang tech debt. Ini adalah retrospektif nyata — momen ketika tim menyadari bahwa kecepatan pengembangan awal (MVP) kini menjadi utang teknis yang mencekik iterasi bisnis. Kita akan membedah biaya sesungguhnya, strategi migrasi, dan cara meyakinkan manajemen bahwa berhenti sejenak untuk memperbaiki fondasi adalah keputusan bisnis paling cerdas yang bisa diambil.


1. The Reckoning: Ketika MVP Menjadi Monster

1.1 Cerita yang Familiar

Setiap startup pernah di titik ini. Bulan pertama, semuanya terasa ajaib — fitur di-ship dalam hitungan hari, pivot terjadi dalam hitungan jam, dan "kita perbaiki nanti" menjadi mantra tim.

Kemudian "nanti" datang.

1.2 Tanda-Tanda Tech Debt Sudah Compounding

Deploy Fear

Tim takut deploy di hari Jumat — bahkan di hari Senin pun sudah mulai cemas. Setiap release terasa seperti bermain Russian roulette.

Merge Conflict Hell

Pull request sederhana memicu konflik di 15 file. Developer menghabiskan lebih banyak waktu resolve conflict daripada menulis kode baru.

Onboarding Nightmare

Engineer baru butuh 2-3 bulan untuk produktif, bukan karena domain complexity, tapi karena codebase yang tidak bisa dipahami.

Feature Paralysis

Fitur yang seharusnya selesai dalam 3 hari membutuhkan 3 minggu karena harus 'menyiasati' arsitektur yang rapuh.

The Silent Killer

Tech debt tidak pernah datang tiba-tiba. Ia compound — seperti bunga pinjaman yang tidak dibayar. Setiap sprint yang dihabiskan tanpa membayar utang, bunga bertambah. Dan suatu hari, bunganya lebih besar dari kemampuan tim untuk membayar.


2. The Cost of Bad Code: Menghitung Kerugian Bisnis

2.1 Dimana Waktu Developer Benar-Benar Habis

Salah satu momen paling menyadarkan adalah ketika kami melakukan time audit selama 4 sprint berturut-turut dan menemukan data ini:

Data Point yang Mengejutkan

Developer menghabiskan 70% waktu hanya untuk mengatasi konflik kode, memperbaiki regresi, dan menavigasi arsitektur yang rapuh — alih-alih membangun fitur. Artinya dari 5 engineer, hanya 1.5 engineer yang benar-benar produktif membangun value baru.

2.2 Menghitung Kerugian dalam Rupiah

Mari kita kuantifikasi kerugian nyata ini agar bisa berbicara dalam bahasa yang dipahami manajemen.

analysis/healthy-team.ts
const healthyTeam = {
  engineers: 5,
  monthlyRate: 30_000_000, // per engineer, fully loaded
  totalMonthlyCost: 5 * 30_000_000, // = Rp 150,000,000

  timeDistribution: {
    featureDevelopment: 0.55, // 55%
    bugFix: 0.1, // 10%
    codeReview: 0.1, // 10%
    techDebt: 0.1, // 10% (proactive)
    meetings: 0.1, // 10%
    mergeConflicts: 0.05, // 5%
  },

  effectiveFeatureOutput: 5 * 0.55, // = 2.75 engineer equivalents
  monthlyFeatureValue: 2.75 * 30_000_000, // = Rp 82,500,000 worth of features
  velocityTrend: "accelerating",
};
analysis/troubled-team.ts
const troubledTeam = {
  engineers: 5,
  monthlyRate: 30_000_000,
  totalMonthlyCost: 5 * 30_000_000, // = Rp 150,000,000 (sama!)

  timeDistribution: {
    featureDevelopment: 0.15, // 15% — tragis
    bugFix: 0.2, // 20%
    codeReview: 0.1, // 10%
    mergeConflicts: 0.25, // 25% — pembunuh utama
    workarounds: 0.15, // 15%
    debugging: 0.1, // 10%
    waiting: 0.05, // 5%
  },

  effectiveFeatureOutput: 5 * 0.15, // = 0.75 engineer equivalents
  monthlyFeatureValue: 0.75 * 30_000_000, // = Rp 22,500,000 worth of features
  velocityTrend: "decelerating",
};
MetrikTim SehatTim BermasalahDelta
Biaya BulananRp 150 JutaRp 150 JutaSama
Waktu untuk Fitur55%15%-40pp
Output Efektif2.75 engineer0.75 engineer3.67x lebih rendah
Value per BulanRp 82.5 JutaRp 22.5 JutaRp 60 Juta terbuang
Value per TahunRp 990 JutaRp 270 JutaRp 720 Juta terbuang
Velocity TrendNaikTurunDiverging

The Real Cost

Dengan biaya tim yang identik (Rp 150 Juta/bulan), tim dengan codebase bermasalah kehilangan Rp 720 Juta per tahun dalam bentuk waktu engineer yang terbuang. Ini setara dengan membuang 2.4 engineer setiap bulan. Dan angka ini belum termasuk opportunity cost dari fitur yang tidak di-ship, customer yang hilang, dan moral tim yang menurun.

2.3 The Compound Interest of Tech Debt

Perhatikan grafik di atas. Kedua tim mulai dari titik yang sama (40 story points per sprint). Tim yang tidak berinvestasi di refactoring mengalami penurunan velocity yang terus-menerus — dari 40 ke 10 dalam 8 quarter.

Tim yang mengalokasikan 20% sprint untuk tech debt memang mengalami sedikit penurunan di awal (karena bandwidth berkurang), tetapi kemudian velocity-nya meledak karena codebase semakin bersih dan mudah di-extend.

The Math

Setelah 2 tahun, tim yang berinvestasi di refactoring menghasilkan output 6.5x lebih banyak dari tim yang mengabaikan tech debt — padahal mereka "hanya" menggunakan 80% waktu untuk fitur.


3. Migrasi Skala Besar: Zero Downtime Transformation

3.1 Anatomy of a Large-Scale Migration

Retrospektif berikut adalah dari proyek nyata: migrasi frontend legacy ke arsitektur modern tanpa mengganggu pelanggan aktif.

Prop

Type

3.2 The Strangler Fig Pattern

Strategi yang kami gunakan: Strangler Fig Pattern — secara bertahap mengganti komponen legacy dengan komponen baru, tanpa ever melakukan "big bang" switch.

3.3 Reverse Proxy: Kunci Zero Downtime

Konsep paling critical: menggunakan reverse proxy (Nginx/Caddy/Traefik) untuk merutekan traffic antara legacy dan aplikasi baru berdasarkan path atau feature flag.

nginx/migration-routing.conf
# Phase 2: Route-based migration
# Halaman yang sudah dimigrasi → New App
# Halaman yang belum → Legacy App

upstream legacy_app {
    server 127.0.0.1:3000;
}

upstream new_app {
    server 127.0.0.1:4000;
}

server {
    listen 80;
    server_name app.example.com;

    # Halaman yang sudah dimigrasi ke new app
    location /dashboard {
        proxy_pass http://new_app;
    }

    location /settings {
        proxy_pass http://new_app;
    }

    location /reports {
        proxy_pass http://new_app;
    }

    # Semua halaman lain masih di legacy
    location / {
        proxy_pass http://legacy_app;
    }
}

Gradual Rollout

Dengan pendekatan ini, Anda bisa memigrasikan satu halaman per sprint, memvalidasi dengan pengguna nyata, dan rollback dalam hitungan detik jika ada masalah — cukup ubah konfigurasi proxy. Zero downtime, zero drama.

3.4 Lesson: Apa yang Berjalan Baik dan Apa yang Tidak

Design System First

Membangun component library dan design system sebelum memulai migrasi memastikan konsistensi UI dan mempercepat development halaman berikutnya secara eksponensial.

Feature Flag per Route

Menggunakan feature flag memungkinkan rollout bertahap (1% → 10% → 50% → 100%) dan rollback instan. Tidak ada satu pun insiden downtime selama migrasi.

Parallel Comparison Testing

Menjalankan kedua versi secara paralel dan membandingkan output membantu menangkap perbedaan perilaku sebelum rollout penuh.

API Contract via OpenAPI

Mendefinisikan kontrak API sebelum migrasi memastikan backend dan frontend bisa berkembang secara independen.

Shared State Management

Session dan authentication state antara legacy dan new app menjadi sumber bug paling banyak. Cookie conflict, token expiry mismatch, dan CORS issues menghabiskan 3 minggu ekstra.

CSS Specificity Wars

Legacy CSS yang bersifat global berbenturan dengan CSS Modules di app baru. Beberapa halaman mengalami "style bleeding" yang sulit di-debug.

Underestimated Edge Cases

Halaman yang terlihat "sederhana" ternyata memiliki puluhan edge case yang hanya ditemukan di production — form validation rules, conditional rendering berdasarkan user role, integrasi third-party.

LessonDetail
Isolasi CSS sejak hari pertamaGunakan CSS Modules, CSS-in-JS, atau Tailwind — jangan pernah biarkan global CSS bocor antar sistem
Definisikan auth boundarySession management harus diselesaikan sebelum migrasi halaman pertama, bukan sambil jalan
Migrasi dimulai dari halaman paling sederhanaBukan halaman paling penting. Bangun muscle memory tim dengan halaman low-risk dulu
Budget 30% bufferSetiap estimasi migrasi harus ditambah 30% untuk edge cases yang tidak terlihat di awal
Jangan freeze fitur baruBisnis tidak bisa menunggu. Fitur baru dibangun di new app, legacy hanya di-maintain minimal

4. Menjual Refactor ke Manajemen

4.1 Mengapa Ini Sulit

The Communication Gap

Ketika engineer bilang "kita butuh refactoring", manajemen mendengar "kita mau main-main dengan kode tanpa menghasilkan sesuatu yang terlihat oleh customer." Gap komunikasi ini adalah akar masalah. Solusinya bukan menghindari percakapan — tapi mengubah bahasanya.

4.2 Framework: The Refactoring Business Case

Jangan pernah mendatangi manajemen dengan "kita perlu refactor karena kodenya jelek." Datang dengan business case yang terstruktur.

Quantify the Problem (Data, Bukan Opini)

Kumpulkan data selama 2-4 sprint:

business-case/problem-quantification.ts
const currentState = {
  // Velocity metrics
  avgVelocity: {
    sixMonthsAgo: 42, // story points per sprint
    current: 18, // story points per sprint
    trend: "declining",
    projectedIn6Months: 8,
  },

  // Time distribution
  timeOnFeatures: 0.15, // 15% — should be 50%+
  timeOnBugFixes: 0.25, // 25% — should be <15%
  timeOnMergeConflicts: 0.2, // 20% — should be <5%

  // Business impact
  avgFeatureDeliveryTime: {
    sixMonthsAgo: "5 hari",
    current: "18 hari",
  },

  // Incident frequency
  productionIncidents: {
    sixMonthsAgo: "1 per bulan",
    current: "4 per bulan",
  },

  // Team health
  developerSatisfaction: 3.2, // out of 10
  attritionRisk: "HIGH",
};

Calculate the Cost of Inaction

business-case/cost-of-inaction.ts
const costOfInaction = {
  // Wasted engineer time per bulan
  wastedTimePerMonth: {
    engineers: 5,
    wastedPercentage: 0.55, // 55% waktu terbuang
    monthlyCost: 5 * 30_000_000 * 0.55,
    // = Rp 82,500,000 per bulan terbuang
  },

  // Projected over 12 months
  annualWaste: 82_500_000 * 12,
  // = Rp 990,000,000 (hampir Rp 1 Miliar!)

  // Attrition cost jika developer resign
  attritionCost: {
    probabilityOfResignation: 0.4, // 40% risk
    costPerReplacement: 150_000_000, // 5x monthly salary
    expectedAttritionCost: 0.4 * 2 * 150_000_000,
    // = Rp 120,000,000 (2 engineers likely to leave)
  },

  // Opportunity cost: fitur yang tidak di-ship
  lostRevenue: {
    featuresDelayed: 8, // per quarter
    avgRevenuePerFeature: 50_000_000,
    quarterlyLoss: 8 * 50_000_000,
    // = Rp 400,000,000 per quarter
  },

  totalAnnualCost: 990_000_000 + 120_000_000 + 400_000_000 * 4,
  // = Rp 2,710,000,000 (Rp 2.71 Miliar!)
};

Present the Investment (Bukan "Biaya")

business-case/refactoring-investment.ts
const refactoringInvestment = {
  duration: "8 minggu focused refactoring",
  teamAllocation: "3 dari 5 engineers (2 tetap di fitur)",
  investmentCost: 3 * 30_000_000 * 2, // 3 eng x 2 bulan
  // = Rp 180,000,000

  expectedOutcomes: {
    velocityRecovery: "18 → 35 story points (+94%)",
    featureDeliveryTime: "18 hari → 7 hari (-61%)",
    bugRate: "-60% production incidents",
    mergeConflicts: "-80% conflict resolution time",
    developerSatisfaction: "3.2 → 7.5 (out of 10)",
  },

  roi: {
    annualSavings: 990_000_000 * 0.6, // 60% waste recovered
    // = Rp 594,000,000 per tahun
    investmentCost: 180_000_000,
    roiMultiple: 594_000_000 / 180_000_000,
    // = 3.3x ROI in year 1
    paybackPeriod: "~4 bulan",
  },
};

Show the Timeline with Milestones

Propose Risk Mitigation

2 Engineers Tetap di Fitur

Bisnis tidak berhenti. 2 dari 5 engineer tetap fokus pada fitur prioritas tinggi selama periode refactoring, memastikan pipeline produk tidak kering.

Rollback Plan

Setiap perubahan refactoring dilakukan di branch terpisah dengan feature flag. Jika terjadi masalah, rollback bisa dilakukan dalam hitungan menit, bukan jam.

Weekly Progress Report

Setiap minggu, manajemen mendapat laporan: apa yang sudah diperbaiki, metrik yang berubah, dan ETA penyelesaian. Transparansi penuh, tanpa kejutan.

Kill Switch

Jika setelah 4 minggu tidak ada improvement yang terukur, tim kembali ke mode fitur 100%. Manajemen punya kontrol penuh.

4.3 Pitch Deck: Satu Slide yang Meyakinkan

Jika Anda hanya punya satu slide untuk meyakinkan manajemen, gunakan ini:

Tanpa RefactoringDengan Refactoring
InvestasiRp 0Rp 180 Juta (8 minggu)
Kerugian TahunanRp 2.71 MiliarRp 990 Juta
Feature Delivery18 hari/fitur (memburuk)7 hari/fitur (membaik)
Production Incidents4x/bulan (naik)1x/bulan (stabil)
Developer Attrition Risk40% (2 orang)10% (normal)
Velocity TrendTurun 50% per 6 bulanNaik 20% per quarter
Net Impact (Year 1)-Rp 2.71 Miliar+Rp 594 Juta savings
ROIN/A3.3x

Kalimat Penutup Pitch

"Menghentikan rilis fitur selama 8 minggu untuk memperbaiki fondasi bukan menghambat bisnis — ini menyelamatkan Rp 1.72 Miliar per tahun dan mengembalikan kemampuan tim untuk bergerak cepat. Tanpa investasi ini, dalam 12 bulan kita akan membutuhkan rewrite total yang biayanya 5-10x lebih besar."


5. Playbook: Strategi Refactoring yang Terukur

5.1 Prioritization Matrix

Tidak semua tech debt sama. Gunakan matrix ini untuk memprioritaskan:

5.2 The 20% Rule

Sustainable Debt Management

Setelah refactoring besar selesai, alokasikan 20% setiap sprint untuk tech debt prevention. Ini seperti membayar cicilan — kecil, konsisten, dan mencegah utang menumpuk lagi. 20% hari ini mencegah 100% rewrite besok.

strategy/sprint-allocation.ts
interface SprintAllocation {
  featureDevelopment: number;
  techDebtPaydown: number;
  bugFixes: number;
}

const sustainableAllocation: SprintAllocation = {
  featureDevelopment: 70, // Fitur baru untuk bisnis
  techDebtPaydown: 20, // Bayar cicilan tech debt
  bugFixes: 10, // Buffer untuk bug tak terduga
};

// Monitor: Jika bugFixes konsisten > 20%,
// itu tanda tech debt masih terlalu tinggi

5.3 Metrics Dashboard: Track the Recovery

Setelah refactoring dimulai, track metrik ini setiap sprint:

Prop

Type


6. Lessons Learned: What We'd Do Differently


7. Conclusion

TL;DR

Tech debt bukan masalah teknis — ini masalah bisnis yang kebetulan manifest di kode. Cara mengatasinya bukan dengan bahasa teknis, tapi dengan bahasa bisnis: kerugian yang terukur, investasi yang terkuantifikasi, dan ROI yang bisa dipertanggungjawabkan.

Akui Bahwa Kecepatan MVP Punya Harga

Setiap shortcut di masa lalu adalah pinjaman yang kini harus dibayar. Bukan kesalahan — itu keputusan yang tepat saat itu. Tapi sekarang konteksnya berbeda, dan hutangnya harus dikelola.

Kuantifikasi Kerugian, Bukan Keluhan

Jangan bilang "kodenya berantakan." Bilang: "Tim kehilangan Rp 720 Juta per tahun karena 70% waktu terbuang untuk navigasi arsitektur yang rapuh." Data mengalahkan opini.

Migrasi Bertahap, Bukan Big Bang

Strangler Fig Pattern. Feature flags. Reverse proxy. Zero downtime. Tidak ada alasan untuk mempertaruhkan bisnis yang sedang berjalan demi refactoring.

Jual dengan ROI, Bukan Ego

Manajemen tidak peduli bahwa kode Anda "lebih bersih." Mereka peduli bahwa investasi Rp 180 Juta menghasilkan penghematan Rp 594 Juta per tahun. Frame as investment, not expense.

Cegah, Jangan Tunggu Krisis

Alokasikan 20% setiap sprint untuk tech debt. Bayar cicilan. Jangan tunggu sampai bunganya lebih besar dari principal.

"The best time to refactor was 6 months ago. The second best time is now." Setiap hari yang berlalu tanpa tindakan, bunga tech debt bertambah. Mulai hari ini — bukan dengan rewrite besar-besaran, tapi dengan satu langkah terukur yang membuktikan bahwa memperbaiki fondasi adalah investasi, bukan biaya.


Related Articles

Refactoring & Compounding Tech Debt | Faisal Affan