Exercise Answers¶
Page Maps¶
graph LR
family["Python Programming"]
program["Python Object-Oriented Programming"]
section["Resources Failures Safe Evolution"]
page["Exercise Answers"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
own["name the owner of the resource"] --> fail["separate domain failure from boundary failure"]
fail --> retry["justify retry and idempotency"]
retry --> surface["review logs and public surface"]
surface --> evolve["extend behavior without weakening old contracts"]
These are model answers for the CertificateIssuanceService scenario from the exercise
page. They are not here to hand you fixed wording. They are here to show what a strong
survivability packet sounds like when another maintainer is trying to understand who
cleans up, who may retry, and what promise callers are actually getting.
How to use this page well¶
Compare your own packet against this answer page using four checks:
- did I name the cleanup owner instead of only the resource?
- did I distinguish domain trouble from boundary trouble?
- did I explain what makes retry safe or unsafe?
- did I keep the public contract separate from the transport and storage details underneath it?
If your answer reaches the same verdict but hides those boundaries, it is still too weak.
What strong Module 05 answers have in common¶
Across the whole packet, strong answers usually show all of these:
- one explicit owner for each finite resource lifetime
- one explicit statement of what callers are promised after failure
- retry policy tied to idempotency rather than optimism
- logging decisions tied to audience and recovery value
- internal mechanics kept off the public surface
- one honest note about what extension pressure could still break
If your packet sounds like operations advice without ownership and contract language, the module has not landed deeply enough yet.
Answer 1: Resource owner¶
A clean ownership split looks like this:
| Resource | Owner | Why |
|---|---|---|
| template file handle | template-loading boundary | it opens and closes the file in one place |
| temporary rendered PDF | issuance workflow boundary | it creates and deletes the temp artifact |
| object storage client session | storage adapter | transport client lifetime should not leak to callers |
| database transaction or unit of work | repository or application boundary | commit and rollback authority must stay explicit |
Callers should not need to know:
- where temp files live
- how storage sessions are cleaned up
- whether the email adapter reuses connections
The bug when ownership is split is usually either leaked resources or duplicate cleanup that fails in confusing ways.
Answer 2: Context-manager boundary¶
A good with boundary surrounds the lifetime of the temporary rendered PDF:
- create temp file
- write rendered bytes
- upload from the temp path or stream
- always delete temp file on exit
Why not higher?
- callers would now own cleanup knowledge they should never need
Why not lower?
- cleanup would scatter across success and failure branches
The point is visible lifetime, not just fewer lines.
Answer 3: Unit-of-work boundary¶
One coherent unit of work might include:
- persist "certificate issued" record
- persist storage object key
- persist external issuance identifier if one exists
That unit should either:
- succeed together
- or fail together with no visible "issued" record
The email send should usually happen after the authoritative persistence step, because a notification can be retried more safely than a corrupted issuance record can be repaired.
Operator evidence on failure should say:
- whether persistence committed
- whether upload succeeded
- whether email was attempted
Without that evidence, recovery is guesswork.
Answer 4: Domain errors versus boundary failures¶
Example split:
- "certificate already issued" is a domain error
- "template file missing" is a boundary failure
- "object storage timeout" is a boundary failure
- "SMTP unavailable" is a boundary failure
Caller meaning:
- domain error tells the caller the request contradicts current truth
- boundary failure tells the caller the request may be valid but the environment failed
Flattening them together harms both recovery and review:
- callers cannot tell whether to retry
- operators cannot tell whether the model or the environment is broken
Answer 5: Retry policy¶
Suppose the workflow crashes after the issuance record is stored but before the email is sent.
Safe retry requires:
- issuance record creation to be idempotent by a stable issuance key
- upload path to tolerate same-key replacement or detect existing object safely
- email send to be deduplicated or intentionally tolerant of repeat sends
Retry policy belongs near the workflow boundary, not inside callers, because callers should not need to reconstruct partial failure semantics themselves.
Answer 6: Logging and propagation¶
Good logging split:
- storage adapter logs transport details only where those details are actionable
- workflow boundary logs one high-level failure summary with issuance identifiers
- propagated exception is translated into a caller-meaningful boundary failure
Bad pattern:
- file loader logs
- storage adapter logs
- workflow logs
- caller logs again
That produces four noisy records for one fault and still leaves the caller unclear on what happened.
Answer 7: Public surface¶
Stable public surface:
issue_certificate(attendee_id)- maybe
reissue_certificate(attendee_id)if the business supports it
Internal details:
- temp-file handling
- storage bucket naming
- raw SMTP client behavior
- retry loop implementation
If callers start depending on internal adapters directly, future changes such as moving from temp-file rendering to in-memory streaming become breaking changes for no good reason.
Answer 8: Failure-shaped smell¶
A common smell is copied retry logic:
- workflow retries upload one way
- caller retries email another way
- scheduled job retries the whole thing a third way
Why it survives green tests:
- happy-path tests do not expose contradictory retry ownership
Repair:
- move retry rules to the workflow boundary
- give adapters simpler contracts
- document which side effects are idempotent and which are not
That narrows responsibility and makes review possible.
Answer 9: Safe extension¶
Add requirement:
- also generate a PNG preview for the certificate
Safe extension path:
- attach preview generation inside the issuance workflow boundary
- reuse the same cleanup owner for temp artifacts
- keep issuance record semantics unchanged
Unsafe shortcut:
- let a caller generate the preview separately and hope it remembers the same cleanup, logging, and retry rules
The proof should show that:
- old issuance behavior still works
- cleanup still happens once
- retries do not duplicate externally visible success
Answer 10: Survivability packet¶
A strong final packet says something like:
CertificateIssuanceServiceowns the workflow contract. File and temp-artifact lifetimes are hidden behind explicit cleanup boundaries. Persistence of the issuance record is authoritative; email delivery is downstream and retryable. Domain errors are separated from transport failures. The main remaining risk is duplicate outward notification, so retry policy and idempotency keys must be reviewed together.
That note is useful because it tells the next maintainer:
- who owns cleanup
- where truth is committed
- what can be retried
- what still deserves pressure testing
What a strong packet sounds like overall¶
A strong final packet usually sounds like this:
This workflow has one visible cleanup owner, one visible commit boundary, one caller contract after failure, and one explicit retry story. Internal mechanics remain hidden unless they change the promise.
That is the level of explicitness a missed-class learner should be aiming for.
Exit check¶
Leave this answer page only when you can say:
My answers now survive challenge: I can name the cleanup owner, distinguish domain from boundary failure, explain the retry contract, and say what callers may rely on without learning the internals.