Over the years at Slack — usually during periods of high growth — teams have defaulted to working in a waterfall where an idea is proposed and researched, then designed or spec’d, then built, in that order. It’s not the most efficient way to work and as a company grows — with more people involved at each step — it only gets slower.
There is an incredibly high cost to going backwards; if you realize you got it wrong during the build phase, going back to the drawing board can be seen as a departure or a failure to plan adequately. Hindsight is 20/20, as they say, so why wait until the end of the project to benefit from that clarity? Escaping this mode in order to work iteratively requires a high degree of trust and autonomy that takes time for teams to build, so it helps to talk explicitly about process. We’ve found success by focusing on the prototyping process as a way to break out of outdated, linear waterfall thinking. What we’ll share here is not a prescriptive methodology but rather a simple framework that can be adapted to a team’s needs.
I’ll be borrowing some terms and ideas from the Design Thinking process championed by Tim Brown at IDEO and written up succinctly on interaction-design.org, so if this post resonates with you then the Design Thinking literature has a lot more to offer!
Prototyping is a process for learning and evaluating. If you are at a point in a project where you have an assumption you’d like to validate, a specific direction you’d like to get feedback on, or a technical plan you want to evaluate, prototyping can give you the information necessary to make a firm decision.
Prototyping can also be used to evaluate broad opportunities before investing the full resources of a team into them. Building a rough version of that feature or system you’ve been kicking around for a year gives you the chance to decide not to pursue it. This is a skill that enables product and engineering teams to assist with critical business decisions.
Most importantly, prototyping gives us the guardrails to fail safely and productively. Failure is an important part of the learning process and is just as valuable as success.
There are three steps in the prototyping process: hypothesize, execute, and evaluate.
1. Come up with a hypothesis and define success and failure
Your hypothesis is the statement that the prototype should prove or disprove. It’s important to define what success looks like up front — especially when evaluating subjective design directions — but equally important is deciding what failure looks like. Remember that failure is not something to be avoided here, it’s simply one of the possible outcomes to plan for. A failure will almost always lead to a reformation of the problem and a new prototype; this is the relationship between the ideation and prototyping phases of the design process.
Let’s look at some example hypotheses we had at Slack and the success/failure criteria that might accompany them.
💡 What-you-see-is-what-you-get messages can be displayed seamlessly alongside existing messages in the database.
✅ Older messages continue to display correctly alongside newer WYSIWYG-formatted messages.
✅ The search infrastructure can be modified to return results from both formats.
❌ All old messages need to be migrated to a new format to support WYSIWYG.
Result: Success. This was one of the early prototypes that paved the way for the large WYSIWYG engineering project.
💡 CSS-in-JS will make it easier and lower-risk to implement dark mode.
✅ We put together a basic dark mode prototype using a CSS-in-JS solution.
❌ The CSS-in-JS migration path causes us to fork our styles into two places that don’t stay coordinated without additional tooling.
❌ CSS-in-JS doesn’t unlock new technical possibilities for us.
Result: Failure. The migration path using CSS-in-JS didn’t seem worth the investment at that point in time and we instead opted to use CSS variables, which you can read more about in a previous blog post.
💡 We could make Slack render in under 1 second by serving static HTML from the CDN rather than rendering a template from the server.
✅ Our proof-of-concept is close to, or under, 1 second of load time.
❌ We hit an insurmountable technical snag.
❌ We can’t come convincingly close to 1 second.
Result: Success. This was the original prototype that led us to rearchitect the Slack client. You can read more about that project in this blog post.
It’s important to write these down before you start. The goalposts are easy to move as the prototype comes together, especially when it’s exciting.
2. Write some throwaway code
Prototyping is chance to validate an idea without all of the baggage that comes with production-quality engineering. If you’re planning to throw the code away at the end, then shortcuts like hardcoding data, skipping the tests, ignoring the linter, etc. are all fair game.
Working in a separate repo or isolating the prototype to its own page can help mitigate safety concerns and increase code readability if writing production code is necessary. Always take the time to plan your technical approach; safety is essential!
3. Evaluate, document, & make a decision
The takeaway is not the code or the output itself, but the learning that comes out of it. Take the time to evaluate and document the work and decide what you’ll do with this new information. What went well and what didn’t? Were there any surprises? Did you meet your success criteria? Were any of your assumptions incorrect?
Documenting the lessons learned is not just a useful way to recap the work — it can become an important historical artifact down the road when another team is considering similar ideas. The ability to say “we tried that; here’s what we found” gives others valuable signal to work with.
If your prototype failed, the next step might be to formulate a new hypothesis and try a different approach. If the prototype was successful and demonstrated the viability you were looking for, perhaps you capture what you learned in a detailed technical spec that serves as the basis for an engineering project.