Two minutes, one command
The last fix to this page left a text editor, went through one command, and was online about two minutes later. Nothing in between asked anyone to log in, and no one clicked publish.
That is the point. We write about crawling Instagram, ranking creators and the things that break at three in the morning, and the writing should cost less than the work it describes. So the blog runs on the tools we already use all day: a text file, git, and a build.
37 s
Full build
3
Languages
0
Databases
An article is a folder
Everything one article needs lives in one directory, and nothing else in the repo knows about it.
apps/blog/content/how-virev-blog-works/
├── en.md
├── ko.md
├── ja.md
├── components/
│ └── DeployFlow.svelte
├── topology.svg
└── style.cssThe locale files hold the prose. Beside them sits whatever that story needs and nothing more: a Svelte component written for one diagram, a stylesheet that only this page loads, a CSV that only this chart reads.1 When the story is finished, its code stops mattering to everyone else.
A language is published when its file exists and its frontmatter does not say draft: true. That single rule feeds the index, the sitemap, the RSS feed and the language links, so a folder with only en.md is an English article and there is nothing else to configure.
No CMS, and what that costs
A content system would add a login, a database, a second copy of every article and a migration the first time a field changes.2 Git already records who changed what and when, a text file diffs cleanly, and a pull request is a review. So the page you are reading is a file, and the history of this sentence is a commit.
The price is a repo checkout. Anyone who writes here needs one, and that rules out writers who do not use git.
We took that trade on purpose. Everyone who writes here already has the repo open.
From Markdown to a static page
Each .md file is compiled by mdsvex into a Svelte component, which is why a paragraph can sit next to a chart and why a file can import a component that lives beside it. SvelteKit then renders every page to HTML at build time. At request time there is no server, only files on a CDN.
Two things happen during that build that a reader notices. Code is highlighted by Shiki, so the colours arrive as plain HTML and no highlighter runs in the browser.3The entire client script on this page positions the margin notes and tracks the table of contents. An article with a chart also loads ECharts, and only then. And Markdown footnotes turn into the notes in the right margin: a small plugin replaces [^1] with a numbered reference and moves the note next to the line that cites it. The HTML is complete without JavaScript; the script only measures where each reference landed.
The rule that decides what is published is short enough to read in full:
export function isPublished(entry: Entry): boolean {
return !entry.meta.draft;
}
/** Locales of one article whose file exists and is not a draft. */
export function publishedLocales(entries: Entry[], slug: string): Locale[] {
const set = new Set(
entries.filter((e) => e.slug === slug && isPublished(e)).map((e) => e.locale)
);
return LOCALES.filter((l) => set.has(l));
}How a deploy works
Four steps, and none of them is a form.
- 1 commit
- 2 push
- 3 build
- 4 live
commit.mjs stages only the files you name.
Commit. node scripts/repo/agent/commit.mjs -m "post(<slug>): title" apps/blog/content/<slug> stages the files you name and nothing else. A dozen agents share this checkout, so a commit that grabs everything would take someone else's half-finished work with it.
Push. git push origin sieun/dev. The push must fast-forward. If it is rejected, someone else pushed first: fetch and push again. Nobody merges by hand.
Build. Vercel runs an ignored-build step first and skips the build when apps/blog did not change in that push. When it did, the whole blog is rebuilt in about forty seconds.
Live. The new build replaces the old one when it succeeds, and the URL never changes.
When it breaks
A broken article fails the build, and a failed build is never deployed. The previous version stays online and the log names the file. That is the safety net: the worst case is that a new article does not appear, not that the site goes down.
Undoing a published article is git revert and a push, or one click in the Vercel dashboard. Changing the reading page itself, the margin notes or the type scale, rebuilds every article in the same deploy, because there is one set of templates and the articles are data going through it.
Three languages, published one at a time
en.md, ko.md and ja.md share the components, the data and the diagrams, and each one goes live on its own schedule. The language switch above lists only the languages this article actually has, and the hreflang links follow the same list, so a search engine is never pointed at a translation that does not exist yet.
Writing the Korean version is copying the English file and translating the prose. The chart stays the chart.