Building a Project Versioning System: When Content Strategy Meets Technical Implementation
On This Page
The Trigger: Projects That Outgrow Their Writeups
A few days into building my portfolio, I hit an unexpected problem. My AWS Security Infrastructure project had evolved significantly—what started as a simple Terraform state backend had grown into a comprehensive compliance platform with Config aggregators, CloudTrail integration, and OIDC authentication.
But when I updated the project writeup, I realized I was erasing history. The original v1.0 documentation—showing the foundation I built and the decisions I made—was gone. Replaced entirely by the v2.0 content.
This felt wrong. The evolution of a project is part of its story. How do you document growth without cluttering the portfolio?
First Instinct: Just Add Versioning
My initial approach was straightforward: add version fields to the content schema and create archived copies of old writeups. Simple, right?
// content.config.ts
version: z.string().optional(),
versionStatus: z.enum(['current', 'previous']).optional(),
majorVersions: z.array(z.object({
version: z.string(),
date: z.coerce.date(),
summary: z.string(),
slug: z.string(),
})).optional(),
I moved projects from flat files (project-name.md) to nested folders (project-name/index.md + project-name/v1-0.md). Updated the routing. Built UI components for version dropdowns and changelogs.
It worked. But then I made a mistake that taught me something important.
The Over-Versioning Trap
In my enthusiasm, I versioned everything. Six projects became v2.0 overnight, each with a v1-0.md archive file. It felt comprehensive. It felt professional.
It was also wrong.
When I actually compared the v1.0 and v2.0 content for each project, four of them were identical. The only changes were frontmatter updates to add versioning fields. The actual project writeup content hadn’t changed at all.
I had created version archives for documentation improvements, not project evolution.
The Content Strategy Realization
This forced me to think about what versioning actually means in a portfolio context:
Version when:
- The project itself significantly evolved (new features, architecture changes)
- The writeup documents genuinely different functionality
- Preserving the historical context adds value for readers
Don’t version when:
- You’re just improving documentation quality
- The formatting or structure changed but content stayed the same
- Minor updates that belong in a changelog entry
The key insight: versioning is for project evolution, not writeup evolution.
Cleaning Up: From Six Versions to Two
I deleted the unnecessary v1-0.md files for four projects and reverted their frontmatter to v1.0. Only two projects kept their version archives:
- portfolio-website: v1.0 (initial launch) → v2.0 (versioning system + layout refactor)
- aws-security-infrastructure: v1.0 (state backend foundation) → v2.0 (compliance platform)
These genuinely represented different phases of the projects. The others? They got changelog entries instead.
The Technical Gotcha: Filenames Matter
Along the way, I discovered a fun Astro quirk. When I named files v1.0.md, the routes didn’t work. The URLs should have been /projects/project-name/v1.0/, but they were returning 404s.
Turns out Astro’s glob loader treats dots as file extensions. My v1.0.md file was being interpreted as a file with extension .0.md, producing a slug of v10 instead of v1.0.
The fix: Use dashes in filenames (v1-0.md) but display dots in the UI (v1.0). Document the convention and move on.
// File structure
project-name/v1-0.md → Route: /projects/project-name/v1-0/
// UI display
"v1.0" (from frontmatter version field)
Small detail, but it would have tripped up anyone maintaining this system later.
User Experience Decisions
With the technical foundation working, I had to think about how users would actually interact with versioning.
The Jarring Banner Problem
My first design for previous version pages was a big orange banner at the top: “You’re viewing a previous version!” It was… loud. On a documentation site with hundreds of archived versions, this makes sense. On a portfolio with maybe 2-3 versioned projects, it felt like yelling.
Solution: Integrate the version notice into the Version History dropdown component. Current pages show a dropdown listing previous versions. Previous pages show the same component but with “Viewing Previous Version v1.0” and a link back to current. Same design language, less visual noise.
Two-Column Layout for Long Content
Project writeups were getting long—this one is 600+ lines of Markdown. A single-column layout meant endless scrolling with no navigation landmarks.
Solution: Documentation-style two-column layout on desktop. Sticky table of contents in the left sidebar (220px), content on the right. On mobile, the TOC collapses into an expandable <details> element at the top.
The sticky TOC uses CSS Grid and scroll-spy JavaScript to highlight the current section. It’s the same pattern you see on sites like MDN, Astro’s docs, or Tailwind’s documentation.
Breadcrumbs for Context
With nested routes (/projects/portfolio-website/v1-0/), users needed orientation. Where am I? How do I get back?
Added a simple breadcrumb component: Home → Projects → Portfolio Website. Nothing fancy, but it provides the mental model for navigation.
The Automation Layer
Manually creating version archives, updating frontmatter, and maintaining consistency across files is tedious. So I automated it.
A new slash command (/create-previous-version) handles the entire workflow:
- Asks which project to archive
- Reviews git history for undocumented changes
- Creates the v1-0.md snapshot with proper frontmatter
- Updates index.md with majorVersions reference
- Commits both files with a conventional commit message
The automation also includes content strategy logic—it warns if the v1.0 and index.md content are identical (suggesting a changelog entry instead of a full version archive).
Lessons for Portfolio Platform Thinking
1. Design for Your Actual Update Frequency
A portfolio isn’t a documentation site with daily releases. Most projects get written up once and updated maybe 2-3 times over their lifetime. Versioning infrastructure should match this reality—lightweight, not enterprise-grade.
2. Content Strategy Deserves Engineering Time
I spent as much time thinking about when to version as I did building how to version. The technical implementation was straightforward. The content decisions were harder.
3. UX Consistency Beats Feature Richness
Having a separate banner for previous versions felt like a feature. But it broke visual consistency with current version pages. Integrating both states into the same component reduced code and improved user experience.
4. Document Your Conventions
The dash-vs-dot filename convention is non-obvious. Future me (or anyone forking this portfolio) would be confused without documentation. Encoding conventions in automation (the slash command enforces v1-0.md format) prevents drift.
5. Clean Up Your Experiments
I could have left all six projects as v2.0 with v1-0.md archives. It was working. But “working” isn’t the same as “correct.” Taking time to clean up the over-versioning made the system more honest and maintainable.
The Final Architecture
After all the iteration:
src/content/projects/
├── portfolio-website/
│ ├── index.md ← v2.0 (current)
│ └── v1-0.md ← v1.0 (archived)
├── aws-security-infrastructure/
│ ├── index.md ← v2.0 (current)
│ └── v1-0.md ← v1.0 (archived)
├── aws-security-lab/
│ └── index.md ← v1.0 (no versioning needed)
└── [4 more projects without versioning]
Components:
- VersionHistory.astro: Dropdown for current pages, “view current” link for previous pages
- Changelog.astro: Renders version history timeline
- Breadcrumbs.astro: Navigation context
Schema fields:
version: Display version (e.g., “v2.0”)versionStatus: “current” | “previous”majorVersions: Array of previous versions with dates and summarieschangelog: Array of all updates (major and minor)
Was It Worth It?
For two versioned projects? Probably overkill from a pure ROI perspective.
But the exercise forced me to think carefully about:
- How my portfolio should grow over time
- What “version” means for project documentation
- How to balance feature completeness with simplicity
- When automation adds value vs. complexity
These are the same decisions you make building any content platform. The portfolio just happens to be a small, safe place to practice them.
Plus, when the AWS Security Infrastructure evolves again (v3.0 with Security Hub integration?), the versioning system will be ready. And I’ll have the v2.0 documentation preserved as a historical artifact—not lost to an overwrite.
Building a portfolio is surprisingly good practice for platform thinking. Every feature decision—even something as simple as versioning—has UX, content strategy, and maintenance implications. The best systems are the ones that make the right thing easy and the wrong thing hard.