This is the first in a series of blog posts covering mental models for software development. These are representations of various concerns of the software development process, which I have found to be helpful in thinking about the work I do.
This first post describes the model I use for thinking about the different attributes of a software system, both its features and its qualities.
When I think about this, I have an image in my head that looks something like this:
Let me explain.
When we’re developing a software system, we’re concerned about two aspects of it:
- The correctness of the system.
- The level of quality to which it is made.
I’ll deal with each of these in turn.
In this mental model, correctness is a measure of whether a system is fit for purpose. A system is said to be correct if it meets both the functional and non-functional requirements of the organization that owns it and the people that use it.
- Functional requirements (FRs) define the operations of the system, ie what it does.
- Non-functional requirements (NFRs) define the constraints within which the system must operate, ie how well it works.
For example, a functional requirement may be that a system allows users to search for a product by its stock keeping unit (SKU) code, and a non-functional requirement may be that it returns search results in less than one second while supporting up to 1000 concurrent searches.
The combination of operations (defined as FRs) and their constraints (defined as NFRs) make up the features of the software system.
Features – everything above the line – are of concern to the organization that owns the software system and/or the people who use it. Features are defined in a system’s requirements specification, preferably in the form of measurable acceptance criteria.
A software solution either meets its acceptance criteria or it doesn’t. It is either correct or incorrect.
In this mental model, quality refers to the internal design of the system. How well a software solution is made determines how easily it can be changed, debugged, tested, deployed, and so on.
While the features of a software system are important to the business people, its internal quality attributes – everything below the line – are primarily of concern to the people who develop, maintain and operate it, the technical people.
Quality is a much more subjective matter. It is not a binary property, like correctness. A software system can be made to a higher or lower quality, but it is never possible to say whether the design is "correct" or "incorrect".
Different principles of good software design may need to be traded off against one another. And anyway, what is "good" design is a matter of opinion. Computer programmers on the same team will often have differing opinions on what the optimal design for their solution should be.
For these reasons, the quality attributes of software are not easily measurable. Metrics such as cyclometric complexity can alert us to code smells, but those are not the same things as anti-patterns. Code smells should always be evaluated via more qualitative assessments of the code.
There are other ways to think about the various attributes of a software system.
The concept of "features" is understood in different ways. Commonly, features are understood to mean the functions of the system, what it does, distinct from NFRs for performance, security, reliability, and so on.
But I prefer to think of features as being the fusion of all the externally-facing attributes, anything that is meaningful to a system’s owner and users. Those stakeholders care about what the system does, but they also care about how well it does it – how reliable and available it is, how secure their data is in the system, and how easy it is to use.
People care about features.
The concept of "quality" is even more nebulous. It is not unusual for people to think of quality as a single attribute, a binary property determined by whether the software works or not. In practice, the quality of a software system is determined by many factors, and different stakeholders will have different perspectives on which factors are important.
Personally, I have found it useful to think about software quality as a fairly narrowly-scoped subset of attributes that are primarily of concern to the developers and operators of the system, separate from the things the owner and users care about. These internal qualities are formed from the way the code is written and organized, the way data is structured and distributed, and how the system is deployed and operated in production.
Supporting artifacts such as technical documentation and automated test scripts also contribute to the ease with which a software solution can be developed and operated over the long term. These materials contribute to the quality of the solution, even if they are not part of the software’s code and configuration.
Quality attributes versus NFRs
In my mind, non-functional requirements are distinct from quality attributes. But some software attributes span both categories.
Consider security. A system may have an NFR that sensitive data be encrypted in transit. The categories of data deemed to be sensitive would be agreed with the software owner, normally. But it should not be necessary to have NFRs covering the validation and sanitizing of user input. It’s just good coding standards to protect software services from SQL injection and other common attack vectors.
The boundary between internally-facing quality attributes and externally-facing operational constraints can be a little fuzzy. One way to think about the partition is between runtime qualities and static qualities. Qualities that are observable at runtime, like performance and accessibility, should be treated as operational constraints and specified as NFRs. Qualities that are embedded in the static structure of the code and configuration of the system, such as testability and scalability, should be the sole responsibility of the technical people.
An alternative guiding principle is that if a design choice is relevant to the owner or users of a system, it should be included in the system’s specification. For example, I would specify any solutions implemented to mitigate the risks of denial of service attacks, since such solutions will show up in the system’s behavior. Rate limiting, for example, has an impact on users.
Objectives such as operability and supportability are somewhat fuzzy, but since they are relevant to both the owner and users of a system, they should be specified, even if only in vague terms.
If in doubt, specify quality requirements, even if the acceptance criteria are somewhat generic!
Benefits of this model
In my opinion, the best thing about this mental model is that the division of responsibilities between the business people and the technical people is clear:
- The feature attributes above the line are the concern of the business people.
- The design attributes below the line are the responsibility of the technical people.
This mental map shows the design attributes as providing the foundation on which features are built. If a system is designed to a high quality, it will be relatively easy to change the system’s features, compared to systems designed to a lower quality.
For example, it will be easier to implement security protocols in a system designed with a clear layering of concerns, such that UI code is decoupled from business logic, and business logic is decoupled from data access, and so on. Different security protocols can be easily implemented in the relevant layers of the system. Ditto for performance optimizations.
And, of course, it is always easier to extend or make changes to specific areas of functionality if a system’s design makes rational use of modularity and encapsulation.
Choices made in the design of a system will have an impact on the features it can support. For example, non-functional requirements may be traded off against one another. A system’s design may be optimized for security and reliability, but some design decisions made within those constraints may have come at the expense of some measures of performance.
So, ultimately, the internal quality attributes of a software system do have a direct impact on its external features, especially over the long term.
It is right that technical people have the autonomy to make design choices on behalf of the software owner, as only they have the expertise to do so. In the end, though, the software owner will also care that the invisible internal qualities of the system are good. Success of the software solution will depend on it.