Skip to main content
Contact us
  • 9 min read

Code optimisation for legacy applications

Profile, prioritise and fix slow legacy code without a risky rewrite. See the staged approach growing Australian teams use. Get in touch.

National Digital designs, builds, hosts and supports the platforms it delivers, with no reseller or integrator between the client and its engineers.

  • Platform Engineering
  • Application Performance Optimisation
  • Legacy System Modernisation

Quick answer:Legacy code optimisation profiles real production load, fixes the highest-impact code paths first, and isolates risky modules behind stable interfaces, without a full system rewrite.

Quick answer

What does code optimisation for legacy applications involve?

High confidenceVerified

It means profiling actual execution paths, removing redundant logic and risky dependencies, and improving performance incrementally behind stable interfaces while the system keeps running in production.

Additional Context

Most legacy optimisation work fails because teams guess where the slowdown lives, skipping the profiling step that would show it under real production load.

Sources

  • Notifiable Data Breaches Scheme

    The OAIC's Notifiable Data Breaches reporting groups breach causes into malicious attack, human error and system fault, the last of which covers vulnerabilities such as those in unmonitored legacy code.

Platform Engineering

What Changes When You Optimise Legacy Code

Code optimisation for a legacy application targets the specific operations costing the business time today, identified through profiling of real transactions. That usually starts with legacy system modernisation techniques applied narrowly: removing redundant database calls, cutting synchronous waits that stack up under load, and tightening the queries behind the screens staff use most. The system keeps running the whole time, with each fix shipped on its own schedule.

Teams that have done this work repeatedly notice the same pattern: the slowest code path is rarely the one the profiler highlights first. It's often a nightly batch job or an export routine that never appears in daytime testing because nobody runs load tests against it. Finding that path requires watching the system under conditions close to how staff and customers actually use it, not a synthetic benchmark run once in a test environment.

The Trade-off Teams Consistently Underestimate

Every optimisation decision on old code trades certainty for speed. Changing a tightly coupled module directly is faster to write but risks breaking something nobody remembers depends on it. The safer, slightly slower route isolates that module behind a clean boundary first, following the same logic as a strangler fig migration pattern, so the risky internals can be replaced in stages once the interface is stable. Businesses running their trading, invoicing or fulfilment through the system in question rarely have the option of a weekend of downtime to find out which approach was right.

Platform Engineering

Legacy Code Optimisation, Without the Rewrite

Problem

Older applications slow down operations teams rely on daily, yet the system is too embedded in trading, invoicing or fulfilment to pause for a full rebuild, leaving IT managers stuck between accepting degraded performance and risking a destabilising rewrite.

Common Pain Points

  • Profiler finds nothing obvious Standard profiling tools miss batch jobs and overnight processes that never run during normal testing.
  • Fear of breaking production Nobody fully understands the old code, so teams avoid touching it even once it's clearly slow.
  • No test safety net Without regression tests, every change to legacy code risks breaking a workflow nobody remembers exists.

Business Impact

  • Time Wasted Staff time lost to manual workarounds around known slow screens
  • Cost Implication Ongoing support costs without proportional performance gains
  • Opportunity Cost Engineering capacity consumed maintaining old code instead of shipping new features

Solution

Profile the system under real load, fix the highest-impact code paths first, and wrap risky modules behind stable interfaces so each change ships safely without disrupting daily operations.

Our Approach

  1. Profile under production-like load Instrument the application to find where time and database resources actually go, using traffic patterns close to real daily use rather than a synthetic benchmark.
  2. Fix the highest-impact paths first Target the small number of modules the profiling data shows are driving most of the slowdown, before touching anything else.
  3. Wrap risky modules behind stable interfaces (Ongoing)Isolate fragile legacy code behind an API boundary so it can be improved or replaced incrementally without breaking dependent systems.

Expected Outcome

Faster response times on the workflows staff use most, with changes shipped incrementally so the business keeps trading throughout.

Key Takeaways

What Actually Moves the Needle on Legacy Code

  • Profile before you touch anything in the codebaseCritical

    Legacy slowdowns are rarely where developers assume; production-realistic profiling usually points to a different module entirely.

  • Fix the few modules carrying most of the loadImportant

    Optimisation effort concentrated on the handful of high-traffic code paths delivers more measurable improvement than a broad refactor.

  • Isolate risky code behind a stable interfaceImportant

    An API boundary around a fragile module lets teams improve or replace it later without forcing every dependent system to change at once.

  • Treat missing tests as the real blocker to progressCritical

    The absence of automated regression tests is usually what makes legacy changes feel unsafe to ship, more than the code's age itself.

Legacy code optimisation works best as a staged, measured process: profile first, fix concentrated hotspots, isolate risk behind stable interfaces, and build a test safety net before deeper changes.

Where Legacy Code Risk and Slowdowns Concentrate

These points ground legacy code optimisation decisions in documented breach-cause categories and the realistic pattern of where performance risk concentrates in ageing systems.

  • Malicious attack, human error, system fault

    Notifiable breach categories

    Significance: high

    The OAIC's Notifiable Data Breaches scheme groups breach causes into three categories, and system fault is one of them, covering vulnerabilities unresolved code paths create in unmonitored legacy applications.

    Source: OAIC Notifiable Data Breaches reporting (oaic.gov.au)

  • Few modules, high impact

    Where slow code concentrates

    Significance: medium

    Performance issues in legacy codebases cluster in a small number of high-traffic modules or batch jobs, which is why targeted profiling finds more value than a blanket refactor across the whole codebase.

  • Required before refactor

    Test coverage before refactor

    Significance: medium

    Legacy systems rarely ship with automated regression tests, so teams that skip writing characterisation tests before touching the code typically discover broken behaviour only after it reaches production.

Methodology

Methodology combines the OAIC's published breach-cause categories with general software engineering practice for profiling and staged refactoring of ageing codebases.

Implementation Approach

Measure Before You Optimise

Any performance claim about optimised code needs a baseline measured under conditions close to real use, including the latency Australian users see when traffic crosses state links or international routes. Load testing against production-like traffic is what separates a genuine fix from a change that only looks faster in a developer's environment. Database calls deserve particular attention here: a module that feels fine locally can still fall apart in database query optimisation terms once it's handling the row counts and concurrent connections of real production data.

Shipping Changes Without a Big-Bang Rewrite

Once the highest-impact code paths are identified, the safest sequence is small, independently deployable changes behind feature flags or versioned interfaces, so each release can be rolled back without touching the rest of the system. This is also where API versioning strategy earns its keep: a stable contract in front of the legacy module means the optimisation work happening behind it never forces every dependent team to redeploy on the same schedule. Measured this way, performance work on an old system ships as a sequence of small, low-risk releases that staff barely notice.

Ready to Fix What's Actually Slowing Your Legacy System?

Send us the system under real load and get the fixes with the biggest operational payoff, shipped incrementally so day-to-day trading never stops.

Common Questions About Optimising Legacy Code

How is optimising legacy code different from rewriting it?

Optimisation targets specific slow operations inside the existing system, such as a report that times out or a batch job that blocks other processing, and ships fixes incrementally while the application stays live. A rewrite replaces the whole system at once, which takes longer, costs more and risks losing functionality nobody documented. Most teams only reach for a rewrite once optimisation has clearly hit a ceiling the old architecture itself causes.

Can code optimisation happen while the application stays live in production?

Yes, and for most trading businesses it has to. The usual approach wraps the module being changed behind a stable interface, ships improvements behind feature flags, and tests each change against production-like load before it reaches every user. Staff keep using the system throughout, with changes released in small batches, each one reversible if something unexpected shows up.

How do you find the slow parts of an old codebase without good documentation?

Profiling under realistic load finds this faster than reading code, because it shows exactly where time and database calls concentrate during actual use. Combine that with transaction logs and database query statistics, and the handful of modules actually causing delays usually becomes clear within the first couple of profiling sessions, long before anyone has reverse-engineered the full system.

Is legacy code optimisation the same as application modernisation?

They overlap but aren't identical. Optimisation improves performance inside the system as it exists today. Application modernisation is the broader program that might include optimisation alongside re-platforming, API-led integration or moving services to cloud infrastructure. Many engagements start with optimisation because it delivers a measurable improvement quickly, then decide which modernisation steps are worth the investment.

What usually breaks when teams skip testing before touching legacy code?

Without automated regression tests, changes to legacy code tend to resurface old assumptions nobody remembers making, like a report that silently depended on a bug being present. The failure usually surfaces in production when a user hits it, because nothing exercised that code path during testing. Writing characterisation tests for today's behaviour before changing anything is the step most schedules compress first and regret later.

When does it make more sense to replace a legacy module than optimise it?

Optimisation stops paying off once a module's underlying architecture caps how fast it can run, no matter how much tuning goes in, or once the platform it depends on loses vendor support entirely. At that point targeted replacement, scoped to just that module and connected through a stable API, usually costs less and carries less risk than continuing to optimise code built on a foundation that's being phased out.