Migration Plan

drewkhoury-website (Hugo / S3) → drewkhoury.com (Astro / Firebase)
Decisions: 0 / 14 Tasks: 0 / 28 Entries: 52

One site, three views.

Merge drewkhoury-website (Hugo, 22 essays + bio + GSD series) into portfolio-clean (Astro, 12 projects + 40 experiments). Result: a single site at drewkhoury.com that shows everything Drew has shipped — readable, hireable, and playable — with three switchable homepage modes for different audiences.

52
entries to migrate
14yr
range (2013–2026)
3
view modes (Almanac / Editorial / Atelier)
~3wk
build, weekend pace

Operating principles

Preserve URLs verbatim

Medium-UUID slugs stay. The Manager README URL stays. SEO and inbound links survive untouched.

Migrate everything timestamped

Every essay, talk, project, and venture — including the dormant ones. The timeline IS the artifact.

One content collection, multiple views

Almanac (default) is the chronological feed; Editorial leads with writing; Atelier leads with making. Settings toggle.

Reverse the merge direction

Portfolio's design system + Astro stack. But drewkhoury.com is the canonical domain. portfolio-drew.web.app retires.

Before / after

Today

Two sites, two stacks, two identities

  • drewkhoury.com — Hugo, hugo-clarity theme, S3-hosted. 22 essays, bio, manager README, GSD pillar.
  • portfolio-drew.web.app — Astro 6.3, themed, Firebase. 12 projects, 40 experiments, Cmd+K palette.
  • No cross-linking. Two design systems. Two URLs to share.
After

One site, one stack, layered identity

  • drewkhoury.com — Astro, Firebase, the portfolio's design system.
  • Almanac is the front door (chronological feed of everything).
  • Editorial / Atelier modes available via settings ⚙.
  • About page tells the evolution story (consulting → principal → maker).
  • portfolio-drew.web.app redirects to drewkhoury.com.

Where to start

Make the Decisions first — they unlock the migration. Then review the URL Map and Schemas. The Phases tab lays out the 4-week build sequence. Mockups show the destination.

Inventory

All 52 content entries from the Hugo site, each with a suggested new category, new URL, and migration plan. Five views over the same data. Click a pill to filter by type; click a chip to scope.

Showing 52 of 52

Decisions

14 choices that unblock migration. Your selections persist in this browser. Recommendations are pre-marked. None are locked — change your mind anytime.

Progress: 0 / 14 decisions made

Each decision shapes a migration step. Once all 14 are made, the script can run end-to-end without further input.

URL Map

Every URL on the Hugo site and what happens to it. Preserve means the URL keeps resolving on the new site (zero-risk path). Redirect means a 301 in firebase.json. New means a new surface that didn't exist before. Removed means deleted (rare).

Essays — all UUIDs preserved (22 URLs)

Reports (3) + Manager README consolidation

Notes (3)

Talks — moved from /categories/public/ to /talks/ (11 URLs)

Projects — added to existing /projects/ collection (2 URLs)

Pages — consolidations (5 URLs + redirects)

Aliases — Hugo declared these as redirect targets; preserve as redirects

New surfaces — added by the merger

Content collection schemas

Astro content collections use Zod schemas. Each content type gets its own collection with a defined front-matter shape. The migration script transforms Hugo TOML/YAML into this Astro shape.

Essay essay

What it is

Long-form thinking, 500+ words. Standard reading layout. Standalone or part of a series. URL: /essays/[slug] (or preserved Medium-UUID slug at /post/[slug]).

Source content

22 entries: 14 main essays + 8 GSD essays. All migrated verbatim, slugs preserved.

// src/content/config.ts
const essays = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    date: z.date(),
    description: z.string().optional(),
    tags: z.array(z.string()).default([]),
    series: z.string().optional(),       // 'gsd' | '3-musketeers' | 'dx' | 'one-devops-please'
    slug: z.string().optional(),        // preserved Medium UUID slug
    featureImage: z.string().optional(),
    coAuthors: z.array(z.string()).default([]),
    eraNote: z.string().optional(),      // "Written in 2019 Liatrio era, GAE/serverless has shifted since."
    status: z.enum(['active', 'archived']).default('active')
  })
});

Report report

What it is

Pillar pieces with extra craft — "the occasional ten-page report nobody asked for". Visually special in feed. URL: /reports/[slug].

Source content

3 entries: Manager README, Principals for Principals, Optimizing for DX in Cloud-Native World.

const reports = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    date: z.date(),
    description: z.string(),
    tags: z.array(z.string()).default([]),
    pinned: z.boolean().default(false),   // Manager README is pinned
    readingTime: z.number().optional(),    // minutes
    coverImage: z.string().optional(),
    tldr: z.string().optional()             // pull-quote for hero
  })
});

Note note

What it is

Short, dated, roundups. Less polished than essays. URL: /notes/[slug].

Source content

3 entries: AWS 2021 Highlights, IT Consultancy Ramblings, possibly VSM.

const notes = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    date: z.date(),
    tags: z.array(z.string()).default([]),
    eraNote: z.string().optional()
  })
});

Talk talk

What it is

Public speaking entries. Workshop / meetup / podcast / webinar. URL: /talks/[slug].

Source content

11 entries, 2013–2022.

const talks = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    date: z.date(),
    venue: z.string().optional(),         // "Melbourne Meetup", "Cloud Native Louisville"
    format: z.enum(['meetup', 'workshop', 'webinar', 'podcast', 'panel']),
    coHosts: z.array(z.string()).default([]),
    recordingUrl: z.string().url().optional(),
    slidesUrl: z.string().url().optional(),
    city: z.string().optional()
  })
});

Project project

What it is

Things Drew built. Already defined in portfolio-clean. Hugo adds 2 entries.

Source content

14 total (12 existing + The Network 2022 v1 + Ari by Design).

// Extends portfolio-clean's existing project schema with: status, era
const projects = defineCollection({
  type: 'data',
  schema: z.object({
    // existing fields...
    name: z.string(),
    slug: z.string(),
    tagline: z.string(),
    intro: z.string(),
    // new fields for the migration:
    status: z.enum(['active', 'dormant', 'archived', 'historical']).default('active'),
    era: z.string().optional(),           // "2022 v1", "2026 rebuild"
    supersededBy: z.string().optional()   // slug of newer version (e.g., the-network → the-network)
  })
});

Page page

What it is

Static surfaces not in the chronological feed. Implemented as .astro files in src/pages/, not a content collection.

Source content

5 pages: /about, /manager-readme, /hire, /now (optional), /archive.

Build phases

Four phases, roughly one weekend each. Check off as you go — progress persists. Phase 1 unblocks everything else; Phase 4 is the cutover.

Sample pages

Visual proof of what the new site looks like. Three homepage modes plus deep pages for reading, about, and a series index. Open each to flick through; they share a design system.

Homepage modes (already built)

[ FILTER CHIPS: All | Writing | Making | Speaking ] ──── 2026 ──────────────────────────── ★ ten-page report · The Manager README ▸ essay · 3 Musketeers tips ▸ project · The Guild ▸ experiment · Standup Killer ──── 2023 ──────────────────────────── ▸ essay · Tips for using 3 Musketeers ▸ ★ pinned report · The Manager README
PROPOSAL 3 · DEFAULT
The Almanac

Chronological feed of everything. The merger becomes the design.

Open mockup
┌────────────────────────────────────┐ │ FEATURED ESSAY · 14 min │ │ Optimizing for DX in a Cloud- │ │ Native World │ └────────────────────────────────────┘ RECENT WRITING │ MAKING ─ post │ ─ proj ─ post │ ─ proj [ GSD PILLAR SERIES (dark strip) ]
PROPOSAL 1 · ALT MODE
The Editorial

Writing leads. Projects credential. Hire-friendly.

Open mockup
// hi there I build small, careful things that change how teams work — tools, experiments, and the occasional ten-page report. PROJECTS ───────────────── ● devpipe | one local command ● the-guild | five personas ● vsm | maps draw themselves [ FROM THE FIELD NOTEBOOK (dark) ]
PROPOSAL 2 · ALT MODE
The Atelier

Maker leads. Writing folded in as field notebook.

Open mockup
┌──────────────────────────────────┐ │ PROPOSAL 1 · The Editorial │ │ PROPOSAL 2 · The Atelier │ │ PROPOSAL 3 · The Almanac │ │ INVENTORY · 52 entries │ └──────────────────────────────────┘
INDEX
All proposals + inventory

Landing page linking to every artifact.

Open index

Deep pages (new — to be built)

Mockups intentionally minimal — focus is on layout + IA, not pixel-perfect styling. The real build inherits from portfolio-clean's design system.

Risks register

Things that could go wrong, with a planned mitigation for each. Severity reflects impact × likelihood, not just impact.