In "The Problem with User Stories", I tried to show the importance of distinguishing the problem domain from the solution domain in the design of software systems. This essay illustrates this point further and focuses on the problem of decoupling requirements.
The problem of functional coupling is hard to discuss in the abstract so I start with an example from a recent project and generalize in a second part.
The overall function of the software application I want to talk about is toby providing them relevant content and services.
In this application, we needed a way to denote a product to the user in a textual form like "Your Apple Computer" or "Your Samsung Refrigerator". The solution adopted was to assign a category to a product and build the name from this attribute.
Later, we added a new requirement to attach content to all the products of a given category: PDF manuals, how-to videos, parts and accessories... So we naturally implemented this requirement by creating a new relationship between a piece of content and a category as shown in the model below.
This design seemed acceptable at first. We usually don't want to multiply concepts unnecessarily. However, in doing so, we coupled the two requirements by reusing the concept of category.
We quickly realized that we wanted more specific categories in order to attach more relevant content to the products. There was no real design discussions about this problem and some people in the organization decided to simply create more specific categories like "Refrigerator (French Door)" and "Refrigerator (Side-by-side)". However, because the two requirements were coupled, this change impacted how we were denoting the products to the users. "Your Samsung Refrigerator (French Door)" is an awkward name.
Of course, bugs were reported, meetings were held, trade-offs were discussed. This coupling of the requirements resulted in lower quality of the software and increased costs for the organization. Was is it avoidable?
I believe it was. The first mistake was to use the concept of "Category" to express the new requirement regarding the attachment of content to products. This concept belongs to the solution domain, not the problem domain. We introduced the concept of category for a very specific purpose, being able to denote a product to its owner. Therefore, from the beginning our two requirements were not independent. So the first thing we should have done is make those two requirements independent.
We would then have been able to discuss what kind of categories we want to support to attach content to products, what level of granularity we need... Then we would have considered several design options for this requirement.
For example, we could have designed a new content-specific category scheme in parallel of the existing one. This would have decoupled the requirements for sure but it would also have added complexity to manage several categorization schemes in parallel.
Or we could have extended the concept of category to build a hierarchy of categories. A product would have inherited the content from its category and all its parent. The name of the product would be attached to the top-level category. In this case, the requirements would be decoupled because a product can effectively be attached to multiple categories: the category it is linked to and all its ancestors. So we now have multiple relationships between product and category, not just one.
Or we could have attached the content to a new concept of "Feature" and associated one or more features to the products if we wanted to inherit their content. This would have decoupled the requirements and allowed us more flexibility than a single category scheme.
Or we could have thought about other scenarios where we would want to attach content to a group of products and find an abstraction for all those cases.
What solution we went for is not relevant here and depends on other parameters that I don't want to get into. However, the important point is that all the above design options effectively keep the two requirements independent. If any of the requirement changes, we can tweak the design to respond to the change without affecting other requirements.
I hope this example illustrates the importance of spending time analyzing requirements. In particular, we need to make sure that requirements are expressed in the problem domain and independent from each other.
Moreover, we also need to keep in mind to not introduce coupling when designing a solution. One way to create such coupling is to reuse a concept for different requirements.
This coupling creates unnecessary trade-offs in the design because we cannot tweak a requirement without impacting the other one.