Developers treat the words in an interface as filler — the last thing you slot in before shipping, copied from whatever the framework scaffolded or whatever felt vaguely professional. So the button says Submit, the failure says An error occurred, and the confirmation dialog asks Are you sure? about something the user has no way to be sure about. Each of those is a tiny abdication. You spent two days on the data layer behind that button and four seconds on the word stamped across its face.
That word is the interface. It is the part the user actually reads, the only instruction they get at the moment of action, and the cheapest thing in your entire stack to fix. You don't need a redeploy of your backend or a redesign of your layout. You need to change a string. Microcopy is the highest-leverage UX work you can do, and most builders skip it entirely because it doesn't feel like building.
The reason microcopy gets neglected is that it doesn't look like a problem. The button works. The form posts. The error fires. Nothing is broken, so nothing gets fixed. But "works" and "communicates" are different bars, and the gap between them is exactly where your product starts feeling like an internal tool instead of a product someone would pay for.
Watch what generic copy actually does to a user. They click a button labeled Submit and they have to guess what they just did — did it save a draft, send an email, charge a card? They hit An error occurred and they have no idea whether to retry, change their input, or give up. They read Are you sure? and they pause, because the dialog is asking them to confirm a decision it refused to describe. Every generic word forces the user to do work that the word was supposed to do for them.
The deeper trap is treating copy as decoration that sits on top of the interface rather than as the interface itself. A button with no label is not a button. A form field with no label is a riddle. The words aren't a coat of paint over the controls — they are the controls, expressed in the only language the user reads.
You spent two days on the data layer behind that button and four seconds on the word stamped across its face.
Here's the part that should sting and motivate in equal measure: this is the cheapest UX in your product. Visual hierarchy means restructuring a layout. Performance means profiling and refactoring. Copy means editing a string and saving the file. The return on a careful sentence is enormous precisely because the cost is nearly zero.
The single most common microcopy failure is the button that names the mechanism instead of the result. Submit describes what the form does to an HTTP request. It tells the user nothing about what happens in their world. The fix is to name the outcome the user is about to receive.
Buttons and microcopy should name the result the user gets — "Create invoice", "Send reset link", "Delete project" — not the mechanism that produces it ("Submit", "OK", "Confirm"). The label is a promise about what happens next. If the user can predict the consequence of clicking from the words alone, the label is doing its job.
The test is simple. Read the button label out of context, with no surrounding screen. Can you tell what will happen when it's clicked? Submit fails. Save changes passes. OK fails. Send invitation passes. The label should be a verb plus the noun it acts on — the action and its object, named in the user's vocabulary, not your codebase's.
Look at the difference across a few common surfaces:
| The mechanism (avoid) | The outcome (use) |
|---|---|
| Submit | Create account |
| OK | Save changes |
| Submit | Send reset link |
| Confirm | Delete 3 files |
| Submit | Publish post |
| Yes | Cancel subscription |
This costs you nothing but attention. In a React component, the entire fix is one prop:
// Before: the mechanism
<button type="submit">Submit</button>
// After: the outcome, named as a promise
<button type="submit">Send reset link</button>
Naming the outcome also clarifies your own thinking. When you force the button to describe what it does, you sometimes discover the button does too much, or does something the user wouldn't expect, or sits next to three other buttons that all say Submit and now have to be told apart. This connects directly to One Primary Action — once each button names its outcome, the one button that matters becomes obvious, and the secondary actions stop competing for the same generic word.
A button label is a promise about what happens next; name the result, never the mechanism.
There's a destructive-action corollary. For anything irreversible — delete, cancel, archive — the outcome label should be specific enough to make the user pause for the right reason. Delete is weaker than Delete project, which is weaker than Delete project "Q3 Launch". The more weight the action carries, the more the label should spell out exactly what is about to vanish.
If the button says Send invoice but clicking it only saves a draft, you've made the label a promise you don't keep — which is worse than a vague label. The outcome must match what actually happens. When in doubt, the button names the literal next state, not the eventual goal.
Generic errors are where products go to feel broken. An error occurred. Something went wrong. Invalid input. These are not messages; they are the absence of a message dressed up as one. They tell the user that you knew a failure happened and chose not to explain it.
A useful error answers three questions, in plain language, in this order: what happened, why, and how to fix it. Most failed errors answer zero of the three. Most acceptable errors answer one. A good error answers all three in a sentence or two.
The pattern is mechanical once you see it. Take any error you currently throw and rewrite it against the three questions:
What: We couldn't save your changes.
Why: Your session expired.
Fix: Sign in again — your draft is kept.
You rarely need all three sentences spelled out separately. The skill is compressing them into one natural line. "Your session expired — sign in again and we'll keep your draft" carries what, why, and how in fourteen words. "That username is taken. Try another." does it in five.
Two rules keep error copy honest. First, never blame the user — write "That date is in the past", not "You entered an invalid date". The user isn't invalid; the input needs adjusting, and your job is to point at the field, not the person. Second, never expose the machine — a raw stack trace, an HTTP status code, or a NullPointerException in front of an end user is a confession that no one translated the failure into human terms.
Validation errors belong next to the field that caused them, not in a banner at the top of the form. A message that says "Required" under the empty field is worth more than a paragraph at the top listing every problem. This is the same instinct behind The Field Tax — reduce the work the user does to recover, the same way you reduce the work they do to complete.
This is also the moment to point at The Four States Rule. Every data view needs an empty, loading, error, and ideal state — and the error state is made almost entirely of words. The Four States Rule tells you that you need an error design; Label the Outcome and the what-why-fix pattern tell you what the words in it should say. A beautifully designed error state with the copy "Something went wrong" is still a failure. The visual is the frame; the sentence is the picture.
A raw stack trace in front of an end user is a confession that no one translated the failure into human terms.
The small surfaces are where confusion is cheapest to prevent and most often ignored. Three of them deserve named attention.
Empty states are a first impression, not an error. When a list has no rows yet, the worst thing you can show is a blank panel or the word No data. That tells a new user nothing about what the feature is for or how to start. A good empty state names what will live here and gives the one action that fills it: "No invoices yet. Create your first one to get started." plus a button labeled — naturally — Create invoice. The empty state is where The Four States Rule and microcopy meet most directly: it's a state defined almost entirely by its sentence.
Helper text is the line under a field that prevents an error before it happens. It is not the label and it is not a placeholder. Use it to set expectations the user can't guess: password rules, format requirements, what an unfamiliar field means. "Use 8+ characters with a number" under a password field saves a round-trip through a validation error. The rule is that helper text should reduce uncertainty, not restate the obvious — "Enter your email" under a field already labeled Email is noise.
<label for="pw">Password</label>
<input id="pw" type="password" aria-describedby="pw-hint" />
<p id="pw-hint">Use 8 or more characters with at least one number.</p>
Putting the field name only in the placeholder, so it vanishes the moment the user types, is a recurring footgun. The user forgets what the field was, screen readers handle it poorly, and a half-filled form becomes a memory test. Keep a visible label; use the placeholder for a format example like name@company.com, or skip it entirely.
Confirmation dialogs are where Are you sure? does its worst work. The dialog interrupts the user, so it owes them a clear question and clearly labeled choices. Are you sure? with OK and Cancel forces the user to reconstruct what they were doing and then guess which button does the scary thing. The fix is to put the outcome in both the question and the buttons.
Title: Delete "Q3 Launch"?
Body: This removes the project and its 14 tasks. You can't undo this.
Buttons: [ Keep project ] [ Delete project ]
Notice the button labels. Not OK and Cancel — Delete project and Keep project. Each names its outcome, so a user can act correctly while skimming, which is how people actually read dialogs. The dialog stops being a trap and becomes a clear fork in the road.
Once the structural problems are fixed — outcomes named, errors made actionable, small surfaces filled — what's left is voice. Voice is consistency and brevity and the simple decision to sound like a person rather than a system dialog.
Consistency first. Pick a register and hold it across every screen. If one button says Save and another says Update and a third says Apply changes for the same kind of action, the inconsistency reads as carelessness even when each word is fine alone. Pick the verb you'll use for saving, the verb for deleting, the verb for creating, and use them everywhere. This is the copy version of Decide Once, Reuse Forever: a small dictionary of decided words, reused, instead of a fresh improvisation per screen.
Brevity second. Cut every word that isn't carrying weight. "In order to" is "to". "Please be aware that" is nothing — delete it. "You are now able to" is "You can". Microcopy is read in a glance, often by someone mid-task who doesn't want to read at all. The shortest version that still answers the question wins.
Tone third. Write the way you'd say it to a colleague standing next to you. The corporate, passive, machine-translated register is a habit you can drop one string at a time, and dropping it is most of what makes copy feel finished. Run your strings through this swap:
Operation completed successfully. → Saved.
This action is irreversible. → This can't be undone.
Input validation failed. → That email doesn't look right.
User authentication required. → Sign in to continue.
You wouldn't say "Operation completed successfully" out loud — you'd say "Saved" or "Done". You wouldn't say "User authentication required" — you'd say "Sign in to continue". Every string in your app is a line of dialogue; read it the way a person would speak it.
One caution on personality: a little warmth goes a long way, and jokes age badly. The goal isn't to be clever; it's to be clear and human. Skip the exclamation points, skip the forced enthusiasm, and never put a joke in an error message — the user reading it is already frustrated, and humor at that moment lands as mockery.
Consistency and brevity do more for your voice than personality ever will; sound like a person, not a system dialog.
The reason all of this works is that it's cheap and the user feels it instantly. They can't see your clean architecture, but they read every word on the screen. The copy is the part of your craft that's exposed, and a careful sentence signals that someone was paying attention everywhere else too.
Search your codebase for button labels — Submit, OK, Confirm, Yes. Replace each with a verb-plus-object that names the result: Create account, Send reset link, Save changes. Success: every button passes the out-of-context test — you can tell what it does from the label alone.
Pick the error users hit most. Rewrite it to answer what happened, why, and how to fix it, in one or two plain sentences. Remove any stack trace, status code, or blame. Success: a non-technical user could read it and know their next move.
Take a single screen and read every word aloud as if to a colleague. Replace anything that sounds like a system dialog — "Operation completed", "Invalid input" — with how you'd actually say it. Cut filler words. Success: nothing on the screen sounds like it was written by the database.
Find a list or view that's blank for new users. Replace the blank panel or "No data" with a sentence naming what lives there plus one button to fill it. Success: a first-time user knows what the feature is for and how to start.
Make a short list of the actions in your app — save, delete, create, cancel — and pick one word for each. Find and replace inconsistent variants across the codebase. Success: the same action is named the same way on every screen.
The copy is the part of your craft that's exposed; a careful sentence tells the user someone was paying attention everywhere they can't see.