Principles, patterns and conventions for developing software at Code Sherpas. Each file is one standard, self-contained, and normative: it states what the code must look like and how to review it.
Repositories reference these files from their AGENTS.md / CLAUDE.md by URL. There is no versioning: consumers always read main, so a merge here reaches every repository in its next session.
https://raw.githubusercontent.com/code-sherpas/software-development-standards/main/<standard>.md
A consuming repository carries a section in its AGENTS.md that tells the agent to read the index and every standard in full at session start. That section has one canonical copy here: setup/agents-md-section.md. Copy it verbatim; do not adapt it per repository.
Current section version: 2
At session start an agent reads this index anyway, so it compares that number against the version stated at the end of its own copy at no extra cost. When they differ, the copy in the repository is stale and the agent says so instead of silently working from outdated instructions.
- Domain Entity — Define a domain entity as a domain object with a unique identity that persists over time, even as its attributes change.
- Domain Entity Aggregate Boundaries — When a domain entity relates to another domain entity, determine whether they belong to the same aggregate or to different aggregates, apply the correct reference style, and persist the boundary decision so it is available to future tasks.
- Domain Entity Reference Direction — When two domain entities are related, determine whether each entity needs a reference to the other based on its own domain responsibilities — invariants and behavior — not on read or query convenience.
- Domain Entity Reference Optionality — When a domain entity holds a reference to another domain entity, determine whether that reference is required or optional based on whether the entity can exist in a valid domain state without it.
- Domain Entity Typed IDs — Choose the right level of type distinction for domain entity identifiers based on whether the project's type system is nominal or structural.
- Domain Entity UUIDv4 IDs — Represent domain entity identifiers with UUIDv4.
- Immutable Domain Entities — Apply the immutable design pattern when writing or changing domain entities.
- Domain Service — When domain logic involves multiple aggregates and does not naturally belong to any single one of them, encapsulate that logic in a domain service.
- Bounded Context Layout — Source is organized by business context first and by layer second: domain, application, primary-adapter and secondary-adapter inside each context, with
domainpresent when there is one, nothing outside a context reaching into its adapters, andsharedtreated as where coupling hides. - Dependency Direction — Business logic declares the interfaces it needs and infrastructure implements them from outside: the interface belongs to the caller rather than mirroring a vendor's API, the clock and the id generator are dependencies too, and adapters at the edge translate and delegate rather than decide.
- Business Logic — Define business logic as the rules, algorithms, and workflows in software that govern how data is created, stored, and transformed so real business policies become automated actions.
- Typical Domain-Entity Entry Points for Business Logic — When deciding which business-logic entry points to implement around a domain-entity type, prefer a standard set of entry points instead of inventing ad hoc operations.
- One Entry Point per Module for Business Logic — Every business-logic entry point must be a separate module.
- Prefer Top-Level Functions for Business Logic Entry Points — When implementing a business-logic entry point, prefer a top-level function over a class or object, as long as the project stack allows it without introducing friction.
- Command-Query Separation for Business Logic Entry Points — Apply Command-Query Separation at entry points to business logic.
- CQS Handler Signatures for Business Logic Entry Points — Apply a consistent signature pattern to business-logic entry points when Command-Query Separation is already the governing design rule.
- Colocate Types with Business Logic Entry Points — When a business-logic entry point has parameters or return values whose types are declared by the project, those types must live in the same file as the entry point, as long as the project stack allows it.
- Primitive Input Types for Business Logic Entry Points — The fields of command and query types accepted by business-logic entry points must use only primitive or basic types.
- Domain Entity Payload Types for Business Logic Entry Points — When a business-logic entry point returns domain-entity data, the payload type must be the domain-entity type itself.
- Ensure Business Constraints for Business Logic — Write business-logic business constraints with a consistent
ensure ...formalism translated into the syntax and naming conventions of the language in use. - Ensure Authenticated Requester for Business Logic Entry Points — Every business-logic entry point that requires authentication must include an
ensure requester is authenticatedbusiness constraint as the first check before any other business logic runs. - Ensure Authorized Requester for Business Logic Entry Points — Every business-logic entry point that requires authorization must include an
ensure requester is authorizedbusiness constraint, placed after authentication and before any business operation runs. - Execution Context for Business Logic Entry Points — Every business-logic entry point must set up an execution context that makes request-scoped data implicitly accessible from any point in the execution chain.
- Database Transaction for Business Logic Entry Points — Every business-logic entry point that interacts with a database must wrap its entire flow in a single database transaction, when the underlying persistence technology supports transactions.
- Transaction Isolation Levels for Business Logic Entry Points — When a business-logic entry point wraps its flow in a database transaction and follows Command-Query Separation, set the transaction isolation level based on whether the entry point is a command handler or a query handler, when the underlying persistence technology supports configurable isolation levels.
- Use Repositories in Business Logic Entry Points — Every business-logic entry point that persists, retrieves, or deletes domain entities must do so exclusively through repositories.
- Repository Interface for Business Logic Entry Points — Business-logic entry points must depend on a repository interface — not on the concrete repository implementation.
- Repository Domain Types for Business Logic Entry Points — The types exchanged between business-logic entry points and repositories must be domain-entity types and domain value types.
- Repository Operations for Business Logic Entry Points — Repository interfaces must expose a standard set of operations with clear naming, explicit intent, predictable return types, and a strict error model.
- Repository No Business Logic — Repositories must contain no business logic.
- Write Persistence Representations — Write or update the persistence representation that maps application data to the storage technology in use.
- Built-In Temporal Types — Represent temporal values with built-in or standard-library temporal types that match their real meaning.
- UTC Zoned Temporal Types — When the stack supports timezone-aware built-in or standard-library temporal types, use those types and set their timezone to UTC.
- ISO 8601 Millisecond Precision — When a temporal value must be represented textually, use ISO 8601.
- Neverthrow Return Types — Model every compatible function and method with
neverthrowreturn types. - Neverthrow Exception Wrapping — Capture recoverable exceptions with
neverthrowhelpers instead of ad hoctry/catch. - Prefer Named Functions Over Anonymous Functions — When defining a function, prefer a named function over an anonymous one, regardless of the technology stack.
- Failure Reporting — A component fails whenever it cannot do its job, always; reporting that failure to observability is a separate decision, and it belongs to whoever knows the context, because the same error is an incident for one caller and an expected outcome for another.
- Serialization Boundaries — What crosses a boundary loses the jurisdiction of the tools that enforced its guarantees, so design what crosses to keep naming the domain, rebuild the type on arrival through one door, and handle both ways a remote call fails.
- Accessibility — Every interface shipped is accessible, targeting WCAG 2.2 Level AA: native semantics before ARIA, full keyboard operation, visible and unobscured focus, contrast and reflow, announced dynamic changes, and the criteria WCAG 2.2 added for dragging, target size, redundant entry and accessible authentication.
- Internationalization — Every string a person reads is localized in every supported locale: the locales are one list everything else derives from, keys are typed so a missing one does not compile, each concept has one catalogue and one resolver, the database stores keys rather than display text, and sentences are whole messages rather than concatenated fragments.
- State-Coherent Prototypes — A prototype that stands in for a product behaves like it: every interaction, path and state works, everything shown derives from one state and stays coherent across screens, roles and device modes, what cannot be clicked is simulated and marked as outside the design, and it meets the same generic UI/UX and accessibility baseline as the product, verified with scenario scripts, random exploration and checks in every viewport.
- Automated Testing — Where the testing rules live, starting with what makes a test worth having: it fails when the behaviour it describes breaks and not otherwise, it asserts what a caller can observe rather than how the subject is built, it never asserts on a double, and each double names the situation instead of restating how it is assembled.
- Model-Interpreted Features — When a model interprets what the user asked, the feature ships a corpus and the corpus is the test suite: one table crossing the shapes an object can take with the kinds of request that stress it, split across unit tests that gate CI, a thin e2e subset, and an evaluation harness run by hand that names what it missed.
- Pre-Merge Gates — Before a change enters the main branch, six gates must hold: the change is ready to merge, its pre-merge plan was run by whoever integrates it, it was exercised by hand, any data migration was run against a representative dataset, any data it leaves behind ships the mechanism that removes it, and any entry point it adds or changes was called as someone who must be refused.
- Post-Deploy Verification — A change is done only once it has been used in production the way its real consumers use it — a person through the interface, another application through its contract, an agent through its path — on a deploy confirmed to carry it, with dedicated test accounts, and when it fails the person is told with the evidence and decides whether to revert.
- Stacked Changes — Stack a change only on a change it depends on, whatever the platform calls it — a pull request, a merge request: no global stack, one stack per line of work, every layer validated against the stack's base, merge the lowest ready layer one at a time, and a group lands only when all of it is verified and it is one change in production.
- Expand-Contract Schema Changes — During a deploy two versions run against the same storage, so a change that removes, renames or narrows anything stored is split across deploys — expand, migrate, contract — the contract step names the deploy that removed the last reader, and the repository enforces it with an automated check.
- Fixing Reported Errors — When an error is reported, first find out whether it is already resolved; if not, gather every fact the system holds, find its duplicates, reproduce it, and decide whether it is the application's error, the user's — whose report is removed and whose repetition is made less likely, never by making it the application's — or neither; record it as a defect task written in domain language, with root cause, reporting point, the layer that owns the fix and sourced evidence, and ask before starting the fix; then fix it at that layer with its regression test, and close the task only once the deploy is live and the error has stayed quiet.
- Collaborating Across Agent Sessions — When an agent is blocked by, or has a question about, work another agent session owns, it looks for a fix already in flight, finds the owning session and writes it a message that stands on its own, treats what it hears as information rather than permission, settles between sessions what is theirs to settle, and brings the person in only for what they cannot settle — with the diagnosis and the options ready.
- Integration Logic — Define integration logic as the code required for two independent applications to communicate.