Markdown blog engine with RSS + sitemap
Every developer eventually wants their own blog. Building it from scratch teaches HTTP serving, templating, content modelling, SEO basics (RSS / sitemap / OG tags), and deploy. It's the simplest project that touches all the things a junior backend engineer should know.
Resume bullet (when finished)
“Built a static markdown-driven blog engine with full RSS 2.0 feed, sitemap.xml, syntax-highlighted code blocks, dark theme, and a 95+ Lighthouse score; deployed to Vercel with under 30KB of JS shipped to the client.”
Locked tech stack
No "choose your language" — analysis paralysis kills completion. Follow the stack to the letter on your first build.
Milestones (7 · ~15h)
- M1~2h
Hello-blog: render one .md file as HTML
FastAPI app loads `posts/hello.md`, converts to HTML via markdown-it-py, renders Jinja2 layout. Visible at /.
CHECK BEFORE MOVING ON:
- Why parse markdown server-side instead of client-side?
- Where does the Jinja2 template separate concerns from the route?
$ git commit -m "feat: render single markdown post via FastAPI + Jinja2" - M2~2h
Frontmatter + post index
Parse YAML frontmatter (title, date, slug, tags). List view at / shows all posts sorted by date.
CHECK BEFORE MOVING ON:
- What's frontmatter and why is it the right place for metadata?
- Why not use a database for 3 posts?
$ git commit -m "feat(content): frontmatter parsing + index page" - M3~2h
Syntax highlighting via Pygments
Fenced code blocks (```python) render with Pygments-generated HTML + CSS theme.
CHECK BEFORE MOVING ON:
- Why server-side highlight (Pygments) over client-side (highlight.js)?
- What's the cost — page size or build time — and how do you mitigate?
$ git commit -m "feat(content): Pygments syntax highlighting" - M4~2h
RSS 2.0 feed at /feed.xml
Standards-compliant RSS — `application/rss+xml` content type, valid pubDate, GUID per post.
CHECK BEFORE MOVING ON:
- Why does GUID matter for a feed reader?
- What's the difference between RSS 2.0 and Atom?
$ git commit -m "feat(seo): RSS 2.0 feed" - M5~2h
sitemap.xml + OG cards
Sitemap with lastmod per post. Per-post og:title, og:description, og:image. Validated at validator.w3.org/feed.
CHECK BEFORE MOVING ON:
- Why does Google need a sitemap when it can crawl your links?
- What 3 OG tags are non-negotiable for unfurl quality?
$ git commit -m "feat(seo): sitemap + OG tags" - M6~3h
Lighthouse 95+ pass
All 4 categories ≥ 95 on the production deploy. Document any compromises.
CHECK BEFORE MOVING ON:
- Which Lighthouse category usually trips up Python web apps?
- What's the easiest perf win you found?
$ git commit -m "perf: Lighthouse 95+ across all categories" - M7~2h
Deploy to Vercel
vercel.json with Python serverless function, build verified on a clean clone, prod URL in README.
CHECK BEFORE MOVING ON:
- Why is Vercel's Python runtime cold-start slower than Node's?
- What's a 'cold start' anyway?
$ git commit -m "ops: Vercel deploy + production URL"
60-second demo storyboard
What you say in the recruiter screen when they ask "tell me about your latest project." Practice it out loud.
- 0-5s: 'I built a static markdown blog engine that scores 95+ on Lighthouse.'
- 5-20s: Live demo — view the deployed blog, scroll the index, open a post.
- 20-35s: View source on a post page — show RSS feed link, OG tags, sitemap.
- 35-50s: Lighthouse audit live in front of the recruiter (or screenshot if they're rushed).
- 50-60s: Repo link + one architectural decision (e.g. 'why I used FastAPI for a static-ish site').
STAR talking points for behavioral round
STAR — PERF OPTIMIZATION
Situation: initial Lighthouse score was 78 on Performance. Task: get to 95+. Action: profiled with `lighthouse --view`, found the largest hits were unminified Pygments CSS (180KB) and synchronous font loading. Inlined critical CSS, lazy-loaded the syntax-highlight stylesheet, added font-display: swap. Result: Performance 97, total page weight 28KB.
STAR — DESIGN TRADE-OFF
Situation: chose between Pygments and highlight.js for code blocks. Task: justify the pick. Action: server-side Pygments = zero JS, faster paint, no CLS. Client-side highlight.js = bigger bundle but easier to add themes later. Picked server-side because Lighthouse target was 95+. Result: held at 28KB and theme is now a CSS file swap, not a runtime decision.
STAR — CONTENT MODELLING
Situation: started with posts as Python dicts in a list. Task: scale it without buying a CMS. Action: moved to YAML frontmatter + .md files indexed at boot. Result: adding a post is one git commit, no DB, no admin UI to maintain.
Production references — how grown-up systems do this
Vercel →
Vercel's own blog engine uses a similar markdown-based pipeline — their open-source Next.js blog template is the canonical reference.
Pygments →
Pygments docs explain how to integrate server-side highlighting in a Python web app — every Python project that renders code uses this lib.
Google Search Central →
RSS / sitemap docs from Google themselves — the source of truth for SEO infrastructure.
Self-review rubric (before you claim done)
Correctness
- All posts render with title, date, tags, syntax-highlighted code.
- /feed.xml validates at https://validator.w3.org/feed/
- /sitemap.xml lists every post with current lastmod.
- OG tags present and valid on every post page.
Code quality
- All routes typed (`Annotated[...]` style).
- Templates DRY — `base.html` + `{% block content %}` pattern.
- Frontmatter parsing handles missing fields gracefully (no 500s).
- Static assets fingerprinted for cache busting.
Testing
- pytest covers frontmatter parsing happy + sad paths.
- At least one integration test that hits a post URL and checks rendered output.
- CI runs on every push.
Docs
- README explains how to add a post + deploy.
- Lighthouse screenshots in /docs.
- Architecture diagram one-pager.
✱ AI code review
Get a senior-style review before you call it done
Push your finished work to GitHub, open a PR, paste the PR URL below. Claude reviews the diff against this project's rubric and replies with strengths, must-fix items, and one teachable principle.
Tick the rubric items honestly, write the README, push to GitHub, get the AI review above. Once it's clean, email support@learnpython.academy with the repo link — we feature the best ones on /success-stories.
Need Python first? Start Foundations →