Every team eventually ships a payload like this:
{
"userId": 42,
"created_at": "2026-09-01T10:00:00Z",
"IsActive": true
}
Three fields, three conventions. It passes code review because every line looks fine in isolation. It breaks in production because every consumer now has to special-case field names.
The failure mode is silence
The expensive version of this bug does not throw. Your backend is written in Python with snake_case columns, the frontend is TypeScript and sends camelCase, and the serializer you assumed existed... does not. So userId arrives, nothing reads it, and the handler writes user_id: null. No 422, no stack trace — just rows that look wrong three weeks later.
Both ends were internally consistent. That is what makes it survive review: naming style is a contract between two codebases, and nothing in either repo checks the contract.
Where the two styles come from
It is not a style disagreement, it is two ecosystems meeting:
- SQL, Python and Ruby idioms use snake_case:
user_id,created_at. - JavaScript, Java and C# idioms use camelCase / PascalCase:
userId,createdAt.
So the boundary belongs to one explicit layer — a serializer, a DTO, a mapper. When that layer is missing, ORM column names leak straight into your public JSON, and the API's naming becomes whatever the database happens to use.
Round-trip it, don't eyeball it
Pick one convention per boundary and assert it in a test. Cheapest version: serialize a sample object, convert each key, and compare. Or snapshot one real response body in CI, so an accidental created_at shows up as a diff instead of a support ticket.
Two traps that break naive converters:
-
Acronyms split inconsistently.
parseHTTPResponsebecomesparse_httpresponseorparse_http_responsedepending on the rule. Round-trip both directions in a test before trusting a bulk rename. -
Filesystem casing. Renaming
UserService.tstouserservice.tsbuilds fine on a case-insensitive macOS volume and fails on a case-sensitive Linux CI. Same root cause: casing treated as decoration instead of part of the name.
The practical rule
One canonical spelling per layer — user_id in Postgres, userId in JSON, USER_ID in the env file — and no variants in between. When I need to flip a list of identifiers between conventions (column names → JSON keys, or the reverse before a migration), I use the Case Converter at codetoolbox.pro — paste the whole list once and it returns nine casings at the same time, running entirely in the browser, so nothing internal leaves the machine.
What naming convention does your team enforce — and do you enforce it with a test, or with a convention doc nobody rereads?
Top comments (0)