Content Publishing Workflow: How This Writing Section Works
This site's writing section is built on a simple but powerful content publishing system. No CMS, no database—just Markdown files that become fully-featured blog posts.
The Publishing Model
Every post you're reading is an MDX file stored in the repository. When you deploy, Next.js pre-renders every post as static HTML. Fast, simple, version-controlled.
Directory Structure
content/writing/
├── systems-design/
│ └── building-reliable-systems.mdx
├── monitoring/
│ └── your-post-here.mdx
├── automation/
│ └── content-publishing-workflow.mdx
└── research/
└── another-post.mdx
Categories are folders. Drop a .mdx file in the right folder, and it appears on the site. No configuration needed.
Creating a New Post
Step 1: Choose Your Category
Four categories are available:
systems-design– Architecture, design patterns, system modelingmonitoring– Observability, alerting, operational excellenceautomation– Workflows, tooling, efficiencyresearch– Investigations, experiments, deep dives
Create your file in the appropriate directory.
Step 2: Add Frontmatter
Every post needs YAML frontmatter at the top:
---
title: "Your Post Title Here"
excerpt: "A one-sentence summary that appears in lists and RSS feeds."
publishedAt: "2024-12-28"
category: "Automation"
tags: ["tag1", "tag2", "tag3"]
---
Field Rules:
title: Required. Used for page title and Open Graph metadata.excerpt: Required. Keep it under 160 characters for SEO.publishedAt: Required. ISO date format (YYYY-MM-DD).category: Must match the folder name display format.tags: Optional array. Used for related post recommendations.
Step 3: Write in MDX
MDX is Markdown with superpowers. Standard Markdown works, plus you get:
Code blocks with syntax highlighting:
interface Post {
slug: string;
title: string;
publishedAt: string;
}
const posts = getAllPosts();
Custom components:
You can use the custom Callout component for emphasis:
<Callout type="info">
This is an informational callout. Type can be "info", "warning", or "success".
</Callout>
Smart links:
External links automatically open in new tabs. Internal links use Next.js routing.
How the RSS Feed Works
The RSS feed is generated dynamically at build time from your content files.
Feed Generation Process
- Content Parsing: The
getAllPosts()function reads every.mdxfile - Metadata Extraction: Frontmatter is parsed with
gray-matter - RSS XML Construction: Posts are formatted as RSS 2.0 XML
- Static Route: Served at
/writing/rss.xml
The Feed Route
Located at app/writing/rss.xml/route.ts:
export async function GET() {
const posts = getAllPosts();
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://attakorah.com";
const rss = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Attakorah - Writing</title>
<link>${siteUrl}/writing</link>
...
</channel>
</rss>`;
return new Response(rss, {
headers: {
"Content-Type": "application/xml",
"Cache-Control": "public, max-age=3600",
},
});
}
What Gets Included
Each post in the RSS feed includes:
- Title: Your post title
- Link: Direct URL to the post
- GUID: Permanent unique identifier (same as link)
- Description: Your excerpt text
- Publication Date: RFC 2822 formatted date
- Categories: Your category and all tags
Feed Updates
The RSS feed updates automatically when you:
- Add a new post
- Modify existing frontmatter
- Deploy the site
No manual regeneration needed. It's built at deploy time.
Sitemap Integration
Every post is automatically added to the XML sitemap at /sitemap.xml.
Sitemap Priority
- Homepage:
1.0 - Writing index:
0.9 - Individual posts:
0.8
Change Frequency
Posts are marked as monthly changeFrequency, signaling to search engines that content is stable but may be updated.
The Content Parsing System
The heart of the system is lib/content.ts. It provides:
Core Functions
getAllPosts()
Returns all posts sorted by publication date (newest first). Used by:
- Writing list page
- RSS feed
- Sitemap generation
getPostBySlug(slug)
Fetches a single post with full content. Used by individual post pages.
getRelatedPosts(slug, limit)
Finds related posts using a scoring algorithm:
- Same category: +10 points
- Shared tag: +1 point per tag
Returns the top N highest-scoring posts.
Reading Time Calculation
Every post displays estimated reading time. Calculated automatically using the reading-time package:
import readingTime from "reading-time";
const stats = readingTime(content);
const minutes = Math.ceil(stats.minutes); // Always rounds up
Average reading speed: 200 words per minute (industry standard).
Deployment Workflow
Local Development
npm run dev
Posts are parsed on-demand during development. Hot reload works for content changes.
Production Build
npm run build
All posts are:
- Parsed and validated
- Converted to static HTML
- Added to sitemap
- Included in RSS feed
What Happens at Build
getAllPosts()scans content directory- Frontmatter is extracted and validated
- MDX content is compiled to React components
- Static pages are generated for each slug
- Sitemap and RSS feed are generated
Build Output
Route (app)
┌ ○ /writing
├ ● /writing/[slug]
│ └ /writing/building-reliable-systems
│ └ /writing/content-publishing-workflow
└ ƒ /writing/rss.xml
○= Static page●= SSG (Static Site Generation)ƒ= Dynamic route
Performance Characteristics
Why This Approach Works
No Database Queries: Content is read from filesystem at build time, not runtime.
No Client-Side Rendering: Posts are pre-rendered HTML. First paint is instant.
CDN-Friendly: Every page is a static asset. Cache forever, invalidate on deploy.
Version Control: Content lives in Git. Full history, branching, pull requests.
Trade-offs
Not Suitable For:
- User-generated content
- Real-time updates
- Content that changes hourly
Perfect For:
- Technical writing
- Documentation
- Long-form articles
- Portfolios
Adding Your First Post
Ready to publish? Here's the complete workflow:
1. Create the File
touch content/writing/systems-design/my-first-post.mdx
2. Add Frontmatter and Content
---
title: "My First Post"
excerpt: "Learning how to publish content on this platform."
publishedAt: "2024-12-28"
category: "Systems Design"
tags: ["tutorial", "getting-started"]
---
# My First Post
Your content here...
3. Preview Locally
npm run dev
# Visit http://localhost:3000/writing/my-first-post
4. Build and Deploy
npm run build
# Commit and push to trigger deployment
5. Verify
Check these URLs:
- Post page:
/writing/my-first-post - Writing index:
/writing(should show your post) - Sitemap:
/sitemap.xml(should include your post) - RSS:
/writing/rss.xml(should list your post)
Conclusion
This publishing system prioritizes simplicity and performance. No admin panel, no database, no complexity.
Write Markdown. Commit. Deploy. Done.
The automation handles everything else: parsing, rendering, sitemap updates, RSS feed generation, and static site optimization.
It's reliable because it's simple. It's fast because it's static. It works because it's built on fundamentals that don't change.
Related Posts
Effective Alerting: Less Noise, More Signal
How to design alert systems that wake you up for the right reasons and stay silent for the wrong ones.
Investigating Memory Leaks: A Systematic Approach
A practical guide to finding and fixing memory leaks in production systems using profiling tools, heap dumps, and strategic instrumentation.
Building Reliable Systems: Lessons from Production
Key principles and patterns for designing systems that stay running when it matters most.