Working Safely with Legacy Systems

Suraj Kumar

Suraj Kumar

Last updated November 9, 2026·13 min read

Forward Deployed Engineer course · lesson 12 of 24
  1. 1What Is a Forward Deployed Engineer?
  2. 2A Day in the Life of a Forward Deployed Engineer
  3. 3FDE Career Paths, Tiers, and Interview Gates
  4. 4Build Your Forward Deployed Engineer Skill Map
  5. 5Requirements Gathering with a Customer
  6. 6Discovery, Scoping, and Sequencing Customer Work
  7. 7Audit Customer Data Before You Build
  8. 8Enterprise Workflow, Business Value, and ROI
  9. 9Choosing an AI Approach for a Customer
  10. 10Build a Working Prototype Fast
  11. 11Integration Engineering and the Last Mile
  12. 12Working Safely with Legacy Systems
  13. 13Data Pipelines for AI Features
  14. 14Evals as Acceptance Criteria
  15. 15Debugging Agent Logic and Observability
  16. 16Deploying in the Customer's Cloud
  17. 17Cost, Latency and Inference Choices
  18. 18Security, Privacy and AI Governance
  19. 19Stakeholders and Communication
  20. 20Technical Writing for Handoff
  21. 21UAT, Go-Live, and Day 2 Operations
  22. 22Executive Reporting and Proving ROI
  23. 23The Product Feedback Loop
  24. 24Capstone: Run an Engagement End to End
View full syllabus

After this lesson, you will be able to learn an unfamiliar system's behavior, put a small adapter around its interface, and plan changes so a failed request is less likely to create duplicate work. You will build and test a Python adapter with bounded retries, an idempotent write key, and a dry-run mode. NorthPort Freight is fictional. Its order records and legacy interface are synthetic examples, not details from a real customer system.

A legacy system is an existing system that a team must keep using even when its code, documentation, interface, or support model is difficult to change. Age alone does not tell you whether a system is risky. A newer service can be poorly understood; a decades-old program can have a stable and well-documented interface. The practical question is what the system does today, what depends on it, and how safely you can learn more.

NorthPort's fictional dispatch team uses an old ordering application. Dispatchers know that a short status code means an order is held, but no current document explains every code. Other jobs read its nightly export. The system still supports work people need to complete. An FDE who changes the field or its meaning without checking those dependencies can break a report, a handoff, or a user's routine.

A new workflow passes through an adapter that translates its order and status terms into the legacy system's fields and back

Read before changing

Start with people and evidence the customer already has. Ask who operates the system, who understands the workflow, what must stay available, and what changes are already scheduled. Look for runbooks, interface descriptions, support tickets, release notes, batch schedules, and examples of real interactions that the owner has approved you to inspect. A document can be outdated, so compare it with observed behavior and ask about mismatches.

Write down what you can observe without changing state. Identify inputs, outputs, timing, error responses, side effects, file locations, and downstream users. A batch file is a group of records transferred together, often on a schedule. Find out whether the file is overwritten or appended, how its name signals the date, what encoding it uses, whether an empty file means "no changes" or "export failed," and who notices a missed run. A screen may reveal behavior that no document captures. If you have customer approval to inspect it, record the steps, fields, and decisions a user makes. Do not automate clicks just because the screen is visible. A label can move, a dialog can appear, or a session can expire. Screen automation can be a last resort when the system has no supported interface and a human owner agrees to the failure and maintenance plan.

Direct database access also needs care. A database can contain fields whose meaning is unclear, and a direct write can bypass checks performed by the application. Prefer a supported service or export. If the owner says a database connection is the only route, agree on a read-only inspection first, confirm which tables and fields are in scope, and ask the database owner about transactions, constraints, and backups before proposing writes. Never infer that a column is safe to edit from its name alone.

Record unknowns explicitly. For example: "We saw the dispatch screen change the status after approval, but we have not confirmed whether the nightly job reads that field." That sentence tells a reviewer what was observed and what still needs an owner. Avoid turning an assumption into a requirement by repeating it in a ticket or diagram.

Capture behavior with characterization tests

A characterization test records what the existing system does for an input so you can spot an unintended change later. It answers "what happens now?" A specification test checks "what should happen?" You may not know the intended behavior yet. Start by capturing a small set of representative, approved inputs and outputs, then ask the workflow owner whether each result is expected.

Suppose the legacy export contains ORDER_NO=NP-100 and DSP_ST=O. A test can record that the current parser reads this row as order NP-100 with status open, if that mapping has been verified with the customer. Add cases for a held order, an empty status, an unknown code, and a missing order number. Do not guess what a code means and then write a test that makes the guess look official.

Tests do not prove that every behavior is correct. They make the captured examples repeatable and give you a way to notice when the adapter changes them. Keep the observed input, output, source, date, and confirming owner with the test notes. If the system changes and a test fails, inspect the difference with the owner before updating the expectation. A failing test may reveal a regression or a legitimate change.

For unfamiliar code, keep the first change narrow. Michael Feathers's paper on working effectively with legacy code calls the early tests that preserve observed behavior test coverings. He recommends building a safety net around the part you need to change before refactoring or adding new behavior. You do not need to document the entire application before making progress.

Put a translation layer at the edge

An adapter is a small piece of code that lets one interface work with another. Here, it keeps old field names and codes out of the newer workflow. The new code can ask for an Order with a readable status. The adapter translates that request to the legacy format and converts the returned record back into the workflow's terms.

An anti-corruption layer is a boundary that translates between one system's model and another, so legacy concepts do not spread through the new code. The Legacy Mimic pattern describes an adapter or similar layer that lets a new system use the old system's existing interface while translating between their models. Keep the layer small enough that a reviewer can see its field mappings, allowed operations, and error handling.

The adapter is also a useful place to enforce a narrow contract. It can reject an unknown status, normalize a blank field, validate an operation identifier, and turn a legacy error into a clear application error. It should not quietly "fix" data by inventing a value. If a legacy record cannot be interpreted, return an explicit error and send the case to someone who can resolve it.

The adapter depends on a narrow protocol: it states which methods another object must provide, without requiring that object to inherit from a particular base class. LegacyPort is the seam between the adapter and a real system. An in-memory substitute keeps the example and tests away from networks, credentials, customer data, and real legacy databases.

The synthetic legacy record uses ORDER_NO, DSP_ST, and DISPATCH_DATE. The example maps O to open and H to held; these codes are invented for this lesson. Confirm every field and code with a system owner before making a real adapter.

legacy_adapter.py

python
1from dataclasses import dataclass
2from typing import Callable, Mapping, Protocol, TypeVar
3import time
4
5
6class LegacyError(Exception):
7    pass
8
9
10class LegacyTemporaryError(LegacyError):
11    pass
12
13
14class LegacyRecordNotFound(LegacyError):
15    pass
16
17
18class LegacyPermanentError(LegacyError):
19    pass
20
21
22class LegacyPort(Protocol):
23    def fetch_order(self, order_reference: str) -> Mapping[str, str]:
24        ...
25
26    def mark_reviewed(self, order_reference: str, operation_id: str) -> None:
27        ...
28
29
30@dataclass(frozen=True)
31class Order:
32    reference: str
33    status: str
34    dispatch_date: str
35
36
37@dataclass(frozen=True)
38class WriteResult:
39    status: str
40    attempts: int
41    order_reference: str
42
43
44T = TypeVar("T")
45
46
47class LegacyAdapter:
48    STATUS_CODES = {"O": "open", "H": "held"}
49
50    def __init__(
51        self,
52        port: LegacyPort,
53        *,
54        dry_run: bool = True,
55        max_attempts: int = 3,
56        base_delay_seconds: float = 0.1,
57        sleep: Callable[[float], None] = time.sleep,
58    ) -> None:
59        if max_attempts < 1:
60            raise ValueError("max_attempts must be at least 1")
61        if base_delay_seconds < 0:
62            raise ValueError("base_delay_seconds cannot be negative")
63        self.port = port
64        self.dry_run = dry_run
65        self.max_attempts = max_attempts
66        self.base_delay_seconds = base_delay_seconds
67        self.sleep = sleep
68
69    def get_order(self, order_reference: str) -> Order:
70        self._validate_identifier(order_reference, "order_reference")
71        raw, _ = self._with_retries(
72            lambda: self.port.fetch_order(order_reference)
73        )
74        legacy_reference = raw.get("ORDER_NO", "").strip()
75        status_code = raw.get("DSP_ST", "").strip()
76        dispatch_date = raw.get("DISPATCH_DATE", "").strip()
77        if not legacy_reference or not dispatch_date:
78            raise LegacyPermanentError("legacy order is missing required fields")
79        if status_code not in self.STATUS_CODES:
80            raise LegacyPermanentError(
81                f"unknown legacy status code: {status_code or '<blank>'}"
82            )
83        return Order(
84            reference=legacy_reference,
85            status=self.STATUS_CODES[status_code],
86            dispatch_date=dispatch_date,
87        )
88
89    def mark_reviewed(
90        self, order_reference: str, operation_id: str
91    ) -> WriteResult:
92        self._validate_identifier(order_reference, "order_reference")
93        self._validate_identifier(operation_id, "operation_id")
94        if self.dry_run:
95            return WriteResult("dry_run", 0, order_reference)
96        _, attempts = self._with_retries(
97            lambda: self.port.mark_reviewed(order_reference, operation_id)
98        )
99        return WriteResult("applied", attempts, order_reference)
100
101    def _with_retries(self, operation: Callable[[], T]) -> tuple[T, int]:
102        for attempt in range(1, self.max_attempts + 1):
103            try:
104                return operation(), attempt
105            except LegacyTemporaryError:
106                if attempt == self.max_attempts:
107                    raise
108                delay = self.base_delay_seconds * (2 ** (attempt - 1))
109                self.sleep(delay)
110        raise AssertionError("retry loop ended unexpectedly")
111
112    @staticmethod
113    def _validate_identifier(value: str, name: str) -> None:
114        if not value or not value.strip():
115            raise ValueError(f"{name} cannot be blank")

max_attempts counts the first call. With three attempts, the adapter can call once and retry twice. The delay grows between attempts, and the injected sleep function makes that schedule testable without waiting during tests. Only LegacyTemporaryError is retried. A missing record, invalid field, or permission error should not be retried as though waiting will fix it.

The port's mark_reviewed contract requires the legacy side to recognize the same operation_id if a request is repeated. If the first call applies the change but its response is lost, the retry must return the result for that same operation instead of applying a second effect. A random new identifier on each retry defeats that protection. The HTTP Semantics standard defines idempotent request effects and cautions against automatically retrying a non-idempotent request without a way to know it is safe. A local wrapper cannot create durable idempotency if the remote system cannot store or check the operation key.

Try the adapter with a synthetic system

This example provides the LegacyPort methods with a tiny in-memory implementation. It simulates a temporary failure after saving a write, which resembles the ambiguous case where a server commits a change and the reply is lost. The operation ID lets a retry retrieve the prior result. This behavior belongs to the synthetic store, not to any real NorthPort system.

The adapter defaults to dry_run=True. In that mode, mark_reviewed validates its identifiers and returns a preview without calling the write method. Reading an order is still allowed. A dry run can help inspect the intended action, but it is not an approval and does not prove a production write will succeed.

example.py

python
1from legacy_adapter import (
2    LegacyAdapter,
3    LegacyPermanentError,
4    LegacyRecordNotFound,
5    LegacyTemporaryError,
6)
7
8
9class DemoLegacyStore:
10    def __init__(self) -> None:
11        self.records = {
12            "NP-100": {
13                "ORDER_NO": "NP-100",
14                "DSP_ST": "O",
15                "DISPATCH_DATE": "2026-11-05",
16            }
17        }
18        self.completed_operations: dict[str, str] = {}
19        self.write_effects: list[str] = []
20        self.fail_after_save = 1
21
22    def fetch_order(self, order_reference: str) -> dict[str, str]:
23        try:
24            return self.records[order_reference].copy()
25        except KeyError as error:
26            raise LegacyRecordNotFound(order_reference) from error
27
28    def mark_reviewed(self, order_reference: str, operation_id: str) -> None:
29        if operation_id in self.completed_operations:
30            if self.completed_operations[operation_id] != order_reference:
31                raise LegacyPermanentError("operation ID belongs to another order")
32            return
33        if order_reference not in self.records:
34            raise LegacyRecordNotFound(order_reference)
35        self.completed_operations[operation_id] = order_reference
36        self.write_effects.append(operation_id)
37        self.records[order_reference]["REVIEWED"] = "Y"
38        if self.fail_after_save:
39            self.fail_after_save -= 1
40            raise LegacyTemporaryError("reply lost after save")
41
42
43store = DemoLegacyStore()
44preview = LegacyAdapter(store).mark_reviewed("NP-100", "review-NP-100-1")
45print(preview)
46
47adapter = LegacyAdapter(
48    store,
49    dry_run=False,
50    base_delay_seconds=0,
51    sleep=lambda _: None,
52)
53print(adapter.mark_reviewed("NP-100", "review-NP-100-1"))
54print(adapter.get_order("NP-100"))
55print("saved effects:", len(store.write_effects))

Run it with python3 example.py. The output should show a dry-run result, an applied result after two attempts, an Order with status open, and saved effects: 1. The second attempt returns the result for the same operation ID, so the synthetic store records one effect. The zero delay keeps the sample quick; in a real integration, choose limits and delays with the system owner and the provider's documented behavior.

The code is intentionally a seam, not a customer-ready connector. It does not open a socket, parse a batch file, authenticate, or implement a vendor protocol. A production LegacyPort would translate these methods to the customer's supported interface. It would need to map real error responses carefully: only a documented transient failure should become LegacyTemporaryError, and the target must support an idempotency key or another verified deduplication mechanism before retrying writes.

Add tests around the seam

The tests below use a separate fake store so each behavior can be controlled. A fake is a small test object that stands in for a dependency and returns known results. It is useful for checking adapter logic; it cannot show that a real legacy interface has the same behavior. Keep a separate customer-approved test for the real interface when access is available.

The first tests capture the synthetic field mapping and reject a code the adapter does not know. The write tests check the default dry run, validation, temporary failure before a write, an ambiguous reply after a write, permanent errors, and exhausted attempts. The sleep recorder verifies the delay schedule without pausing the test run.

test_legacy_adapter.py

python
1import unittest
2
3from legacy_adapter import (
4    LegacyAdapter,
5    LegacyPermanentError,
6    LegacyRecordNotFound,
7    LegacyTemporaryError,
8)
9
10
11class FakeLegacyStore:
12    def __init__(self) -> None:
13        self.records = {
14            "NP-100": {
15                "ORDER_NO": "NP-100",
16                "DSP_ST": "O",
17                "DISPATCH_DATE": "2026-11-05",
18            }
19        }
20        self.completed_operations: dict[str, str] = {}
21        self.write_effects: list[str] = []
22        self.write_attempts = 0
23        self.fail_before_save = 0
24        self.fail_after_save = 0
25        self.permanent_failure = False
26
27    def fetch_order(self, order_reference: str) -> dict[str, str]:
28        try:
29            return self.records[order_reference].copy()
30        except KeyError as error:
31            raise LegacyRecordNotFound(order_reference) from error
32
33    def mark_reviewed(self, order_reference: str, operation_id: str) -> None:
34        self.write_attempts += 1
35        if self.permanent_failure:
36            raise LegacyPermanentError("write is not allowed")
37        if operation_id in self.completed_operations:
38            if self.completed_operations[operation_id] != order_reference:
39                raise LegacyPermanentError("operation ID belongs to another order")
40            return
41        if self.fail_before_save:
42            self.fail_before_save -= 1
43            raise LegacyTemporaryError("system is busy")
44        if order_reference not in self.records:
45            raise LegacyRecordNotFound(order_reference)
46        self.completed_operations[operation_id] = order_reference
47        self.write_effects.append(operation_id)
48        self.records[order_reference]["REVIEWED"] = "Y"
49        if self.fail_after_save:
50            self.fail_after_save -= 1
51            raise LegacyTemporaryError("reply was lost")
52
53
54class LegacyAdapterTests(unittest.TestCase):
55    def setUp(self) -> None:
56        self.store = FakeLegacyStore()
57        self.delays: list[float] = []
58
59    def adapter(self, **options: object) -> LegacyAdapter:
60        options.setdefault("base_delay_seconds", 0.1)
61        return LegacyAdapter(
62            self.store,
63            sleep=self.delays.append,
64            **options,
65        )
66
67    def test_maps_legacy_fields_to_order(self) -> None:
68        order = self.adapter().get_order("NP-100")
69        self.assertEqual(order.reference, "NP-100")
70        self.assertEqual(order.status, "open")
71        self.assertEqual(order.dispatch_date, "2026-11-05")
72
73    def test_missing_order_raises_not_found(self) -> None:
74        with self.assertRaises(LegacyRecordNotFound):
75            self.adapter().get_order("NP-404")
76
77    def test_unknown_status_is_rejected(self) -> None:
78        self.store.records["NP-100"]["DSP_ST"] = "?"
79        with self.assertRaises(LegacyPermanentError):
80            self.adapter().get_order("NP-100")
81
82    def test_dry_run_does_not_write(self) -> None:
83        result = self.adapter().mark_reviewed("NP-100", "op-1")
84        self.assertEqual(result.status, "dry_run")
85        self.assertEqual(result.attempts, 0)
86        self.assertEqual(self.store.write_attempts, 0)
87
88    def test_blank_operation_id_is_rejected(self) -> None:
89        with self.assertRaises(ValueError):
90            self.adapter(dry_run=False).mark_reviewed("NP-100", " ")
91        self.assertEqual(self.store.write_attempts, 0)
92
93    def test_temporary_failure_retries_with_bounded_delays(self) -> None:
94        self.store.fail_before_save = 2
95        result = self.adapter(dry_run=False).mark_reviewed("NP-100", "op-2")
96        self.assertEqual(result.attempts, 3)
97        self.assertEqual(self.delays, [0.1, 0.2])
98        self.assertEqual(self.store.write_effects, ["op-2"])
99
100    def test_ambiguous_reply_reuses_key_without_duplicate_effect(self) -> None:
101        self.store.fail_after_save = 1
102        result = self.adapter(dry_run=False).mark_reviewed("NP-100", "op-3")
103        self.assertEqual(result.attempts, 2)
104        self.assertEqual(self.store.write_attempts, 2)
105        self.assertEqual(self.store.write_effects, ["op-3"])
106
107    def test_operation_id_cannot_be_reused_for_another_order(self) -> None:
108        self.store.records["NP-101"] = {
109            "ORDER_NO": "NP-101",
110            "DSP_ST": "O",
111            "DISPATCH_DATE": "2026-11-06",
112        }
113        adapter = self.adapter(dry_run=False)
114        adapter.mark_reviewed("NP-100", "op-4")
115        with self.assertRaises(LegacyPermanentError):
116            adapter.mark_reviewed("NP-101", "op-4")
117        self.assertEqual(self.store.write_effects, ["op-4"])
118
119    def test_permanent_error_is_not_retried(self) -> None:
120        self.store.permanent_failure = True
121        with self.assertRaises(LegacyPermanentError):
122            self.adapter(dry_run=False).mark_reviewed("NP-100", "op-5")
123        self.assertEqual(self.store.write_attempts, 1)
124        self.assertEqual(self.delays, [])
125
126    def test_retries_stop_at_attempt_limit(self) -> None:
127        self.store.fail_before_save = 5
128        with self.assertRaises(LegacyTemporaryError):
129            self.adapter(dry_run=False, max_attempts=3).mark_reviewed(
130                "NP-100", "op-6"
131            )
132        self.assertEqual(self.store.write_attempts, 3)
133        self.assertEqual(self.delays, [0.1, 0.2])
134
135    def test_invalid_retry_policy_is_rejected(self) -> None:
136        with self.assertRaises(ValueError):
137            self.adapter(max_attempts=0)
138        with self.assertRaises(ValueError):
139            self.adapter(base_delay_seconds=-0.1)
140
141
142if __name__ == "__main__":
143    unittest.main()

Copy the two code files and the test file into one empty folder, then run python3 -m unittest discover -s . -p 'test_*.py'. The expected result is Ran 11 tests followed by OK. The example can be checked separately with python3 example.py. Both commands use only the Python standard library. Python 3.10 or newer is required for the type syntax; the official Python unittest documentation explains the test case and runner used here.

A useful mutation check confirms the tests can catch a broken safeguard. In mark_reviewed, temporarily change if self.dry_run: to if False: and rerun the suite. test_dry_run_does_not_write should fail because the fake store receives a write. Restore the original condition and rerun all tests before relying on the result. A green test suite is evidence about these defined cases, not proof of production safety.

Choose a safe order of access

Begin with reads through a supported interface and compare the values with what a system owner expects. A shadow check sends a copy of an input through a new path while the existing system remains authoritative; the new path's result is recorded for comparison and does not control the user's workflow. Shadowing can increase load, so use a small sample and agree on limits before sending duplicate reads. Avoid shadow writes unless the target explicitly guarantees they cannot change business state.

After the read and comparison behavior is understood, use a dry run to show which write the adapter would make. Have the owner review the exact target and operation key. Then test one controlled write in an approved environment with a recovery plan. Increase scope only after the result, audit trail, and rollback procedure have been reviewed. A rollback for an external side effect may mean a compensating action or operator procedure, not an automatic reversal.

Access grows from read-only observation to shadow comparison and dry-run review before a controlled, idempotent write

A direct screen integration or database connection belongs near the end of this sequence because it can couple your code to details the system owner may change. If there is no supported interface, document why the alternative is needed, who accepts its fragility, what will detect a break, and how access can be removed. If an owner cannot approve the route, keep the adapter at a fixture or test seam.

Keep the change small and reversible

When the goal is eventual replacement, avoid a single rewrite that moves every workflow at once. The strangler fig pattern replaces parts of an existing application gradually, with routing that sends selected work to the new implementation while other work stays on the old one. AWS Prescriptive Guidance describes this pattern for incremental migration of a monolith. Its examples assume access to the application and routing layer. If a customer does not control those pieces, the pattern may not be available to your team.

A smaller code-level migration can use branch by abstraction. The team places an interface between callers and the current implementation, then can provide a second implementation behind the same interface. Martin Fowler's description of branch by abstraction shows how an abstraction can support a gradual replacement. An FDE often does not own the legacy application or its callers, so use this as awareness for discussions with the product team rather than a promise that you can migrate the system yourself.

Keep a change log for the seam: the old behavior captured, mapping decisions, permissions, retry policy, operation key, tested failure cases, and current owner. Name the limits too. The code here has no durable store and no real provider. It assumes the old system can enforce idempotency by operation ID. If that capability is absent, disable write retries and agree how an operator will check an uncertain result before trying again.

Use this review sheet before the first connection:

AreaRecord before changing anythingOwner or evidence
Business useWhich user task depends on this system, and what must keep working?
Observed behaviorWhich approved inputs, outputs, schedules, error cases, and side effects were recorded?
Data mappingWhich old fields and codes map to the new model? Who confirmed each meaning?
Access pathWhich supported API, event, or batch export can be used? If none, why is a screen or database route necessary?
Read and shadowWhat is the sample size, load limit, comparison rule, and stop condition?
Write safetyIs the operation id stable across retries? Can the target deduplicate it? What happens after an unknown result?
Dry runWhat would change, and who reviewed the preview?
RecoveryWho can pause the integration, inspect a partial result, and perform a compensating action?
Change and supportWho owns the adapter, old interface, alerts, and future field changes?
ExitHow can this seam be disabled or replaced without losing needed records?

Mistakes beginners make

  1. Editing a field after reading its name. Check actual behavior and confirm its meaning with someone who owns the workflow.
  2. Testing only the result you wish the old system produced. First capture what it does now, then agree on any behavior that should change.
  3. Spreading legacy codes throughout the new application. Translate them at one adapter boundary and reject codes the adapter cannot explain.
  4. Retrying a write with a new request identifier each time. Keep one stable operation ID and confirm the target can deduplicate it.
  5. Treating dry-run output as proof the production write will work. It checks the proposed action without exercising the write path.
  6. Shadowing every request without checking capacity. Agree a small sample and a load limit with the system owner.
  7. Reaching directly into a database or automating a screen as the first option. Ask about a supported interface and document why a last-resort path is necessary.
  8. Planning a full replacement before isolating one workflow. Start with a small seam and make sure the existing path remains usable during the change.

Recap and try it yourself

Learn how the old path behaves before changing it. Capture a few approved examples in characterization tests, translate legacy fields at a narrow adapter, and keep reads separate from writes. A dry run previews an action. A shadow comparison observes a second path. Neither grants permission to change customer data. Retry only failures the adapter can classify, and only repeat a write when the target has a documented way to recognize the same operation.

Try it: use the included synthetic records to add one new status code. First write a test that captures the observed mapping, then update the adapter. Add a fake write that fails after it saves and verify that the same operation ID produces one effect. Finally, fill in the review sheet for one fictional NorthPort workflow and mark which facts would need confirmation from a real system owner.

Sources

Checked on 5 November 2026. The system and data in the code examples are fictional. The adapter and tests run locally without a customer system. They demonstrate the boundary and failure cases stated in the lesson, not a production integration.