Card 04/ 08

ConceptDifficulty: Intermediate2 min

Error Codes a Client Can Branch On

A message is for people and changes; a stable code is for programs and does not.

Every kind of error needs a stable, machine-readable identifier, such as a problem type URI or a short code like out-of-stock, that a client's program can compare. The message beside it is for people and will be reworded, translated and fixed; the identifier must never change once published, because client code depends on it.

A client branching on the message, and the same client branching on the type

On the messagebreaks when the text changes

if (body.detail.includes("out of stock")). It works until someone corrects the wording to "unavailable", translates it into Spanish, or adds the product name in front.

On the typestable across releases

if (body.type === "https://api.example.com/problems/out-of-stock"). The text can change in every release, and the branch still runs.

One identifier per thing the client does differently. If a client would handle "out of stock" and "product discontinued" differently, they need two types. If it would handle every validation fault the same way, one validation type with a list of fields is enough. Codes too fine to act on are noise; codes too coarse force the client back to parsing text.

Document every type. List each type with its status code, what causes it, and what a client should do. A type URI that resolves to that page in a browser is a convenience for the developer reading a log.

Never reuse or rename. Changing a published type breaks clients as surely as renaming a field. A new kind of error gets a new type. Clients should also treat an unknown type as the general case for its status code, so a new type is not a breaking change.

Not the status code alone. Many different failures share 409 or 422. The status says the class; the type says which failure in it.

So give every error a type a program can compare, document it, and keep the words for people.