Back to Learning & Growth
Q2Question 2 of 5

Driving a Technical Improvement or Migration: plan, challenges, and outcome

Key Concepts
  • Big migrations succeed by moving incrementally with a fallback, never by a big-bang cutover.
  • Use the strangler-fig pattern: new system gradually takes over while old runs side by side.
  • Shift traffic gradually via canary: route a small percentage to new, compare results, increase only when parity is proven.
  • Name the real challenges: data consistency, behavioral parity, team coordination.
  • Quantify the outcome: deployment time, failure rate, or performance improvement.
  • Keep a fallback so you can revert instantly if the new path misbehaves.
  • The key discipline is verifying parity continuously, not trusting the new system on faith.

Definition

Big migrations succeed by moving incrementally with a fallback, measuring at each step, and never risking a big-bang cutover.
  • Incremental, not big-bang – Migrate piece by piece with the old system running as a fallback.
  • Strangler-fig pattern – The new system gradually takes over responsibilities from the old one.
  • Canary traffic shifting – Route a small percentage of real requests to the new system and compare results.
  • Parity verification – Continuously compare old and new outputs; block rollout if they disagree.
  • Quantified outcome – Measure deployment time, failure rate, or performance before and after.
  • Fallback always ready – At any point you can revert to the old system instantly.

Imagine National Bank of India moving from one giant old transfer system to a set of smaller modern services, without ever stopping customer transfers for Rajesh or Priya. Driving a migration is like renovating a bank branch while it stays open: you move one counter at a time, keep the old one running as a fallback, and only close the old counter once the new one proves itself.

Understanding the Concepts

What is this question really testing?

The interviewer is checking that you manage risk during large technical change. A big-bang cutover that works is luck; an incremental migration with verification at each step is engineering.

JAVA
package com.bank.interview;
public class MigrationRisk {
    enum Approach { BIG_BANG, INCREMENTAL }
    public static String assess(Approach approach) {
        return switch (approach) {
            case BIG_BANG     -> "High risk: if it fails, everything breaks";
            case INCREMENTAL  -> "Low risk: fallback always ready, parity verified";
        };
    }
    public static void main(String[] args) {
        System.out.println(assess(Approach.INCREMENTAL));
    }
}

What is the strangler-fig pattern?

The new system gradually takes over pieces of the old one while both run side by side. You route a configurable percentage of traffic to the new service.

JAVA
package com.bank.service;
import java.util.Random;
public class StranglerRouter {
    private static final int NEW_SERVICE_PERCENT = 20;
    private final Random random = new Random();
    public String route(String transferId) {
        if (random.nextInt(100) < NEW_SERVICE_PERCENT) {
            return "NEW_SERVICE:" + transferId;
        }
        return "OLD_MONOLITH:" + transferId;
    }
    public static void main(String[] args) {
        StranglerRouter router = new StranglerRouter();
        for (int i = 0; i < 5; i++) {
            System.out.println(router.route("TXN-" + (5000 + i)));
        }
    }
}

How do you verify parity between old and new?

Run each request through both old and new logic, compare the results, and serve the trusted old result if they disagree. This protects customers while proving the new system.

JAVA
package com.bank.service;
import java.math.BigDecimal;
import java.util.logging.Logger;
public class ParityChecker {
    private static final Logger log = Logger.getLogger(ParityChecker.class.getName());
    public static void main(String[] args) {
        BigDecimal oldResult = new BigDecimal("50000.00");
        BigDecimal newResult = new BigDecimal("50000.00");
        boolean match = oldResult.compareTo(newResult) == 0;
        if (match) {
            log.info("Parity PASS: old=" + oldResult + " new=" + newResult);
        } else {
            log.warning("Parity FAIL: serving old result, blocking rollout increase");
        }
    }
}
TEXT
Parity PASS: old=50000.00 new=50000.00

How do you shift traffic gradually?

Start at a small percentage, hold until mismatches stay at zero, then increase: twenty percent, fifty percent, one hundred percent.

JAVA
package com.bank.service;
public class TrafficShift {
    public static void main(String[] args) {
        int[] phases = {5, 20, 50, 100};
        for (int pct : phases) {
            System.out.println("Phase: " + pct + "% to new service");
            System.out.println("  Gate: zero mismatches for 48 hours before next phase");
        }
    }
}

What are the real challenges in a banking migration?

Data consistency between old and new, behavioral parity (same fees, same rounding), and team coordination across multiple services.

JAVA
package com.bank.interview;
import java.util.List;
public class MigrationChallenges {
    public static void main(String[] args) {
        List<String> challenges = List.of(
            "Data consistency: old and new must agree on balances",
            "Behavioral parity: same fees, same rounding, same edge cases",
            "Team coordination: 3 teams must align on contract changes",
            "Rollback safety: can revert at any phase without data loss"
        );
        challenges.forEach(c -> System.out.println("  - " + c));
    }
}

How do you quantify the outcome?

Measure what improved: deployment time, failure rate, or latency. "Deployment time dropped from a slow monolith release to minutes" is proof.

JAVA
package com.bank.service;
import java.util.logging.Logger;
public class MigrationOutcome {
    private static final Logger log = Logger.getLogger(MigrationOutcome.class.getName());
    public static void main(String[] args) {
        log.info("Deployment time: 2 hours (monolith) -> 8 minutes (service)");
        log.info("Customer incidents during migration: 0");
        log.info("Behavioral mismatches: 3 found and fixed during canary, 0 in production");
    }
}

What is the key discipline?

Verify parity continuously. Do not trust the new system on faith. The data must prove it behaves exactly like the old one before you rely on it.

JAVA
package com.bank.interview;
public class MigrationDiscipline {
    public static void main(String[] args) {
        System.out.println("Trust is earned by data, not assumed by optimism.");
        System.out.println("Only increase rollout when parity is proven.");
        System.out.println("Always keep the fallback ready.");
    }
}

What are common interview follow-up questions?

Expect: "What would you do differently?" (start canary earlier), "How long did it take?" (give a specific timeline), and "What if parity failed?" (serve old result, fix mismatch, re-verify).

JAVA
package com.bank.interview;
public class MigrationFollowUps {
    public static void main(String[] args) {
        System.out.println("Differently? -> Start canary earlier to find mismatches sooner");
        System.out.println("How long? -> 6 weeks from first canary to 100% rollout");
        System.out.println("Parity failed? -> Serve old result, fix, re-verify, then continue");
    }
}

Visual Overview

DIAGRAM
┌────────────────────────────────────────────────────────┐
│         Incremental Migration Flow                     │
├────────────────────────────────────────────────────────┤
│                                                        │
│  ┌──────────────┐   ┌──────────────┐                   │
│  │  Old Monolith │   │  New Service  │                  │
│  └──────┬───────┘   └──────┬───────┘                   │
│         │                  │                           │
│         └────────┬─────────┘                           │
│                  │                                     │
│          ┌───────▼───────┐                             │
│          │  Router        │                            │
│          │  (% config)    │                            │
│          └───────┬───────┘                             │
│                  │                                     │
│          ┌───────▼───────┐                             │
│          │ Parity Checker │                            │
│          │ old == new?    │                            │
│          └───────┬───────┘                             │
│                  │                                     │
│     ┌────────────┼────────────┐                        │
│     ▼            ▼            ▼                        │
│  5% → 20% → 50% → 100%                               │
│  Gate: zero mismatches for 48h before next phase       │
│                                                        │
│  Fallback: revert to old at any phase                  │
└────────────────────────────────────────────────────────┘

Key Takeaway

At National Bank of India, the transfer logic for Rajesh's and Priya's accounts lived inside a large old monolith that was slow to deploy and risky to change. I drove the migration of that logic into a new dedicated transfer service. The hard part was that this code moves real customer money, so any behavioral difference between old and new could mis-handle a transfer.

My plan was an incremental, strangler-fig migration. I put a router in front that could send a small percentage of real transfers to the new service while the old monolith kept running as a fallback. For every request I ran it through both old and new logic and compared the results, so I could prove the new service produced exactly the same outcomes before relying on it.

The main challenges were behavioral parity and team coordination. When the new service disagreed with the old, the router served the trusted old result and blocked us from increasing the rollout until we fixed the discrepancy. I kept the percentage low until mismatches stayed at zero, then raised it gradually.

The outcome was that we fully migrated the transfer logic with zero customer-facing incidents, and deployment time dropped from hours to minutes. Large migrations succeed not through courage but through discipline: move incrementally, keep a fallback, verify parity continuously.

Remember one sentence: Migrate incrementally with a fallback, verify parity between old and new systems continuously, and only trust the new system once the data proves it behaves identically.

---

Share
Use ← → arrow keys to navigate