Managing site content in plain files (Markdown, HTML, etc.) is an approach that leans on a very old — and very sensible — UNIX idea: everything is files. For many projects this is not just nostalgic; it’s practical. Below I explain why files often win over databases for content management, and how to work with them effectively.
The Unix principle — everything is a file
When content lives as files, you treat each article, page or asset as a first-class file in a folder tree. Filenames often act as slugs (the URL path). If you need a different URL, you can override it with a slug: field in the frontmatter. That simple mapping keeps things explicit and easy to reason about.
Example frontmatter + filename:
---
title: "How managing content in files is more efficient than in a database"
slug: "/files-vs-database" # optional — filename can also be used
date: 2025-09-19
tags: [static-site, git, unix]
---
Your article body...
File: content/posts/files-vs-database.md
Because content is files, you can use standard, battle-tested tools:
- Unix/GNU tools: sed, awk, grep, regular expressions — for fast batch edits and transformations.
- Scripting: Python (or Node, Ruby, etc.) for more complex automation (generate indexes, convert formats, etc.).
- Any editor: vim, Emacs, VS Code, Notepad — pick what you love and you’re productive immediately.

These tools let you do bulk refactors, search-and-replace, or migrations in seconds — no complex admin UI needed.
Version control with Git: track, revert, branch
Storing content in files means it sits naturally in Git:
- See exactly what changed with git diff.
- Revert a specific change even deep in history with git checkout <commit> — path/to/file.
- Work in branches, open PRs, review diffs, merge — exactly the developer workflow you trust.
These visibility and workflow features are either painful or impossible in many popular CMSes (WordPress, Drupal) where the “published” state is buried behind admin UIs and database rows.
graph TD
%% Nodes
U1["Alice: VS Code"]
U2["Bob: Notepad++"]
F1["Local Repo (feature-branch)"]
F2["Local Repo (feature-branch)"]
ORIGIN["Remote Repo (GitHub)"]
PR["Pull Request"]
MAIN["Main Branch"]
PUBLISH["Publish"]
%% Flows
U1 u1@--> F1
U2 u2@--> F2
F1 f1@-->|"push"| ORIGIN
F2 f2@-->|"push"| ORIGIN
ORIGIN f3@-->|"pull"| F1
ORIGIN f4@-->|"pull"| F2
ORIGIN --> PR --> MAIN --> PUBLISH
f1@{ animation: slow }
f2@{ animation: slow }
f3@{ animation: slow }
f4@{ animation: slow }
u1@{ animation: slow }
u2@{ animation: slow }
%% Styling
classDef user fill:#fbe9e4,stroke:#df9277,stroke-width:2px,color:#5a2e23,font-weight:bold,rx:8,ry:8;
classDef local fill:#f6d1c5,stroke:#d97d61,stroke-width:2px,color:#4a241a,font-weight:bold,rx:8,ry:8;
classDef remote fill:#efb9a6,stroke:#c96a52,stroke-width:2px,color:#3d1d15,font-weight:bold,rx:12,ry:12;
classDef review fill:#f6d1c5,stroke:#b8573f,stroke-width:2px,color:#3d1d15,font-weight:bold,rx:8,ry:8;
class U1,U2 user;
class F1,F2 local;
class ORIGIN remote;
class PR review;
class MAIN remote;
class PUBLISH remote;
linkStyle default stroke:#df9277,stroke-width:2px;
Feature Branch Workflow for Documentation
gitGraph TB:
commit id:"doc feature C init" tag:"release 1.1"
commit id:"doc feature C update"
branch Alice
checkout Alice
commit id:"doc feature A draft"
commit id:"doc feature A refactor"
commit id:"doc feature A review"
commit id:'Revert "doc feature A refactor"' type: REVERSE
checkout main
commit id:"refactor tables"
commit id:"doc feature D init"
branch Bob
checkout Bob
commit id:"doc feature B draft"
commit id:"doc feature B review"
checkout main
commit id:"refactor images"
merge Alice
commit id:"doc feature D update" tag:"release 1.2"
merge Bob
commit id:"doc feature E init"
This Git workflow illustrates a documentation development process using feature branches. The main branch tracks official releases, while contributors create separate branches for their own work.
- Work on feature C begins on
main
, leading to release 1.1.
- Alice creates a branch to draft and review document A before merging it back into
main
.
- Bob works in parallel on document B in his own branch, completing a draft and review cycle.
- Meanwhile, additional updates to feature D are developed on
main
, culminating in release 1.2 after merging Alice’s work.
- Finally, Bob’s branch is merged, and a new feature (E) is initialized.
This workflow highlights parallel development, isolation of work in branches, and structured integration into main
for tagged releases.
Security: fewer attack surfaces
No DB = no SQL injection. Plain files eliminate a class of vulnerabilities related to dynamic query building, mis-sanitized input, or poorly-configured database engines. There are still security considerations (XSS, server config, secret leakage), but the risk of a corrupted or malicious query is gone.
Cost: free for small projects on modern CDNs
Static sites (generated from files) can be hosted for free or very cheap on platforms like Vercel, Netlify, GitHub Pages — perfect for blogs, documentation, marketing sites. No database server bills, no complex infrastructure to manage.
Speed & SEO: ultra-fast delivery
When pre-built files are served from a CDN they’re delivered extremely quickly to users worldwide. Faster page loads improve user experience and SEO. Static assets are easy to cache and scale.
A static-file setup has fewer moving pieces than a LAMP/LEMP stack:
- No database engine to crash or become corrupted.
- Fewer software components to update and secure.
- Less surface for subtle runtime bugs (plugin incompatibilities, failed migrations, etc.).
Fewer components → fewer failure modes.
More control over content changes
Git-based workflows let you:
- Preview diffs before publishing.
- Accept contributions via PRs (great for docs and community-driven sites).
- Automate deployments on merge (CI → build → CDN).
This creates an auditable chain from edit → review → publish.
Caveats: when a database might still be right
Files are great for many use cases, but databases make sense when you need:
- Complex relational querying across large datasets.
- Real-time collaborative editing with conflict resolution.
- Advanced user-generated content workflows with fine-grained permissions out-of-the-box.
If your project needs those, a hybrid approach (files for the majority of content + database for the dynamic parts) also works well.
Quick practical checklist to get started
- Keep each post/page as a Markdown file with YAML frontmatter.
- Use filenames as slugs; override with slug: when necessary.
- Put everything under Git and push to GitHub/GitLab.
- Configure a static site generator (Hugo, Jekyll, Eleventy, Next.js, etc.).
- Connect your repo to a CDN (Vercel/Netlify) for automatic builds on push.
- Use grep, sed, awk or small Python scripts for bulk edits.
- Review changes through PRs and git diff before deployment.
Managing content as files embraces simplicity, transparency and control. You gain powerful Unix tooling, Git-based workflows, better security against database-specific attacks, lower cost for small projects, and excellent performance when combined with CDNs — all reasons why files are often the more efficient choice over a database-driven CMS.