When a developer first opens an unfamiliar service, the first question is "what does it actually do?". The answer usually has to be pieced together: reading the code, looking into the database, asking colleagues. This wastes time and leads to mistakes.
A specification is a single document that answers this question. It describes one Bounded Context in domain terms: what the service can do, what roles it has, which events it publishes, how the lifecycle of its entities is organized. The business analyst, the architect and the developer fill it in together; an AI agent uses it as a source of memory and generates code from it.
Why not just a README
A README describes "how to run it". A specification describes "what it is and why it is built this way".
Without a spec, the same thing is named differently in the code, in the tasks and on calls. A new developer does not know where the boundary of the service is — what it should do and what it should not. An AI agent cannot generate correct code without knowing the business rules.
The specification solves three problems:
- it fixes a single language — the same terms in code, documents and conversations;
- it sets the boundary of the service — what is inside, what is outside, what is on the seam;
- it becomes a source for generating code, tests and diagrams.
Format principles
Before looking at the structure, it is important to understand a few rules it rests on.
The unit of a spec is a Bounded Context, not an aggregate. One context = one spec. If a context has several aggregates, the spec is split into a root file (context-level sections) and one file per aggregate. Splitting one context into several specs is not allowed: Ubiquitous Language, Context Map and roles are properties of the whole context.
Domain without technology. Every section except "Technical implementation" is written in domain terms. Frameworks, annotations, the database schema, topics and the stack belong only to the "Technical implementation" section. This keeps the spec understandable to the business and prevents it from going stale when technologies change.
Nothing is duplicated. Each fact lives in one place; the rest reference it. Events are described on the owning aggregate; transitions in the lifecycle matrix; errors in commands and business rules.
Cards + strict tables. Commands, queries and use cases are ### Name cards with tagged fields: readable to a human, parseable by an agent. Structural sections (access, events, integrations) are tables.
File layout
The spec lives in docs/spec/ and is always split into two levels:
docs/spec/
<service>-spec.md # root: context-level sections
aggregates/
<aggregate>.md # one file per aggregate: aggregate-level sections
An aggregate always lives in a separate file aggregates/<name>.md, even if there is only one. At Levels 1–2, where there are no DDD aggregates yet, this file holds the central entity of the context (product.md, notification.md). The "root + aggregates/" structure is the same across all specs.
At the top of each file you add a minimal frontmatter to identify the chunk during indexing. For the root — context, bounded-context, level. For an aggregate file — context, aggregate, level.
What goes into the root file
The root file describes the context as a whole. It consists of eleven sections:
| # | Section | What is inside |
|---|---|---|
| 1 | Bounded Context | mission, subdomain (Core/Supporting/Generic), owner, aggregates table, what is inside/outside the boundary, seams |
| 2 | Integrations (Context Map) | context map (mermaid) + edges table + links to contracts (OpenAPI/AsyncAPI) |
| 3 | Ubiquitous Language | context glossary; "not to be confused with" |
| 4 | Roles and access | roles, shared ABAC rules, PII; access to operations lives in each aggregate |
| 5 | Domain events | contract of the published language: aggregate → external events → topic |
| 6 | Use Cases | end-to-end scenarios as ### UC-N cards |
| 7 | Processes | Saga/Process Manager: cross-aggregate processes, sequence diagrams |
| 8 | UI specification | link between statuses and UI, error texts for the user |
| 9 | Acceptance criteria | Given / When / Then |
| 10 | Non-functional requirements | performance, availability, consistency, security, observability — target values without tooling |
| 11 | Technical implementation | the only technical section: C2 containers, stack, DB schema (ER + indexes) |
What goes into an aggregate file
Each aggregate is described in seven sections:
| # | Section | What is inside |
|---|---|---|
| 1 | Domain model | aggregate root + entities, value objects (via their invariant), class diagram |
| 2 | Lifecycle | statuses + transition matrix (commands, events, policies/timeouts) + state diagram |
| 3 | Access | "operation × role" matrix + ABAC |
| 4 | Business rules | list of BR-<prefix>NN with type, command and error code |
| 5 | Commands | cards: Transition · Input · Preconditions · Logic · Emits · Errors |
| 6 | Domain events | table "event — trigger — scope — subscribers" |
| 7 | Queries | cards: Question · Parameters · Returns · Logic (read source, filter, consistency) |
A few important conventions
Value Objects are described through their invariant. Not through fields and types (that is the database's domain), but through what they protect. For sum types — the variants: Discount: Percentage | Fixed.
Business rules are coded with an aggregate prefix. BR-O01, BR-D01 — so that codes do not clash between aggregates.
Commands in "Commands" are only the public ones. Reactions to events and policies (timeouts) are not separate cards. They are already reflected in the transition matrix and in "Domain events".
There is no error catalog. Error codes live in "Commands" (the "Errors" field) and "Business rules"; the texts for the user live in "UI"; the HTTP mapping is an artifact of the API, not of the spec.
Cross-aggregate references are by ID, without foreign keys. <other>_id is a logical reference; this is reflected both in class diagrams and in the ER.
Maturity level — how much to fill in
A spec does not require the same depth for every service. The depth is determined by the maturity level of the service:
| Level | Architecture | What changes in the spec |
|---|---|---|
| 0 — As-is | reverse-engineered from code | a snapshot "as it is"; not-declared for gaps; the starting point for migration |
| 1 — Layered | Controller → Service → Repository | no DDD aggregates (one "module"); "Domain model" = ER + tables; "Domain events" and "Processes" are skipped with a note |
| 2 — Use Case Pattern | UseCase + Handler | commands = UseCase classes; events — only if actually published; CQRS + Read Model is an option of this level |
| 3 — DDD + Hexagonal | aggregates, domain events, ports/adapters | full depth, split by aggregate |
A section that does not apply at the current level is not silently skipped — you write the heading and a one-line note: "Not applicable at Level 1".
Level 0 — as-is from code. This is a special mode: a spec for an existing service without a business brief is generated by the ucp-spec-tier-0 skill, reading the code, migrations and configuration. It sets level: 0, does not invent aggregates where the code has no domain logic, and marks missing data with the literal not-declared. This is the starting point for moving up to the required level.
In short
- A spec describes one Bounded Context in domain terms; business rules, roles, events, lifecycle.
- Files are split into the root (context level) and
aggregates/<name>.md(aggregate level); the structure is the same for all maturity levels. - Everything that is not "Technical implementation" is written without mentioning frameworks, annotations or the database schema.
- Commands, queries and use cases are
### Namecards; structural sections are normalized tables. - Business rules are coded with an aggregate prefix (
BR-O01); each fact lives in one place. - The depth of the spec = the maturity level of the service (0–3); non-applicable sections are marked, not skipped.
What to read next
- Marketplace case study — filled-in examples of specs for real services.
- Maturity levels — how Levels 1, 2 and 3 differ.
- DDD strategic patterns — context boundaries and the Context Map.
- Setting up the skills —
ucp-spec-design(from a business description),ucp-spec-tier-0(from code),ucp-spec-review(review).