When Use Cases Aren't Enough
- The Power of Use Cases
- Project Type Limitations
- Event-Response Tables
- Use Cases Don't Replace Functional Requirements
- Use Cases Reveal Functional Requirements
Use Cases Reveal Functional Requirements
Rather than expecting use cases to contain 100 percent of the system’s functionality, I prefer to employ use cases to help the analyst discover the functional requirements. That is, the use cases become a tool rather than being an end unto themselves. Users can review the use cases to validate whether a system that implemented the use cases would meet their needs. The analyst can study each use case and derive the functional requirements the developer must implement to realize the use case in software. I like to store those functional requirements in a traditional SRS, although you could add them to the use case description if you prefer.
I’m often asked, “Which comes first: use cases or functional requirements?” The answer is use cases. Use cases represent requirements at a higher level of abstraction than do the detailed functional requirements. I like to focus initially on understanding the user’s goals so that we can see how they might use the product to achieve those goals. From that information, the analyst can derive the necessary functionality that must be implemented so that the users can perform those use cases and achieve their goals.
Functional requirements—or hints about them—lurk in various parts of the use case. The remainder of this chapter describes a thought process an analyst can go through to identify the less obvious functional requirements from the elements of a use case description.
Preconditions state conditions that must be true before the actor can perform the use case. The system must test whether each precondition is true. However, use case descriptions rarely state what the system should do if a precondition is not satisfied. The analyst needs to determine how best to handle these situations.
Suppose a precondition for one use case states, “The patron’s account must be set up for payroll deduction.” How does the system behave if the patron attempts to perform this use case but his account is not yet set up for payroll deduction? Should the system notify the patron that he can’t proceed? Or should the system perhaps give the patron the opportunity to register for payroll deduction and then proceed with the use case? Someone has to answer these questions and the SRS is the place to provide the answers.
Postconditions describe outcomes that are true at the successful conclusion of the use case. The steps in the normal flow naturally lead to certain postconditions that indicate the user’s goal has been achieved. Other conditions, however, might not be visible to the user and therefore might not become a part of a user-centric use case description.
Consider an automated teller machine. After a cash withdrawal, the ATM software needs to update its record of the amount of cash remaining in the machine by subtracting the amount withdrawn. Perhaps if the cash remaining drops below a predetermined threshold the system is supposed to notify someone in the bank to reload the machine with additional money. I doubt any user will ever convey this information during a discussion of user requirements, yet the developer needs to know about this functionality.
How can you best communicate this knowledge to the developers and testers? There are two options. One is to leave the use case at the higher level of abstraction that represents the user’s view and have the requirements analyst derive the additional requirements through analysis. The analyst can place those requirements in an SRS that is organized to best meet the developer’s needs. The second alternative is for the analyst to include those additional details directly in the use case description. That behind-the-scenes information is not part of the user’s view of the system as a black box. Instead, you can think of that information as being white-box details about the internal workings of the use case that the analyst must convey to the developer.
Normal and Alternative Flows
The functionality needed to carry out the dialogue between the actor and the system is usually straightforward. Simply reiterating these steps in the form of functional requirements doesn’t add knowledge, although it might help organize the information more usefully for the developer. The analyst needs to look carefully at the normal flow and alternative flows to see if there’s any additional functionality that isn’t explicitly stated in the use case description. For example, under what conditions should the system offer the user the option to branch down an alternative flow path? Also, does the system need to do anything to reset itself so that it’s ready for the next transaction after the normal flow or an alternative flow is fully executed?
The analyst needs to determine how the system could detect each exception and what it should do in each case. A recovery option might exist, such as asking the user to correct an erroneous data entry. If recovery isn’t possible, the system might need to restore itself to the state that existed prior to beginning the use case and log the error. The analyst needs to identify the functionality associated with such recovery and restore activities and communicate that information to the developer.
Many use cases are influenced by business rules. The use case description should indicate which business rules pertain. It’s up to the analyst to determine exactly what functionality the developer must implement to comply with each rule or to enforce it. These derived functional requirements should be recorded somewhere (I recommend documenting them in the SRS), rather than just expecting every developer to figure out the right way to comply with the pertinent business rules.
Special Requirements and Assumptions
The use case might assume that, for instance, the product’s connection to the Internet is working. But what if it’s not? The developer must implement some functionality to test for this error condition and handle it in an appropriate way.
In my experience, the process of having the analyst examine a use case in this fashion to derive pertinent functional requirements adds considerable value to the requirements development process. There’s always more functionality hinted at in the use case than is obvious from simply reading it. Someone needs to discern this latent functionality. I would prefer to have an analyst do it rather than a developer. If your developers are sufficiently skilled at requirements analysis they could carry out this task, but they might not view it as part of their responsibilities.
I recently spoke to a highly experienced developer who said it was much more helpful to receive requirements information organized in a structured way from the analyst than to have to figure out those details on his own. This developer preferred to rely on the analyst’s greater experience with understanding the problem domain and deriving the pertinent functional requirements. Not only did this result in better requirements, but it also allowed the developer to focus his talents and energy where he added the most value—in designing and coding the software.
My philosophy of employing use cases as a tool to help me discover functional requirements means that I don’t feel a need to force every bit of functionality into a use case. It also gives me the option of writing use cases at whatever level of detail is appropriate. I might write some use cases in considerable detail to elaborate all their alternative flows, exceptions, and special requirements. I could leave other use cases at a high level, containing just enough information for me to deduce the pertinent functional requirements on my own. That is, I view use cases as a means to an end. The functional requirements are that “end,” regardless of where you choose to store them or whether you even write them down at all.
Deriving functional requirements from the use case always takes place on the journey from ideas to executable software. The question is simply a matter of who you want to have doing that derivation and when. (See Chapter 13) If you deliver only use cases without all the functional detail to developers, each developer must derive the additional functionality on his own. A developer with little requirements analysis expertise might overlook some of these requirements. It’s also unlikely that all developers will record the functional requirements they identify. This makes it hard for testers to know exactly what they need to test. It also increases the chance that someone will inadvertently fail to implement certain necessary functionality. If you’re outsourcing construction of the software, you can’t expect the vendor’s developers to accurately determine the unstated functionality from a use case description.
You will almost always have additional functional requirements that do not fit nicely into a particular use case. Earlier in this chapter, I mentioned the example of logging in to a system. Clearly, that functionality must be implemented, but I don’t consider it to be a use case. You might also have functional requirements that span multiple use cases. Consider the behavior the system should exhibit if a required connection to the Internet goes down. The Internet connection could fail while the user is executing any use case. Therefore, this error condition doesn’t constitute an exception flow associated with a specific use case. It needs to be detected and handled in multiple operations. The analyst can place all the functional requirements that are not associated with or derived from a particular use case into the logically appropriate section of the SRS.
As an alternative to creating separate use case and SRS documents, you could select use cases as the organizing structure for the bulk of the functional requirements in the SRS. IEEE (Institute of Electrical and Electronics Engineers) Standard 830-1998, “IEEE Recommended Practice for Software Requirements Specifications,” provides guidance on how to create an SRS (IEEE 1998). According to this standard, you might organize the functional requirements by system mode, user class, objects, feature, stimulus (or external event), response, or functional hierarchy. You could also combine or nest these organizing schemes. You might have functional requirements grouped by stimulus within user class, for example. There is no universally optimal way to organize your functional requirements. Remember that the paramount objective is clear communication to all stakeholders who need to understand the requirements.
I have found use cases to be a highly valuable technique for exploring requirements on many types of projects. But I also value the structured software requirements specification as an effective place to adequately document the functional requirements. Keep in mind that these documents are simply containers for different types of requirements information. You can just as readily store use cases, functional requirements, and other types of requirements information in the database of a requirements management tool. Just don’t expect use cases to replace all your other strategies for discovering and documenting software requirements.