database design
software engineering
overengineering
beginner guide
question closed

First-time database design am I overengineering?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

First-time database designers often worry that they are either oversimplifying the schema or overengineering it. The usual answer is to design for the real use cases you know today, enforce clear data rules, and avoid adding tables, abstractions, or flexibility that the application does not yet need.

What Overengineering Looks Like

A database is overengineered when its structure is noticeably more complicated than the current problem requires. That usually shows up as too many tables, highly abstract relationships, or "future-proofing" that makes everyday queries harder without solving a real present need.

Typical warning signs include:

  • splitting obvious entities into many tiny tables too early
  • designing for multiple product lines when you only have one
  • adding generic metadata systems instead of normal columns
  • optimizing for massive scale before usage exists

Good schema design is not about using every advanced idea you know. It is about making valid data easy to store and invalid data hard to store.

Start with the Main Business Facts

A good beginner strategy is to write down the facts your application must remember. For a simple store, that might be:

  • customers
  • products
  • orders
  • order items

That naturally suggests a relational design:

sql
1CREATE TABLE customers (
2    id SERIAL PRIMARY KEY,
3    email TEXT NOT NULL UNIQUE,
4    name TEXT NOT NULL
5);
6
7CREATE TABLE products (
8    id SERIAL PRIMARY KEY,
9    name TEXT NOT NULL,
10    price NUMERIC(10, 2) NOT NULL
11);
12
13CREATE TABLE orders (
14    id SERIAL PRIMARY KEY,
15    customer_id INT NOT NULL REFERENCES customers(id),
16    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
17);
18
19CREATE TABLE order_items (
20    order_id INT NOT NULL REFERENCES orders(id),
21    product_id INT NOT NULL REFERENCES products(id),
22    quantity INT NOT NULL CHECK (quantity > 0),
23    PRIMARY KEY (order_id, product_id)
24);

This is not simplistic. It is a clean design based on real entities and relationships.

Normalize, but Stop When the Model Is Clear

Normalization is useful because it reduces duplication and improves consistency. For most beginner projects, aiming for a sensible, roughly third-normal-form design is enough.

For example, putting customer email directly on every order row creates duplication. Moving customers into their own table is a clear improvement. But creating separate tables for every tiny variation in naming or categorization can make the schema harder to understand than the problem itself.

A practical question is: does this new table represent a real business concept, or am I creating it only because it feels academically pure? If the answer is the second one, you may be overengineering.

Design Queries You Actually Need

A schema should support the reads and writes your application will perform. That means you should test a few real queries while designing.

sql
1SELECT o.id, c.name, o.created_at
2FROM orders o
3JOIN customers c ON c.id = o.customer_id
4ORDER BY o.created_at DESC;

If the schema makes normal queries painfully complicated, it is often a sign that the design has drifted away from the actual product.

This does not mean denormalize immediately. It means check whether the structure reflects how the application works.

Add Constraints Before Adding Complexity

Beginners sometimes spend energy on elaborate schema patterns while skipping the basics that matter more:

  • primary keys
  • foreign keys
  • uniqueness constraints
  • 'NOT NULL'
  • simple CHECK constraints

Those rules do more for data quality than an exotic table layout. A modest schema with strong constraints is better than a clever schema that accepts bad data.

Plan for Change, Not for Every Hypothetical

Databases do evolve, so you should not hard-code yourself into a corner. But there is a difference between leaving room for change and building for every imagined future.

For example, adding a status column to orders is reasonable if you already know orders move through a few states. Building a generic workflow engine with configurable state machines is probably too much unless the product already needs it.

Schema evolution through migrations is normal. You do not have to solve version two on day one.

Common Pitfalls

The biggest pitfall is mistaking complexity for good design. More tables and more indirection do not automatically make the model more professional.

Another common mistake is the opposite one: putting unrelated data into one giant table to avoid thinking about relationships. Underengineering creates its own problems with duplication and inconsistent updates.

Developers also forget to validate the design with real operations. If you never test inserts, updates, and common reports, you may discover too late that the schema is awkward in practice.

Finally, do not design only from an abstract normalization checklist. Design from actual business entities, then refine with normalization and constraints.

Summary

  • A database is overengineered when its complexity exceeds the current business need.
  • Start from the core entities and relationships your application must support.
  • Use normalization to remove real duplication, but do not invent tables without a clear purpose.
  • Strong constraints often matter more than elaborate abstractions.
  • Expect to evolve the schema with migrations instead of solving every possible future requirement up front.

Course illustration
Course illustration

All Rights Reserved.