Photo by Dawid Małecki on Unsplash

Standardization as a Last Resort

Lessons from a Failed Component API

Alan B Smith
Mar 22 · 6 min read

It’s always fun to promote successes, but it’s also good to learn from failures. I’d like to share a bit about a component API I built that was a failure in hopes that your components will avoid a similar fate.

Context

We use a double-stranded modular scale for all our typography and spacing, and it’s really cool. Here’s a great article if you’d like to read more about modular scales. The basic idea is using math to create a harmonious type scale. If you were looking at the individual values you might not think they were in pattern, but they feel harmonious when you look at the typography. Below are the values in our scale:


3px, 6px, 9px, 12px, 13px, 15px, 18px, 20px, 27px, 30px, 40px, 45px, 60px, 90px;

Note: 15px isn’t actually a part of the generated modular scale. But it was adopted as we often have a use case for it.

Line heights also mostly followed this scale and were mostly 1.5em for the font. (Sometimes we round to make the numbers easier though).

The Problem Being Solved

While the scale makes a lot of sense mathematically, it was challenging for developers to pick up and keep in their heads. It’s not as simple as a base-8 scale (three steps up on a base-8 scale is 24px). We did have some Sass variables to help obfuscate this: $scale-base-up-01, $scale-base-down-02, etc. But developers either weren’t aware of them or didn’t find them useful.

My Failed Solution

I thought the solution for an unintuitive scale would be to create a somewhat more intuitive standard using style props in our component API. Here’s an example:

<Text scale={1}>Our default text</Text>

This component would render some text styled like this:

font-size: 13px;
line-height: 20px;
// other text styles

Awesome! It seemed more simple than keeping the scale in your head and doing some math for the line-heights. If you know the base value, 12px, you should be able to move up and down the scale predictably. Moving up from 1 to 2 to 3 felt better, but it wasn’t a helpful standard, and it created a lot of confusion.

  • “Wait, why isn’t the base the lowest number?”
  • “Why does scale={6} return font-size: 30px?”
  • “Which step is 20px again?”

All very valid questions, and they point to a failed design.

Note: It’s often easy to think that user error is a the user’s fault, but it’s more often failed design. If your design doesn’t work for your intended users, it’s bad design. And it’s really important to note these issues and address them.

The Problem with Standards

Standards have a lot of aliases: regulations, guidelines, best practices, etc. And in software, we’re often in the business of standardizing everything in the name of consistency and maintainability.

  • “We use trailing commas in our lists. It’s easier to update.”
  • “Component names are nouns, functions are verbs, and props should be alphabetized.”
  • “We use BEM and only nest styles two levels deep.”
  • “Commit messages should start with the name of the ticket, followed by a short description using a present tense verb, and closed with a semantic release flag.”

Note: I’m not picking on any particular technology or pattern here, just providing examples I often hear.

Standards often have good reasons for existing. Despite being bothersome and pedantic, they can make things more maintainable and consistent. But they also have issues. They are almost always overly-opinionated, the trade-offs are rarely considered, and the purpose is often lost in the rules. I can’t count the number of times I’ve been in a team discussion and said, “I have a preference for x, but I don’t really care. Just choose x or y, but pick a standard so we aren’t doing both.” This seems to particularly be an issue in JavaScript.

Standards as a Last Resort

I was recently re-reading The Design of Everyday Things, and a section struck me while thinking about this. Don Norman talked about standards being a last resort at consistency, and that was something I very much needed to hear. I’ll summarize a bit:

  • The first option should the an intuitive solution.
  • If it’s not intuitive, it should be discoverable.
  • If those aren’t possible or reliable, it should be standardized.

Strive for the first two before you start raving about how you need to Standardize All The Things.™

Standards are useful and important tools in creating consistent patterns, processes, and practices. But they are not nearly as effective as the first two options and have far more negative side effects.

Intuitiveness

Car handles are designed in a way that your hand and arm are naturally positioned to pull the door open. You might have never thought of it. That’s because it’s an intuitive design. If car manufacturers decided that you should instead push then pull or lift to open the door and claimed it as a standard people would be very annoyed. The intuitive design that your users never have to think about is always the best choice.

Note: It’s important to remember that the “intuitive” pattern is based on your users’ intuition, not your own. You might have the same intuition, but that’s an assumption you should test.

Discoverability

While CLIs are highly efficient for experienced users, they have the problem of not being as intuitive as their GUI counterparts. When you approach a new program running in a CLI, you see a blank screen with a blinking cursor. to overcome this lack of intuitive design, CLIs have adopted a common practice of adding a help command:

$ npm — helpUsage: npm <command>where <command> is one of:
access, adduser, audit, bin, bugs, c, cache,
ci, cit, clean-install, clean-install-test,
completion, config, create, ddp, dedupe,
deprecate, dist-tag, docs, doctor, edit,
explore, get, help, help-search, hook, i, init,
install, install-ci-test, install-test, it,
link, list, ln, login, logout, ls, org,
outdated, owner, pack, ping, prefix, profile,
prune, publish, rb, rebuild, repo, restart,
root, run, run-script, s, se, search, set,
shrinkwrap, star, stars, start, stop, t, team,
test, token, tst, un, uninstall, unpublish,
unstar, up, update, v, version, view, whoami
npm <command> -h quick help on <command>
npm -l display full usage info
npm help <term> search for help on <term>
npm help npm involved overview
Specify configs in the ini-formatted file:
/Users/asmith/.npmrc
or on the command line via: npm <command> — key value
Config info can be viewed via: npm help config
npm@6.8.0 /usr/local/lib/node_modules/npm

It would be better for a user to have an intuitive choice, but if that’s not possible, at least we can move forward with these discoverable options.

Standardization

If both of those options fail, create a standard. You should try to make the standard somewhat intuitive or discoverable, if possible: choose a natural mapping, link to documentation, include information in your onboarding process, etc. But it’s likely that if you followed the flow above, you’re at this stage because there is no clear option, or at least there isn’t one that is clearly better than the other options. And that’s totally fine, make a choice and move forward.

Back To The Problem of Type Scales

The problem with our font scale was trying to make a non-intuitive scale a more intuitive standard with the hope that the component API would be more helpful over time.

Perhaps, I should have instead tried to make the scale values more discoverable. We use TypeScript, which I would argue has a very unintuitive and cumbersome design. But one of the things it does well is provide discoverability, especially when paired with Visual Studio Code IDE. It allows you to see types and values on hover and warns you when you use an incorrect type.

This is a rare compliment, TypeScript. Don’t get smug. You still have a long way to go.

Perhaps I could have not mapped the scale to linear values and instead listed the values directly in the scale, and have the component handle the line height and other font styles.

<Text size={13}>Our default text</Text>

And TS could help the scale be more discoverable like this:

VSCode popover explaining text scale type values

In any case, I jumped too quickly to make a standard with this component API, and I wish I had remembered Don Norman’s lesson first. Standards are better than chaos, but should be our third option, not our first.

Wrapping Up

Thanks for reading! It’s hard to talk about failures, but they’re often great opportunities to learn and grow. I hope there were some lessons about design patterns and establishing patterns that will be helpful for you and your team.

You can find me on Twitter and GitHub. If you enjoyed this, you should also subscribe to my newsletter!

Alan B Smith

Written by

Developer-designer, writer, & artist. Sr. Frontend Engineer at @SendGrid