Article
All Code is Designed — Sometimes Unintentionally— Part II
February 27, 2019
We are all designers whether we acknowledge it or not. Whenever we write a function, add a property, or update our documentation, we’re changing the design of our codebase. Good design comes down to communicating effectively using only the tools available, and this is really hard. In the previous article , we learned about the six different components of design and how they apply to the code that we write. These six components are the tools we have at our disposal when communicating with the consumer of our code. Before we can understand how to effectively apply the six components of design, we need to know what to communicate.
The Seven Stages of Action
The first step toward knowing what to communicate is understanding how our code will be used. When we know how our code will be used, we can anticipate potential road blocks and errors our users will encounter as they interact with our code. With this knowledge we can strategically place signifiers, affordances, mappings, and constraints to guide the user, providing helpful feedback when a mistake is made.
Don Norman outlines the path the user will take in his seven stages of action:
1. Form the goal
2. Plan the action
3. Specify an action sequence
4. Perform the action sequence
5. Perceive the state of the world
6. Interpret the perception
7. Compare the outcome with the goal
These seven steps form a loop. The user forms a goal, makes a plan for achieving that goal, acts on that plan, and interprets the feedback. At this point, if the goal is achieved the user can move on. If the goal is not achieved, the user will reformulate a new plan and try again.
Steps 2–4 comprise the feedforward part of the loop. In this part of the loop, the user will read method signatures, property names, and documentation to formulate and execute their plan. As the designer, you may choose to limit the users possible actions with constraints or use mapping to provide an initial conceptual model for the user.
Steps 5–7 comprise the feedback part of the loop. This is where providing thoughtful errors helps the user understand what went wrong and how they can fix it. As the user interacts with your code and receives feedback, they will update their conceptual model of how your code works. Notice that half of the execution loop is dedicated entirely to one of the design components: feedback. We should not neglect the importance of designing for error scenarios.
The Seven Stages of Action and Three Levels of Processing
These seven steps work at three different levels of processing in the brain: Visceral, Behavioral, and Reflective.
The Reflective Level — Steps 2 & 7
The reflective level is the only conscious level of the three. This is the level where we interact with the six components of design, form our conceptual model for how a thing works, and reflect on our experiences to decide how we feel about a piece of code. When working with a new piece of code, we spend a lot of time at this reflective level because it is the level where the feedforward/feedback loop is found.
The Behavioral Level — Steps 3 & 6
The behavioral level is subconscious and is home to learned skills. In the real world, an example of design at the behavioral level would be Apple’s Human Interface Guidelines (HIG). The HIG provides high level details of how an app should work. The reason for the existence of the HIG is to ensure that the user of an iPhone is provided with consistent interactions among all the apps on their phone. These consistent interactions allow a user to learn behavior in one app and apply that learned behavior to an entirely different app. This maintains consistency in user experience despite many people working independently of one another.
In code, the behavioral level is dictated by language, platform, and code base norms. Optionals are a core feature of the Swift language. Once you learn how to use optionals, that learning is portable across code bases. The delegate pattern is a common design pattern on the iOS platform — once you learn the delegate pattern, you will have an easier time learning other parts of Apple’s frameworks. Many times in our own code bases, we have established norms which allow us to take learnings from one part of the code base and apply them to others parts.
The Visceral Level — Steps 4 & 5
The visceral level is subconscious and is all about initial reactions. In the real world, an example of a visceral reaction would be someone reacting in disgust when seeing a specific color or hearing a specific sound. If you ask the person why they dislike the color or sound, they will provide you with a justification which is no more meaningful than “Because I don’t like it.”
When we write code, visceral-level design involves things like naming conventions, using tabs vs spaces, or putting the opening braces for a function on the same line as the function definition vs. the next line. These are all small, nuanced decisions that can elicit a visceral response from the user of our code. If you’re not following the convention of the codebase, it will cause confusion and maybe even anger. Take a listen to the first four minutes of the Accidental Tech Podcast — Episode 312 . Do you hear the visceral response and design discussion?
“Consistency in design is virtuous. It means that lessons learned with one system transfer readily to others.”
- Don Norman
The astute reader would point out that Step 1 — setting a goal — is noticeably absent. That’s because setting a goal can occur at multiple levels. Goal setting can be completely subconscious, and we can even achieve some goals without even thinking about it. Imagine a scenario where you’re intently focused on doing something, but you need some more light to see. Turning on a light becomes a goal and it’s likely that you don’t need to spend any time consciously planning or executing the steps required to turn a light on.
Design is Difficult
Effective communication is the bedrock of quality code. Many bugs in software arise from an incorrect or incomplete understanding of how the code behaves. If you don’t want to end up with a codebase that is frustrating to use and difficult to maintain, it is imperative to know the seven stages of action and how to apply the six components of design to our code.