INSIGHTS / MODERNIZATION

Modernizing software without starting over.

A difficult codebase can still contain years of operational knowledge. Replacing it all at once means rediscovering rules, exceptions, and integrations. Before choosing a rewrite, separate the problem you can observe from the solution you have in mind.

1. Start with an observable problem

“The system is old” does not establish a priority. Does checkout take too long? Does a small change affect several screens? Does deployment need manual intervention? Pick a workflow and record its current behavior: response time, failures, change effort, or recovery time. This baseline gives you a way to tell whether the intervention helped.

2. Map what must keep running

List API consumers, scheduled work, external integrations, and exceptional business rules. Observe logs without unnecessarily recording sensitive data. Talk to the people operating the system. A database may serve more applications than the documentation suggests; changing a column can break a process nobody mentioned in the first meeting.

3. Add protection before making changes

Characterization tests capture the current behavior of a component even when its implementation is imperfect. Prioritize financial rules, permissions, and workflows that are hard to recover. Separate required behavior from known bugs so a defect does not become a requirement. A representative, appropriately protected environment helps rehearse the change.

4. Choose a small boundary

An isolated integration, a screen with clear responsibilities, or a background job may be a better first step than replacing the architecture. Define a contract between old and new components. If both versions coexist, decide which one writes each piece of data and how consistency is maintained. Dual writes without reconciliation add another problem.

5. Plan recovery before release

Gradual rollouts and feature flags can limit exposure, but they need clear stop conditions. Data migrations need their own plan: reverting application code does not necessarily undo a database change. Favor compatible changes, such as adding a field before removing its predecessor, and rehearse restoration when it is part of the recovery strategy.

6. Compare, document, and clean up

After release, compare the workflow against the baseline. Watch errors, latency, and operational effects over a period that reflects real usage. Remove the old path only once the transition is stable. Update documentation, delete temporary flags, and record dependencies that the next stage still needs to address.

When a rewrite can be reasonable

A structural constraint may change the calculation: an unsupported dependency, an essential requirement the system cannot meet, or adaptation costs that exceed a bounded replacement. Even then, a small technical prototype and a data migration plan help assess risk. Compare the full cost of maintaining, adapting, and replacing, including training, parallel operation, and future maintenance.

What to bring to an initial conversation

Bring a concrete example of the problem, who relies on the system, how it is deployed, and the integrations you know about. You do not need a new architecture already designed. A useful first outcome may be an assessment with priorities, risks, and an initial change small enough to validate.

Backend and integrations for existing systems

LET’S TALK ABOUT YOUR PROJECT

What would you
like to build?

Send us an idea, a question, or a few words about what isn’t working today. That’s enough to get the conversation started.