CMPUT-301 · Lesson 3 of 12 · 35 min

Requirements engineering

Requirements describe what a system must do and the qualities it must have. They are the bridge between people who need software and people who build it, and they are where many projects go wrong: the Standish Group's long-running surveys and countless post-mortems name unclear, incomplete or changing requirements among the top causes of failure. Requirements engineering is the set of activities that discover, analyze, document, validate and manage requirements throughout a project. This lesson covers the kinds of requirements, how to elicit them from stakeholders, how to write them so they can be tested, the two dominant formats (user stories and use cases), how to prioritize and trace them, and the accessibility and privacy requirements that law imposes on most public-facing software. ROB-499 introduced requirements for a robotics capstone and PMP-121 covered stakeholder analysis for project managers; here the focus is on software requirements a team can build and test.

Kinds of requirements

Functional requirements describe what the system does: the services it provides, how it reacts to inputs, and what it outputs. 'A volunteer can sign up for an available shift' and 'the system emails a reminder 24 hours before each shift' are functional. Non-functional requirements, often called quality attributes, describe how well the system does it: performance, availability, security, usability, accessibility, maintainability, portability and so on. 'Pages load in under 2 seconds' and 'the system meets WCAG 2.1 level AA' are non-functional. Constraints restrict how the system may be built, for example 'must run on the city's existing PostgreSQL servers' or 'must store data only in Canada'.

Non-functional requirements are easy to neglect and expensive to retrofit, because they often shape the architecture. A system designed for 50 users in one office may need fundamental redesign to serve 50,000 users across the country; a system designed without accessibility in mind may need its user interface rebuilt. The international standard ISO/IEC 25010, revised in 2023, gives a checklist of quality characteristics: functional suitability, performance efficiency, compatibility, interaction capability (formerly usability), reliability, security, maintainability, flexibility (formerly portability) and safety.

Kinds of requirements with testable examples
TypeExampleHow it is checked
FunctionalA coordinator can cancel a shift; signed-up volunteers are notifiedAcceptance test of the scenario
Performance95 percent of searches return in under 1 second with 300 concurrent usersLoad test
AvailabilityAvailable 99.5 percent of each month, excluding announced maintenanceExternal monitoring
SecurityPasswords stored with a salted, slow hash; sessions expire after 30 minutes idleCode review, security testing
AccessibilityMeets WCAG 2.1 level AAAutomated checks plus manual testing with screen readers
MaintainabilityNew report types can be added without changing the database schemaDesign review
ConstraintRuns on the client's existing Linux serversDeployment review

Stakeholders and elicitation

A stakeholder is anyone who affects or is affected by the system: users of several kinds, customers who pay, managers, operators, regulators, and people who will never touch it but whose data it holds. Requirements elicitation is the work of discovering what they need. It is called elicitation rather than gathering because requirements are rarely lying around waiting to be collected. Users often cannot describe what they need in the abstract, describe solutions instead of problems ('add a button that exports to Excel' rather than 'I need to send the monthly totals to the board'), leave out what seems obvious to them, and disagree with each other.

Elicitation techniques
TechniqueGood forWatch out for
InterviewsUnderstanding goals, pain points and vocabularyPeople describe what they think they do, not what they do
Observation (contextual inquiry)Discovering the real workflow and workaroundsTime-consuming; people behave differently when watched
WorkshopsBuilding agreement among several stakeholder groupsLoud voices dominate; needs a skilled facilitator
QuestionnairesReaching many users for frequency and preference dataCannot ask follow-up questions
Document and system analysisRules, forms, reports and the old system's behaviorDocuments may be out of date
Prototypes and mock-upsGetting concrete feedback on interfacesUsers may think a prototype is nearly finished

A common thread is to ask about goals and problems before solutions. The 'five whys' technique, borrowed from lean manufacturing, keeps asking why until the underlying need appears. A food bank coordinator asks for a spreadsheet export. Why? To count volunteer hours. Why? To report to the city. Why? Because the city's grant requires monthly hours by program. That answer suggests a monthly report in the city's format, which is more useful than an export the coordinator must then process by hand.

Writing good requirements

A good requirement is necessary, unambiguous, verifiable, feasible, consistent with the others and traceable to its source. Verifiable is the property most often missing. 'The system shall be user-friendly' cannot be tested; 'a first-time volunteer can sign up for a shift in under 3 minutes without help, in usability testing with 8 participants' can. Watch for vague words: fast, easy, flexible, robust, efficient, user-friendly, minimal, as appropriate, and so on. Replace them with numbers, conditions and measurement methods.

Quantifying non-functional requirements usually needs three parts: the quality being measured, the target value, and how and under what conditions it is measured. Percentiles are better than averages for response time, because an average of 1 second can hide a tail where one request in ten takes 8 seconds, which is the experience users remember.

Why response-time requirements use percentiles: a good mean can hide a slow tail
import random, statistics

random.seed(301)
# Simulated response times (seconds): most requests are quick, a few are very slow
times = [random.lognormvariate(-0.6, 0.45) for _ in range(950)] + \
        [random.uniform(3.0, 9.0) for _ in range(50)]

def percentile(data, p):
    s = sorted(data)
    k = max(0, min(len(s) - 1, round(p / 100 * len(s)) - 1))
    return s[k]

print(f"mean   {statistics.mean(times):.2f} s")
print(f"median {percentile(times, 50):.2f} s")
print(f"p95    {percentile(times, 95):.2f} s")
print(f"p99    {percentile(times, 99):.2f} s")
target = 2.0
ok = percentile(times, 95) <= target
print(f"requirement 'p95 <= {target} s':", "met" if ok else "NOT met")
Output
mean   0.87 s
median 0.54 s
p95    2.03 s
p99    8.17 s
requirement 'p95 <= 2.0 s': NOT met

Worked problem

Making a requirement testable

Rewrite each requirement so it can be verified: (a) "The search must be fast." (b) "The system should be secure." (c) "Reports should be easy to produce." (d) "The app must work offline."

Show solution
  1. (a) "With 300 concurrent users and 50,000 shift records, 95 percent of searches return results in under 1 second, measured at the server."
  2. (b) Security is many requirements, for example: "All traffic uses TLS 1.2 or later"; "Passwords are hashed with a memory-hard algorithm such as Argon2id"; "A user can see only the volunteer records of programs they coordinate."
  3. (c) "A coordinator can produce the monthly hours report for any program in at most 3 clicks from the dashboard; 7 of 8 test participants do so without help."
  4. (d) "Volunteers can view their upcoming shifts without a network connection; sign-ups made offline are queued and sent within 1 minute of reconnecting, and conflicts are reported to the user."

Answer: Each rewrite names a measurable target, the conditions and how it will be checked. Vague qualities such as "secure" usually split into several concrete requirements.

User stories

Agile teams usually express requirements as user stories: short descriptions of a capability from a user's point of view, written in the form 'As a <role>, I want <capability>, so that <benefit>.' For example: 'As a volunteer, I want to see open shifts near me, so that I can choose one that fits my schedule.' The format keeps attention on who needs something and why. A story is deliberately not a full specification; Ron Jeffries described it as a card, a conversation and a confirmation. The card is the short text, the conversation is the discussion in which the team and product owner work out the details, and the confirmation is the set of acceptance criteria that say when the story is done.

Bill Wake's INVEST mnemonic describes good stories: Independent (can be built in any order), Negotiable (details are open for discussion), Valuable (delivers value to a user or customer), Estimable (the team can size it), Small (fits comfortably in a sprint) and Testable (has clear acceptance criteria). Large stories, called epics, are split into smaller ones. Good splits follow the workflow or data variations, for instance 'sign up for a shift' then 'cancel a sign-up' then 'join a waiting list when a shift is full', rather than technical layers such as 'build the database' and 'build the screen', which deliver no value alone.

Acceptance criteria are often written in the Given/When/Then style from behavior-driven development (BDD): Given a starting context, When an event occurs, Then an observable outcome follows. Each scenario is concrete enough to become an automated test.

Given/When/Then acceptance criteria turned into executable checks
# Acceptance criteria for "As a volunteer, I want to sign up for an open shift"
# written as Given/When/Then scenarios and executed against a small model.

class Shift:
    def __init__(self, capacity):
        self.capacity, self.volunteers, self.waitlist = capacity, [], []

    def sign_up(self, name):
        if name in self.volunteers:
            return "already signed up"
        if len(self.volunteers) < self.capacity:
            self.volunteers.append(name)
            return "confirmed"
        self.waitlist.append(name)
        return "waitlisted"

def scenario(title, given, when, then):
    shift = given()
    result = when(shift)
    ok = then(shift, result)
    print(f"{'PASS' if ok else 'FAIL'}: {title}")

scenario("Given a shift with a free spot, when Ana signs up, then she is confirmed",
         lambda: Shift(2), lambda s: s.sign_up("Ana"),
         lambda s, r: r == "confirmed" and "Ana" in s.volunteers)
scenario("Given a full shift, when Ben signs up, then he is put on the waiting list",
         lambda: (s := Shift(1), s.sign_up("Ana"))[0], lambda s: s.sign_up("Ben"),
         lambda s, r: r == "waitlisted" and s.waitlist == ["Ben"])
scenario("Given Ana is signed up, when she signs up again, then nothing changes",
         lambda: (s := Shift(2), s.sign_up("Ana"))[0], lambda s: s.sign_up("Ana"),
         lambda s, r: r == "already signed up" and s.volunteers == ["Ana"])
Output
PASS: Given a shift with a free spot, when Ana signs up, then she is confirmed
PASS: Given a full shift, when Ben signs up, then he is put on the waiting list
PASS: Given Ana is signed up, when she signs up again, then nothing changes

Use cases

Use cases, popularized by Ivar Jacobson and later by Alistair Cockburn, describe a sequence of interactions between an actor and the system that achieves a goal. A fully dressed use case has a name, a primary actor, preconditions, a main success scenario as numbered steps, extensions (alternative and error flows keyed to the steps where they branch), and postconditions. Use cases are more detailed than user stories and better at capturing the alternative flows where many defects hide. Many teams use both: stories to plan and prioritize, and use-case-style scenarios for complex interactions.

A fully dressed use case in table form
SectionUse case: Volunteer signs up for a shift
Primary actorVolunteer
PreconditionsThe volunteer is logged in and has completed orientation
Main success scenario1. Volunteer searches open shifts by date. 2. System lists shifts with free spots. 3. Volunteer selects a shift. 4. System shows details and asks for confirmation. 5. Volunteer confirms. 6. System records the sign-up and sends a confirmation email.
Extension 2aNo shifts match: system suggests nearby dates.
Extension 5aShift filled while the volunteer was deciding: system offers the waiting list.
Extension 5bVolunteer already has an overlapping shift: system warns and asks whether to continue.
PostconditionsThe volunteer appears on the shift roster; the free-spot count is reduced by one.

Lesson 5 shows how use case diagrams summarize actors and goals graphically. The diagram is only an index; the value lies in the written scenarios.

Prioritizing requirements

There is never time to build everything, so requirements must be ranked. The MoSCoW method sorts them into Must have, Should have, Could have and Won't have this time. The Kano model distinguishes basic expectations (users are angry if missing, but not delighted if present), performance attributes (more is better) and delighters (unexpected features that please). For agile backlogs, a simple and effective method is to rank by value relative to cost, sometimes refined as weighted shortest job first: the cost of delaying an item divided by its size. Whatever method is used, the product owner or customer decides priorities, and the team supplies cost estimates and technical risk.

Weighted shortest job first puts valuable, small, urgent items at the top
# Ranking a backlog by weighted shortest job first: cost of delay / size
backlog = [
    # (story, user/business value, time criticality, risk reduction, size in points)
    ("Sign up for a shift",         8, 8, 3, 5),
    ("Shift reminder email",        5, 3, 1, 2),
    ("Monthly hours report",        8, 8, 2, 3),
    ("Waiting list",                3, 2, 1, 3),
    ("Single sign-on with city ID", 3, 1, 8, 8),
]
ranked = sorted(backlog, key=lambda s: (s[1] + s[2] + s[3]) / s[4], reverse=True)
print(f"{'story':30s} {'CoD':>4s} {'size':>5s} {'WSJF':>5s}")
for name, v, t, r, size in ranked:
    cod = v + t + r
    print(f"{name:30s} {cod:4d} {size:5d} {cod / size:5.2f}")
Output
story                           CoD  size  WSJF
Monthly hours report             18     3  6.00
Shift reminder email              9     2  4.50
Sign up for a shift              19     5  3.80
Waiting list                      6     3  2.00
Single sign-on with city ID      12     8  1.50

Traceability and change

Traceability links each requirement to its source (the stakeholder or regulation that asked for it), to the design elements and code that implement it, and to the tests that verify it. A traceability matrix makes these links visible. It answers questions such as: which tests cover the privacy requirements, which requirements are untested, and what must be retested if a requirement changes. Regulated industries require it; other teams can get much of the benefit by tagging tests and commits with story or requirement identifiers.

A traceability matrix as data, with checks for gaps in both directions
# A small traceability check: every requirement needs at least one test,
# and every test should trace to a requirement.
requirements = {"R1": "Volunteer signs up for a shift",
                "R2": "Waiting list when a shift is full",
                "R3": "Reminder 24 hours before a shift",
                "R4": "Coordinator monthly hours report",
                "R5": "Meets WCAG 2.1 AA"}
tests = {"test_signup_confirmed": ["R1"], "test_signup_full_waitlists": ["R1", "R2"],
         "test_report_totals": ["R4"], "test_report_csv_format": ["R4"],
         "test_login_page_loads": []}

covered = {r for links in tests.values() for r in links}
print("untested requirements:", sorted(set(requirements) - covered))
print("tests with no requirement:", [t for t, links in tests.items() if not links])
for r in sorted(requirements):
    ts = [t for t, links in tests.items() if r in links]
    print(f"{r}: {len(ts)} test(s)")
Output
untested requirements: ['R3', 'R5']
tests with no requirement: ['test_login_page_loads']
R1: 2 test(s)
R2: 1 test(s)
R3: 0 test(s)
R4: 2 test(s)
R5: 0 test(s)

Requirements change, and an iterative process expects that. What must be managed is uncontrolled change, often called scope creep, where small additions accumulate without anyone weighing their cost. A simple discipline is that every change goes into the backlog, is estimated, and displaces something else of lower priority unless the schedule or budget changes. In contract work, change requests are formal and priced.

Accessibility and privacy as requirements

Accessibility and privacy requirements come partly from law. Title II of the Americans with Disabilities Act covers state and local governments; a Department of Justice rule issued in 2024 requires their websites and mobile apps to meet WCAG 2.1 level AA, and in April 2026 the department extended the compliance dates by one year, to April 2027 for larger entities and April 2028 for smaller ones. Title III, covering businesses open to the public, has no specific technical rule, but courts and settlements routinely use WCAG as the yardstick, and thousands of accessibility lawsuits are filed each year. Section 508 of the Rehabilitation Act requires federal agencies to buy and build accessible technology, using a standard aligned with WCAG 2.0 level AA. For privacy, the United States has no general federal privacy law; sector laws such as HIPAA for health data, FERPA for education records and COPPA for children's data apply, along with comprehensive state laws such as the California Consumer Privacy Act as amended by the CPRA. These obligations should appear in the backlog as explicit, testable requirements, not as a vague hope to be 'compliant'.

Privacy by design, a framework developed by Ann Cavoukian when she was Ontario's Information and Privacy Commissioner, asks teams to treat privacy as a default requirement from the start: collect only the data needed, keep it only as long as needed, restrict who can see it, and make the privacy-protective choice the default. CMPUT-291 lesson 11 covered how these ideas apply to database design.

Worked problem

From interview notes to a backlog

Notes from an interview with a food bank's volunteer coordinator: "We use a paper sign-up sheet. People forget their shifts, maybe one in eight don't show. I spend Friday afternoons phoning reminders. The city wants hours per program each month. Some volunteers are teenagers and need a parent's permission. A few volunteers use screen readers." Write four user stories with one acceptance criterion each, and identify one constraint or non-functional requirement.

Show solution
  1. As a volunteer, I want to sign up for a shift online, so that I can choose shifts without visiting the office. Given an open shift, when I confirm, then I appear on its roster.
  2. As a volunteer, I want a reminder before my shift, so that I do not forget it. Given a confirmed shift tomorrow at 9:00, when it is 9:00 today, then I receive an email and text reminder.
  3. As a coordinator, I want a monthly hours report by program, so that I can meet the city's grant reporting. Given recorded attendance, when I choose a month, then I see total hours per program in the city's format.
  4. As a parent, I want to approve my teenager's volunteering, so that the food bank has my consent. Given a volunteer under 16 (the age is set by the organization's policy), when they register, then they cannot sign up until a parent approves by email.
  5. Non-functional: the sign-up flow meets WCAG 2.1 AA and is tested with a screen reader. Privacy: minors' data is visible only to coordinators.

Answer: The interview reveals goals (fewer no-shows, less phoning, grant reporting) behind the coordinator's words. Stories capture each goal with a testable criterion; accessibility and privacy become explicit requirements.

Common mistakes

  • MistakeWriting requirements with vague words such as fast, easy or secure.

    InsteadState a measurable target, the conditions and the measurement method, for example a 95th-percentile response time under a stated load.

  • MistakeRecording the solution a stakeholder suggests instead of the underlying need.

    InsteadAsk why until the goal appears; a different solution often serves it better.

  • MistakeSplitting stories by technical layer.

    InsteadSplit by workflow steps, rules or data variations so each story delivers something a user can see and test.

  • MistakeIgnoring non-functional requirements until late.

    InsteadPerformance, security and accessibility shape the architecture; capture them early and test them continuously.

  • MistakeTreating legal accessibility and privacy obligations as someone else's problem.

    InsteadTurn them into explicit backlog items with acceptance criteria, and test them like any other requirement.

Summary and key terms

  • Requirements state what a system must do (functional) and how well (non-functional quality attributes), plus constraints on how it may be built.
  • Elicitation uses interviews, observation, workshops, questionnaires, document analysis and prototypes, and focuses on goals before solutions.
  • Good requirements are necessary, unambiguous, verifiable, feasible, consistent and traceable; vague quality words must be replaced by measurable targets.
  • User stories follow "As a role, I want capability, so that benefit", should satisfy INVEST, and carry Given/When/Then acceptance criteria.
  • Use cases describe a main success scenario plus extensions for alternative and error flows.
  • Prioritization methods include MoSCoW, Kano and weighted shortest job first; traceability links requirements to sources, code and tests.
  • Accessibility (WCAG-based law) and privacy law impose requirements on most public-facing software and belong in the backlog from the start.
Functional requirement
A statement of a service or behavior the system must provide.
Non-functional requirement
A required quality of the system, such as performance, security or accessibility.
Constraint
A restriction on how the system may be built or deployed.
Stakeholder
Anyone who affects or is affected by the system.
Elicitation
Discovering requirements from stakeholders and other sources.
User story
A short statement of a capability from a user's perspective, with acceptance criteria.
Acceptance criteria
Conditions that a story must satisfy to be accepted, often written as Given/When/Then.
Use case
A structured description of interactions between an actor and the system to achieve a goal.
Traceability
Links between requirements, their sources, design, code and tests.
Scope creep
Uncontrolled growth of requirements without corresponding changes to time or budget.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. Classify: "Exports must be in CSV"; "A coordinator can merge duplicate volunteer records"; "Must run on the client's Linux servers"; "Merging takes under 2 seconds".

    Show answer

    Functional (or constraint if imposed by an external system); functional; constraint; non-functional (performance).

  2. Rewrite "the site should be highly available" testably.

    Show answer

    For example: available 99.9 percent of each calendar month, excluding scheduled maintenance announced 48 hours ahead, measured by an external probe every minute.

  3. Give the INVEST letters and their meanings.

    Show answer

    Independent, Negotiable, Valuable, Estimable, Small, Testable.

  4. Split the epic "Volunteers manage their shifts" into three stories.

    Show answer

    For example: sign up for a shift; cancel a sign-up up to 24 hours before; swap a shift with another volunteer.

  5. Write a Given/When/Then scenario for canceling a shift less than 24 hours before it starts.

    Show answer

    Given I am signed up for a shift starting in 10 hours, when I try to cancel, then I am told to phone the coordinator and my sign-up remains.

  6. Why are percentiles preferred to averages for response-time requirements?

    Show answer

    Averages hide slow tails; users notice the slow requests, which a 95th or 99th percentile captures.

  7. A stakeholder asks for "a button to download all volunteer data". What would you ask?

    Show answer

    Why: what will they do with it, who receives it, which fields are needed, how often. The real need may be a specific report, and exporting everything raises privacy concerns.

  8. What does a traceability matrix let you find?

    Show answer

    Untested requirements, tests that trace to nothing, and which tests to rerun when a requirement changes.

  9. Name two accessibility laws or standards that apply where you live and the WCAG level they use.

    Show answer

    For example: ADA Title II rule (WCAG 2.1 AA) and Section 508 (aligned with WCAG 2.0 AA).

  10. In MoSCoW, what does the W stand for and why is it useful?

    Show answer

    Won't have this time; it makes explicit what is out of scope so expectations stay realistic.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz