Requirements and Values in Software System Design
Software system design is a messy endeavor. Every design choice comes with a trade off, and a choices in one area invariably limit your possibilities in another. When we take on a design project, we wade into this vast sea of possibilities. This is always daunting: how do we limit the design space? How do we guide our design? Where do we even start?
Every system has a set of requirements: properties that the design must obtain. Requirements are the way we determine what the system must do (and sometimes what it must not do). Requirements set the stage. Well-formed requirements should be easy to describe; and it should be easy to know if the system meets its design requirements. Without knowing a system's requirements, we are in the wilderness.
However, requirements alone are not enough. Even within the boundaries of a given set of requirements, the parameters of a design are vast. While good requirements sharpen a design's focus, they far from determine its outcome.
This is where the blood, sweat, and tears come in. It's where we begin to grasp the contours of the problem. It's the space within which we prototype, debate, formulate hypotheses, explore, think. It's where human creativity and ingenuity plays its most important role.
And crucially, this is where values enter the picture. Our values are our lighthouses. They help to guide our designs — sometimes from afar, but sometimes much more directly. Our values are sometimes ineffable, and sometimes they oversimplify. Sometimes they are nuanced, occasionally they are pithy.
Everyone ends up with slightly different values, informed by their own experience, foibles, oncall rotations, aesthetic appreciation, and academic backgrounds. While no-one's values are alike, there are persistent schools of thought. Such schools sometimes arise from long-term projects and efforts that carry strong technical ideas forward. Sometimes schools of thought are localized in space or time. The Unix room in Bell Labs represents one such school of thought. So does McCarthy's AI lab.
In modern times, values have become decidedly corporate. Every engineering organization develops its own set of values, born out of experience and culture. The values applied to engineering work at Amazon differ from those at Google, or Facebook. These values are often influenced by business needs. For example, a medical device company will put a large premium on correctness, while a consumer oriented company might sacrifice correctness for customer satisfaction.
Driven by requirements, guided by values
The line between requirements and values is often blurry. What might be a requirement in one design might be one of the values that drive another. This confusion can often lead to a kind of fog during design discussions. If requirements are clear, it should also be clear whether a particular design meets those requirements. If they are not, then ones values will often be mistaken for requirements.
Disagreements and tensions in design discussions are natural and expected. They are a healthy part of guiding our arrival at a solution. However, it's also useful to communicate clearly about what these arguments are *about*, to have a discussion at the right level. Are we arguing about a design's requirements (inviolable as they are) or is there a differences in how we apply values to drive the design to begin with?
Some common criteria for software systems design
To make this more concrete, let's talk about some of the design criteria that drive system design — either as values or requirements. It's useful to have the language to talk about these as we endeavor to build systems.
The list is somewhat chaotic — it is not complete, nor is it orthogonal; but I do think it represents a collection of common criteria and trade-offs that are made in system design.
Simplicity is the holy grail of system design (who can argue for complexity?). To say that good design is simple is almost tautological. However, it's also the criteria most readily sacrificed by requirements or values.
Consistency is the idea that a system's behavior is regular and predicable. Consistent systems provide strong guarantees that make it simpler (sometimes much simpler) to build other systems on top of.
Completeness is the degree to which the system provides the functionality ventured. Often simplicity is in conflict with completeness. For example, we might choose to simply fail if our system reaches certain edge cases, to keep the design simple and to avoid having to maintain rarely-exercised exceptional code paths.
Correctness is the degree to which the system functions correctly. As with completeness, we may in some cases sacrifice correctness for simplicity.
Speed determines how quickly the system gets its job done. We'll often sacrifice simplicity and efficiency to improve the system's speed.
Efficiency is characterized by how parsimonious a system is with computational resources. Often we trade efficiency off for simplicity and completeness.
Scalability is the property that a system can handle larger workloads by adding more resources. Ideally such systems scale linearly; and almost always scalability applies only up to a limit. We often sacrifice consistency and simplicity for scalability.
Portability is the degree to which a design can be employed on multiple configurations. For example, a portable system might work on spinning hard disk drives as well as flash storage; or it may be easily ported to a different computer architecture.
Extensibility determines how easily a user may add new functionality not anticipated by the system's original designers.
Elegance is somewhat ineffable. You know something is elegant when you see it. Usually it means that a solution was solved in a parsimonious way. Elegant systems are also often simple.
Generality is the property that a system may be employed widely, across many different uses, often with very different requirements.
Reliability is a big topic. It's the property that a system provides dependable service. A highly reliable system will continue to provide service even under the most trying circumstances.
Approachability is the property that a system caters to new users. Approachable systems confer a kind of instant familiarity to newcomers.
Orthogonality means that a system does not contain overlapping features. Instead, individual features are designed so that they are independent from other features, but instead may be combined with good effect.
A strategy
So what can we do with this? How can we escape the miasma of requirements, values, trade-offs? First, it helps to have the language to talk about these various criteria. Second, it helps to be clear about what a system's requirements are, and which are the values driving its design.
One approach I think works well is this: write down the requirements, then write down how the design will be guided by values.
Requirements should be as complete as possible, and, importantly, it should be very easy to tell if a system meets a set of requirements or not.
Once the requirements are sorted out, write down your desiderata (Latin: "things desired"). Your desiderata document how you intend to use your values to guide the design. They lay your values bare, and help you to have more cogent discussions about the particulars of some design decisions.
A personal philosophy
One of the all-time best slides of computer science is this one from Simon Peyton Jones:
It describes (in his view) the trade-offs made in Haskell's type system. In particular, he uses a "power/weight ratio" framework for illustrating the design point of Haskell's type system. I think this is a really good way to look at it: you want to match the system's power with the weight (complexity) it introduces. The best systems have a really high power/weight ratio.
Elsewhere
No essay about values and trade-offs in design can be complete without mentioning Butler Lampson's classic paper, Hints for Computer System Design. It's a classic that has stood up well to the test of time, and I encourage everyone to read it carefully.
Also, I really enjoyed The School of Niklaus Wirth. This book is apropos for a couple of reasons: first, it documents the truly powerful and influential value system of Niklaus Wirth (of Pascal, Oberon fame). Second, Wirth's designs are famous for their unrelenting pursuit of simplicity, and for attaining mind-blowing power/weight ratio. Well worth the read.