Design played a very minor role in the early day of SharpestMinds. There were a couple of reasons for this: (1) none of us were competent designers and (2) we wanted to validate our ideas quickly—why waste time and effort making something pretty if nobody ends up using it?
To put this in perspective, here is one of the earliest iterations of the SharpestMinds mentor dashboard:
I found this screenshot on my laptop from August 2018. It’s clearly from a testing environment, but I know we pushed a version of this to production—this is what our first batch of mentors saw. We took Reid Hoffman’s advice to heart: “If you are not embarrassed by the first version of your product, you've launched too late.”
In retrospect, there are many design flaws (and not just aesthetic ones). But we were lucky to have close relationships with our first batch mentors. So we were able to talk to them regularly, learn, and iterate.
Eventually, we had to take design more seriously. I talked about this transition in a previous blog post, Designing at the right level of abstraction:
[W]e embraced a design-on-the-fly, just-make-it-work attitude. Without much more than a text or verbal description, we would simultaneously plan, implement, and design new features… This approach worked well enough while we iterated towards product/market fit—our early adopters could accept a sub-par user experience because they believed in the mission.
At some point, however, we noticed a shift from market risk (do people want this?) to execution risk (can we deliver a good enough product?). This shift caused us to raise our standards for the minimum in minimum viable product (MVP). New features need to look good, feel good, and map well onto our user’s mental model of the product. This means more effort spent planning and designing.
Initially, our approach was very waterfall-like. Our lead designer (at the time, our co-founder Jeremie) would go off on his own and create a design for the next feature. Then I—as CTO and sole developer—would do my best to build the feature and implement the design.
As I built, I would freely make design concessions to reduce complexity, handle edge cases, and ship within our appetite. Doing my best to approximate the original design, but being comfortable with "close enough".
My definition of “close enough”, however, was not always shared. Aspects of the design that seemed trivial to me were critical in the eyes of the designer. Because of the waterfall nature of our approach, these disagreements would not show themselves until the end of the cycle—when the designer would see the concessions made to their initial design.
These situations were leading to increased friction between development and design. The designer felt that the integrity of the feature was being sacrificed. The developer felt resentment at being micromanaged over tiny details.
We’ve since resolved this tension with three key process changes:
Design should start at a high level of abstraction.
Design is on ongoing process that is coupled with development.
Design decisions need a common framework and language.
1. Design should start at a high level of abstraction
If the first step in the planning process is a detailed design—fonts, colors, and all—it becomes easy to get distracted by minute details instead of taking a step back and thinking deeply about the problem. Planning with the design first can also lead to wasted effort. A detailed design can anchor you to a specific implementation and blind you to alternatives. Alternatives that may be much simpler.
Our approach here is heavily inspired by Shape Up. Before a feature is built, it must be pitched. That pitch should include some rough prototype designs of the feature.
Our prototype designs sit somewhere between traditional wireframes and the fat marker sketches suggested in Shape Up. The point is to show all the affordances and actions that are available on each page and element and how they interact with each other. The layout is merely a loose suggestion, something to be iterated on once the pitch is approved.
Here is a recent example of a design prototype for the SharpestMinds mentor dashboard:
A developer, armed with the above, can start right away—building the backend and infrastructure and wiring things up to a bare-bones front-end. Meanwhile, a designer can start iterating in parallel.
2. Design is on ongoing process that is coupled with development.
Most software work is "discovered" while building. Challenges and problems that were not obvious during the planning phase are uncovered once you start writing code. I think the same is true of design.
Often, it is developers that will unearth discovered design work as they build:
“This component needs to handle text of variable length.”
“Some users don’t have this property.”
"This will not look good on mobile."
"This is missing an affordance."
“How do we handle this rare state?”
"This introduces too much complexity."
"This is not consistent with the rest of our app."
Our goal with each new feature is to discover work as early as possible. The earlier problems are identified, the faster, easier, and more cheaply they can be fixed.
Instead of passing down a design and moving on, the designer works and iterates in public. Starting with the prototype above and gradually moving down the layers of abstraction. This leaves more room for input from the rest of the team—most importantly, developers.
Developers will have a sense of what assumptions are being made and which design choices will introduce the most complexity. Communicating this to the designer before the design gets too specific will help save wasted effort designing something with flawed assumptions or uncessary complexity.
But this communication should be a two-way street. When the developers hit a challenge implementing a design detail, they should rope in the designer and cooperate on a compromise. Both parties should feel like their input is being fully valued. And, by roping in the designer on these challenges, they will develop a better mental model and better anticipate these challenges in future designs.
3. Design decisions need a common framework and language.
One of the reasons design is so tricky is that there is a lot of subjectivity involved. Claiming that a design has the right "feel" is not helpful. Without a “why” to justify certain design choices, it will be hard to convince others that they are worth implementing.
We need to be able to communicate the reasoning behind design choices in order to debate trade-offs as we built. So how should we think and talk about design? How do we convince each other what is "good" or "bad" design?
What we need is a common framework and language for discussing design. This is still something we’re working on, but The Design of Everyday Things by Don Norman has given us a useful base. It mostly pre-dates the internet, but it has some very useful frameworks for thinking about design at a high level.
Norman claims that anyone using a product should always be able to determine the answers to these questions:
What do I want to accomplish?
What are the alternative action sequences?
What action can I do now?
How do I do it?
What happened?
What does it mean?
Is this okay? Have I accomplished my goal?
The information that helps answer questions of execution (the first 4 questions) is feedforward—how does the user know what to do. The information that aids in understanding what happened (questions 5-7) is feedback.
This leads to Norman's 7 fundamental principles of design:
Discoverability: It is possible to determine what actions are possible and the current state of the device.
Feedback: There is full and continuous information about the results of actions ad the current state of the product or service. After an action has been executed, it is easy to determine the new state.
Conceptual model: The design projects all the information needed to create a good conceptual model of the system, leading to understanding and a feeling of control. The conceptual model enhances both discoverability and evaluation of results.
Affordances: The proper affordances exist to make the desired action possible.
Signifiers: Effective use of signifiers ensures discoverability and that the feedback is well communicated and intelligible.
Mappings: The relationships between controls and their actions follow the principles of good mapping, enhanced as much as possible through spatial layout and temporal contiguity.
Constraints: Providing physical, logical, semantic, and cultural constraints guides actions and eases interpretation
We’ve started adopting some of these terms and concepts in our debates about design. Of particular use are the concepts of feedforward and feedback. We’ve made many poor design choices in the past by failing to recognize the value of feedforward signaling. Users should be able to discover what actions are possible, but they should also have some sense of what will happen after they take each action.
Looking at that initial early design again—the buttons do a poor job of signaling what they actually do. Mentors would be hesitant to click either of them, not knowing what’s on the other side. Will clicking “interview” start a video call right away? How will this notify the applicant? "The “reject button suffers similar flaws. It is a harsh word that most folks would be hesitant to click.
These are simple examples, but feedforward signaling just wasn’t something we thought about in the early days—the text and design of buttons felt somewhat arbitrary. Now that we have a framework and language to apply, it’s easier to argue for specific design choices. Instead of “I think this button looks better,” we can say “This button does a better job at signaling.”