Most founders draw a schema for a product they will not build for two years. Tables for analytics, tables for teams, tables for plans they have not sold. Skip all of it. Your data model exists to support the one feature you are launching this month. Anything else is weight.
Three or four tables is enough. You can add a column in five minutes when a real user gives you a real reason.
Sketch the smallest schema that runs your core feature:
| Table | Purpose | Typical fields |
|---|
| users | Tied to your auth from user auth | id, email, created_at |
| primary_entity | The thing your product makes or tracks | id, user_id, title, body |
| relationship | Links between entities, only if needed | id, user_id, entity_id |
Pick the tool that matches your stack:
- Supabase — full Postgres with a friendly UI and instant REST and GraphQL APIs.
- PocketBase — single-binary backend, dead simple, perfect for solo founders.
- NocoDB — spreadsheet-style UI on top of a real relational database.
Define types. Set foreign keys. Push it live. Then prove it works: create a record by hand in the dashboard, then read it back from your app code. If the row appears, your model is live.
Bad: 12 tables including subscription_tiers, team_members, and audit_logs before you have one user.
Good: users, projects, entries. Ship. Add tables when reality demands them.
Bad: storing JSON blobs everywhere because "we might need flexibility."
Good: typed columns with sensible defaults. Add a JSON field only when the shape is genuinely unknown.
- Designing for a million users. Build for ten. Indexes and partitions are tomorrow's problem.
- Skipping foreign keys to "go faster." You will pay for orphaned rows within a week.
- Treating the schema as final. It is not. It is a starting point you will edit.
Pick three or four entities, define their fields and relationships, and push the schema live in your chosen database.
A live database with your core tables defined, foreign keys set where they belong, and at least one row created and successfully read by your app.
Most founders draw a schema for a product they will not build for two years. Tables for analytics, tables for teams, tables for plans they have not sold. Skip all of it. Your data model exists to support the one feature you are launching this month. Anything else is weight.
Three or four tables is enough. You can add a column in five minutes when a real user gives you a real reason.
Sketch the smallest schema that runs your core feature:
| Table | Purpose | Typical fields |
|---|
| users | Tied to your auth from user auth | id, email, created_at |
| primary_entity | The thing your product makes or tracks | id, user_id, title, body |
| relationship | Links between entities, only if needed | id, user_id, entity_id |
Pick the tool that matches your stack:
- Supabase — full Postgres with a friendly UI and instant REST and GraphQL APIs.
- PocketBase — single-binary backend, dead simple, perfect for solo founders.
- NocoDB — spreadsheet-style UI on top of a real relational database.
Define types. Set foreign keys. Push it live. Then prove it works: create a record by hand in the dashboard, then read it back from your app code. If the row appears, your model is live.
Bad: 12 tables including subscription_tiers, team_members, and audit_logs before you have one user.
Good: users, projects, entries. Ship. Add tables when reality demands them.
Bad: storing JSON blobs everywhere because "we might need flexibility."
Good: typed columns with sensible defaults. Add a JSON field only when the shape is genuinely unknown.
- Designing for a million users. Build for ten. Indexes and partitions are tomorrow's problem.
- Skipping foreign keys to "go faster." You will pay for orphaned rows within a week.
- Treating the schema as final. It is not. It is a starting point you will edit.
Pick three or four entities, define their fields and relationships, and push the schema live in your chosen database.
A live database with your core tables defined, foreign keys set where they belong, and at least one row created and successfully read by your app.