Making good GUIs is hard

After spending a lot of time building this blog, I decided to explore why I think it's hard to build good UIs. Also, I try not to sound discouraging.

I’ve been working on graphical user interfaces (GUIs) on the web, mobile, and desktop for over a decade now. Over time, one thing that really stood out to me is this notion that building UIs is somehow easy. I disagree.

But first, I'd like to separate the overall discipline of "building GUIs" from the tools and frameworks used to build them. While the tools can make the building process enjoyable, I don't want to discuss them in this post. Instead, I want to focus on what the core of building UIs is.

The thing is, the more I work on and with GUIs, the more I’m convinced that the opposite is true — GUIs are inherently hard to build. Let me explain why.

It’s right in the name

GUIs are graphical user interfaces. They’re the primary point of contact with an actual person. And the thing about people is that they’re way more complicated than computers.

To demonstrate, take another kind of interface — Application Programming Interfaces. You could say that APIs are also written for people and that computers don’t care. While this is true, it’s also true that APIs operate in a very constrained problem space:

  • they are tightly defined in terms of inputs and outputs
  • the people dealing with them are (sort of) trained to do so

In contrast, GUIs present surfaces for people to interact with. Theoretically, you don’t need much training to interact with GUIs thanks to familiar metaphors from the real world — buttons, knobs, sliders, forms, etc. Beyond that, nothing is really defined.

So actually, when you’re about to build a GUI, you really have a blank canvas in front of you that is supposed to eventually help people (who maybe don’t know what they’re doing) achieve their goals without getting lost.

That doesn’t sound easy, right?

The devil is in the details

To solve hard problems, you have to scope them down. And that’s another thing that makes it non-obvious — the sheer number of decisions you have to make.

Who’s your audience? In what situations are they going to use your thing? What about first-time users — do they interact with it differently than experienced users? How much effort do you want to put into this? What are the limitations of your platform?

Answers to those questions aren’t really specific to GUIs, really. They only draw a rough outline and influence further, more specific decisions. For example: Should your UI work on mobile devices? Does someone have to be online to use it? What if somebody is, say, colorblind — is it hard for them to use your thing? Do you want it to blend into the operating system well?

The rabbit hole of decisions can become pretty deep.

A somewhat real-world example

I feel like I spent much more time working on this blog’s layout and its post editor than on the underlying library to use use Matrix as a blog, so I think they’re a good example. But first, to set the stage, let’s start with the library and the decisions that shaped it.

matrix-blog is a library written in TypeScript to talk with a Matrix server as if it was a backend for a blog. It only has one dependency, node-fetch, and that’s mostly for convenience. It’s made of two layers — its own Matrix client and an API to interact with blogs and their posts. There are a few decisions I had to make to make this happen:

  • I wrote a Matrix client because I wanted to learn more about the Matrix protocol and see if it’s hard to work with (it’s not).
  • The library is as stateless as possible to see if I can offload everything onto Matrix.
  • I wanted to use it on the web-based post editor, so this limited me to JavaScript and friends.
  • I picked TypeScript mostly because the Matrix API is typed — it’s better if the compiler prevents me from making errors.
  • I didn’t write any tests because it’s mostly an experiment and I wanted to iterate quickly. That said, the APIs are designed to be testable if I changed my mind.
  • There’s no login method because I didn’t want to think about session storage.
  • Each dependency incurs a cost of giving up understanding parts of your codebase. Sometimes the cost is worth the productivity boost, sometimes it’s not. I like to understand as much as possible.

That’s pretty much it. Let’s contrast this with the decisions that shaped this website:

  • I wanted the site to reflect my personal tastes, but also feel light and be legible, so I designed the theme from scratch. This alone involved:
    • picking colors and fonts,
    • figuring out type scales,
    • arranging the layout,
    • designing a favicon (it’s hard!),
    • deciding how much visual flair can I afford.
  • The website should not just feel light, but also be light. Therefore, I:
    • used only 4 images as SVGs,
    • optimized them to be as small as possible using SVGOMG,
    • reduced the size of the heading webfont using subsetting,
    • used a static site generator (11ty) to avoid hitting a server,
    • used as little HTML markup as I could,
    • don’t use client-side JavaScript aside from a <1kb gzipped script for my self-hosted analytics.
  • I wanted the website to work well on all major browsers and devices, so I:
    • used SVGs because they scale across sizes,
    • spent quite some time aligning the top image across screen sizes,
    • made sure that the browser features I use are widely available,
    • tried it on whatever browser I had at hand,
    • asked friends to check it on devices I didn’t have at hand.
  • I don’t set any cookies because I care about privacy and I didn’t want to show an obnoxious cookie banner.

That’s a much longer list for something that just displays content! And there's still a lot to improve!

Everything is hard if you want to do it right

Of course, you could argue that if you want to build things well, then it’s always going to take more time, energy, and decisions. That’s true. You could also decide that you don’t care about the looks and not do any of the things I've done. That’s fine.

The point I tried to express in this blog post is that, on average, GUIs are hard to build because of the number of decisions you have to make. That's just how they are! But that shouldn't discourage anyone from building UIs! On the contrary, I hope people will treat them as any other challenging problem to solve. I also hope that it will lead to better user experiences for everyone.

Discuss this post on Matrix