Skip to content
Legacy Modernisation

Recovering Business Rules When You Do Not Have the Source Code

12 min read

When the source code has gone, the business rules have not. They are still visible in the system’s behaviour, in its outputs, in the data it has accumulated, and in the heads of the people who use it. Recovering them is an evidence exercise, not a decompilation exercise.

  • Five independent sources of evidence: the people, the screens, the outputs, the data, and controlled probing of the live system.
  • Verify against real worked examples, never against somebody’s description of the rule.
  • Grade every recovered rule by confidence, and track coverage so you know what you have not found.
  • Expect to find rules that are wrong. Record the implemented behaviour and the corrected behaviour separately, and make it a client decision.
  • The rules that get missed are the rare ones, so hunt for them deliberately rather than waiting for them to appear.

This work sits inside our legacy application modernisation service.

Why this is a different problem from recovering requirements

Recovering what a system does at the level of screens, workflows, and data is one exercise. Recovering the rules encoded inside it is another, and it is considerably less forgiving.

The difference is tolerance for error. If you misunderstand a screen layout, the replacement looks wrong and somebody says so in the first demonstration. If you misunderstand a pricing rule, the replacement produces plausible figures that are quietly incorrect, and nobody notices until an invoice run or a regulatory return. Rules fail silently.

That asymmetry drives everything below. The method for recovering business rules is more evidential, more sceptical, and slower per item than the workshop-led approach described in our guide to reverse engineering a legacy system that has no documentation. That guide covers the wider recovery of pain points, information architecture, and the conceptual data model. This one covers the calculations and conditions that sit underneath.

The standard advice on business rule extraction assumes you have source code to analyse, and most published tooling is built around mainframe and COBOL estates. That assumption fails in exactly the situations where the need is most urgent:

  • The source has been lost, or exists only as a version nobody can confirm matches production.
  • The build environment no longer exists, so you cannot prove what the deployed binary was built from.
  • The system was delivered compiled, and the contract never included source.
  • The source exists and is readable, but is so heavily patched that reading it tells you what happens without telling you which parts still matter.

Where the rules actually are

There are five independent sources. Independence is the point: a rule confirmed by two sources that cannot both be wrong in the same way is worth far more than a rule asserted confidently by one.

The people

The operators know the exceptions. They will tell you that trade customers are billed differently, that a discount applies below a certain volume, and that something specific happens at the end of the financial year.

What they are reliable about is that a rule exists and roughly why. What they are unreliable about is the exact threshold, the rounding, and the boundary condition. People remember the rule as they understand it, which is often the rule as it was explained to them years ago. Treat their input as a map of where to look, not as the specification.

The screens

The interface enforces a great deal: mandatory fields, formats, ranges, dependencies between fields, and options that appear only in certain states.

This is why screen captures earn their place. A field that rejects a value, a control that greys out, and an error message that names a limit are all direct evidence of a rule. Error messages in particular are worth harvesting systematically, because a message such as “cannot exceed the remaining allowance” states a rule outright.

The outputs

Invoices, statements, remittances, reports, and exports are the single richest source for calculations, and the most commonly overlooked.

An invoice is a worked example that the system produced itself. It shows the inputs, the intermediate figures, and the result. A batch of a few hundred real invoices spanning different customer types and periods lets you infer and then test a pricing model without seeing a line of code. Where outputs feed a regulator or a payment scheme, the format itself encodes further rules.

The data

The database is a record of every rule the system has enforced over its life, and profiling it is cheap.

Look for the shape of the data rather than its content:

  • Columns that have never been null, indicating a mandatory rule that may not be declared anywhere.
  • Values that never fall outside a range, indicating a validation.
  • Combinations that never co-occur, indicating a constraint.
  • Distributions that change sharply at a date, indicating a rule that was introduced or altered then.

That last one matters more than it first appears, and it is covered again below.

Controlled probing

Where you can run the system in a safe environment, put known inputs through it and record what comes out. This is the only source that lets you ask a question the historical data does not answer.

Probing is how you pin down boundaries. If a discount applies above a threshold, the historical data may only tell you the threshold is somewhere between two values. Ten deliberate transactions will tell you exactly where it sits, and whether the boundary is inclusive.

Two cautions. Never probe production, because you will create real records and real side effects such as emails, payments, or ledger entries. And confirm the environment you are probing is genuinely representative, since a test environment three configuration versions behind production will confidently teach you rules that no longer apply.

Verification: worked examples, not agreement

This is the discipline that separates a specification you can build from a plausible document.

Derive from one set of cases, verify against another. Hold back a portion of your worked examples. A rule that reproduces cases it was not derived from is confirmed. A rule that only reproduces the cases used to build it has been fitted, not discovered.

Never verify by asking whether a description sounds right. People agree with descriptions of rules they believe are in force. The system applies the rule that is implemented. Those diverge more often than anyone expects, and the divergence is precisely what you are trying to find.

Weight the awkward cases. Ask explicitly for the refunds, the part-periods, the cancellations mid-cycle, the accounts that were migrated from the previous system, and the ones the finance team describes as unusual. Ordinary cases confirm the rule you already guessed. Awkward cases find the conditions you have missed.

Be careful with generated rules. Where AI assists in inferring a rule from a batch of examples, it will produce a fluent statement of a rule that fits the data it was shown. That is a hypothesis, and a good one. It is not evidence, and it must go through the same gate as any other candidate.

Grade confidence, and track what you have not found

A rule register that lists rules without qualifying them hides the risk. Grade each entry.

GradeMeaningTreatment in the build
ConfirmedReproduces held-back worked examples, or is corroborated by two independent sourcesBuild to it. Cover it with tests derived from the worked examples.
InferredConsistent with the evidence, not yet tested against held-back casesBuild to it, flag it, and get it confirmed before go-live.
ReportedAsserted by users, no supporting evidence found yetDo not build to it until evidence is found. It may not exist.
ContestedTwo sources disagreeEscalate to a named decision-maker. Do not resolve it in the analysis.
Known gapAn area you have established you cannot yet see intoTrack explicitly. This is the honest record of residual risk.

The last row is the one teams leave out, and it is the most valuable. A register with no gaps is not a register of a fully understood system. It is a register that has stopped looking.

Track coverage against the domain rather than against your own list. Work through the entity model and the functional areas and ask what rules ought to exist for each. An area with no rules recorded against it is a prompt, not a result.

The rules that get missed

Predictable categories, worth hunting deliberately.

Rules that fire rarely. Year-end processes, leap-year handling, annual uprating, and dormancy rules may not appear in a month of observed behaviour. Look at a year of data, not a week.

Rules with a date boundary. Long-lived systems accumulate rules that changed on a date, with the old behaviour retained for older records. Recovering only the current rule breaks anything that touches history. This is why a distribution that shifts sharply at a date is worth investigating.

Rules implemented outside the application. Scheduled jobs, database triggers, spreadsheets, and integration middleware all enforce rules that never appear on a screen. Ask what runs overnight, and ask what finance does at month end.

Rules nobody has triggered for years. A credit limit that no customer has approached still exists and still needs to exist. Absence of evidence in the data is not evidence of absence.

Rules that are actually defects. A calculation that has been subtly wrong for years, with the business correcting the output by hand, is a rule as implemented and a defect as experienced. Record both, then make it an explicit client decision. Silently correcting it in the replacement produces figures that no longer reconcile with history. That is a conversation to have before the build, not after the first reconciliation.

How this feeds the build

The register is not the deliverable. What the build team needs is the register turned into executable checks.

Every confirmed rule should arrive with the worked examples that confirmed it, and those examples should become test cases in the new system. That is the mechanism that carries the recovered understanding into the replacement. It also gives you something a document cannot: proof that the new system reproduces the old system’s outputs on real historical cases, before anybody depends on it.

Two further practices are worth building in.

Run the two systems in parallel on real data where the domain allows it, comparing outputs and investigating every divergence. Each divergence is either a defect in the new system or a rule you did not recover. Both are worth finding before cutover.

Keep the register after go-live. It is the documentation the original system never had, and it is the artefact that stops the replacement becoming the next undocumented legacy system.

Frequently asked questions

Can business rules be recovered without any source code at all?
Most of them, yes, but not all, and the difference matters. Rules that fire on every transaction are straightforward to recover from observed behaviour and worked examples. Rules that fire rarely, such as annual processes, error handling for conditions that seldom arise, or thresholds nobody has crossed for years, are the ones that get missed. The goal is not perfect recovery. It is knowing which rules you have confirmed, which you have inferred, and which you know are still missing.
Is the database a good source of business rules?
It is the best single source after the people, and it is routinely underused. Constraints, defaults, triggers, and stored procedures encode real rules. More usefully, the data itself is a record of every rule the system has ever enforced. If a column has never held a negative value across two million rows, something prevented it. Profiling the data surfaces candidate rules that nobody thought to mention.
How do you verify a recovered rule is correct?
Against worked examples, not against descriptions. Ask the system, or the people who use it, to produce real cases with real inputs and real outputs, including awkward ones. A rule is confirmed when it reproduces the outputs of cases it was not derived from. A description of a rule that somebody agrees with is not verification, because people describe the rule they believe is in force rather than the one the system actually applies.
What about rules that are simply wrong?
You will find them, and they need a decision rather than a transcription. A legacy system often contains calculations that have been subtly incorrect for years, with the business quietly correcting the output downstream. Record the rule as it is actually implemented, record the corrected behaviour separately, and make the divergence an explicit decision for the client. Silently fixing it during a rebuild produces figures that no longer reconcile with history.
Do we still need this if we do have the source code?
Usually yes, as a cross-check rather than as the primary method. Source tells you what the code does, which is not the same as which rules are load-bearing, which are dead, and which are actively wrong. Where source exists it is the best evidence for calculations and edge cases, and it should be used first for those. It remains poor evidence for intent.
How long does business rule recovery take?
It depends almost entirely on the volume of calculation in the domain rather than on the size of the codebase. A workflow-heavy system with little arithmetic can be covered quickly. A billing, pricing, or regulatory calculation engine takes considerably longer, because every rule needs verification against worked examples and that verification is human work that does not compress.

Ready to transform your software?

Let's talk about your project. Contact us for a free consultation and see how we can deliver a business-critical solution at startup speed.