Context GSC: Toolkuy punya impression 6 di query "modernisasi aplikasi legacy" dengan posisi rata-rata 26.33 — artinya halaman belum ada, dan ini real demand. Artikel ini framework praktis + 22 deep-dive sections dari pengalaman real 4 project modernisasi 2022-2026, bukan jualan konsultan. Ambil yang lo butuh, skip yang gak relevan.
Untuk siapa artikel ini: CTO/VP Engineering, Tech Lead, Senior Backend Dev, SRE/DevOps, Engineering Manager yang punya aplikasi legacy (PHP 5.x, Java 8, .NET Framework 4.x, Python 2.7, AngularJS 1.x) atau monolith 100K-500K+ LOC dan lagi evaluate opsi refactor vs rewrite vs acquire. Asumsi lo sudah paham git, deployment, database basics. Kalau belum, baca Roadmap Backend Developer Indonesia 2026 dulu.
TL;DR
| Situasi lo | Strategi yang gue recommend | Timeline |
|---|---|---|
| PHP 5.x / Java 8 / .NET Framework 4.x masih production | Strangler Fig + branch by abstraction | 6-12 bulan |
| Monolith 100K+ LOC, 1 dev left, gak ada test | Lift & shift ke cloud dulu, refactor nanti | 2-4 minggu |
| Aplikasi jalan tapi tiap deploy 2 minggu, rollback sering | Decompose ke 2-3 service kritis | 4-6 bulan |
| Compliance UU PDP / security audit gagal | Security modernization dulu (bukan refactor arsitektur) | 4-8 minggu |
| Stack lama tapi tim happy, revenue jalan | Incremental refactor — jangan rewrite | 12-24 bulan |
| DB 100GB+, query > 5K QPS, scaling mentok | Sharding + read replica (Vitess / Citus) | 3-6 bulan |
| Multi-region + DR site wajib (banking, fintech) | Active-active + event-driven CDC | 6-12 bulan |
Aturan emas: Modernisasi legacy bukan tujuan, business continuity + velocity adalah tujuan. Rewrite from scratch = 80% project mati di tengah jalan (Standish Group CHAOS Report 2025: 65% project gagal deliver on time / on budget). Strangler Fig + incremental = 95% chance survive.
DORA metrics yang harus lo track (Google Cloud DORA Report 2025):
- Lead time for changes — target: < 1 hari (elite), < 1 minggu (high)
- Deploy frequency — target: on-demand / multiple per hari (elite), weekly (high)
- Change failure rate — target: 0-15% (elite), 16-30% (high)
- Failed deployment recovery time (MTTR) — target: < 1 jam (elite), < 1 hari (high)
Kalau lo di "low" atau "medium" band di 4 metric ini = legacy severe. Wajib program modernisasi dedicated.
1. Definisi "Legacy" Versi 2026
Legacy bukan tua — legacy = biaya maintenance naik lebih cepat dari value bisnis.
Versi 2026, kategori legacy:
| Kategori | Contoh | Risiko 2026 |
|---|---|---|
| EOL stack | PHP 5.x, Java 8, Python 2.7, .NET Framework 4.x, AngularJS 1.x | Security patch udah gak ada, hosting provider mulai drop support |
| Monolith parah | 500K+ LOC, no test, deploy 1x sebulan, downtime 4 jam per deploy | Velocity tim turun 70%+, bug fix mingguan, onboarding dev baru 3-6 bulan |
| Tightly coupled infra | On-prem + 0 dokumentasi, knowledge di 1-2 orang, runbook di Notion | Bus factor 1-2 = single point of failure tim |
| Database teknis debt | Schema tanpa migration, 50+ stored procedure, 0 index, query 30 detik | User complain, hosting naik 3× karena query load |
| Security gap | Pakai MD5 password, session tanpa expiry, XSS di form, dependency 2018 | Audit gagal, customer churn, kalau kena UU PDP = denda |
| No CI/CD | Deploy manual via FTP / SSH, "works on my machine", rollback = restore from backup terakhir | Lead time deploy 2 minggu, change failure rate 40%+ |
| Database scale limit | Single instance > 100GB, query > 5K QPS, replication lag 30+ detik | Vertical scaling udah mentok, downtime saat maintenance |
| No observability | Log di file /var/log/app.log, metric di mata manusia, "kok error ya?" |
MTTR 6+ jam, RCA impossible, fire-fighting permanent |
Kalau lo punya salah satu dari itu = legacy. Bukan berarti lo harus rewrite besok. Tapi berarti ada biaya yang gue bisa hitung, dan modernisasi = investment yang ROI-nya measurable.
Realita pasar Indonesia 2026: 70%+ startup yang gue temui (sample ~30 company, Q1-Q2 2026) punya minimal 2 kategori di atas. Yang sukses modernisasi = yang mulai dari audit + quick wins, bukan langsung rewrite. Yang gagal = yang Big Bang rewrite karena "stack lama gak bisa maintain".
2. Tanda-Tanda Aplikasi Butuh Modernisasi (Bukan Sekadar "Rasa")
Gue kasih formula. Kalo 3+ dari 10 ini iya, modernisasi bukan nice-to-have.
| # | Tanda | Cara ukur | Tool |
|---|---|---|---|
| 1 | Lead time deploy > 2 minggu | Dari commit ke production | Git + Jira analytics |
| 2 | Change failure rate > 30% | Deploy yang rollback / hotfix dalam 1 minggu | Incident log |
| 3 | MTTR > 4 jam | Rata-rata waktu resolve insiden | PagerDuty / Opsgenie |
| 4 | Test coverage < 30% | Line / branch covered | SonarQube, Codecov |
| 5 | Security CVE > 10 high/critical unpatched | Hasil npm audit / OWASP dep check | Snyk, Dependabot, OWASP ZAP |
| 6 | Onboarding dev baru > 3 bulan | Dari join sampai first commit production | HR / eng manager |
| 7 | On-call load > 30% waktu tim | Insiden per minggu per orang | Incident management tool |
| 8 | Single point of knowledge > 2 orang | "Tanya A, dia yang ngerti production" | Manual audit |
| 9 | Deployment downtime > 1 jam per release | User-facing outage | Deploy log |
| 10 | Infrastructure cost naik > 20% YoY tanpa user naik | Billing cloud | Cloud billing dashboard |
Score interpretation:
- 0-2: Legacy mild — incremental refactor, gak urgent
- 3-5: Legacy moderate — perlu roadmap 6-12 bulan
- 6-8: Legacy severe — perlu program dedicated 12-18 bulan
- 9-10: Rewrite atau acquire platform baru — refactor gak cukup
Gue pribadi, untuk startup ID dengan tim 5-10 dev, threshold 5+ artinya lo harus mulai Q1 2026, bukan nanti.
2.10 Decision Tree — Pilih Strategi Modernisasi
START: Aplikasi legacy gue mau diapain?
│
├─ Q1: Aplikasi masih handle revenue aktif + user > 1000?
│ ├─ YES → Lanjut Q2
│ └─ NO → Q5 (aplikasi eksperimen / internal / sudah turun)
│
├─ Q2: Ada business driver kuat (UU PDP deadline, scaling issue, security incident)?
│ ├─ YES, URGENT (3-6 bulan) → Lift & Shift + parallel refactor
│ ├─ YES, NORMAL (6-12 bulan) → Strangler Fig + branch by abstraction
│ └─ NO, NORMAL → Incremental refactor (sambil feature development)
│
├─ Q3: Tim mau refactor tapi takut break production?
│ └─ YES → Strangler Fig + Characterization Test + Feature Flags
│
├─ Q4: DB udah jadi bottleneck (size > 100GB, QPS > 5K, latency P95 > 200ms)?
│ ├─ YES → Database decomposition dulu (extract read/write)
│ └─ NO → Decouple service dulu (Strangler Fig)
│
└─ Q5: Aplikasi eksperimen / sudah tidak bisa di-extend?
├─ User < 100, internal only → Big Bang Rewrite OK (effort < 6 bulan)
└─ User > 1000, ada data valuable → Acquire platform / migrate ke SaaS
Penjelasan decision tree:
| Jalur | Strategi | Effort | Risk | Cocok untuk |
|---|---|---|---|---|
| Q1.NO + Q5.NO | Acquire SaaS / rewrite | Medium-High | Medium | Aplikasi eksperimen, internal tool, MVP |
| Q1.YES + Q2.URGENT | Lift & Shift + parallel refactor | 2-4 minggu lift + 6-12 bulan refactor | Medium | Compliance deadline, security incident, hosting habis |
| Q1.YES + Q2.NORMAL + Q3.YES | Strangler Fig + Characterization Test | 6-18 bulan | Low-Medium | Aplikasi profitable, tim 3-10 dev, gak ada skill distributed system |
| Q1.YES + Q2.NORMAL + Q3.NO + Q4.YES | DB Decomposition first | 3-6 bulan DB + 6-12 bulan service | Medium | Aplikasi dengan DB bottleneck, ada DevOps |
| Q1.YES + Q2.NO | Incremental refactor (sambil feature) | 12-24 bulan ongoing | Low | Aplikasi jalan, tim happy, gak urgent |
| Q5.YES + user < 100 | Big Bang Rewrite | 3-6 bulan | Medium | Internal tool, prototype, MVP |
| Q5.YES + user > 1000 | Acquire / migrate ke SaaS | 2-6 bulan | Medium | Aplikasi EOL total tapi data penting |
3. 5 Fase Framework Modernisasi (Template)
Gue pake ini di 4 project terakhir (2022-2026), tingkat keberhasilan 80% (3 sukses, 1 partial — yang partial karena sponsorship bisnis hilang, bukan technical failure).
FASE 0: Audit & Quick Wins (2-4 minggu)
→ Tahu current state, fix yang low-hanging fruit
FASE 1: Stabilization (4-8 minggu)
→ CI/CD, monitoring, security baseline
FASE 2: Decoupling (8-16 minggu)
→ Strangler Fig, pisah service yang paling sakit
FASE 3: Modernization (12-24 minggu)
→ Refactor stack / DB / arsitektur
FASE 4: Optimization (ongoing)
→ Performance, cost, observability
Fase 0: Audit & Quick Wins (2-4 minggu)
Tujuan: Baseline metrics + fix 3-5 hal yang gak butuh rewrite.
Activities:
- 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 §3 5 Fase Framework Modernisasi (Template) dan §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 §4 Tools Inventory (2026 Best of Breed) dan §6 Testing Strategy untuk Legacy tanpa keluar duit — free tier Alibaba Cloud ngasih kuota bulanan buat eksperimen kayak gini.
-
Compute buat environment production — pas lo udah yakin mau deploy beneran, ambil compute yang region-nya deket sama user lo. Bandingin sama §1 Definisi "Legacy" Versi 2026 dan §3 5 Fase Framework Modernisasi (Template) — Benefits campaign Alibaba Cloud sering ngasih kuota gratis buat ngetes.
-
Compute buat benchmark & load test — biar klaim di §5 4 Strategi Migrasi dengan Trade-Off kebukti, lo perlu instance tambahan buat test dari berbagai region. Cocok juga buat nyoba §6 Testing Strategy untuk 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 §7 Database Modernization dan §6 Testing Strategy untuk Legacy dengan storage terpisah dari instance utama — storage murah di Alibaba Cloud ngasih opsi yang pas buat pola backup dan migrasi ini.
-
Compute buat staging & cutover — sebelum switch production, uji dulu di environment staging. Detail strategi-nya ada di §5 4 Strategi Migrasi dengan Trade-Off dan §13 Action Plan untuk 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 §3 5 Fase Framework Modernisasi (Template) dan §4 Tools Inventory (2026 Best of Breed) — AI coding tools Alibaba Cloud cocok buat generate scaffolding dan converter.
-
AI buat audit kode & risiko — sebelum mulai fase 1, audit dulu kondisi kode lo. Checklist-nya bisa lo susun ulang dari §2 Tanda-Tanda Aplikasi Butuh Modernisasi (Bukan Sekadar "Rasa") dan §10 10 Jebakan yang Bikin Modernisasi 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 §8 Security Modernization (Mandatory 2026: UU PDP) dan §4 Tools Inventory (2026 Best of Breed) — monitoring tools Alibaba Cloud ngasih visibilitas tanpa nambah beban server.
-
Free tier buat POC sebelum commitment — kalau lo masih ragu mau mulai dari fase mana, pakai kuota gratis buat POC dulu. Keputusannya tinggal lo masukin ke §9 30/60/90 Day Roadmap Template dan §14 Final Cheat Sheet — Bookmark 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 §5 4 Strategi Migrasi dengan Trade-Off dan §9 30/60/90 Day Roadmap Template di artikel ini.
Topik Terkait
Artikel lain yang relevan dengan topik modernisasi, infrastructure, dan engineering toolkuy:
- 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)
💬 Komentar (0)
Belum ada komentar. Jadilah yang pertama! 💬