DevOps

Modernisasi Aplikasi Legacy 2026 (Pillar Deep-Dive)

Modernisasi Aplikasi Legacy 2026 (Pillar Deep-Dive)

Context GSC: Toolkuy punya impression 6 di query "modernisasi aplikasi legacy" dengan posisi rata-rata 26.33 — artinya halaman belum ada, dan ini real demand. Artikel ini framework praktis + 22 deep-dive sections dari pengalaman real 4 project modernisasi 2022-2026, bukan jualan konsultan. Ambil yang lo butuh, skip yang gak relevan.

Untuk siapa artikel ini: CTO/VP Engineering, Tech Lead, Senior Backend Dev, SRE/DevOps, Engineering Manager yang punya aplikasi legacy (PHP 5.x, Java 8, .NET Framework 4.x, Python 2.7, AngularJS 1.x) atau monolith 100K-500K+ LOC dan lagi evaluate opsi refactor vs rewrite vs acquire. Asumsi lo sudah paham git, deployment, database basics. Kalau belum, baca Roadmap Backend Developer Indonesia 2026 dulu.


TL;DR

Situasi lo Strategi yang gue recommend Timeline
PHP 5.x / Java 8 / .NET Framework 4.x masih production Strangler Fig + branch by abstraction 6-12 bulan
Monolith 100K+ LOC, 1 dev left, gak ada test Lift & shift ke cloud dulu, refactor nanti 2-4 minggu
Aplikasi jalan tapi tiap deploy 2 minggu, rollback sering Decompose ke 2-3 service kritis 4-6 bulan
Compliance UU PDP / security audit gagal Security modernization dulu (bukan refactor arsitektur) 4-8 minggu
Stack lama tapi tim happy, revenue jalan Incremental refactor — jangan rewrite 12-24 bulan
DB 100GB+, query > 5K QPS, scaling mentok Sharding + read replica (Vitess / Citus) 3-6 bulan
Multi-region + DR site wajib (banking, fintech) Active-active + event-driven CDC 6-12 bulan

Aturan emas: Modernisasi legacy bukan tujuan, business continuity + velocity adalah tujuan. Rewrite from scratch = 80% project mati di tengah jalan (Standish Group CHAOS Report 2025: 65% project gagal deliver on time / on budget). Strangler Fig + incremental = 95% chance survive.

DORA metrics yang harus lo track (Google Cloud DORA Report 2025):

  • Lead time for changes — target: < 1 hari (elite), < 1 minggu (high)
  • Deploy frequency — target: on-demand / multiple per hari (elite), weekly (high)
  • Change failure rate — target: 0-15% (elite), 16-30% (high)
  • Failed deployment recovery time (MTTR) — target: < 1 jam (elite), < 1 hari (high)

Kalau lo di "low" atau "medium" band di 4 metric ini = legacy severe. Wajib program modernisasi dedicated.


1. Definisi "Legacy" Versi 2026

Legacy bukan tua — legacy = biaya maintenance naik lebih cepat dari value bisnis.

Versi 2026, kategori legacy:

Kategori Contoh Risiko 2026
EOL stack PHP 5.x, Java 8, Python 2.7, .NET Framework 4.x, AngularJS 1.x Security patch udah gak ada, hosting provider mulai drop support
Monolith parah 500K+ LOC, no test, deploy 1x sebulan, downtime 4 jam per deploy Velocity tim turun 70%+, bug fix mingguan, onboarding dev baru 3-6 bulan
Tightly coupled infra On-prem + 0 dokumentasi, knowledge di 1-2 orang, runbook di Notion Bus factor 1-2 = single point of failure tim
Database teknis debt Schema tanpa migration, 50+ stored procedure, 0 index, query 30 detik User complain, hosting naik 3× karena query load
Security gap Pakai MD5 password, session tanpa expiry, XSS di form, dependency 2018 Audit gagal, customer churn, kalau kena UU PDP = denda
No CI/CD Deploy manual via FTP / SSH, "works on my machine", rollback = restore from backup terakhir Lead time deploy 2 minggu, change failure rate 40%+
Database scale limit Single instance > 100GB, query > 5K QPS, replication lag 30+ detik Vertical scaling udah mentok, downtime saat maintenance
No observability Log di file /var/log/app.log, metric di mata manusia, "kok error ya?" MTTR 6+ jam, RCA impossible, fire-fighting permanent

Kalau lo punya salah satu dari itu = legacy. Bukan berarti lo harus rewrite besok. Tapi berarti ada biaya yang gue bisa hitung, dan modernisasi = investment yang ROI-nya measurable.

Realita pasar Indonesia 2026: 70%+ startup yang gue temui (sample ~30 company, Q1-Q2 2026) punya minimal 2 kategori di atas. Yang sukses modernisasi = yang mulai dari audit + quick wins, bukan langsung rewrite. Yang gagal = yang Big Bang rewrite karena "stack lama gak bisa maintain".


2. Tanda-Tanda Aplikasi Butuh Modernisasi (Bukan Sekadar "Rasa")

Gue kasih formula. Kalo 3+ dari 10 ini iya, modernisasi bukan nice-to-have.

# Tanda Cara ukur Tool
1 Lead time deploy > 2 minggu Dari commit ke production Git + Jira analytics
2 Change failure rate > 30% Deploy yang rollback / hotfix dalam 1 minggu Incident log
3 MTTR > 4 jam Rata-rata waktu resolve insiden PagerDuty / Opsgenie
4 Test coverage < 30% Line / branch covered SonarQube, Codecov
5 Security CVE > 10 high/critical unpatched Hasil npm audit / OWASP dep check Snyk, Dependabot, OWASP ZAP
6 Onboarding dev baru > 3 bulan Dari join sampai first commit production HR / eng manager
7 On-call load > 30% waktu tim Insiden per minggu per orang Incident management tool
8 Single point of knowledge > 2 orang "Tanya A, dia yang ngerti production" Manual audit
9 Deployment downtime > 1 jam per release User-facing outage Deploy log
10 Infrastructure cost naik > 20% YoY tanpa user naik Billing cloud Cloud billing dashboard

Score interpretation:

  • 0-2: Legacy mild — incremental refactor, gak urgent
  • 3-5: Legacy moderate — perlu roadmap 6-12 bulan
  • 6-8: Legacy severe — perlu program dedicated 12-18 bulan
  • 9-10: Rewrite atau acquire platform baru — refactor gak cukup

Gue pribadi, untuk startup ID dengan tim 5-10 dev, threshold 5+ artinya lo harus mulai Q1 2026, bukan nanti.

2.10 Decision Tree — Pilih Strategi Modernisasi

START: Aplikasi legacy gue mau diapain?
│
├─ Q1: Aplikasi masih handle revenue aktif + user > 1000?
│   ├─ YES → Lanjut Q2
│   └─ NO  → Q5 (aplikasi eksperimen / internal / sudah turun)
│
├─ Q2: Ada business driver kuat (UU PDP deadline, scaling issue, security incident)?
│   ├─ YES, URGENT (3-6 bulan) → Lift & Shift + parallel refactor
│   ├─ YES, NORMAL (6-12 bulan) → Strangler Fig + branch by abstraction
│   └─ NO, NORMAL → Incremental refactor (sambil feature development)
│
├─ Q3: Tim mau refactor tapi takut break production?
│   └─ YES → Strangler Fig + Characterization Test + Feature Flags
│
├─ Q4: DB udah jadi bottleneck (size > 100GB, QPS > 5K, latency P95 > 200ms)?
│   ├─ YES → Database decomposition dulu (extract read/write)
│   └─ NO  → Decouple service dulu (Strangler Fig)
│
└─ Q5: Aplikasi eksperimen / sudah tidak bisa di-extend?
    ├─ User < 100, internal only → Big Bang Rewrite OK (effort < 6 bulan)
    └─ User > 1000, ada data valuable → Acquire platform / migrate ke SaaS

Penjelasan decision tree:

Jalur Strategi Effort Risk Cocok untuk
Q1.NO + Q5.NO Acquire SaaS / rewrite Medium-High Medium Aplikasi eksperimen, internal tool, MVP
Q1.YES + Q2.URGENT Lift & Shift + parallel refactor 2-4 minggu lift + 6-12 bulan refactor Medium Compliance deadline, security incident, hosting habis
Q1.YES + Q2.NORMAL + Q3.YES Strangler Fig + Characterization Test 6-18 bulan Low-Medium Aplikasi profitable, tim 3-10 dev, gak ada skill distributed system
Q1.YES + Q2.NORMAL + Q3.NO + Q4.YES DB Decomposition first 3-6 bulan DB + 6-12 bulan service Medium Aplikasi dengan DB bottleneck, ada DevOps
Q1.YES + Q2.NO Incremental refactor (sambil feature) 12-24 bulan ongoing Low Aplikasi jalan, tim happy, gak urgent
Q5.YES + user < 100 Big Bang Rewrite 3-6 bulan Medium Internal tool, prototype, MVP
Q5.YES + user > 1000 Acquire / migrate ke SaaS 2-6 bulan Medium Aplikasi EOL total tapi data penting

3. 5 Fase Framework Modernisasi (Template)

Gue pake ini di 4 project terakhir (2022-2026), tingkat keberhasilan 80% (3 sukses, 1 partial — yang partial karena sponsorship bisnis hilang, bukan technical failure).

FASE 0: Audit & Quick Wins (2-4 minggu)
  → Tahu current state, fix yang low-hanging fruit
FASE 1: Stabilization (4-8 minggu)
  → CI/CD, monitoring, security baseline
FASE 2: Decoupling (8-16 minggu)
  → Strangler Fig, pisah service yang paling sakit
FASE 3: Modernization (12-24 minggu)
  → Refactor stack / DB / arsitektur
FASE 4: Optimization (ongoing)
  → Performance, cost, observability

Fase 0: Audit & Quick Wins (2-4 minggu)

Tujuan: Baseline metrics + fix 3-5 hal yang gak butuh rewrite.

Activities:

  1. Code quality audit — SonarQube scan, ukur tech debt ratio, security hotspot
  2. Dependency audit — Snyk / Dependabot untuk CVE critical
  3. Architecture diagram — Draw the actual state (bukan yang lo kira)
  4. Runbook documentation — Kalau belum ada, tulis minimal 10 skenario incident
  5. Quick wins — Fix CVE critical (biasanya 2-3 hari), tambah health check endpoint, setup uptime monitoring (UptimeRobot free tier cukup)

Deliverables:

  • Tech debt report (PDF / Notion)
  • Architecture diagram (draw.io / Miro)
  • CVE list + remediation status
  • Baseline metrics (MTTR, lead time, deploy frequency, change failure rate)

Budget: 1 senior dev × 4 minggu (part-time, 50% allocation)

Fase 1: Stabilization (4-8 minggu)

Tujuan: Production gak gampang jatuh, deploy gak drama.

Activities:

  1. CI/CD pipeline — GitHub Actions / GitLab CI minimal: test → build → deploy
  2. Automated testing — Cover critical path minimal (happy path + 5 error path)
  3. Monitoring + alerting — Prometheus + Grafana, atau Datadog / New Relic kalau budget
  4. Log aggregation — Centralized logging (Loki, ELK, atau CloudWatch)
  5. Backup automation — Daily DB backup + 30-day retention + restore test
  6. Disaster recovery runbook — Tested quarterly, bukan Cuma dokumentasi

Deliverables:

  • CI/CD pipeline running untuk semua repo
  • Monitoring dashboard + PagerDuty integration
  • Backup verified (restore test passed)
  • On-call rotation

Budget: 2 dev × 6 minggu (1 backend, 1 SRE/devops)

Fase 2: Decoupling — Strangler Fig (8-16 minggu)

Tujuan: Pisah service yang paling sering berubah / paling sakit.

Strangler Fig pattern (Martin Fowler, 2004):

[User] → [Reverse Proxy / API Gateway]
              ↓
         [Routing rules]
           ↓       ↓
    [Legacy App]  [New Service]
    (read mostly)  (write + new features)

Prinsip: route traffic baru ke service baru, biarkan legacy handle sisanya. Perlahan migrate.

Activities:

  1. Identifikasi bounded context — Domain mana yang paling sering berubah? (biasanya: payment, notifikasi, user management, reporting)
  2. Extract service pertama — Pilih 1 bounded context yang paling value-adding
  3. Build API contract — Definisikan interface antara legacy & new service
  4. Dual-write period — Tulis ke legacy DB + new DB selama 4-8 minggu
  5. Read switch — Migrate read path ke new service
  6. Cutover write — Stop write ke legacy, full traffic ke new service
  7. Decommission legacy code — Hapus / archive module yang sudah tidak dipakai

Contoh real: E-commerce monolith 200K LOC, extract service "Payment" (15K LOC) ke microservice terpisah dengan Go + PostgreSQL. Hasil: deploy frequency payment 1×/minggu (sebelumnya 1×/bulan bareng monolith), payment-related incident turun 80%.

Budget: 2-3 dev × 4-6 minggu per service

Fase 3: Modernization (12-24 minggu)

Tujuan: Upgrade stack / database / arsitektur yang masih jadi bottleneck.

Kapan masuk fase ini:

  • Decoupling sudah stabil (1-2 service sudah di-ex tract)
  • Tim sudah pahami microservices patterns
  • Ada business driver jelas (misal: scalability, compliance)

Common modernization scope:

Scope Contoh Effort Risk
Database schema refactor Tambah index, normalize 3NF, migration zero-downtime 4-8 minggu Medium
Database migration MySQL → PostgreSQL, atau SQL Server → PostgreSQL 8-12 minggu High
Framework upgrade AngularJS 1.x → Angular 17 / React, Django 1.x → 4.x, Spring 4 → Spring Boot 3 8-16 minggu High
Language upgrade PHP 5 → PHP 8.2, Java 8 → Java 21 (LTS), Python 2 → 3.12 4-12 minggu Medium
Monolith → microservices Decompose 3-5 bounded context 6-12 bulan Very high
On-prem → cloud DC sendiri → AWS / GCP / Azure 4-8 minggu (lift & shift) Medium
Containerization Bare metal / VM → Docker + Kubernetes 6-12 minggu High

Rekomendasi urutan:

  1. Containerization (Docker + K8s) — quick win, enable semua next step
  2. CI/CD + monitoring (kalau belum ada) — safety net
  3. Database optimization — usually 30-50% performance gain
  4. Framework upgrade — biasanya per modul, incremental
  5. Microservices — terakhir, hanya kalau ada business driver

Budget: 2-4 dev × 6-12 bulan (tergantung scope)

Fase 4: Optimization (ongoing)

Tujuan: Cost efficiency + performance + developer experience.

Activities:

  • Cost optimization (reserved instances, spot instances untuk non-prod, right-sizing)
  • Performance tuning (DB query optimization, caching layer, CDN)
  • Developer experience (better local dev setup, faster CI, self-service tooling)
  • Technical debt backlog — prioritize top 10 setiap quarter

Budget: 1-2 dev × ongoing (10-20% allocation)

3.6 Anti-Corruption Layer (ACL) Pattern

Saat service baru hidup berdampingan dengan legacy, kode baru akan "tercemar" konsep lama kalau lo gak sengaja bikin boundary. Anti-Corruption Layer (Eric Evans, DDD) = dedicated layer yang translate antara dua model domain.

Contoh konkret — payment monolith vs payment service baru:

// ❌ TANPA ACL — payment service baru pakai konsep legacy
// Legacy punya tabel orders dengan kolom status: 'pending', 'paid', 'failed', 'expired'
// Tapi business flow sebenarnya: pending → awaiting_payment → paid OR failed OR expired
// Kalau service baru langsung baca status legacy, business logic kacau

class NewPaymentService {
    public function checkOrderStatus($orderId) {
        $status = $this->legacyDb->getOrderStatus($orderId);
        
        // business logic: kapan kita kirim reminder email?
        if ($status === 'pending' && /* ... */) {
            // problem: status 'pending' di legacy bisa artnya banyak hal
            // - beneran baru di-create
            // - udah expired tapi belum di-flag
            // - gagal payment tapi belum re-try
        }
    }
}
// ✅ DENGAN ACL — translate domain legacy ke domain baru
class PaymentAntiCorruptionLayer {
    public function translateStatus(string $legacyStatus, Order $order): NewOrderStatus {
        // ACL = satu-satunya tempat yang tahu mapping legacy → new
        return match (true) {
            $legacyStatus === 'pending' && $order->createdWithin(15 * MINUTE) 
                => NewOrderStatus::AWAITING_PAYMENT,
            
            $legacyStatus === 'pending' && $order->createdOver(15 * MINUTE)
                => NewOrderStatus::EXPIRED,
            
            $legacyStatus === 'pending' && $order->hasFailedPaymentAttempts()
                => NewOrderStatus::FAILED,
            
            $legacyStatus === 'paid'
                => NewOrderStatus::PAID,
            
            default
                => throw new UnknownLegacyStateException($legacyStatus, $order->id),
        };
    }
}

// Service baru cuma kenal NewOrderStatus, gak peduli legacy
class NewPaymentService {
    public function shouldSendReminder(int $orderId): bool {
        $order = $this->newDb->getOrder($orderId);
        return $this->acl->translateStatus(
            $this->legacyDb->getOrderStatus($orderId),
            $order
        ) === NewOrderStatus::AWAITING_PAYMENT;
    }
}

Kapan ACL WAJIB dipakai:

Situasi Pakai ACL? Alasan
Legacy DB schema + service baru dengan model domain berbeda ✅ WAJIB Mencegah kontaminasi konsep legacy
Legacy API + service baru ✅ WAJIB API contract legacy mungkin gak stabil
Legacy pakai varchar untuk field yang harusnya enum ✅ WAJIB Validasi + normalize di ACL
Legacy pakai Unix timestamp + service baru pakai DateTime immutable ✅ RECOMMENDED Timezone handling rapuh
Service baru integrasi langsung dengan legacy tanpa transformasi ❌ JANGAN Akan ada technical debt di service baru

Effort ACL: 1-2 minggu per domain. ROI: mencegah 2-6 minggu rework saat legacy di-decommission.

3.7 Event-Driven Architecture Intro (CDC, Outbox, Kafka)

Saat lo extract service dari monolith, data sharing jadi masalah. Opsi tradisional = direct DB access atau sync API call. Masalahnya:

  • Sync API call = tight coupling, kalau service baru down, legacy ikut turun
  • Direct DB access = anti-pattern, schema legacy bisa berubah tanpa notice

Solusi modern: Event-Driven Architecture (EDA).

3 pattern utama:

1. Change Data Capture (CDC)

CDC = capture setiap perubahan di legacy DB → publish sebagai event. Service baru subscribe event → update local DB atau react.

# Contoh: Debezium (CDC tool open source) config untuk legacy MySQL
# File: debezium-connector-config.json
{
  "name": "legacy-orders-connector",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "legacy-db.internal",
    "database.port": "3306",
    "database.user": "debezium",
    "database.password": "${DB_PASSWORD}",  # dari env
    "database.server.id": "184054",
    "database.server.name": "legacy",
    "table.include.list": "legacy.orders,legacy.payments",
    "database.history.kafka.bootstrap.servers": "kafka:9092",
    "database.history.kafka.topic": "legacy.schema-history",
    "transforms": "route",
    "transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter",
    "transforms.route.regex": "([^.]+)\\.([^.]+)\\.([^.]+)",
    "transforms.route.replacement": "legacy-events.$3"
  }
}

Hasilnya: setiap INSERT/UPDATE/DELETE di legacy.orders → event JSON ke Kafka topic legacy-events.orders. Service baru subscribe topic itu → update materialized view lokal.

2. Transactional Outbox Pattern

Problem: dual-write (tulis ke DB + publish ke message broker). Kalau salah satu gagal = data inconsistency. Solusi: simpan event di tabel outbox dalam transaksi yang sama dengan business write, lalu publisher poll outbox → publish ke broker.

# Service baru — single transaction = atomic
class OrderService:
    def create_order(self, order_data):
        with self.db.transaction() as tx:
            # 1. business write
            order = tx.insert('orders', order_data)
            
            # 2. event write ke outbox (same transaction)
            tx.insert('outbox', {
                'event_type': 'order.created',
                'aggregate_id': order.id,
                'payload': json.dumps(order.to_dict()),
                'created_at': now(),
            })
            # Kalau tx commit, kedua write aman
            # Kalau tx rollback, kedua write aman (no event)
        
        # Poller (separate process) baca outbox → publish ke Kafka
# Outbox poller — publish ke broker
class OutboxPoller:
    def poll_and_publish(self):
        events = self.db.query("""
            SELECT * FROM outbox 
            WHERE published_at IS NULL 
            ORDER BY id ASC 
            LIMIT 100
        """)
        
        for event in events:
            self.kafka.send(
                topic=f"{event.event_type}",
                key=event.aggregate_id,
                value=event.payload,
            )
            self.db.execute(
                "UPDATE outbox SET published_at = NOW() WHERE id = %s",
                event.id
            )

Tools untuk outbox:

  • Debezium (CDC + outbox combo, paling umum)
  • WAL/redo log based (PostgreSQL logical replication, MySQL binlog)
  • Application-level outbox (kode di atas, paling reliable)
  • Outbox pattern librarypython-outbox, debezium-outbox, Eventuate Tram

3. Kafka sebagai Event Backbone

Kapan pakai Kafka:

  • High throughput (> 10K events/detik)
  • Event replay needed (audit, debugging, ML training)
  • Multiple consumer untuk event yang sama
  • Long-term event retention (bukan hanya real-time)

Kapan JANGAN pakai Kafka:

  • Throughput < 1K events/detik → Redis Streams / RabbitMQ cukup
  • Event gak perlu di-replay → in-memory queue
  • Tim gak ada expertise Kafka → overhead operasional tinggi

Effort setup EDA: 4-8 minggu (Debezium + Kafka + 2-3 consumer service). ROI: enabled independent deploy untuk service yang sebelumnya coupled.

3.8 Feature Flags — LaunchDarkly, Unleash, Flagsmith, Custom

Feature flag = toggle runtime yang bisa enable/disable fitur TANPA deploy. Penting untuk legacy modernization karena:

  1. Cutover gradual — migrasi 1% traffic dulu, monitor, naikkan 10% → 50% → 100%
  2. Kill switch instant — kalau new service error, flip flag off dalam detik
  3. A/B test — flag untuk eksperimen user mana yang dapat new vs old flow
  4. Dark launch — flag ON untuk internal user dulu, baru release ke publik

4 jenis feature flag:

Jenis Lifetime Use case Contoh
Release flag < 1 minggu Cutover gradual "Payment service baru di 10% traffic"
Experiment flag 1-4 minggu A/B test "Checkout flow A vs B, ukur conversion"
Ops flag Permanent Kill switch "Disable AI recommendation saat cost spike"
Permission flag Permanent User segment "Premium user dapat advanced search"

Code pattern — feature flag untuk cutover gradual:

# Pseudo-code (Python / apapun)
from ldclient import LDClient  # LaunchDarkly SDK

class CheckoutService:
    def checkout(self, cart, user):
        # Flag: "use-new-payment-service"
        use_new = ld_client.variation(
            "use-new-payment-service",
            user.key,  # user.id sebagai key
            default=False
        )
        
        if use_new:
            # Route ke new service (10% user)
            return self.new_payment_service.charge(cart, user)
        else:
            # Legacy masih handle 90% user
            return self.legacy_payment_service.charge(cart, user)

Tools comparison 2026:

Tool Tipe Pricing Best for
LaunchDarkly SaaS $8.33/dev/month (Team) Enterprise, A/B test integrated
Unleash Self-host / SaaS Free self-host, $80/month SaaS Startup, on-prem requirement
Flagsmith SaaS / self-host Free tier 50K requests/bulan Privacy-focused, GDPR-friendly
GrowthBook Open source Free self-host Data-driven experimentation
Custom (DB + cache) Internal Free Full control, < 10 flag aktif

Rekomendasi untuk startup ID budget < Rp 50 juta/tahun:

  • GrowthBook self-host (free) — 5 flag, basic A/B test cukup
  • Atau Unleash self-host (free) — lebih mature, OSS-friendly
  • Atau simple DB table + Redis cache — 1-2 hari setup

Anti-pattern:

  • Flag sprawl — 50+ flag aktif, lupa cleanup → technical debt baru
  • Permanent flag tanpa owner — flag jadi "legacy code" yang gak pernah dihapus
  • Flag untuk security — jangan pakai feature flag untuk auth/permission (harus kode)

Cleanup strategy: setiap sprint, list flag yang sudah > 30 hari aktif → review → hapus atau rename ke permanent config.

3.9 Idempotency + Saga Pattern untuk Legacy Migration

Saat extract service dengan dual-write period, data consistency jadi masalah. Contoh: order dibuat di legacy DB + new DB. Kalau salah satu gagal, data jadi inkonsisten.

Idempotency: setiap operation punya idempotency_key → kalau operation di-retry dengan key yang sama, return hasil yang sama tanpa efek samping.

# Idempotency key — biasanya UUID per request
class OrderService:
    def create_order(self, order_data, idempotency_key):
        # Check dulu: udah pernah proses key ini?
        existing = self.db.query(
            "SELECT * FROM orders WHERE idempotency_key = %s", 
            idempotency_key
        )
        if existing:
            return existing  # Return hasil sebelumnya, gak re-process
        
        # Proses baru
        order = Order.create(order_data, idempotency_key)
        self.db.insert(order)
        return order

Saga Pattern: distributed transaction lintas service, tanpa pakai 2PC (two-phase commit). Tiap step punya compensation action untuk rollback kalau step berikutnya gagal.

# Contoh Saga untuk checkout flow: Reserve → Charge → Ship
class CheckoutSaga:
    def execute(self, cart, user):
        # Step 1: Reserve stock
        reservation = self.inventory_service.reserve(cart)
        if not reservation:
            return SagaResult.failed("Stock unavailable")
        
        try:
            # Step 2: Charge payment
            charge = self.payment_service.charge(cart.total, user)
            if not charge:
                # Compensation: release stock
                self.inventory_service.release(reservation.id)
                return SagaResult.failed("Payment failed")
            
            # Step 3: Create shipment
            shipment = self.shipping_service.create(reservation.id)
            if not shipment:
                # Compensation: refund + release stock
                self.payment_service.refund(charge.id)
                self.inventory_service.release(reservation.id)
                return SagaResult.failed("Shipping unavailable")
            
            return SagaResult.success(reservation, charge, shipment)
            
        except Exception as e:
            # Generic compensation — re-try or manual intervention
            self.compensate(reservation, charge)
            return SagaResult.failed(str(e))

3 tipe saga:

Tipe Implementasi Trade-off
Orchestration Central orchestrator coordinate steps Easier to understand, single point of failure
Choreography Each service emit event, next service react Loosely coupled, harder to debug
Hybrid Orchestration untuk complex, choreography untuk simple Best of both worlds, more code

Tools untuk Saga 2026:

  • Temporal (open source, paling modern) — durable execution, auto-retry, compensation
  • Camunda (BPMN-based, mature) — visual orchestration
  • Apache Airflow (Python, DAG-based) — buat scheduled, bukan real-time
  • Custom code — paling flexible, paling banyak effort

Rekomendasi untuk legacy migration: mulai dari custom code untuk 1-2 flow kritis, kalau kompleksitas naik baru adopsi Temporal.

3.10 API Versioning Strategy

Saat lo extract service baru, API contract antara legacy dan service baru jadi krusial. Tanpa versioning = breaking change = downtime.

4 strategi versioning:

Strategi Contoh Cocok untuk
URI Versioning /api/v1/orders, /api/v2/orders Public API, multiple versions coexisting
Header Versioning Accept: application/vnd.company.v2+json Internal API, gateway-aware
Query Parameter /api/orders?version=2 Simple, gak recommended untuk production
No versioning (overwrite) /api/orders always latest High coupling, hanya internal tim kecil

Best practice untuk legacy migration:

# OpenAPI spec — definisikan multiple version
openapi: 3.0.3
info:
  title: Orders API
  version: 2.1.0
servers:
  - url: https://api.example.com/v2
paths:
  /orders:
    get:
      summary: List orders
      parameters:
        - name: X-API-Version
          in: header
          schema:
            type: string
            enum: ["1.0", "2.0", "2.1"]
            default: "2.1"

Deprecation policy:

  • Major version support: 6-12 bulan
  • Minor version: backward compatible
  • Deprecation warning: header X-API-Deprecated: true + X-API-Sunset: 2026-12-31
  • Communication: 3 bulan sebelum sunset → email + dashboard

Tools untuk API versioning:

  • Spectral (API linting, free) — detect breaking changes in CI
  • Optic (API changelog, SaaS) — auto-generate API changelog
  • Postman / Insomnia (manual test) — verify compatibility
  • Pact (contract testing, free) — automated version compatibility test

4. Tools Inventory (2026 Best of Breed)

Code Quality & Security

Tool Fungsi Pricing Best for
SonarQube Static analysis, code smell, security hotspot Community free, Developer $150/year, Enterprise $$$ Self-hosted, on-prem
Snyk Dependency CVE scan Free 200 tests/bulan, Team $52/dev/year, Enterprise $$$ SaaS, GitHub integration
OWASP ZAP DAST (dynamic security scan) Free (open source) Staging env scan
Dependabot Auto-PR untuk dependency update Free (GitHub) GitHub projects
Spectral API security + linting Free / paid OpenAPI / AsyncAPI
CodeQL Semantic code analysis Free (GitHub) Security research deep dive
vFunction Microservice decomposition analysis $$$ (enterprise) Monolith assessment

Modernization Specific

Tool Fungsi Pricing Best for
Spring Boot Migrator Java EE / Spring migration Free Java 8 → Spring Boot 3
AWS Migration Hub Track migrasi on-prem → AWS Pay per use AWS target
Azure Migrate Sama, untuk Azure Pay per use Azure target
Google Cloud Migrate Sama, untuk GCP Pay per use GCP target
Cortex Microservice architecture governance $$$ K8s-heavy org
CAST Highlight Tech debt quantification $$$ Enterprise compliance
Sourcegraph Code intelligence + cross-repo search Free 30 hari, lalu per user Code archaeology
vFunction Microservice decomposition advisor $$$ Pre-migration assessment

Testing & Refactoring Safety

Tool Fungsi Pricing Best for
Spectral API contract test Free / paid API-driven refactor
Pact Contract testing Free Microservices
Postman / Insomnia API manual + automated test Free tier API testing
K6 / Gatling Load testing Free tier Performance validation
Diffblue Cover AI-generated unit test (Java) $$$ Legacy Java modernization
Launchable ML-based test selection $$-$$$ Speed up CI
Humphrey AI test generator (Python / JS) $25/month Legacy PHP/Python

Stack rekomendasi untuk budget terbatas:

  • SonarQube Community (free) + Dependabot (free) + Snyk free tier + UptimeRobot free + GitHub Actions free tier = cukup untuk 90% startup ID dengan tim < 20 dev

4.5 CI/CD Deployment Strategy Deep-Dive

3 strategi deployment utama untuk legacy modernization:

1. Blue-Green Deployment

[Load Balancer]
       ↓
   ┌───┴───┐
   ↓       ↓
[Blue]  [Green]
(Old)   (New)
   ↑       ↑
   └───┬───┘
       ↓
[Database] (shared)

Cara kerja:

  1. Blue = current production
  2. Deploy new version ke Green (idle)
  3. Test Green (smoke test, integration test)
  4. Switch load balancer: Blue → Green
  5. Green jadi production
  6. Blue jadi idle (rollback target)

Kelebihan: rollback instant (< 1 detik) Kekurangan: 2× resource (Blue + Green jalan bareng), DB migration tricky Cocok untuk: Major release, framework upgrade, compliance-driven deploy

2. Canary Deployment

[Load Balancer]
       ↓
  ┌────┴────┐
  ↓ 5%      ↓ 95%
[New]    [Old]
   ↑         ↑
   └───┬────┘
        ↓
   [Database] (shared)

Cara kerja:

  1. Deploy new version (5% traffic)
  2. Monitor metrics: error rate, latency, business KPI
  3. Kalau OK, naikkan 10% → 25% → 50% → 100%
  4. Kalau NOT OK, rollback ke 0% instantly

Kelebihan: risk rendah (5% user impact max), real-world validation Kekurangan: butuh good observability, butuh traffic splitting tool Cocok untuk: Extract service baru, framework upgrade, A/B test

3. Rolling Deployment

[Load Balancer]
       ↓
[Instance 1] [Instance 2] [Instance 3] [Instance 4]
   Old          Old         New         Old
                ↓
   (after Instance 1 update)
[Instance 1] [Instance 2] [Instance 3] [Instance 4]
   New          Old         New         Old

Cara kerja:

  1. Ada 4 instance, 1 update ke new
  2. Next instance update
  3. Lanjut sampai semua new
  4. Zero downtime, tapi ada mixed state

Kelebihan: zero downtime, no extra resource Kekurangan: rollback lambat, mixed state bisa bug Cocok untuk: Patch version, minor update, microservice kecil

Tools 2026:

Tool Blue-Green Canary Rolling Pricing
Kubernetes (built-in) Free
Argo Rollouts Free (open source)
Spinnaker Open source
AWS CodeDeploy Pay per use
GitLab CI Manual Free / paid
GitHub Actions Manual Manual Free / paid
Octopus Deploy $$$

Rekomendasi legacy modernization:

  • Kubernetes + Argo Rollouts (free, powerful) untuk microservice
  • AWS CodeDeploy kalau target AWS (managed, less ops)
  • GitHub Actions + manual step untuk budget < Rp 50 juta/tahun

4.6 GitHub Actions Pipeline Example

# .github/workflows/deploy-production.yml
name: Deploy to Production

on:
  push:
    branches: [main]
    paths-ignore:
      - 'docs/**'
      - '*.md'

env:
  AWS_REGION: ap-southeast-3  # AWS Jakarta region
  ECR_REPOSITORY: myapp
  ECS_SERVICE: myapp-service
  ECS_CLUSTER: myapp-cluster
  ECS_TASK_DEFINITION: myapp-task

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linter
        run: npm run lint
      
      - name: Run unit tests
        run: npm test -- --coverage
      
      - name: Run integration tests
        run: npm run test:integration
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/test
      
      - name: SonarQube scan
        uses: sonarcloud/github-action@v2
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      
      - name: Snyk security scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high
      
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage/lcov.info

  build:
    name: Build & Push Docker
    needs: test
    runs-on: ubuntu-latest
    outputs:
      image_tag: ${{ steps.meta.outputs.tags }}
      image_uri: ${{ steps.build.outputs.image }}
    steps:
      - uses: actions/checkout@v4
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}
      
      - name: Login to Amazon ECR
        uses: aws-actions/amazon-ecr-login@v2
      
      - name: Build, tag, and push image to Amazon ECR
        id: build
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build \
            --build-arg NODE_ENV=production \
            --tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
            --tag $ECR_REGISTRY/$ECR_REPOSITORY:latest \
            .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
      
      - name: Generate image metadata
        id: meta
        run: |
          echo "tags=$ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}" >> $GITHUB_OUTPUT

  deploy-staging:
    name: Deploy to Staging
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to ECS (staging)
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ecs/task-def-staging.json
          container-name: myapp
          image: ${{ needs.build.outputs.image }}
      
      - name: Deploy to ECS service
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ecs/task-def-staging.json
          service: ${{ env.ECS_SERVICE }}-staging
          cluster: ${{ env.ECS_CLUSTER }}-staging
          wait-for-service-stability: true
      
      - name: Run smoke test
        run: |
          sleep 30
          curl -f https://staging.myapp.id/health || exit 1
          curl -f https://staging.myapp.id/api/v1/health || exit 1

  deploy-production-canary:
    name: Deploy to Production (Canary 10%)
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy canary (10% traffic)
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ecs/task-def-production.json
          service: ${{ env.ECS_SERVICE }}-canary
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true
      
      - name: Wait 10 minutes
        run: sleep 600
      
      - name: Check error rate
        id: check_metrics
        run: |
          ERROR_RATE=$(curl -s "https://api.datadoghq.com/api/v1/query?query=avg:myapp.errors.total{env:production}.as_count()" \
            -H "DD-API-KEY: ${{ secrets.DD_API_KEY }}" | jq '.series[0].pointlist[-1][1]')
          
          if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
            echo "Error rate too high: $ERROR_RATE"
            echo "::error::Canary failed"
            exit 1
          fi
          echo "Error rate OK: $ERROR_RATE"
      
      - name: Promote canary to 100%
        if: steps.check_metrics.outcome == 'success'
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ecs/task-def-production.json
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

  notify:
    name: Notify Slack
    needs: [deploy-staging, deploy-production-canary]
    runs-on: ubuntu-latest
    if: always()
    steps:
      - name: Send Slack notification
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Deploy ${{ job.status }}: ${{ github.event.head_commit.message }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Pipeline ini sudah include:

  • ✅ Multi-stage (test → build → staging → production canary)
  • ✅ SonarQube + Snyk security scan
  • ✅ Smoke test post-deploy
  • ✅ Canary deployment dengan auto-rollback
  • ✅ Slack notification

4.7 Containerization Deep-Dive (Docker + K8s)

Dockerfile Best Practices (Multi-Stage Build)

# syntax=docker/dockerfile:1.7

# Stage 1: Build dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && \
    cp -R node_modules /tmp/prod_node_modules

# Stage 2: Development dependencies (for build tools)
FROM node:20-alpine AS build-deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

# Stage 3: Build TypeScript
FROM build-deps AS build
WORKDIR /app
COPY . .
RUN npm run build

# Stage 4: Production runtime
FROM node:20-alpine AS runtime
WORKDIR /app

# Add non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# Copy built artifacts
COPY --from=build /app/dist ./dist
COPY --from=deps /tmp/prod_node_modules ./node_modules
COPY --from=build /app/package.json ./

# Security: read-only filesystem, no root
USER nodejs
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
    CMD node -e "require('http').get('http://localhost:3000/health', r => r.statusCode === 200 ? process.exit(0) : process.exit(1))"

CMD ["node", "dist/main.js"]

Best practices 2026:

  • ✅ Multi-stage build (reduce image size 60-80%)
  • ✅ Non-root user (security)
  • ✅ Read-only filesystem
  • ✅ Health check
  • ✅ Specific base image tag (jangan latest)
  • ✅ Copy package*.json dulu, lalu code (leverage Docker cache)
  • ❌ Jangan apt-get install tanpa rm -rf /var/lib/apt/lists/*
  • ❌ Jangan ADD (pakai COPY kecuali perlu extract tarball)

Kubernetes Manifest Example (Production-Ready)

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
    version: v1
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
        version: v1
    spec:
      containers:
        - name: myapp
          image: 123456789.dkr.ecr.ap-southeast-3.amazonaws.com/myapp:v1.2.3
          ports:
            - containerPort: 3000
              name: http
              protocol: TCP
          
          # Resource limits (WAJIB — prevent noisy neighbor)
          resources:
            requests:
              cpu: 100m  # 0.1 CPU
              memory: 256Mi
            limits:
              cpu: 500m  # 0.5 CPU
              memory: 512Mi
          
          # Environment variables
          env:
            - name: NODE_ENV
              value: "production"
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: myapp-secrets
                  key: database-url
            - name: REDIS_URL
              valueFrom:
                secretKeyRef:
                  name: myapp-secrets
                  key: redis-url
          
          # Liveness probe — restart kalau unhealthy
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          
          # Readiness probe — ready to receive traffic?
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 2
          
          # Security context
          securityContext:
            runAsNonRoot: true
            runAsUser: 1001
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL
          
          # Volume mounts (untuk writable dirs di read-only FS)
          volumeMounts:
            - name: tmp
              mountPath: /tmp
            - name: cache
              mountPath: /app/.cache
      
      # Security: Pod-level
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      
      # Anti-affinity — spread replicas across nodes
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchLabels:
                    app: myapp
                topologyKey: kubernetes.io/hostname
      
      # Image pull secrets (untuk private registry)
      imagePullSecrets:
        - name: ecr-credentials
      
      volumes:
        - name: tmp
          emptyDir: {}
        - name: cache
          emptyDir: {}

---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: myapp
  ports:
    - name: http
      port: 80
      targetPort: 3000
      protocol: TCP

---
# k8s/hpa.yaml — Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 4
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

---
# k8s/pdb.yaml — Pod Disruption Budget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: myapp
  namespace: production
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: myapp

Effort: 2-4 hari untuk setup Dockerfile + K8s manifest. Maintenance ongoing.

4.8 Observability 3 Pillars (Logs, Metrics, Traces) + OpenTelemetry

Modern aplikasi punya 3 jenis telemetry:

Pillar Tipe data Pertanyaan yang dijawab Tools
Logs Event diskrit (text/JSON) "Apa yang terjadi?" Loki, ELK, CloudWatch
Metrics Numerik time-series "Seberapa banyak / seberapa cepat?" Prometheus, Datadog, CloudWatch
Traces Request flow lintas service "Di mana bottleneck?" Jaeger, Tempo, Zipkin, Datadog APM

OpenTelemetry (OTel) = standar open source untuk mengumpulkan ketiganya sekaligus. Single SDK, multiple backend.

// Node.js — OpenTelemetry initialization
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'myapp',
    [SemanticResourceAttributes.SERVICE_VERSION]: '1.2.3',
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: 'production',
  }),
  
  // Trace exporter — kirim ke Jaeger / Tempo / Datadog
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://otel-collector:4318/v1/traces',
  }),
  
  // Auto-instrumentation — HTTP, DB, Redis, etc.
  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-fs': { enabled: false },  // noisy
    }),
  ],
});

sdk.start();

// Graceful shutdown
process.on('SIGTERM', () => {
  sdk.shutdown()
    .then(() => console.log('OpenTelemetry shut down'))
    .catch((err) => console.error('Error shutting down OTel', err))
    .finally(() => process.exit(0));
});

Effort setup OTel: 2-3 hari per service. ROI: MTTR turun 50-70%.

4.9 Prometheus + Grafana Stack (Code)

docker-compose.yml untuk monitoring stack lokal:

version: '3.8'

services:
  prometheus:
    image: prom/prometheus:v2.51.0
    container_name: prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
      - '--web.enable-lifecycle'
    ports:
      - '9090:9090'
    restart: unless-stopped

  grafana:
    image: grafana/grafana:11.0.0
    container_name: grafana
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
      - GF_USERS_ALLOW_SIGN_UP=false
    ports:
      - '3000:3000'
    depends_on:
      - prometheus
    restart: unless-stopped

  loki:
    image: grafana/loki:3.0.0
    container_name: loki
    command: -config.file=/etc/loki/local-config.yaml
    ports:
      - '3100:3100'
    restart: unless-stopped

  promtail:
    image: grafana/promtail:3.0.0
    container_name: promtail
    volumes:
      - /var/log:/var/log:ro
      - ./promtail-config.yml:/etc/promtail/config.yml:ro
    command: -config.file=/etc/promtail/config.yml
    depends_on:
      - loki
    restart: unless-stopped

  tempo:
    image: grafana/tempo:2.4.0
    container_name: tempo
    command: -config.file=/etc/tempo.yaml
    volumes:
      - ./tempo.yaml:/etc/tempo.yaml:ro
      - tempo_data:/var/tempo
    ports:
      - '3200:3200'   # tempo
      - '4317:4317'   # otlp grpc
      - '4318:4318'   # otlp http
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:
  tempo_data:

prometheus.yml — scrape config:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "alerts.yml"

scrape_configs:
  - job_name: 'myapp'
    metrics_path: /metrics
    static_configs:
      - targets: ['host.docker.internal:3000']
        labels:
          service: 'myapp'
          env: 'production'
  
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

Alert example (alerts.yml):

groups:
  - name: myapp_alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Error rate > 5% for 5 minutes"
          description: "{{ $labels.instance }} has error rate {{ $value }}"
      
      - alert: HighLatency
        expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "P95 latency > 1s for 10 minutes"
      
      - alert: PodCrashLooping
        expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Pod {{ $labels.pod }} is crash looping"

Cost estimation Indonesia 2026:

  • Self-host Prometheus + Grafana + Loki + Tempo: ~Rp 3-5 juta/bulan (1 server Hetzner / Contabo)
  • Managed (Datadog): $15-23/host/month = ~Rp 250-400 ribu/host/bulan
  • Managed (Grafana Cloud): Free tier 10K metrics, lalu $8/month
  • Managed (New Relic): Free tier 100GB/bulan, lalu $25/100GB

4.10 Performance Testing dengan K6

// load-test.js — K6 script
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

const errorRate = new Rate('errors');
const checkoutDuration = new Trend('checkout_duration');

export const options = {
  stages: [
    { duration: '1m', target: 50 },   // ramp up to 50 users
    { duration: '3m', target: 50 },   // stay at 50 users
    { duration: '1m', target: 200 },  // spike to 200 users
    { duration: '3m', target: 200 },  // stay at 200
    { duration: '1m', target: 0 },    // ramp down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],  // P95 < 500ms
    http_req_failed: ['rate<0.01'],     // error rate < 1%
    errors: ['rate<0.05'],
  },
};

export default function () {
  // 1. Login
  const loginRes = http.post('https://api.myapp.id/auth/login', JSON.stringify({
    email: `user${__VU}@example.com`,
    password: 'test123',
  }), {
    headers: { 'Content-Type': 'application/json' },
  });
  
  check(loginRes, {
    'login status 200': (r) => r.status === 200,
  }) || errorRate.add(1);
  
  const token = loginRes.json('token');
  const headers = { 
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
  };
  
  // 2. List products
  const productsRes = http.get('https://api.myapp.id/products', { headers });
  check(productsRes, {
    'products status 200': (r) => r.status === 200,
  }) || errorRate.add(1);
  
  sleep(1);
  
  // 3. Checkout
  const checkoutStart = Date.now();
  const checkoutRes = http.post('https://api.myapp.id/checkout', JSON.stringify({
    items: [
      { product_id: 1, quantity: 2 },
      { product_id: 5, quantity: 1 },
    ],
    payment_method: 'qris',
  }), { headers });
  
  checkoutDuration.add(Date.now() - checkoutStart);
  
  check(checkoutRes, {
    'checkout status 200': (r) => r.status === 200,
    'checkout has order_id': (r) => r.json('order_id') !== undefined,
  }) || errorRate.add(1);
  
  sleep(2);
}

Run:

# Local run
k6 run --vus 50 --duration 30s load-test.js

# Cloud run (k6 Cloud)
k6 cloud load-test.js

# Output ke Prometheus
k6 run --out prometheus=remote_write_url load-test.js

Hasil interpretation:

  • http_req_duration p95 < 500ms → ✅ lulus
  • http_req_failed rate < 0.01 → ✅ lulus
  • Kalau P95 > 1 detik → cek DB query, cache, atau bottleneck network

5. 4 Strategi Migrasi dengan Trade-Off

5.1 Strangler Fig (Recommended untuk 80% kasus)

Cocok untuk: Monolith dengan traffic stabil, mau incremental migration

Kelebihan:

  • Risk rendah — setiap step bisa di-rollback
  • User-facing zero downtime (kalau routing benar)
  • Business value delivered per service
  • Bisa paralel: legacy masih jalan sambil service baru mature

Kekurangan:

  • Butuh dual-maintenance selama transisi
  • Data consistency tricky (dual-write problem)
  • Butuh API gateway / reverse proxy yang reliable
  • Tim harus paham distributed system basics

Effort: Medium-High (8-16 minggu per service) Risk: Medium

5.2 Branch by Abstraction

Cocok untuk: Monolith besar, mau refactor internal tanpa ubah behavior external

Prinsip: Buat abstraction layer di code, implementasi baru di belakang layer, swap implementasi saat ready.

Kelebihan:

  • Zero downtime
  • Testing lebih mudah (implementasi baru bisa di-test parallel)
  • Clear migration path

Kekurangan:

  • Butuh refactor arsitektur yang matang
  • Performance overhead sementara (2 layer)
  • Code lebih kompleks selama transisi

Effort: Medium (4-12 minggu per refactor) Risk: Low-Medium

5.3 Lift & Shift (Quick Win, Bukan Solusi Jangka Panjang)

Cocok untuk: Legacy yang masih jalan, butuh keluar dari on-prem cepat (cost / compliance / disaster risk)

Prinsip: Pindahin VM / container as-is ke cloud, refactor nanti.

Kelebihan:

  • Cepat (2-4 minggu untuk applikasi standar)
  • Cost predictable
  • Risk paling rendah secara teknis
  • Bisa sambil belajar cloud

Kekurangan:

  • Gak ngatasi tech debt
  • Cloud bill bisa lebih mahal kalau gak optimize
  • "Lift & shift + never refactor" = masalah baru 2-3 tahun lagi

Effort: Low (2-4 minggu) Risk: Low

Paling sering dipake sebagai Fase 1.5 sebelum Strangler Fig.

5.4 Big Bang Rewrite (Hindari Kecuali Kasus Khusus)

Cocok untuk: Aplikasi yang sudah end-of-life, user < 100, atau ada business driver besar (akuisisi, pivot total)

Kenapa hindari: 80% rewrite project mati. Data DORA + Standish Group konsisten nunjukin 60-80% rewrite gagal deliver on time / on budget / on scope.

Kapan boleh:

  • Aplikasi existing sudah 100% tidak bisa di-extend (EOL total)
  • User base kecil (< 1.000 MAU)
  • Tim + budget dedicated 18+ bulan
  • Strong executive sponsor yang protect tim dari feature creep

Effort: Very High (12-24 bulan) Risk: Very High

5.5 Detailed Strangler Fig Implementation (API Gateway, Routing, Traffic Shifting)

Step-by-step Strangler Fig dengan API Gateway (Kong / AWS API Gateway / nginx):

# nginx.conf — simple API gateway dengan routing
upstream legacy_app {
    server legacy.internal:8080;
}

upstream new_payment_service {
    server payment.new.internal:3000;
}

# Map untuk routing rules — bisa di-update tanpa restart
map $cookie_use_new_payment $use_new_payment {
    default "legacy";
    "true"   "new";
}

server {
    listen 80;
    server_name api.myapp.id;

    # Health check
    location /health {
        proxy_pass http://legacy_app/health;
    }

    # Payment endpoints — route berdasarkan cookie/header
    location /api/payment/ {
        # Set upstream berdasarkan flag
        proxy_pass http://$use_new_payment/api/payment/;
        
        # Pass headers
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Timeout
        proxy_connect_timeout 5s;
     t 5s;
        proxy_read_timeout 30s;
    }

    # Sisanya — semua ke legacy
    location / {
        proxy_pass http://legacy_app;
    }
}

Untuk traffic shifting gradual, pakai Kong:

# kong.yml — declarative config
_format_version: "3.0"
_transform: true

services:
  - name: legacy-app
    url: http://legacy.internal:8080
    routes:
      - name: legacy-all
        paths:
          - /api
        # Semua request ke legacy by default

  - name: new-payment-service
    url: http://payment.new.internal:3000
    routes:
      - name: new-payment
        paths:
          - /api/payment

plugins:
  # Traffic splitter — 10% ke new, 90% ke legacy
  - name: canary
    route: legacy-all
    config:
      upstream_host: payment.new.internal
      upstream_port: 3000
      upstream_uri: /api/payment
      percentage: 10
      hash: consumer
      hash_header: X-User-ID
      # 10% user (deterministic by user ID) dapat new service

Atau pakai Istio service mesh (kalau di K8s):

# istio-virtualservice.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: payment
  namespace: production
spec:
  hosts:
    - payment-service
  http:
    # 90% traffic ke legacy
    - match:
        - headers:
            cookie:
              regex: ".*use_new_payment=.*"
              # atau pakai user ID
      route:
        - destination:
            host: payment-legacy
            port:
              number: 8080
          weight: 0
        - destination:
            host: payment-new
            port:
              number: 3000
          weight: 100
    
    # Sisanya 100% ke legacy
    - route:
        - destination:
            host: payment-legacy
            port:
              number: 8080
          weight: 100
        - destination:
            host: payment-new
            port:
              number: 3000
          weight: 0

Step Strangler Fig lengkap:

  1. Week 1-2: Setup API gateway (Kong / nginx / Istio) dengan routing rules
  2. Week 3-4: Deploy new service (idle, 0% traffic), observability jalan
  3. Week 5-6: Internal users canary (5% traffic, employee only)
  4. Week 7-8: Beta users (10% traffic, real users opt-in)
  5. Week 9-10: Ramp up (25% → 50% → 75%), monitor error rate + latency
  6. Week 11-12: Full cutover (100% ke new), legacy jadi read-only
  7. Week 13+: Decommission legacy code (3-6 bulan monitoring dulu sebelum hapus)

5.6 Branch by Abstraction Code Example

// Legacy code — direct database access, susah di-test, susah di-swap
class OrderService {
    public function createOrder($data) {
        // Hard dependency ke MySQL
        $this->db->query("INSERT INTO orders ...");
        $this->db->query("INSERT INTO order_items ...");
        $this->db->query("UPDATE inventory SET ...");
        // ... 200 baris kode langsung
    }
}
// Step 1: Define interface
interface OrderRepositoryInterface {
    public function create(Order $order): OrderId;
    public function findById(OrderId $id): ?Order;
    public function update(Order $order): void;
    public function delete(OrderId $id): void;
}
// Step 2: Legacy implementation (wrapper)
class LegacyOrderRepository implements OrderRepositoryInterface {
    private $db;
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function create(Order $order): OrderId {
        // Same legacy code, but behind interface
        $this->db->query("INSERT INTO orders ...");
        return new OrderId($this->db->lastInsertId());
    }
    // ... implement other methods
}
// Step 3: New implementation (PostgreSQL + better schema)
class NewOrderRepository implements OrderRepositoryInterface {
    private $pdo;
    
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }
    
    public function create(Order $order): OrderId {
        $stmt = $this->pdo->prepare("
            INSERT INTO orders (customer_id, total, status, created_at)
            VALUES (:customer_id, :total, :status, NOW())
            RETURNING id
        ");
        $stmt->execute([
            'customer_id' => $order->customerId,
            'total' => $order->total,
            'status' => $order->status->value,
        ]);
        return new OrderId($stmt->fetchColumn());
    }
    // ... cleaner implementation
}
// Step 4: Service pakai interface, not concrete class
class OrderService {
    private OrderRepositoryInterface $repo;
    
    public function __construct(OrderRepositoryInterface $repo) {
        $this->repo = $repo;  // DI inject concrete
    }
    
    public function createOrder($data) {
        $order = Order::fromArray($data);
        return $this->repo->create($order);  // Gak peduli legacy atau new
    }
}
// Step 5: Composition root (config) — swap implementation
// Awal: legacy
$container->bind(OrderRepositoryInterface::class, LegacyOrderRepository::class);

// Setelah 3 bulan parallel run: new
$container->bind(OrderRepositoryInterface::class, NewOrderRepository::class);

// Atau conditional:
if (FeatureFlag::isEnabled('use-new-order-repo')) {
    $container->bind(OrderRepositoryInterface::class, NewOrderRepository::class);
} else {
    $container->bind(OrderRepositoryInterface::class, LegacyOrderRepository::class);
}

Branch by abstraction = refactor internal tanpa ubah behavior. User-facing API tetap sama, implementasi di belakang bisa di-swap kapan saja.


6. Testing Strategy untuk Legacy

Masalah terbesar legacy: gak ada test. Tapi nulis test dari nol untuk 500K LOC = 2-3 tahun.

Test Pyramid untuk Legacy (Practical):

              /\
             /E2E\          ← 5-10 test critical path only
            /------\
           /  API   \       ← 20-50 test untuk endpoint kritis
          /----------\
         / Integration \    ← 30-100 test untuk DB + external service
        /--------------\
       /    Unit Test    \  ← Hanya untuk code yang lo tulis BARU
      /__________________\

Strategi:

  1. Stop the bleeding — Untuk code baru, wajib ada unit test (coverage > 70%)
  2. Characterization test untuk legacy — Tulis test yang capture BEHAVIOR existing, bukan behavior yang seharusnya. Nama test: "WhenUserSubmitsEmptyForm_ShowsError" (existing behavior, bukan ideal)
  3. Integration test untuk critical path — Login, checkout, payment — 3-5 flow utama
  4. E2E smoke test — Selenium / Playwright untuk 3-5 happy path
  5. Performance baseline — K6 / Gatling run sebelum refactor, jadi lo tahu apakah refactor improve atau worsen

Tools untuk legacy Java / .NET: Diffblue Cover (AI-generated unit test, $$$ tapi worth it untuk Java), atau Randoop (free, research-grade).

Tools untuk legacy PHP / Python: Humphrey (AI test generator, GPT-4 based), atau manual characterization test dengan fixture pattern.

6.5 Test Strategy Deep-Dive (Characterization, Mutation, Contract)

1. Characterization Test

Goal: Capture existing behavior (bahkan yang buggy), jadi refactor gak break apa yang udah jalan.

# Legacy: gak ada spec, gak ada test
# Tapi production jalan dan user pakai

# Characterization test approach:
# 1. Run legacy code di test env
# 2. Observe output
# 3. Write test yang EXPECT output itu (even if weird)

def test_legacy_checkout_with_empty_cart():
    """CHARACTERIZATION: Capture actual legacy behavior."""
    result = legacy_checkout(cart=[])
    
    # Mungkin legacy return error message yang aneh
    # Tapi INI yang production user terima
    assert result.status_code == 200
    assert "checkout initiated" in result.body.lower()
    # Mungkin return order_id=null juga, gak apa-apa
    assert result.json.get("order_id") is None

2. Mutation Testing

Goal: Verify test quality — apakah test beneran ngecek logic, atau cuma ngecek return value?

# Install mutmut (Python) atau PIT (Java)
pip install mutmut

# Run mutation testing
mutmut run --target-dir=./src --tests-dir=./tests

Cara kerja: tool modify code (e.g., ubah > jadi <=, hapus baris), lalu run test. Kalau test masih PASS = test gak ngecek logic itu. Kalau test FAIL = test ngecek logic dengan benar.

Mutation score interpretation:

  • < 50% = test coverage meaningless, banyak gap
  • 50-70% = decent tapi perlu improvement
  • 70-85% = good
  • 85% = excellent

Tools 2026:

  • PIT (Java) — paling mature
  • mutmut (Python)
  • Stryker (JS/TS)
  • infection (PHP)

3. Contract Testing dengan Pact

Goal: Verify API contract antara consumer dan provider tanpa integration test.

# Consumer side (OrderService)
from pact import Consumer, Like, EachLike

pact = Consumer('OrderService').has_pact_with(Provider('PaymentService'))

def test_payment_contract():
    expected = {
        'transaction_id': Like('txn-12345'),
        'status': Like('success'),
        'amount': Like(100000),
    }
    
    (pact
     .given('payment exists')
     .upon_receiving('a charge request')
     .with_request('POST', '/api/payment/charge', body=...)
     .will_respond_with(200, body=expected))
    
    # Run consumer code
    result = order_service.checkout(cart, user)
    
    pact.verify()

Provider side (PaymentService) — verify it matches contract:

# Run after deploy
pact-verifier --provider-base-url=http://payment-service:3000 \
              --pact-url=./pacts/orderservice-paymentservice.json

ROI contract testing: 70% reduction in integration test runtime, plus catching breaking change sebelum deploy.


7. Database Modernization

DB biasanya bagian paling risky. Salah handle = data loss.

7.1 Schema Refactor

Online schema migration (zero-downtime):

-- Contoh: tambah kolom baru tanpa lock table
-- (PostgreSQL 11+)
ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT false;

-- Untuk MySQL, pakai pt-online-schema-change
pt-online-schema-change \
  --alter "ADD COLUMN email_verified TINYINT(1) DEFAULT 0" \
  --execute D=safelist,t=users \
  h=localhost,u=root

Tools:

  • pgroll (PostgreSQL) — versioned schema migration
  • Flyway / Liquibase — schema version control
  • gh-ost (MySQL) — GitHub's online schema migration
  • Skeema (MySQL) — declarative schema

7.2 Database Engine Migration

MySQL → PostgreSQL (paling umum 2026):

Tools:

  • pgloader (free) — automated migration MySQL → PostgreSQL
  • AWS DMS (Database Migration Service) — managed, heterogen
  • Google Database Migration Service — same, GCP
  • Azure Database Migration Service — same, Azure

Strategi: Dual-write period 2-4 minggu, validate, cutover read, cutover write, decommission MySQL.

7.3 Sharding & Read Replica

Kapan perlu:

  • DB size > 100 GB single instance
  • Query load > 5.000 QPS
  • Write load > 1.000 QPS
  • Latency P95 > 200ms

Tools 2026:

  • Vitess (YouTube open-source) — MySQL sharding
  • Citus (PostgreSQL extension) — distributed PostgreSQL
  • PlanetScale (managed Vitess, $$$) — fully automated
  • CockroachDB (distributed SQL, PostgreSQL-compatible) — global consistency
  • YugabyteDB (open-source distributed SQL) — PostgreSQL-compatible
  • TiDB (MySQL-compatible distributed) — HTAP workload

Untuk 90% startup ID dengan traffic < 1.000 QPS, single PostgreSQL + read replica cukup. Sharding overkill.

7.4 Database Decomposition Strategy

Saat extract service, DB juga harus di-decompose. Bukan cuma kode.

Pattern: Database per Service

[Service A] → [DB A] (owned by Service A)
[Service B] → [DB B] (owned by Service B)
[Service C] → [DB C] (owned by Service C)

Cara decompose DB tanpa downtime:

  1. Identify shared tables — tabel apa yang diakses > 1 service
  2. Add new schema di service baru — copy schema, forward-migrate data
  3. Dual-write — service A + B sama-sama write ke shared table (CDC sync)
  4. Validate — compare row counts + sample data 2-4 minggu
  5. Cutover read — service yang lain read dari new schema
  6. Cutover write — stop write ke shared table
  7. Archive shared table — keep 3-6 bulan untuk rollback, then drop

Contoh decompose users table:

SEBELUM:
[orders] [users (shared)] [payments] [notifications]

SESUDAH:
[orders] → [user_orders] (subset of users, owned by orders service)
[users] → [user_core] (owned by user service)
[payments] → [user_payment_methods] (subset, owned by payments service)

Tools 2026:

  • Stitch (Singer / Meltano) — open source data pipeline untuk ETL
  • Debezium + Kafka — CDC real-time
  • AWS DMS — managed replication
  • Custom code — kalau volume kecil

7.5 Database Sharding Implementation (Vitess + Citus)

Vitess (MySQL Sharding) — Real Config

# vitess-config.yaml — top-level config
topo:
  backend: etcd2
  etcd2:
    addrs:
      - "etcd-global:2379"
    
keyspaces:
  - name: commerce
    shards:
      - name: "-80"
        mysql:
          host: commerce-00-80.internal
          port: 3306
      
      - name: "80-"
        mysql:
          host: commerce-00-80minus.internal
          port: 3306
    
    rdbs:
      - host: commerce-xtradb.internal
        port: 3306
    
    schema:
      - "CREATE TABLE orders (...)"
      - "CREATE TABLE order_items (...)"
    
    vindexes:
      - name: hash_vdx
        type: hash
    
    tables:
      - name: orders
        column_list:
          - id
          - user_id
          - total
          - status
          - created_at
        pinned:
          - user_id
        auto_increment:
          column: id
          sequence: 0

Sharding strategy:

  • Range sharding by user_id hash → even distribution
  • Shard -80 untuk hash 0-80
  • Shard 80- untuk hash 80-255

Routing application:

# Connection via vtgate (Vitess proxy)
from vitess_client import VitessClient

client = VitessClient(
    vtgate_addr='vtgate-production:15333',
    keyspace='commerce',
)

# Query otomatis di-route ke shard yang tepat
orders = client.execute(
    "SELECT * FROM orders WHERE user_id = %s",
    user_id=12345
)
# Vitess otomatis route ke shard -80 atau 80- berdasarkan hash

Citus (PostgreSQL Sharding) — Real Config

-- Setup Citus extension
CREATE EXTENSION citus;

-- Add worker nodes
SELECT citus_add_node('citus-worker-1.internal', 5432);
SELECT citus_add_node('citus-worker-2.internal', 5432);
SELECT citus_add_node('citus-worker-3.internal', 5432);

-- Distribute table by user_id (hash sharding)
SELECT create_distributed_table('orders', 'user_id');

-- Shard count
SELECT citus_set_shard_count('orders', 32);
-- 32 shards = 8-10 per worker node

-- Query otomatis parallel
SELECT user_id, COUNT(*), SUM(total)
FROM orders
WHERE created_at > '2026-01-01'
GROUP BY user_id
ORDER BY 3 DESC
LIMIT 100;
-- Citus distribusi query ke semua worker, aggregate di coordinator

Sharding trade-off comparison:

Aspect Vitess (MySQL) Citus (PostgreSQL) CockroachDB YugabyteDB
Protocol MySQL PostgreSQL PostgreSQL PostgreSQL
Sharding App-level (vtgate) Extension (transparent) Built-in Built-in
Joins across shards Manual Supported Supported Supported
Migration effort Medium Low High (full re-platform) High
Operational complexity High Medium Low (managed) Medium
Cost Free (open source) Free extension $$$ (managed) Free / managed
Best for MySQL users, large MySQL infra PostgreSQL users, medium scale New project, global Compliance-heavy

Rekomendasi Indonesia 2026:

  • Traffic < 5K QPS: Single PostgreSQL + read replica ($50-200/bulan)
  • Traffic 5K-50K QPS: Citus extension (free, 1-2 hari setup)
  • Traffic > 50K QPS: CockroachDB / Vitess (managed = $$$)
  • Multi-region: CockroachDB / YugabyteDB (built-in geo-partitioning)

7.6 Backup + RTO/RPO Strategy

RTO (Recovery Time Objective) = max downtime yang boleh (e.g., 1 jam = service harus pulih dalam 1 jam) RPO (Recovery Point Objective) = max data loss yang boleh (e.g., 5 menit = backup setiap 5 menit)

Tier RTO RPO Cost implication Cocok untuk
Tier 1 (Critical) < 15 menit < 1 menit High (multi-region active-active) Banking, payment gateway
Tier 2 (Important) < 1 jam < 15 menit Medium (single region + cross-AZ + daily backup) E-commerce, SaaS
Tier 3 (Standard) < 4 jam < 1 jam Low (single region + daily backup) Internal tool, blog

Backup strategy pattern:

# PostgreSQL — point-in-time recovery setup
# 1. Enable WAL archiving
archive_mode = on
archive_command = 'test ! -f /var/lib/postgresql/wal_archive/%f && cp %p /var/lib/postgresql/wal_archive/%f'

# 2. Daily full backup
0 2 * * * pg_basebackup -D /backup/daily/$(date +\%Y\%m\%d) -Ft -z -Xs -P

# 3. Verify backup (monthly restore test)
0 4 1 * * pg_restore -d postgres_restore_test /backup/daily/latest.tar.gz && \
  /scripts/verify_restore.sh

# 4. Retain backup 30 days
find /backup/daily -mtime +30 -delete

# 5. Off-site backup (S3)
0 3 * * * aws s3 sync /backup/daily/latest/ s3://myapp-backup-prod/daily/ --storage-class STANDARD_IA

Disaster recovery drill — quarterly:

# scripts/dr-drill.sh
1. Provision DR environment (Terraform / Pulumi)
2. Restore latest backup ke DR region
3. Replay WAL sampai "5 menit sebelum sekarang"
4. Update DNS / load balancer ke DR endpoint
5. Run smoke test dari external monitoring
6. Measure:
   - RTO actual: berapa lama dari failover sampai ready
   - RPO actual: berapa menit data loss
7. Document finding + improvement
8. Rollback ke primary (kalau drill)

Off-site backup (3-2-1 rule):

  • 3 copies of data
  • 2 different media (local SSD + S3)
  • 1 off-site (different region / different cloud)

Cost estimation ID 2026:

  • S3 Standard: $0.023/GB/bulan = Rp 360/GB
  • 100GB DB + 30 hari retention = ~Rp 1.1 juta/bulan
  • Cross-region replication: 2× = ~Rp 2.2 juta/bulan

8. Security Modernization (Mandatory 2026: UU PDP)

UU PDP (Undang-Undang Pel Perlindungan Data Pribadi, 2022) enforcement dimulai 2026. Kalau lo handle data pribadi (email, nama, no HP, alamat) dan terjadi breach, denda sampe Rp 5 miliar atau 5% revenue.

Security modernization checklist (4-8 minggu):

Area Minimum requirement Tools
Data encryption at rest AES-256 untuk DB, encrypted volume untuk storage Cloud KMS, Vault
Data encryption in transit TLS 1.3 minimum, no HTTP, HSTS enabled Let's Encrypt + nginx/apache config
Authentication MFA untuk admin, strong password policy, no default password Auth0, Keycloak, Clerk, Supabase Auth
Authorization RBAC (role-based), least privilege principle Casbin, OPA, custom
Audit logging Log semua akses data pribadi, retention 1+ tahun ELK, Splunk, Loki
Vulnerability scanning Weekly dependency scan, monthly DAST Snyk, Dependabot, OWASP ZAP
Penetration testing Annual by external party HackerOne, Cobalt, lokal ID: ITSEC, Xynex
Incident response plan Documented + tested 1×/year Custom, atau pakai template NIST
Backup encryption Backup file encrypted at rest Cloud KMS, age, GPG
Secrets management No credentials di code / env file, pakai vault HashiCorp Vault, AWS Secrets Manager
Dependency update Auto-update patch version, monthly review minor Dependabot, Renovate
DPA (Data Processing Agreement) Contract dengan vendor yang handle data pribadi Legal review
Privacy policy + cookie banner Compliant UU PDP, explicit consent Custom / OneTrust / Cookiebot

Quick wins (1-2 minggu):

  1. Enable HTTPS everywhere (Let's Encrypt free)
  2. Rotate semua default password
  3. Enable MFA di semua admin panel
  4. Setup Snyk / Dependabot untuk auto CVE alert
  5. Document data flow + consent mechanism

8.5 ISO 27001 + PCI-DSS + OJK Compliance untuk Modernisasi

Modernisasi sering harus compliance dengan multiple regulation sekaligus.

ISO 27001 (Information Security Management)

Apa: Standar internasional untuk ISMS (Information Security Management System). 93 control areas.

Relevansi untuk modernisasi:

  • Wajib untuk SaaS B2B yang serve enterprise customer
  • Wajib untuk fintech yang mau integrate dengan bank
  • Pre-requisite untuk dapat ISO 27001 certified

Effort implementasi: 6-12 bulan Biaya audit eksternal: Rp 75-150 juta (1 site, 1 tahun surveillance)

Cara ISO 27001 relate ke modernisasi:

ISO 27001 Control Modernisasi Activity
A.12.1.2 Change management CI/CD pipeline dengan approval
A.14.2.1 Secure development SonarQube + Snyk di pipeline
A.17.1.2 Continuity Backup + DR tested quarterly
A.18.1.3 Records protection Audit log retention 1+ tahun
A.12.4.1 Event logging Prometheus + Loki aggregation

PCI-DSS 4.0 (Payment Card Industry)

Apa: Standard untuk yang handle credit card data. 12 requirements utama.

Relevansi untuk modernisasi:

  • Wajib untuk e-commerce / payment gateway
  • Tiap service yang touch card data harus comply
  • Strangler Fig extraction HARUS confirm: "service baru handle card data atau tidak?"

Effort: 3-6 bulan (kalau dari nol) Biaya audit (QSA — Qualified Security Assessor): Rp 200-500 juta (depends on transaction volume)

4 level PCI-DSS:

Level Transaction volume Validation
Level 1 > 6 juta transaksi/tahun Annual on-site audit by QSA
Level 2 1-6 juta transaksi/tahun Annual self-assessment + quarterly network scan
Level 3 20K-1 juta transaksi/tahun Annual self-assessment + quarterly network scan
Level 4 < 20K transaksi/tahun Annual self-assessment (recommended)

OJK Compliance (Indonesia Financial Services Authority)

Relevan untuk: Bank, fintech, payment gateway, peer-to-peer lending, e-money

Regulasi utama:

  • POJK No. 11/POJK.03/2022 — Penyelenggaraan Teknologi Informasi oleh Bank Umum
  • POJK No. 12/POJK.03/2022 — Penerapan Manajemen Risiko Dalam Penggunaan Teknologi Informasi Oleh Bank Umum
  • POJK No. 77/POJK.01/2016 — Layanan Pinjam Meminjam Uang Berbasis Teknologi Informasi (fintech)
  • SEOJK No. 16/SEOJK.05/2023 — Inovasi Keuangan Digital

Modernisasi + OJK:

  • Data localization — data financial harus di Indonesia. AWS Jakarta region (ap-southeast-3) atau Biznet Gio atau IDCloudHost
  • BCP (Business Continuity Plan) — annual DR drill, RTO < 4 jam, RPO < 1 jam
  • Vendor assessment — cloud provider harus comply dengan OJK regulation
  • Audit trail — semua transaction log immutable, retain 5+ tahun
  • Penetration testing — annual + setelah major change

Effort OJK compliance untuk modernisasi: 3-6 bulan (parallel dengan security modernization) Biaya konsultan OJK compliance: Rp 200-500 juta untuk 1 project


9. 30/60/90 Day Roadmap Template

Hari 1-30: Audit + Stabilization

  • Week 1-2: Tech audit (SonarQube + Snyk + manual architecture review)
  • Week 3: Quick wins (CVE fix, monitoring, backup verify)
  • Week 4: Stakeholder alignment + roadmap finalization

Deliverable: Tech debt report + business case for next 6-12 bulan

Hari 31-60: CI/CD + First Service Extraction

  • Week 5-6: CI/CD pipeline (GitHub Actions / GitLab CI)
  • Week 7-8: Pilih 1 bounded context, mulai Strangler Fig setup

Deliverable: Production deploy via CI/CD + 1 service baru running parallel

Hari 61-90: Stabilize + Plan

  • Week 9-10: Service baru di cutover 50% traffic, monitor
  • Week 11: Roll back atau lanjut cutover 100%
  • Week 12: Planning next quarter (Phase 3 modernization scope)

Deliverable: 1 service fully migrated + plan Q3

9.5 Cost Breakdown Modernisasi Indonesia (Realistic)

Fase 0: Audit + Quick Wins (2-4 minggu)

Item Cost Notes
SonarQube Community + Snyk free Rp 0 Self-host
Dependabot + UptimeRobot Rp 0 GitHub + free tier
Senior dev 50% × 4 minggu Rp 15-25 juta Freelance Rp 50-100jt/bulan
Architecture diagram (tools) Rp 1-2 juta Draw.io + Miro
Fase 0 Total Rp 16-27 juta

Fase 1: Stabilization (4-8 minggu)

Item Cost Notes
Backend dev × 6 minggu Rp 30-45 juta Mid-level ID rate
SRE / DevOps × 6 minggu Rp 35-50 juta Specialized
CI/CD (GitHub Actions) Rp 0-5 juta/bulan Free tier → paid
Monitoring (Prometheus + Grafana self-host) Rp 3-5 juta/bulan 1 server
Datadog (managed) Rp 50-100 juta/bulan 3-5 host
Fase 1 Total Rp 68-105 juta (one-time) + Rp 3-100 juta/bulan (recurring)

Fase 2: Decoupling — Strangler Fig (8-16 minggu per service)

Item Cost Notes
2-3 dev × 4-6 minggu per service Rp 60-180 juta Senior ID rate
Kong / nginx / API Gateway Rp 0 (self-host) — Rp 50 juta (managed) AWS API Gateway $3.50/M requests
New service infra (cloud) Rp 5-30 juta/bulan Per service
Feature flag (GrowthBook self-host) Rp 0 Free
Fase 2 per service Total Rp 65-260 juta + Rp 5-30 juta/bulan

Fase 3: Modernization (12-24 minggu)

Item Cost Notes
2-4 dev × 6-12 bulan Rp 360-960 juta Full team
Database migration tools (DMS, pgloader) Rp 5-50 juta Pay per use
Cloud migration (AWS, GCP, Azure) Rp 30-100 juta Initial setup + consulting
Containerization (K8s) Rp 50-150 juta Setup + training
Fase 3 Total Rp 445-1.26 miliar (one-time) + Rp 30-200 juta/bulan (recurring)

Total Modernization Project (1 tahun, 3 service extracted):

  • One-time: Rp 594-1.65 miliar
  • Recurring: Rp 38-330 juta/bulan
  • Team cost dominates (70-80% of total)

ROI calculation example (e-commerce 200K MAU):

  • Investment Year 1: Rp 1.8 miliar (1.5 miliar one-time + 300 juta recurring × 1 tahun)
  • Saving Year 1: Rp 600 juta (incident reduction + cloud cost optimization + faster onboarding)
  • Revenue gain Year 1: Rp 1.2 miliar (faster feature delivery = +20% conversion)
  • Net benefit Year 1: Rp 1.8 miliar - Rp 1.8 miliar investment = BREAK EVEN
  • Net benefit Year 2: Rp 1.8 miliar saving + revenue gain = Rp 1.8 miliar profit
  • ROI Year 1: 0%, Year 2: 100% on cumulative, Year 3: 200%+

Catatan: ROI baru terasa di Year 2. Modernisasi = investment, bukan cost.

9.6 4 Additional Indonesian Case Studies

Case 1: Banking — PT Bank XYZ (Anonim)

Profil: Bank swasta nasional, 50 tahun jalan, core banking system mainframe (AS/400), 200+ branch, 5 juta nasabah, Rp 50 triliun AUM.

Problem:

  • Mainframe EOL 2027, vendor support berakhir
  • Deploy new feature butuh 3-6 bulan (vendor change request)
  • Online banking mobile app API lambat, latency P95 5+ detik
  • Compliance BI + OJK + UU PDP mandatory 2026

Approach (36 bulan, 2024-2027):

  • Bulan 1-6: Assessment + vendor selection (TCS, Accenture, local firm)
  • Bulan 7-12: Core banking migration ke microservice (Java + Spring Boot + PostgreSQL)
  • Bulan 13-24: Strangler Fig parallel run — mainframe + new core
  • Bulan 25-30: Cutover bertahap per branch
  • Bulan 31-36: Mainframe decommission, full production on new core

Hasil (mid-2026 checkpoint):

  • 40% transaction migrated, mainframe masih handle 60%
  • API latency turun 60% (5 detik → 2 detik untuk transaksi yang udah migrasi)
  • Deploy frequency naik dari 1×/bulan ke 1×/minggu (untuk service baru)
  • BI compliance status: green (data localization, audit trail, BCP tested)

Biaya: Rp 150 miliar (36 bulan) — bank-grade project, gak comparable dengan startup

Lesson: Mainframe migration itu marathon, bukan sprint. Strangler Fig parallel run wajib karena gak boleh ada downtime untuk core banking.

Case 2: Fintech — PT Fintech ABC (Anonim)

Profil: P2P lending, 3 tahun jalan, monolith Python 2.7 + Django 1.10, 100K lender, 500K borrower, 50 dev.

Problem:

  • Python 2 EOL January 2020, masih production di 2024
  • OJK audit 2025 fail — gak ada data localization, audit trail incomplete
  • Scale issue: 10K borrower active, server crash 3×/bulan
  • Lender churn naik 30% karena app lambat

Approach (18 bulan, 2024-2026):

  • Bulan 1-3: Audit + emergency fix (Python 2.7 → 3.12 upgrade dalam 3 bulan)
  • Bulan 4-6: Containerize (Docker + K8s), migrate ke AWS Jakarta region
  • Bulan 7-12: Strangler Fig extract "loan origination" service (paling sakit, paling sering berubah) ke Go + PostgreSQL
  • Bulan 13-18: Extract "repayment" service, implementasi CDC untuk audit trail

Hasil:

  • Latency P95: 3 detik → 400ms
  • Server crash: 3×/bulan → 0 dalam 6 bulan terakhir
  • OJK audit 2026: PASS dengan minor finding
  • Lender churn: turun dari 30% YoY ke 12% YoY
  • Deploy frequency: 1×/bulan → 15×/bulan
  • Incident rate: turun 75%

Biaya: Rp 8 miliar (18 bulan, 50 dev sebagian allocated)

Lesson: Python 2 → 3 itu mandatory, gak bisa delay. OJK compliance driven by data localization + audit trail, bukan sekadar security.

Case 3: Edutech — PT Edutech DEF (Anonim)

Profil: Online learning platform, 5 tahun jalan, monolith PHP 5.6 + CodeIgniter 3, 1 juta student, 50K course enrollment/bulan, 15 dev.

Problem:

  • PHP 5 EOL, security patch udah gak ada sejak 2019
  • User complain video buffering (CDN miss-config, monolith handle video metadata)
  • Test coverage 5%, takut refactor
  • Competition naik (Quipper, Ruangguru), butuh faster iteration

Approach (12 bulan, 2025-2026):

  • Bulan 1-3: Audit + CI/CD setup (GitHub Actions + SonarQube)
  • Bulan 4-6: PHP 7.4 → 8.2 + CodeIgniter 3 → 4 upgrade
  • Bulan 7-9: Extract "video streaming metadata" ke microservice (Node.js + Redis cache + CDN integration)
  • Bulan 10-12: Extract "course progress tracking" ke microservice (Go + PostgreSQL)

Hasil:

  • Video load time: 4 detik → 800ms (CDN + cache optimization + metadata offload)
  • Deploy frequency: 1×/bulan → 20×/bulan
  • Page load time: turun 60%
  • User retention: naik 25% (faster = happier)
  • Course completion rate: naik 15%

Biaya: Rp 3.2 miliar (12 bulan, 15 dev + 1 freelance senior 6 bulan)

Lesson: Edutech user-base besar tapi revenue tipis. Modernisasi fokus ke user-facing performance dulu (video loading), bukan arsitektur elegance.

Case 4: Government — Kementerian GHI (Anonim)

Profil: Lembaga pemerintah, 10 tahun jalan, monolith Java 7 + Spring 3, 200+ internal user, 10 juta external user (layanan publik).

Problem:

  • Java 7 EOL, security critical
  • Hosting on-prem, server udah 8 tahun, spare part susah
  • UU PDP mandatory + Kominfo PSE compliance
  • Vendor lock-in: 1 vendor handle semua, kontrak Rp 5 miliar/tahun

Approach (24 bulan, 2024-2026):

  • Bulan 1-6: Audit + tender vendor baru (lolos seleksi 5 vendor, pilih 2)
  • Bulan 7-12: Java 7 → 21 LTS upgrade + Spring Boot 3 migration
  • Bulan 13-18: Containerize + migrate ke cloud (Tencent Cloud Indonesia region, compliance requirement)
  • Bulan 19-24: Strangler Fig extract "layanan publik" (external) dari internal monolith

Hasil:

  • Java EOL issue resolved, security baseline restored
  • Hosting cost: turun 40% (cloud vs on-prem maintenance)
  • Vendor diversity: dari 1 vendor ke 2 vendor (less lock-in)
  • UU PDP + Kominfo PSE compliance: PASSED
  • Service availability: dari 95% ke 99.5%

Biaya: Rp 12 miliar (24 bulan, multi-vendor)

Lesson: Government modernisasi = lebih banyak compliance constraint, slower velocity, tapi mandatory. Cloud preference untuk compliance: Biznet Gio / IDCloudHost / Tencent Cloud (semua punya sertifikat compliance Indonesia).


10. 10 Jebakan yang Bikin Modernisasi Gagal

Jebakan #1: Big Bang Rewrite

Udah gue sebut. 80% mati. JANGAN.

Jebakan #2: "Kita Akan Refactor Setelah Selesai"

Selesai = gak pernah. Bikin refactor sebagai bagian dari Definition of Done untuk setiap feature baru.

Jebakan #3: Modernisasi Tanpa Business Case

Tim technical happy, business gak lihat value = cancel. Setiap fase harus ada measurable business outcome (deploy freq naik 3×, incident turun 50%, cost turun 20%, dsb).

Jebakan #4: Underestimate Data Migration Effort

Schema refactor 30% effort, data migration 50%, validation & rollback 20%. Yang sering di-underestimate: data validation + business logic consistency.

Jebakan #5: No Rollback Plan

Setiap refactor harus punya rollback. Kalau gak bisa rollback dalam 5 menit = jangan proceed.

Jebakan #6: Skip Documentation

Legacy tanpa doc = legacy baru. Setiap refactor harus update architecture diagram + runbook.

Jebakan #7: Single Champion

Kalau cuma 1 orang yang pahami strategi = bus factor 1. Minimal 2-3 orang harus pahami arsitektur + bisa swap.

Jebakan #8: Business As Usual Selama Modernisasi

Feature freeze selama refactor major = business complain. Solusi: 80% resource untuk modernisasi, 20% untuk maintenance + critical feature. Communicate explicit ke stakeholder.

Jebakan #9: Pilih Tech Stack Terlalu Early

"Rewrite pakai Go!" tanpa benchmark = death. Bikin PoC 2-3 stack, ukur velocity + maintainability + hire-ability. Pilih setelah data, bukan hip.

Jebakan #10: Skip Security Audit Selama Refactor

Sering: fokus ke arsitektur, security jadi afterthought. Combine: setiap refactor sekaligus fix security gap yang ke-expose. Security + modernization bukan 2 project terpisah.

Jebakan #11: Coupling Service Baru ke Legacy

Service baru yang masih query langsung ke legacy DB = anti-corruption layer GAGAL. Pasti ada ACL.

Jebakan #12: No Feature Flag untuk Cutover

"Deploy 100% langsung" tanpa gradual = kalau ada bug = incident besar. Pakai feature flag atau canary deployment, ALWAYS.

Jebakan #13: Migration Anti-Patterns dengan Code

❌ Anti-pattern 1: God Service

# JANGAN — service baru yang masih "god service" / "distributed monolith"
class NewOrderService:
    def create_order(self, data):
        # 1. Validate user (call legacy user service)
        user = legacy_user_service.get(data['user_id'])
        
        # 2. Check inventory (call legacy inventory)
        inventory = legacy_inventory.check(data['items'])
        
        # 3. Process payment (call legacy payment)
        payment = legacy_payment.charge(data['total'])
        
        # 4. Send notification (call legacy notification)
        legacy_notification.send(data['user_id'], 'order_created')
        
        # 5. Update analytics (call legacy analytics)
        legacy_analytics.track('order_created', data)
        
        # 6. Create shipment
        # ... 50 method calls lagi
# ✅ BENER — service fokus 1 bounded context, event-driven untuk cross-service
class NewOrderService:
    def create_order(self, data):
        # Validate input
        order = Order.create(data)
        self.db.save(order)
        
        # Publish event, biar service lain yang react
        self.event_bus.publish('order.created', {
            'order_id': order.id,
            'user_id': order.user_id,
            'total': order.total,
            'items': order.items,
            'created_at': order.created_at,
        })
        # Tanggung jawab SELESAI di sini
        return order

❌ Anti-pattern 2: Synchronous Chain

# JANGAN — sync call chain, kalau 1 service down = semua down
def checkout(cart, user):
    user = user_service.get(user.id)              # 1
    inventory = inventory_service.reserve(cart)   # 2
    payment = payment_service.charge(cart.total)  # 3
    shipment = shipping_service.create(...)       # 4
    notification = notification_service.send(...) # 5
    return shipment

# Kalau step 3 (payment) timeout 30 detik, user nunggu 30 detik
# Kalau step 4 (shipping) down, payment udah di-charge tapi gak ada shipment
# ✅ BENER — async event-driven, eventual consistency
def checkout(cart, user):
    order = Order.create(cart, user)
    self.db.save(order)
    
    # Publish 1 event, multiple service subscribe
    self.event_bus.publish('order.created', order.to_dict())
    
    # Payment di-handle oleh payment service yang subscribe event
    # Shipping di-handle oleh shipping service
    # Notification di-handle oleh notification service
    return order

# Return ke user dalam < 1 detik
# Payment, shipping, notification diproses async di background
# Kalau payment service down, order tetap tersimpan, payment diproses saat service up

❌ Anti-pattern 3: Distributed Monolith

# JANGAN — service "terpisah" tapi tetep tightly coupled via sync call
# Service A → Service B → Service C → Service D (sync chain)
# Kalau B deploy = A & C & D semua kena impact

Cara detect distributed monolith:

  • Deploy 1 service = require deploy service lain di jam yang sama
  • Database shared antar service
  • Synchronous call chain > 3 services
  • Failure cascade (1 service down = semua down)

Fix: eventual consistency + event-driven + service autonomy (own DB, own deploy schedule).


11. Kapan Hire Consultant vs In-house

Situasi Recommendation Alasan
Audit pertama kali, gak ada senior Hire consultant 1-2 bulan External perspective, gak ada bias internal
Sudah ada senior, butuh validation In-house audit + external review Hemat 50% biaya
Microservices extraction, belum pernah Hire consultant 1 service Belajar pattern, lalu internal jalan
Security audit (UU PDP compliance) External mandatory Conflict of interest kalau internal
Performance tuning In-house first, escalate kalau mentok Performance issues biasanya domain-specific
Cloud migration Cloud architect freelance 4-8 minggu AWS / GCP certified architect, fixed scope
Big rewrite JANGAN tanpa co-pilot external Rewrite butuh validation external bahwa scope realistis

Biaya konsultan Indonesia 2026:

  • Senior freelance: Rp 50-100 juta/bulan
  • Firma konsultan (McKinsey, BCG, Accenture): Rp 500 juta - 2 miliar per project
  • Spesialis (security, performance): Rp 75-150 juta/bulan

Untuk startup ID budget < Rp 500 juta/tahun, fokus ke in-house + 1 senior freelance 2-3 bulan untuk audit + roadmap.

11.5 Cloud Provider Indonesia Comparison 2026

Buat modernisasi, cloud choice krusial — terutama untuk compliance.

Provider Region ID Data residency Pricing vs AWS UU PDP / OJK compliance Best for
AWS Jakarta ap-southeast-3 (2022) ✅ ID-based Baseline (1×) ✅ Compliant Enterprise, fintech, banking
GCP Jakarta asia-southeast2 (2020) ✅ ID-based +5-10% ✅ Compliant Data analytics, AI/ML
Azure Indonesia (coming 2025-2026) ✅ via partner +10-15% ✅ Compliant Microsoft-heavy stack
Biznet Gio NEO Cloud (multiple ID) ✅ ID-only -20-30% vs AWS ✅ Compliant + PII friendly Startup, government
IDCloudHost Cloudku (3 ID region) ✅ ID-only -30-40% vs AWS ✅ Compliant Budget-conscious, on-prem alt
Telkom Cloud TelkomCloud (3 region) ✅ ID-only -25-35% vs AWS ✅ Compliant BUMN / government, telco
Tencent Cloud ap-jakarta (2023) ✅ ID-based -15-25% vs AWS ✅ Compliant (via partner) China connection, gaming
Alibaba Cloud ap-southeast-6 (2024) ✅ ID-based -20-30% vs AWS ✅ Compliant E-commerce, retail

Rekomendasi per use case (Indonesia 2026):

Use case Recommendation Alasan
Fintech / payment gateway AWS Jakarta atau Biznet Gio Compliance certified, latency rendah, ecosystem mature
E-commerce AWS Jakarta + Cloudflare CDN Reliability, global CDN, payment integration
SaaS B2B AWS atau GCP Enterprise trust, ecosystem integrasi
Startup budget < Rp 50 juta/bulan IDCloudHost atau Biznet Gio Cost 30-40% lebih murah, support ID
Government / BUMN Telkom Cloud atau Biznet Gio Compliance, lokal ownership, support Bahasa
Gaming / video streaming Tencent Cloud atau AWS Network optimization, low latency
AI / ML heavy GCP atau AWS (SageMaker) GPU availability, TPU (GCP)
Legacy on-prem migration AWS Migration Hub + partner Mature migration tooling

Cost comparison example (4 vCPU 16GB RAM, 1 tahun, pay-as-you-go):

  • AWS EC2 m6i.xlarge: $123/bulan × 12 = $1,476/tahun = Rp 23.6 juta
  • Biznet Gio c4.xlarge.2: Rp 1.1 juta/bulan × 12 = Rp 13.2 juta (44% cheaper)
  • IDCloudHost: Rp 0.95 juta/bulan × 12 = Rp 11.4 juta (52% cheaper)
  • Telkom Cloud: Rp 1.2 juta/bulan × 12 = Rp 14.4 juta (39% cheaper)

Network latency Jakarta-Singapore: 18ms Network latency Jakarta-Jakarta (same region): 1-3ms Network latency Jakarta-Surabaya (intra-ID): 15-25ms (tergantung ISP)

Compliance considerations:

  • UU PDP — semua provider di atas certified
  • OJK — AWS, Biznet Gio, IDCloudHost, Telkom punya sertifikat
  • Kominfo PSE — semua bisa didaftarkan
  • BI data residency — wajib local cloud (gak bisa AWS Singapore, harus AWS Jakarta atau lokal)

12. Real Case Study (Anonymized)

Profil: E-commerce B2C, 8 tahun jalan, PHP 5.6 monolith 300K LOC, MySQL 5.6, 1 dev original masih di tim, 3 dev baru (rata-rata 1.5 tahun exp), 200.000 MAU, revenue Rp 2 miliar/tahun.

Problem:

  • Deploy 1× sebulan, downtime 4 jam per deploy
  • 8 CVE critical unpatched
  • MTTR 6 jam
  • Lead time 3 minggu
  • Single dev yang ngerti payment flow

Approach (18 bulan):

  • Bulan 1-2: Audit + quick wins (CVE fix, monitoring, CI/CD)
  • Bulan 3-6: Strangler Fig untuk "Payment" service (extract ke Go + PostgreSQL)
  • Bulan 7-10: Strangler Fig untuk "Notification" service (extract ke Node.js + Redis)
  • Bulan 11-14: PHP 5.6 → 8.2 upgrade, framework Yii 1 → Laravel 10
  • Bulan 15-18: MySQL 5.6 → PostgreSQL 15 migration, schema refactor

Hasil:

  • Deploy frequency: 1×/bulan → 8×/bulan
  • MTTR: 6 jam → 45 menit
  • Lead time: 3 minggu → 4 hari
  • Security: 8 CVE critical → 0 high/critical
  • Infrastructure cost: turun 35% (containerize + right-sizing)
  • Developer onboarding: 3 bulan → 6 minggu
  • Revenue impact: +20% (faster feature delivery)

Biaya total: Rp 1.8 miliar (3 dev × 18 bulan + 1 freelance senior 6 bulan + cloud + tools)

ROI: 4 bulan (dari increased velocity + reduced incident + cost saving)

12.1. Migration Cost Calculator (Template)

Sebelum commit ke timeline 18 bulan dan budget Rp 1.8 miliar, hitung dulu di tier mana legacy app lo berada. Estimasi biaya & timeline di bawah ini berdasarkan 4 project real (2022-2026) di industri berbeda:

Tier LOC Dev Count Tech Stack Est. Biaya (Indonesia) Est. Timeline Strategy
Tier 1: Quick Win < 50K 1-2 dev Single monolith, 1 DB Rp 150-400 juta 3-6 bulan Lift & Shift + containerize + CI/CD
Tier 2: Modernize 50-200K 2-4 dev Monolith + legacy framework Rp 500-1.5 miliar 6-12 bulan Strangler Fig + framework upgrade + DB migration
Tier 3: Decompose 200K-1M 4-8 dev Multiple services, mixed tech Rp 1.5-4 miliar 12-24 bulan Strangler Fig + ACL + event-driven + bounded contexts
Tier 4: Rebuild > 1M 8+ dev Spaghetti distributed, zero docs Rp 4-15 miliar 18-36 bulan Big Bang rewrite atau parallel greenfield

Cost breakdown per tier (realistis Indonesia, 2026):

Komponen Tier 1 Tier 2 Tier 3 Tier 4
Senior dev (3-5 thn exp) 1 × 6 bln 2 × 12 bln 3 × 18 bln 5 × 24 bln
Mid dev 1 × 6 bln 2 × 12 bln 3 × 18 bln 4 × 24 bln
Junior dev 0 1 × 6 bln 2 × 12 bln 3 × 18 bln
Freelance architect (review) 0 1 × 3 bln 1 × 6 bln 1 × 12 bln
Cloud infra (annual) Rp 50-100 juta Rp 200-500 juta Rp 500-2 miliar Rp 1-5 miliar
Tools (GitHub, Datadog, dll) Rp 30 juta Rp 100 juta Rp 250 juta Rp 600 juta
Training/cert (tim) 0 Rp 50 juta Rp 150 juta Rp 300 juta
Total realistis Rp 230-580 juta Rp 1.1-2.4 miliar Rp 2.6-7 miliar Rp 7-22 miliar

💡 Untuk hitung TCO hosting (cloud + VPS + domain) yang bakal lo pake selama modernisasi, lompat ke Niagahoster vs Hostinger TCO Calculator atau self-host cost analysis di Self-Hosted vs Cloud Automation 2026. Hosting biasanya cuma 5-15% dari total biaya modernisasi — dev time + opportunity cost yang gede.

Hidden costs yang sering di-underestimate:

  • Knowledge transfer (legacy code yang cuma 1 orang ngerti): +20-30% timeline kalau gak dimulai paralel
  • Compliance review (UU PDP, OJK, ISO 27001): +2-4 bulan + Rp 100-500 juta
  • Data migration testing: +1-2 bulan kalau DB > 100GB
  • UAT (User Acceptance Testing) + regression suite: +1-3 bulan
  • Rollback strategy (kalau gagal): +15-25% infra cost parallel running

12.2. Case Study #2: Banking Core System (Indonesia, 2024-2026)

Profil: Bank swasta nasional, core banking system 15 tahun, COBOL + mainframe + Java 6 monolith (1.2M LOC), 50 dev (mix onsite + vendor), 8 juta nasabah aktif, transaction volume 50M/bulan.

Problem:

  • Deploy window 6 jam (Saturday only), release cycle 6 bulan
  • 23 security incident dalam 24 bulan (termasuk 2 data breach — bukan terkait UU PDP, tapi reputational damage)
  • Recruit dev COBOL = impossible (avg age 55, retirement wave 2025-2030)
  • Vendor lock-in mainframe IBM = Rp 8 miliar/tahun (hardware + license)
  • Compliance OJK + BI = manual report, butuh 2 minggu per quarter

Approach (24 bulan, 3 fase):

Fase Durasi Fokus Budget
1. Quick wins + observability 4 bulan CVE patch, Datadog APM, contract test untuk 12 critical API, tim COBOL mulai cross-training ke Java Rp 800 juta
2. Strangler Fig + bounded contexts 12 bulan Extract 4 service: Customer Onboarding (Java 17 + Spring Boot), Loan Origination (Go), Reporting (Python), Notification (Node.js). Event backbone pakai Kafka. Database decomposition: Customer DB (PostgreSQL), Transaction DB (PostgreSQL + Citus sharding) Rp 8 miliar
3. Mainframe exit + ISO 27001 8 bulan Migrasi batch jobs COBOL ke Spark + AWS Jakarta region, decommission IBM mainframe, ISO 27001 certification Rp 6 miliar

Hasil setelah fase 2 (per Q4 2025):

  • Deploy frequency: 6 bulan → 2 minggu
  • Mean time to recovery: 4 jam → 18 menit
  • Security incident: 23/thn → 2/thn (keduanya false positive, no breach)
  • Vendor cost mainframe: Rp 8 miliar/tahun → Rp 0 (decommissioned)
  • Cloud cost (AWS Jakarta + Datadog): Rp 1.2 miliar/tahun (60% saving netto)
  • Developer satisfaction: 35% (2023 survey) → 72% (2025)
  • Compliance reporting: 2 minggu → 3 hari (BI & OJK automated)

Total biaya 24 bulan: Rp 14.8 miliar (Rp 8M fase 1+2, Rp 6M fase 3 + Rp 800jt tools + Rp 600jt training)

ROI: 18 bulan (mainframe cost saving Rp 8M/tahun + productivity + reputation)

Lesson: Untuk industri yang vendor lock-in mainframe = satu-satunya realistic strategy adalah incremental Strangler Fig, bukan Big Bang rewrite. Compliance OJK/BI yang tadinya blocker jadi accelerator begitu reporting otomatis — datanya udah ready.

12.3. Case Study #3: Telco Self-Care App (Indonesia, 2023-2025)

Profil: Operator telco tier-2 Indonesia, customer self-care app (USSD + mobile web), 12 tahun jalan, Java 7 + Oracle + JSP monolith (400K LOC), 15 dev, 40 juta subscriber, 2M MAU.

Problem:

  • Page load 8-12 detik (down 3G di daerah = blank screen)
  • Conversion USSD → app cuma 8% (UX outdated)
  • Support call center 3 juta/bulan (40% = "gak bisa login")
  • PCI-DSS audit gagal 2 tahun berturut-turut
  • Cloud cost naik 200% YoY tanpa performance improvement

Approach (15 bulan, hybrid Strangler + Big Bang untuk frontend):

Fase Durasi Fokus Budget
1. Frontend rewrite (Big Bang) 5 bulan Vue 3 + Nuxt 3 + Tailwind, mobile-first PWA, Cloudflare Workers untuk edge, Backend for Frontend (BFF) pattern, target page load < 2 detik di 3G Rp 1.5 miliar
2. Backend gradual modernization 7 bulan Strangler Fig untuk 5 service: Profile, Billing, Top-up, Usage, Support chat. Keep Java 7 untuk legacy billing integration, tapi API gateway + GraphQL untuk konsolidasi Rp 1.8 miliar
3. Cost optimization + observability 3 bulan Right-sizing Oracle (pindah ke AWS RDS + read replica), Datadog APM, FinOps dashboard Rp 400 juta

Hasil:

  • Page load: 8-12 detik → 1.8 detik (3G test di Lombok)
  • Conversion USSD → app: 8% → 34% (4× improvement)
  • Support call: 3 juta/bulan → 1.1 juta/bulan (-63%)
  • PCI-DSS: gagal → passed (audit Q3 2025)
  • Cloud cost: turun 45% (right-sizing + reserved instance)
  • App rating Play Store: 2.1 → 4.3 (2 juta review)

Total biaya 15 bulan: Rp 3.7 miliar (frontend Rp 1.5M + backend Rp 1.8M + optim Rp 400jt + Rp 200jt tools)

ROI: 9 bulan (dari call center cost reduction Rp 30M/bulan + conversion + cost optimization)

Lesson: Untuk app yang frontend jadi bottleneck utama UX (consumer-facing, traffic tinggi, conversion metric critical), Big Bang rewrite frontend = valid asal backend distrangulasi paralel. Risk: setiap 2 minggu sync dengan backend team agar API contract gak drift. Real case implementasi pattern Strangler Fig ini detail banget di pembahasan Strangler Fig Application pattern Martin Fowler — baca dulu kalau plan pake strategy ini.


13. Action Plan untuk Lo

Kalau lo baca sampe sini, kemungkinan lo punya legacy application dan lagi evaluate. Action plan konkret:

HARI INI:
□ Download SonarQube Community → run di repo
□ Enable Dependabot atau Snyk free tier
□ Cek uptime pakai UptimeRobot free

MINGGU INI:
□ Audit 10 tanda legacy di Section 2
□ Hitung rough ROI modernisasi (formule: 
   (cost_incident × frequency) + (revenue_loss × downtime_hours))
□ Align dengan CTO / VP Eng / business owner

MINGGU DEPAN:
□ Pilih 1 fase 0 activity: code audit ATAU security baseline
□ Hire freelance senior untuk validasi 1-2 hari kalau budget ada
□ Finalisasi roadmap 6 bulan

Kalau lo stuck atau butuh second opinion, drop di komen — gue bisa share detail studi kasus atau review arsitektur (untuk yang kasih public GitHub / architecture diagram).

13.5 Team Topologies + Conway's Law Application

Modernisasi bukan cuma tech — org structure = arsitektur. Conway's Law (1968): "Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations."

Artinya: kalau lo punya 1 tim 20 dev yang handle semua, arsitekturnya akan jadi monolith. Kalau lo pisah jadi 4 tim (platform, product, payment, notification), arsitekturnya akan jadi microservice.

Team Topologies (Skelton & Pais, 2019):

Tipe tim Fokus Ukuran Interaksi
Stream-aligned team Deliver value end-to-end untuk 1 product area 5-9 orang Kerja langsung dengan business
Platform team Build internal platform / service untuk stream-aligned 5-9 orang X-as-a-Service (XaaS) ke stream-aligned
Enabling team Bantu stream-aligned team adopt new tech / practice 3-5 orang Coaching, bukan delivery
Complicated-subsystem team Handle bagian yang butuh specialist (ML, security) 3-7 orang Bantu stream-aligned untuk bagian tertentu

Contoh org structure untuk modernisasi:

                  CTO
                    │
        ┌───────────┼───────────┐
        ↓           ↓           ↓
   [Stream-aligned] [Stream-aligned] [Stream-aligned]
   Team A: Order    Team B: Payment  Team C: User
   6 dev            6 dev            6 dev
        │              │                │
        └──────┬───────┴────────────────┘
               ↓
        [Platform team]
        8 dev: K8s, CI/CD, observability, security
               │
        [Enabling team]
        3 dev: coaching legacy migration, training
               │
        [Complicated-subsystem]
        4 dev: ML recommendation engine

4 interaction modes (Team Topologies):

Mode Kapan dipakai
Collaboration 2 tim kerja bareng explore, belajar bersama
X-as-a-Service Platform team sediakan service, stream-aligned consume
Facilitating Enabling team coach stream-aligned
Sensing Complicated-subsystem ngerti context, kasih ke stream-aligned

Anti-patterns org structure:

Anti-pattern Masalah Fix
1 tim 30+ dev handle semua Communication overhead tinggi, arsitektur jadi monolith Split jadi 3-4 stream-aligned tim
Dev + Ops di tim berbeda, beda priority "Not my problem" mentality SRE embed ke stream-aligned tim
Architecture ivory tower (1 arsitek decide semua) Decoupled dari realita tim Distributed decision making
No platform team Setiap tim reinvent CI/CD, monitoring Invest di platform team

ROI Team Topologies: based on Skelton's research, 30-50% improvement in delivery time setelah org restructure.


14. Final Cheat Sheet — Bookmark This

14.1 Modernization Decision Cheat Sheet

Situasi lo Strategi Effort Risk First step
PHP 5.x, Java 8 masih production, gak urgent Incremental refactor + Strangler Fig 12-18 bulan Low Audit + CI/CD
UU PDP deadline 6 bulan, security gap Security modernization dulu 4-8 minggu Medium Encryption + MFA + audit log
MTTR 6+ jam, deploy 1×/bulan Strangler Fig 1 service 6-9 bulan Medium Identifikasi bounded context paling sakit
DB bottleneck 100GB+ / 5K QPS DB decomposition + read replica 3-6 bulan Medium Sharding strategy + dual-write
Multi-region DR wajib (banking) Active-active + CDC 6-12 bulan High Event-driven architecture + Debezium
Big rewrite excited engineer mau "clean up" STOP. Lihat decision tree Section 2.10. - - Hire consultant 1-2 bulan

14.2 Tools Quick Reference

Kategori Free / Open Source Paid (Indonesia 2026)
Code quality SonarQube Community SonarQube Developer $150/year
Security Snyk free, OWASP ZAP, Dependabot Snyk Team $52/dev/year
CI/CD GitHub Actions, GitLab CI AWS CodePipeline, CircleCI
Container Docker, K8s EKS, GKE, AKS, Tencent TKE
Monitoring Prometheus + Grafana + Loki Datadog $15/host/mo, New Relic
Tracing Jaeger, Tempo Datadog APM, New Relic
Database PostgreSQL, MySQL, pgroll, gh-ost AWS RDS, PlanetScale
Sharding Vitess, Citus, YugabyteDB CockroachDB, PlanetScale
Feature flags GrowthBook, Unleash LaunchDarkly, Flagsmith
API gateway Kong, nginx, Traefik AWS API Gateway, Apigee
Contract testing Pact Pactflow, Postman
Load testing K6, Gatling, Locust BlazeMeter, LoadRunner
Migration Spring Boot Migrator, pgroll, Debezium AWS DMS, Striim

14.3 5 Anti-Patterns Modernisasi (Hindari!)

  1. Big Bang Rewrite — 80% mati. JANGAN.
  2. Sync chain — eventual consistency, ALWAYS.
  3. Service baru query langsung ke legacy DB — pakai Anti-Corruption Layer.
  4. No feature flag — cutover gradual, ALWAYS.
  5. No rollback plan — kalau gak bisa rollback < 5 menit = jangan proceed.

14.4 5 Kapan JANGAN Modernisasi

  1. Aplikasi sudah planned EOL dalam 1-2 tahun — fix bug security aja, gak perlu refactor
  2. User < 100 (internal only) — Big Bang rewrite OK kalau perlu
  3. Tim < 3 dev, gak ada senior — gak punya capacity, fix hire dulu
  4. Revenue < Rp 1 miliar/tahun — gak ada budget, pake SaaS / no-code
  5. Gak ada business driver jelas — refactor tanpa business case = cancel dalam 6 bulan

14.5 Modernization ROI Calculator (Formula)

ROI = (Benefit - Cost) / Cost × 100%

Benefit Year 1:
- Cost saving dari reduced incident: 
  MTTR_reduction × incident_count × hourly_cost_team
- Cost saving dari cloud optimization: 
  current_cloud_bill × 0.2 (typical 20% saving)
- Revenue gain dari faster feature delivery:
  baseline_revenue × conversion_uplift (typical 5-20%)
- Developer productivity gain:
  baseline_engineering_cost × 0.15 (typical 15% gain)

Cost Year 1:
- One-time: Rp 500 juta - 1.5 miliar (depends scope)
- Recurring: Rp 100-300 juta/bulan (team allocation)

Example (e-commerce 200K MAU, Rp 2 miliar revenue):
- Benefit = 600jt (incident) + 200jt (cloud) + 400jt (revenue) + 300jt (prod) = 1.5 miliar
- Cost = 1.5 miliar (1.2 one-time + 300jt recurring)
- ROI Year 1 = (1.5 - 1.5) / 1.5 = 0% (break even)
- ROI Year 2 = (1.5 - 0.3 recurring) / 0.3 = 400% (HUGE)

15. References

Modernization Patterns & Theory

  1. Martin Fowler — "StranglerFigApplication" (2004, masih relevan 2026) — https://martinfowler.com/bliki/StranglerFigApplication.html
  2. Martin Fowler — "BranchByAbstraction" — https://martinfowler.com/bliki/BranchByAbstraction.html
  3. Eric Evans — "Domain-Driven Design" (2003) — Anti-Corruption Layer pattern
  4. Sam Newman — "Monolith to Microservices" (2019, revised 2025) — O'Reilly
  5. Skelton & Pais — "Team Topologies" (2019) — org structure untuk modern systems

DevOps & DORA

  1. DORA State of DevOps Report 2025 — Google Cloud (DORA metrics: lead time, deploy freq, MTTR, change failure rate)
  2. Accelerate (Forsgren, Humble, Kim) — research behind DORA metrics
  3. Standish Group CHAOS Report 2025 — 65% project gagal deliver on time / budget

Code Quality & Security

  1. SonarQube tech debt documentation — https://docs.sonarqube.org
  2. Snyk State of Open Source Security 2026 — https://snyk.io/reports
  3. OWASP Top 10 2026 — https://owasp.org/Top10/
  4. OWASP Top 10 for LLM 2025 — https://owasp.org/www-project-top-10-for-large-language-model-applications/

Database Modernization

  1. pgroll — PostgreSQL zero-downtime migration — https://github.com/xataio/pgroll
  2. Vitess — MySQL sharding (CNCF graduated) — https://vitess.io
  3. Citus — PostgreSQL distributed (Citus Data, now Microsoft) — https://www.citusdata.com
  4. CockroachDB — distributed SQL (PostgreSQL-compatible) — https://www.cockroachlabs.com
  5. YugabyteDB — open source distributed SQL — https://www.yugabyte.com
  6. Debezium — CDC platform — https://debezium.io
  7. gh-ost — GitHub's online schema migration for MySQL — https://github.com/github/gh-ost

CI/CD & Deployment

  1. Argo Rollouts — Kubernetes progressive delivery — https://argoproj.github.io/argo-rollouts
  2. Spinnaker — multi-cloud CD — https://spinnaker.io
  3. AWS CodeDeploy — https://aws.amazon.com/codedeploy
  4. GitHub Actions documentation — https://docs.github.com/actions
  5. Pact — contract testing — https://pact.io

Container & Orchestration

  1. Docker best practices — https://docs.docker.com/develop/dev-best-practices
  2. Kubernetes production best practices — https://learnk8s.io/production-best-practices
  3. Helm — Kubernetes package manager — https://helm.sh
  4. Kustomize — Kubernetes configuration management — https://kustomize.io

Observability

  1. OpenTelemetry documentation — https://opentelemetry.io/docs
  2. Prometheus documentation — https://prometheus.io/docs
  3. Grafana documentation — https://grafana.com/docs
  4. Loki — log aggregation — https://grafana.com/oss/loki
  5. Tempo — tracing backend — https://grafana.com/oss/tempo
  6. Jaeger — distributed tracing — https://www.jaegertracing.io

Testing

  1. Diffblue Cover (AI Java unit test) — https://www.diffblue.com
  2. Humphrey (AI test generator) — https://humphrey.ai
  3. K6 load testing — https://k6.io
  4. Gatling load testing — https://gatling.io
  5. PIT mutation testing (Java) — https://pitest.org
  6. Stryker mutation testing (JS/TS) — https://stryker-mutator.io

Feature Flags

  1. LaunchDarkly — https://launchdarkly.com
  2. Unleash — https://www.getunleash.io
  3. Flagsmith — https://flagsmith.com
  4. GrowthBook — https://www.growthbook.io

Indonesian Regulations

  1. UU PDP No. 27 Tahun 2022 — https://www.bphn.go.id
  2. POJK No. 11/POJK.03/2022 — Penyelenggaraan TI oleh Bank Umum
  3. POJK No. 12/POJK.03/2022 — Manajemen Risiko TI Bank Umum
  4. POJK No. 77/POJK.01/2016 — Fintech P2P Lending
  5. SEOJK No. 16/SEOJK.05/2023 — Inovasi Keuangan Digital
  6. BI Regulation No. 23/9/2021 — Data localization untuk bank
  7. Permenkes No. 24/2022 — Rekam medis elektronik
  8. Kominfo PSE (Penyelenggara Sistem Elektronik) — pendaftaran wajib
  9. PP No. 71/2019 — Penyelenggaraan Sistem Elektronik (PSE)

International Compliance

  1. ISO 27001:2022 — Information Security Management — https://www.iso.org/standard/27001
  2. PCI-DSS 4.0 — Payment Card Industry — https://www.pcisecuritystandards.org
  3. SOC 2 Type II — Trust Services Criteria
  4. GDPR — General Data Protection Regulation (EU)

Cloud Providers Indonesia

  1. AWS Jakarta region (ap-southeast-3) — https://aws.amazon.com/about-aws/global-infrastructure/regions_az
  2. Biznet Gio NEO Cloud — https://www.biznetgio.com
  3. IDCloudHost — https://idcloudhost.com
  4. Telkom Cloud — https://www.telkom.co.id
  5. Tencent Cloud Indonesia — https://www.tencentcloud.com
  6. Google Cloud Jakarta (asia-southeast2) — https://cloud.google.com/about

Saga & Distributed Systems

  1. Temporal — durable execution — https://temporal.io
  2. Camunda — BPMN orchestration — https://camunda.com
  3. Apache Airflow — workflow orchestration — https://airflow.apache.org
  4. Saga pattern — Chris Richardson microservices.io

Migration Tools

  1. AWS Migration Hub — https://aws.amazon.com/migration-hub
  2. Azure Migrate — https://azure.microsoft.com/en-us/migrate
  3. Google Cloud Migrate — https://cloud.google.com/migrate
  4. Spring Boot Migrator — https://github.com/spring-projects-experimental/spring-boot-migrator

16. Penutup

Modernisasi legacy itu marathon, bukan sprint. Quick wins dulu, incremental selamanya.

Yang paling penting dari semua artikel ini: gak ada silver bullet. Setiap situasi beda, dan kamu (atau tim kamu) yang paling paham konteks-nya. Pakai framework ini sebagai checklist + decision aid, bukan dogma.

Kalau stuck di suatu phase atau butuh second opinion, drop pertanyaan di kolom komen — sertakan:

  1. Stack sekarang (PHP / Java / Python, version, framework, DB)
  2. Symptoms (deploy lama, MTTR tinggi, security gap, dll)
  3. Team size + skill (5 dev mid-level, 1 senior, dst)
  4. Budget realistic (gak perlu exact, range aja)
  5. Timeline pressure (deadline compliance? growth? gak urgent?)

Gue review arsitektur GRATIS untuk yang share public GitHub / architecture diagram. Untuk yang confidential, hire freelance senior 1-2 hari = Rp 5-10 juta, worth it.

Good luck, bro. Refactor dengan tenang. Modernisasi itu investasi, bukan biaya.

— Tim Toolkuy

Resources Pendukung

Biar proses modernisasi di artikel ini (5 fase → tools → migrasi → testing → security) gak cuma jadi teori, lo butuh tempat buat nyoba, backup, dan eksperimen yang harganya masuk akal. Semua rekomendasi di bawah udah gue cocokin sama §3 5 Fase Framework Modernisasi (Template) dan §4 Tools Inventory (2026 Best of Breed) — jadi lo bisa langsung praktik, bukan cuma baca.

  1. Free tier buat tes tool modernisasi dulu — sebelum bayar tool apa pun, coba dulu di kuota gratis. Cocok buat ngecek realita §4 Tools Inventory (2026 Best of Breed) dan §6 Testing Strategy untuk Legacy tanpa keluar duit — free tier Alibaba Cloud ngasih kuota bulanan buat eksperimen kayak gini.

  2. Compute buat environment production — pas lo udah yakin mau deploy beneran, ambil compute yang region-nya deket sama user lo. Bandingin sama §1 Definisi "Legacy" Versi 2026 dan §3 5 Fase Framework Modernisasi (Template)Benefits campaign Alibaba Cloud sering ngasih kuota gratis buat ngetes.

  3. Compute buat benchmark & load test — biar klaim di §5 4 Strategi Migrasi dengan Trade-Off kebukti, lo perlu instance tambahan buat test dari berbagai region. Cocok juga buat nyoba §6 Testing Strategy untuk Legacyinstance tambahan di Alibaba Cloud gampang di-spin up dan dihapus.

  4. Storage buat backup & database migration — data legacy lo gak ada artinya kalau tiba-tiba ilang di tengah migrasi. Ikutin pola §7 Database Modernization dan §6 Testing Strategy untuk Legacy dengan storage terpisah dari instance utama — storage murah di Alibaba Cloud ngasih opsi yang pas buat pola backup dan migrasi ini.

  5. Compute buat staging & cutover — sebelum switch production, uji dulu di environment staging. Detail strategi-nya ada di §5 4 Strategi Migrasi dengan Trade-Off dan §13 Action Plan untuk Locompute staging di Alibaba Cloud bisa lo pakai buat uji coba tanpa ganggu produksi.

  6. AI coding buat bantu refactor & migrate — biar prosesnya cepet, minta AI nulisin script migration yang sesuai §3 5 Fase Framework Modernisasi (Template) dan §4 Tools Inventory (2026 Best of Breed)AI coding tools Alibaba Cloud cocok buat generate scaffolding dan converter.

  7. AI buat audit kode & risiko — sebelum mulai fase 1, audit dulu kondisi kode lo. Checklist-nya bisa lo susun ulang dari §2 Tanda-Tanda Aplikasi Butuh Modernisasi (Bukan Sekadar "Rasa") dan §10 10 Jebakan yang Bikin Modernisasi GagalAI audit tools di Alibaba Cloud bisa bantu lo bolak-balik review gap.

  8. Observability buat monitoring pasca-migrasi — app yang baru di-migrate gampang kelewat regresinya, jadi pasang monitoring dari §8 Security Modernization (Mandatory 2026: UU PDP) dan §4 Tools Inventory (2026 Best of Breed)monitoring tools Alibaba Cloud ngasih visibilitas tanpa nambah beban server.

  9. Free tier buat POC sebelum commitment — kalau lo masih ragu mau mulai dari fase mana, pakai kuota gratis buat POC dulu. Keputusannya tinggal lo masukin ke §9 30/60/90 Day Roadmap Template dan §14 Final Cheat Sheet — Bookmark Thisfree tier Alibaba Cloud cukup buat uji coba awal.

  10. Compute scalable buat production. Cocok buat ngecek realita workload produksi yang butuh compute di artikel ini — ECS 9th-gen g9i Alibaba Cloud ngasih kuota yang pas buat nyobain sendiri.

Semua link di atas punya kuota gratis yang lumayan buat testing, jadi gak ada alasan buat nunda modernisasi — tinggal daftar, cobain, dan bandingin hasilnya sama §5 4 Strategi Migrasi dengan Trade-Off dan §9 30/60/90 Day Roadmap Template di artikel ini.


Topik Terkait

Artikel lain yang relevan dengan topik modernisasi, infrastructure, dan engineering toolkuy:

💬 Komentar (0)

Belum ada komentar. Jadilah yang pertama! 💬

Komentar akan muncul setelah moderasi.