Quality attributes

Your software can only be successful if it meets the needs of both the customer and the business. To do this you need to identify what is important to the customer and to the business.

Quality Attributes, also known as Architecture Characteristics, or -ilities, summarise what is important to the success of the system you are building, e.g. scalability, availability, or responsiveness. Identifying quality attributes is one step toward software that does what users and the business need it to do.

But where do these quality attributes come from, and how do you use them to create successful and high quality software?

The source

When you are designing your system, such as defining requirements, scenarios, and business processes, you will be identifying what the system needs to functionally do. Quality attributes are also typically created as a design activity.

Information from your context (anything outside the system you are producing, e.g. customers, domain experts, business strategy, legislation, other systems, etc) is used to inform decisions on what is important for your system.

You will also discover what some call non-functional requirements. Despite the term non, non-functional requirements are just another type of requirement, and they define how the system needs to perform.

Functional and non-functional requirements are part of a specification, and that specification is fluid. As long as all requirements are taken into account in design, architecture, and engineering, it does not matter which type they are.

Quality attributes can be sourced from both functional and non-functional requirements. You can start to see a chain here. Something in the context, e.g. an integration or legislation, leads to a requirement, e.g. “the system must not crash if the external system is down” or “financial information must not change once it enters the system”, leads to identifying fault tolerance or data integrity as quality attributes.

An external green box labelled ‘context’ contains a small grey box labelled ‘external system’ and a yellow box labelled ‘design’. The design box contains the text ‘must integrate’ with an arrow pointing to the external system box. An arrow also leads from ‘must integrate’ to the text ‘must not crash if external system unavailable’. A further arrow leads from the ‘must not crash…’ text to a white oval with the label ‘fault tolerance’.
The chain from the context, through requirements, to a quality attribute

It is important to link to the sources of your quality attributes to show how they came to be. This is a form of validation for your defined quality attributes and inspires trust in anyone viewing the design. Linking could be an actual hyperlink (depending on your documentation system) or it might be in the form of a table or text reference (e.g. in a table of your quality attributes you have a column for their sources).

Refinement

Great, you have a list of quality attributes as long as your arm, or longer. What now?

If you showed your list of quality attributes to your stakeholders, they likely would say all the attributes on the list are needed and all have priority. But if everything is prioritised then nothing is prioritised. Despite this tendency, you will need your stakeholders to help you define and decide upon quality attributes.

A screenshot of the non-exhaustive list of quality attributes from Wikipedia showing the text: Notable quality attributes include: accessibility, accountability, accuracy, adaptability, administrability, affordability, agility (see Common subsets below), auditability, autonomy, availability, compatibility, composability, confidentiality, configurability, correctness, extensibility, failure transparency, fault-tolerance, fidelity, flexibility, inspectability, installability, integrity, interchangeability, interoperability, learnability, localizability, maintainability, manageability, mobility, reliability, repeatability, reproducibility, resilience, responsiveness, reusability, robustness, safety, scalability, seamlessness, self-sustainability, serviceability (a.k.a. supportability), securability (see Common subsets below), simplicity, stability. Credibility, modifiability and standards compliance are half cut off at the end to show the list is not complete.
A partial list of quality attributes from Wikipedia ( CC BY-SA 4.0 )

You may have heard that you should have no more than 7 quality attributes for a complex system, and fewer for simpler systems. That is a nice rule of thumb, but should be applied while also knowing the nuances of the relationships between quality attributes.

Some quality attributes are related, and some can even be mutually exclusive. Some are high-level, and some are low-level. Some are even functional as opposed to non-functional in their behaviour.

Some attributes are sub-attributes of others:

  • Maintainability can be considered a parent of deployability and testability.
  • Agility could be considered a parent of many other attributes, including maintainability, deployability, and testability.

For attributes that are related you should:

  • Think about their definitions: Are you using similar words to mean the same thing? You may be able to combine or remove some of your attributes.
  • Consider whether you really mean a sub-attribute: e.g. is deployability what is important, rather than maintainability?
  • Break down attributes into sub attributes and define exactly what you mean by each: Naming and defining what is really important will help to avoid over-engineering and under-engineering.

Confounding attributes

Some attributes are regularly misunderstood or confused:

  • Performance and responsiveness: Performance is the amount of time it takes the system to process a request. Responsiveness is the amount of time it takes to get a suitable response to the user (even if the request is still pending).
  • Confidentiality and security: Confidentiality is limiting access and awareness of information and behaviour within a context. Security has a broader remit, which can include confidentiality, but can have additional attributes outside of that, such as availability, access control, lifecycle, integrity, etc.
  • Scalability and elasticity: Scalability is the ability for your system to keep performance/responsiveness the same as the number of requests or users increases. Elasticity is the ability for the system to respond quickly to extreme load changes, e.g. the number of requests suddenly jumps up from 1,000 to 100,000 or vice versa, jumps down.

Ensure you understand and create specific definitions for all your attributes along with your stakeholders. You all need to share the same understanding or the system engineered will not match the needs and expectations of your stakeholders.

Mutually-exclusive attributes

Some attributes are mutually-exclusive:

  • Performance and security are like oil and water. You cannot have a high-performing system that is also highly secure. The checks, encryption, etc, required to keep things secure, lower the performance.
  • High-availability, transactionality, and CAP theorem. This particularly affects distributed systems. Even a sharded, distributed application using partitioned data is constrained by CAP theorem (consistency, availability, and partition tolerance).

For attributes that are mutually exclusive, you can:

  • Assess which parts of your system they apply against. If the opposing attributes apply to different parts of the system you are less likely to have problems applying them. If they overlap, you will need to balance them.
  • Decide how to get a balance between the opposing attributes. Which one should be prioritised when using the attributes to make decisions? Does the priority or balance change depending on which part of the system you are looking at?

Specificity: creating system attributes

So far this article has talked about quality attributes, which are usually a single word and a definition. So far, in refinement, you have added information about where the attribute applies in your system, but you should also make the attribute statement more specific for added benefit. By doing this you create system attributes.

Instead of confidentiality applying to the whole system, you can change this to confidentiality when transferring or more specifically confidentiality when transferring data. This exercise may also inspire further discussion which creates further understanding. Maybe, after further discussion, you refine this one statement to confidentiality of data in transit and confidentiality of data at rest. Even if confidentiality has a definition, at least one of these specifics may have been missed in implementation.

This is still only one attribute, confidentiality, when thinking about limiting your quality attributes. You might end up going further, either at your first refinement or in the future, and specifying which specific data (e.g. HIPAA, PII, etc) the attribute applies to. This is great as you are having conversations and increasing understanding. This understanding then avoids or reduces over- or under-engineering.

System attributes are much easier to verify in your implementation than quality attributes. How would you check that confidentiality had been implemented? Whereas confidentiality of data in transit and confidentiality of data at rest are defined enough to be fairly easily assessed.

Refined, not just defined

The important thing here is that you aren’t just defining a list and moving on. You are thinking about what all these attributes mean, along with their sources (requirements, constraints, stakeholders, legislation, etc.) to really dig down into what is important for the system. You don’t just want a list of 3-7 words. That isn’t useful.

Your quality attributes, their definitions, and your quantitative requirements (e.g. the system must support between 1 and 20,000 users), along with qualitative requirements all support the design, architecture, and engineering of high quality software.

Here’s an example of a refinement from a quality attribute to a system attribute:

  • Security was further refined down to confidentiality, applying to the whole system
  • Confidentiality was defined as personal data in the whole system must be private
  • Confidentiality was further refined to:
    • first confidentiality when transferring
    • then confidentiality when transferring data
    • then confidentiality of data in transit and confidentiality of data at rest
    • then finally (at least for now) confidentiality of PII data in transit and confidentiality of PII data at rest

Confidentiality of PII data in transit and confidentiality of PII data at rest are much more useful when implementing a design in architecture and engineering, than security or even confidentiality with its definition.

But haven’t we just gone back to defining requirements? What are quality attributes if not success requirements (or criteria) of the system? Both quality attributes and requirements need this kind of scrutiny in order to understand what is really needed for the user and the business.

How to use your quality and system attributes for successful software

Design activities are often the first things to be skipped or reduced due to time and cost. Making time for activities such as defining scenarios and quality attributes, links the intentions and needs in your context (customers, the business, etc) through to your architecture and engineering. If your engineering doesn’t match the intentions from your context your software is not successful or high quality.

How can quality and system attributes be used to improve software quality?

Driving decisions

When making a decision some or all of your attributes can be used to improve the decision made, guiding the decision towards what is important to the success of the system.

Along with constraints and other applicable requirements, your attributes should be used to assess all the options when making a decision. Where the decision is complex or will have an effect outside a small team, an ADR (Architecture Decision Record) or other form of decision record can be an effective way to help make and record the decision.

Some of your requirements will come from your organisation, as well as your product team. Organisational policy and strategy, as well as outside influences like legislation and standards, all feed into your system design.

Guiding system decomposition

Along with aspects such as code volatility and data dependencies, both quality and system attributes are useful in modularising your system.

I mentioned above that some attributes will apply to the whole system and others apply to part of the system being designed. Some attributes may even be inherited from the system’s context. Attributes can be used to help define boundaries within your system, which may be implemented as different services or modules.

The ACED model uses frames to help you understand the scope of attributes across software and organisational boundaries.

If scalability is only applicable to one part of your system, then that part might be best split into a separately deployable component (or components) which can then be scaled either horizontally or vertically. There will always be trade-offs, of course, and so this decision will vary with every context.

For system decomposition the most efficient way to apply your attributes is to apply quality attributes first, and then to apply system attributes to validate and refine your selected boundaries.

A green box labelled ‘context’ contains a white box labelled ‘system’ and the text ‘attribute applicable in wider context’ with arrows indicating the whole context box. The white system box contains the text ‘attribute applicable across system’, with arrows indicating the whole system. It also contains three blue boxes each labelled ‘sub-system’. One blue sub-system box contains the text ‘attribute applicable across sub-system’ with arrows indicating the whole sub-system. The other two subsystem boxes have text above them (and within the system box) ‘attribute applicable across sub-systems’ with arrows indicating the two subsystems.
Attributes can have different scopes which can be used to help define the decomposed structure of a system

Validating system design and verifying implementation

Once you have a design how do you know if it will produce a successful and high quality system? Even before implementation has begun (or finished) you can validate your design against your system attributes.

The functionality of your system is most important to its success. Your attributes are the next most important aspects. Validate your design and verify your implementation against your system attributes, along with your requirements.

System attributes and requirements can also be used to define tests for the implementation of your design. There are many forms of tests, and yours could include architecture fitness functions, performance testing, or tests to check data integrity or confidentiality.

Attributes and requirements that are quantitative, or easy to state as true or false, will be much easier to validate than qualitative attributes and requirements. Hence, why system attributes are needed for this validation and verification because quality attributes, by their nature, are qualitative.

What’s next?

Defining and using quality and system attributes is just one way to improve the quality of software. The ACED model helps you to take a step back and consider how you are taking the needs and intentions of a system through from the context all the way to the engineered implementation.

You can find out more about the ACED model at acedmodel.com and also its origins in Software Design: What went wrong? and Better Software Design: Changing how we talk about software.

To be the first to hear about new posts in the knowledge vault and blog, sign up to our Software Design newsletter below 👇🏻