Code is a User Interface!

The Art of Designing Code – Part 1

ℹ️

This article is a written, enhanced version of my talk ▶️ The Art of Designing Code, presented at iOS Conf SG in Singapore.


Imagine you need a photo editing app. You are offered two different apps with the same functionality. They only look different:

Two different user interfaces with the same functionality

Which one would you choose?

You will probably agree that the second option looks more appealing. But it also has a better user interface with respect to usability:

  • Each editing option is presented with a small thumbnail image. This way, you get a good idea of what it does – without the need to actually apply it to the image.
  • When you select an editing option you can immediately see the effect it has on the image.
  • Some editing options offer additional parameters. You can tweak these parameters directly in the image using finger gestures.
  • Rather than a flat list, you see three tabs at the bottom that represent the respective editing categories. You only see the options of the category you selected at a time. You can focus on your intent and won’t be distracted by options you’re currently not interested in.
  • The navigation bar uses simple, well-known icons rather than text. We are so familiar with these simple shapes that our brains can understand them faster.

These are only a few examples to show why it’s really important to put a lot of thought in the user interface of an app. Users don’t want to read a manual before using an app. They just want it to work — without thinking much about it. An app should be easy and intuitive to use.

A user interface is the means of developers (and designers) to convey an app’s functionality to its users. Whenever we fail at creating good user interfaces, users get confused and lose efficiency when interacting with our app.

Similarly, developers get confused and lose efficiency when interacting with messy code. That’s because:

Code is a user interface itself.

The only difference to an app’s user interface is that the user of our code is another developer (or even our future self) rather than the end user of the app. (Feel free to call it a developer interface instead.)

Designers use a couple of basic design principles when they create user interfaces and other artwork. Now, if code is a user interface — shouldn’t it be possible for developers to apply the same basic principles to code? Lets give it a try!

We’ll start off with the simple principles that are usually already taken care of by your IDE (code editor). Then we’ll move on to those principles that you are in charge of as a developer.

Design Principles

Space

We usually don’t have much control over the horizontal spacing in our code. It’s either the programming language or the IDE that requires or enforces a particular spacing. However, we do have control over the vertical spacing and can add line breaks at our own discretion.

The human mind identifies spacing as a separator between clusters of information. Thus, we can use line breaks to help other developers understand the structure of our code faster.


Code Example: Only three line breaks help our minds to understand the structure of this code a lot faster.

The more vertical spacing between clusters of code, the easier it gets for our mind to identify the clusters as such. It might sometimes make sense to use several line breaks in order to visually separate blocks of code.

Alignment

In addition to vertical spacing, alignment is another design tool that helps our minds identify clusters. It’s always a good idea to indent nested code using tab stops.


Code Example: Indentation helps our minds identify nested clusters of code.

However, there are other scenarios as well when it makes sense to do this: for example, when you write a very long line of code that does not really fit on a regular screen anymore (you shouldn’t → see below), breaking it into a new line improves readability. In this case, indenting the line (i.e. deliberately breaking the alignment) helps other developers understand that this is a continuation of the previous line.

(Note: You’ll see this a lot in code that makes use of reactive patterns like RxSwift because it invites developers to chain multiple function calls.)

Color & Contrast

Most pieces of artwork focus on only a few key colors. Take this promotion poster, for example that I photographed on the island of Koh Tao.

It has an ocher background color, the prime color mainly used for regular text is black and a bright blue is purposefully used as a contrast color to highlight the most important facts:

  • Free Entry!
  • Free Activities!
  • Delicious Drinks!
  • On Sundays

Code Example: Color helps us to focus on keywords and to distinguish between different types of code.

Modern IDEs automatically color our code. Sometimes, they simply choose the wrong colors though: Comments are usually colored in some shade of green, which is a relatively strong color both on white and black background. As a consequence, they visually stick out of the code, guiding our eyes to focus on the comments rather than the actual code.

The good news is that many IDEs allow you to tweak the visual appearance of your code, including its color. You might want to consider using a subtle gray color for comments and documentation and stronger colors for function names, for example.

Proximity

The reason it’s so hard to find a needle in a haystack is because things that don’t belong together were simply thrown together. (What’s a needle to do in a haystack anyway? If we put similar things close to each other, it becomes a lot easier to find them.

Designers use this principle of proximity and usually cluster similar pieces of information.

On the poster from the previous section, the promoted drinks and activities are each clustered in a black circle. The social media profiles are in close proximity to each other and all of the location information is clustered in a single line.

Equally, developers will find specific functions or variables faster in code if those related to each other are in close proximity.


Code Example: Placing related functions and variables in close proximity makes it easier to find them in code.

In the example above, outlets (= connections between specific user interface files and code) and other properties come first, followed by a cluster of methods related to the view life cycle and a cluster of functions which get called whenever the user interacts with the app.

Balance

For reasons of aesthetics, designers would usually not leave half of a page blank and put all the information and images on the other half. They try to make their artwork appear well balanced.

In code, we don’t need to worry too much about the “balance” of a single file. It is important though to keep an eye on the balance of your files relative to each other.

Code Example: When files are too long or too short, it’s usually a sign that something is wrong with your code.

When a file is too long and spreads over several hundred or even thousands of lines, it is a strong indicator that it contains something like a “god class”, a type that has many responsibilities and violates the single responsibility principle not only once. A large file makes it really hard and time-consuming to find the piece of code you’re looking for – even if the file is well structured, following the proximity principle.

When a file is too short (just a handful of lines), it’s either because you decided to strictly follow a particular architectural pattern without any exceptions for reasons of consistency (which might be a good thing) or because you are over-engineering the app (which might be due to the fact that you follow a strict architectural pattern 😉).

In the code example above, we have a view model with only one color property. Unless we expect the view model to grow and get additional properties, we could use the color directly rather than wrapping this single property inside another type and thereby adding complexity to the code.

Repetition

Repetition is a bad thing in code. If we discover (almost) the same code at two or more different places, it means that the code should have been extracted into a separate function to make it reusable and less error-prone. Avoiding repetition is the very reason why we use functions in the first place! But this is not the kind of repetition I’m talking about.

Designers don’t usually repeat the same piece of information on their artwork either. However, they do repeat their design patterns. On the poster, the name of the drink Sangria is written in a bold type face, blue color and all capitalized. The price for this drink is written in a lighter type face, white color and a reduced font size for the currency symbol. This pattern is repeated for all four drinks, so we immediately understand that there are four drinks on promotion with their respective prices.

As developers, we should repeat our coding patterns as well:



When we apply the same pattern to all classes of a certain kind, we don’t need to reinvent the wheel every time and other developers only need to understand how the code in a single class works in order to understand all classes of this kind. That’s a big plus for efficiency!

In the code example above, every view controller is designed to have a single view model property and an update function that takes a single parameter of the view model’s type. The update function is the one place that updates the appearance of the view controller. Once, this is clear to a developer, she or he knows where to make modifications to any view controller’s appearance.

ℹ️

Side Note: Yes, you could formalize these requirements with protocols and subclassing but this would require generics which might make the code more complex and thus achieve the opposite of what you want. Additionally, your programming language might not support generics or have certain restrictions on them. In iOS development, for example, you cannot make a Cocoa Touch class like a view controller generic because it needs to be interoperable with Objective-C.

Repeating patterns is a good practice in every part of your app. Not only can you repeat code patterns, but folder structures as well. On a previous project I’ve been working on, we split our app into several modules. Each module would have its own folder with a Readme file on top, followed by a visual user interface file (it’s called a storyboard in iOS development), a coordinator file and a view model file. This way, it was very easy to find the relevant files when a developer started working with a specific module.

Code is a Language!

As we have seen in this part of the article, many basic design principles can and should be applied to code as well in order to make it easier to work with. However, it’s not only designers we can learn from. When writing code, we constantly use a programming language. Consequently, it makes only sense to examine the tool set of people who deal with language on a professional basis every day: journalists.

In part 2 of this article we will take a close look at the powerful tricks radio and TV producers use to make their pieces easier to understand for their audience. We will see how these tricks can be applied to code as well, improving its readability dramatically.

Read on!