Skip to content
Aug 14, 2026·8 min read

Legacy system knowledge after a rewrite

Preserve legacy system knowledge by turning expert judgment, production cases, and operating practice into tests, decisions, and durable ownership.

Legacy system knowledge after a rewrite

The people who understand an old system are not a temporary inconvenience on the way to cleaner code. They are part of the system's current operating model. A rewrite that ignores what they know can reproduce every visible screen and still fail on the first unusual refund, quarter-end adjustment, or batch restart.

The useful goal is not to "download" someone's brain before they retire. It is to turn claims about behaviour into evidence that another person can inspect: examples, tests, decision records, operating procedures, and named uncertainties. That work also gives the experts a credible role after cutover. Treat them as witnesses and reviewers, not as obstacles or human specification documents.

The old system includes its people

Legacy system knowledge lives in several places at once. Some sits in source code and job definitions. Some appears in production data, runbooks, ticket history, and reconciliation spreadsheets. The rest lives in the people who know that a customer status of 7 means something different before month-end, or that a failed batch must restart from the third checkpoint because the first two steps are not idempotent.

Calling all of that "tribal knowledge" is too vague to be useful. I separate it into four kinds because each needs different evidence:

  • Business rules: what outcome the organization intends for a given case.
  • Observed behaviour: what the current system actually does, including defects that callers depend on.
  • Operational practice: how people schedule, recover, reconcile, and override the system.
  • Historical rationale: why a rule, table, or workaround exists and what might break if it disappears.

Those categories often conflict. A subject matter expert may describe the approved rule while production follows an old exception. An operator may have a safe recovery procedure that no developer knows. A finance manager may call a rounding discrepancy a bug while a downstream report relies on it. The rewrite team must preserve the distinction long enough to make an explicit decision.

This is why an org chart is a poor map of system knowledge. The most informative person may be a support analyst who can predict which input causes a nightly job to stall, or a former developer who now works in operations. Start with system events, not titles: who gets called when billing fails, who signs off reconciliation, who understands rejected records, and who knows why an apparently unused export still runs?

People are also fallible. Expertise deserves respect, but memory is not proof. The old system's people provide hypotheses, examples, and context. Tests and records turn those contributions into something the migration can rely on.

Interviews need cases, not tours

The fastest way to waste an expert's time is to ask, "How does the system work?" You will get a tour of menus and the happy path. Neither reveals the conditions that caused the last ten production incidents.

Run interviews around concrete cases. Ask the person to bring a completed transaction, a rejected transaction, a manual adjustment, a restart, and an output that required reconciliation. Then have them replay what they saw, what they expected, what they changed, and how they knew the result was acceptable. Screen recordings can help, but the written record must capture inputs and decisions, not just clicks.

I use a small claim ledger during these sessions. Each row has a claim, a source, a representative example, a proposed check, an owner, and a confidence level. A row might read: "A suspended account may receive a credit but not a debit," sourced from the collections lead, with two transaction IDs and a proposed boundary test. Another might say: "Operators restart JOB17 at STEP30 after a timeout," sourced from the night operator, with the relevant run log and a recovery test still missing.

The confidence field prevents polite conversation from hardening into false certainty. Use plain labels such as confirmed by traffic, confirmed by repeatable test, supported by two people, single recollection, and disputed. Confidence describes evidence, not seniority.

Ask awkward follow-ups. What do you do outside the system? Which fields do you never trust? What do you check before approving the output? Which error message means "try again" even though it sounds fatal? What happened the last time this rule changed? Who disagrees with your version? These questions uncover shadow spreadsheets, phone approvals, and compensating controls that a code scan cannot see.

Keep sessions short enough that experts can remain precise. Send the resulting claims back for correction within a day or two, while the examples are still familiar. A transcript is raw material, not documentation. Someone must resolve names, attach evidence, and split compound claims before the session produces migration assets.

Separate intent from compatibility

A rewrite must distinguish intended policy from compatibility behaviour, because preserving either one by accident creates expensive surprises. Teams routinely blur "the business wants this" with "the old program does this." Those statements can point in opposite directions.

For every behaviour that matters, record three answers: what the old system does, what the organization wants after the rewrite, and what existing callers or reports expect. Then assign a disposition. Preserve means the new system must match. Correct means the new system intentionally differs and needs an approved expectation. Retire means the behaviour and its consumers go away together. Unknown means cutover cannot safely depend on it yet.

Consider a billing program that rounds each line before adding the invoice. The written policy says to add unrounded lines and round the total. Production customers may have received line-rounded invoices for years, while a general ledger import expects the resulting pennies. "Fixing" the calculation inside the rewrite can break reconciliation even when the new answer is mathematically preferable.

The decision cannot hide in a developer's pull request. Finance should choose whether to preserve the result, adjust the ledger interface, or introduce the corrected rule on a stated boundary. The parity suite then encodes the chosen outcome. Documentation records why the old and new results differ. Support receives an example they can recognize.

Michael Feathers, in Working Effectively with Legacy Code, uses tests to put existing behaviour under control before changing it. That idea applies beyond source code. A characterization test tells you what happens now. It does not declare that the behaviour is correct. Migration governance begins where characterization stops: a person with authority decides what becomes a contract.

This distinction also prevents experts from carrying unfair responsibility. The person who remembers a workaround should not have to decide alone whether it survives. Their job is to expose the behaviour and its consequence. The accountable business and engineering owners decide its future in writing.

Turn statements into executable examples

Knowledge becomes durable when a claim can fail a test. Prose still matters, but prose alone permits two readers to imagine different boundary conditions.

Write examples at the system boundary whenever possible. Capture the smallest input that triggers the rule, the relevant starting state, the expected outputs, and permitted side effects. Avoid tests that assert internal call sequences from the old implementation. Those tests preserve structure rather than behaviour and punish any honest modernization.

A behaviour record can be simple enough for an expert to review:

{
  "case": "credit_on_suspended_account",
  "starting_state": {"status": "suspended", "balance": 12500},
  "input": {"type": "credit", "amount": 2500},
  "expected": {
    "accepted": true,
    "balance": 10000,
    "audit_code": "CR-SUSP"
  },
  "source": "collections_review_14"
}

The values need a unit and a meaning. If 12500 means minor currency units, say so in the fixture convention. If dates use the local business day rather than UTC, encode that condition. Many supposed parity failures are actually ambiguous fixtures.

Build families of tests around boundaries, not a single golden example. For the suspended account rule, test a credit, a debit, zero, the maximum accepted amount, a status transition during processing, and a replay of the same request. The expert often knows which of these cases has caused trouble. The engineer knows where implementation boundaries may leak. Both views belong in the suite.

Record the old result and the approved result separately when they differ. A useful harness can report MATCH, APPROVED_DIFFERENCE, UNEXPLAINED_DIFFERENCE, or NOT_COMPARABLE. A binary pass or fail encourages teams to bless unexplained changes merely to make a dashboard green.

CodeHero uses a parity harness against recorded production traffic while rewriting the architecture rather than transliterating the old source. The same discipline should govern expert knowledge: each important recollection needs a case that can be replayed, an approved decision, or a visible unresolved status.

Production traffic is evidence with blind spots

Use the whole codebase
CodeHero reads every language in the tree together, including the jobs and scripts people forget.

Recorded production traffic is the strongest source for common behaviour, but it does not contain every rule the rewrite must preserve. Traffic tells you what occurred during the capture window. It says little about rare year-end paths, disaster recovery, unused emergency functions, rejected inputs blocked upstream, or events that operators repaired manually before recording.

Treat traffic and expert testimony as complementary. Start by replaying captured requests through the old and new systems and comparing externally visible results. Cluster differences by endpoint, transaction type, output field, and error class. Then show representative clusters to the people who operate or own those flows. They can identify a harmless timestamp difference, a known defect, or a missing rule much faster than a migration team reading raw diffs.

Traffic requires context to become a stable test corpus. Redact or tokenize sensitive values while preserving relationships. Pin reference data that would otherwise change between runs. Record clock assumptions, locale, ordering rules, and dependencies that supplied responses. Keep the original capture identifier so an investigator can trace a failed case without copying production data into a ticket.

Sampling deserves suspicion. A busy endpoint can swamp a corpus while a low-volume, high-consequence transaction appears once. Build coverage views by business event and risk, not request count alone. Ask experts which events must appear even if no recent capture contains them. Then construct synthetic cases from an approved example and run them against the old system where that is safe.

Do not use traffic replay as an excuse to avoid deciding what outputs matter. Byte-for-byte comparison will flag generated identifiers, timestamps, ordering, and harmless formatting. Over-normalization can hide a missing ledger entry. Name the observable fields and tolerances for each class of transaction. An expert should be able to explain why a difference is ignored.

Finally, keep a quarantine for cases that cannot yet be compared. A message may invoke an unavailable partner, depend on expired reference data, or trigger an irreversible action. Quarantined cases need an owner and a reason. If the team simply deletes them from the corpus, uncertainty vanishes from the report while remaining in the system.

Documentation must explain decisions

Good migration documentation tells the next engineer what the system promises, where that promise is checked, and why an exception exists. A catalogue of screens and tables becomes stale before cutover because it describes the old shape rather than the new contract.

Give each important behaviour a compact record. Include the business event, preconditions, accepted and rejected outcomes, authoritative owner, test identifiers, operational response, and decision history. Linkage inside your repository or documentation system can connect these items, but the content should remain readable without opening five other pages.

Decision records matter most where parity was intentionally broken. State the old behaviour, the chosen behaviour, affected consumers, approvers, rollout condition, and rollback signal. Avoid vague entries such as "fixed calculation issue." Write the exact difference: "The new invoice rounds the total once; the ledger adapter carries a balancing line for invoices created before the policy date."

Runbooks need the same treatment. An instruction such as "restart the batch if it hangs" is dangerous. Define how the operator detects the hang, which checkpoint is safe, what duplicate side effects to inspect, which reconciliation proves completion, and when to escalate. Turn safe preconditions into automated checks when possible. Leave judgment calls explicit when automation would pretend certainty.

Documentation ownership must move with the system. During migration, the old-system expert may verify a rule while a new-system engineer writes the record and test. After cutover, the service owner owns both. This paired authorship prevents the expert from becoming the permanent secretary for a platform they no longer operate.

Searchability matters, but a single giant knowledge base is not the answer. Keep behaviour close to executable tests, operational procedures close to the service, and policy decisions where accountable owners review them. Use consistent case IDs across those stores. The ID is the thread; forced consolidation usually creates a graveyard.

Review documentation by attempting a task. Give an engineer who missed the migration a failed case and ask them to explain the expected outcome, locate the test, and find the recovery path. Their confusion is a documentation defect with a reproducible example.

Experts must not become a queue

Keep the odd behavior visible
The parity harness exposes changed outputs before an old exception surprises the new service.

Old-system experts should have real authority in the rewrite, but making every decision wait for one person replaces hidden knowledge with a visible bottleneck. The answer is a review protocol that spends their attention on ambiguity and risk.

Assign each expert a bounded role. They may own claims for a business area, approve representative examples, classify parity differences, or verify a runbook. State which decisions they can make and which require a business owner, security owner, or service owner. A RACI chart is optional; an explicit escalation path is not.

Prepare material before asking for review. Do not invite an operator to watch thousands of replay results scroll past. Group differences, remove known noise, select examples, and state the question in decision form: preserve, correct, retire, or investigate? Include the source case and downstream consequence. Ten minutes of expert judgment can then replace hours of wandering through logs.

Use two-person coverage for high-consequence areas. Pair the long-tenured expert with someone who will remain responsible after cutover. The second person writes the test or runbook, demonstrates it, and handles the next related question. The expert corrects the work rather than dictating every sentence. That is how knowledge transfer becomes observable.

Protect time formally. Migration review added on top of a full operational workload will lose to the next incident, as it should. Managers need to remove other duties, schedule decision windows, and track unanswered claims as delivery risks. Do not measure participation by meeting attendance. Measure claims resolved, examples approved, unexplained differences closed, and recovery procedures demonstrated.

Watch for approval theatre. If reviewers receive a hundred-page document on Friday and a sign-off request on Monday, the signature proves nothing. Small, case-based reviews create a usable audit trail and let disagreement surface early.

Compensate people for the role the project needs, and make the post-cutover path concrete. Some will become domain owners, product analysts, service operators, test designers, or modernization leads. Some may choose to leave. Respectful treatment cannot guarantee retention, but contempt almost guarantees that the most useful warnings arrive late or not at all.

Disagreement is a risk signal

When two experts describe the same rule differently, do not average their answers or let the more senior title win. The disagreement usually points to a hidden condition: different customer groups, dates, regions, input channels, or recovery states.

Write both claims in the ledger and ask each person for a case where their version applies. Compare those cases with source code paths, configuration, data history, tickets, and traffic. The goal is to find the predicate that makes both accounts coherent, or to prove that the system behaves inconsistently.

Suppose one operator says a rejected payment can be replayed safely, while another says replay creates duplicates. Their procedures may differ because one queue assigns an idempotency token and another older channel does not. A generic "retries are safe" test would conceal the exact boundary that the rewrite needs to enforce.

Some disputes are policy disputes rather than factual ones. Product wants a customer-friendly override; compliance wants a hard rejection; operations has been applying a manual compromise. Code cannot resolve that conflict. Name the accountable owner, show concrete consequences, and record the decision beside the tests it changes.

Silence is also evidence. An area with no confident owner, no recent traffic, and no repeatable example deserves more scrutiny, not less. Teams often label it unused because deletion makes the plan easier. Check schedulers, access logs, generated files, downstream imports, and calendar-driven procedures before retiring it. If evidence remains weak, isolate the function behind a cutover control and monitor for demand rather than pretending certainty.

Track unresolved disagreements through go-live criteria. Each needs an owner, affected scope, safe fallback, and deadline. A migration can proceed with known uncertainty if the blast radius is bounded and rollback is real. It should not proceed because the uncertainty fell off a meeting agenda.

The social part matters here. Experts who fear that every discrepancy will be blamed on them will smooth over contradictions. Review the system, not the person's memory. Reward the person who produces a troublesome counterexample, because that example is cheaper before cutover than after it.

Missing experts change the method

Leave the old architecture behind
The rewrite targets Go, Rust, TypeScript, and Postgres while holding approved behavior steady.

If the person who knew the system has already left, the rewrite can still proceed, but the team must replace recollection with a more deliberate evidence search. Do not appoint the nearest employee as an all-purpose expert. That creates confident answers from someone who has seen only part of the system.

Begin with traces of decisions. Incident tickets show which failures mattered and who participated. Change requests explain why a condition appeared. Batch logs and scheduler calendars expose time-dependent work. Reconciliation files show what another department considered authoritative. Support macros reveal error messages that recur. Source history may identify people who reviewed a module even when its original author is gone.

Build a witness map from those traces. A witness may know one narrow boundary: the accountant who receives an export, the partner team that sends a file, the service desk analyst who recognizes duplicate cases, or the infrastructure engineer who restored the last failed run. Ask each person only about events they directly handled. Several narrow accounts are safer than one borrowed grand narrative.

Use the old system as an experimental subject where safety permits. Clone production-like state into an isolated environment, vary one input at a time, and record outputs and side effects. Start with cases recovered from logs, then probe boundaries visible in branches, validation tables, and error handling. Never run exploratory transactions against live financial, industrial, or customer workflows merely because documentation is thin.

Static analysis can identify candidate rules, but it cannot tell you whether a branch is current policy, dead code, or an old defect that a caller expects. Mark extracted rules as unconfirmed until traffic, a repeatable execution, or an accountable owner supports them. The absence of a person makes confidence labels more important, not less.

Set cutover controls according to the remaining uncertainty. A poorly understood report might run in parallel for a defined set of business events. A rare irreversible transaction may need manual approval and a rollback route. An unused-looking export may remain isolated and monitored until its calendar trigger passes. These controls cost time, but they expose the cost honestly.

Sometimes the evidence never becomes strong enough. The responsible answer is a bounded exception with an owner, detection method, and recovery procedure. Inventing certainty produces a cleaner status report and a dirtier incident. A missing expert raises the proof burden; it does not remove it.

Write the minimum evidence required for each risk class before testing begins, so delivery pressure cannot quietly lower the threshold later.

Cutover changes the job, not the need for judgment

After the rewrite, the old-system experts should stop being the only people who can keep the business moving. Their judgment still matters, but it should operate through owned tests, decisions, and procedures rather than emergency phone calls.

Define the transition before cutover. List each recurring responsibility: approving exceptions, reconciling outputs, updating reference data, restarting work, explaining reports, and triaging defects. Name the new owner, the supporting artifact, a demonstration date, and the old expert's exit condition. "Knowledge transferred" is not an exit condition. "The new service owner completed two representative reconciliations and recovered a failed batch in rehearsal" is.

Run operational rehearsals against realistic failures. Disable a dependency, introduce a duplicate message, expire reference data, and force a partial batch. The new owners should diagnose and recover using the new observability and runbooks while the old expert watches. Any whispered hint becomes a missing check or instruction to capture afterward.

Keep experts in a limited review loop during early operation, with clear routes and an end date. They should examine novel parity failures and policy questions, not approve ordinary changes forever. If every production issue still routes to them, the migration moved code without moving ownership.

Retain the old environment and its evidence according to legal, operational, and data rules. The team may need to reproduce a disputed output or explain a historical transaction. That does not mean leaving an unmaintained system connected indefinitely. Define access, isolation, data retention, and the authority to run it.

The final test is absence. Can the new owners operate through a close, recover a failure, answer a support question, and change a documented rule while the former expert is unavailable? If not, identify the missing evidence and rehearse again.

A rewrite succeeds when the organization can explain its behaviour without folklore and change it without summoning one particular person. The people who carried the old system deserve more than a ceremonial interview. Give them precise cases to judge, record the disagreements they uncover, and make their knowledge executable. That leaves the new team with evidence instead of stories, and it leaves the experts with a legacy better than permanent on-call duty.

FAQ

How do you capture knowledge from legacy system experts?

Use concrete cases instead of broad interviews. Record each claim with its source, an example, a confidence level, and a proposed test or procedure, then send it back to the expert for correction.

What is tacit knowledge in a legacy system?

Tacit knowledge is the judgment people apply without finding it in code or formal documentation. It includes recovery choices, distrusted fields, calendar exceptions, manual checks, and the reasons a workaround still exists.

Should a rewrite preserve every old system behavior?

No. Classify each important behavior as preserve, correct, retire, or unknown, and have an accountable owner approve the choice. A characterization test proves what happens now; it does not prove the behavior deserves to survive.

How do you turn expert knowledge into tests?

Start with a specific input, relevant starting state, expected output, and permitted side effects. Add boundary cases and record old and approved results separately when the rewrite intentionally changes behavior.

Can production traffic replace subject matter expert interviews?

No. Traffic covers behavior that occurred during the capture period, but experts know about rare events, manual repairs, blocked inputs, and calendar-driven work. Use traffic to test ordinary paths and experts to find the missing ones.

What if legacy system experts disagree?

Keep both claims and ask for a real case supporting each one. The conflict often reveals a hidden condition such as channel, date, customer group, or recovery state; if it is a policy conflict, route it to the accountable owner.

How do you prevent one expert from blocking a migration?

Give experts bounded review duties and prepare clustered examples before asking for decisions. Pair each long-tenured expert with a future owner who writes and demonstrates the test or runbook.

What documentation should a system rewrite produce?

Document behavior contracts, intentional differences, operational recovery, ownership, and the tests that enforce each promise. Screen catalogues and interview transcripts are source material, not finished documentation.

What do you do if the original system expert has left?

Search incident tickets, logs, schedules, reconciliation files, source history, and downstream users for narrow witnesses and repeatable examples. Increase cutover controls where evidence remains weak instead of assigning certainty to the nearest employee.

When is legacy knowledge transfer complete?

It is complete when new owners can explain expected behavior, operate a close, recover representative failures, and change a documented rule without calling the former expert. Attendance at interviews and a signed document do not meet that test.