Skip to content
Part IA Easter Term

Lehman's Laws and Legacy Code

Lehman’s Laws of Software Evolution

Software is never “finished.” The vast majority of its lifetime is spent in evolution — maintenance, adaptation, and extension. Meir Lehman formulated a set of empirical laws describing how real-world E-type (embedded in the real world) software systems evolve:

Continuing Change

A system must be continually adapted, or it becomes progressively less satisfactory.

The environment around a software system changes continuously: tax regulations are amended, competitors add features that raise user expectations, security threats evolve, hardware architectures shift. A system frozen in place becomes less useful (and less secure) with every passing month, even if its own source code is untouched. The system’s fitness relative to its environment degrades.

Increasing Complexity

As a system evolves, its complexity increases — unless active work is done to maintain or reduce it.

Every modification adds new code, new dependencies, new interactions. Without deliberate refactoring, the system’s architecture drifts: a clean modular design becomes a tangled web over a decade of patches and additions. Entropy increases by default; reducing it requires energy (engineer effort).

Self-Regulation

Global evolution processes are self-regulating, with statistically determinable trends and invariants.

At the scale of an organisation or an industry, the rate of growth, the distribution of release sizes, and the rate of defect introduction follow predictable statistical patterns. Individual projects vary wildly, but the aggregate is stable.

The Four Types of Maintenance (with Effort Share)

This is a reprise from Lecture 1, now with typical percentages from empirical studies:

TypeTypical shareDescription
Perfective~50%Improving performance, maintainability, readability, or adding new features that users demand. The system is not broken — it needs to be better.
Adaptive~25%Updating the system to work in a changed environment: new OS version, cloud migration, new tax-regulation API, TLS 1.3 compliance.
Corrective~20%Fixing bugs discovered after deployment.
Preventive~5%Finding and fixing latent problems before they become actual bugs — refactoring a fragile module before it causes an incident.

The headline: most maintenance is perfective (keeping the system relevant), not corrective (fixing breakage). Software is maintained because the world changes, not because it was built incorrectly.

Legacy Code

Legacy code is code without tests, or code we are afraid to modify.

The colloquial definition — “code inherited from someone else” — captures the surface, but the deeper definition is about testability and modifiability. A system written last month, with no tests, is legacy code. A system written in 1975, with a comprehensive test suite and clean architecture, is not.

Legacy code is the foundation of most profitable businesses. The original authors may have long since retired; the language (COBOL, Fortran) may have few living experts; but the system processes billions of pounds of transactions daily and cannot simply be turned off.

The COBOL Pandemic Crisis (2020)

During the COVID-19 pandemic, unemployment systems in several US states (New Jersey, Kansas, others) crashed under unprecedented load. The systems were written in COBOL in the 1970s — a language almost no living programmer under 60 years old knows. State governors went on television to recruit retired COBOL programmers as volunteers.

Lesson: legacy code is often the most business-critical code an organisation owns. It is also the least understood, the least tested, and the hardest to change — precisely the combination that produces catastrophes under stress.

Software Archaeology

When you inherit a legacy system with no documentation, no tests, and no surviving original authors, you must reconstruct understanding from the only source that is always available: the source code itself.

Tools and techniques:

Tool / TechniquePurpose
git blameSee who last modified each line, and when. Trace back through the change history to understand the evolution of a function.
git log -S "functionName"Search the entire commit history for when a specific string or function was added, modified, or removed. Recover intent from the commit message that introduced it.
Linked issue-tracker IDsCommit messages like Fixes #123 or PROJ-456 link code changes to business context — the bug report or feature request that motivated the change.
Characterisation (Golden Master) TestsIf you have no tests and no specification, record the system’s current output for a given input. Assert that output stays identical after your changes. You are testing for consistency, not correctness.

Key insight: software archaeology is about recovering intent, not just behaviour. Why was this sleep(500) statement added? What bug did it work around? Without understanding intent, you risk removing a hack that prevented a catastrophic failure under a specific, rare condition — which may re-surface months later in production.

The High Cost of Reading

Engineers spend far more time reading code than writing it (estimates range from 10:1 to 20:1). Understanding the side effects of a 500-line function, building a mental map of data flow you did not design, and tracing an edge-case bug across six modules written by four different teams — this is the expensive part of maintenance. Every minute invested in readability and clear structure during initial development returns itself many times over during the system’s decades of maintenance.

Beware tribal knowledge: the implicit understanding that lives in engineers’ heads but is never captured in the system. When those engineers leave, the knowledge evaporates. Comments are cheap; the cost of losing critical undocumented context can be enormous.

Expected Learning

  • State at least two of Lehman’s Laws and explain their implications for long-lived systems.
  • List the four types of maintenance with their typical effort shares, and classify a given maintenance task (e.g. migrating from on-premises to cloud → Adaptive).
  • Define legacy code in terms of testability and confidence, and explain why it is economically significant.
  • Describe the pandemic COBOL case study and the lesson it teaches about legacy systems.
  • Describe software archaeology — its tools, techniques, and goal of recovering intent.
  • Explain why the high cost of reading code makes readability an economic, not aesthetic, concern.

Past Paper Questions

Example Sheet 2, Question 5 tests the four maintenance types (classify given tasks) and asks you to explain why freezing a successful 20-year-old system is impossible, using Lehman’s Laws. Example Sheet 2, Question 8(a) directly tests software archaeology techniques.