As a team lead, I often find myself creating specifications for requested features. They bring a lot of benefits to the process of building an application. However, writing good specifications is a skill that must be practiced and it isn’t one that is taught very often. Solid specifications are important and I’m going to share the properties I look for when writing specifications to fill that gap.
Fundamentally, specifications document the requirements and functionality of a system at a level removed from the actual implementation. This allows non-developers to read and understand the system requirements. This higher level also makes it easier for developers working on the system, and even related systems, to be sure that their changes don’t cause regressions. A specification also enables a dialogue between the stakeholders in the specification and the specification writer to iterate on the specification without committing to the full process of implementation.
A specification has four major components. While they are distinct in the final artifact I often find it natural to bounce between components when writing a specification.
The first component of a specification is definition. This is a place to introduce new jargon and clarify or refresh the reader on old jargon that is used in the specification.
The second component of a specification is state. These are things like shopping carts, forms, or other domain models that are relevant to the functionality. These should include as much detail as necessary to make sense in the context of the specification, but should refrain from including irrelevant implementation details. For example, the choice of a linked list or an array for a backing data structure in a shopping cart isn’t relevant to the specification of how the shopping cart works, because the choice of one over the other is based on efficiency concerns. If, however, the specification had provisions for certain performance characteristics then that choice would be important to clarify in the specification. It is also very important to specify the default or empty values for any models and the range of possible values for the models.
The third component of a specification is transitions, or how the values of the domain model change. This component should name and specify when and how the state changes. As an example, think of adding a product to a shopping cart. Is it added before all the other products in the cart or after them? That is important information to include. However, like the state component, this component should not include irrelevant detail.
The final component of a specification is invariants. These are constraints that hold for all possible states of the system. An example invariant on the domain model is that a shopping cart always has less than 10 items. Invariants can also be specified on transitions, e.g. if a user reorders their shopping cart the total items in the cart never change.
The importance of invariants cannot be overstated.
Invariants allow developers to reason about a system and predict its behavior. For an alternative view on invariants, think of traditional automated tests. Each test imposes an invariant on the system and warns the developer if that invariant is broken. In the same manner, the invariants in a specification are prime candidates for automated testing.
I’ve covered the major components of a specification, now I’ll cover the three major properties of a good specification.
The first property is that of consistency. A specification must be consistent both internally and with any other systems with which it coexists. A consistent specification allows the formulation of the invariants that were discussed above. If any of the invariants can be proven false within the context of the specification then it is inconsistent.
The second property of a good specification is completeness. Trivially this means that all relevant information is included in the specification. It also means that all edge cases and potential weak points are covered. Systems that have ambiguous or undefined behavior within their scope are incomplete, and specifications that result in those systems are also incomplete.
The last property of a good specification is precision. The language used in a specification must be unambiguous and not open to interpretation. It must mean the same thing to every reader. If it does not then it risks being misinterpreted by the stakeholders or the implementators causing wasted effort and broken promises.
One aspect that I’ve left out of the above discussions is that of correctness; does the specification specify what the stakeholders intended?
This question cannot be answered by the specification itself. A discussion with the stakeholders is required. In this discussion, the specification writers must explain the functionality of their system in a manner that the stakeholders can understand. Both parties need to agree on the features and tradeoffs that are being made. I’ve found it is best if this discussion happens continuously as the specification is being written, to avoid too many rewrites. Eventually, the specification will converge on the functionality the stakeholders are looking to implement.
In this post, I’ve given an overview of the qualities of a good specification. Good specifications include definitions, domain models, how the models change, and invariants of the models and transitions. They are also consistent, complete, and precise. When a specification adheres to these principles it becomes a tool useful for all members of a team that aids maintenance of old features and implementation of new ones.
Finally, the process of writing a specification must involve discussion with stakeholders to ensure the right functionality is being created. This ensures the correct system is created.