Course outline · choose a lesson
Specify the ownership, trust, network and meaning at each boundary
Turn a system trace into boundary contracts with responsible owners, permission checks, approved routes and explicit domain meanings. Work through a misleading customer-ID join and a dependency schedule instead of treating arrows as guarantees.
28 MIN
By FDEInterviews · Updated
Transcript
This is lesson eight of Foundations of Forward Deployed Engineering. For each connection, state who owns changes, which identity and permissions apply, what network route is approved, and what the data means. Boundaries deserve explicit checks. Failures inside components remain part of the investigation too.
The previous lesson traced a record and marked the limits of its evidence. Now we turn the important crossings into contracts, contracts that can guide implementation, review and recovery.
Start with the arrow. Architecture diagrams can show ownership, trust and network boundaries well, when their authors include them. A diagram containing only boxes may leave those questions open.
The remedy is to annotate or supplement the diagram. Don't assume every diagram conceals the truth. Components can fail internally, through incorrect logic, resource exhaustion or storage errors. Boundaries add a different risk: mismatched assumptions between parts that otherwise work. So investigate both.
Between your service and their system sit three boundaries: ownership, which defines coordination and correction rights, trust, which concerns permissions as well as identity, and network, where connectivity is necessary but isn't permission. They're useful views. They aren't an exhaustive list of every failure mode.
Ownership comes first. A source owned by another team may still support an approved correction API, a mapping layer or a joint repair. Ownership doesn't mean you can only wait.
It means you need to know who can authorize each change, and how the source and its derived copies remain consistent. Take a malformed identifier, where the possible responses include a contract-approved normalization, an explicit mapping, quarantine, rejection of the whole batch, or correction at the source.
Choose based on the meaning and the consequence. If malformed rows make the batch unsafe, continuing with the rest may be wrong, but if they're isolated and the contract permits partial processing, blocking all the work may be unnecessary.
Where it's appropriate, record the original values, the transformation rule and its version, and the reconciliation evidence. Don't silently repair an identifier merely because a transformation lets the join succeed. Nor should you reject a valid canonicalization rule simply because it changes the source's formatting.
Next, meaning. A shared word may hide different models. A bounded context is an explicit boundary within which a domain model is coherent, with mappings between models.
And a bounded context isn't simply another name for a team's ownership boundary, or for a network segment. In this synthetic freight example, billing's customer is the legal party paying an invoice, while dispatch's customer field identifies the delivery site. Both use small integers, but equal integers don't establish equal entities.
SQL will join any two integer columns without complaint, because the type system knows about integers and nothing about payers or sites. The meaning lives in the column names, and the join condition ignored them.
The lesson has a SQLite fixture to run. Three tables: billing customers, delivery sites, and shipments. The first query joins shipments straight to billing, on payer ID equals site ID. The second goes through delivery sites, and joins billing on the site's payer ID. Both are syntactically valid. Only one means anything.
Here's what it prints. The same-shaped join gives shipment S1 to North Retail, while the explicit mapping gives S1 to South Wholesale. S2 flips the same way. Both queries return two rows, so a row-count check alone misses the error.
The mapping in this fixture is supplied domain evidence. In a real system, verify its owner, current validity, tenant scope and change history, and remember that the example doesn't establish that every site has exactly one payer forever.
The same shape hides a second failure. If a shipment's site has no mapping row at all, the inner join returns nothing for it, and the shipment disappears from the report without a warning. Left joins at both steps keep it, with an empty payer beside it, so the gap is a row someone can see, and the row count still reconciles. A left join on the first step alone still loses the shipment at the second join.
Now trust, which concerns permissions as well as identity. Authentication identifies the caller or service. Authorization decides which objects and actions that identity may access.
A broad service credential must not erase the end user's restrictions when it returns results. For retrieval, enforce the applicable permissions before unauthorized content reaches model context or the response. Ingestion filters, tenant-specific indexes and current query-time checks may work together. The right design depends on how access changes and on the serving paths.
So "query time instead of ingestion" is an incomplete rule, when caches, embeddings, direct fetches and revoked grants also need controls. Trace the grant source and its freshness, then test at least an allowed user, a denied user, a revoked grant and a cache hit.
A document omitted from the UI can still leak, if its text reaches the model or a downloadable endpoint. And keep authorization outside model-generated claims.
Next, the network. Connectivity is necessary, but it isn't permission. Describe the source, destination, port and protocol, DNS, proxy and TLS requirements, credentials, and the approval owner.
A successful call from a laptop doesn't establish that the deployed workload can make it, and a route being technically reachable doesn't authorize sending customer data through it.
If an external service is unavailable or disallowed, a queue can decouple timing but can't make the route permitted, and a cache can reduce calls while it introduces storage, staleness and access questions. An approved local alternative, or a different workflow, may be needed. Don't propose queueing forever as a solution to a permanently prohibited destination.
Use bounded checks from the intended environment, under the actual network process, and record whether a failure was DNS, connection, TLS, authentication, authorization, or application behavior. These layers can look alike. They need different owners and repairs.
Now calculate the dependency path. The slowest request isn't automatically the whole project timeline. Some prerequisites run in parallel, while integration verification may start only after several finish.
In this synthetic plan, a route review takes five working days, an identity review takes three, and implementation takes four, and we assume all three can start together with separate assigned capacity. The end-to-end check needs both approvals and the implementation. Then it takes two days.
Earliest completion takes the longest of the three, the five-day route review, and adds two, which is seven working days under those assumptions. Now suppose the identity review can't start until the route review finishes. The path becomes five plus three plus two, which is ten days, assuming implementation still finishes in parallel.
And if the same engineer must implement and perform another task, capacity may change the schedule again. So record the dependency and staffing assumptions, rather than reporting the largest single number as the answer.
Finally, write the boundary register. For the freight path, use one row per important crossing, and link the source of each decision.
Fill each row in order, from the producer and consumer, to who owns changes, which identity and permissions apply, the approved route, what the data means on each side, the failure behavior, and the evidence status, with its source.
A useful unresolved row says: payer mapping history not supplied, billing data owner to confirm changes before historical reporting. It doesn't say billing is the blocker. The first identifies a testable dependency. The second turns uncertainty into blame.
Your turn. Pause the video here. Run the join fixture, change the site-to-payer mappings, and verify the explicit join follows them. Try crossed mappings, and mappings where each payer ID happens to equal its site ID. The same-shaped join ignores the mapping, so it can return the right answer by coincidence and still be the wrong rule. Add a missing site mapping, and decide how the real workflow should expose that unresolved shipment. Write three negative checks for the trust boundary, including revoked access and a cached response. For a proposed external route, state what it needs for both connectivity and data use, and explain why a queue waives neither. Finally, calculate the seven-day and ten-day plans, and name the assumptions that separate them. Your register passes when it names decisions, contracts and evidence, not a box label or a handshake in the diagram.
To recap. Boundaries require contracts for responsibility, meaning, permissions and connectivity, and an ownership boundary permits coordinated repair without justifying silent transformation or automatic partial processing. Equal field types and row counts don't establish equivalent domain meaning. Apply permission controls across all serving paths, including caches and current grants, and plan from dependencies and capacity, not only the longest individual request.
That's lesson eight. Next in the series, test replay, slow dependencies and changing data before you add load. The full lesson, with its exercise and drill, is on fdeinterviews.com.
TL;DR: For each connection, state who owns changes, which identity and permissions apply, what network route is approved and what the data means. Boundaries deserve explicit checks, while failures inside components remain part of the investigation too.
Where you are. The previous lesson traced a record and marked evidence limits. Now turn the important crossings into contracts that can guide implementation, review and recovery.
An arrow needs a contract
Architecture diagrams can show ownership, trust and network boundaries well when their authors include them. A diagram of nothing but boxes may leave those questions open, and the remedy is to annotate or supplement it, not to assume every diagram hides the truth.
Components can fail internally through incorrect logic, resource exhaustion or storage errors, and boundaries add the risk of mismatched assumptions between parts that work fine on their own, so investigate both. The three boundary types below are useful views, not an exhaustive list of all failure modes.
Ownership defines coordination and correction rights
A source owned by another team may still support an approved correction API, a mapping layer or a joint repair. Ownership does not mean you can only wait; it means you need to know who can authorize each change and how the source and derived copies remain consistent.
For a malformed identifier, possible responses include a contract-approved normalization, explicit mapping, quarantine, rejection of the whole batch or source correction. Choose based on meaning and consequence. If malformed rows make the batch unsafe, continuing the rest may be wrong; if they are isolated and the contract permits partial processing, blocking all work may be unnecessary.
Record original values, transformation rule/version and reconciliation evidence where appropriate. Do not silently “repair” an identifier merely because a transformation lets the join succeed. Nor should you reject a valid canonicalization rule simply because it changes the source's formatting.
A shared word may hide different models
Martin Fowler's bounded-context explanation describes explicit boundaries within which a domain model is coherent, with mappings between models. A bounded context is not simply a synonym for a team ownership boundary or network segment.
In this synthetic freight example, billing's customer is the legal party paying an invoice, while dispatch's customer field identifies the delivery site. Both use small integers, and equal integers do not make equal entities. SQL will join any two integer columns without complaint, because the type system knows about integers and nothing about payers or sites; the meaning lives in the column names, and the join condition ignored them.
Run the SQLite fixture to see a query that is syntactically valid and semantically wrong:
import sqlite3
conn = sqlite3.connect(':memory:')
conn.executescript('''
CREATE TABLE billing_customer (payer_id INTEGER PRIMARY KEY, name TEXT);
CREATE TABLE delivery_site (site_id INTEGER PRIMARY KEY, payer_id INTEGER, name TEXT);
CREATE TABLE shipment (shipment_id TEXT PRIMARY KEY, site_id INTEGER);
INSERT INTO billing_customer VALUES (1, 'North Retail'), (2, 'South Wholesale');
INSERT INTO delivery_site VALUES (1, 2, 'Harbor depot'), (2, 1, 'Hill depot');
INSERT INTO shipment VALUES ('S1', 1), ('S2', 2);
''')
wrong = conn.execute('''
SELECT s.shipment_id, b.name
FROM shipment s JOIN billing_customer b ON b.payer_id = s.site_id
ORDER BY s.shipment_id
''').fetchall()
correct = conn.execute('''
SELECT s.shipment_id, b.name
FROM shipment s
JOIN delivery_site d ON d.site_id = s.site_id
JOIN billing_customer b ON b.payer_id = d.payer_id
ORDER BY s.shipment_id
''').fetchall()
print('same-shaped IDs:', wrong)
print('explicit mapping:', correct)
assert correct == [('S1', 'South Wholesale'), ('S2', 'North Retail')]
# same-shaped IDs: [('S1', 'North Retail'), ('S2', 'South Wholesale')]
# explicit mapping: [('S1', 'South Wholesale'), ('S2', 'North Retail')]
Both queries return two rows, so a row-count check alone misses the error. The mapping in this fixture is supplied domain evidence; in a real system, verify its owner, current validity, tenant scope and change history. Nor does the example establish that every site has exactly one payer forever. The same shape hides a second failure, too. If a shipment's site has no mapping row at all, the inner join returns nothing for it and the shipment disappears from the report without a warning. Left joins at both steps keep it, with an empty payer beside it, so the gap is a row someone can see and the row count still reconciles; a left join on the first step alone still loses the shipment at the second join.
Trust concerns permissions as well as identity
Authentication identifies the caller or service; authorization decides which objects and actions that identity may access. A broad service credential must not erase the end user's restrictions when returning results.
For retrieval, enforce the applicable permissions before unauthorized content reaches model context or the response. Ingestion filters, tenant-specific indexes and current query-time checks may work together; the right design depends on access changes and the serving paths. “Query time instead of ingestion” is an incomplete rule when caches, embeddings, direct fetches and revoked grants also need controls.
Trace the grant source and its freshness, and test at least an allowed user, a denied user, a revoked grant and a cache hit. A document hidden in the UI can still leak if its text reaches the model or a downloadable endpoint, so keep authorization outside model-generated claims.
Network connectivity is necessary but not permission
Describe the source, destination, port/protocol, DNS, proxy, TLS requirements, credentials and approval owner. A successful call from a laptop does not show that the deployed workload can make it, and a route being technically reachable does not authorize sending customer data through it.
If an external service is unavailable or disallowed, a queue can decouple timing but cannot make the route permitted. A cache can reduce calls but brings its own storage, staleness and access questions. An approved local alternative or a different workflow may be needed. Do not propose queueing forever as a solution to a permanently prohibited destination.
Use bounded checks from the intended environment under the actual network process. Record whether a failure was DNS, connection, TLS, authentication, authorization or application behavior, because these layers produce similar symptoms and need different owners and repairs.
The dependency path, calculated
The slowest request is not automatically the whole project timeline: some prerequisites run in parallel, while integration verification may start only after several finish.
In this synthetic plan, a route review takes five working days, an identity review three and implementation four, and all three can start together with separately assigned capacity. The end-to-end check needs both approvals and the implementation, then takes two days. Earliest completion is max(5, 3, 4) + 2 = 7 working days under those assumptions.
If identity review cannot start until the route review finishes, the path becomes 5 + 3 + 2 = 10 days, assuming implementation still finishes in parallel. If the same engineer must implement and also do another task, capacity may change the schedule again. Record dependency and staffing assumptions rather than reporting the largest single number as the answer.
The boundary register
For the freight path, use one row per important crossing: producer and consumer, data meaning, current owner, permitted identity/action, route, contract/version, failure behavior and evidence status. Link the source of each decision.
A useful unresolved row says “payer mapping history not supplied; billing data owner to confirm changes before historical reporting.” It does not say “billing is the blocker,” because the first names a testable dependency and the second turns uncertainty into blame.
The spine above is one row of the boundary register, drawn as the order you fill it in, from the two ends of the arrow to the evidence behind every claim on it.
Do this before moving on
Run the join fixture, change the site-to-payer mappings and verify that the explicit join follows them. Try crossed mappings and mappings where each payer ID happens to equal its site ID: the same-shaped-ID join ignores the mapping, so it can return the right answer by coincidence and still be the wrong rule. Add a missing site mapping and decide how the real workflow should expose that unresolved shipment; an inner join silently omits it, so a reconciliation count or left joins at both steps may be required.
Write three negative checks for the trust boundary, including revoked access and a cached response. For a proposed external route, state both the connectivity and the data-use prerequisites, and explain why adding a queue waives neither.
Finally, calculate the seven-day and ten-day dependency plans and identify the assumptions that distinguish them. Your boundary register passes when it names decisions, contracts and evidence instead of relying on a box label or a handshake in the architecture diagram.
Go deeper
- Multi-tenancy isolation is the trust boundary treated as an architectural requirement.
- VPC deployment is what the network boundary looks like when you have to deploy inside it.
- PII handling covers what changes the moment data crosses an ownership boundary into your system.
- Data quality is the discipline for data you cannot fix at source.
- Permissions-aware retrieval over ten million documents is the trust boundary as an interview question, and it is a common filter.
- The reference architecture is the checklist of layers and trust boundaries to walk while you look.
Key takeaways
- Boundaries require contracts for responsibility, meaning, permissions and connectivity.
- An ownership boundary permits coordinated repair; it does not justify silent transformation or automatic partial processing.
- Equal field types and row counts do not establish equivalent domain meaning.
- Apply permission controls across all serving paths, including caches and current grants.
- Plan from dependencies and capacity, not only the longest individual request.
Check yourself
Answer before you look. Recalling it is what makes it stick; recognising it does not.
1Both joins return two rows. Does that establish the payer attribution is correct?
2An external endpoint is prohibited. Does putting requests in a queue make the integration acceptable?
3Parallel tasks take five, three and four days, followed by two days of checking. What is the earliest completion under the supplied independent-capacity assumption?
Sign in free to save your progress and return to this lesson.
