I’ve talked to a lot of designers who are transitioning into design engineering.
Many of them have been learning to prototype with Claude Code, or set up agents to automate routine tasks, which are excellent.
But a really excellent design engineer doesn’t just ship like an engineer. They also think like an engineer, and a really good one at that.
Non-exhaustively, here are a few engineering thoughts that have helped me as I increasingly juggle between design and engineering:
Every action births an invisible technical multiverse of outcomes.
We often design similar amounts of success and error flows, but unlike a few possible successes, errors don’t add, they multiply. Every possible error might occur alongside every other possible error, and that all those exponential universes need to be accounted for in some way.
These don’t just include errors that you can debug on localhost. They might happen from different devices or operating systems, caching errors, server issues or buffer overload, security and memory leaks, geofencing or location bans, and so on.
Engineering is messy. A lot of it is just about mitigating insanity. For every “built out” user flow whose success state seems complete, you’ll need to often step back to thoughtfully consider a dozen resulting universes of possible errors, unknown edge cases, and failed states.
If design is not how it looks, but how it works, engineering is not just how it works, but how it tests.
Scheme twice, code once.
Beyond basic websites and web apps, you’re going to deal with increasing complexity. You might squash bugs out as you go, but there’s a point where you can’t outrun a bad architecture or system design.
Instead, lay out all your assumptions, connections, and needs ahead of time and diagram them. Where are things reused? Which features share dependencies? Where is data shared, and how do permissions work?
Even if you don’t know all the answers, they’re good to ask to figure out the scope of what you’re doing, and where you need to investigate best and most applicable practices.
The architectural demands of scale.
After you go beyond prototyping and start shipping to prod, you might be serving thousands or even millions of users. At that point, things start to break down.
APIs feel instant until users start queueing and latency compounds. State feels local until it has to sync across regions and devices.
At that point, you'll need to consider the physical limits of software. Memory isn’t infinite. Networks have physical boundaries. Tokens and storage costs balloon. Edge cases may grow unreasonably common, and you might even have to design for resource-scarce states.
Engineering is opinionated, too.
Engineering cultures are just like design cultures. Every engineer and engineering organization will have a different relationship with their codebase and its abstractions.
Some places will care a lot about performance or code cleanliness; other places care most about speed or user feedback. Some might be extremely purist in their use of dependencies, while others are library and framework monkeys.
It’s good to adapt the “culture” of engineering you use in different scenarios. For prototyping, think fast and loose; for maintainability, think methodical and systems-heavy.
Care about speed, performance, and size.
Finally, as you get really good at this stuff, mind the meta layer: things users don’t see, but feel.
Latency is a design problem. A 50ms delay feels instant; 300ms feels sluggish; a second feels broken. Technical issues can erode user love before it has the chance to spark.
Speed feels simpler, more intuitive, even more “beautiful,” regardless of how it actually looks.
And size matters more than you think. Every dependency, every uncompressed image, and every line of code is a cost on load time and memory. The easiest way to make something fast is often just to make less of it.