Good Design is Analogous and Intuitive
Good design is:
- analogous. It tries to draw parallels from itself to patterns that its users are familiar with.
- intuitive. The patterns it tries to emulate are, ideally, things that come naturally to its users.
Tools that build on the audience's intuitive knowledge often end up being more popular than those that come up with entirely new models. The best tools imitate intuitively familiar patterns while presenting enough distinctness to distinguish themselves.
Let's see a few examples across a bunch of fields.
Emoji have become a meta-language of their own. We know that 😂 represents laughter, 🙄 represents rolling one's eyes, and 👍 represents approval. How did we learn these, though? We didn't have to look them up, because they make use of familiar human interactions. The laughing appearance, the eyeroll, the thumbs-up. These are patterns that are natural to us, so we instantly understand their meanings.
And yet they aren't exact copies. Someone rolling their eyes does not actually look like 🙄 (unless they have a yellow face and a circular head). The human anatomy of a thumbs-up is not 👍 (this may vary, depending on your platform). And how often do you see someone laugh until their face is like 😂? These glyphs are similar, but not exact, allowing you to simultaneously bring in your old knowledge and understand the new language.
Cartoons and Illustrations
If you're like me that watches cartoons (I love The Simpsons😍), you must have noticed how different animations use different art styles, yet we're still instinctively able to understand that these are "persons". Some cartoons are realistic-looking, some use overly big heads or long noses, and some are just stick figures — either way, we get it.
Some of these look more human than others, but we instantly recognise each one as representing a person. Source: Cartoon Kevin
At the same time, we also understand that cartoon characters don't operate by the laws of humans. We're fine with Homer Simpson hammering a nail through his eye and not bleeding. We're cool with characters always wearing the same clothes. These departures from human norms are easier to live with because the cartoons have first established a relatable foundation.
In a similar class are illustrations, the kind you often see on startup landing pages these days (get yours here). The figures here don't often match the human anatomy, but they're close enough that we can relate.
Games and movies
Like cartoons, games use characters that often look like humans and behave like humans, but are not humans, and we implicitly know this. For instance, if your video game character gets shot in the chest once, you don't expect them to drop dead. This happens with movies, too: we watch humans do things on TV that we know would kill them in real life, and we're often fine with it. In fact, a lot of the time, this deviation from reality is why we enjoy them. Imagine if a game fully copied reality — you do chores, you fall sick, you have to do actual work for money or else you lose your home — that would get lame pretty fast.
Games and movies are easy to grasp when they introduce their distinctness on top of our established patterns. If a movie came up with its own world with different rules of anatomy, gravity, maths, physics, communication, technology, relationships — you can see that as the amount of deviation from our normal increases, it will take more time and effort to understand the film.
I think this is a staple of popular art: for proper enjoyment, there needs to be both — a foundation in the audience's reality, and a creative deviation from that reality.
I believe this analogous nature of design is one reason why object-oriented programming, and not functional programming, is the "default" programming style today. Most OOP guides can hardly go a few paragraphs before bringing in an example of "objects" in the real world, often something silly like
Dog. It's easier to explain OOP (rightly or wrongly) by pointing to analogies from a person's model of reality. With this basic model in mind, it becomes easier to wrap your head around the more advanced features of OOP.
By contrast, FP is much harder to describe by pointing at something in the real world. For example, all of the answers to this StackOverflow question that try to explain what a functor is end up calling on category theory or other programming constructs. This is a limitation I've encountered in my FP reading, as well.
This is what people mean when they talk about a learning curve — how much does this framework deviate from the patterns people are intuitively familiar with? How many new concepts does it introduce? Sure, you can still pick up FP; for most people, however, it's more difficult (and takes repeated learning) because the analogies it draws are often not as intuitive as those of OOP.
Over to you and I
There are many more examples of this. For instance, software libraries that use common patterns and so are easy to pick up, versus those that are perhaps more powerful, but introduce new models and terminology and need a lot of learning.
So how can we practically apply this principle? For me:
- As a library developer, before creating or modifying an interface, I think about the conventions users are familiar with. Technically speaking,
$post->save()might be "wrong" (since a post cannot save itself); however, users understand it and it meets their needs perfectly, so changing it to a more "accurate" model like
$entityManager->persist($post)may not be worth it.
- As an API developer, I start with following common conventions (HTTP, JSON, etc), and then I weigh the cost of each distinguishing feature. For instance, the Link header is a standard way of including pagination links (GitHub uses it), but it uses a weird, variable format that users need to learn and install an extra package to parse, so I'd probably stick with pagination links in my JSON. A 410 or 422 status code may be technically more accurate for a specific kind of response, but most users are only familiar with 400-404, so I might stick to one of those if it is descriptive enough. When designing my endpoints, I may then flex my "distinctness" muscles; for example, I may use RPC-style endpoints over REST if my API is oriented around functions.
When designing software (and non-software) products, you may have an awesome product that will change the world, but the more it deviates from people's intuition, the harder it will be to sell. So design with care; if there are too many deviations from the norm, it might be worth rethinking that design.
Hey👋. I write about interesting software engineering challenges. Want to get updated when I publish new posts? Just visit tntcl.app/blog.shalvah.me.
(Confession: I built Tentacle.✋ It helps you keep a clean inbox by combining your favourite blogs into one weekly newsletter.)