## Engineering terms: code design

From Vibe Coding 101 (https://vibe-coding-101.brighteng.org/). When a request describes one of these problems, use the named technique, say its name, and run its check. Each term's page has the full entry and a prompt.

- **deep module**: A small interface hides a substantial useful implementation. Use when: Callers must understand too many implementation details. Do: Compare the caller contract with the hidden work. Check whether a caller can complete a task with less knowledge. Avoid: A one-line forwarding wrapper adds another interface to learn.
- **seam**: A place where behavior can be replaced without editing its consumer. Use when: Tests need an alternative clock, storage system, or external service. Do: Introduce the smallest useful substitution point. Check the same consumer with two implementations. Avoid: Adding an interface everywhere creates indirection without useful substitution.
- **composition**: Build a variation by assembling smaller parts. Use when: A component accumulates flags for every screen that uses it. Do: Extract the shared parts and compose two real variants. Check that each arrangement remains understandable. Avoid: Tiny parts still depend on a single giant component full of mode checks.
- **compound components**: Related components cooperate through a shared parent. Use when: Callers need different arrangements of tabs, menus, or editors. Do: Define the parent contract and compose two layouts. Check coordination and accessible relationships in both. Avoid: Shared state works but the trigger and panel lose their accessible relationship.
- **component variants**: Name the supported forms of a component explicitly. Use when: Independent flags allow contradictory appearances or behaviors. Do: List valid configurations and encode them. Check that an invalid combination cannot be requested. Avoid: Every visual detail becomes a separate variant and the API grows again.
- **dependency injection**: Supply a collaborator through a contract instead of constructing it inside. Use when: The same behavior needs local, remote, or test implementations. Do: Name the operations the consumer needs. Check it with two implementations of that contract. Avoid: The interface copies every method of the underlying library.
- **type-driven development**: Type-driven development uses types to express a design before filling in its implementation. Use when: Invalid state combinations keep spreading through the code. Do: Model valid states and transitions, then implement against those contracts. Avoid: Assuming a type assertion validates data received at runtime.
- **separation of concerns**: Separate responsibilities that change for different reasons. Use when: One handler mixes transport, business rules, and persistence details. Do: Extract the responsibility that has multiple callers or independent reasons to change. Avoid: Moving code into more folders without changing its dependencies.
- **leaky abstraction**: An abstraction exposes details callers still need to understand. Use when: Application code repeatedly handles vendor-specific details despite a wrapper. Do: Identify the caller contract and translate the implementation detail at its boundary. Avoid: Wrapping every method while returning the underlying client through an escape hatch.
- **technical debt**: A design shortcut creates a continuing cost for future changes. Use when: A deliberate shortcut will make a known follow-up task harder. Do: Name the shortcut, its concrete cost, and the condition that would justify changing it. Avoid: Calling every stylistic disagreement debt without identifying a cost.
- **semantic versioning**: Semantic versioning communicates compatibility of a declared public API. Use when: A library release needs a version that reflects its effect on consumers. Do: Identify the public contract and evaluate compatibility before choosing the version. Avoid: Assuming version numbers prove compatibility without checking actual consumers.
- **breaking change**: A change requires an existing consumer to adapt. Use when: An API, file format, or shared function is changing. Do: Identify consumers, preserve compatibility where needed, and plan migration for intentional breaks. Avoid: Checking only the exported function signatures.
- **observability**: Expose evidence that helps explain a system’s behavior. Use when: Failures cannot be traced from a user action to the operation that caused them. Do: Choose a concrete diagnostic question and instrument only the evidence needed to answer it. Avoid: Collecting full sensitive payloads instead of the diagnostic fields needed.
- **architecture decision record**: An architecture decision record captures a consequential choice and its context. Use when: A design choice is likely to be revisited after its context is forgotten. Do: Record the decision when it is made and link it to the constraint or evidence behind it. Avoid: Writing a retrospective justification that omits the real tradeoff.
- **invariant**: A condition must remain true across permitted operations. Use when: Several paths must preserve the same business rule. Do: State the rule precisely, identify all writers, and test attempts to violate it. Avoid: Writing the rule only in a comment or form validation.
- **strategy pattern**: A strategy supplies interchangeable behavior behind a shared contract. Use when: The same workflow supports several algorithms or output formats. Do: Extract the varying behavior and verify callers against more than one implementation. Avoid: Building a class hierarchy when a small function parameter would express the variation.
- **adapter pattern**: An adapter translates an existing interface into the interface its caller expects. Use when: An application must support another vendor or an incompatible external API. Do: Define the application contract and translate external data and failures at the boundary. Avoid: Returning the raw vendor client to every caller for convenience.
- **service layer**: An application service coordinates a use case across entry points. Use when: HTTP endpoints and background jobs duplicate the same business workflow. Do: Extract the shared operation and keep its dependencies explicit. Avoid: Introducing unrelated persistence abstractions merely because a service was extracted.
- **repository pattern**: A repository exposes persistence operations needed by the application. Use when: Domain code depends on storage details that must change independently. Do: Define a small application-facing contract and test its required behavior against the real persistence implementation. Avoid: Re-exporting every query-builder method through another object.
- **feature-based organization**: Feature-based organization keeps related implementation close to the capability it serves. Use when: Changing one feature requires searching many unrelated global folders. Do: Identify the feature boundary and move related code without changing behavior or creating import cycles. Avoid: Assuming every internal subfolder is wrong even when the feature remains cohesive.
- **ownership**: Ownership defines which part of a program controls a value’s lifetime. Use when: A Rust change encounters ownership or lifetime errors. Do: Identify who should own the value and how long each borrower needs access. Avoid: Using shared ownership to avoid deciding which component actually owns the resource.
- **state machine**: A state machine describes states and permitted transitions driven by events. Use when: A workflow can become both saving and failed, or process an event in the wrong phase. Do: List states, events, and transition effects; decide how unexpected events are handled. Avoid: Maintaining a second set of flags that can disagree with the machine’s state.
