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.
TL;DR
- Modernisasi aplikasi legacy idealnya memakai pola Strangler Fig dan incremental refactor, karena rewrite from scratch memiliki angka kegagalan 80% sementara pendekatan incremental punya peluang survive 95%.
- Framework modernisasi terdiri dari 5 fase: Audit & Quick Wins (2-4 minggu), Stabilization (4-8 minggu), Decoupling (8-16 minggu), Modernization (12-24 minggu), dan Optimization yang berjalan ongoing.
- Empat DORA metrics yang perlu dipantau saat modernisasi adalah lead time for changes, deploy frequency, change failure rate, dan failed deployment recovery time (MTTR), dengan target elite berupa lead time di bawah 1 hari dan MTTR di bawah 1 jam.
- Pola pendukung migrasi penting untuk diterapkan antara lain Anti-Corruption Layer, Transactional Outbox, Change Data Capture dengan Debezium dan Kafka, feature flags, serta Saga pattern dengan idempotency key.
- Berdasarkan sampel sekitar 30 perusahaan di Indonesia pada Q1-Q2 2026, lebih dari 70% startup memiliki minimal 2 kategori legacy, dan yang berhasil modernisasi adalah yang mulai dari audit dan quick wins, bukan Big Bang rewrite.
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, pelajari dulu roadmap backend developer 2026 secara mandiri.
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:
- Code quality audit — SonarQube scan, ukur tech debt ratio, security hotspot
- Dependency audit — Snyk / Dependabot untuk CVE critical
- Architecture diagram — Draw the actual state (bukan yang lo kira)
- Runbook documentation — Kalau belum ada, tulis minimal 10 skenario incident
- 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:
- CI/CD pipeline — GitHub Actions / GitLab CI minimal: test → build → deploy
- Automated testing — Cover critical path minimal (happy path + 5 error path)
- Monitoring + alerting — Prometheus + Grafana, atau Datadog / New Relic kalau budget
- Log aggregation — Centralized logging (Loki, ELK, atau CloudWatch)
- Backup automation — Daily DB backup + 30-day retention + restore test
- 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:
- Identifikasi bounded context — Domain mana yang paling sering berubah? (biasanya: payment, notifikasi, user management, reporting)
- Extract service pertama — Pilih 1 bounded context yang paling value-adding
- Build API contract — Definisikan interface antara legacy & new service
- Dual-write period — Tulis ke legacy DB + new DB selama 4-8 minggu
- Read switch — Migrate read path ke new service
- Cutover write — Stop write ke legacy, full traffic ke new service
- 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:
- Containerization (Docker + K8s) — quick win, enable semua next step
- CI/CD + monitoring (kalau belum ada) — safety net
- Database optimization — usually 30-50% performance gain
- Framework upgrade — biasanya per modul, incremental
- 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 library —
python-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:
- Cutover gradual — migrasi 1% traffic dulu, monitor, naikkan 10% → 50% → 100%
- Kill switch instant — kalau new service error, flip flag off dalam detik
- A/B test — flag untuk eksperimen user mana yang dapat new vs old flow
- 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:
- Blue = current production
- Deploy new version ke Green (idle)
- Test Green (smoke test, integration test)
- Switch load balancer: Blue → Green
- Green jadi production
- 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:
- Deploy new version (5% traffic)
- Monitor metrics: error rate, latency, business KPI
- Kalau OK, naikkan 10% → 25% → 50% → 100%
- 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:
- Ada 4 instance, 1 update ke new
- Next instance update
- Lanjut sampai semua new
- 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*.jsondulu, lalu code (leverage Docker cache) - ❌ Jangan
apt-get installtanparm -rf /var/lib/apt/lists/* - ❌ Jangan
ADD(pakaiCOPYkecuali 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→ ✅ lulushttp_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:
- Week 1-2: Setup API gateway (Kong / nginx / Istio) dengan routing rules
- Week 3-4: Deploy new service (idle, 0% traffic), observability jalan
- Week 5-6: Internal users canary (5% traffic, employee only)
- Week 7-8: Beta users (10% traffic, real users opt-in)
- Week 9-10: Ramp up (25% → 50% → 75%), monitor error rate + latency
- Week 11-12: Full cutover (100% ke new), legacy jadi read-only
- 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:
- Stop the bleeding — Untuk code baru, wajib ada unit test (coverage > 70%)
- Characterization test untuk legacy — Tulis test yang capture BEHAVIOR existing, bukan behavior yang seharusnya. Nama test: "WhenUserSubmitsEmptyForm_ShowsError" (existing behavior, bukan ideal)
- Integration test untuk critical path — Login, checkout, payment — 3-5 flow utama
- E2E smoke test — Selenium / Playwright untuk 3-5 happy path
- 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:
- Identify shared tables — tabel apa yang diakses > 1 service
- Add new schema di service baru — copy schema, forward-migrate data
- Dual-write — service A + B sama-sama write ke shared table (CDC sync)
- Validate — compare row counts + sample data 2-4 minggu
- Cutover read — service yang lain read dari new schema
- Cutover write — stop write ke shared table
- 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
-80untuk 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):
- Enable HTTPS everywhere (Let's Encrypt free)
- Rotate semua default password
- Enable MFA di semua admin panel
- Setup Snyk / Dependabot untuk auto CVE alert
- 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!)
- ❌ Big Bang Rewrite — 80% mati. JANGAN.
- ❌ Sync chain — eventual consistency, ALWAYS.
- ❌ Service baru query langsung ke legacy DB — pakai Anti-Corruption Layer.
- ❌ No feature flag — cutover gradual, ALWAYS.
- ❌ No rollback plan — kalau gak bisa rollback < 5 menit = jangan proceed.
14.4 5 Kapan JANGAN Modernisasi
- Aplikasi sudah planned EOL dalam 1-2 tahun — fix bug security aja, gak perlu refactor
- User < 100 (internal only) — Big Bang rewrite OK kalau perlu
- Tim < 3 dev, gak ada senior — gak punya capacity, fix hire dulu
- Revenue < Rp 1 miliar/tahun — gak ada budget, pake SaaS / no-code
- 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
- Martin Fowler — "StranglerFigApplication" (2004, masih relevan 2026) — https://martinfowler.com/bliki/StranglerFigApplication.html
- Martin Fowler — "BranchByAbstraction" — https://martinfowler.com/bliki/BranchByAbstraction.html
- Eric Evans — "Domain-Driven Design" (2003) — Anti-Corruption Layer pattern
- Sam Newman — "Monolith to Microservices" (2019, revised 2025) — O'Reilly
- Skelton & Pais — "Team Topologies" (2019) — org structure untuk modern systems
DevOps & DORA
- DORA State of DevOps Report 2025 — Google Cloud (DORA metrics: lead time, deploy freq, MTTR, change failure rate)
- Accelerate (Forsgren, Humble, Kim) — research behind DORA metrics
- Standish Group CHAOS Report 2025 — 65% project gagal deliver on time / budget
Code Quality & Security
- SonarQube tech debt documentation — https://docs.sonarqube.org
- Snyk State of Open Source Security 2026 — https://snyk.io/reports
- OWASP Top 10 2026 — https://owasp.org/Top10/
- OWASP Top 10 for LLM 2025 — https://owasp.org/www-project-top-10-for-large-language-model-applications/
Database Modernization
- pgroll — PostgreSQL zero-downtime migration — https://github.com/xataio/pgroll
- Vitess — MySQL sharding (CNCF graduated) — https://vitess.io
- Citus — PostgreSQL distributed (Citus Data, now Microsoft) — https://www.citusdata.com
- CockroachDB — distributed SQL (PostgreSQL-compatible) — https://www.cockroachlabs.com
- YugabyteDB — open source distributed SQL — https://www.yugabyte.com
- Debezium — CDC platform — https://debezium.io
- gh-ost — GitHub's online schema migration for MySQL — https://github.com/github/gh-ost
CI/CD & Deployment
- Argo Rollouts — Kubernetes progressive delivery — https://argoproj.github.io/argo-rollouts
- Spinnaker — multi-cloud CD — https://spinnaker.io
- AWS CodeDeploy — https://aws.amazon.com/codedeploy
- GitHub Actions documentation — https://docs.github.com/actions
- Pact — contract testing — https://pact.io
Container & Orchestration
- Docker best practices — https://docs.docker.com/develop/dev-best-practices
- Kubernetes production best practices — https://learnk8s.io/production-best-practices
- Helm — Kubernetes package manager — https://helm.sh
- Kustomize — Kubernetes configuration management — https://kustomize.io
Observability
- OpenTelemetry documentation — https://opentelemetry.io/docs
- Prometheus documentation — https://prometheus.io/docs
- Grafana documentation — https://grafana.com/docs
- Loki — log aggregation — https://grafana.com/oss/loki
- Tempo — tracing backend — https://grafana.com/oss/tempo
- Jaeger — distributed tracing — https://www.jaegertracing.io
Testing
- Diffblue Cover (AI Java unit test) — https://www.diffblue.com
- Humphrey (AI test generator) — https://humphrey.ai
- K6 load testing — https://k6.io
- Gatling load testing — https://gatling.io
- PIT mutation testing (Java) — https://pitest.org
- Stryker mutation testing (JS/TS) — https://stryker-mutator.io
Feature Flags
- LaunchDarkly — https://launchdarkly.com
- Unleash — https://www.getunleash.io
- Flagsmith — https://flagsmith.com
- GrowthBook — https://www.growthbook.io
Indonesian Regulations
- UU PDP No. 27 Tahun 2022 — https://www.bphn.go.id
- POJK No. 11/POJK.03/2022 — Penyelenggaraan TI oleh Bank Umum
- POJK No. 12/POJK.03/2022 — Manajemen Risiko TI Bank Umum
- POJK No. 77/POJK.01/2016 — Fintech P2P Lending
- SEOJK No. 16/SEOJK.05/2023 — Inovasi Keuangan Digital
- BI Regulation No. 23/9/2021 — Data localization untuk bank
- Permenkes No. 24/2022 — Rekam medis elektronik
- Kominfo PSE (Penyelenggara Sistem Elektronik) — pendaftaran wajib
- PP No. 71/2019 — Penyelenggaraan Sistem Elektronik (PSE)
International Compliance
- ISO 27001:2022 — Information Security Management — https://www.iso.org/standard/27001
- PCI-DSS 4.0 — Payment Card Industry — https://www.pcisecuritystandards.org
- SOC 2 Type II — Trust Services Criteria
- GDPR — General Data Protection Regulation (EU)
Cloud Providers Indonesia
- AWS Jakarta region (ap-southeast-3) — https://aws.amazon.com/about-aws/global-infrastructure/regions_az
- Biznet Gio NEO Cloud — https://www.biznetgio.com
- IDCloudHost — https://idcloudhost.com
- Telkom Cloud — https://www.telkom.co.id
- Tencent Cloud Indonesia — https://www.tencentcloud.com
- Google Cloud Jakarta (asia-southeast2) — https://cloud.google.com/about
Saga & Distributed Systems
- Temporal — durable execution — https://temporal.io
- Camunda — BPMN orchestration — https://camunda.com
- Apache Airflow — workflow orchestration — https://airflow.apache.org
- Saga pattern — Chris Richardson microservices.io
Migration Tools
- AWS Migration Hub — https://aws.amazon.com/migration-hub
- Azure Migrate — https://azure.microsoft.com/en-us/migrate
- Google Cloud Migrate — https://cloud.google.com/migrate
- 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:
- Stack sekarang (PHP / Java / Python, version, framework, DB)
- Symptoms (deploy lama, MTTR tinggi, security gap, dll)
- Team size + skill (5 dev mid-level, 1 senior, dst)
- Budget realistic (gak perlu exact, range aja)
- 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 bagian 3 5 Fase Framework Modernisasi (Template) dan bagian 4 Tools Inventory (2026 Best of Breed) — jadi lo bisa langsung praktik, bukan cuma baca.
-
Free tier buat tes tool modernisasi dulu — sebelum bayar tool apa pun, coba dulu di kuota gratis. Cocok buat ngecek realita bagian 4 Tools Inventory (2026 Best of Breed) dan bagian 6 Testing Strategy untuk Legacy tanpa keluar duit — free tier Alibaba Cloud ngasih kuota bulanan buat eksperimen kayak gini.
-
Compute buat environment production — pas lo udah yakin mau deploy beneran, ambil compute yang region-nya deket sama user lo. Bandingin sama bagian 1 Definisi "Legacy" Versi 2026 dan bagian 3 5 Fase Framework Modernisasi (Template) — Benefits campaign Alibaba Cloud sering ngasih kuota gratis buat ngetes.
-
Compute buat benchmark & load test — biar klaim di bagian 5 4 Strategi Migrasi dengan Trade-Off kebukti, lo perlu instance tambahan buat test dari berbagai region. Cocok juga buat nyoba bagian 6 Testing Strategy untuk Legacy — instance tambahan di Alibaba Cloud gampang di-spin up dan dihapus.
-
Storage buat backup & database migration — data legacy lo gak ada artinya kalau tiba-tiba ilang di tengah migrasi. Ikutin pola bagian 7 Database Modernization dan bagian 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.
-
Compute buat staging & cutover — sebelum switch production, uji dulu di environment staging. Detail strategi-nya ada di bagian 5 4 Strategi Migrasi dengan Trade-Off dan bagian 13 Action Plan untuk Lo — compute staging di Alibaba Cloud bisa lo pakai buat uji coba tanpa ganggu produksi.
-
AI coding buat bantu refactor & migrate — biar prosesnya cepet, minta AI nulisin script migration yang sesuai bagian 3 5 Fase Framework Modernisasi (Template) dan bagian 4 Tools Inventory (2026 Best of Breed) — AI coding tools Alibaba Cloud cocok buat generate scaffolding dan converter.
-
AI buat audit kode & risiko — sebelum mulai fase 1, audit dulu kondisi kode lo. Checklist-nya bisa lo susun ulang dari bagian 2 Tanda-Tanda Aplikasi Butuh Modernisasi (Bukan Sekadar "Rasa") dan bagian 10 10 Jebakan yang Bikin Modernisasi Gagal — AI audit tools di Alibaba Cloud bisa bantu lo bolak-balik review gap.
-
Observability buat monitoring pasca-migrasi — app yang baru di-migrate gampang kelewat regresinya, jadi pasang monitoring dari bagian 8 Security Modernization (Mandatory 2026: UU PDP) dan bagian 4 Tools Inventory (2026 Best of Breed) — monitoring tools Alibaba Cloud ngasih visibilitas tanpa nambah beban server.
-
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 bagian 9 30/60/90 Day Roadmap Template dan bagian 14 Final Cheat Sheet — Bookmark This — free tier Alibaba Cloud cukup buat uji coba awal.
-
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 bagian 5 4 Strategi Migrasi dengan Trade-Off dan bagian 9 30/60/90 Day Roadmap Template di artikel ini.
Topik Terkait
Artikel lain yang relevan dengan topik modernisasi, infrastructure, dan engineering toolkuy:
- Audit Konten Sebelum Nulis: Framework @seonatia (Natia Kurdadze) — SEO-First Content 2026 (audit framework yang bisa lo pake sebelum nulis/migrate dokumentasi)
- PRD vs SRS vs SDD: Framework Dokumentasi Solo Developer (template dokumentasi yang WAJIB ada sebelum kickoff modernisasi besar)
- Niagahoster vs Hostinger 2026 Pillar: TCO + Decision Framework (hitung TCO hosting buat staging + production selama modernisasi 18-24 bulan)
- Local LLM Self-Hosted (Llama 3, Mistral, Qwen) — Panduan VPS 2026 (kalau lo mau pake AI buat code migration assist — hemat 90% vs OpenAI)
FAQ
Apa definisi legacy versi 2026?
Legacy bukan soap usia aplikasi, melainkan kondisi ketika biaya maintenance naik lebih cepat dari value bisnisnya. Kategorinya mencakup EOL stack seperti PHP 5.x dan Java 8, monolith 500K+ LOC tanpa test, security gap, tidak ada CI/CD, hingga database yang sudah mentok di skala 100GB+ dan di atas 5K QPS.
Kapan sebaiknya pakai Strangler Fig dibanding Big Bang rewrite?
Strangler Fig cocok untuk aplikasi yang masih menangani revenue aktif dengan lebih dari 1000 user, biasanya selesai dalam 6-18 bulan dengan risiko low-medium. Big Bang rewrite hanya disarankan untuk aplikasi eksperimen atau internal tool dengan user di bawah 100 yang bisa selesai dalam 3-6 bulan.
Apa itu Anti-Corruption Layer (ACL) dan kapan wajib dipakai?
ACL adalah dedicated layer yang menerjemahkan model domain legacy ke model domain baru, mengambil konsep dari DDD ala Eric Evans. ACL wajib dipakai saat legacy DB schema bertemu service baru dengan model domain berbeda, saat integrasi lewat legacy API yang contract-nya tidak stabil, dan saat legacy memakai varchar untuk field yang seharusnya enum, dengan effort sekitar 1-2 minggu per domain.
Bagaimana cara mencegah data inconsistency saat dual-write period migrasi?
Gunakan idempotency key agar operasi yang di-retry tidak menimbulkan efek samping ganda, serta Transactional Outbox pattern yang menyimpan event di tabel outbox dalam transaksi yang sama dengan business write. Untuk distributed transaction lintas service, Saga pattern dengan compensation action di tiap step bisa menggantikan two-phase commit, dengan tools seperti Temporal dan Camunda sebagai opsi.
Strategi deployment apa yang direkomendasikan untuk migrasi service baru?
Canary deployment adalah pilihan utama untuk extract service baru karena risikonya rendah dengan mulai dari 5% traffic lalu naik bertahap hingga 100% sambil memantau error rate. Rekomendasi tooling-nya adalah Kubernetes dengan Argo Rollouts yang gratis dan open source, atau AWS CodeDeploy untuk target AWS, dan blue-green deployment lebih cocok untuk major release seperti framework upgrade karena rollback instan di bawah 1 detik.
Sumber & Referensi
Rujukan resmi yang dipakai untuk memverifikasi klaim di artikel ini:
💬 Komentar (0)
Belum ada komentar. Jadilah yang pertama! 💬