# Ch. 2 — Software Architecture Fundamentals

## Summary

The terminology backbone of the CPSA-F curriculum (iSAQB learning goals LG 1-1 … 1-10). Defines software-intensive systems, argues that every system inherently has an architecture ("a framework for change"), builds the core vocabulary — building blocks, interfaces, views, architectural levels — and closes with a bird's-eye view of the design process (Twin Peaks) and the architect's role.

## Key points

### Why every system has an architecture

<div class="el-ul" id="bkmrk-the-magic-rectangle%3A">- **The magic rectangle**: functionality, quality, effort, time — the four axes every project is judged on. Requirements engineering and architecture design are the highest-leverage disciplines because both force far-reaching decisions at the moment of least knowledge.
- **Software-intensive system**: a system whose essential tasks are carried out by software building blocks. Three categories, each with a typical instinct: *information systems* (data-heavy, many users → layered architectures, data/transaction problems), *embedded systems* (physical, resource-constrained, safety-critical → loosely coupled processes over buses, scheduling/comms problems), *mobile systems* (autonomous, personal, intermittently connected → shared-memory processes, UI-vs-hardware tension). Real systems blur categories, but each points to a toolbox.
- **Every system has an architecture** — inherent, not optional; the only choice is explicit design vs. accident. Rausch: *"Software architecture is a framework for change."* Load-bearing wall analogy: architecture decides which parts are load-bearing (expensive to change) and which are decorative (cheap to change) — and that split is **relative to which future changes actually happen**, not a fixed property of the code.
- **Definition (ISO/IEC/IEEE 42010:2011)**: "fundamental concepts or properties of a system in its environment embodied in its elements, relationships, and in the principles of its design and evolution." Architecture objectives are **long-term**, often amortizing only after the project ends — unlike short-term project objectives (LG 1-7). Implicit assumptions and constraints must be made explicit (LG 1-8).

</div>### Building blocks &amp; interfaces

<div class="el-ul" id="bkmrk-building-block%C2%A0%28deli">- **Building block** (deliberately not "component"): any abstraction of code, from function to subsystem. Three defining characteristics: 
    1. *Provided and required interfaces* — provided interfaces are a contract to the outside world, but only honored when the block's own required interfaces are satisfied.
    2. *Encapsulation and interchangeability* — implementation is hidden behind interfaces; anything offering the same provided/required interfaces should be swappable without callers noticing.
    3. *Configuration and hierarchical (de)composition* — a building block can itself be a configuration of smaller building blocks wired together.
- **Interface**: a well-defined access point (syntax, behavior, errors, non-functional characteristics, protocols, semantics…). Interfaces can **never be fully specified** — Java's `Collection` documents everything except insert performance, which is exactly what decides `ArrayList` vs `LinkedList`. The architect decides which unstated aspects matter enough to pin down.
- **View depth**: black box (provided/required interfaces only — caller's view), gray box (+ technical/runtime interfaces), white box (internal configuration — implementer's view).
- **Who defines an interface**: standard (third party) / provided (exporter — most common) / required (importer — plugin style) / independent (neither side owns it + an adapter connects them). Independent maximizes decoupling but costs effort; if an adapter is used as a shortcut without ever generalizing the interface, "temporary" quietly becomes permanent.

</div>### Describing architecture: views &amp; levels

<div class="el-ul" id="bkmrk-ieee-42010-descripti">- **IEEE 42010 description model**: stakeholders → concerns → viewpoints (conventions) → views, plus documented **rationale**. Views are **projections** — the same 3D object casts a circle from below and a triangle from the side; neither view is wrong or complete on its own.
- **Four architectural levels, two dimensions** (abstraction × perspective): architectural style (high/functional, e.g. "3-layer web + MVC"), technical infrastructure (high/technical, e.g. "thin client + app container + relational DB"), functional **A-architecture** (domain building blocks), technical **T-architecture** (cross-cutting concerns: persistence, transactions, logging). Siedersleben: **"A and T are blood groups — don't mix them."**
- **Environment**: four surrounding areas, each a two-way street — project management, product management/requirements engineering, execution platform/operations (the most neglected interface), tools/dev environment.
- **Quality of an architecture** is relative to objectives and lifecycle — good architecture keeps the magic rectangle achievable. ISO 25010 gives a quality-attribute checklist to start from.

</div>### Design process &amp; the architect's role

<div class="el-ul" id="bkmrk-twin-peaks-model%3A-re">- **Twin Peaks model**: requirements and architecture descend in parallel iterative spirals — effort estimates only become real once a draft architecture exists. Four equally weighted, **non-sequential** activities: analyze requirements/constraints; design views and technical concepts; evaluate architecture and decisions; support/review implementation.
- The architect is both a communication platform and the owner of the design/implementation blueprint, interfacing with nearly every other role on the project.

</div>## One example, all the vocabulary — `DocumentStore`

One running scenario to hold the terms together, built from the two real incidents discussed below: a `DocumentStore` building block that saves files to a cloud provider.

<div class="el-table" dir="ltr" id="bkmrk-concept-in-documents"><table><thead><tr><th dir="ltr">Concept</th><th dir="ltr">In `DocumentStore`</th></tr></thead><tbody><tr><td dir="ltr">Building block</td><td dir="ltr">`DocumentStore` itself — an abstraction from "save a file" down to whatever actually implements it</td></tr><tr><td dir="ltr">Provided interface</td><td dir="ltr">`save(file) -> id`, `fetch(id) -> file` — the promise made to every caller</td></tr><tr><td dir="ltr">Required interface</td><td dir="ltr">A `CloudClient` (network + auth) — the promise only holds if this dependency is satisfied</td></tr><tr><td dir="ltr">Encapsulation &amp; interchangeability</td><td dir="ltr">Swap `GoogleDriveClient` for `OneDriveClient` behind the same `DocumentStore` interface; callers shouldn't notice</td></tr><tr><td dir="ltr">Configuration &amp; decomposition</td><td dir="ltr">`DocumentStore` = `RetryPolicy` + `Cache` + a `CloudClient` adapter, wired together internally</td></tr><tr><td dir="ltr">Interface incompleteness (the `Collection` lesson)</td><td dir="ltr">`save()` doesn't document max file size or latency — same kind of gap as `Collection` omitting insert performance; someone still has to decide if the gap matters</td></tr><tr><td dir="ltr">Interface definer type</td><td dir="ltr">Independent interface + adapter: `DocumentStore` belongs to neither Google nor Microsoft; `GoogleDriveAdapter` / `OneDriveAdapter` implement it</td></tr><tr><td dir="ltr">Black / gray / white box</td><td dir="ltr">Caller sees black box (`save`, `fetch`); ops sees gray box (retry/timeout config); adapter author sees white box (raw Drive API calls)</td></tr><tr><td dir="ltr">A/T blood groups</td><td dir="ltr">`DocumentStore` and its adapters are pure T-architecture; the mistake would be naming it `ClaimAttachmentStore` and hardcoding claim logic inside — A leaking into T</td></tr><tr><td dir="ltr">**The bug actually hit**</td><td dir="ltr">No independent interface existed — code called `SaveToGoogleDrive()` directly, a *provided* interface named after its *required* dependency. Migrating providers meant hunting every call site instead of writing one new adapter.</td></tr></tbody></table>

</div>**The one habit that would have prevented both real incidents below**: name interfaces after what they *promise* (the capability), never after who currently *provides* them or which domain concept happens to *call* them.

## Discussion notes

Three probes, all resolved with real examples from Mehdi's own work:

<div class="el-ol" id="bkmrk-load-bearing-walls-%2F">1. **Load-bearing walls / framework for change.** Worked through a 40-page static HTML site: nav duplication and content-in-markup are decorative under "keep the site current," but become load-bearing the moment a requirement like i18n arrives — because now every hardcoded string is something a translation process must touch. Mehdi's own framing: *"the whole HTML content became a load bearing \[wall\] that was not before."* Fix while cheap = separate content from structure (externalize strings) before the requirement lands, not after.
2. **A/T mixing, case 1 — ORM naming.** Mehdi had custom ORM functions named after domain concepts (`claim()`, `filing()`, `process()`) instead of technical ones. Domain vocabulary had leaked into the T-architecture's *provided interface names*, so a later business-vocabulary change forced a rename across every consuming service — a technical migration that should have been invisible to callers instead broke all of them. Fixed by renaming the technical side back to technical terms; the rename itself was costly precisely because the interface names had become a de facto contract.
3. **A/T mixing, case 2 — `SaveToGoogleDrive()`.** A different but related failure: the interface was named after a *required* dependency (Google Drive) rather than the *capability* it provides. No adapter layer existed, so when the provider needed to change to OneDrive, the fix was hunting down and manually verifying every call site — the cost an independent-interface-plus-adapter design would have avoided by confining the change to one new adapter.

</div>**Cross-link**: LG 1-8 (implicit assumptions → explicit statements) is the same phenomenon as the METR paper's "AI lacks tacit repo context" ([digests/metr-early-2025-ai-developer-productivity-rct](app://obsidian.md/digests/metr-early-2025-ai-developer-productivity-rct)) — knowledge that lives only in maintainers' heads, or in a function name nobody questioned, is invisible to any newcomer, human or AI.

## Concepts

<div class="el-ul" id="bkmrk-concepts%2Fsoftware-ar">- [concepts/software-architecture](app://obsidian.md/concepts/software-architecture) — architecture as inherent, framework for change, load-bearing walls relative to anticipated change
- [concepts/building-blocks-and-interfaces](app://obsidian.md/concepts/building-blocks-and-interfaces) — the three characteristics, interface completeness, interface definer types, the `DocumentStore` example
- [concepts/architectural-views-and-levels](app://obsidian.md/concepts/architectural-views-and-levels) — IEEE 42010 description model, four levels, A/T blood groups
- [concepts/twin-peaks-model](app://obsidian.md/concepts/twin-peaks-model) — requirements/architecture co-evolution, four non-sequential design activities (light for now — grows with ch. 3)

</div>## Open questions

<div class="el-ul" id="bkmrk-%22test-your-knowledge">- "Test your knowledge" section (LG 1-1 … 1-10) not yet used — good material for a future review session.
- How do the four architectural levels map onto the Twin Peaks design activities? Revisit in ch. 3 (Designing Software Architectures).
- When is it worth paying for an independent interface + adapter *upfront* versus accepting the risk and refactoring later? (effort vs. risk tradeoff — ties into Twin Peaks' point that estimates only firm up once a draft architecture exists)

</div>