How to Write Requirements

There are many formal languages, notations, and layouts for writing requirements; these including specialized diagrams, logics, and more. Because the study of these notations is a subject in its own right, in this course we will stick to English, sometimes stylized.

Irrespective of what language you use, it's essential to remember the following:

Use Cases

In this course, we'll ask you to write requirements as use cases. You can, and should, read more about them on Wikipedia and elsewhere on the Web. This is only an overview.

Example Task

Design and build a cash register for a fast-food chain.

First, we must identify the participants in the system. The participants may not all be humans; indeed, in some systems very few of them are. For instance, in an automobile control system, the participants include the wheels, engine, steering, various sensors and actuators...and only incidentally the driver. These are all typically called actors.

Example Actors

Walk-In Customers, Drive-Up Customers, Cash Register, Food Preparation Workers, Counter Staff, Drive-Up Window Staff, Branch Managers, Store, Location Owners, Franchise Management, Franchies Stockholders.
Comment: As this example illustrates, there may be hierarchies and relationships amongst actors. For instance, it is sometimes necessary to differentiate between different kinds of customers, but at other times it is convenient to refer to them customers uniformly. Thus, object-oriented design (which does not necessarily have to be implemented as an object-oriented program) uses is-a (a Walk-In Customer is-a Customer) and has-a (a Store has a Branch Manager) relations to model actors.

Actors engage in actions. We identify examples of these actions, called scenarios. A scenario describes which actors are involved and a sequence of actions or events. When describing a scenario, take into account both the necessary and the available information and stimuli. Scenarios are concrete.

Example Scenario

Name: OrderAtRegister

Actors: Walk-In Customers (c), Counter Staff (s), Cash Register (r)
Goal: c want to place an order by paying s
  1. c approach counter.
  2. s greets c.
  3. c state desired order.
  4. s enters order data into r.
  5. s asks c how they wish to pay.
  6. s processes payment with r.
  7. s makes r print receipt.
  8. s hands a member of c the receipt, repeating the order designator.
  9. s thanks c and acknowledges any response.
  10. If c are still in front of console, s points to pick-up area to indicate where order will be available.
  11. s shifts gaze to next available customer or to console in order to prompt c to move on.
  12. c moves to food pick-up area.
Comment: This looks fairly straight-forward; how many more scenarios can there be? Well, let's think. Did you notice that even if a group of people showed up together, there was only one payment? Sometimes a group arrives at the counter together, is greeted just once, but each member pays separately. Sometimes a customer orders an item, then changes their mind. Sometimes a customer asks for a change that requires an override from a manager (who wasn't part of the above scenario at all). A customer might ask a question that requires an inquiry made to the kitchen or the manager. A customer might present a coupon. A customer might place an order for something not in stock. A customer might get an emergency call, leave, and return hours later for their food! So this scenario really should have been called SimpleOrderAtRegister. And we haven't begun to account for drive-up orders, perhaps phone-in orders, etc.

Finally, we collect groups of related scenarios into use cases. A use case generalizes is a collection of related scenarios. It is meant to capture all the possible behaviors related to some functionality. It drops detail specific to individual scenarios, and generalizes over their commonality. A use case can refer to other use cases, rather like procedures invoking one another.

Gathering scenarios into use cases is a fine art. If we generalize too little, we don't get useful abstractions (and find ourselves with too many use cases). If we generalize too much, there is too little detail for the user to comment on (or find flaws in). There is no single right answer, so try several groupings before settling on one; also be aware that different parts of the development process will care about different groupings.

Example Use Case

Name: TakingOrder

Actors: Customers, Order Staff, Branch Manager, Food Preparation Workers, Cash Register
Initiator: Customers
  1. Customers approach Order Staff.
  2. Order Staff greets Customers.
  3. Customers and Order Staff execute OrderStatement using Cash Register.
  4. Order Staff, Food Preparation Workers, and Manager execute FoodClarification, if necessary.
  5. Order Staff and Customers execute PaymentNegotiation. using Cash Register
  6. Order Staff engages Branch Manager in Order Reconciliation, if necessary.
  7. Order Staff issues Customers order acknowledgment from Cash Register.
  8. Order Staff thanks Customers and acknowledges any response.
  9. Order Staff shifts attention to next Customers.
  10. Customers move to collect order.
Comment: We have generalized all ordering scenarios into one use case. From one perspective, this is great: it captures what is common to both walk-in and drive-in orders while eliminating all their differences. On the other hand, it also masks huge physical differences of location, lines of sight, and means of mobility. The person who eventually designs the database may not care, but the person who builds the physical interface probably does.

As you record these scenarios and abstract them, you may find that some information you obtain doesn't fit into any one work-flow. In particular, you will find expectations about behaviors under all circumstances. Don't lose these; record them separately! They will eventually lead to system tests.

Example System Expectations
Comment: The implementation of some of these expectations will already be manifest in the scenarios. Others, however, may not be reflected in any of them, because they are at a different level of abstraction than the scenarios reflect, or their implementation requires the interaction of many different parts of the system. Preserving these as a separate check-list that must be obeyed by all implementations helps us avoid losing them.

Finally, you will encounter one other kind of information: statements about the operating environment. Record these separately: they tell the system designers what they can expect and rely upon. (In some cases, the environment may not provide enough for the designer to provide what is expected!)

Example Operating Environment
Comment: Notice that we haven't said anything about the uninterrupted availability of power (the generator may need to be operated manually). Therefore, the designer of the cash register will probably need to include some sort of battery facility. But this, in turn, may drive costs above the upper-bound specified.

Level of Detail

The Wikipedia article gives an extensive template for what one might put in a use case (and, similarly, in a scenario). You don't need to (and probably shouldn't) use all of these for every single scenario and use case: use your discretion. However, you may find the template a useful checklist. Some of the entries may remind you of aspects that you hadn't thought of initially, and may force you to revisit information or perhaps even ask additional questions of users. At any rate, remember that verbosity can be the enemy of readability, and you ultimately want your users to read your use cases!