Why Keeping Data in a JSON File on Disk Is Better Than a Database (Until You Reach Product-Market Fit)
When launching a new web app or SaaS project, simplicity and speed are crucial. Using a JSON file instead of a database can be the smartest choice in the early stages. Here's why:
Benefits of Using JSON Files
- Cost-Effective
- Running a JSON file locally costs nothing compared to managed database services like Postgres or Firebase.
- At $0 MRR, every dollar counts, so JSON helps keep your infrastructure costs near zero.
- Perfect for Small Data Volumes
- JSON works great when dealing with hundreds or thousands of records.
- For example, if you have under 5,000 users with basic stock alerts, a JSON file will be fast enough.
- Simpler Codebase
- No need to set up database connections, ORM libraries, or write SQL queries.
- The entire data structure is a single file you can easily load, edit, and save.
- Fast MVP Development
- Fewer tools, fewer dependencies, and faster shipping for a minimal viable product (MVP).
When to Switch to a Database
As your app grows, you’ll eventually outgrow JSON. Here are the signs that it’s time to transition to a database:
Key Indicators:
- User Growth: JSON works well up to around 5,000-10,000 users. Beyond that, concurrent writes become risky.
- Concurrent Access: JSON files are not ideal for multiple simultaneous writes. A database handles transactions safely.
- Data Size: JSON files exceeding 50MB become slow to read/write. Databases are optimized for larger datasets.
- Complex Queries: Searching, filtering, and aggregating data gets slower and more complicated with JSON files.
- MRR Milestone: Once you pass $1,000 MRR, it's worth considering a database. At this point, the risk of losing customer data outweighs the simplicity benefits of JSON.
General Rule of Thumb:
- JSON: Ideal for MVPs and up to 5,000 users or $1,000 MRR.
- Database: Recommended for 5,000+ users, above $1,000 MRR, or complex data needs.
Example JSON Data Structures for a Stock Alerting App
Imagine you're building a stock alerting app where users can set price alerts. Here's a structured way to store the data using JSON.
Basic JSON Structure (For MVP)
{
"users": {
"1": {
"email": "user1@example.com",
"watched_tickers": ["AAPL", "TSLA"],
"alerts": [
{
"ticker": "AAPL",
"threshold": 250.00,
"condition": "above",
"alert_sent": false
},
{
"ticker": "TSLA",
"threshold": 380.00,
"condition": "below",
"alert_sent": false
}
]
}
},
"tickers": {
"AAPL": {
"current_price": 242.70
},
"TSLA": {
"current_price": 394.00
}
}
}
Python Code to Access the Data (Basic MVP)
import json
# Load the data
def load_data():
with open('data.json') as f:
return json.load(f)
data = load_data()
# Access user alerts
user_id = "1"
for alert in data['users'][user_id]['alerts']:
print(f"Alert for {alert['ticker']}: {alert['condition']} {alert['threshold']}")
Load the JSON from a file. Print all of the alerts set up for a user.
More Advanced JSON with Separate Alerts Object (Scaling a Bit More)
You may want to eventually move the alerts out of the users object into their own object for:
- Centralized Management: Alerts are stored in a single place, making it easier to manage and update across all users.
- Separation of Concerns: Users' data and alerts are kept distinct, resulting in a cleaner, more modular structure.
- Scalability: Easier to scale and modify the alert system (e.g., adding fields) without complicating the user data model.
{
"users": {
"1": {
"email": "user1@example.com",
"watched_tickers": ["AAPL", "TSLA"]
}
},
"tickers": {
"AAPL": {
"current_price": 242.70
},
"TSLA": {
"current_price": 394.00
}
},
"alerts": {
"alert_1": {
"user_id": "1",
"ticker": "AAPL",
"threshold": 200.00,
"condition": "below"
"alert_sent": false
},
"alert_2": {
"user_id": "1",
"ticker": "TSLA",
"threshold": 350.00,
"condition": "below",
"alert_sent": false
}
}
}
Python Code to Access This Structure:
# Checking if any alerts should trigger
data = load_data()
for alert_id, alert in data['alerts'].items():
stock = data['tickers'][alert['ticker']]
if alert['condition'] == 'below' and stock['current_price'] < alert['threshold']:
print(f"Sending alert to user {alert['user_id']} for {alert['ticker']}")
For each of the alerts, check if the current price has dropped below the alert threshold and send the alert.
Best Practices for Using JSON Files in Early Stages
- File Locking: Use tools like Python's
filelockto avoid data corruption with concurrent writes. - Data Backups: Automate periodic backups of your JSON files to prevent data loss.
- Splitting Data: If your data grows, split files (e.g.,
users.json,alerts.json) instead of a single massive file. - Version Control: Use Git to track changes to your JSON files.
- Read-Heavy Workloads: JSON excels when reads dominate over writes.
When to Transition to a Database (Examples)
- <5,000 users with simple alerts: Stick with JSON files.
- 5,000-10,000 users: If data modification frequency increases, consider SQLite.
- 10,000+ users with real-time alerts: Postgres or a managed database is more suitable.
Why Pieter Levels Ran JSON Files for Years with Nomadlist:
Pieter Levels famously ran Nomadlist on JSON files for years because:
- Low traffic initially.
- Mostly read operations with minimal writes.
- Cost efficiency before revenue generation.
Conclusion: Start Simple, Scale Smart
JSON files offer a fast, simple, and cost-effective way to manage data when you're validating a product idea. They allow you to keep your stack minimal and focus on building the product rather than managing databases.
However, once you grow past a few thousand users or need concurrent writes and real-time operations, switching to a proper database will become necessary for performance and data integrity.
✅ Build fast, validate your idea, and scale only when needed.