Writing the code that ships a design changes how you draw it. Once you have implemented a layout, you stop drawing things that are quietly impossible and start drawing things that survive contact with real data.
The messy middle
You stop designing perfect states and start designing for loading, empty, error, and offline — the states that never make it into a portfolio shot but define the real experience. A screen is only as good as its worst state.
The empty state is the one most designers skip and most users see first. A new account is, by definition, empty. The first impression of your product is the state you spent the least time on.
Designing with the grain of the platform
Knowing how something is built tells you what is cheap and what is expensive. A native control is free, accessible, and battle-tested. A custom reimplementation of that same control is a tax you pay forever.
// Cheap, accessible, and correct on every platform.
function SubmitButton({ pending }: { pending: boolean }) {
return (
<button type="submit" disabled={pending} aria-busy={pending}>
{pending ? "Saving…" : "Save changes"}
</button>
);
}
Every attribute there — type, disabled, aria-busy — does work that a div would force you to rebuild by hand, usually worse.
The reverse is also true
Designing taught me that the best engineering decision is often the one that removes a screen entirely. The fastest interface is the one you never had to build, and the most reliable code is the code that does not exist.
Moving between the two roles is not context-switching. It is the same job seen from two ends, and each end keeps the other honest.