Client work Reflections

A new language needs a new design

Legacy banking modernization

By Ted Cao

What to examine

Code can translate successfully while its assumptions do not. Configuration loading, concurrency and dependencies may behave differently in the target runtime, changing how a banking service handles real requests.

Approach

Separate business behavior that must remain from implementation patterns that need redesign. Approve the target and transition design, then deliver small slices with explicit acceptance criteria and tests linked to the original rules.

Lesson

Preserve the intended behavior; make the new runtime’s design decisions explicit.

Define what must survive the move

The starting point is an approved account of business behavior: the decisions the service makes, the inputs it relies on, the exceptions it preserves, and the contracts other systems expect. That account should be separate from the old language and framework. Otherwise a translation can faithfully reproduce an obsolete structure while still losing a rule hidden in configuration.

The design review asks two different questions. Which outcomes must remain equivalent for customers and operations? Which implementation choices should change to make the target system maintainable? AI can draft a mapping between old and new code, but it cannot decide the business intent or accept the risk of changing it.

Expose the runtime mismatch

A source system may read centralized configuration at startup while the target reads environment or service configuration differently. Threading, shared state, blocking calls, error propagation, and dependency assembly can also change across runtimes. A line-by-line port hides these decisions inside apparently successful code.

For each pattern, write down how the old system behaves and what the target will do instead. Review configuration refresh, concurrent requests, retry behavior, failure isolation, and resource ownership at the design level. The architect or technical lead should approve those choices before a task is handed to a coding assistant. A generated test passing against an unreviewed design proves very little.

Design the transition as well as the destination

The new service is unlikely to replace every dependency at once. During a gradual migration, old and new components may coexist, share data, or call each other. The transitional design should identify which system owns each decision and record how requests and data cross the boundary. It also needs a reconciliation method and an answer for what happens when the two systems disagree.

Incremental replacement provides a planning discipline: move one bounded capability, compare its behavior, and expand only after the team understands the operational consequences. The design must also account for the temporary cost and complexity of running two systems together.

Give each slice a testable contract

After the target and coexistence plans are approved, divide the work into units with clear acceptance criteria. Each unit should point back to the verified rule, interface design, and relevant failure cases. A narrow task gives an AI assistant enough context to draft code and unit tests without forcing it to infer the entire domain from a large repository.

Developers still review the pull request for correctness, edge cases, and defensive behavior. The review should cover what the code assumes about configuration and external services, not only whether its syntax is valid. A small change is easier to reason about, but smallness alone does not make it safe.

Classify differences before deciding

Compare each slice with the agreed scenarios and the legacy test bed. Differences can be required behavior, an intentional policy change, a known defect in the old system, an environment mismatch, a mistaken requirement, or a new implementation defect. Recording these categories prevents the team from treating every mismatch as either a regression or an improvement.

Business and technical owners should decide the disposition of consequential differences. If the approved rule was wrong, return to the rule record; if the design was wrong, revise the architecture; if the implementation was wrong, correct the slice. That traceable route back is the reason to validate in stages instead of waiting for a final release test.

Prepare the operational decision

Before moving live traffic, the team needs evidence about monitoring, fallback, data consistency, and the conditions that would trigger a pause or rollback. Transfer and account flows demand particular care because a technical success can still leave a business reconciliation problem. A release plan should name who can make the go-live and backout decisions and how exceptions will be handled.

The modernization is ready to expand only when the replacement's behavior is understood within the slice's approved scope. A new language is an opportunity to improve design. It is not, by itself, proof that the service expresses the right rule or can be operated safely.

Adapted from Human–AI Collaborative SDLC for Legacy Banking Modernization.

Where this work lives

Related capabilities and industries

All client work

Let’s talk about your next big initiative

Tell us about your project scope, modernization goals, or delivery team needs.

Let’s talk