Draft / Scheduled Content
This article is a draft or scheduled for future publication. The content is subject to change.
Why You Probably Don't Need a Backend
The Default Reflex
Here is how almost every modern software project starts:
- You get a cool idea.
- You run
npx create-next-app. - You immediately run
mkdir server(orbackend), initiate a Docker Compose file with PostgreSQL, spin up an Express/FastAPI/Go app, and figure out how to configure CORS, JWT authentication, and database migrations.
We do this because it’s what we know. We are trained to think in terms of client-server architecture. We assume that if an application needs data, it must fetch that data from an active server database over HTTP on every single page load.
But as the project goes live, reality sets in.
Suddenly, you are responsible for maintaining a database. You have to monitor connection pools, manage database migrations, run security updates on the OS, and set up Prometheus alerts to tell you if the server runs out of memory. If your project gets featured on Hacker News, your backend crashes under the sudden spike of 500 concurrent connections. If it gets no traffic, you are still paying $15/month for idle compute and DB instances just to keep the lights on.
It doesn’t have to be this way.
For a massive category of web applications, you do not need an active backend. A static site hosted on a global CDN, combined with a background builder job running in GitHub Actions or GitLab CI, is not only cheaper and easier to maintain—it is objectively better.
The “Static + CI” Architecture (The Git-as-a-Database Pattern)
Think about the data your app actually uses. Does it change every millisecond? Or does it change once an hour, once a day, or only when you publish a new article?
If you are building a job board, a price tracker, a portfolio, a documentation site, a news aggregator, a dashboard showing public metrics, or even a local business directory, the data changes slowly. Fetching that data from a database on every page load is a waste of compute resources.
Instead, let the build pipeline do the heavy lifting:
+---------------------+
| Scheduled Cron Job | <--- Triggered every hour (GitHub Actions / GitLab CI)
+---------------------+
|
v
+---------------------+
| Python/Node Script | <--- Scrapes APIs, pulls DB records, queries RSS feeds
+---------------------+
|
v
+---------------------+
| Commit Data to Git | <--- Updates jobs.json or content/posts/
+---------------------+
|
v
+---------------------+
| Static Site Build | <--- Rebuilds HTML & JS (Astro, Hugo, Next.js)
+---------------------+
|
v
+---------------------+
| Edge CDN (Pages) | <--- Served globally at 0ms latency, zero database calls
+---------------------+
The Worker Script
Instead of writing a REST API, you write a simple script (Node, Python, or Go) that fetches your data, formats it, and writes it directly to a static file in your repository (like src/data/jobs.json).
Here is a simple example in Node.js (scripts/update-jobs.js):
import fs from 'fs';
import fetch from 'node-fetch';
async function syncData() {
console.log("Fetching latest job listings...");
const response = await fetch("https://api.externalpartner.com/jobs?status=active");
const jobs = await response.json();
// Save directly to the frontend's source folder
fs.writeFileSync(
'./src/data/jobs.json',
JSON.stringify(jobs, null, 2)
);
console.log(`Saved ${jobs.length} jobs to static repository.`);
}
syncData();
The CI Pipeline
You schedule this script to run periodically using GitHub Actions or GitLab CI. The runner executes the script, checks if the data changed, commits the changes back to the repository, and triggers a redeployment.
Here is a complete GitHub Actions workflow (.github/workflows/sync-data.yml):
name: Hourly Data Sync
on:
schedule:
- cron: '0 * * * *' # Run every hour
workflow_dispatch: # Allow manual trigger
jobs:
build-and-sync:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Dependencies
run: npm ci
- name: Fetch and Sync Data
run: node scripts/update-jobs.js
- name: Commit and Push Changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add src/data/jobs.json
if ! git diff-index --quiet HEAD; then
git commit -m "chore: auto-update jobs database [skip ci]"
git push
else
echo "No data changes detected. Skipping commit."
fi
Why this is brilliant:
- 0ms Database Latency: The user gets a pre-built JSON file or pure HTML. There are no database queries, no connection pools to overflow, and no backend routing.
- Infinite Scaling: Since the site is 100% static, it is cached globally at the edge on Cloudflare, GitHub Pages, or Netlify. It can handle 10,000 requests per second without sweating, costing you exactly $0.
- Instant Version History: Your database is your Git history. If something goes wrong or bad data gets imported, you don’t have to restore a database backup. You just run
git revertto go back to the last clean state.
But What About…? (The “Dynamic” Excuses)
“But wait,” you say, “my app needs dynamic features! A static site can’t handle user input!”
Yes, it can. We no longer live in 2005. The modern web ecosystem has commoditized the generic parts of backends. Here is how you replace the most common backend dependencies:
1. Contact and Intake Forms
- The Backend Way: Write an
/api/contactroute, validation logic, configure an SMTP server client, and handle spam prevention. - The Static Way: Use a static-friendly form provider like Web3Forms, Formspree, or SmartForms. You point your standard HTML form action directly to their endpoint, and they handle verification, spam filtering, and email forwarding.
<form action="https://api.web3forms.com/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR-KEY-HERE">
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button type="submit">Submit</button>
</form>
2. Full-Text Search
- The Backend Way: Set up Elasticsearch or Meilisearch, configure syncing cron jobs, and expose a search API.
- The Static Way: Use Pagefind. Pagefind runs during your static site build. It parses your generated HTML files, extracts keywords, and outputs a highly optimized static search index (usually only a few hundred kilobytes). In the browser, a lightweight JS script queries this index locally. It is blazingly fast, runs offline, and requires zero servers.
3. User Comments
- The Backend Way: Build a
commentsdatabase table, design moderation panels, implement markdown sanitization to prevent XSS. - The Static Way: Use Giscus or Utterances. They use GitHub Discussions or Issues as the database. Users sign in with their GitHub account, and comments are stored directly in your repo’s discussions tab, completely managed by GitHub.
4. Interactive State and Authentication
If you need user bookmarks, local settings, or client-side folders, don’t rush to a DB.
- Use the browser’s localStorage or IndexedDB.
- If you absolutely need cross-device user sync, use a lightweight Backend-as-a-Service (BaaS) provider like Supabase or Firebase directly from the frontend. Let them handle the authentication and security rules, leaving your frontend codebase serverless.
When a Backend is Actually Mandatory
We shouldn’t be dogmatic. There are absolute limits to what static files and Git pipelines can do. You definitely need a custom backend if your application falls into these categories:
| Use Case | Why Static/CI Fails |
|---|---|
| Real-time Collaboration | Apps like Google Docs, Figma, or chat apps require WebSockets and immediate, sub-second state synchronization between multiple clients. |
| Sensitive Calculations / Secrets | If your application has proprietary pricing algorithms, custom machine learning logic, or handles sensitive API keys that must never be exposed to the client browser. |
| Instant Global Writes | Social networks or forums where users write content that must be immediately visible to other users globally, with strict moderation and relational index updates. |
| Transactional High-Write Systems | Core banking platforms, real-time airline ticket booking, or stock trading systems where race conditions (double spending/booking) must be avoided at all costs. |
If your app isn’t doing these things, you are wasting time, energy, and money writing a backend.
The Operational Comparison
Let’s look at the maintenance reality of both approaches over a 12-month horizon:
| Operational Metric | Standard Backend Setup (VPS / Serverless) | Static Site + Git CI Pipeline |
|---|---|---|
| Hosting Cost | $10 - $100+/month (VM, Database, Redis, NAT) | $0 (GitHub Pages, Cloudflare Pages, Netlify) |
| Security Risks | Server exploits, SQL injection, database leakage, expired SSL certificates | Practically zero (Read-only static assets on CDN) |
| Scaling Strategy | Load balancers, horizontal scaling, replica DBs | Auto-scaled by global CDN infrastructure |
| Backup Strategy | Weekly DB dumps, point-in-time recovery setups | Every data change is a Git commit. Pure history |
| Deployment Complexity | Docker builds, orchestration, environment variables management | Git push / Git merge |
Conclusion: Keep it Simple
Every line of backend code you write is a line you have to maintain, secure, test, and debug. Every infrastructure piece you deploy is a potential point of failure.
If your web application is primarily displaying information, aggregating data from third-party sources, or performing calculations that can run in the user’s browser, do yourself a favor: Delete the backend folder.
Write a simple Node script, schedule it with a cron job on GitHub Actions or GitLab CI, check the data into Git, and let the CDN handle the rest. You will spend less time debugging servers and more time building features.
Related Content
Kubernetes is the Ultimate Developer Money Pit for Startups
Startups are adopting Kubernetes because Big Tech does. In doing so, they inherit massive complexity, exorbitant cloud bills, and dedicated infrastructure team requirements before they even have product-market fit. You probably just need a single VPS.
Modular Monoliths Win More Fights Than Microservices
Why the modular monolith is often the most honest architecture choice: less ceremony, fewer distributed systems surprises, and more time spent shipping actual product instead of negotiating with Kubernetes.
The SPA Obsession Has Ruined the Web
Single Page Applications (SPAs) were supposed to make the web feel like desktop apps. Instead, they gave us megabytes of JavaScript, blank pages during loading, broken back buttons, and over-engineered build steps. It's time to admit SPAs are a failure for 90% of websites.