For AI agents: the complete documentation index is available at https://docs.flezise.com/26.09/llms.txt, the full documentation bundle is available at https://docs.flezise.com/26.09/llms-full.txt, and this page is available as Markdown at https://docs.flezise.com/26.09/artifacts/specs/best-practices.md.

Best practices

This page answers the questions that come up most often when writing specs, organizing requirements, and getting a spec through the review gate.

Writing requirements

How do I write a good requirement?

Write every requirement in EARS form. For behavior that happens in response to something, use the event-driven form: When <trigger>, the system shall <response>. For a rule that always holds, use the ubiquitous form: The system shall <behavior>.

Attach testable acceptance criteria to every requirement: quantified, checkable, and independent, each producing a clear pass or fail. Vague words hide untestable requirements. Replace fast, secure, scalable, user-friendly, intuitive, robust, seamless, and reliable with a number, a threshold, or an observable outcome.

For example, "The system shall return search results in under 300 ms at p95 with 500 concurrent users." The acceptance criterion reads: "A load test at 500 concurrent users shows p95 latency under 300 ms."

See Requirements and EARS for the full syntax and side-by-side examples.

When do I use the event-driven versus the ubiquitous EARS form?

Use the event-driven form when the behavior has a trigger: a user action, a state the system reaches, or a time condition. The trigger makes the requirement conditional and testable as a specific scenario.

Use the ubiquitous form for a rule that always holds regardless of what a user does or what state the system is in. Most non-functional requirements fit here: a latency target, an availability threshold, a constraint that applies system-wide.

A conditional requirement written without a trigger is a finding the verifier agent raises. Resolve it by naming the trigger explicitly.

How do I keep requirements traceable?

Assign each requirement a stable prefix ID before you submit the PRD: FR- for functional, NFR- for non-functional, CON- for constraint, BR- for business rule. IDs do not change after they are assigned, even when the requirement text is revised.

Reference requirements by their IDs everywhere downstream. An epic lists the FR- IDs it covers; a story lists the IDs its acceptance criteria satisfy. Cover each requirement exactly once across the chain: a requirement covered by two items means neither is fully responsible for it.

Never restate a requirement in a downstream spec. Write "this story covers FR-007" and let the PRD carry the text.

Structuring the work

How big should an epic be?

Group epics by user journey first. An epic owns use cases from one user journey; within a journey, cluster by shared domain data, services, or integration points. Shared infrastructure is not a grouping reason on its own.

Target no more than four epics for twelve functional requirements. A single-FR epic is too small; fold it into the most related one.

Non-functional requirements never form an epic. They appear in the covers list of every epic they constrain, keeping the constraint traceable without assigning it as a standalone deliverable.

How do I write a good story?

Start with the user story form: "As a [role], I want [feature], so that [benefit]." Then write acceptance criteria in Gherkin:

Given <context>
When <action>
Then <outcome>

Each scenario is independently implementable and testable in one focused agent session. Check it against INVEST: independent, negotiable, valuable, estimable, small, and testable.

Attach a Definition of Ready (what must be true before work starts) and a Definition of Done (what must be true before the story closes). A story without both is not ready to schedule.

Brownfield changes

When should I use a Change Spec?

Use a Change Spec for any brownfield change that touches existing behavior. A Change Spec is an executable delta: it names the current behavior, the expected behavior after the change, the impact boundary (affected code and contracts), a design delta, Gherkin acceptance criteria, an ordered implementation-task checklist, verification steps, and a rollback plan.

Start by classifying the change kind: feature, bugfix, documentation, dependency upgrade, migration, refactor, or configuration. The kind shapes the impact boundary and the appropriate verification.

See artifact types for where a Change Spec sits in the brownfield lane.

How do I prevent regressions?

Capture the behavior that must not change alongside the fix. Name the specific inputs, the expected outputs, and how to verify them. Write this as a Gherkin scenario so the acceptance criteria are machine-parseable and map directly to tests.

A regression boundary with no verification step is a finding the verifier agent raises on a Change Spec. Resolve it by making the verification explicit before you submit.

Getting a spec through review

What gets a spec approved faster?

Resolve every finding the verifier agent raises before you expect a human approver to vote. The Review cockpit shows outstanding findings, assumptions, and verification items; bring all three counts to zero first.

Give each requirement checkable acceptance criteria. A requirement without a pass/fail criterion is a blocking finding. Cover exception and error flows alongside the happy path: validation failures, authorization rejections, timeouts, and empty states. For UI specs, also cover every interactive state (happy, empty, loading, error, destructive, stale, latency, and recovery), plus accessibility and role visibility.

How do I revise an approved spec?

Approved specs are never edited in place. Choose the action that fits the scope of the change:

ActionWhen to use
New VersionSubstantive change; creates a fresh draft from the approved content and starts a new review cycle
Request ChangesTargeted fix while In Review; moves the spec to Revise without a full re-draft
RejectSpec needs a fundamental rethink; creates a new draft from the reviewed content and returns to Draft
How do I import existing material?

Upload document (available from the Artifacts empty state and the New Artifact picker) brings an existing file into the project as an artifact. The pipeline builds only from approved artifacts; an uploaded document that stays in Draft does not feed into downstream generation.

Submit the uploaded artifact for review and approve it before the pipeline can build from it.

Next steps