TS-1: Software Requirements Specification

This technical standard covers best practices for defining and managing software requirements.

Without requirements or design, programming is the art of adding bugs to an empty text file.

– Louis Srygley

A software requirements specification (SRS), also known as a system requirements specification, defines the acceptance criteria (ACs) for a software system. It is the basis for the software’s design, implementation, and verification.

This technical standard covers two aspects of requirements specification:

  • What a good requirements specification looks like.
  • How a specification is managed over time so it stays an accurate, trusted description of the system as it actually is in production.

A reference implementation of everything described here is maintained separately at kieranpotts/specs.

Scope

A software requirements specification is two things.

  • It is a living description of what the production system does today.
  • It is a chronological record of the product decisions that shaped the current system.

Thus, the current specification is justified by the reasoning that produced it.

Living specification

A software requirements specification is a living document that answers the question: "What does the system do right now?"

A living specification is kept synchronized with the evolving software system itself. Changes to specification artifacts are implemented at the same time as the corresponding changes in code and configuration.

When a requirement changes, the specification is overwritten. The specification holds no history of its own. It only describes the system as it exists at a point in time.

Product decision log

A specification alone tells you what the system does, but not why.

A good software requirements management workflow keeps a second artifact alongside the specification — a product decision log.

Every significant proposed change — whether ultimately implemented or rejected — SHOULD be preserved as a record of the decision and its rationale.

The product decision log is the institutional memory of the product. When someone asks "why doesn’t the catalog support multiple currencies?" or "why did we reject open, unauthenticated access?", the answer is on record.

Implementation agnostic

A software requirements specification SHOULD be agnostic of implementation. It records what the system does and why, in the language of the business — but not how it is built, beyond what is strictly necessary to understand the system’s functional and non-functional requirements.

Technical decisions — about the technologies, tools, and architectural patterns used to construct, verify, and host the solution — SHOULD live separately. A request for comments (RFC) repository to capture technical decision making is a useful companion to the SRS that serves this purpose — see TS-3: Design Docs.

Keeping apart requirements and implementation means the specification artifacts will survive replatforming. Rebuild the system on an entirely different stack and its SRS need not change. Also, free of technical detail, the specification will be more valuable to its primary audience — not the technical teams, but the business people and other non-technical stakeholders.

Product requirements document

This technical standard distinguishes a software requirements specification (SRS) from a product requirements document (PRD). Both describe a system from the user’s perspective, and both stay light on implementation detail. What separates them is formality and completeness.

A PRD is an upstream artifact, owned by product management, that defines the problem in an informal format, plus desired outcomes and timelines for delivery. A PRD captures what a product should do and why it matters — the problem, the target users, the desired outcomes. It needs only enough detail to align stakeholders on broad goals and scope.

Like the SRS, a PRD is concerned with what the product must do, not how it will do it. It does not get into user interface design, data schemas, or other implementation detail.

A PRD typically covers:

  • Framing. Authors, purpose and scope, the stakeholders, the customer segment and target market, and a product overview.
  • Problem. What is to be solved, and the proposed solution.
  • Requirements. Use cases, and functional and non-functional requirements stated informally. Non-functional coverage typically spans performance targets, usability, technical constraints (security, network, platform, integration), environments, support, and how the product should interact with people and with other systems.
  • Givens. Assumptions, constraints, and dependencies.
  • Delivery. Success metrics, high-level workflow plans, timelines, and milestones.

Several of these topics — use cases, functional and non-functional requirements, assumptions, constraints, dependencies — reappear in the SRS. The difference is depth and obligation. The PRD raises this information in only as much detail as required to align stakeholders. The SRS makes the requirements precise and testable.

The SRS is the PRD’s successor. The SRS turns the product vision, defined in the PRD, into a verifiable, testable specification. The SRS is more refined than the PRD. It reads less like a product vision and more like a contract between the business and the technical teams.

Both stay implementation-neutral, but the SRS may lean slightly closer to it. A good SRS models the domain, and that domain model becomes the foundation for the software architecture. The SRS may also, where strictly necessary to state an acceptance criterion, touch on interfaces such as APIs. Certainly, design docs and test cases can be driven directly from the SRS.

Unlike the PRD, the SRS is maintained by technical stakeholders in collaboration with business stakeholders, and kept close to the code. The PRD originates from the business stakeholders and it is more ephemeral. The PRD need not be persisted once it has been translated into an SRS.

-

PRD

SRS

Question answered

What should the product do, and why?

What, precisely, does the system do?

Primary audience

Business stakeholders

Engineers and testers

Author

Product managers

Software architects and technical leads

Formality

Just enough to align on goals, scope, and priorities

Detailed and testable

Typical contents

Problem statement, resource constraints, prioritization, roadmap, user personas, success metrics and KPIs

Domain model, actors, functional requirements as testable scenarios, non-functional requirements as measurable thresholds

Executable tests

Executable tests complement requirements specifications, but they are not the same thing.

Executable tests can help to confirm that a system does what it is specified to do, but they don’t capture motivation, scope, or the alternative behaviors that were considered but ultimately left out-of-scope or subsequently deprecated.

Ideally, executable specifications would live directly in the SRS. Acceptance criteria would be written in a form that can also run as tests, eg. Gherkin scenarios backed by step definitions. If the specification and the verification are the same artifact, they cannot drift apart.

In practice, this isn’t always possible. Some acceptance criteria don’t map cleanly onto a test-runner format. Or the SRS may be maintained in a document system that can’t execute code. In these cases, the SRS SHOULD remain the source of truth for intent, and the requirements SHOULD be bound to their verification by cross-reference instead.

Persistence

Requirements documents have a tendency to rot over time. Written once at the start of a project, they are abandoned as the system evolves past them. Eventually they describe a system that no longer exists, and stakeholder trust in the specification deteriorates.

A specification can avoid that fate by binding itself to production.

To achieve this, a change to the specification artifacts MUST be merged in the same change-set as the corresponding changes to code and configuration. The specification and the behavior it describes move together, or not at all.

This requires specification artifacts to be kept close to the code, and for the maintenance of those artifacts to be deeply integrated into normal software development methods and tools, such that the specification evolves in lockstep with the system.

For this reason, this technical standard strongly RECOMMENDS that software specifications be persisted under the same version control system as used to manage change in the software’s code and configuration.

The payoff is a single, trustworthy answer to the question: "What does the system do right now?" New team members, support staff, auditors, and other stakeholders can read the software specification and have confidence that it is correct.

Wikis and issue trackers are commonly used to manage software requirements, but these keep the specification artifacts isolated from the source code and configuration, and subsequently the specification tends to drift from reality in time. Issue trackers are good for managing in-flight work but are poor as durable, structured records.

Using version control to manage specifications has other advantages, too. It provides:

  • Branching and review. A proposed change can be drafted and negotiated in isolation before it touches the specification on the main line.
  • An immutable history. A merged commit is a permanent, tamper-evident record, and past snapshots of the specification can be recovered.
  • Diffs. A reviewer can see precisely what a change does to the specification, rather than re-reading the whole document.
  • Blame and log. Any line of the specification can be traced back to the change and commit that introduced it. The specification’s changelog is built in to the version control substrate.

Minimal friction is added to the software development lifecycle, because the specifications live where the work already happens. And the SRS can be integrated into existing automation tools and delivery pipelines, further improving quality and productivity.

Best practice is to persist software requirements artifacts under the same code repositories as the software itself. Several placements are possible:

  • Colocated inside the service or module (eg. src/specification/). The specification is versioned with the code. One revision or merge updates both behavior and specification atomically. This is RECOMMENDED for a single service or a small monorepo.
  • A dedicated top-level folder in a monorepo (eg. /specification/ at the root of a repo that covers several software components). This is appropriate when multiple components or services share a single product-level specification.
  • A separate repository. This becomes necessary when a codebase spans multiple repositories. It is also necessary where non-technical stakeholders need read access to the specification but should not also have write access to the code. This separation adds costs to coordinate code and specification changes, so it is NOT RECOMMENDED until the separation becomes necessary.
my-service/
├── src/                      ← Production code.
├── specification/            ← Specification artifacts.
│   ├── context/
│   └── requirements/
├── proposals/                ← Product proposals archive.
└── README.md
Example repository structure

Generated views

One of the trade-offs of persisting software requirements under version control is that they become less accessible to non-technical stakeholders.

Where product owners, auditors, or external partners need a readable view of the specification, but should not be able to edit it directly, you SHOULD publish a generated, read-only rendering as part of an automated build step.

The ./specification/ tree remains the single source of truth, but different views can be generated as derived artifacts for different audiences.

Responsibility

A requirements specification MUST be written and maintained collaboratively, between the customer — or a representative, such as a product owner — and the software development team. Ownership, however, lies with the technical teams. They are responsible for maintaining the specification and keeping it synchronized with production.

The collaboration is necessary because the line between business requirement and technical design is blurry. For example, "passwords must be strong" is a business requirement, but to realize it requires decisions on business rules that require technical expertise, but business stakeholders may still care about those business rules. Examples:

  • Minimum 12 characters.
  • No maximum length below 64 characters.
  • Checked against a corpus of known-breached passwords, and rejected on a match.
  • No mandatory composition rules, and no forced periodic expiry.

The last of these shows why collaboration is so necessary. Composition rules and expiry are what most stakeholders expect "strong" to mean, but current guidance holds that both weaken security in practice, by pushing users toward predictable substitutions and incremental variations. This is a design trade-off that requires product and technical stakeholders to collaborate on. The business owns the risk appetite, and the development team knows the industry best practices.

It follows that the development team’s role is not simply to implement whatever is asked for. Where a stated requirement would not achieve the outcome behind it, saying so is part of the job.

Good engineers build to requirements. Great engineers push back on requirements.

– Jeff Bezos (2018)

Acceptance criteria

Requirements SHOULD be written as acceptance criteria. Acceptance criteria — also known as conditions of satisfaction — are the set of conditions a software system must satisfy to be accepted by the customer. They are written to be understandable to business stakeholders, and are used to define the scope of the software system.

Acceptance criteria SHOULD be written from the perspective of the end users of the software. They define user needs and wants, not prescribe specific solutions. This means ACs SHOULD NOT include technical implementation details, or even make reference to software.

Acceptance criteria SHOULD be written in a consistent style and set of conventions, and MUST be written in the language of the business domain, so they are understood by business stakeholders.

Acceptance criteria SHOULD capture requirements, not convey instructions for what the business wants implemented in software. A useful analogy is software development teams as teams of doctors rather than waiting staff in a restaurant. A waiter tries to give customers exactly what they ask for. A doctor discovers and learns about an underlying problem, then prescribes a solution.

This distinction matters because the work sometimes involves having difficult conversations, such as telling a customer that requested changes cannot be made within the allocated time and budget. Success in delivering software depends on development teams being able to represent their rational viewpoint, so the customer can make better, informed decisions.

Acceptance criteria MUST cover both functional and non-functional (quality) requirements. What the software does (specified via functional requirements), combined with the constraints within which it must operate (specified via non-functional requirements), is the whole of what the system is obliged to deliver.

A solution is said to be "correct" if it meets both its functional and non-functional acceptance criteria. See also:

  • TS-13: Functional Testing, which covers best practices for the verification of a software system against its functional requirements.
  • TS-14: Performance Testing, which covers best practices for verifying that software meets its non-functional (quality) requirements.

Requirements SHOULD be specified using consistent acceptance criteria definition conventions. These conventions differ slightly between functional and non-functional requirements.

Structure of a specification

A specification SHOULD be organized so that every category of requirement has a single, unambiguous home. The objective is for no requirements to be left undocumented.

The taxonomy below is RECOMMENDED by this technical standard.

specification/
│
├── context/              Descriptive — the problem space.
│   ├── overview/           Mission, problem, scope.
│   ├── constraints/        Regulatory, legal, business; assumptions.
│   ├── model/              Domain entities, relationships, ERD.
│   ├── actors/             Participants and hierarchy. No permissions.
│   └── glossary/           Ubiquitous language.
│
└── requirements/         Prescriptive — the contract.
    │
    ├── behaviors/          Functional requirements.
    │   ├── features/         Scenarios.
    │   ├── rules/            Invariants, policies, lifecycle transitions.
    │   ├── access/           Which actors may exercise which capabilities.
    │   ├── interfaces/       External contract: operations, resources, events.
    │   └── journeys/         Wireframes and/or call-sequences.
    │
    └── qualities/          Non-functional requirements, grouped by
                            quality characteristic.

proposals/                Immutable archive of every proposed change.
Specification structure

The proposals/ tree is a sibling of specification/, not part of it. The specification describes the system as it is now. The proposal archive records why.

The top-level split is between context (what is) and requirements (what must be).

Context is descriptive. This part of the specification covers the domain, its vocabulary, its participants, and the conditions the system operates within. None of it is a requirement in its own right. Rather, the context is the shared frame of reference from which requirements are derived.

Requirements are prescriptive. Every statement is an obligation that can, in principle, be verified against the running system.

Keeping the two apart means a reader always knows whether they are reading background or a binding commitment. The separation prevents descriptive material from quietly accreting the force of a requirement.

Identifying requirements

Every requirement MUST have a stable identifier. Requirements are referenced constantly — from each other, from proposals, from tests, from tickets, and from code. Therefore, references MUST be unambiguous and durable.

This technical standard RECOMMENDS the following identifier scheme. Identifiers are assigned per category, using a single letter and a number:

  • F = Features (functional requirements).
  • Q = Qualities (non-functional requirements).
  • R = Rules (policies, invariants, lifecycle transitions).

Where a single artifact holds several independently-verifiable statements, the statements are numbered within it, giving a two-part identifier:

F3          The "reserve a product" feature.
F3.2        The second scenario within it.

Q1          Latency.
Q1.4        The fourth latency threshold stated.

R5          A single business rule.

Features and qualities are identified at both levels because the two levels are referenced for different purposes. A proposal edits a whole feature (F3). A test verifies one scenario (F3.2).

Rules are already atomic — one rule, one statement — so they take only a single-part identifier.

An identifier, once assigned, MUST NOT be reused for a different requirement, even after the original is removed. Identifiers, once assigned, are permanent. Gaps in a sequence represent requirements that once existed.

Identifiers are stable under renaming. A feature’s title, filename, and wording may all change without changing its identifier. This is what makes identifiers more durable than URLs and file paths.

The identifier SHOULD be stated at the head of the artifact it names, and the full set SHOULD be indexed so an identifier can be resolved to its requirement without searching the tree.

An identifier is retired permanently when its requirement is removed, but it MUST NOT be reused for new requirements.

Deprecated requirements

A requirement whose withdrawal has been agreed, but whose behavior is still in production, MUST be marked deprecated in the specification.

Only when the behaviors are fully removed from the production system should the corresponding requirements be fully deleted from the specification.

Deprecation markers SHOULD appear at the head of the artifact they apply to. Where a deprecation applies to a single scenario or a threshold, rather than a whole artifact, it is marked at the statement level.

A machine-readable form is RECOMMENDED where the format allows one — a @deprecated tag on a Gherkin feature or scenario, for example — so that deprecated requirements can be listed mechanically.

Context

The context part of a specification is descriptive. It establishes the problem space the system operates in and the domain the system is built around. None of it is a requirement in its own right. Rather, context is the shared frame of reference from which the requirements are derived.

The following sub-sections are RECOMMENDED.

specification/
│
├── context/              Descriptive — the problem space.
│   ├── overview/           Mission, problem, scope.
│   ├── constraints/        Regulatory, legal, business; assumptions.
│   ├── model/              Domain entities, relationships, ERD.
│   ├── actors/             Participants and hierarchy. No permissions.
│   └── glossary/           Ubiquitous language.
│
└── requirements/         Prescriptive — the contract.
Context structure

Overview

Start with a high-level description of the system, written in prose. This should cover:

  • The mission. The value the system delivers, and to whom.
  • The problem it solves.
  • Its scope. What is in, and — just as importantly — what is explicitly out.

The scope statement is particularly important. It’s what stops the specification quietly expanding to describe systems and responsibilities that belong elsewhere.

Constraints

Constraints describe the conditions imposed on the system from the outside. The conditions are the "givens" the system must work within, rather than behaviors the system is expected to exhibit (requirements, or "thens").

Conditions include:

  • Regulatory and legal constraints, such as applicable laws and standards (eg. data-protection law).
  • Business constraints, such as contractual terms and commercial boundaries.
  • Assumptions the specification depends on. If an assumption proves false, the affected requirements must be revisited.
  • Dependencies. External systems and services the system relies on.

Constraints are descriptive. A regulatory or contractual constraint may read like a requirement, but it is actually a boundary set by the world — a law, a contract, a dependency, a standing assumption — not a behavior the system chooses to exhibit.

Where a constraint implies a testable obligation on the system, state that obligation as a rule or quality, and cross-reference it from the constraint, so the constraint itself remains purely a statement of the boundary.

Model

The domain model documents the entities in the system’s domain — the categories of information the system stores — their meaningful attributes, and how they relate to one another. An entity-relationship diagram may be embedded to summarize the model visually.

The model anchors the project’s shared vocabulary and is the foundation from which the actors and features are derived.

The model declares the states an entity can hold (eg. a status of available, reserved, or sold), but the allowed transitions between those states should be defined separately in the business rules.

In practical terms, the model is where the SRS comes closest to the software architecture. There may be a lot of necessary overlap with the design docs here. After all, a sound domain model tends to become the backbone of the implementation.

For modeling techniques, see TS-4: Modeling.

Actors

Actors are the participants in the domain — the people, organizations, and external systems that have goals the system must serve. The actors are derived from the domain model.

Actors are typically arranged as a hierarchy, ordered by privilege. Privileges are inherited down the hierarchy. An actor holds every capability of the actors below it, plus whatever is granted to it directly.

Even small differences between actor types can have a significant impact on the system’s design and the effort required to build it, so the hierarchy is worth stating explicitly.

Actors describe who the participants are, but not what each is permitted to do. Permissions — the capabilities that each actor is allowed to access — SHOULD be specified separately under the requirements section. Actors, and their hierarchy, are part of the domain model, which is background context, while access rules are requirements that the system is expected to enforce.

Glossary

It is RECOMMENDED to include a glossary of terms that define the project’s ubiquitous language. Capture every domain term, each with a single agreed meaning that’s used consistently throughout the specification.

Consistent vocabulary is what keeps the model, the actors, and the requirements coherent. Using the same term to mean the same thing in the glossary, the model, and every scenario is one of the highest-leverage things a specification can get right.

The glossary is separate from the model, though there will be much overlap between the two. The model defines entities, but not every domain term is an entity. For example, reservation, credential, and currency are domain vocabulary with no structural definition. The glossary is the flat index of all domain vocabulary, including but not limited to domain entities and their properties.

For terms that are also domain entities, define the term briefly in the glossary and link to the model for the full entity structure.

Behaviors

Functional requirements ("behaviors") MUST be defined distinctly from non-functional requirements ("qualities").

The functional requirements of a software system are what it does from the perspective of the users who interact with it.

This technical standard RECOMMENDS that the behaviors of a system be documented across five complementary sections.

  • Features. Concrete, scenario-level behaviors.
  • Rules. Covers policies, invariants, and entity lifecycle transitions that hold across many features.
  • Access. Maps actors to functional capabilities they are permitted to access.
  • Interfaces. Defines the system’s external contract — the operations, resources, and events the system exposes.
  • Journeys. How features combine into end-to-end flows, from the perspective of different actors.
specification/
│
├── context/              Descriptive — the problem space.
│
└── requirements/         Prescriptive — the contract.
    │
    ├── behaviors/          Functional requirements.
    │   ├── features/         Scenarios.
    │   ├── rules/            Invariants, policies, lifecycle transitions.
    │   ├── access/           Which actors may exercise which capabilities.
    │   ├── interfaces/       External contract: operations, resources, events.
    │   └── journeys/         Wireframes and/or call-sequences.
    │
    └── qualities/          Non-functional requirements, grouped by
                            quality characteristic.
Behaviors structure

Features

Features document the concrete, scenario-level behaviors of the system.

It is RECOMMENDED that features be organized by actor. Because privileges are inherited down the actor hierarchy (see Actors), each feature can be specified once against the lowest-privileged actor that can exercise it.

Each feature takes an identifier (F1, F2, …), and each scenario within it a two-part identifier (F1.1, F1.2, …). The feature-level identifier is what a proposal edits and what a journey or interface refers to. The scenario-level identifier is what an automated test verifies.

Acceptance criteria for functional requirements can be captured in a variety of ways. A basic, unstructured list is a good starting point. For example, "As a newcomer I can register for an account by creating a login and providing my contact information" might be translated into the following technical condition of satisfaction:

  • A username must be provided.
    • It must be between 4 and 20 characters long.
    • It must be unique.
    • The username is case sensitive.
  • A password must be provided.
    • It must be between 8 and 20 characters long.
    • It may contain numbers, letters, and special characters.
    • It must contain at least one letter and one number.
  • An email must be provided.
    • The email must be provided in a valid format.
    • The email must be confirmed by double entry.
    • The email must not already exist in the system.

But this is not sufficient for complex systems, which need a more structured approach. This technical standard RECOMMENDS the Gherkin language to specify features. The language itself is covered in depth under [Executable specifications].

Whichever format is used, the following practices keep scenarios useful as specification:

  • Scenarios SHOULD be kept concrete. Prefer "then the response contains at most 10 records" over "then a reasonable number of records is returned."
  • A scenario SHOULD define one behavior. If a scenario has two unrelated Then outcomes, it is probably two scenarios.
  • The project’s ubiquitous language SHOULD be used. The same term MUST mean the same thing in the glossary, the model, and every scenario.
  • Unhappy paths SHOULD be covered too — empty results, not-found, unauthorized. The edge cases are where requirements are most often ambiguous.
  • Cross-cutting invariants SHOULD be lifted out of scenarios and into rules, then referenced. See Rules.

Rules

A feature is a concrete, scenario-level behavior. Policies (eg. "a pet can’t be reserved and sold at the same time") and validation constraints are cross-cutting concerns that tend to span multiple features. Specifying them at the level of features would duplicate them across every feature they touch, leading inevitably to drift.

The solution is to state policies and validation constraints as business rules in one central place. Each rule gets a single authoritative statement with a stable identifier (R1, R2, and so on) that can be cross-referenced from feature scenarios and elsewhere as necessary.

Similarly, entity lifecycle rules live under business rules, not in the domain model. The states that an entity can hold are part of its structure, so the domain model declares that they exist. But which state transitions are allowed, and who may trigger them, is a prescriptive constraint — a rule. Thus, the model defines the vocabulary of states, but business rules define the conditions under which those states may transition from one to another. A state-transition diagram is a good way to express an entity lifecycle.

State each rule so that it is unambiguous and, where possible, testable. Give each a stable identifier so it can be cross-referenced from other specification artifacts. For example:

  • R1 — Single status. A record has exactly one status at any time. It is never in more than one state at once.

Access

This section defines which actors have permissions to exercise capabilities of the system. It is RECOMMENDED to be defined as a simple actor-permission map.

Since permissions are inherited down the actor hierarchy (see Actors), a capability available to a lower-privileged actor can be assumed to be available to all higher-privileged actors. Therefore, it is sufficient to state each capability once, against the lowest-privileged actor that holds it. Example:

Capability

Minimum actor

List and filter the catalog

Authenticated User

Search the catalog

Authenticated User

Retrieve a single record by ID

Authenticated User

Reserve a record

Authenticated User

Withdraw a record from the catalog

Administrator

Read this as the floor, not the whole matrix. An Administrator can list, search, retrieve, and reserve as well, by inheritance, and stating so explicitly would be the duplication the hierarchy exists to avoid.

Access rules are defined separately to the actors to which they apply. That’s because, while actors are derived from the domain model (background context), access rules are rules that the system must enforce (requirements).

The capabilities are defined in the feature scenarios. Rules may be used to govern finer-grained conditions that may otherwise introduce unwanted complexity if defined in the actor-permissions map.

Interfaces

Interfaces describe the system’s external contract — the operations it exposes, the resources they act on, and any events it emits.

This SHOULD be a behavioral contract only. Protocol, transport, payload formats, and endpoint naming are technical design decisions, not requirements. State here only what a consumer can rely on: the operations available, their inputs and outputs (specified in domain terms), and the actors permitted to invoke them.

Journeys

While interfaces define the external contract of the system, journeys define the user experience.

The relative weight of journeys and interfaces depends on the system. For a user-facing product, interfaces may be thin and journeys rich. For a headless service, the reverse.

Wireframes MAY be used to specify the journey through UI systems, while annotated call sequences will be more appropriate for headless systems. These SHOULD be at the level of structure and flow. Pixel-perfect visual designs are downstream concerns that do not belong in a requirements specification.

Whatever the format, the journeys should show how the system’s features combine into coherent end-to-end flows — the paths a consumer follows to accomplish their goals. Whereas features specify what the system does, journeys show how those functions come together.

Journeys apply only where there is a multi-step flow worth documenting. A feature that is made available through a single atomic operation has no journey to be documented.

Qualities

The qualities of a system are its non-functional requirements (NFRs) — also known as quality requirements (QRs) or cross-functional requirements (CFRs). They define the operational constraints within which the software system is expected to work. They include, but are not limited to, security, performance (throughput, latency), scalability, availability, portability, data retention requirements, and so on.

The scope here is the system’s dynamic quality attributes — those that emerge at runtime and are observable externally, in the way the system behaves for real users in production. These are distinct from a system’s static quality attributes — internal qualities of the code and design (readability, modularity, naming, structure) that emerge at or before compile time and matter to developers rather than users.

Static qualities are out-of-scope for the requirements specification. They are not requirements the business specifies. Rather, they are design concerns, addressed through code design and refactoring. See also:

Organizing qualities

Qualities SHOULD be grouped by quality characteristic, using the product quality model of ISO/IEC 25010 as the RECOMMENDED taxonomy:

  • Functional suitability. Completeness, correctness, appropriateness.
  • Performance efficiency. Time behavior, resource utilization, capacity.
  • Compatibility. Co-existence, interoperability.
  • Interaction capability. Usability, accessibility, learnability.
  • Reliability. Availability, fault tolerance, recoverability.
  • Security. Confidentiality, integrity, authenticity, accountability.
  • Flexibility. Adaptability, scalability, installability.
  • Safety. Operational constraint, fail-safe, hazard warning.

This technical standard excludes the ISO/IEC 25010 category of maintainability (modularity, reusability, testability) from the taxonomy. These are static qualities — design concerns addressed through code design and refactoring, not requirements the business specifies. Where a maintenance concern is genuinely observable at runtime, such as the time taken to restore service after a fault, it belongs under reliability as a recoverability requirement.

Prioritizing qualities

Qualities MUST be prioritized relative to one another, in the same way functional requirements are. A specification that names a dozen qualities without saying which of them matter most has recorded a wish list, not a set of requirements.

Prioritization is necessary because qualities compete. Raising one often lowers another. Multi-factor authentication raises security and lowers usability. Caching a derived value raises performance and puts correctness at risk. Tuning for one runtime raises throughput and lowers portability. No design maximizes every quality at once, so every design resolves these conflicts one way or another. The only question is whether the business chose the resolution, or left it to whoever wrote the code.

The RECOMMENDED unit of prioritization is the quality characteristic, not the individual threshold. Ranking the eight ISO/IEC 25010 characteristics against one another for a given product is an exercise a business stakeholder can reason about. Ranking forty individual thresholds is not.

The ranking is an input to design, not an output of it. Like any architecturally-significant requirement, it MUST be settled before the increment that depends on it is designed — see Architecturally-significant requirements.

Where two qualities conflict directly, the specification MUST state which one takes precedence, and the proposal that settled it MUST record why. A threshold on its own does not tell a future reader that something was given up to reach it.

This is a different axis of prioritization from the MoSCoW ranking of proposals awaiting implementation, which orders the delivery queue — see Prioritizing proposals. A quality may rank highest for the product and still sit low in the delivery order.

How a design absorbs one of these compromises, once it has been decided, is covered in TS-2: Software Design Qualities.

Value and cost

Every quality threshold has a price, and the price rises steeply toward the top of the scale. The level to specify is therefore not the highest level achievable, but the level at which the value of meeting it exceeds the cost of meeting it.

A quality goal SHOULD be justified by what failing to meet it would cost the business. Karl Wiegers describes a manufacturer whose process-control system had to run continuously, with no downtime acceptable at all. Meeting that requirement took two independent, redundant computer systems, so that one could be taken offline for updates while the other kept running. The redundancy was expensive, and still cheaper than halting production every time the control system went down.

That is the shape of the argument a quality requirement needs. "Availability of 99.99%" is a testable threshold, but an arbitrary one until somebody has said what an hour of downtime costs. Thresholds set without that figure tend to ratchet upward: no stakeholder asked to name a number volunteers a lower one, and the cost of the extra nines lands on a budget the person naming them does not hold.

Two practical consequences follow:

  • State the loss, then derive the threshold. The business stakeholder is the authority on what an outage, a slow page, or a breach costs. The threshold that follows from that figure is a technical judgment.
  • Record the level that was rejected. Where a higher threshold was considered and turned down on cost, the proposal SHOULD say so. Otherwise the question is reopened, and re-costed, at every subsequent review.

Where the cost in question is cloud infrastructure spend, see TS-50: Cloud Economics.

Architecturally-significant requirements

Many NFRs are architecturally significant, heavily influencing fundamental design choices such as technology stacks and databases. For example, scalability and durability require big up-front decisions on both the software architecture and the production infrastructure, eg. planning for redundancy in additional hardware, network, or bandwidth capacity.

For these reasons, non-functional requirements can be much harder to change than functional requirements (FRs) later in a project. A quality requirement that bears on the architecture MUST therefore be identified before the increment that depends on it is designed, rather than discovered once it is built.

Some NFRs, such as uptime guarantees, will also be reflected in Service Level Agreements (SLA) and are therefore significant to business stakeholders and customers.

Acceptance criteria

Like FRs, NFRs SHOULD be specified as acceptance criteria — measurable, testable benchmarks. For example, "the system handles 1,000 concurrent users" can be tested by simulating that load and measuring performance under it.

A quality requirement that cannot be measured cannot be verified, and an unverifiable requirement is just a wish. Wherever a quality is objectively measurable, it MUST be stated as a concrete threshold, ideally at a named percentile and load: "list responses within 300 ms at the 95th percentile under normal load," not "the API should be fast." Vague qualities give the false comfort of a requirement without the substance of one. The exception is the genuinely subjective — see Subjective quality requirements.

Each quality takes an identifier (Q1, Q2, …), and each threshold stated within it a two-part identifier (Q1.1, Q1.2, …). Thresholds are identified individually because they are verified individually. A single quality such as latency may state different budgets for different operations, percentiles, and load conditions, and each is a separate pass or fail.

Where possible, constraints should specify metrics against which the system can be benchmarked, such as Mean Time Between Failures (MTBF), Mean Time to Recover/Repair (MTTR), or the application’s crash rate and time to recovery. More indirect measurements such as the open/close rates of incidents, and user analytics such as shopping cart abandonment rate, may also be useful.

Alternatively, it may be possible to define acceptance criteria in terms of conformance to published standards, such as AES-256 (for encrypting sensitive data at rest), TLS 1.3 (for data in transit), the Web Content Accessibility Guidelines (for accessibility), and the EU General Data Protection Regulation (for the handling of personally-identifiable data).

Cite a specific version, and state the conformance level where the standard defines one — "WCAG 2.2 Level AA", not "accessible". A standard named without a version is not a testable threshold, and it ages badly.

Some quality attributes, such as aspects of security, authentication, and authorization, naturally lend themselves to verification using conventional functional testing techniques. These are best specified as user stories and included as part of the functional requirements. Authorization requirements, for example, can mostly be dealt with by specifying personas or roles and the permissions associated with each. Example:

As an administrator,
I want to change the email server settings via a UI
so that I don't need to raise IT change requests to do this
Example user story

Subjective quality requirements

Some NFRs are more nebulous and subjective. User experience is a case in point. UX is an entirely subjective quality, difficult to specify and measure in quantifiable terms. It must instead be satisfied through the development process — explicit user research, continuous testing via A/B strategies, or user satisfaction surveys.

Net Promoter Score (NPS) surveys can be a good option for keeping track of user sentiment. NPS is a simple, but powerful, tool to measure customer loyalty and satisfaction. It’s based on a single core question: "How likely are you to recommend our product/service/company to a friend or colleague?" Respondents rate their likelihood on a scale from zero (not likely) to 10 (extremely likely). Based on the responses, customers are categorized into three groups:

  • Promoters (score 9-10): Loyal customers who are likely to help fuel growth.
  • Passives (score 7-8): Satisfied but unenthusiastic customers who are vulnerable to competitive offerings.
  • Detractors (score 0-6): Unhappy customers who can damage your brand and impede growth through negative word-of-mouth (ie. actively discourage others from engaging).

The NPS is calculated with the formula:

NPS = % Promoters - % Detractors

NPS surveys often include an open-ended question, such as "What is the primary reason for your score?", to understand what’s working well and what needs improvement.

Verification

Where an objective quality requirement is verified by an automated check — eg. a load test, a security scan, an accessibility audit — the requirement SHOULD cross-reference that check, and the check SHOULD cross-reference the requirement it verifies, by its identifier.

Executable specifications

Wherever practical, it is RECOMMENDED to specify requirements in a format that allows automated verification without requiring tests to be maintained separately. This means the specifications themselves MUST be presented in a format that, as well as being accessible to humans, can also be parsed by machines and translated directly into generated, executable tests.

This technical standard RECOMMENDS the Gherkin format for this purpose. Gherkin is a testable specification format that can be parsed by machines and used as the basis for automated acceptance testing.

The Gherkin language was originally developed as part of the Cucumber testing framework, but it has since been adopted by other testing frameworks including SpecFlow, Behat, JBehave, and Lettuce.

Gherkin is a business-readable domain-specific language. It specifies a software system on a feature-by-feature basis from the point of view of different groups of users. One of the objectives of the language’s design is to provide a unified specification that can be written and understood by both technical and non-technical stakeholders. Gherkin provides a common standard for product managers and developers to collaborate on the core business requirements.

Gherkin SHOULD be used as much as reasonably practical to specify behavioral scenarios. It MAY also be used to specify some non-functional requirements, too, where those requirements can be reduced to an automate-able test.

The following documentation covers the basic features of the Gherkin language that are well supported by most BDD-style testing frameworks. This is the RECOMMENDED baseline syntax. Various test frameworks support additional extended syntax, which you MAY opt in to.

The canonical Gherkin specification lives at https://cucumber.io/docs/gherkin/reference/. Note this is the version supported by the latest release of Cucumber. Language support varies between other test frameworks.

Basic structure

A feature is described in a plain text file with the .feature extension, and a file MUST contain at most one Feature block. Gherkin is line-oriented — one statement per line, and line endings terminate statements or open new blocks. Structure is determined by keywords — Feature, Background, Scenario, Given, When, Then — not by indentation. Parsers ignore leading whitespace outside doc strings, so indentation is purely a readability convention. Nesting steps two spaces under their scenario, and scenarios two spaces under their feature, is the conventional layout and SHOULD be followed.

Business rules are structured by features, scenarios, and steps. A software feature may have multiple scenarios, and a scenario may play out over multiple steps.

Feature: <title>
  In order to <realize some business value>
  As a <user type>
  I want to <achieve some goal>

  Background:
    Given <state>
    (And <expectation>)

  Scenario: <title>
    (<optional description>)
    Given <state or precondition>
    (And <state or precondition>)
    When <event or action>
    (And <event or action>)
    Then <expected outcome>
    (And <expected outcome>)

  (further scenarios)
Template
Feature: Refund item
  In order to be confident in my purchases
  As a customer
  I want to receive refunds for faulty goods

  Scenario: A customer returns a faulty microwave
    Given a customer has bought a microwave for $100
    And the customer has a valid receipt
    When the customer returns the microwave
    Then the customer should be refunded $100
Simple example

Feature files

.feature files are the concrete, scenario-level functional requirements.

How much behavior belongs in one file is a matter of judgment. A file SHOULD be scoped to a single feature — but where a feature is large, it is better split across several files, each covering one coherent aspect of it, than allowed to grow into a scenario dump. The test is whether a reader can hold the file’s subject in their head.

Each file carries its own feature identifier, so a feature split across several files takes several identifiers. Identifiers name files, not concepts.

A scenario SHOULD NOT restate a business rule that is stated in the rules section. Better instead to cross-reference rules by their identifiers.

The opening lines of each .feature file MUST have the following structure:

Feature: <short description of feature>
  In order to <realize some business value>
  As a <user type>
  I want to <achieve some goal>

This describes the business value the feature delivers, in the format of an acceptance test — a twist on the classic user story formula, which traditionally has the following structure:

As a <user type>, I want to <some goal> so that <some reason>

A user story tells us who the feature is for, what the user wants to achieve, and why. Gherkin flips this format around, putting the business value at the start of the acceptance criteria.

In order to <realize some business value>
As a <user type>
I want to <achieve some goal>

This top section is for the benefit of humans. It is not used by machines in behavior-driven tests.

Scenarios

At this level, both user stories and Gherkin describe the problem, not the solution. But Gherkin goes further by describing how the problem will be solved in the software. This is done via one or more scenarios.

A scenario is a concrete example that illustrates a business rule, expressed as a sequence of steps. Scenarios MUST follow this pattern:

  • Describe an initial context.
  • Describe an event.
  • Describe an expected outcome.
Scenario: <some determinable business situation>
  (<optional description>)
  Given <state or precondition>
  (And <state or precondition>)
  When <event or action>
  (And <event or action>)
  Then <expected outcome>
  (And <expected outcome>)
Template

Scenarios are also tests. Each scenario is an executable specification of the system. Examples:

Scenario: A user posts to their blog
  Given a user is logged in
  When the user publishes a new blog post
  Then the user receives confirmation that the post was published
  And the post is published immediately

Scenario: A user schedules a post to be published in the future
  Given a user is logged in
  And the user has drafted a new blog post
  When the user schedules the post for a future date
  Then the user receives confirmation of the scheduled publication date
  And the post is not immediately published

Steps

Scenarios are composed of multiple steps. The number of steps per scenario is unlimited, but scenarios SHOULD aim for five or fewer steps. Any longer, and scenarios lose their expressive power as specification and documentation.

Each step MUST start with one of the following keywords:

  • Given
  • When
  • Then
  • And
  • But

An asterisk (*) MAY be used in place of any of these. It carries no meaning of its own, taking the sense of the preceding step, and is useful where a list of preconditions reads awkwardly as a chain of And steps.

Steps are also known as "givens", "whens", and "thens".

Givens are preconditions that put the system in a known state before a user or some external system starts interacting with it. Things that "happened earlier" are also okay.

Given no users are logged in
Given the customer is logged in as an administrator
Given a $100 microwave was sold on 2020-11-03

Behavioral test frameworks will use the Given information to configure the system-under-test to an initial state. This may involve creating mock objects or adding records to a database, for example.

Whens are used to describe a system event or user action. This can be a person interacting with the system, or it can be an event triggered by another part of the system, or a combination of both. Whatever happens, it causes a transition in state.

When the customer submits their credentials
When the customer returns the microwave
When the overnight settlement run completes

A When step SHOULD name the action in business terms, not the mechanics of performing it. "When the customer submits their credentials" survives a redesign of the login screen; "When the customer fills the username field and presses the login button" does not, and binds the specification to a user interface that is an implementation choice. See Implementation.

A scenario SHOULD have no more than one or two When steps. Any more, and the scenario SHOULD be split into multiple smaller ones.

Thens are assertions — actual outcomes (what the system actually does) compared against expected outcomes (what it is supposed to do). Each Then step MUST describe a specific, verifiable (ie. testable) result, output, or expected new state. Thens SHOULD observe outcomes related to the business value in the feature description — a report, user interface repaint, message, command output, etc. — rather than something deeply buried in the system, such as a database change. Assertions about the system’s internal state or implementation details SHOULD be avoided.

A scenario MAY have multiple Given, multiple When and multiple Then steps, but they MUST always be grouped in that order. For better readability, subsequent Given, When and Then keywords MAY be swapped for And or But.

Steps are the basis for automated behavior-driven testing. Test automation systems will take the trailing portion of each step — the text after the keywords Given, When, Then, And and But — and map it to a callback function, which is executed. Typically, the callbacks for each Then step will inspect some aspect of the system — a log entry, user interface repaint, command output, etc. — to verify the assertion made in the step.

Step arguments

A step whose subject does not fit comfortably on one line MAY carry an argument on the lines beneath it. Two forms are available, and both are passed to the step definition as a final parameter.

A doc string carries a block of text, delimited by triple quotes. It is used where the value is prose, a payload, or anything else whose internal line breaks matter.

Given a customer has submitted the following complaint:
  """
  The microwave arrived with a cracked door.
  I would like a refund rather than a replacement.
  """

A data table carries rows of structured values, delimited by pipes.

Given the catalog contains the following products:
  | name      | status    | price |
  | Microwave | available | 100   |
  | Toaster   | reserved  | 40    |

A data table attached to a step is not the same construct as the Examples table of a scenario outline, though they look alike. A data table is a single argument to one step within a single scenario. (An Examples table generates several scenarios from a template.)

A step argument that runs to dozens of rows is usually test fixture data that has escaped into the specification, in which case the scenario has probably stopped illustrating the underlying business rule effectively.

Backgrounds

The same Given steps can end up repeated across all scenarios in a feature file. Repeated preparation steps are usually incidental detail, not essential to describing individual scenarios. Such details SHOULD be moved to the background — a single Background section, placed near the top of a feature file between the Feature block and the first Scenario, written just like a scenario except with no title.

Background:
  Given a $100 microwave was sold on 2020-11-03
  And today is 2020-11-18

Background sections provide context to all subsequent scenarios. Test automation systems will run the background script immediately before testing each scenario, thus putting the system-under-test into the same preset state for every test.

Rule blocks

A Rule block groups the scenarios that together illustrate one business rule. It sits between the Feature block and the scenarios it owns.

Feature: Reserve a product

  Rule: A product can only be reserved while it is available

    Scenario: Reserve an available product
      Given a product with status "available"
      When a Partner reserves the product
      Then the reservation is confirmed

    Scenario: Cannot reserve a sold product
      Given a product with status "sold"
      When a Partner reserves the product
      Then the response indicates the product is not available

This is the Gherkin counterpart of the business rules described under Rules. Where the specification states a rule centrally with a stable identifier, the Rule block SHOULD name that identifier, so the scenarios that verify a rule can be traced to the rule they verify:

  Rule: R3 — Reservation requires an available product

Rule is supported by current versions of the major frameworks, but it is a later addition to the language than the rest of the syntax described here. Confirm support before adopting it.

Scenario outlines

A complex business rule with several variable inputs or outputs can end up producing several scenarios that only differ by their values.

Copying-and-pasting scenarios to test different values creates duplication. A scenario outline SHOULD be used instead. It is an alternative to a scenario that allows the expression of a scenario using a template with <placeholders> and a table of replacement values.

Scenario Outline: Eating
  Given there are <start> cucumbers
  When I eat <eat> cucumbers
  Then I should have <left> cucumbers

  Examples:
    | start | eat | left |
    |  12   |  5  |  7   |
    |  20   |  5  |  15  |

In this case, two scenarios are generated from this scenario outline — one per row of the Examples table. It is the equivalent of writing the following ordinary scenarios:

Scenario: Eating
  Given there are 12 cucumbers
  When I eat 5 cucumbers
  Then I should have 7 cucumbers

Scenario: Eating
  Given there are 20 cucumbers
  When I eat 5 cucumbers
  Then I should have 15 cucumbers

Variables in the scenario outline steps are marked up with < and >.

A scenario outline section MUST be followed by one or more Examples sections. Examples are tabular data used to populate the scenario outline’s variables. The table MUST have a header row with column names corresponding to the variables encoded in the scenario outline steps. Each table row produces a new scenario, with the variables filled in with its values.

Tags

A tag is a label attached to a feature, rule, scenario, or Examples table, written as an @-prefixed word on the line above.

@checkout @slow
Scenario: Checkout with a saved payment method

Tags are metadata for the test runner, not part of the specification’s meaning. Their main use is selection — running or excluding a subset of scenarios, such as a smoke-test set, or those known to be slow.

Tags MAY also be used to cross-reference requirement identifiers, giving a machine-readable binding between a scenario and the rule or quality it verifies — @R3, or @Q1.4. This is a convenience for tooling, and does not replace stating the rule centrally.

Tags SHOULD be few and their meanings documented.

Implementation

A .feature file is not by itself executable. Each step MUST be wired up to a callback function — often called a step definition or glue code — that the test framework executes when the step runs. The step text is matched against the step definitions by a pattern (a regular expression or similar), and any placeholders in the step text are passed to the callback as arguments.

Step definitions SHOULD be kept thin. A step definition’s job is to translate business language into a call against the system-under-test, not to contain business logic itself. Where several steps across different features share the same wording, their step definitions SHOULD be reused rather than duplicated.

Step definitions SHOULD communicate directly with the business rule implementation, going through as few layers as possible. Scenario outlines in particular SHOULD NOT be automated using UI automation such as Selenium WebDriver. Scenario outlines SHOULD only be used to validate the implementation of a business rule that behaves differently based on variable input parameters. Validating a business rule through a UI is slow, and when there is a failure it is difficult to pinpoint where the error is. Testing close to the business rule, by contrast, is fast, and errors become easy to diagnose and fix.

Enforcement

Wiring a specification up to a test framework achieves nothing unless the result gates delivery. Where a requirement is expressed as an executable specification — a Gherkin scenario, a quality benchmark, a security scan — the build SHOULD fail when that check fails. A specification that can be silently violated without breaking the build is only as trustworthy as the discipline of whoever remembers to check it by hand.

For quality gate stages and their placement in the delivery pipeline, see TS-12: Quality Assurance.

Requirements elicitation

Gherkin scenarios are the RECOMMENDED format for expressing detailed, testable acceptance criteria, but they are not always the best starting point for discovering what those criteria should be.

This section covers five techniques that are most useful earlier in the life of a specification, before requirements have been broken down into individual scenarios. Once that structure is understood, it is RECOMMENDED to express the detailed acceptance criteria as Gherkin scenarios.

They are presented in the order they are typically applied. Impact mapping decides which capabilities are worth building at all. Use case analysis and event storming map the scope of a domain. Story mapping sequences that scope into releasable increments. Example mapping then takes a single story and breaks it into the rules and examples that become scenarios.

The techniques narrow as they go, from a business goal down to a single scenario. A project need not apply all five. What matters is that the scope is understood before scenarios are written, not which technique established it.

All five discover what the system does. None of them surfaces what the system must be like while doing it. Qualities need a different line of questioning, covered in Eliciting qualities at the end of this section.

Impact mapping

Impact mapping, developed by Gojko Adzic, works backward from a business goal to the capabilities that might achieve it. Where the other techniques here take the scope as given and explore it, impact mapping is what decides whether a capability belongs in the scope at all.

The map is a four-level hierarchy, built by asking a question at each level:

  • Goal (why?). The measurable business outcome being pursued. A goal is not a feature — it is stated as a number to be moved, so that it is possible to tell afterward whether it was reached.
  • Actors (who?). Who can affect that outcome — including those who can obstruct it, not only those the system serves.
  • Impacts (how?). The change in each actor’s behavior that would move the goal. This is the level the technique exists for, and the one most often skipped.
  • Deliverables (what?). What the system could do to cause each impact. These become candidate features.

Reading the map from the bottom up gives each deliverable a justification: this feature exists to cause that behavior change, in that actor, in service of that goal. A deliverable that cannot be traced up to a goal is one nobody has justified, and is a candidate to be cut.

Impact mapping produces goals and candidate deliverables, and both belong in the proposal that argues for the change, not in the specification. The specification records what the system does, and the reasoning behind it lives in the proposal archive.

Use cases

Use case analysis, introduced by Ivar Jacobson in 1992, is an alternative technique for capturing functional requirements. A use case describes a complete interaction between an actor and the system in pursuit of a goal.

Use cases are a useful tool for mapping out the full scope of an actor’s interaction with the system before breaking it down into individual, independently testable scenarios.

A use case SHOULD state the following:

  • Actor. The participant pursuing the goal, named from the actor hierarchy.
  • Goal. What the actor is trying to achieve, stated as an outcome rather than as a sequence of operations.
  • Preconditions. What MUST already hold before the interaction can begin.
  • Main success scenario. The numbered steps of the interaction when nothing goes wrong — the shortest path from precondition to goal.
  • Extensions. The alternative and failure paths, each numbered against the step of the main scenario it branches from.
  • Postconditions. What holds once the goal is reached.

The extensions are what make the technique worth the effort. Enumerating each step’s failure paths surfaces the unhappy paths that an actor-goal statement alone leaves implicit, and it is those paths that most often turn out to be under-specified.

A use case describes a whole interaction, so it is broader than a scenario. One use case typically yields several scenarios — the main success scenario becoming one, and each extension becoming another.

Use cases and Gherkin overlap, and a project need not use both. Where scenarios are written directly, use case analysis is best treated as a discovery step whose output is discarded once the scenarios exist. Maintaining both as specification artifacts creates two descriptions of one behavior, and therefore drift.

Event storming

Event storming, invented by Alberto Brandolini, is a workshop-based technique for exploring a business domain.

Unlike use cases and Gherkin, which are specification formats, event storming is a discovery technique. Technical experts collaborate with domain experts to identify domain events — the significant things that happen — and to arrange them in chronological order.

In a workshop, the experts and engineers work around an unlimited modeling surface — ideally, a long wall or a paper scroll — and record their individual findings on color-coded sticky notes. A workshop typically proceeds through phases, each phase adding a new color of note to the timeline built up so far:

  • Domain events (orange). Everything of significance that happens in the business, stated in the past tense (eg. "Order received"), placed on the timeline in roughly the order they occur.
  • Commands (blue). The actions, taken by an actor or an external system, that trigger each event.
  • Actors (small yellow). Who or what issues each command.
  • Aggregates (large yellow). The entities that receive commands and produce events. Related events and commands are clustered together.
  • Hotspots (pink/purple). Problems, risks, and unanswered questions surfaced during the discussion.

A facilitator keeps the group moving chronologically, resolves naming disagreements, and ensures the surface stays a shared model rather than several disconnected fragments. Hotspots (pink or purple notes) are used to park questions for resolution at a later date, so as not to derail the current session.

Event storming is particularly effective at identifying the components of a system and the interactions between them. It is an especially good fit for exploring the domains of event-driven architectures.

Story mapping

Story mapping, popularized by Jeff Patton, is a technique for arranging user stories into a two-dimensional map. A horizontal backbone shows the major steps in an end-to-end user journey, with each step broken down vertically into the individual stories that support it, ordered by priority.

The map makes it possible to see the whole shape of a system at a glance, and to slice out a coherent, minimal release by taking a thin horizontal band across the map, rather than a single vertical stack of stories.

A story placed on the map is a placeholder for a future conversation, not yet a specification. Before it is scheduled into a release, it SHOULD meet the "INVEST" criteria:

  • Independent. Schedule-able and implementable without depending on other unscheduled stories.
  • Negotiable. A placeholder for discussion, not a fixed contract.
  • Valuable. Delivers value to an actor, not merely a technical task.
  • Estimable. The team has enough shared understanding to size it.
  • Small. Fits comfortably within a single release increment.
  • Testable. Has, or can be given, clear acceptance criteria.

A story that fails the "small" or "estimable" tests is usually too coarse-grained to be scheduled. It is RECOMMENDED to split it along one of its natural seams, rather than attempting to size or build it as-is:

  • Workflow steps. Split a multi-step journey into one story per step.
  • Business rule variations. Split by rule or edge case (eg. by payment method, or by validation branch) rather than building all variations at once.
  • Data variations. Split by input type or format handled.
  • Operations. Split a story that bundles create, read, update, and delete into one story per operation.

Splitting SHOULD stop once each resulting story is independently valuable and testable. Splitting a story into slices that are not independently releasable (eg. by architectural layer, such as "build the API" and "build the UI") is NOT RECOMMENDED, since neither slice delivers value on its own.

Story mapping is most useful in sequencing and prioritizing the scope identified through use case analysis or event storming into releasable increments. In this regard, story mapping is a bit closer to implementation planning than it is to requirements discovery.

Example mapping

Example mapping, developed by Matt Wynne, is a lightweight, timeboxed workshop technique for breaking a single user story down into its underlying rules and the concrete examples that illustrate each rule.

The process uses index cards of four colors:

  • Story (white).
  • Rule (yellow).
  • Example (green).
  • Question (red).

Red cards are blockers that must be resolved before the story can be implemented.

Unlike use cases, event storming, and story mapping, which operate at the scope of a whole system or user journey, example mapping operates directly on a single story, immediately before it is built.

Example mapping translates directly into Gherkin scenarios and examples, making it the RECOMMENDED technique for the final step of requirements elicitation before scenarios are written.

Eliciting qualities

Qualities are rarely volunteered. Where a stakeholder raises one at all, it arrives as an adjective — the system should be reliable, or secure, or user-friendly — and the stakeholder considers the requirement given. It is not. An adjective names a concern; it does not state a threshold anyone can build against or test. Turning the one into the other is the elicitor’s job, and it is done by questioning.

Two questions do most of the work:

  • How would we tell whether the system was reliable enough? This asks for the observation that would settle the matter, which is the raw material of a measurable threshold.
  • What are some examples of the system not being reliable enough? Stakeholders find counterexamples far easier to produce than definitions, and the examples they reach for first name the concern they actually have. "Reliable" often turns out to mean one specific failure that hurt somebody last year.

This is the same principle example mapping applies to functional rules — drive out the abstraction with concrete cases — turned on quality attributes. See Example mapping.

Three further questions convert the answers into a specifiable requirement:

  • Under what conditions? A threshold without a stated load, data volume, or network condition is not testable. Establish the conditions during elicitation, not when someone tries to write the test.
  • Who notices, and what do they do about it? This finds the real observer of the quality, who is often not the end user.
  • What does it cost when it happens? This is what justifies the threshold that gets chosen — see Value and cost.

Qualities SHOULD be elicited from stakeholders beyond the system’s users. Operations, support, security, and compliance functions hold most of the availability, recoverability, and regulatory requirements, and a round of elicitation that speaks only to users will produce almost no quality requirements at all.

Quality elicitation does not need a workshop of its own. The techniques above already generate the openings: event storming hotspots are frequently quality concerns in disguise ("what happens if this call times out?"), and example mapping’s red cards catch the same thing at story level. Treat those as prompts to ask the questions above, rather than parking them as functional unknowns.

Once elicited, qualities are stated as measurable thresholds and ranked against one another — see Prioritizing qualities.

Proposal lifecycle

A specification is only as valuable as it is trustworthy, and trust depends on process. A specification MUST be kept current with production, and changes to it MUST be deliberate and recorded.

Changes to a specification are therefore introduced through proposals. Each one is drafted, reviewed, decided, and — if accepted — merged alongside the code that implements it, then preserved permanently as a record of the decision.

This section covers that lifecycle, and the practices that keep the specification trustworthy as it evolves.

Two artifacts

A complete requirements practice MUST maintain two artifacts side by side, in the same repository:

  • The specification. Mutable and always describing the as-is production system.
  • The proposal archive. An immutable, append-only log of every significant proposed change, including those that were rejected.

The specification answers "what does the system do?" The archive answers "why is it that way?"

The two artifacts SHOULD be colocated, so as to prevent drift between the description of the system and the record of the reasoning behind it.

A version control system is RECOMMENDED as the home for both artifacts.

Lifecycle states

Each proposal SHOULD move through a defined state machine, reflecting the process of drafting, discussing, and ultimately accepting or rejecting a proposed change to the system specification.

The following lifecycle states are RECOMMENDED for a baseline implementation.

stateDiagram-v2
  [*] --> Draft
  Draft --> Proposed
  Proposed --> Accepted
  Proposed --> Rejected
  Accepted --> Released
  Released --> Superseded
  Rejected --> [*]
  Released --> [*]
  Superseded --> [*]

The states have the following meanings.

  • Draft. The proposal is being written. It is not yet ready for peer review. Early feedback MAY be solicited, but no decision is being sought yet.
  • Proposed. The proposal is complete and open for a decision. It is now formally reviewed and negotiated with the relevant stakeholders.
  • Accepted. The proposal is approved by the product managers. Implementation is being tracked via the normal product backlog system. Its specification edits are not yet on the main line, and MAY continue to evolve during implementation.
  • Rejected. The proposal will not be taken forward.
  • Released. The change has been built, its specification edits are merged alongside the implementing code, and the change-set has been deployed to production.
  • Superseded. A previously released change is no longer in effect. A later proposal replaced or removed the feature.

Prioritizing proposals

Acceptance says a change will be made, not when. A backlog of accepted proposals awaiting implementation therefore needs a priority order, and the RECOMMENDED scheme for expressing it is MoSCoW.

  • Must. The release is not viable without it. A failed Must is a failed release.
  • Should. Important, and included unless something has to give.
  • Could. Desirable. The first thing dropped when time runs short.
  • Won’t. Explicitly out-of-scope for now, though the proposal remains accepted and it may be scheduled for delivery in the future, when time and budget allows.

Withdrawing a requirement

Removing behavior from a system is a change like any other, and MUST be proposed, reviewed, and decided through the same lifecycle.

Withdrawal takes two phases. Announcing a removal and implementing it are separate tasks, which may be separated by a long interval.

  1. Deprecated. The proposal to remove the behavior is released. The behavior is still in production, so the requirement stays in the specification, but marked as @deprecated or similar.
  2. Removed. Once the behavior is gone from production, the specification artifacts are deleted, in the same change-set that removes the implementing code.

The two phases MAY be carried by one proposal or by two. One proposal is usually simpler, but two are warranted where the time gap between deprecation and removal is long.

Binding the specification to production

A proposal’s specification edits MUST be merged in the same change-set as the code and configuration that implement them, and MUST NOT be merged ahead of them.

It is not enough for a proposal to be approved. The change MUST also be designed, built, and tested before its specification edits reach the main line. This is what keeps the specification synchronized with the running system, and it makes drift a merge-time concern rather than a documentation debt.

Where a project batches releases rather than deploying on merge, the main line of the specification repository will briefly lead production — describing behavior that is built and merged but not yet deployed. This window SHOULD be kept short. It is the release pipeline’s job to close it, not the specification’s. Withholding the merge instead would strand the specification on a long-lived branch and reintroduce the drift the practice exists to prevent.

Between acceptance and merge, a proposal’s specification edits MAY continue to evolve — in response to technical feedback, discoveries made while building, or feedback from real users in beta tests and staged roll-outs. It is expected that accepted proposals will continue to evolve through implementation. The important thing is that, by the time a proposal and its corresponding specification edits land in the main trunk, they are an accurate reflection of the final code and configuration changes shipped to production.

Concurrently supported versions

"The production system" is unambiguous only where one version is live at a time. Products distributed to customers — on-premises installations, long-term support branches, or mobile clients that users update at their own pace — have several versions running simultaneously, each with different behavior.

Where this applies, the main line MUST describe the newest released version. Each other supported version is a branch or tag of the specification repository, cut at the point that version was released, so that a reader can retrieve the specification for the version they are running.

Best practices

  • Scope each proposal atomically. One feature or one quality requirement — the smallest thing that can be reviewed, decided, and shipped independently of any other change. Where several changes genuinely depend on one another, such as specifying a greenfield system, they SHOULD be kept as separate atomic proposals grouped under an "epic" container.
  • Review cross-functionally. The product teams SHOULD read a proposal for scope and business intent, QA SHOULD read it for missing or ambiguous acceptance criteria, and engineering SHOULD review and approve it for feasibility.
  • Specify the end state, not a changelog. Write "authenticated callers can filter the catalog by species", not "add a species filter". The diff already shows what is changing. The prose SHOULD read as a description of the destination, so that on release the specification is simply true.
  • Keep description and reasoning in their proper homes. The specification says what the system does, while the proposals say why changes were made over time. Rationale MUST NOT be smuggled into the specification, and a proposal MUST NOT restate the specification — it SHOULD instead link to the artifacts it edits. The proposal is where to be honest about motivation, alternatives, and trade-offs accepted — these are what make the decision log valuable years later.
  • Leave out implementation details. How the change is rolled out — migration steps, sequencing, feature flags — is an implementation concern and MUST NOT appear in the specification.
  • Keep feedback out of the record. Open-ended debate and negotiation SHOULD happen in a discussion thread attached to the proposal, separate from the document and its revision history. The document’s history then stays a clean record of how the proposal evolved. Once merged, the thread’s conclusions MAY be summarized in the document.
  • Treat merged proposals as immutable. A merged proposal MUST NOT be edited or deleted. To revisit a decision, open a new proposal that supersedes the original, cross-referencing the two. Every merged proposal SHOULD be assigned a stable, sequential identifier and listed in an index, so that stewards with no knowledge of the project’s history still inherit an appreciation for why the system is the way it is.
  • Record rejections as carefully as acceptances. A rejected proposal is a decision worth remembering, not a failure to hide. Its specification edits MUST be reverted, but the document MUST be preserved in the archive, so the next person with the same idea can read why it was not pursued. Write its motivation, alternatives, and trade-offs with the same care as a winning one.
  • Trace requirements to their implementation. Where the tooling allows, each requirement SHOULD be linked to its implementing component, its test suite, and any tracking ticket for outstanding work, citing requirement identifiers such as F3.2 or Q1.4. A feature or quality is easiest to trust when a reader can follow it to the code that implements it and the tests that verify it.

Definition of Ready

It is RECOMMENDED that software projects have a Definition of Ready (DoR): a documentation artifact that defines a set of criteria that MUST be met before a new feature, user story, or other development task is considered "ready" to be worked on.

The DoR SHOULD be formatted as a simple, short checklist. The purpose is to provide a quick point of reference for teams to check they’ve got everything they need to successfully complete a development task, before proceeding with it. The checklist SHOULD be generic enough to be applicable to all kinds of development tasks, and emphasis SHOULD be placed on the readiness of the requirements.

Example:

  • Are the requirements clear and (sufficiently) unambiguous?
  • Are the acceptance criteria defined in a testable format, and can the tests be automated?
  • Is it clear who the stakeholders are?
  • Does the team have the knowledge and resources required to complete the task?
  • Can the work be done independently of other tasks running in parallel?
  • Can the work be implemented in small increments?
  • Can the design be iterated based on feedback?

The DoR belongs to this technical standard because most of what it gates is requirements readiness. A task is ready when its acceptance criteria are unambiguous, testable, and small enough to build — which is a statement about the specification, not about the team’s delivery process.

The DoR has a counterpart at the other end of the development task: the Definition of Done (DoD), which defines the conditions under which work has ended. The DoR describes when work MAY begin; the DoD describes when it is complete. The DoD is a delivery-process concern and is specified separately — see TS-12: Quality Assurance.


References