Why Should Protocol Errors Have Stable Codes?
From the earlier reference collection. Read the current software blog.
Stable error codes let clients respond consistently while giving operators clear, human-readable explanations.
Separate meaning from wording
An error response serves two audiences. A client needs to decide what to do next; an operator needs to understand what went wrong. If software makes decisions by searching a sentence such as “account is temporarily unavailable,” a copy edit or translation can change its behavior. A stable code gives the condition a durable identity, while a message can explain the current circumstances in ordinary language.
For example, a service might return code RATE_LIMITED with the message “Too many requests for this account; try again after 14:32 UTC.” A client can recognize the code and apply its retry policy. A support engineer can read the message and see the relevant account and time. The code remains the same if the wording changes or the message is translated.
Define behavior, not just labels
A code is useful only when its meaning is documented. State whether RATE_LIMITED is temporary, whether the response includes a retry delay, and whether the client should reduce its request rate. Define how clients should treat an unrecognized code too; a safe default is to stop the failed operation and show the supplied explanation rather than guessing that it is retryable.
Also distinguish conditions that require different client behavior. INVALID_CREDENTIALS may prompt the user to sign in again, while INSUFFICIENT_SCOPE may require an administrator to grant permission. Collapsing both into AUTH_ERROR can make a client offer the wrong remedy, even if the operator message is precise.
Keep the contract stable
Once clients depend on a code, changing its meaning can break them without changing the message schema. Avoid reusing a retired code for a new condition. If a condition needs a different response, introduce a new code and describe the compatibility implications. The code set can grow over time; older clients should have a defined fallback for values they do not recognize.
A practical error object might contain code, message, and optional structured details such as a field name or request identifier. Document which fields are always present, which can be omitted, and whether details are safe to display. Do not make clients parse identifiers out of the prose.
Make failures diagnosable
Good error design reduces brittle integrations and shortens diagnosis. Test it by asking what an old client does when it receives a new code, what a user sees when a message is translated, and whether an operator can connect the response to a request. A protocol should make the code carry the machine-readable meaning and the explanation carry the context. That division keeps automation predictable while leaving room for clear, helpful language.