Validation is one of the places where a product makes a claim about reality.
The code may look like plumbing: check a type, trim a string, reject a value. But each rule answers a product question. What counts as a name? When is a date complete? Which files belong in this workflow? Is a partially typed value invalid, or merely unfinished?
Those decisions become visible at the edges of the system. A number field may accept 0, while a title field must reject an empty string. A filename may contain spaces but must not escape its directory. A date can be valid according to the browser and still fail because the booking window has closed.
Consider a workspace that requires lowercase, hyphenated, unique project slugs. A useful contract is specific:
New Projectfails because it contains spaces and uppercase characters.new_projectfails because underscores are outside the format.new-projectfails when another project already owns it.
The interface can explain those rules before submission. The server still has to enforce them after submission. If the browser says a slug is available and the server rejects it without a usable explanation, the product has presented two different contracts.
The server boundary is where validation stops being a UI preference. It protects the system, but it also states what the system believes. That belief should be legible in the response: “slug already taken,” “file type must be PDF,” or “date must be within the next 30 days” gives a person a path forward. 400 Bad Request mostly gives them another attempt.
Good validation narrows ambiguity without making ordinary work mysterious. The rule should be strict where the system needs certainty, flexible where input is naturally incomplete, and explained in the same terms the product uses everywhere else.
Related posts

About Jeremy London
Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.