I pulled up to a Dairy Queen drive-thru last week and ordered a meal in the wrong order. The cashier couldn't take it. Not because the information was missing (I'd given her every piece she needed) but because I'd given them to her in a sequence the point-of-sale system couldn't absorb. She made me repeat elements three times to get the magic incantation right. I know it wasn't her fault.
This bug lives in nearly every piece of software I've used. The interaction is conceptually order-independent (bold then italic, italic then bold, doesn't matter), but the system underneath models it as sequential, and the order leaks out to the user. That leak is what I want to talk about.
Why Idempotent and Not Orthogonal
In math, an idempotent operation produces the same result whether you apply it once or a thousand times. In engineering, idempotency usually means you can retry a request without changing the outcome. mkdir foo fails the second time; mkdir -p foo doesn't. The flag is what makes the operation idempotent.
I'm borrowing the term a little loosely for design, because the closest engineering concept (orthogonality) already means something else in the design world. Orthogonal designs are two or more approaches to the same underlying need. A contact form and an LLM chat that collect the same fields are orthogonal: different surfaces, same outcome, and a team can ship either or both without one blocking the other. That's a useful framing, but it's not what I want to talk about here.
The property I'm after is whether the order of independent actions affects the result. Bold then italic gives you the same component as italic then bold. Adding a leading icon and changing a label are independent operations; either sequence ought to produce the same output state.
Many user-facing interactions ought to be idempotent by default. The cases where order genuinely matters (a wizard, a checkout, an irreversible commit) are the exceptions, and they should be designed as exceptions. Yet most software treats idempotent interactions as if they were sequential, because the data model underneath happens to be sequential and nobody pushed back.
Idempotency in design isn't a strict math property. It's a promise to the user that the tool will meet them wherever they start, in whatever order they think.
The Drive-Thru Test
The Dairy Queen story has a counterexample down the road. I can pull up to a McDonald's drive-thru and say "number one, large, with a Diet Coke." I can also say "Diet Coke, large, number one." Or "a Big Mac and a Coke, make it a meal, make the Coke diet, make the meal large." All three describe the same order, and McDonald's point-of-sale absorbs them. Someone on that team thought through what the user actually says and built the system to handle any phrasing.
Dairy Queen (and most typical configurations from Micros, Aloha, and the rest) has not. Try to upgrade the drink before the meal is built and the system gets confused. The cashier compensates by holding the order in memory, reordering it for the system, then entering it, sometimes mid-sentence while the customer moves on to the next item. Fast-food locations struggle to find people who can handle the drive-thru specifically because of that cognitive overhead.
Think of it as a pigeonhole problem. The order has a fixed set of slots (item, size, modifications, drink), and a sufficiently good interface routes incoming information into the right slot regardless of arrival order. The hard work is on the team building the system, not the user placing the order. Most teams skip that hard work, and the friction lands on the user.
Where the Tools Fall Apart
Design tools are full of this. Figma is the worst offender I work in daily: nested components, instance overrides at every layer, and an override-resolution algorithm where the order you make changes determines whether your other changes survive. Swap a child icon before you change the parent's state and the state change silently fails to propagate. Reverse the order and it works. I'll dig into the specific cases in a future post; the point here is the philosophical one. The tool's behavior is consistent. It just isn't idempotent.
Designers who use Figma long enough split into two groups. One learns the order-dependence as muscle memory and develops a private set of workarounds: set the color last, don't touch the size after you've set the variant, don't swap the nested instance until everything else is locked in. The other stays constantly frustrated and never quite figures out why their components keep breaking. Some of the failure modes are nuanced enough that even experienced designers can't articulate the cause; they just clean up after the tool and chalk it up to Figma being Figma. None of the workarounds are documented anywhere, because the tool can't carry the rule itself. It's tribal knowledge passed designer-to-designer.
Real Constraints vs Shadow Constraints
Not every interaction should be idempotent. Some flows are inherently sequential. You can't ship a package before you've packed it. You can't deploy code before it builds. You can't charge a card before you have the cart total. Pretending those constraints away doesn't make the design better — it makes it unworkable.
The test isn't whether the order matters; it's whether the ordering is a real constraint or a shadow constraint. Real constraints come from physics, business rules the user actually expects, or genuinely irreversible operations. Shadow constraints come from engineering and design decisions: simplifying assumptions, specific UI choices, poor data modeling, shortcuts to ship a feature seven years ago that now require a major refactor, or legacy assumptions carried in from third-party integrations.
The first-name/last-name field is one of the most common shadow constraints in software. At Reva, I pushed the team hard to use full name plus preferred name instead — better UX, better internationalization, what WCAG actually recommends. But every credit and criminal screening service we integrated with required first and last as separate fields. So we built a shim to split a full name on submission, plus a manual override flow for the rare case the split was wrong. It was right about 999 times out of 1,000, which still meant a meaningful number of applicants needed the override. All of that extra design and engineering work existed because of a third-party data model nobody on our team owned. The constraint wasn't real for our users; it was inherited from upstream systems we couldn't change.
Figma's idempotency problems are the same shape, but deeper in the stack. Most of the failure modes are almost certainly direct outcomes of engineering decisions in the override-resolution data model: simplifying assumptions from earlier versions, shortcuts that became load-bearing as the product grew. At this point, some are probably easier to fix with a partial rewrite than a patch. That's the long-term cost of treating idempotency as a series of one-off bugs instead of a property of the system.
Catch It Early or Pay Later
Idempotency, or whatever you want to call this property, almost never makes it onto a design system roadmap. It shows up as individual bugs ("color resets when size changes") that get triaged one at a time and never get connected to the property they share. The property is: the order of independent actions affects the result. Once that's named as a class of problem, the triage changes. Instead of fixing the color-reset bug in isolation, the team asks which other attribute pairs are accidentally coupled.
The catch is that this work pays the biggest dividends when it's done early. Once the data model and the engineering constraints set, fixing them gets exponentially more expensive. Start with a shopping cart table that assumes one account per cart and you've quietly locked out roommates sharing groceries; the DB constraints, the auth checks, and the order-history queries all assume that shape now. DoorDash took years to make office group lunch ordering feel like anything other than a phone passed around a conference room, because the original model didn't anticipate it. Yardi still makes you create a new account with a different email address every time you apply to a property managed by a company you've already rented from. The unique constraint on email is scoped to a single property — a database decision pretending to be a product feature.
The interactions you ship today carry the constraints of the model you commit to. If you're designing a system right now, list the actions that compose into a single state and ask which are genuinely ordered and which only look that way because of how something underneath was modeled. If possible, fix the underlying models before they ship. It's easier to create a many-to-many relationship today that is not used in the UI, than to change that data relationship in the future. Even if you can't make and fulfill the promise to the user right now, leaving the door open gives you options.
Ask how you can apply idempotent design early and often. Make the promise to users in advance instead of begging forgiveness when it feels complicated later.
Top comments (0)