Project Requirements
Your team has a partner, a project idea, and notes from a first meeting where everyone seemed to agree on what to build. A week later two teammates are building different versions of the same feature, and the partner brings up something new at every meeting. This page covers how to write requirements a teammate can test, what goes in docs/requirements.md, and how to keep that file accurate as the project changes. It doesn’t cover technology and architecture choices, which belong in an ADR and your technical design, or how to order the work within a sprint.
A requirement is a specific, testable statement about what the product must do or how well it must do it. It isn’t a wish (“the app should be fast”), a task (“build the login page”), or a design decision (“use PostgreSQL”). Tasks go on the board and design decisions go in an ADR. Keeping requirements apart from solutions is harder than it sounds: Méndez Fernández et al. (2017) surveyed 228 companies in 10 countries for the Naming the Pain in Requirements Engineering (NaPiRE) study, and a quarter of the respondents ranked stakeholders’ difficulty separating requirements from known solution designs among their five most critical requirements problems.
Keep the requirements in the repository as docs/requirements.md, next to the code rather than in a shared drive. There, a pull request can change a requirement in the same commit as the behavior it describes, and your AI coding tools can read the file before they build anything, once you point them at it.
What Is a Requirement?
Section titled “What Is a Requirement?”Sort requirements into three kinds, and give each kind its own section in the document:
- Functional: what the system does. “A user can reset their password by email.” “The pipeline rejects a record whose timestamp is missing.”
- Non-functional: how well it does it, with a number. “The dashboard loads in under 3 seconds on the partner’s office Wi-Fi.” “A fresh clone runs the full pipeline in one documented command.”
- Constraints: limits you didn’t choose. “Must run on the partner’s existing Postgres 14.” “No personal data leaves the partner’s network.” “Ships under the upstream project’s license.” An inherited codebase belongs here too, because its CI, its review rules, and its hosting bind you before you write a line.
ISO/IEC 25010 defines nine quality characteristics, and they make a checklist for the non-functional section: functional suitability, performance efficiency, compatibility, interaction capability, reliability, security, maintainability, flexibility, and safety. Ask of each whether your partner would notice it missing, write a measured requirement for each one they would, and leave the rest out on purpose.
Textbooks divide requirements further: Wiegers and Beatty’s Software Requirements, for example, gives business requirements and user requirements a chapter each. Those categories answer whose requirement it is, which makes them good questions to ask in an interview and poor headings for a document, since a reader looking for a requirement searches by what it’s about.
The formal treatment is ISO/IEC/IEEE 29148, the international standard for requirements engineering of systems and software, which replaced IEEE 830. It defines the construct of a good requirement, the attributes and characteristics a requirement carries, and the information items a requirements process produces. For an overview of what it formalizes, read the Software Requirements chapter of the Guide to the Software Engineering Body of Knowledge, and adopt the standard itself when a contract or a regulator asks you to; short of that, the time its formality costs is better spent confirming requirements with users.
Requirements come from the people who have the problem: your partner, the people who will use the product, the maintainers of the project you’re joining. Start with interviews and a few observed sessions of the current workflow, then look at what existing tools already do. In an interview, ask what people did the last time the problem came up rather than what they would want, because a question about the future gets an opinion and a question about the past gets a fact. Rob Fitzpatrick’s The Mom Test is a book on asking those questions without collecting biased feedback.
Addressing a Problem
Section titled “Addressing a Problem”Start from a problem that a group of people you can name actually has. A clear problem gives every requirement a reason to exist, and it gives you a way to decline the requests that don’t serve it: a feature that touches nothing in the problem statement is a different project, and you can say so.
Open the document with a problem statement: what the problem is, who has it, why it matters, and what evidence you have that it’s real, in one paragraph. Expect to rewrite it. The first version is written before the team has watched anyone work, and each interview or prototype can show that the problem sits somewhere else. When you reframe it, say what changed in the commit message, so the file’s history records what the team learned and when.
What Makes a Good Requirement?
Section titled “What Makes a Good Requirement?”Write each requirement so a teammate could mark it passed or failed without asking you. That takes specific nouns, a measurable bar for anything about quality, and an acceptance criterion wherever the behavior isn’t obvious. “The system should be fast” can’t be checked, so it can’t be finished either; “the dashboard loads in under three seconds on the partner’s office Wi-Fi” can be both. The difference shows up at the demo, where an untestable requirement turns into an argument about whether it was met.
A user story states a functional requirement from the user’s side: “As a coordinator, I want to see open shifts, so that I can fill gaps before the weekend.” Mike Cohn’s Mountain Goat Software argues that the written story is a reminder to have a conversation, and that the full requirement comes out of that conversation. Write a functional requirement as a user story where its benefit isn’t obvious, because the “so that” clause is where the benefit gets written down, and as a plain statement where that clause would tell the reader nothing new.
Lucassen et al. (2016) surveyed 182 practitioners: 59% used the template “As a role, I want goal, [so that benefit]”. A majority agreed that user stories and a template improved their productivity and the quality of their work. Although the template marks the “so that” clause optional, interviewees in the same study stressed it, and credited it with less confusion among stakeholders, less discussion, and more autonomy for developers (Lucassen et al., 2016).
Bill Wake’s INVEST mnemonic checks a single story: is it Independent, Negotiable, Valuable, Estimable, Small, and Testable? It’s the best-known set of guidelines for user stories, though only 23.5% of the practitioners in the same survey used it, while 39.5% used no guidelines at all, and about half the sample was neutral on whether guidelines improved their productivity or quality (Lucassen et al., 2016).
For acceptance criteria, Given-When-Then states a precondition, an action, and the expected result. Gherkin formalizes that pattern into executable specifications that Cucumber runs as tests, so a criterion written this way is most of the way to a test. Neither format is required. Use one when a requirement has conditions, because the structure makes you name each condition, and use a plain sentence when it doesn’t.
Here is the shape of a docs/requirements.md for a small web project, with an ID on each requirement and a reason on each priority label, here a MoSCoW bucket:
# Requirements
## Problem statementCoordinators at <partner> schedule ~40 volunteer shifts a week by emailand spreadsheet; three were double-booked last month (per the coordinator).
## Functional requirementsPriority labels below use MoSCoW: need to the product, not build order.- FR-1 (Must: nothing else works without it): A coordinator creates a shift with date, time, place, headcount. Acceptance: end before start fails.- FR-2 (Must: it prevents the double-booking): A volunteer claims an open shift. Acceptance: a claim on a full shift fails.- FR-3 (Should, assumed: a short shift can still be filled by phone): Email the coordinator when a claimed shift is released within 24 h of its start.
## Non-functional requirements- NFR-1 (Should: slow is tolerable, broken isn't): The board loads in under 2 s on the partner's office Wi-Fi.- NFR-2 (Must: some volunteers can't use a mouse): Every action works by keyboard alone.- NFR-3 (Must: as NFR-2): axe-core reports no serious WCAG 2.2 AA violation.
## Constraints- C-1: Sign-in through the partner's Google Workspace; no new accounts.- C-2: Volunteer names and emails never leave the partner's tenant.- C-3: Runs in the partner's Render account; their IT lead's review rules apply.
## Success metrics- Double-bookings in the first 4 weeks of use: 0 (from the claim log).- Coordinator time on scheduling: under 1 h a week (the coordinator's log).
## Non-goals- Payroll or hours export.- Shift swaps between volunteers, until after launch.
## Open questions- Does v1 need recurring shifts? Owner: PM, before FR-1 is built.- Do volunteers need the schedule offline at the venue? Owner: PM, asks the partner before the first deploy.Give each requirement an ID so a pull request, a test, or a commit message can cite it.
Label each requirement with its priority and the reason, as the Must and Should labels above do. Any prioritization method that sorts requirements into buckets or quadrants works: MoSCoW, as here, or impact-effort or cost-value; a ranked list, such as a Scrum backlog, is a build order and goes on the board. Name the method once, above the first label, so a reader knows what the labels mean, and leave the constraints unlabeled, because the team didn’t choose them and can’t drop them. The label and its reason record why the requirement matters, which a successor or an AI tool reading the repository needs in order to judge a trade-off without asking you.
Label a requirement nobody has confirmed as assumed, as FR-3 is, so a reader can tell a guess from a fact and knows which requirements to take back to the partner.
Write the non-goals down, because each costs one line and is settled before the request arrives. When it does, you can answer “we decided against that” instead of “we didn’t think of that”, which moves the partner’s request from an oversight you owe them to a trade-off you can discuss.
List what you don’t know yet as open questions. Give each one an owner and a point in the project by which it’s answered: an open question tells the reader what’s still unsettled, while a confident requirement nobody checked looks settled until someone builds it.
Track the work that implements each requirement, and the order you build it in, on your board rather than in a second document. The order depends on more than the priority label: what blocks what, who’s free, and what the partner needs to see next. It changes every sprint, so a copy in the file would disagree with the board within one. GitHub Projects is enough, and so is any tracker whose items can cite “FR-2”.
Story Mapping
Section titled “Story Mapping”A user story map, a technique Jeff Patton popularized, lays out the user’s activities from left to right in the order they happen, with the stories that serve each activity stacked top to bottom by priority. A horizontal line drawn across the map marks a release: everything above it ships, and everything below it waits. The Nielsen Norman Group’s overview describes this layout and walks through a worked example.
Draw the map as a team, and with your partner if you can, because the useful part is the disagreement over where the line goes, and a map drawn by one person hides that disagreement. The stories above the first line become your functional requirements, and the line itself is the first draft of what you’re committing to ship. User Story Mapping is the session to run.
Validation
Section titled “Validation”A requirement is validated when someone other than its author has confirmed it’s the right requirement. Two confirmations are cheap enough to run on every requirement that matters:
- Your partner reads it. Review the document with your partner, or someone else who knows the users, early and again after every reframing. A review of the written document catches a misunderstanding for the price of an hour, rather than at the demo, after the sprints built on it. Communication flaws between the project team and the customer were the second most often named critical problem in the NaPiRE survey (Méndez Fernández et al., 2017).
- A prototype hits it. A clickable mockup or a thin working slice tests a requirement faster than another meeting, and it shows whether the thing is feasible at the same time.
Once a requirement is confirmed, name a test after it. A test called test_second_claim_on_full_shift_fails checks FR-2 on every CI run. That’s verification (you built it right) rather than validation (it’s the right thing to build), but it keeps a confirmed requirement from quietly breaking.
Marty Cagan argues that a product idea carries four big risks: whether users will value it, whether they can use it, whether the team can build it, and whether it works for the business. Ask the same four questions of each requirement, with your partner standing in for the business. Cut or rewrite a requirement that fails one of them, because it costs as much to build as one that passes.
Measuring Success
Section titled “Measuring Success”The problem statement says what you’re fixing; success metrics say how you’ll know it’s fixed. Pick two or three, each with a number, a decision rule, and a collection method, and write them before you look at any data, because a metric chosen after seeing the data tends to be the one that looks good. What they count depends on the level you’re aiming at; choose them from the success metric frameworks. Keep the metrics in docs/requirements.md, next to the requirements they judge.
Best Practices for Writing Requirements
Section titled “Best Practices for Writing Requirements”These rules keep each requirement testable and the file current:
- Back each requirement with something you can point to: a partner quote, an observed session, a competitor that does it, a number. “The coordinator asked for it” is evidence; “it seemed useful” isn’t.
- Draw a wireframe or a flow diagram next to any requirement a paragraph struggles to describe, because a reader who can see the screen asks fewer questions about it.
- Update the file in the same pull request as the code that changes the behavior, so the document and the code can disagree for no longer than one review.
- Keep a functional requirement’s acceptance criteria in the file, not only on the board, until it’s finished and a test named after it runs in CI; then the test’s name replaces the criteria, and the statement stays.
- Keep the problem statement at the top, where a reviewer, a new teammate, and your AI tools read first.
- Write the reason next to each priority label, so whoever changes it later knows what they’re overriding.
- Keep requirements and design decisions apart. “Must store data durably” is a requirement; “use PostgreSQL” belongs in an ADR.
- Split any requirement that joins two behaviors with “and”, since each can pass or fail on its own.
Some Truths about Software Requirements
Section titled “Some Truths about Software Requirements”A requirements document can be busywork, and the version that turns into busywork is easy to recognize: long, unprioritized, written once, and stored somewhere other than the repository. Nothing in the team’s daily work touches that version, so nobody notices when it stops matching the product.
Expect much of the first version to be wrong. Requirements are guesses about what people need until a user or a partner confirms them, and the first guesses are made by a team that hasn’t yet watched anyone use anything. Practitioners report the same pattern: in the NaPiRE survey, incomplete or hidden requirements was the most often named critical problem (48% of respondents), and moving targets, meaning changing goals, business processes, or requirements, tied for third (33%) (Méndez Fernández et al., 2017). That’s why the assumed label matters: an unconfirmed requirement that looks identical to a confirmed one gets built with the same confidence.
The functional list is the part that duplicates other tools. Once your board items carry the FR IDs and their acceptance criteria, and your tests encode those criteria, the list in the file says the same thing a third time, and a copy nobody updates soon disagrees with the code. Don’t replace the requirements with a link to the board, though: the board isn’t versioned with the code, so someone reading the repository at a past commit can’t see what the criteria were then.
Shrink the criteria instead. Keep every requirement’s ID, priority label, and one-line statement, finished or not, because a test name says what is checked, not what a user can do, and a successor or an AI tool should learn what the product does without reading the test suite. Keep the criteria of every requirement still to be built, and once a finished one’s test runs in CI, replace its criteria with the test’s name (FR-2’s “Acceptance: a claim on a full shift fails” becomes “Test: test_second_claim_on_full_shift_fails”), because the test then carries the criteria with the code. A requirement checked only by hand keeps its criteria. The board also never holds the problem statement, the non-goals, the non-functional bars, the constraints, the success metrics, and the open questions, so keep those current in the file.
One argument for heavy up-front requirements is that a problem gets more expensive to fix the longer it goes unfound. Menzies et al. (2017) tested that delayed issue effect in 171 projects and found no evidence for it: resolving an issue in a later phase didn’t take consistently or substantially more effort than resolving it soon after it was introduced. Every one of those projects used the Team Software Process, which the authors describe as a mature process built for risk mitigation, and they don’t claim the result generalizes beyond it. They suggest the effect may still hold in high-assurance software, in architecturally complex systems, and in projects with poor engineering discipline, while saying they have no evidence either way. Where a late change can mean a recall or a recertification, as on a safety-critical, regulated, or hardware project, write a formal, contract-like requirements document anyway, because catching a wrong requirement early there is worth the paperwork.
Requirements in Industry and Academia
Section titled “Requirements in Industry and Academia”In industry the equivalent artifact is the product requirements document (PRD): a product’s features, behavior, constraints, and success metrics, written by a product manager for the engineers and designers who build it. Amazon starts earlier. Werner Vogels, its CTO, describes a process called Working Backwards: the team starts with the documents it would need at launch, the press release and the FAQ, then the customer experience and the user manual, and works from there toward documents closer to the implementation. Both exist for the reason your problem statement does, to force agreement on what’s being built before anyone builds it.
Open source writes its requirements into proposal processes. Python’s PEP 1 asks a proposal that changes the language, its library, or its ecosystem for a motivation section explaining why what exists is inadequate for the problem it solves, and the Rust RFC process asks for a written design before a substantial change is made.
In research, the equivalent is the research question and a preregistered analysis plan, written before the data is collected or analyzed so the analysis can’t be fitted to the results.
References
Section titled “References”- Méndez Fernández, D., Wagner, S., Kalinowski, M., Felderer, M., Mafra, P., Vetrò, A., Conte, T., Christiansson, M.-T., Greer, D., Lassenius, C., Männistö, T., Nayebi, M., Oivo, M., Penzenstadler, B., Pfahl, D., Prikladnicki, R., Ruhe, G., Schekelmann, A., Sen, S., Spinola, R., Tuzcu, A., de la Vara, J. L., and Wieringa, R. (2017). Naming the Pain in Requirements Engineering: Contemporary Problems, Causes, and Effects in Practice. Empirical Software Engineering. https://doi.org/10.1007/s10664-016-9451-7
- Lucassen, G., Dalpiaz, F., van der Werf, J. M. E. M., and Brinkkemper, S. (2016). The Use and Effectiveness of User Stories in Practice. Requirements Engineering: Foundation for Software Quality (REFSQ 2016), Lecture Notes in Computer Science 9619. https://doi.org/10.1007/978-3-319-30282-9_14
- Menzies, T., Nichols, W., Shull, F., and Layman, L. (2017). Are Delayed Issues Harder to Resolve? Revisiting Cost-to-Fix of Defects throughout the Lifecycle. Empirical Software Engineering. https://doi.org/10.1007/s10664-016-9469-x
Additional Readings
Section titled “Additional Readings”Sources and further reading
- Software Requirements, 3rd Edition, Karl Wiegers and Joy Beatty, Microsoft Press.
- User Stories Applied, Mike Cohn, Addison-Wesley.
- Specification by Example, Gojko Adzic, Manning: acceptance criteria written as examples that become tests.
- Inspired, Marty Cagan, Wiley: product discovery, including the four risks above.
- Writing Requirements, Jama Software: phrasing guidance from a requirements tooling vendor.
Activities that exercise this
- User Story Mapping: produces the first real version of this document in a single session.
- The Mom Test: interviews that surface requirements without leading the person to the answer you wanted.
- Stakeholder Mapping: who has a requirement to give you, and whose you haven’t heard yet.
- User Manual: writing the manual first surfaces requirements you hadn’t noticed.
- Prioritization: choosing the method behind the priority labels, and ordering the board.
- Define your Personas and Find Users: how an assumed requirement becomes a confirmed one.
- Identify Success Metrics: the metrics this document records, chosen before the data comes in.
- Definition of Done: the bar a requirement has to clear to count as met.
- Domain Model: the shared vocabulary your requirements are written in.
- Analyze an Existing Codebase: the constraints an inherited codebase puts on you, written down before you change it.
- Lean Canvas and Jobs to Be Done Theory: framing what the product is for before listing what it does.
- Current vs Ideal Workflow and Is / Is-Not: two fast ways to bound scope with a partner.
- User’s Emotional Objectives: the requirements people don’t state directly.
- Prototype an Interface: the cheapest way to confirm a requirement before building it, at low fidelity first and high fidelity once the shape has settled.
- Create Good Survey or Interview Questions: how you avoid leading the partner to the answer you wanted.
- Define your Research Questions, Define the Data to Collect, and Hypothesis Testing: the equivalents of a requirements list when your project answers a question rather than builds a feature.
- Ideate About User Activities and Do a User Activity: requirements found by watching rather than asking.