Software Architecture as a Web of Interconnected NFRs
Software architecture encompasses a broad set of system characteristics, often referred to as the ilities — such as scalability, reliability, portability, flexibility, testability, observability, and others. These qualities are typically non-functional requirements, also known as architecturally significant requirements, because they fundamentally shape a system’s structure and behavior. As Martin Fowler wryly put it: "I define architecture as a word we use when we want to talk about design but want to puff it up to make it sound important." While there may be countless potential attributes, only a small number are critical to the success of any particular system.
Architecture focuses not on what a system does (its functionality, like processing payment transactions), but rather on how it performs under certain conditions — whether it's resilient, secure, scalable, extensible, standards-compliant, auditable, or maintainable. These aspects matter regardless of the system’s domain.
Architects are primarily concerned with these non-functional aspects, whereas developers usually focus on delivering the functional requirements. A strong architect is an engineer who crafts solutions that align with a clear, prioritized understanding of a few critical qualities (typically no more than five or six). These priorities should be established early and remain consistent, since many quality attributes conflict — e.g., modifiability may come at the expense of stability. In situations where qualities like performance and security are both important, the architect must clarify which takes precedence.
Every architectural decision comes with trade-offs. For instance, increasing a system's openness—making it easier to integrate with other systems and support various protocols—can reduce security, as openness and security often work against each other. However, openness tends to increase heterogeneity, allowing support for diverse technologies, which can be beneficial in many contexts.
Similarly, a system that is highly scalable—able to seamlessly handle a jump from 5 users to 5 million and back (real scalability goes both ways) — can become harder to observe. The more scalable a system is, the harder it may be to monitor or understand its internal state. On the flip side, systems that are highly observable are usually easier to test, meaning high scalability can indirectly reduce testability.
Other trade-offs include transparency, which may enhance deploy-ability but reduce observability, or security, which can increase business value but potentially limit autonomy. Systems that emphasize autonomy — being able to operate even with component failures — tend to be more resilient, though possibly at the cost of consistency or increased vulnerability to security attacks at isolated components.
Ultimately, all architecturally significant qualities are interrelated — either directly or inversely. Boosting one often impacts others, positively or negatively.
The core idea is that no system can be optimized for all quality attributes simultaneously. Software architects must make conscious, business-driven, and clearly communicated design trade-offs.
A robust architecture is one that resists frequent change. It defines the foundational aspects of the system that should remain stable over time. A reliable indicator of strong architecture is how infrequently it requires modification after deployment — especially in light of the selected ilities against which the design was optimized for.
The best practice is to identify a small set — ideally no more than five — of key quality attributes, then agree with stakeholders on their relative importance. For instance, if both security and openness are important, the predefined ranking will dictate which takes precedence during design. A priority list like “security, consistency, observability, resiliency, openness” offers clarity and direction. Once established, these priorities should remain stable to ensure the system is well-aligned with its goals, which ultimately leads to more efficient implementation and smoother operation.