Skip to main content

Writing

The words are part of the interface

An error message is the highest-value writing in any product, and the place it is most often skipped. Three levels decide what is settled here and what belongs to a product. German is the default; English is the documented alternative, and both are produced by the same formatter.


The three levels

Knowing which level a decision belongs to prevents the two common mistakes: putting a product-specific choice into the shared standard, and re-deciding a shared rule in every product.

The three levels of writing decision
Level Scope Decided Covers
1 — System Every product Once, here Terminology for system concepts, the wording of shared components, formats, accessibility rules. Not negotiable per product.
2 — Fundamentals Every product Once, here Principles, gender-fair language, sentence construction, number and date formats, text accessibility. A product may add to it, never contradict it.
3 — Product voice One product Per product Form of address, tone, domain vocabulary, how much personality.

Form of address is level 3, and one decision

German distinguishes Sie and Du; English distinguishes register rather than pronoun. It is a product decision — but one decision per product, never mixed within an interface.

  • Public and customer-facing: German Sie; English formal-neutral — “Enter your email address”.
  • Internal tools for colleagues: German Du reads as collegial; English stays the same and gets terser.

A product's own guide fills the level-3 slot in agent/consumer-AGENTS.template.md, which is deliberately empty here — Dessau DS has no voice of its own to impose.

Principles

  1. Clear over clever. One idea per sentence. Active voice. Concrete terms.
  2. Only as much text as the decision needs. Every extra sentence is a sentence between the reader and what they came to do.
  3. One concept, one term. Never a synonym for variety. If it is a “project” once, it is a project everywhere — not a “workspace” in the next sentence.
  4. Describe what the reader does or experiences, not what the system does. “Your changes are saved”, not “The system has persisted the changes”.
  5. Calm and factual. No alarm language, no exclamation marks, no judgement of the reader. Nothing “unfortunately” happened, and nobody “failed” to do anything.
  6. Say what to do next. Particularly in errors and empty states. A message that only reports a state is half a message.
  7. Never all-caps as a stylistic device. It costs legibility, defeats word-shape recognition, and some screen readers read genuinely capitalised text letter by letter.

Gender-fair language

Binding at level 2, in both languages.

German — prefer genuinely neutral over any marker

Neutral German formulations and what they replace
Prefer Instead of
Nutzende, Bearbeitende, MitarbeitendeBenutzer, Bearbeiter, Mitarbeiter
das Team, die Person, allejeder Mitarbeiter
Wer ein Projekt anlegt, …Der Nutzer, der ein Projekt anlegt, …

No generic masculine. Where a neutral term is genuinely unavailable, a gender star (Nutzer*innen) is acceptable as a second choice — a fallback, not the default, because screen readers handle it inconsistently.

Often the simplest fix removes the problem entirely: address the reader directly. “Dein Projekt” rather than “das Projekt des Nutzers”.

English

Singular they for a person of unstated gender. Never he/she, and never he as a generic. Address the reader as you wherever possible.

Numbers, dates and units

German is the default. Produced by DDS.format, never assembled by hand — the separators, the spacing and the order all differ per locale, and assembling them yourself gets one of the three wrong.

Locale
Formatted values, produced live by DDS.format in the selected locale
Thing Formatted DDS.format
Number number()
Amount currency()
Percentage percent() — takes a ratio
Short date date()
Long date dateLong()
Time time() — “ Uhr” is added in the copy
Relative relativeTime()
File size fileSize()

Rendered by DDS.format through Intl, not written into this page. Switching the locale calls DDS.format.setLocale() and re-renders — so what you see is what a product would get.

The four that bite

percent() takes a ratio (0.195), not a percentage. That is the Intl convention, and it avoids the bug where a value is divided by a hundred twice — which produces a plausible small number rather than an obvious error.

Parse with DDS.format.parseNumber(), never parseFloat. parseFloat reads the German 1.234,56 as 1.234 — a silent, plausible, wrong answer, and off by a factor of a thousand.

The narrow no-break space between a number and its unit is deliberate. Never “clean it up”: a value that wraps between the number and its unit is read as two separate things. Use .dds-nowrap for any multi-part value — an account identifier, a phone number, an amount with its unit.

24-hour time in both languages. German appends “ Uhr” in running text; that is a writing rule rather than a formatting one, so it belongs in the copy and not in the formatter.

Errors

The highest-value writing in any product. Structure: what is wrong, then what to do about it. A message that stops after the first half has told the reader they are stuck without telling them how to stop being stuck.

Error messages rewritten
Instead of Write
“Invalid input” Enter an email address, for example name@gmail.com
“Please match the requested format” Enter the reference code as four characters, a hyphen, then four more — for example 7K4M-92QX
“Error 500” We could not save your changes. Your text is still in the form — try again in a moment.
“Field required” Enter your full name
“Ungültige Eingabe” Gib eine E-Mail-Adresse ein, zum Beispiel name@gmx.net

Rules, in order of how often they are broken

  • Distinguish “the service failed” from “there is no such thing.” One says try again, the other says check the spelling. Collapsing them leaves the reader guessing, and it is the single most common failure in search and lookup. It is also why a provider must reject rather than resolve empty.
  • Never blame the reader. No “you failed to”, no “invalid”, no “illegal”.
  • Say whether their input survived. “Your text is still in the form” removes the fear that retrying means retyping.
  • The hint stays. The reader needs the format rule and the failure — not one replaced by the other.
  • Never reveal whether an account exists in an authentication error, and match the response time in both cases.
  • Summary wording matches per-field wording. Two descriptions of one problem is one too many — see the error summary.

Per element

Labels

  • A noun phrase, not a question. “Email address”, not “What is your email address?”
  • Never a placeholder instead of a label. It vanishes on input, fails contrast, and is announced inconsistently.
  • Required stated in words — (required) / (erforderlich). Not an asterisk, not colour.
  • Mark the shorter set: if most fields are required, mark the optional ones.

Buttons

  • A verb naming the action: “Save changes”, “Delete project”, “Änderungen speichern”.
  • Not “OK”, “Yes”, “Submit”, “Confirm”. “Confirm” beside “Cancel” tells the reader nothing about what is about to happen.
  • The label answers “what happens if I press this?” — so a dialog's confirm button repeats the action, not the title.
  • Destructive actions name the thing: “Delete project”, not “Delete”.
  • A trailing ellipsis means a further step follows — “Rename…” opens a field, it does not rename on the spot. Leave it off an action that runs immediately, even a destructive one.

Hints

  • The rule or the format, before the reader gets it wrong. “Format: 7K4M-92QX”.
  • Never repeat the label.
  • Present in the valid state and the error state alike.

Warnings and success

  • State the consequence, not the severity. “Two collaborators will lose access”, not “Warning: permissions change”.
  • Warnings before an action; errors after one.
  • Success confirms what happened and where it now is. Do not congratulate the reader for using the software.

Empty states — two different situations

  • Nothing yet. Explain what this is for and offer the first action.
  • Nothing found. Confirm what was searched and offer a way to broaden it.
  • Never a bare “No data” or “Keine Daten”. See empty states.

Loading

  • Say what is happening, not that something is. “Searching addresses…”, not “Loading…”.
  • Announce it in a live region — a spinner communicates nothing to a screen reader.
  • Debounce. Do not announce on every keystroke.

Destructive actions: never “Are you sure?”

It asks the reader to confirm a decision without giving them the information to make it. Name the scope and the consequence instead: “This removes the project and its 47 documents for everyone. It cannot be undone.”

Say plainly whether it can be undone. The confirming button carries the verb; the cancelling one says what happens instead — “Keep project” reads better than “Cancel”, because “Cancel” is ambiguous next to a cancellable action.

Accessibility in text

Level 2, binding. Most of it is about text read out of context — which is how a screen-reader user encounters a link list, a button, a table cell.

  • Link text says where it goes. Never “click here”, “here” or “read more” alone — someone listing the links on a page hears “read more” six times. “Read the setup guide” works out of context.
  • An accessible name must contain the visible label (WCAG 2.5.3), so a speech-input user can say what they see.
  • Never abbreviate a name in an accessible label. “IB” is not a name.
  • Icon-only controls carry a name; the icon itself is aria-hidden.
  • A link that opens in a new tab says so in a visually hidden span.
  • Write text that can be read aloud. Avoid constructions that depend on layout — “see the box on the right” — and never rely on visual position alone.
  • Expand or explain a term of art the first time it appears.
  • Give a status a word, always. Never only a coloured dot.

Example data

Demo, placeholder and test content is part of the product's quality — and it is the cheapest way to find a whole class of bug before a user does.

Do

  • Realistic and invented — plausible names and addresses that identify nobody.
  • Deliberately include diacritics and non-ASCII: ø, ä, ç, ł, ș, ü, å, İ.
  • Vary the length. A long street name and a long surname are what find truncation.
  • Include the awkward-but-real case: an address with no house number, a single-character surname.
  • Reserve example.org for an address that is technically live — a mailto default, an href, a value that could actually be submitted.
  • Everywhere an email is only read — demo data, format examples in error copy — use a real consumer domain: gmail.com, gmx.net, outlook.com, web.de, orange.fr, with an invented local part.

Don’t

  • Cliché placeholders. They signal unfinished work and hide real layout problems.
  • Lorem ipsum — it has the wrong word lengths and no diacritics at all.
  • Real personal data, in demos, tests, fixtures, screenshots or commit messages.
  • A placeholder domain that could become real.
  • An example.org address in text a person only reads — a demo table, an error message — it reads as a leftover placeholder.

Why the diacritics are the point

A broken charset, a bad sort order, a truncating column, a font missing glyphs — every one of those is invisible in ASCII test data and obvious the moment the data contains ø and İ. Choosing bland example data is choosing not to find those bugs.

example.org vs. a real provider

example.org is reserved for an address that is technically live — a mailto default, an href, a value that could actually be submitted or dialed. That is the only case the resolve-guarantee buys anything. Everywhere else the address is only read — a demo user list, the format example inside an error message — invent it on a real consumer domain instead. Nobody dials "name@gmail.com" in an error message; the realistic domain is what makes it recognisable, where example.org reads as a leftover placeholder to a reader who has never heard of the reserved TLD. A demo table or an error message that uses example.org reads as staged — the cliché placeholder rule above, applied to domains.

When the wording is not obvious

  1. What does the reader need to know to do the next thing?
  2. Write that, in one sentence, in the plainest words available.
  3. Remove everything that is not it.
  4. Read it aloud. If it sounds like software talking about itself, rewrite it from the reader's side.
  5. Check the level: system rule, fundamental, or this product's voice? Put it in the right place.