Building Without a Database
Not every project needs a database. Here's how to think about when files and git are enough.
The default assumption for most web projects is: you'll need a database. I want to challenge that assumption.
For a significant class of projects — personal websites, documentation sites, internal tools with small datasets, content-heavy apps — a database is the wrong default. It adds operational complexity, a deployment dependency, and a migration story, all before you've shipped anything.
What you can do without a database
File-based content is the simplest approach. Markdown or MDX files in a git repo give you:
- Version history for free (git blame, revert, diff)
- Local editing with any text editor
- Offline access
- No running service to maintain
- Trivially deployable to any static host
This site's articles are MDX files. Adding a post is touch content/articles/my-post.mdx. Publishing is a git push.
JSON files work for structured data that doesn't need querying. A list of projects, a speaking schedule, a reading list — these are all fine as JSON files loaded at build time.
Environment variables and config files handle settings and secrets. No need for a config table.
The threshold question
A database earns its complexity when you need:
- User-generated content — data created at runtime that you can't predetermine at build time
- Complex queries — filtering, aggregation, joins across large datasets
- Concurrent writes — multiple users writing to the same data simultaneously
- Scale — data volumes or access patterns a file system can't handle
If your project doesn't need any of these, you probably don't need a database.
What git gives you
Git is an underrated data store. For content, it gives you:
- Full history: every version of every file
- Branching: draft posts on a branch, merge when ready
- Collaboration: pull requests for content review
- Rollback:
git revertto undo a bad publish
This is more than most CMS databases give you.
When you do need a database
None of this is anti-database. When you hit the threshold above, add one. PostgreSQL is excellent. SQLite is great for single-server apps with modest write loads. Redis handles caching and queues.
The point is to start with the simplest thing that works. Add complexity only when you've proven you need it. The cost of a database you don't need is paid every day in deploys, ops, and cognitive overhead. The cost of adding one later is usually a few hours of work.
Start simple. Complicate deliberately.