Course outline

Software Architecture / Evolution: capacity, migration, validation, and review / Lesson 16

Validate architectural claims with focused evidence

Choose static dependency checks, policy tests, adapter tests, contract tests, and recovery scenarios for different claims.

About 4 min readContent reviewed
On this page

Lesson goals

  • Match a test boundary to the architectural property it can establish.
  • Interpret a passing check without extending its claim beyond the fixture.

Write the property before choosing the test tool

Architecture validation begins with a claim that could be false. Booking policy does not import a provider SDK is a dependency claim; no two accepted requests exceed capacity is a concurrency claim; old clients understand the new response is a compatibility claim. They require different evidence. A large end-to-end test may touch all components while explaining little about a failed boundary. Focused checks should identify the property and its scope clearly, and broader scenarios should verify the important interactions that isolated tests cannot establish.

Use dependency checks for dependency rules

A static check can inspect imports, package references, or an explicit dependency graph and reject a forbidden edge. It does not prove runtime isolation or correct business behavior. The following complete program evaluates a synthetic graph, so its output demonstrates the declared rule only. A real codebase would need language-aware extraction and handling for generated or dynamic dependencies. Keep the rule small and understandable; a brittle rule that maintainers routinely disable provides weaker protection than a focused check with an owned exception process.

python A synthetic dependency-rule check
edges = [("booking_policy", "reservation_port"), ("mail_adapter", "mail_sdk")]
forbidden = [(a, b) for a, b in edges if a == "booking_policy" and b.endswith("_sdk")]
print(f"forbidden_edges={len(forbidden)}")
assert not forbidden
Expected output
forbidden_edges=0

There are zero forbidden edges in the supplied graph, and the assertion passes. The program does not scan repository files or claim that a real application follows the rule.

Test the adapter and the consumer where assumptions meet

A policy test can inject a fake repository to examine a cancellation rule. It cannot establish that a SQL adapter maps rollback or conflict results correctly. Adapter tests should exercise an isolated real dependency when feasible; contract tests should compare consumer expectations with provider behavior. Schema compatibility is only part of that contract. Include required error states, unknown optional fields, and representation units. Avoid a fake that simply duplicates the implementation’s assumptions and therefore passes while both fake and production adapter share the same misunderstanding.

Design failure scenarios with observable invariants

For asynchronous booking notifications, a failure matrix should cover a crash after reservation commit, relay redelivery, consumer failure, and expired work. Each row needs an expected durable state and a permitted caller outcome. The illustrative matrix is a test design, not a result log. A recovery test that asserts only that the process restarted misses duplicate messages, lost intentions, and incorrect state labels. Keep experiments isolated and bounded; production failure injection and service administration require their own explicit operational scope and are not part of this public course.

text A proposed invariant-focused test matrix
Scenario: crash after booking commit, before relay publication
Expected: confirmed reservation plus pending outbox intention
Scenario: relay publishes the same event twice
Expected: consumer applies the repeat-sensitive effect at most once under its declared protocol
Scenario: persistent consumer rejection
Expected: bounded retries plus owned unresolved state

The consumer expectation is conditional on its deduplication design. No broker or crash test is executed here; those claims need evidence from the selected implementation.

Keep checks sensitive to the failure you care about

A check is valuable when a meaningful violation makes it fail and a legitimate change can still proceed. Review whether fixtures cover the architectural risk rather than mirroring every line of code. If a rule changes, update the decision record and the check together so maintainers understand the new guarantee. Preserve exact test scope in reports, including dependencies that were replaced by fakes or not executed. Passing a synthetic dependency graph and local functions in this course establishes those examples only; it is not an architecture certification or a production readiness assessment.

Common errors and fixes

A fake repository test is claimed to prove database isolation.

Why it happens
The tested boundary was overstated.
How to fix it
Report the policy claim and add isolated engine evidence for concurrency.
Prevent it next time
Name replaced dependencies in every test summary.

A dependency check silently permits every exception.

Why it happens
Rule maintenance has no accountable owner.
How to fix it
Require justified narrow exceptions with review conditions.
Prevent it next time
Review exception growth and actual violations.

A restart test ignores missing or duplicated work.

Why it happens
Process liveness replaced business correctness.
How to fix it
Assert durable state and permitted effects after recovery.
Prevent it next time
Define invariants before introducing a fault.

Check your understanding

1. Which evidence establishes that an old client ignores a new response field?

Show worked solution

Run that client or its representative deserializer against a response containing the field and verify the relevant behavior. A schema review or provider-only unit test is insufficient when client parsing may reject unknown fields. Record client version and fixture scope so the conclusion remains specific.

2. Why might testing every private helper reduce architecture-test value?

Show worked solution

It can bind tests to implementation details while leaving the consequential boundary untested. Focus architecture checks on dependency rules, contract meaning, invariants, and recovery behavior. Ordinary unit tests can cover local algorithms when useful, but their volume does not substitute for evidence about cross-boundary properties.

Key takeaways

  • A test establishes only the property and boundary it exercises.
  • Architecture checks need meaningful failures and maintainable rules.

Official references