a dotfile but it's really big
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

opencode/skills: software architecture simplification

karitham 1b2464c0 6c160041

+39 -50
+39 -50
modules/opencode/skills/software-architecture/SKILL.md
··· 1 1 --- 2 2 name: software-architecture 3 - 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. 3 + description: > 4 + 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. 4 5 --- 5 6 6 - ## Deep Modules 7 + # Software Architecture Protocol 7 8 8 - Hide complexity behind simple interfaces. A module's value is what it hides, not what it exposes. 9 + This skill provides a systematic framework for managing complexity and side effects in software systems. 9 10 10 - - Interface SHOULD be smaller than implementation 11 - - Users SHOULD NOT need to understand internals 12 - - If callers MUST understand your code to use it, the abstraction has failed 13 - - SHOULD prefer few powerful primitives over many specific ones 11 + ## 1. The "Impure-Pure-Impure" Sandwich Pattern 14 12 15 - ## Small Interfaces 13 + All non-trivial operations MUST follow this sequential workflow to isolate side effects from business logic. 16 14 17 - Minimize surface area. Every public thing is a commitment. 15 + ### Workflow 18 16 19 - - Fewer parameters, fewer methods, fewer exports 20 - - What isn't exposed can be changed freely 21 - - When in doubt, hide it 22 - - MUST seal internal details: unexported types, private fields, package-internal functions 17 + 1. **Gather (Impure Boundary):** Fetch all external state required for the decision. 18 + - _Examples:_ DB queries, API calls, reading system time, generating UUIDs. 19 + 2. **Process (Functional Core):** Pass the gathered data into a pure function. 20 + - _Constraint:_ This function MUST be deterministic. It MUST NOT perform I/O or access global state. It MUST return data or a "Result" struct. 21 + 3. **Commit (Impure Boundary):** Persist the output of the Functional Core. 22 + - _Examples:_ DB writes, sending HTTP responses, logging. 23 23 24 - ## Make Illegal States Unrepresentable 24 + ## 2. Resource-Aware Orchestration 25 25 26 - Parse, don't validate. Transform input into types that guarantee invariants. 26 + Operations MUST be ordered to minimize the surface area of high-latency or locking operations. 27 27 28 - - If a value exists, it's valid—no downstream checks needed 29 - - SHOULD use sum types, newtypes, and enums to constrain possible values 30 - - Bad states SHOULD be compiler errors, not runtime bugs 31 - - Example: `PositiveInt` not `int` with a check; `Pending | Approved | Rejected` not `string status` 28 + - **Short-Circuiting:** Cheap local checks (logic/time guards) MUST occur before expensive remote checks (Network/API). 29 + - **Lock Minimization:** Database transactions (`WithTx`) SHOULD only wrap the final "Commit" phase. 30 + - **Dependency Rejection:** Business logic SHOULD accept raw data structures (e.g., `[]int`) rather than behavioral interfaces (e.g., `UserStore`) to avoid unnecessary "Interface Soup." 32 31 33 - ## Fail Fast at Boundaries 32 + ## 3. Structural Standards 34 33 35 - Validate at system edges, assume valid inside. 34 + ### Module Depth (Ousterhout's Principle) 36 35 37 - - MUST reject bad input immediately with clear errors 38 - - MUST NOT propagate garbage deeper into the system 39 - - Boundaries: API handlers, CLI args, file parsers, external service responses 40 - - Once past the boundary, code can trust the data 36 + - Modules MUST be "Deep": simple interfaces hiding significant internal complexity. 37 + - If an interface is as complex as its implementation, the abstraction SHOULD be removed (Compression-Oriented Design). 41 38 42 - ## Design It Twice 43 - 44 - Before implementing, explore at least two approaches. 45 - 46 - - First idea is rarely the best—bias toward familiar patterns 47 - - Sketch alternatives, compare tradeoffs 48 - - Consider: complexity, performance, extensibility, testability 49 - - Pick the simplest one that solves the real problem 39 + ### State Integrity (Parse, Don't Validate) 50 40 51 - ## Coupling & Cohesion 41 + - Invariants MUST be enforced via the type system (e.g., `NewUserID(string)` vs a raw `string`). 42 + - Invalid states SHOULD be unrepresentable. Use Enums/Sum types instead of multiple dependent Booleans. 52 43 53 - - High cohesion: things that change together, stay together 54 - - Low coupling: modules MUST NOT know about each other's internals 55 - - MUST avoid circular dependencies 56 - - One responsibility per module—if you can't summarize it in one sentence, split it 44 + ## 4. Feedback Loop: Refactoring Pattern 57 45 58 - ## Compression-Oriented Design 46 + When refactoring existing code to this standard: 59 47 60 - Write the direct solution first without abstracting. After the code exists, look for repeated _shapes_ of logic. 48 + 1. **Identify Side Effects:** Find all hidden I/O (e.g., `time.Now()`, `db.Get`). 49 + 2. **Lift I/O:** Move those calls to the caller or the entry point of the function. 50 + 3. **Purify:** Convert the remaining logic into a pure function that accepts the lifted data as parameters. 51 + 4. **Verify:** The core logic MUST be unit-testable without a mocking framework. 61 52 62 - - An abstraction is only valid if it reduces total code 63 - - Do not recognize a pattern from elsewhere and apply it to the current problem 64 - - Interfaces SHOULD be general-purpose rather than special-purpose—but do not design general-purpose interfaces upfront 53 + --- 65 54 66 - ## Before You Code 55 + ## Validation Checklist 67 56 68 - 1. What changes and what stays the same? Put them in different places. 69 - 2. What's the simplest interface that covers the use case? 70 - 3. What can go wrong? How does the system recover? 71 - 4. How would you test this? 72 - 5. Will this be testable? 73 - 6. What will be hard to change later? Make that explicit. 57 + - [ ] **Sandwich:** Is there a clear line where I/O ends and logic begins? 58 + - [ ] **Purity:** Does any business logic function take a `Context` or an interface that performs I/O? (It shouldn't). 59 + - [ ] **Ordering:** Are network calls happening inside a database transaction? (They shouldn't). 60 + - [ ] **Guard Clauses:** Is the code calling an external API before checking simple local requirements? 61 + - [ ] **Types:** Are we using primitives where a domain-specific type could prevent a bug? 62 + - [ ] **Mocks:** Can this logic be tested with simple value assertions?