Skip to content

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: false flag 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_mappings is 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).