Here is a fictitious story of a business client that orders software for a mobile phone to an embedded developer.
Take such a trivial feature of a mobile phone as a the possibility to store last calls. What do you know about it besides that it allows to view last incoming and outgoing calls? This is the level of knowledge that a typical client has and he will express it as a requirement the same way:
— I want that my mobile phone software to store last incoming and outgoing calls.
The developer has two choices: either assault the client with question based on unconfirmed assumptions or tacitly do exactly what was asked. He writes code that keeps two lists ordered by the call time, one list for incoming calls, another one — for outgoing calls.
Soon after, the client finds out that a competing company uses a different design and demands the software to display:
- all calls together with their destination (incoming or outgoing)
- missed calls
- received calls
- outgoing calls
- rejected calls
Unfortunately, the previous code has already been written and is linked tightly to the UI code and to the rest of the mobile phone's OS.
So, instead of scapping the previous design and starting from scratch, the developer is now bound to implement new data structures that consists of one new list that combines incoming and outgoing calls in the right order to satisfy the first additional requirement, and three more lists for each of the 2nd, 3rd and 5th additional requirements.
This seems like nothing, but coupled to the synchronization code and error handling code, the result is likely to look cryptic for everyone, including its own developer.
If the requirements were completely thought of from the start or if there was an opportunity to rewrite the code, the developer would have probably chosen one list with 5 attributes, avoiding synchronization code and cutting the error handling code half.