This repo holds the posts published at wemakedevs.org/blogs. Merging to
main publishes; there is no deploy step. See README.md for the full
human-facing guide — this file is the short version for agents.
- One Markdown file in
posts/. The filename is the URL:posts/my-post.md→wemakedevs.org/blogs/my-post. Lowercase letters, numbers and hyphens only. Never rename it later — that breaks every link. - Images go in
images/<slug>/, where<slug>matches the post filename. - New author? Add a key to
authors.json.
---
title: "Scaling Kubernetes to 10,000 pods"
description: "What broke, what we changed, and the metrics that told us it worked."
datePublished: 2026-07-24
author: kunal-kushwaha
tags: ["kubernetes", "devops"]
---- Required:
title,datePublished(YYYY-MM-DD). A post missing either is silently skipped — it won't appear on the site. - Strongly wanted:
description(shown on cards/search) andtags(filter buttons — reuse existing tags rather than inventing near-duplicates). - Optional:
author(a key inauthors.json),draft: true(hides the post, safe to merge),seoTitle/seoDescription,canonicalUrl. - Quote
titleif it contains a:. - Reading time is computed automatically. Do not write it.
- No
# Heading. Thetitleis rendered as the page heading; start sections at##.##/###become the table of contents. - Image paths are from the repo root:
. Not./diagram.png, not/images/.... - Links to other posts use a relative file path:
[our CI rewrite](./why-we-rewrote-our-ci-in-go.md). - Code blocks support
ts filename="src/server.ts" showLineNumbers {4-6} /importantThing/— all four parts optional. - Only three components exist:
<YouTubeEmbed url="..." />,<Callout type="note|tip|warning|danger" title="...">…</Callout>and<Quote author="..." title="..." avatar="...">…</Quote>. Any other component name renders as plain text. <Quote>is for a quotation with someone's name on it. Every attribute is optional;avataris a repo-root path (images/<slug>/avatar.jpg) or a full URL. For a quotation with no attribution, plain>markdown is still the way.- The MDX gotcha: posts are processed as MDX, so bare
<and{in prose can break parsing. Wrap them in backticks —`x < 5`,`{ "a": 1 }`.
posts/markdown-torture-test.md exercises every supported feature — copy from
it when unsure.
A cover image is optional. If images/<slug>/cover.* is missing when the
post is merged, CI generates a branded card from the title, tags, author and
date and commits it to images/<slug>/cover.png. The markdown is not touched.
Every published post ends up with a cover either way.
So: do not fabricate, download, or block on a cover image. Ship the post without one and let generation handle it. Only add a file when a real, purpose-made image exists.
If you do add one:
| Filename | cover, exactly — hero.png / cover-final.png will not be found. One cover.* per post; two is ambiguous and fails the check |
| Dimensions | 1200 × 630 (or a larger image of the same 1.91:1 shape) |
| Format | .png, .jpg, .jpeg or .webp |
| Size | Under 1 MB |
| Where | images/<slug>/ |
Run through every item. A PR check covers the mechanical ones, but verify them locally first rather than waiting on CI.
- Filename is the intended URL slug: lowercase, hyphens, no spaces or underscores, and it is not a rename of an existing published post.
- Frontmatter parses and has both
titleanddatePublished(YYYY-MM-DD). -
titleis quoted if it contains a:. -
descriptionandtagsare present; tags reuse existing ones. -
authorexists as a key inauthors.json(or is omitted). - No
# H1in the body — sections start at##. - Every image path resolves and is repo-root-relative
(
images/<slug>/...), and the files are actually committed. - Bare
<and{in prose are wrapped in backticks. - Only
<YouTubeEmbed>,<Callout>and<Quote>are used;Callouttypeis one ofnote,tip,warning,danger. - Cross-post links use relative
./post-name.mdpaths and point at files that exist. - Cover: either absent (it'll be generated) or exactly one
images/<slug>/cover.*at 1200×630, under 1 MB. -
draft: trueis removed if the post is meant to go live — and present if it isn't finished.
{
"your-name": {
"name": "Your Name",
"role": "What you do",
"avatar": "images/authors/you.png",
"bio": "A sentence or two.",
"x": "your_handle",
"github": "your-username"
}
}Only name is required. For x, github and linkedin use just the
username, not a URL. Avatars: square, at least 128×128.