Database Convention & Best Practices
Why
Consistency in database naming and design simplifies collaboration, improves performance, and reduces debugging effort. Standards prevent hidden issues and make maintenance easier.
Database Naming Convention
- Use lowercase and underscore for database names, e.g.
ait_app_db. - For multi-environment:
- Development:
{project}_dev, e.g.crm_dev - Staging:
{project}_stg, e.g.crm_stg - Production:
{project}_prod, e.g.crm_prod
- Development:
- For microservices, prefix or suffix with service name, e.g.
orders_service_prod. - Avoid generic names like
test,db, ordata.
Table & Column Naming
Tables
| Recommended Practice | Avoid | Notes |
|---|---|---|
Plural names: users, orders | user, tbl_user | Avoid prefixes like tbl_ |
lower_snake_case: order_items | OrderItems | Keep naming consistent |
Collective names: employees | Vague singular | Clearly represents contents |
Primary key as id, foreign as xxx_id | uid, id_user | Simple and consistent |
Columns
| Recommended Practice | Avoid | Notes |
|---|---|---|
Singular column: name, created_at | Duplicating table name | Avoid redundancy |
| All lowercase | CreatedAt, Created_At | Consistency is key |
Boolean with is_: is_active | active, verified | Boolean intent is clear |
| ENUM for static values | Numeric status | Easier to maintain |
DB-level CHECK constraints | Only application-side | Validation at DB safer |
Timestamp & Audit Columns
- Add
created_at,updated_at,deleted_atwithtimestamp with time zone. - Add
created_by,updated_by,deleted_byfor audit if needed. - Use default values (
now()) and triggers where possible. - Consider
row version(optimistic locking) for concurrency.
Query Optimization Guidelines
- Use
EXPLAINorEXPLAIN ANALYZEfor slow queries. - Avoid
SELECT *, select only needed columns. - Avoid subqueries and large aggregations unless necessary.
- Avoid using functions in
WHEREon indexed columns. - Use
LIMITfor large datasets. - Apply cache for heavy queries (TTL at least 1 minute when possible).
Indexing
- Add indexes for columns used in
JOIN,WHERE, orORDER BY. - Avoid too many indexes; impacts write speed and storage.
- Composite indexes should match query order.
Common Index Use Cases
| Scenario | Example |
|---|---|
| WHERE clause | SELECT * FROM orders WHERE status = 'pending' (status) |
| JOIN conditions | JOIN users ON users.id = orders.user_id (orders.user_id) |
| ORDER BY | ORDER BY created_at DESC (created_at DESC) |
| WHERE + ORDER BY | WHERE user_id = ? ORDER BY created_at DESC (user_id, created_at DESC) |
| GROUP BY/aggregates | GROUP BY status (status) |
| DISTINCT queries | SELECT DISTINCT status FROM orders (status) |
Indexing Anti-patterns
| Scenario | Example |
|---|---|
| Low-cardinality column | Index on boolean like is_active |
| Frequently updated | Index on updated_at |
| Unused columns | Index on internal/debug fields |
| Wrong composite order | Index (a, b) for WHERE b=? AND a=? |
| Indexing all columns | High-write tables |
Tips
- Use
EXPLAINto check index usage. - Partial indexes for subset of rows (PostgreSQL).
- Covering indexes for heavy reads.
- Review and clean up unused/duplicate indexes periodically.
ORM Considerations
- Always review generated SQL from ORM.
- Use
EXPLAINto check real execution plan. - Avoid eager loading unless justified.
- Prevent N+1 queries with proper loading strategy.
- Use parameter binding to prevent SQL injection.
Migration, Seeding, and Access Control
- Use structured migration tools (Flyway, Liquibase, ORM-based).
- Seed scripts must be idempotent, avoid truncating important data.
- Developers should not have
DROP,TRUNCATE, orSUPERprivileges on production. - Audit all schema (DDL) and data change (DML) in production.
- Isolate access roles for dev, staging, and prod.
Data Management & Batch Processing
- Use batch operations for large
DELETE/INSERT(withLIMIT). - Archive old/inactive data to separate tables.
- Partition large tables (>10 million rows).
- Avoid long-held locks.
Monitoring & Performance Tools
- Enable and monitor slow query logs.
- Use tools:
pg_stat_statements(PostgreSQL),performance_schema(MySQL), or APM. - For complex queries, prefer raw SQL over ORM when needed.
Stored Procedure
Stored procedures are discouraged unless:
- Performance cannot be achieved in application layer.
- Logic is reused by multiple consumers of the same DB.
- Strict access control required at database level.
Prefer lightweight SQL functions or keep core logic in application/services.
Race Condition & Locking Strategy
Why It Matters
Race conditions can cause data inconsistency if concurrent changes are not managed. Use locking only when needed, and choose the right strategy for the scenario.
Locking Types
- Pessimistic: Locks row immediately (
SELECT ... FOR UPDATE) - Optimistic: Uses version checking (update only if version unchanged)
| Use Case | Locking Strategy |
|---|---|
| Stock checkout, banking | Pessimistic |
| Profile update, reports | Optimistic |
Tips:
- Avoid long transactions
- Use timeout and retry logic
- Monitor locking with DB tools