An OpenAPI document can make an API look settled. The endpoints are listed, the examples work, and generated clients compile. It is tempting to take that as proof that client and server agree.
Then somebody renames a response field in a refactor. The service's own tests pass because they changed with the code. An older client still expects the old field, and discovers the disagreement after the deployment. The API definition was accurate when it was written. It just was not protecting anyone.
That is the difference between having a schema and having a contract.
What does the contract actually cover?
The contract is the agreement at the wire. It says which operation a client can call, how it sends a request, which values are accepted, what comes back, and how errors are represented. OpenAPI, protobuf, and Thrift are useful ways to write that down. They do not force a running service to honor it.
For a client, the important question is simpler: can I build against this definition and keep working through the next deployment? That is the promise.
The specification describes the promise. The running boundary is where the promise is kept or broken.
Put the check where ownership changes
Validate a request as it enters the service: its content type, required fields, types, bounds, and declared values. Validate a response before it leaves. The same applies to messages on a queue and calls between internal services. An internal API still has callers that can be broken by an uncoordinated change.
The boundary is the right place because it gives each side a clear job. A caller sends a representation that meets the contract. The service either accepts it or returns a declared client error before business logic begins. Inside the boundary, normal types and domain code can do their work without having to guess whether a value was already malformed on arrival.
There is no reason to validate every object repeatedly or adopt one validator everywhere. The point is to make the contract check happen once, at the point where a different owner hands data to the service.
Request validation is only half the job
Most examples stop at rejecting bad input. That is useful, but it leaves the more common breaking change untouched: the server returns something a client does not expect.
A request validator will reject an invalid amount. It will not catch a handler that forgets currency, changes total from a number to a string, or replaces a documented error body with whatever a new framework emits by default. Each response can return 200 and still break clients.
Response validation catches that class of change while it is still local. It can run in production where the overhead is acceptable, at the edge of a service, or in the integration path that qualifies a release. The exact placement is an operational choice. The contract being checked in both directions is the important part.
A checkout request should fail early
Take a checkout endpoint that accepts a positive amount and a currency from the merchant's supported set. A client sends a negative amount, or a currency the merchant cannot process.
Without a contract check, that value can reach pricing code, be coerced into something unintended, or fail later as a generic server error. The client has to work out whether it sent a bad request or whether the service is broken.
With an enforced contract, the request stops at the boundary. The client gets a stable error that identifies the invalid field. No order is created, and the failure is cheap to understand and correct.
Now run the failure in the other direction. A deployment renames a checkout response field while an older generated client still expects the original one. The server may be healthy by every process-level measure. The integration is not. Validating the response against the published contract exposes the break before it becomes the client's production incident.
Schema validity has a limit
Schema checks are deliberately narrow. They establish that the request and response have the agreed shape. They do not establish that the caller may make the request, that an account has credit, that a payment was processed exactly once, or that a balance is current.
Those need authorization, business rules, idempotency, and health checks. A checkout request may have every required field and still be forbidden. A perfectly shaped response may contain stale data. Keeping these checks separate makes it possible to say what each one guarantees and to diagnose the right failure when it does not.
The cost is treating observable change seriously
Runtime enforcement costs some work on the request path, and it makes the schema part of normal product maintenance. That is a real cost. It is also the cost of admitting that a field clients depend on is no longer an internal detail.
Adding a compatible optional field is usually straightforward. Renaming or removing a field requires a migration, a versioned endpoint, or a coordinated release. Compatibility tests and generated-client checks should catch those decisions before runtime. Enforcement is the last guard in that chain, where the client finally meets the service.
An API is dependable when its published constraints survive contact with the deployed code. A document that is never checked is still useful. It just asks clients to trust that it has not drifted.
The one-line version
An API contract becomes real when the running service enforces the request and response shapes its clients depend on.