TL;DR: Pick one style, document it, and stick to it. If you're undecided, plural tables (users, orders) are a safe default: they read naturally in SQL and dodge a few reserved words. The consistency matters more than which side you land on.
Why it matters
A table name is not just a label. It shows up in your SQL, your ORM models, your migration files, your BI dashboards, and your docs. Once a row of code references users, renaming the table later means touching all of those places at once. So the convention you pick early tends to outlive whatever reasoning produced it. A consistent one lowers the cognitive load for everyone reading the schema and shortens the time it takes a new hire to find their way around.
The two camps
Singular (user, order_item)
Pros
- OOP alignment: a table maps cleanly to a class or entity. One row is one
User, soUser.find(1)lining up with theusertable feels right. - Language simplicity: you avoid irregular plurals, which is a real help for international teams who would otherwise have to guess at English pluralization rules.
- Master-detail reads cleanly:
orderandorder_detailsit naturally next to each other.
Cons
- Reserved words:
user,order,group, andsessionare reserved or near-reserved in several databases, so you end up quoting them or working around them. - Query feel:
SELECT * FROM userreads a bit stilted, like you forgot the rest of the sentence.
Plural (users, order_items)
Pros
- Natural language in SQL:
SELECT * FROM users WHERE age > 21reads the way you would say it out loud. - Reserved-word avoidance:
ordersis safer thanorder,groupssafer thangroup. - Framework affinity: Rails pluralizes by default, and a lot of modern stacks follow the same convention, so plural tends to be the path of least resistance there.
Cons
- Semantic mismatch: a row represents a single user but lives in a table called
users, which can grate if you think in entities. - Irregulars:
person/peopleandchild/childrenintroduce edge cases your pluralizer has to handle correctly.
Industry split (rule of thumb)
| Approach | Common In | Key Advantage |
|---|---|---|
| Singular | Enterprise / traditional systems | OOP consistency; entity focus |
| Plural | Web frameworks / modern stacks | Readability; framework defaults |
There's no universal standard; both conventions are widespread. What matters is a deliberate choice and consistency.
Quick decision guide
A few situations where one side has the clearer edge:
- Greenfield and undecided: go plural. It's the lower-friction default in most modern toolchains.
- Existing codebase: match what's already there. Mixing the two in one schema is worse than either choice on its own.
- Reserved-word risk: prefer plural (
orders,groups) to sidestep the quoting. - Strict DDD shops or heavy OOP mapping: singular can fit better, since your tables line up one-to-one with your entities.
Practical conventions (copy-paste)
If you want a concrete starting point rather than a debate, here's a set that holds up well:
- Tables: plural,
snake_case, sousers,orders,order_items,audit_logs. - Join tables: plural plus plural, alphabetical, so
orders_products,roles_users. - Columns: singular,
snake_case, soid,user_id,created_at,updated_at. - Views and materialized views: prefix plus plural, so
v_active_users,mv_daily_signups. - Reference tables: plural, so
countries,currencies,order_statuses. - Irregular nouns: decide once (always
peopleor alwayspersons) and never relitigate it.
Master-detail naming tips
The master-detail pair is where the singular-vs-plural choice shows up most visibly:
- Singular world:
orderandorder_detailis tidy and reads well. - Plural world (the more common one): use
ordersandorder_items. It keeps the collection metaphor intact. Avoidorders_details, and name the child after the object it holds (order_items) rather than reaching for "detail."
Governance (make it stick)
A convention only helps if it survives contact with a busy team, so put some scaffolding around it:
- Write the rule down in your engineering handbook.
- Add a schema linter or CI check that blocks drift before it merges.
- Include examples for the cases people actually trip on: irregular plurals, join tables, reserved words.
Examples
Plural (recommended default)
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
status TEXT NOT NULL,
total_cents INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE order_items (
order_id BIGINT NOT NULL REFERENCES orders(id),
product_id BIGINT NOT NULL REFERENCES products(id),
quantity INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (order_id, product_id)
);Singular (for strict entity alignment)
CREATE TABLE user (
id BIGSERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE order (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES user(id)
);
/* Beware: user/order can conflict with reserved words in some contexts */Final recommendation
Choose one convention, document it, and enforce it. If you don't have a strong reason to do otherwise, pick plural tables plus singular columns with snake_case, and stop reopening the debate in every PR. A schema that's consistently "wrong" by your taste is still easier to live with than one that's half right both ways.