Naming disagreements are some of the longest-running arguments in code review, precisely because both sides are usually defensible. A written standard doesn't need to pick the objectively correct answer — it needs to pick one answer, once, so the same argument doesn't happen on every pull request.
Key takeaways
- snake_case is the safest default across dialects because it needs no quoting anywhere — PostgreSQL and BigQuery lowercase unquoted identifiers, so camelCase behaves differently depending on whether it's quoted.
- Singular vs plural table names is a genuine split across real codebases — the only wrong answer is mixing both in the same schema.
- Reserved words as identifiers force quoting forever, in every query, by everyone who touches the table.
- A one-page naming doc catches drift years before a “rename order_id to orderId everywhere” migration becomes the only fix.
snake_case vs camelCase
PostgreSQL and BigQuery both fold unquoted identifiers to lowercase, so an unquoted customerId column becomes customerid the moment it's created — fine until someone writes a quoted "customerId" reference expecting it to match, and gets a column does not exist error instead. snake_case sidesteps the whole problem: it's the same whether it's quoted or not, on every dialect covered here.
Singular vs plural table names
Singular (order) reads naturally as a model name in ORM code and matches a single conceptual row; plural (orders) reads naturally in a raw SQL FROM clause, where you're always selecting a set. Both are common in real schemas — the only rule that matters is picking one and applying it everywhere, including junction tables.
Primary and foreign key naming
A plain id for the primary key, and {referenced_table_singular}_id for foreign keys, is the most common pattern — a column on makes the relationship obvious from the column name alone, without needing to check the constraint to know what it points to.