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.
Five sources, one verification gate
No rule reaches the specification on the strength of a single source.
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.
| Grade | Meaning | Treatment in the build |
|---|---|---|
| Confirmed | Reproduces held-back worked examples, or is corroborated by two independent sources | Build to it. Cover it with tests derived from the worked examples. |
| Inferred | Consistent with the evidence, not yet tested against held-back cases | Build to it, flag it, and get it confirmed before go-live. |
| Reported | Asserted by users, no supporting evidence found yet | Do not build to it until evidence is found. It may not exist. |
| Contested | Two sources disagree | Escalate to a named decision-maker. Do not resolve it in the analysis. |
| Known gap | An area you have established you cannot yet see into | Track 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.
Related reading
- Reverse Engineering a Legacy System That Has No Documentation covers the wider workshop-led method this sits inside.
- Is It Lawful to Reverse Engineer a System From Recordings and Screenshots? covers ownership and the personal data in recordings and extracts.
- Legacy .NET Application Assessment: A Scoring Framework scores an application before you commit to recovery work.
- Modernise, Rebuild, or Replace Legacy Systems covers the strategic decision this evidence informs.
Frequently asked questions
Can business rules be recovered without any source code at all?
Is the database a good source of business rules?
How do you verify a recovered rule is correct?
What about rules that are simply wrong?
Do we still need this if we do have the source code?
How long does business rule recovery take?
Related guides
Modernising Legacy .NET Applications with AI: The Enterprise Playbook (UK, 2026)
A structured approach to modernising legacy .NET Framework applications using AI-assisted development. Four pillars, real migration patterns, and practical guidance for enterprise teams.
.NET MAUI vs React Native: Choosing a Xamarin Migration Target
An honest comparison of .NET MAUI and React Native for teams leaving Xamarin: support lifecycles, team cost, and when each one is the wrong answer.
Xamarin & PhoneGap Are Dead: The Enterprise Mobile Migration Playbook (2026)
Both Xamarin and PhoneGap have reached end of life. This playbook covers migration paths, framework choices, and practical timelines for enterprise mobile teams.