[placeholder]

Six Attributes of Beautiful Systems

At Squarespace, one of our most important values is:

Design is not a luxury.

We strive to bring excellence in design not only to the tools we create for our customers but also to the code and systems that power them.

On the surface, however, there is a bit of a conundrum: aesthetics and technology are not obviously related. The idea of beauty is not traditionally within the scope of software engineering, which is typically concerned with algorithms, measurable elements such as time and space, performance, and abstractions. Yet, at the same time, engineers can be caught saying:

“That is a beautiful solution.”

The essential difficulty is this: Good design is plainly recognizable in objects that you can actually perceive with your senses, such as art, cars, clothes, furniture, food, or buildings. A creator can advance her craft because there already exists a rich language for describing their various qualities. A painting may be described in terms such as line, tone, texture, movement, scale, or tone. Even beer has a rich lexicon: full-bodied, malty, yeasty, hoppy, etc.

It’s unclear, however, how software engineers are supposed to recognize and judge similar qualities in their work. What does it mean for an abstract thing like software to be beautiful? We know it when we see it because such software is powerful, expressive, and simple—but how does one create it? Is it possible to isolate and articulate? If so, how would one even go about talking about such a thing? Given that we care so much about aesthetics at Squarespace, how do we marry it with code?

What Is a Beautiful System?

What I’ve learned at Squarespace is that technical aesthetics can be described precisely and that developing that vocabulary to use in conversations with colleagues about the merits of technical designs can lead to more beautiful systems, which carry significant practical benefits. Beautiful systems are:

  • Easier to use, to operate, to maintain, and to scale
  • Longer-lasting and are more extensible far into the future
  • Powerful, capturing more use cases with less complexity
  • Adaptable to new requirements
  • Simpler to test

Fred Brooks, author of The Mythical Man-Month, also wrote a great book called the Design of Design. One chapter touches upon what he calls “technical aesthetics” and lays out ideas for how one could identify a logically beautiful system. In this post, I’d like to show you six attributes of beautiful systems, so you can identify and judge them in your own designs. You will come away armed with a precise vocabulary for describing the aesthetics of software, able to share them with your peers and to help improve your designs.

Parsimony

a.k.a. “Minimalism” or “Sparseness” or “Lean and Mean”

Parsimony is the ability to accomplish a great deal with few elements. This results in less conceptual clutter and fewer ways to do the same thing. Parsimonious systems are relatively more usable and learnable simply because there are fewer things to learn, fewer decisions to make, and fewer items to sift through to reach the things you need. They are also inherently safer, leaving fewer ways for you or other engineers to make mistakes in the future: fewer operations means fewer unsafe operations.

A Parsimonious design will also pay dividends in the long run in the form of fewer moving parts to monitor, refactor, maintain, test, and to teach to other engineers who join the team. In general, being aggressive in removing the extraneous is like scraping the barnacles off of a boat. It is probably the single biggest factor in determining how fast you can go. Remember: “Deleted Code Is Debugged Code.”

The Test: Ask Yourself...

For any construct in your system, ask yourself: is this actually just the combination of two other things? Is it redundant with an equivalent thing? If so, remove it!

Good

  • CMYK

Bad

  • Lots of different screws
  • PHP array functions

A Word of Caution

Parsimony, like many of the other attributes in this post, is positive only up to a point, so beware of following this to the extreme. Excessive parsimony leads to the use of non-obvious idioms.

Structural Clarity

To have Structural Clarity, there must be a direct path from what you want to say to how you say it. This principle is largely focused on representation and reducing indirection.

  • Would you rather I show you your bank statement in Roman numerals ($MMCIXD), tally marks (|||), or just arabic numerals? ($1,200).
  • Do most people prefer to type obscure commands into a command line interface just to launch a word processor, or simply tap an icon with a finger?
  • Do you prefer coding in assembly or in C?

The concepts of your system have a structure to hang on and it must be plainly evident. A common way to achieve this is with metaphor. Familiar metaphors can greatly aid in this (e.g., the "Desktop" of Macintosh, the "spreadsheet" of VisiCalc, the "blocks" of Squarespace).

Which of these is clearer?

Map of Europe’s customs/economic/monetary structure. There is no clear structure here.

Map of Europe’s customs/economic/monetary structure. There is no clear structure here.

Consistency

Given familiarity with just part of a system, you can predict the rest of it. Note that Consistency does not mean that everything is the same. (I’d call that “uniformity”, where if several elements were the same, then they’d be redundant and you’d follow Parsimony to eliminate them!)

Rather, Consistency requires there exists an underlying principle uniting all the elements and ensures some form of compatibility. The principle should be simple to state.

For instance, you'd expect that all functions in a package try to maximize reuse and throw similarly formatted exceptions, use similar error codes, and take similar input types.

Example Principle
A Paper Series A[n] is twice as large as A[n-1]
Metric System Every unit is 10X the last
Vitsoe Shelvin All shelves have a standard width and mount anywhere on the wall
UNIX pipes All input and output are text

With consistency, code is literally shorter, requires fewer tests, and is much more generalizable. As a thought experiment, consider coding two different functions: an Imperial unit converter and a Metric unit converter.

The Test: Ask Yourself...

Can I verbally describe my system with a broad, sweeping generalization?

“ALL of our microservices can {insert-behavior-here}! …

… except for {special case} whenever {special user} does a {bad thing} on a Monday…”

If you find yourself using the word “except” a lot, you have inconsistencies.

Bad: JavaScript type coercion

JV 1.png
JV 2.png

JavaScript type coercion is a prime example of inconsistency. Depending on what the arguments are or what operator you use, the result may be a string, a number, or an object with no discernable reason for the differences.

Orthogonality

Do not link what is independent. A change in one function should not affect another. In other words: no side effects. If you ensure that your features are orthogonal, you will have fewer “gotchas” to work around when you need to make changes and fewer cases to test. Entangled code tends to lead to N^2 cases to consider, where N is the number of features that can all interact and break one another.

It will also help ensure a function is usable in as many cases as possible (you don’t want a side effect to make something unusable in an otherwise perfect situation, forcing you add MORE functions and violate Parsimony). For example, many shower controls link temperature with flow rate in a single knob. In a lot of cases, designers attempting to achieve Parsimony end up damaging the Orthogonality of their interfaces.

Bad

Side effects can really slow you down in the long run as you attempt to extend your system to new uses. Over time, they form a really sticky web that holds you back with ever-more knotted requirements: “We can’t do X because Y relies on it, so if we tried that we need to also take care of Z…”

Propriety

a.k.a. The “It should JUST WORK” principle or “No Excuses!”

Do not introduce what is immaterial. Do not expose incidental complexity, where extraneous implementation details affect the interface. If you think about a stick-shift automobile in this light, you’ll see that the shifting of gears is incidental to the task of driving; the gearbox is just an implementation detail. To give another example of an egregious violation of propriety: initially, iPhones were unable to use Facetime unless connected to WiFi. For most users, this limitation was completely arbitrary and frustrating. Proper systems minimize or eliminate exceptions like this.

Example:

Code that throws a totally irrelevant exception.

// let’s book an appointment with Jim the hair stylist

bookAppointment(HairStylist person, Date date) throws Exception

Better:

Code that throws a relevant exception and provides the user a proper message.

bookAppointment(HairStylist person, Date date) throws UnavailableException

The Test: Ask Yourself...

  • Does the recipient care? Is this actionable for them?
  • Is this inherent to the invoker or client’s task? Or is it merely incidental to how you’re achieving it?

Generality

Do not restrict what is inherent. Generality is the ability to use a function for many ends. Keep in mind that this often depends deeply on Consistency. If you can use a function for many ends… strong consistency maximizes the number of ends you can apply it to. Designs should not make too many assumptions about the use of a function. This is where Unix really nails it, keeping standard i/o, pipes, and files completely general.

The Test: Ask Yourself...

Generality tends to be a multiplier and relies on the other properties to be possible:

  • Do not restrict things too tightly; does this code really care that this i san X? Or does it merely care that X is separable?
  • Think from the perspective: what is the minimum that I actually care about to achieve my aim?

Standard power outlet: general

Consistent: all the various appliances

Great Example: $ cat report.csv | wc

Conclusion

You’ve now seen six ways to describe your code. They are distinct but you might also have noticed that they all interact with one another subtly and must be carefully balanced. Use these precise terms going forward, and use these examples to inspire you. I suggest that in the future, ask yourself some questions to help you remember:

In my code / system / design...

  • Can my list of basic concepts be shorter?
  • Do any features have side effects?
  • Have I hidden away the incidental complexity?
  • Does this function make more assumptions about the input than totally necessary?
  • Do all parts relate to each other with a concise, unifying principle?
  • Does my design ever force users to use idioms to express themselves?

Check out our engineering openings

Copy of UI Testing at Squarespace: Part II