- State the goal, constraints, assumptions, and unknowns.
- If ambiguous, document a safe default and/or list options with trade-offs.
- Build only what is required to meet the goal now.
- Avoid speculative features and unnecessary abstraction.
- Minimize dependencies; justify each new dependency or custom build by overall risk and maintenance cost.
- Change only what the goal requires, plus necessary integration points.
- Defer unrelated refactors unless they block delivery.
- Follow existing conventions; if none exist, define lightweight ones and apply consistently.
- Define success criteria (acceptance criteria, metrics, or observable outcomes).
- Validate with automated checks when feasible; otherwise provide clear manual verification steps.
- Record what was verified and what remains unverified.
- Handle realistic edge cases at I/O boundaries and external interactions.
- Treat "impossible" cases as assumptions and document them.
- Address quality attributes as needed (security, privacy, reliability, performance, compliance), scaling rigor to impact.
- Apply KISS: prefer the simplest architecture and design that fit the current scope and risk.
- Apply YAGNI: do not add extension points, layers, or abstractions before there is a concrete need.
- Separate concerns: keep business logic distinct from I/O boundaries (HTTP, CLI, files, databases, external APIs).
- Prefer high cohesion and low coupling.
- Hide internal implementation details behind clear contracts.
- Design for testability: structure code so core behavior can be verified without heavy reliance on frameworks, networks, or global state.