Skip to main content
← All projects
L2Web · static-site· 10-18h total

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.

Open in GitHub Codespaces· free 60h/moOpen in Gitpod

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.

Python 3.12FastAPIJinja2markdown-it-pyPygmentsVercel

Milestones (7 · ~15h)

  1. 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"
  2. 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"
  3. 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"
  4. 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"
  5. 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"
  6. 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"
  7. 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.

  1. 0-5s: 'I built a static markdown blog engine that scores 95+ on Lighthouse.'
  2. 5-20s: Live demo — view the deployed blog, scroll the index, open a post.
  3. 20-35s: View source on a post page — show RSS feed link, OG tags, sitemap.
  4. 35-50s: Lighthouse audit live in front of the recruiter (or screenshot if they're rushed).
  5. 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 →