Topic hub

Auditing Legacy Systems

An audit is a structured examination of an existing system to establish what it does, how it is built, where the risks lie and what it would take to change it. Unlike a casual review, an audit produces a written record that non-technical stakeholders can use t…

Reviewed 25 July 20261 direct guides and sections

Use this hub to

  • Understand the decision before choosing technology
  • Find related cost, risk and ownership guidance
  • Move from planning to acceptance and operation

How to Audit a Legacy Web Application

An audit is a structured examination of an existing system to establish what it does, how it is built, where the risks lie and what it would take to change it. Unlike a casual review, an audit produces a written record that non-technical stakeholders can use to make decisions about modernisation, replacement or continued operation.

Start by defining the audit's scope. Decide whether you are examining the entire application or a specific module, and be explicit about what is out of scope. An unfocused audit generates large volumes of findings but no clear priorities.

Access is the first practical hurdle. You will need read-only access to the source-code repository, the production database schema (not necessarily live data), server configurations, deployment logs and any existing documentation. If the current supplier is still involved, agree in writing what access they will provide and over what timeframe. If access is refused or delayed, that finding itself is significant.

Involve the right people. A developer with experience of the technology stack should perform the technical review. A business analyst or operations manager should contribute the process perspective: what the system is supposed to do, where it falls short and which workflows depend on it. Keep the group small enough to move quickly.

Structure the audit around a fixed set of areas: codebase quality, documentation, security, technical debt, dependencies and database health. Examine each area using the same format: what you found, what it means for the business, and how urgent the finding is. This consistency makes the final report far easier to act on.

What to Look for in a Legacy Codebase

The codebase is the most detailed evidence of how the system has been built and maintained. When you open it, you are looking for signals about the original team's competence, the pace of subsequent changes and the degree to which quality was preserved or abandoned.

Framework and language versions

Check which versions of the programming language, framework and major libraries are in use. If the framework is several major versions behind the current release and no longer receives security patches, that is a concrete risk, not a stylistic preference. Note whether upgrading is a minor adjustment or would require rewriting significant portions of the application.

Code organisation and consistency

Look at how the code is structured. Are files and folders arranged according to the framework's conventions, or has a custom structure grown without clear logic? Inconsistent naming, mixed coding styles and duplicated logic across files suggest multiple developers worked without shared standards. This makes future changes slower and more error-prone because nobody can reliably predict where a piece of logic lives.

Test coverage

Determine whether automated tests exist and, if so, what they actually cover. A large test suite that only checks low-level utility functions while ignoring the core business workflows gives a false sense of safety. Look for integration tests that verify real user processes end to end. If tests are absent entirely, any future change carries higher risk because there is no automated way to confirm nothing else broke.

Signs of rushed work

Hardcoded values that should be configuration, commented-out blocks of old code, large functions that try to do many things at once and files that run to thousands of lines are all indicators that changes were made under pressure. These patterns do not necessarily mean the system is about to fail, but they do mean that the next person to modify the code will need more time to understand it.

Assessing Legacy System Documentation

Documentation in a legacy system often falls into one of three categories: missing, outdated or accurate. Accurate documentation is the least common. Treat whatever you find as a starting point for verification, not a reliable description of the current state.

Begin with architectural documentation. Is there a diagram showing how the application's components fit together, where data flows and where external integrations connect? If this exists, check it against the actual codebase and infrastructure. Discrepancies between the diagram and reality tell you which parts of the system have evolved without the documentation being updated.

Process documentation is often more valuable to the business than technical architecture. Look for descriptions of the workflows the system supports: who does what, in what order, and what happens when something goes wrong. If this was never written down, the knowledge lives only in the heads of current users, which is a business continuity risk if those people leave.

Deployment and operations documentation matters for day-to-day reliability. Check whether there is a clear, repeatable process for deploying changes, rolling back a failed release and recovering from a database issue. If the only person who knows how to deploy is a single developer, that is a dependency worth recording in the audit findings.

When documentation is missing, do not treat it as a simple gap to be filled later. The absence of documentation is itself a finding: it means every future change will take longer, onboarding new people will be harder and the risk of accidental breakage is higher.

Legacy System Security Assessment

Security in a legacy system requires particular attention because older code was often written before current best practices were established, and the system may have been running for years without a formal review. This section of the audit is not a substitute for a professional penetration test; it identifies areas that warrant specialist investigation.

Authentication and session handling

Examine how users log in and how their sessions are managed. Look for password hashing: if passwords are stored in plain text or using an outdated algorithm like MD5, that is a critical finding regardless of whether the system is internal or customer-facing. Check session token handling, timeout behaviour and whether concurrent sessions are controlled. If the system supports single sign-on, verify how tokens are validated and what happens if the identity provider is unavailable.

Input handling and output encoding

Check whether user-supplied data is validated and sanitised before use, and whether output is properly encoded to prevent cross-site scripting. In older applications, these protections were often applied inconsistently or omitted entirely. Look for direct SQL string concatenation rather than parameterised queries, which indicates SQL injection risk.

Dependency vulnerabilities

Run a dependency check against the application's library list. Automated tools can compare installed versions against known vulnerability databases. Pay attention to the severity ratings, but also consider whether the vulnerable library is actually reachable from user-facing code. A vulnerable library that is installed but never called is a lower priority than one that processes every request.

Data protection considerations

Note where personal data is stored, how it is encrypted at rest and in transit, and whether access is restricted according to the principle of least privilege. If the system handles data subject to UK GDPR, document any areas where the current implementation may not meet current expectations. Security and privacy compliance require specialist legal and technical review; the audit's role is to flag where that review should focus.

Identifying Technical Debt in a Legacy Application

Technical debt is the cumulative cost of choosing faster, shorter-term approaches over more sustainable ones. In a legacy system, that debt has usually been compounding for years. The audit's job is not to catalogue every imperfection but to identify debt that materially affects the business's ability to operate, change or secure the system.

Debt that blocks change

The most consequential technical debt is the kind that makes future work expensive or unpredictable. Tightly coupled components, where changing one module unexpectedly breaks another, fall into this category. If the business wants to add a new feature and the development team estimates a disproportionately large amount of time because of the need to untangle existing code, that is debt with a measurable cost.

Debt that affects reliability

Look for patterns that increase the likelihood of failures in production. Shared state between concurrent users, missing error handling around external service calls, and database operations that are not wrapped in transactions all fall into this group. These issues may not cause daily problems, but they create conditions where failures become more likely as load or complexity increases.

Debt that obscures understanding

Some debt does not cause immediate failures but makes the system progressively harder to work with. Poorly named variables and functions, missing abstractions and business logic embedded in user-interface code all slow down anyone who needs to understand or modify the system. This debt is harder to quantify but directly affects the cost and risk of every future change.

When recording technical debt, attach each item to a concrete consequence. "The code is messy" is not a useful finding. "Adding a new customer status requires changes in fourteen files with no tests to confirm correctness, and the last similar change introduced a bug that took three days to diagnose" is a finding that supports a decision.

How to Quantify Legacy System Risk

An audit that only produces a list of problems without ranking them is difficult to act on. Quantifying risk means connecting each finding to a business consequence and an estimate of likelihood, so that stakeholders can make informed decisions about priorities and budgets.

Define impact categories

Group findings by the type of business impact they could cause: operational disruption, data loss, regulatory exposure, inability to deliver planned changes and increased running costs. A single finding might fall into more than one category. An unpatched framework, for example, could cause operational disruption if exploited and regulatory exposure if personal data is compromised.

Assess likelihood honestly

For each finding, consider how likely it is to cause a problem. An internal system with no public-facing interface and network-level access controls presents a different risk profile from a customer portal on the open internet. A database with no automated backups is a high-likelihood risk because hardware failure is not a rare event. A sophisticated attack exploiting an obscure vulnerability in a library that is only reachable from an admin panel is lower likelihood, though still worth recording.

Estimate the cost of inaction

Where possible, express risk in terms the business understands. If the system goes down for a day, what is the cost in lost revenue, delayed orders or manual workarounds? If a data breach occurs, what are the potential regulatory, legal and reputational consequences? If the system cannot be modified to support a planned business change, what is the revenue impact of that delay? These figures do not need to be precise to be useful; they need to be honest enough to support a comparison between the cost of fixing the problem and the cost of accepting it.

Present findings as a prioritised register

Structure the output as a table or register with columns for the finding, its impact category, likelihood, estimated cost of inaction and a suggested priority. This format lets different stakeholders filter for what matters to them: finance can focus on cost, operations on disruption, compliance on regulatory exposure. Avoid aggregating everything into a single risk score, because that obscures the specific reasons for acting.

Dependency Mapping for Legacy Applications

A legacy system rarely operates in isolation. It depends on other software, services, infrastructure and people. Mapping these dependencies is essential because a risk in a dependency is a risk in your system, even if your own code is well-maintained.

Software and library dependencies

Start with the application's direct dependencies: the framework, libraries and packages it imports. For each one, note the version in use, whether that version is still supported, and whether a major upgrade would require code changes. Then consider indirect dependencies: the libraries that your libraries depend on. Automated dependency-analysis tools can help here, but verify the output manually for anything flagged as critical.

External service dependencies

List every external service the application communicates with: payment processors, email delivery services, third-party APIs, authentication providers and any SaaS tools it integrates with. For each, record what happens if that service becomes unavailable. Does the application fail gracefully with a useful error message, or does it crash? Are there fallback mechanisms, and have they been tested? Check the contractual terms for each service, particularly around uptime commitments and notice periods for changes or discontinuation.

Infrastructure dependencies

Document the server environment, operating system version, database engine version and any middleware. Note whether the infrastructure is approaching end of life. If the application runs on a specific operating system version that will lose vendor support, that creates a timeline pressure that is independent of the application's own condition.

People and knowledge dependencies

Identify the individuals who hold critical knowledge about the system: how to deploy it, how to troubleshoot specific failure modes, where the undocumented business rules live. If a single person's departure would leave the business unable to operate or safely modify the system, that is a dependency risk that belongs in the audit register alongside the technical findings.

How to Assess Legacy Database Health

The database is often the most valuable and most fragile part of a legacy system. It holds the business's historical record, and its structure constrains what the application can do. Assessing its health requires looking at schema design, data quality, performance characteristics and the practicalities of future migration.

Schema design and integrity

Examine the table structure for signs of poor normalisation: repeated column groups across tables, data stored in comma-separated strings instead of related rows, and tables that serve multiple unrelated purposes. Check whether foreign-key constraints are enforced at the database level or only in application code. Missing constraints mean the database cannot protect itself from inconsistent data, which becomes a significant risk if data is ever modified outside the main application, such as during a migration.

Data quality

Look for evidence of data quality issues: duplicate records, orphaned rows that reference non-existent parent records, inconsistent formatting in fields that should follow a standard, and records with critical fields left empty. Run targeted queries to quantify the extent of these issues rather than relying on anecdotal reports. A statement like "approximately 4% of customer records have a missing postcode" is more useful than "the data is a bit messy."

Performance indicators

Check for missing indexes on columns that are frequently used in query filters or joins. Look for tables that have grown very large without any partitioning or archiving strategy. Examine slow-query logs if available. Performance problems that are manageable at current data volumes may become critical as the business grows, so note the growth trajectory as well as the current state.

Migration considerations

Record the current state and unresolved risks

Even if the current plan is not to migrate, the audit should note what a future migration would involve. Are there stored procedures or triggers that contain business logic not replicated in the application code? Is the database using features specific to one vendor's engine that would not transfer easily to another? Is the data volume and structure such that a migration would require significant downtime, or could it be done incrementally? These observations feed directly into the modernisation options that will be considered after the audit.