ADR-010-F: Strangler Fig Migration from ASG Central v2¶
Status: Accepted Date: 2026-04-16 Decision Makers: Gautham Chellappa Depends on: ADR-001-F (Elixir), ADR-002-F (Modular Monolith)
Context¶
ASG Central v2 is a CodeIgniter legacy application on MySQL (admin_central, admin_atslive, actatek databases) serving the anchor client's entire HR operation. Finnest replaces v2 over 44 weeks while v2 remains in production.
Options: big-bang cutover (high risk of data loss + user disruption), parallel-run with feature parity then cutover (doubles maintenance for months), or Strangler Fig — migrate domain by domain, extending Finnest's coverage incrementally, with v2 providing functionality not yet migrated.
Brainstorm-11 established a user-preferred phase order that flows from Scout + Verify go-live: Recruitment → Onboarding → CMS/Rostering → Timesheet/Reporting → Mobile/Pact.
Decision¶
Strangler Fig migration, domain by domain, over 44 weeks. Finnest is the new root; v2 is wrapped and replaced incrementally.
Phase order (B11, supersedes B04 original)¶
Phase 1 (Weeks 1-4 post go-live):
[Finnest: Recruit] ──handoff──→ [v2: Onboard, CMS, Roster, T&A, Payroll]
Phase 2 (Weeks 5-8):
[Finnest: Recruit, Onboard] ──handoff──→ [v2: CMS, Roster, T&A, Payroll]
Phase 3 (Weeks 9-16):
[Finnest: Recruit, Onboard, CMS, Roster] ──handoff──→ [v2: T&A, Payroll]
Phase 4 (Weeks 17-24):
[Finnest: + T&A, Reporting] ──handoff──→ [v2: Payroll only]
Phase X (Weeks 25-32):
[Finnest: Everything + Mobile + Pact] ──→ [v2: read-only archive]
Decommission (Week 33+):
v2 decommissioned after 3-month read-only period with no queries
Technical mechanisms¶
- Dual Ecto repos:
Finnest.Repo(Postgres, primary) +Finnest.V2Repo(MySQL via MyXQL, read-only) — AR-05, DA-01, DA-02 - ID mapping table
public.id_mappings— records every migrated v2 INT id → Finnest UUID with migration_batch traceability (B04 Insight 2) - Migration workers run in Oban — read from V2Repo, transform, write to primary Repo, populate id_mappings
- Data quality audit pre-phase — scan v2 for orphans, duplicates, encoding, missing required fields (B04 checklist)
- Dry-run with
write: falseflag before each cutover — produces row-count parity report + statistical diff - Tiered approach for large tables (hot 6mo / warm 2yr / cold archive) to keep cutover windows manageable
- Stakeholder sign-off gate after audit + dry-run, before production migration
- 3-month parallel access window per domain before v2 decommissioned — users can still query v2 for historical lookups
Single handoff direction¶
Each phase extends Finnest's boundary outward. Data flows: Finnest (migrated domains) → v2 (remaining domains). Never bidirectional — prevents write conflicts.
Alternatives Considered¶
| Alternative | Rejected because |
|---|---|
| Big-bang migration | 5K employees + 10+ years of data + no v2 FK constraints = unacceptable data loss risk |
| Parallel run with feature parity then cutover | Doubles maintenance burden; users confused by two systems; no value delivered until cutover |
| Abandon v2 data, start clean | Historical compliance records + pay history + contracts are legally retained; cannot abandon |
| Keep v2 indefinitely, integrate Finnest as add-on | v2 CodeIgniter is the product we're replacing; this defeats the purpose |
| Reverse phase order (migrate Payroll first) | Original B04 order. B11 demonstrated Recruit-first is better because it flows from Scout + Verify go-live and maintains single handoff direction |
Consequences¶
Positive:
- Zero-downtime cutover per domain
- Rollback possible within 24h per phase; event-replay rollback after
- Each phase delivers value to users (more of ASG's workflow on Finnest)
- Risk bounded per phase — a bad migration affects one domain, not the whole system
- Data quality issues surface domain-by-domain, not all at once
- Payroll is LAST — highest financial risk domain has longest validation runway
- Phases align with commercial delivery milestones (Plan A $2.025M phased)
Negative:
- 44-week dual-system operation
- v2 maintenance stream runs in parallel (critical fixes only, no new features)
- ID-mapping table grows over transition period (trivial size but conceptual load)
- Users operate in a mixed state — some workflows on Finnest, some on v2 — during Phases 1-4
Mitigations:
- v2 maintenance is explicitly a separate stream with its own (offshore) team — doesn't interrupt Finnest sprints (B10)
- Users experience single handoff direction — Finnest always reaches out to v2 for remaining domains, UX-wise it feels like one system with an "older features" section
id_mappingsis indexed by both directions for fast v2 → Finnest and Finnest → v2 lookups during parallel window
Risk register (B10):
- Data migration data corruption → data quality audit + dry-run + validation + 24h rollback
- v2 breaks during transition → separate maintenance stream
- Migration consumes Finnest dev time → migration workers in Oban, runs overnight/weekends; dev time focused on forward-feature work
Relationship to Guardrails¶
Enforces: AR-05 (external DB read-only), DA-01 (V2Repo read-only), DA-02 (explicit schema fields only), D9 (Strangler Fig migration), CQ-11 (reversible migrations).