Requirements elicitation
Most requests arrive as a proposed solution. Building it exactly as asked is how you ship something that works and nobody uses.
This runs before interrogation and planning. Interrogation locks decisions; this works out what the decisions are about.
1. The request is a solution. Find the job.
"Add a CSV export to the reports page."
That is a solution. The job might be reconciling our numbers against the finance system every month, in which case a CSV is one answer and a reconciliation view is a better one. Or the job might be my accountant only accepts CSV, in which case a CSV is exactly right and the job is closed.
The question that gets you there is not "why?" — which sounds like a challenge — but:
- "What will you do with it once you have it?"
- "Walk me through the last time you needed this."
- "What happens today when you need it and it isn't there?"
You are looking for the task, not the feature.
2. Find the facts yourself
A question the codebase, the data or the logs can answer is not a question for the user, and asking it burns the credibility you need for the questions only they can answer.
Before asking anything:
- Is this already solved somewhere in the repo? Extending beats inventing.
- What does the data actually say — how many records, how often, which users?
- What do the logs say about the current failure?
- Has this been decided before, in a plan, an ADR, a ticket or a commit?
Ask only what is genuinely a judgement call, a preference, or a fact that lives only in someone's head.
3. Name the user, concretely
"Users want this" is the sound of nobody having asked. Get to a specific person or role, and to a specific moment:
| Vague | Usable |
|---|---|
| Users need better reporting | A regional manager, on Monday morning, comparing last week's sites |
| Make onboarding easier | A new operator, on their phone, on site, with no one to ask |
| It should be faster | The daily 9am bulk import, currently 40 minutes, blocking the team |
The moment matters as much as the person. It tells you the device, the attention available, the tolerance for a second click, and what "fast enough" means.
4. Find the current workaround
There is almost always one — a spreadsheet, a WhatsApp group, a manual step, someone re-typing. It is the single most informative artifact available:
- It shows what the real inputs and outputs are.
- It shows the bar you must clear. If the workaround takes two minutes, a ten-minute "proper" flow loses.
- Its edge cases are the real requirements. The odd column nobody can explain is usually a rule.
Ask to see it. Not a description of it — the actual file.
5. Get the numbers that change the design
Ask for these every time, because the answers move the architecture:
- Volume — how many records, today and in a year.
- Frequency — once a month or every thirty seconds.
- Concurrency — one person at a time, or forty.
- Latency tolerance — instant, a few seconds, or "email it to me when done".
- Retention — how far back does this have to be true.
- Correctness bar — does an approximate answer help, or is it worthless?
An estimate is fine. "I don't know" is an answer too, and it means design for the order of magnitude you can defend.
6. Acceptance criteria that can fail
A criterion that cannot be observed to fail is a slogan.
Bad: The report should be fast and easy to use.
Good: A manager can produce last week's comparison for 12 sites in under
3 clicks from the home screen, and it renders in under 2 seconds
on a mid-range phone on 4G.
Each one is observable, has a subject, and has a number or a concrete outcome someone could disagree about. Write them before the design, not after — criteria written afterwards describe whatever got built.
7. Write the non-goals down
The non-goals list is the highest-value paragraph in the document, and the one that gets skipped. It prevents the scope returning later as an assumption.
Not in this: editing after submission, bulk import, a second language,
notifying anyone, historical data before go-live.
Read it back. Anything the requester flinches at was not actually a non-goal, and you have just found a requirement cheaply.
8. Separate what is fixed from what is preference
Three buckets, and label every requirement into one:
- Hard constraint — a regulation, a contract, an integration that exists, a deadline tied to something external. Not negotiable; design around it.
- Strong preference — how they would like it, with a reason.
- Assumption — something everyone believes and nobody has checked.
Assumptions are where projects die. Pull each one out and mark it as needing verification, with what would verify it.
9. Decide what you can; escalate only what changes the work
You will finish with open points. Sort them:
- Answerable by you — decide, state the assumption in writing, move on.
- Changes the work materially — ask. Different answers mean different builds.
- Can be deferred — note what would force the decision later.
Do not stop the whole thing for a question in the first bucket, and do not silently guess on one in the second.
Output
A short document, not a transcript:
## The job
<what someone is trying to get done, in their words>
## Who, and when
<the specific person and the moment>
## Today
<the current workaround, and what it costs>
## Acceptance criteria
- [ ] <observable, with a number>
## Not in this
- <explicit non-goals>
## Constraints
<hard constraints, and what they rule out>
## Assumptions
<each one, and what would verify it>
## Numbers
volume / frequency / concurrency / latency / retention / correctness bar
## Open
<what still needs an answer, and who has it>
Then hand it to interrogation or design. Do not start building from it.
Checklist
- The underlying job identified, not just the requested feature
- Everything the repo, data or logs could answer was looked up, not asked
- A specific person and a specific moment named
- The current workaround seen, not described
- Volume, frequency, concurrency, latency, retention and correctness captured
- Acceptance criteria observable and falsifiable
- Non-goals written and read back
- Assumptions separated from constraints, each with a way to verify