···11---
22name: software-architecture
33-description: Design principles for non-trivial software design work. Covers deep modules, small interfaces, making illegal states unrepresentable, fail fast at boundaries, exploring alternatives before implementing, coupling and cohesion, compression-oriented design, and pre-implementation questions. Use when designing new components, refactoring existing systems, adding abstractions, or planning API surfaces.
33+description: >
44+ Technical protocol for designing and refactoring non-trivial software. Focuses on the Impure-Pure-Impure sandwich, lifting I/O, and resource-aware orchestration. Use when the user asks to "design a component," "structure code," "refactor a service," or "handle side effects." Keywords: functional core, imperative shell, dependency rejection, deep modules, short-circuiting.
45---
5666-## Deep Modules
77+# Software Architecture Protocol
7888-Hide complexity behind simple interfaces. A module's value is what it hides, not what it exposes.
99+This skill provides a systematic framework for managing complexity and side effects in software systems.
9101010-- Interface SHOULD be smaller than implementation
1111-- Users SHOULD NOT need to understand internals
1212-- If callers MUST understand your code to use it, the abstraction has failed
1313-- SHOULD prefer few powerful primitives over many specific ones
1111+## 1. The "Impure-Pure-Impure" Sandwich Pattern
14121515-## Small Interfaces
1313+All non-trivial operations MUST follow this sequential workflow to isolate side effects from business logic.
16141717-Minimize surface area. Every public thing is a commitment.
1515+### Workflow
18161919-- Fewer parameters, fewer methods, fewer exports
2020-- What isn't exposed can be changed freely
2121-- When in doubt, hide it
2222-- MUST seal internal details: unexported types, private fields, package-internal functions
1717+1. **Gather (Impure Boundary):** Fetch all external state required for the decision.
1818+ - _Examples:_ DB queries, API calls, reading system time, generating UUIDs.
1919+2. **Process (Functional Core):** Pass the gathered data into a pure function.
2020+ - _Constraint:_ This function MUST be deterministic. It MUST NOT perform I/O or access global state. It MUST return data or a "Result" struct.
2121+3. **Commit (Impure Boundary):** Persist the output of the Functional Core.
2222+ - _Examples:_ DB writes, sending HTTP responses, logging.
23232424-## Make Illegal States Unrepresentable
2424+## 2. Resource-Aware Orchestration
25252626-Parse, don't validate. Transform input into types that guarantee invariants.
2626+Operations MUST be ordered to minimize the surface area of high-latency or locking operations.
27272828-- If a value exists, it's valid—no downstream checks needed
2929-- SHOULD use sum types, newtypes, and enums to constrain possible values
3030-- Bad states SHOULD be compiler errors, not runtime bugs
3131-- Example: `PositiveInt` not `int` with a check; `Pending | Approved | Rejected` not `string status`
2828+- **Short-Circuiting:** Cheap local checks (logic/time guards) MUST occur before expensive remote checks (Network/API).
2929+- **Lock Minimization:** Database transactions (`WithTx`) SHOULD only wrap the final "Commit" phase.
3030+- **Dependency Rejection:** Business logic SHOULD accept raw data structures (e.g., `[]int`) rather than behavioral interfaces (e.g., `UserStore`) to avoid unnecessary "Interface Soup."
32313333-## Fail Fast at Boundaries
3232+## 3. Structural Standards
34333535-Validate at system edges, assume valid inside.
3434+### Module Depth (Ousterhout's Principle)
36353737-- MUST reject bad input immediately with clear errors
3838-- MUST NOT propagate garbage deeper into the system
3939-- Boundaries: API handlers, CLI args, file parsers, external service responses
4040-- Once past the boundary, code can trust the data
3636+- Modules MUST be "Deep": simple interfaces hiding significant internal complexity.
3737+- If an interface is as complex as its implementation, the abstraction SHOULD be removed (Compression-Oriented Design).
41384242-## Design It Twice
4343-4444-Before implementing, explore at least two approaches.
4545-4646-- First idea is rarely the best—bias toward familiar patterns
4747-- Sketch alternatives, compare tradeoffs
4848-- Consider: complexity, performance, extensibility, testability
4949-- Pick the simplest one that solves the real problem
3939+### State Integrity (Parse, Don't Validate)
50405151-## Coupling & Cohesion
4141+- Invariants MUST be enforced via the type system (e.g., `NewUserID(string)` vs a raw `string`).
4242+- Invalid states SHOULD be unrepresentable. Use Enums/Sum types instead of multiple dependent Booleans.
52435353-- High cohesion: things that change together, stay together
5454-- Low coupling: modules MUST NOT know about each other's internals
5555-- MUST avoid circular dependencies
5656-- One responsibility per module—if you can't summarize it in one sentence, split it
4444+## 4. Feedback Loop: Refactoring Pattern
57455858-## Compression-Oriented Design
4646+When refactoring existing code to this standard:
59476060-Write the direct solution first without abstracting. After the code exists, look for repeated _shapes_ of logic.
4848+1. **Identify Side Effects:** Find all hidden I/O (e.g., `time.Now()`, `db.Get`).
4949+2. **Lift I/O:** Move those calls to the caller or the entry point of the function.
5050+3. **Purify:** Convert the remaining logic into a pure function that accepts the lifted data as parameters.
5151+4. **Verify:** The core logic MUST be unit-testable without a mocking framework.
61526262-- An abstraction is only valid if it reduces total code
6363-- Do not recognize a pattern from elsewhere and apply it to the current problem
6464-- Interfaces SHOULD be general-purpose rather than special-purpose—but do not design general-purpose interfaces upfront
5353+---
65546666-## Before You Code
5555+## Validation Checklist
67566868-1. What changes and what stays the same? Put them in different places.
6969-2. What's the simplest interface that covers the use case?
7070-3. What can go wrong? How does the system recover?
7171-4. How would you test this?
7272-5. Will this be testable?
7373-6. What will be hard to change later? Make that explicit.
5757+- [ ] **Sandwich:** Is there a clear line where I/O ends and logic begins?
5858+- [ ] **Purity:** Does any business logic function take a `Context` or an interface that performs I/O? (It shouldn't).
5959+- [ ] **Ordering:** Are network calls happening inside a database transaction? (They shouldn't).
6060+- [ ] **Guard Clauses:** Is the code calling an external API before checking simple local requirements?
6161+- [ ] **Types:** Are we using primitives where a domain-specific type could prevent a bug?
6262+- [ ] **Mocks:** Can this logic be tested with simple value assertions?