Astro content collections
The mechanism that keeps this site’s taxonomy honest: every Markdown file belongs to a
collection, and every collection has a Zod schema. Frontmatter is
validated at build time — a decision without a status, a garden note with a typo’d
stage, a tip with a malformed date all fail the build before they can reach
production.
const decisions = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/decisions' }),
schema: z.object({
title: z.string(),
status: z.enum(['proposed', 'accepted', 'superseded', 'deprecated']),
date: z.coerce.date(),
}),
});
Things this bought me on day one:
- The enum on
statusis the ADR lifecycle documentation. z.coerce.date()accepts2026-07-19in YAML and hands templates a realDate.- Collections are queryable (
getCollection('decisions', filter)), which is what makes the homepage’s unified stream a ten-line function instead of a filesystem crawl.
This decision exists because of the constraints in naming things‘s sibling problem: structure that isn’t machine-enforced decays. See it live in ADR-001.