Back to Blog
database-design

Stop Vibe Coding: Design Your DB

How to design a database before writing a single line of backend code

G

Gajanan Rathod

December 24, 20255 min read
Share:
Stop Vibe Coding: Design Your DB

Most people start projects like this:

> I’ll just set up auth real quick and figure the DB later.

That “later” is where projects go to die.

If you’re building anything **serious** which ain't a toy database design is not optional. It’s the backbone. If you mess it up, your code *will* fight you later.

This post walks through **how I design a database before writing a single line of backend code**, using a real project I’m building: **AutoMix**, an audio mixing platform.

**Why Database Design Comes First**

Code can be refactored. UI can be redesigned. Bad database design? That’s pain, migrations, downtime, and regrets.

When you design your DB early:

* Your APIs become clearer * Your business logic simplifies * Your system scales cleaner * You stop guessing while coding

No vibe coding. Only intentional building.

**Step 1: Understand the Problem (Properly)**

Before tables, before diagrams write the **problem statement in plain English**.

AutoMix – Problem Statement

> AutoMix is an audio mixing platform where users can: > > * Create an account using email and password > > * Create multiple projects > > * Upload vocals, beats, and reference tracks > > * Trigger backend processing to mix audio > > * Receive final rendered audio outputs >

This doesn’t need to be fancy. It needs to be **clear**.

If you can’t explain your product simply, you’re not ready to design anything yet.

---

Step 2: Break the Problem into Entities

Now we ask one question repeatedly:

> **What “things” exist in this system?**

From the problem statement, we get:

* **Users** – people using the platform * **Projects** – containers for work * **Assets** – uploaded audio files * **Processing Jobs** – backend tasks * **Renders** – final mixed outputs

These are your **core entities**.

No tables yet. Just clarity.

---

Step 3: Decide Your Tables

Each entity usually becomes a table.

For AutoMix, we’ll have:

* `users` * `projects` * `assets` * `jobs` * `renders`

Simple. Clean. No over engineering.

---

Step 4: Design Each Table (Column by Column)

Now we zoom in and design **what each table stores**.

Users

Every project starts with users.

```plaintext users - id (PK) - email (unique) - password_hash - created_at ```

Projects

Each user can own multiple projects.

```plaintext projects - id (PK) - owner_id (FK → users.id) - name - no_of_renders - completed - created_at ```

Assets

Audio files uploaded inside a project.

```plaintext assets - id (PK) - project_id (FK → projects.id) - filename - filetype - duration - storage_key - uploaded_at ```

Jobs

Each processing request becomes a job.

```plaintext jobs - id (PK) - project_id (FK → projects.id) - requested_by (FK → users.id) - status - started_at - ended_at - created_at ```

Renders

Final outputs generated by jobs.

```plaintext renders - id (PK) - project_id (FK → projects.id) - job_id (FK → jobs.id) - storage_key - filename - filetype - duration - created_at ```

---

Step 5: Define Relationships Clearly

This is where many beginners mess up.

Let’s explicitly define relationships:

* A **user** owns many **projects** * A **project** has many **assets** * A **project** has many **jobs** * A **job** produces one **render**

Foreign key mappings:

```plaintext projects.owner_id → users.id assets.project_id → projects.id jobs.project_id → projects.id jobs.requested_by → users.id renders.project_id → projects.id renders.job_id → jobs.id ```

Once this is clear, your API routes basically design themselves.

---

Step 6: Visualize with ER Diagrams

Before coding, visualize.

Tools I recommend:

* [**Eraser.io**](http://Eraser.io) (my personal favorite) * Lucidchart * Mermaid

Here’s the exact schema I used in Eraser for AutoMix 👇 *(you can paste this directly into Eraser)*

```plaintext // AutoMix Database Schema

users [icon: user, color: blue] { id string pk email email notnull, unique password_hash string notnull created_at timestamp notnull }

projects [icon: gcp-project, color: red] { id string pk owner_id string fk notnull name string notnull no_of_renders int notnull completed boolean notnull created_at timestamp notnull }

assets [icon: audio, color: green] { id string pk project_id string fk notnull filename string notnull filetype enum('wav','mp3','flac','aac','other') notnull duration float storage_key string notnull uploaded_at timestamp notnull }

jobs [icon: k8s-job, color: green] { id string pk project_id string fk notnull requested_by string fk notnull status enum('queued','processing','succeeded','failed') notnull started_at timestamp ended_at timestamp created_at timestamp notnull }

renders [icon: upload, color: green] { id string pk project_id string fk notnull job_id string fk unique storage_key string notnull filename string filetype enum('wav','mp3','other') notnull duration float created_at timestamp notnull }

// Relationships projects.owner_id > users.id assets.project_id > projects.id jobs.project_id > projects.id jobs.requested_by > users.id renders.project_id > projects.id renders.job_id > jobs.id ```

---

Step 7: Iterate Before You Code

This schema is **not final** and that’s okay.

You should:

* Re-read it * Question every column * Add constraints * Remove redundancy * Think about edge cases

Once it feels clean **on paper**, *then* you write code.

---

Final Thoughts

If you skip database design, you’re not moving fast you’re just delaying failure.

Good database design:

* Makes backend logic boring (that’s good) * Reduces bugs * Saves weeks of refactoring * Forces you to understand your own product

Stop vibe coding. Start designing.

If this helped you, save it! future you will thank you.

Enjoyed this article? Share it with others!

Share:

Subscribe to my Newsletter

Get notified when I publish new articles. No spam, unsubscribe anytime.

By subscribing, you agree to receive email updates. Your email will never be shared.