Requirements for an IT system come in several shapes and sizes. Even if you manage to get requirements that are clear, concise, comprehensive, unambiguous and agreed, they could still be badly behaved. I will try to warn you of some of the perils that requirements can cause.
Well-behaved requirements
Before I get onto badly behaved requirements, I’ll describe well-behaved requirements so you can see the contrast more clearly.
What do I mean by well-behaved requirements? I mean that they interact nicely with the system, leading to an easier life when you implement and test them.
The system probably has a hierarchical structure, like this:
A well-behaved requirement interacts with a tidy sub-section of this tree, like in the next picture. The purple bits are changed, and so the blue bit is the smallest part of the system that contains all of the changes.
This kind of change has a couple of nice qualities:
- There’s a lot of the system that it doesn’t touch, so the risk to it breaking is low.
- It’s easy to draw a box around all the work, so the risk of missing something when you’re developing or testing is low. Putting it another way: it’s easy to know when you’ve finished developing and when you’ve finished testing.
Usually the requirements that are well-behaved in this way are functional rather than non-functional, but next I will describe how even functional requirements can be unpleasant.
Cross-cutting requirements
A common way for requirements to not be well-behaved is for them to be cross-cutting requirements. Cross-cutting means that the work doesn’t just sit neatly in one place, but cuts sideways across much, if not all, of the system. Often cross-cutting requirements are non-functional, but functional requirements can be cross-cutting too.
Some examples of cross-cutting non-functional requirements are security, performance and availability. Some functional cross-cutting requirements are:
- All text needs to be localised into a range of foreign language;
- All money amounts need to be calculated with a configurable amount of rounding.
A simple rule of thumb is: Can you point to the one bit of the system that implements this requirement? For instance: Where is security?
Cross-cutting requirements often affect the system in a T-shaped way, if you imagine all the parts of the system laid out side-by-side, and you see how much each part is changed. The change is concentrated in one area (the vertical part of the T), but also affects many parts of the system in a smaller way (the horizontal part of the T).
For instance, for security there could a cryptography module (a vertical bit) and the many places where it is called and where e.g. SQL injection attacks are prevented by using a database framework rather than assembling SQL strings by hand (the horizontal bit). Just because there is a lump where there is a lot of effort concentrated in one place, there could still be a large amount of additional effort spread more thinly over the rest of the system.
The work to implement and test cross-cutting requirements is harder to define clearly. This makes it easier to miss bits altogether, and for estimates to be wrong. Cross-cutting requirements also affect more of the system than a well-behaved requirement with a similar estimate, because of the horizontal part of the T.
Requirements can gang up on you
Sometimes requirements can seem innocuous but get nasty when you combine them with other requirements. As an example, I will pick out a small subset of the requirements behind the UK domestic smart meter system for gas and electricity. First, here is some background.
There is a meter for gas and a meter for electricity in every home that is connected to either or both the national gas and electricity grids, which is roughly 25 million homes. The meters in a given home share a means of phoning home to a clearing house with meter readings – usually this is a connection to a private data network.
A given household can choose between many suppliers (retailers) of gas and electricity, and consumers are encouraged by the government to shop around frequently, so that competition drives down prices and encourages innovation and other improvements by the retailers.
Now for some specific requirements:
- The system must be secure.
- The system must be as cheap as possible, because the cost is passed on to the consumer.
- A meter must measure gas or electricity usage for each half hour in the day and send this data back to a central clearing house.
- When a home switches between retailers, the change must take effect at midnight.
- The tariff that describes how much a consumer will pay for gas or electricity must be downloaded to the meter, so that they can know how much the gas and electricity is costing as a money amount (and not just as e.g. some number of kilowatt hours).
Note that there is no requirement for very low latency and very high reliability for the meter readings getting to the clearing house, unlike with e.g. a high frequency trading system. While it’s desirable to have the readings at the clearing house soon and reliably so that they can be sent to the retailers and others, the data is always available in the home via the meter itself. High reliability and low latency drive up the cost, and so they are probably not worth it for the meter readings.
The system must be secure for several reasons, including fairness. Fairness to the consumer and to the retailer mean that the consumer should be charged for what they use, and neither more nor less. If the readings could be blocked or tampered with then fraud and other fairness problems could happen.
The requirements I’ve listed are only at a very high level, and there are many, many more, but even these few give rise to some interesting choices. There are a few options, but I’ll describe two.
Option 1: All change at midnight
When a consumer changes from retailer A to retailer B, the tariff for retailer B must get through at midnight on the dot. This means that the network must be reliable and low latency, more than it would need to be for meter readings. This will drive up the cost of the whole network, for an event (switching between retailers) that happens much less frequently than other events (sending meter readings back to the clearing house).
Option 2: Getting ready ahead of time
When a consumer changes from retailer A to retailer B, the tariff for retailer B will be sent to the meter ahead of time with an effective date/time of a midnight in the future. How far in advance this has to happen depends on how reliable the network is, but in many ways it doesn’t really matter – as soon as it’s any amount in advance you will get the same problem. The problem is that retailer B’s tariff needs to be sent securely to a meter while it is being serviced by retailer A. Two independent companies need secure access to the same meter, without being able to interfere with the other retailer’s data. As well as adding complexity and hence cost to the access to the network by retailers, it adds complexity and cost to every meter – remember there are meters in about 25 million homes, so cost per meter is very important.
Competing constraints
This is the crux of engineering. There are many constraints (requirements in this case) that apply at once in a given area. It is rare that you can satisfy every constraint in a perfect way, and instead some compromise needs to be struck. It’s like asking: What is the best car? It depends on which constraint or constraints matter most to you – acceleration, affordability, space for passengers, space for luggage, ease of fitting into parking spaces, etc.
In the well-behaved case, the requirements were likely to be narrow in scope i.e. the functional requirements that applied to only a small part of the system such as one class. The narrow scope for at least most of the requirements meant that the total number of requirements applying to a given bit of the system is small, making for a relatively simple job of satisfying them all.
When you have cross-cutting requirements, they each apply to many parts of the system at once. If you have several of these, each applying to much of the system, then that’s an easy way of having many parts of the system that each have many requirements that must be met at once. This is tricky!
Know your enemy
As well as coming in more than one size, requirements come in more than one shape – a neat sub-tree of your system or a T-shape. Knowing the shape will help you stay on top of nasty surprises. Even well-behaved requirements can turn nasty if they gang up with other requirements, so be careful if someone asks you to add “just this one little” requirement to the system. Its interactions with other requirements could make it surprisingly expensive.