Skip to content

Instantly share code, notes, and snippets.

@ollieread
Created March 8, 2026 18:56
Show Gist options
  • Select an option

  • Save ollieread/4a41d908297cd289d96bb1a9eeb54b2d to your computer and use it in GitHub Desktop.

Select an option

Save ollieread/4a41d908297cd289d96bb1a9eeb54b2d to your computer and use it in GitHub Desktop.

Content Repository

This repository is the source of truth for all written content and structured metadata for ollieread.com.

Goals

  • Write everything in plain Markdown + YAML
  • Keep content versioned in Git
  • Keep structure machine-readable
  • Minimise duplication (especially for Laravel release/change coverage)

Directory Structure

articles/                          # Standalone articles
  <slug>.md                        # Single-file format (Markdown with front matter)
  <slug>/                          # Directory format
    article.yaml                   # Article metadata
    article.md                     # Article content (no front matter)
    _notes.md                      # Optional personal notes (not published)

series/                            # Series definitions (YAML)

courses/                           # Mini-courses (directories)
  <course-slug>/
    course.yaml                    # Course metadata
    01-chapter-slug.md             # Course chapters (Markdown)
    02-another-chapter.md
    _notes.md                      # Optional personal notes (not published)

knowledgebase/                     # Short informational snippets
  <slug>.md                        # Single-file format (Markdown with front matter)

projects/                          # Software projects
  <slug>/                          # Directory format only
    project.yaml                   # Project metadata
    project.md                     # Project description/overview (no front matter)
    _notes.md                      # Optional personal notes (not published)

changes/                           # Atomic meaningful changes
  <org>/
    <repo>/
      <change-id>.yaml             # Single-file format
      <change-id>.brief.md         # Optional short writeup (single-file)
      <change-id>.deep.md          # Optional detailed writeup (single-file)
      <change-id>/                 # Directory format
        change.yaml                # Change metadata
        brief.md                   # Optional short writeup
        deep.md                    # Optional detailed writeup
        _notes.md                  # Optional personal notes (not published)

changesets/                        # Per-version collections
  <org>/
    <repo>/
      <version>.yaml

categories.yaml                    # Article categories (format taxonomy)
topics.yaml                        # Topic hierarchy (subject taxonomy)

Concepts Overview

Concept Purpose Location
Article A single published piece of content articles/<slug>.md
Series An ordered collection of related articles series/<slug>.yaml
Course A structured mini-course with chapters courses/<slug>/
Knowledgebase Atomic snippets connected via typed links and topics knowledgebase/<slug>.md
Project A software project you've built or maintain projects/<slug>/
Category What kind of article it is (format/intent) categories.yaml
Topic What the content is about (subject) topics.yaml
Change A single meaningful change to a project changes/<org>/<repo>/<id>.yaml
Changeset All changes that shipped in a version changesets/<org>/<repo>/<version>.yaml

Key Distinctions

Category vs Topic: Categories describe the format or editorial intent (e.g., "deep-dive", "tutorial", "release notes"). Topics describe the subject matter (e.g., "laravel/http/routing"). An article has exactly one category but can have multiple topics.

Change vs Changeset: A Change is a single semantic change (which may span multiple commits/PRs). A Changeset is the collection of all changes that shipped in a specific version. This separation allows the same change to appear in multiple versions (backports) without duplication.

Series vs Course: A Series is a loose collection of related articles that can be read independently. A Course is a structured learning path with ordered chapters that build on each other.

Article vs Knowledgebase: An Article is a full piece of content with narrative structure, context, and depth. A Knowledgebase entry is an atomic snippet — a single concept, technique, or piece of information — connected to other entries via typed links and topics to form a navigable graph. Think of articles as "posts" and knowledgebase entries as "nodes in a wiki".

Project vs Article: A Project is a structured reference page for a piece of software you've built or maintain. It is not narrative content — it's metadata-driven, with links, version info, and connections to related content. Articles, series, knowledgebase entries, and courses can belong to a project via the relationships.project field.


Shared Fields

All authored content types (articles, series, courses, knowledgebase entries, and projects) share a common set of field groups. These are defined once here and referenced by each content type. Changes and changesets use some of the same formats but have their own schema structure.

Each group is a YAML object. Content types may extend a group with type-specific fields nested alongside the shared ones.

content — Publication & Presentation

Controls how the content appears on the site. Every authored content type has this group.

content:
  slug: my-content-slug
  status: published
  summary: >
    A short description for cards, listings, and search results.

Shared Fields

Field Type Required Description
slug string URL slug. Should match the filename or directory name.
status enum Publication status (see below)
summary string Short description for cards/listings (1-2 sentences)

Publication Status Values

These values apply to all content types and describe whether the content is visible on the site:

Value Description
draft Work in progress, not publicly visible
published Live and publicly accessible
archived No longer promoted but still accessible
unlisted Accessible via direct link only

Not all content types use every value. Series and courses typically use draft, published, and archived. Knowledgebase entries use draft, published, and archived.

Type-Specific Extensions

Content types may add fields to this group:

Field Used by Description
category Article Category ID from categories.yaml
kind Knowledgebase What type of snippet (tip, pattern, gotcha, etc.)
tagline Project One-liner subtitle for display beneath the title

classification — Subject & Filtering

Describes what the content is about and provides filtering facets.

classification:
  topics:
    - laravel/http/routing
    - laravel/database/eloquent
  tags:
    - routing
    - eloquent

Fields

Field Type Description
topics array Topic paths from topics.yaml. Assign the most specific (leaf) topics; parent topics are implicit.
tags array Flat, cross-cutting labels for filtering. Don't duplicate information already captured by topics.

provenance — Origin & Applicability

Tracks where the knowledge comes from and which versions it applies to.

provenance:
  versions:
    laravel/framework: ">=10.0"
    php: ">=8.1"
  sources:
    - type: pr
      repo: laravel/installer
      ref: "390"

Fields

Field Type Description
versions object Version constraints by package (see below)
sources array Links to PRs, commits, issues that this content derives from (see below)

Both fields are optional. Use versions when the content is version-specific. Use sources when the content is traceable to specific code changes.

Version Constraints

The versions field maps package names to Composer-style version constraints:

provenance:
  versions:
    laravel/framework: ">=10.0"
    laravel/installer: ">=5.12.0 <=5.16.0"
    php: ">=8.2"

Common constraint formats:

  • >=10.0 — version 10.0 or higher
  • ^10.0 — version 10.x (semver compatible)
  • >=10.0 <12.0 — between versions (exclusive upper bound)
  • >=5.12.0 <=5.16.0 — between versions (inclusive upper bound)
  • * — all versions

Source Object

Field Type Required Description
type enum Type of source reference
repo string Repository in org/repo format
ref string Reference (PR number, commit SHA, compare range)
note string Additional context

Source Type Values

Value Description Example ref
pr Pull request 390
commit Single commit abc123def456
compare Comparison range v12.0.0...v12.1.0
issue GitHub issue 9876

relationships — Connections to Other Content

Links this content to other content in the repository.

relationships:
  project: sprout-for-laravel
  related:
    - slug: service-container-basics
      type: knowledgebase
      rel: builds-on
    - slug: understanding-multitenancy-patterns
      type: article
      rel: see-also

Fields

Field Type Description
project string Project slug if this content belongs to a project
related array Typed links to other content (see below)

The project Field

When a series has a project field, its articles inherit the project association. Articles may also declare project explicitly. If both the article and its series specify a project, the article's value takes precedence.

The publishing system should collect all content associated with a project by:

  1. Finding all content with an explicit relationships.project field matching the project slug
  2. Finding all content belonging to a series that has a matching relationships.project field
  3. Combining these with the project's own relationships.related entries
  4. Deduplicating (explicit project field content takes priority over related)

Related Links

Each entry in related is an object:

Field Type Required Description
slug string Slug of the linked content
type enum Content type of the target. Defaults to the current content type if omitted.
rel enum Relationship type. Defaults to related if omitted.

Content type values: article, series, course, knowledgebase, project

Relationship Types

Value Description
builds-on This entry assumes knowledge from the linked entry
extends This entry adds to the linked entry, without requiring it
alternative A different approach to the same problem
related General connection
supersedes This entry replaces the linked entry
corrects This entry fixes a misconception from the linked entry
see-also Loosely related, "you might also want this"

These relationships are directional — builds-on means this entry builds on that one. The publishing system can infer reverse relationships for graph traversal (e.g., if A builds-on B, then B is a prerequisite for A).

dates — Timestamps

Tracks when content was created and last updated, plus type-specific dates.

dates:
  created_at: 2026-01-22
  updated_at: 2026-01-25

Shared Fields

Field Type Description
created_at date ISO date when first created
updated_at date ISO date of last significant update

Type-Specific Extensions

Field Used by Description
published_at Article ISO date when first published
started_at Project ISO date when the project started

Article

An article is a single published piece of content. Articles can be stored in two formats: a single Markdown file with front matter, or a directory with separate metadata and content files.

Location

Single-file format:

articles/<slug>.md

Directory format:

articles/<slug>/
  article.yaml       # Article metadata
  article.md         # Article content (no front matter)
  _notes.md          # Optional personal notes (not published)

The filename or directory name should match the slug in the metadata.

Use the directory format when you want to keep notes alongside an article, or when the front matter becomes unwieldy.

Schema

Articles use all shared field groups plus these article-specific fields:

title: Laravel's Route Model Binding

content:
  slug: laravels-route-model-binding
  status: published
  summary: >
    A source-driven walkthrough of how Laravel resolves
    route parameters into model instances.
  category: deep-dive

classification:
  topics:
    - laravel/http/routing/route-model-binding
    - laravel/container/binding
  tags:
    - routing
    - eloquent
    - internals

provenance:
  versions:
    laravel/framework: ">=11.0"
  sources:
    - type: pr
      repo: laravel/framework
      ref: "48210"

relationships:
  project: sprout-for-laravel
  related:
    - slug: manual-service-resolution-in-laravel
      type: article
      rel: see-also

dates:
  created_at: 2026-01-22
  published_at: 2026-01-22
  updated_at: 2026-01-25

# Article-specific
canonical_url: https://example.com/original-post

Article-Specific Fields

Field Type Description
content.category string Required. Category ID from categories.yaml
canonical_url string Original URL if republished from elsewhere
dates.published_at date ISO date when first published

Required Fields

title, content.slug, content.status, content.category

Example (Single-file Format)

---
title: Laravel's Route Model Binding

content:
  slug: laravels-route-model-binding
  status: published
  summary: >
    A source-driven walkthrough of how Laravel resolves
    route parameters into model instances.
  category: deep-dive

classification:
  topics:
    - laravel/http/routing/route-model-binding
    - laravel/container/binding
  tags:
    - routing
    - eloquent

dates:
  created_at: 2026-01-20
  published_at: 2026-01-22
  updated_at: 2026-01-25


---

Laravel has many undocumented features...

Example (Directory Format)

articles/laravels-route-model-binding/article.yaml

title: Laravel's Route Model Binding

content:
  slug: laravels-route-model-binding
  status: published
  summary: >
    A source-driven walkthrough of how Laravel resolves
    route parameters into model instances.
  category: deep-dive

classification:
  topics:
    - laravel/http/routing/route-model-binding
    - laravel/container/binding
  tags:
    - routing
    - eloquent

dates:
  created_at: 2026-01-20
  published_at: 2026-01-22
  updated_at: 2026-01-25

articles/laravels-route-model-binding/article.md

Laravel has many undocumented features...

articles/laravels-route-model-binding/_notes.md

## Research notes

- Check if this changed in Laravel 11
- Link to Taylor's PR comment about the naming
- TODO: Add diagram of resolution flow

The _notes.md file is for personal notes and research — it is not published or processed.


Series

A series is an ordered collection of related articles. Articles in a series can still be read independently but are presented together as a cohesive set.

Location

series/<slug>.yaml

Schema

Series use the shared field groups plus these series-specific fields:

name: hidden-parts-of-laravel
title: The Hidden Parts of Laravel

content:
  slug: hidden-parts-of-laravel
  status: published
  summary: >
    Exploring Laravel's undocumented features and hidden functionality.

classification:
  topics:
    - laravel/internals

relationships:
  project: sprout-for-laravel

dates:
  created_at: 2026-01-10

# Series-specific
image: series/hidden-parts-of-laravel.png
series_status: ongoing
articles:
  - laravels-route-model-binding
  - manual-service-resolution-in-laravel
  - laravel-middleware-priority

Series-Specific Fields

Field Type Required Description
name string Internal identifier (immutable, usually matches slug)
image string Path to series cover image
series_status enum Completion status of the series
articles array Ordered list of article slugs

Required Fields

name, title, content.slug, articles

Series Status Values

The series_status field describes the completion state of the series, separate from whether it is published on the site (content.status):

Value Description
ongoing New articles may be added
complete Series is finished
abandoned No longer being updated

Example

name: hidden-parts-of-laravel
title: The Hidden Parts of Laravel

content:
  slug: hidden-parts-of-laravel
  status: published
  summary: >
    Exploring Laravel's undocumented features and hidden functionality
    that you won't find in the official docs.

classification:
  topics:
    - laravel/internals
    - laravel/undocumented

dates:
  created_at: 2026-01-10

image: series/hidden-parts-of-laravel.png
series_status: ongoing

articles:
  - laravels-route-model-binding
  - manual-service-resolution-in-laravel
  - laravel-middleware-priority
  - decorating-services-in-laravel

Course

A course is a structured mini-course with ordered chapters. Unlike a series, courses are designed to be followed sequentially and may be gated (paid/premium).

Location

courses/<course-slug>/
  course.yaml           # Course metadata
  01-introduction.md    # First chapter
  02-next-chapter.md    # Second chapter
  ...
  _notes.md             # Optional personal notes (not published)

Chapter filenames should be prefixed with numbers for sorting (e.g., 01-, 02-).

The _notes.md file is for personal notes and research — it is not published or processed.

Schema

Courses use the shared field groups plus these course-specific fields:

name: laravel-app-bootstrapping
title: Laravel Bootstrapping Internals

content:
  slug: laravel-app-bootstrapping
  status: published
  summary: >
    A short, practical course covering the modern Laravel bootstrap pipeline.

classification:
  topics:
    - laravel/lifecycle/bootstrapping
    - laravel/http/kernel

relationships:
  project: sprout-for-laravel

dates:
  created_at: 2026-02-01

# Course-specific
level: advanced
price_gbp: 29
image: courses/laravel-bootstrapping.png
prerequisites:
  - laravel-service-container-basics
chapters:
  - 01-introduction.md
  - 02-the-bootstrap-pipeline.md
  - 03-middleware-assembly.md
  - 04-request-lifecycle.md

Course-Specific Fields

Field Type Required Description
name string Internal identifier (immutable)
level enum Difficulty level
price_gbp number Price in GBP (omit for free)
image string Path to course cover image
prerequisites array Course slugs that should be completed first
chapters array Ordered list of chapter filenames

Required Fields

name, title, content.slug, chapters

Level Values

Value Description
beginner No prior knowledge required
intermediate Assumes basic framework familiarity
advanced Requires solid understanding of concepts

Chapter Format

Chapters are Markdown files with optional front matter.

---
title: The Bootstrap Pipeline
slug: the-bootstrap-pipeline
summary: How Laravel assembles and executes its bootstrap sequence.
---

Chapter content here...

Chapter Field Reference

Field Type Required Description
title string Chapter title (defaults to filename)
slug string URL slug (defaults to filename without prefix)
summary string Short description
draft boolean If true, chapter is not yet ready

If front matter is omitted entirely, the chapter title is derived from the filename (e.g., 02-the-bootstrap-pipeline.md → "The Bootstrap Pipeline").

Example

courses/laravel-app-bootstrapping/course.yaml

name: laravel-app-bootstrapping
title: Laravel Bootstrapping Internals

content:
  slug: laravel-app-bootstrapping
  status: published
  summary: >
    A short, practical course covering the modern Laravel bootstrap pipeline,
    from entry point to first controller.

classification:
  topics:
    - laravel/lifecycle/bootstrapping
    - laravel/http/kernel

dates:
  created_at: 2026-02-01

level: advanced
price_gbp: 29
image: courses/laravel-bootstrapping.png

prerequisites:
  - laravel-service-container-basics

chapters:
  - 01-introduction.md
  - 02-the-bootstrap-pipeline.md
  - 03-middleware-assembly.md
  - 04-request-lifecycle.md

courses/laravel-app-bootstrapping/01-introduction.md

---
title: Introduction
summary: What we'll cover and why it matters.
---

Every Laravel request follows a specific path through the framework...

Knowledgebase

A Knowledgebase entry is an atomic snippet of knowledge — a single concept, technique, gotcha, or piece of reference information. Entries are connected to each other via typed links and to the broader content via topics, forming a navigable graph of lookup-friendly knowledge.

Unlike articles, knowledgebase entries are not narrative. They're designed to be found, read quickly, and traversed. The value comes from the connections as much as the individual entries.

Use knowledgebase entries for:

  • Quick reference notes about framework features
  • Code patterns and idioms
  • Gotchas and common pitfalls
  • Configuration snippets with explanation
  • Definitions of terms and concepts
  • Worked examples of specific techniques

Location

Knowledgebase entries use the single-file format only:

knowledgebase/<slug>.md

Schema

Knowledgebase entries use all shared field groups plus content.kind:

---
title: Extending Services with afterResolving

content:
  slug: extending-services-with-after-resolving
  status: published
  summary: >
    How to safely extend Laravel services using the container's
    afterResolving callback.
  kind: pattern

classification:
  topics:
    - laravel/container/resolution
    - laravel/package-development
  tags:
    - dependency-injection
    - service-providers

provenance:
  versions:
    laravel/framework: ">=9.0"
  sources:
    - type: pr
      repo: laravel/framework
      ref: "41205"

relationships:
  project: sprout-for-laravel
  related:
    - slug: service-container-basics
      rel: builds-on
    - slug: creating-custom-drivers
      rel: see-also
    - slug: extending-via-macros
      rel: alternative

dates:
  created_at: 2026-01-22
  updated_at: 2026-01-25
---

Knowledgebase-Specific Fields

Field Type Required Description
content.kind enum What type of snippet this is

Required Fields

title, content.slug, content.status, content.kind

Kind Values

Value Description
tip A quick, actionable piece of advice
pattern A reusable code pattern or idiom
gotcha A common pitfall or surprising behaviour
reference Factual reference information
snippet A useful code snippet with context
example A worked example of a specific technique
definition Explanation of a term or concept
cheatsheet A quick-reference table or list

Related Links

In knowledgebase entries, type defaults to knowledgebase when omitted, so entries linking to other entries only need slug and rel:

relationships:
  related:
    - slug: service-container-basics
      rel: builds-on
    - slug: understanding-multitenancy-patterns
      type: article
      rel: see-also

Example

knowledgebase/laravel-installer-defaults-to-sqlite-for-starter-kits.md

---
title: Laravel installer defaults to SQLite for starter kits

content:
  slug: laravel-installer-defaults-to-sqlite-for-starter-kits
  status: draft
  summary: >
    When installing Laravel through the installer, using a starter kit,
    it will always overwrite that starter kit's default database settings
    to use SQLite, unless you explicitly specify the database.
  kind: gotcha

classification:
  topics:
    - laravel/getting-started
  tags:
    - laravel-installer
    - starter-kit
    - sqlite

provenance:
  versions:
    laravel/installer: ">=5.12.0"
  sources:
    - type: pr
      repo: laravel/installer
      ref: "390"

dates:
  created_at: 2026-02-14
---

Starting with version `5.12` of the Laravel installer, the database will always
be SQLite...

Project

A Project is a piece of software you've built or maintain — an open-source package, a tool, a product. Each project gets its own page with a description, links, metadata, and connections to related content across the repo.

Projects differ from articles in that they're not narrative content — they're structured reference pages for things you' ve made. The content body is a freeform description/overview, but the metadata carries the heavy lifting for display, linking, and discoverability.

Projects have two separate status concepts: publication status (content.status) controls whether the listing is visible on the site, while lifecycle describes the current state of the software itself.

Location

Projects always use the directory format:

projects/<slug>/
  project.yaml       # Project metadata
  project.md         # Project description/overview (no front matter)
  _notes.md          # Optional personal notes (not published)

The directory name should match the slug in the metadata.

Schema

Projects use all shared field groups plus project-specific fields:

title: Sprout for Laravel

content:
  slug: sprout-for-laravel
  status: published
  summary: >
    Sprout is an easy to use multitenancy solution that integrates
    seamlessly with your Laravel application, while remaining flexible.
  tagline: Multitenancy for your Laravel application

classification:
  topics:
    - laravel/multitenancy
    - laravel/package-development
  tags:
    - laravel
    - multitenancy
    - open-source

provenance:
  versions:
    laravel/framework: ">=10.0"
    php: ">=8.2"

relationships:
  related:
    - slug: understanding-multitenancy-patterns
      type: article
      rel: see-also
    - slug: tenant-identification-strategies
      type: knowledgebase
      rel: see-also
    - slug: multitenancy-fundamentals
      type: series
      rel: see-also

dates:
  created_at: 2024-03-15
  started_at: 2024-03-15

# Project-specific
lifecycle: active
links:
  website: https://sprout.ollieread.com
  docs: https://sprout.ollieread.com/docs/1.x/installation
  github: https://github.com/sprout-laravel
  packagist: https://packagist.org/packages/sprout-laravel/sprout
current_version: "1.1"
image: projects/sprout-for-laravel.png

Project-Specific Fields

Field Type Required Description
content.tagline string One-liner subtitle for display beneath the title
lifecycle enum Current state of the software
links object Named external links (freeform keys, URL values)
current_version string Latest stable version
image string Path to project image/logo
dates.started_at date ISO date when the project started

Required Fields

title, content.slug, content.status, lifecycle

Lifecycle Values

The lifecycle field describes the state of the software itself, independent of whether the listing is visible on the site:

Value Description
active Actively maintained and developed
stable Maintained but not actively adding features
experimental Early stage, API may change
archived No longer maintained

Links

The links field is a flat object of named URLs. Keys are freeform, but these are conventional:

Key Description
website Project homepage
docs Documentation
github GitHub repository
packagist Packagist listing
npm npm listing
demo Live demo

Any key is valid — the publishing system should render them all.

Example

projects/sprout-for-laravel/project.yaml

title: Sprout for Laravel

content:
  slug: sprout-for-laravel
  status: published
  summary: >
    Sprout is an easy to use multitenancy solution that integrates
    seamlessly with your Laravel application, while remaining flexible.
  tagline: Multitenancy for your Laravel application

classification:
  topics:
    - laravel/multitenancy
    - laravel/package-development
  tags:
    - laravel
    - multitenancy
    - open-source

provenance:
  versions:
    laravel/framework: ">=10.0"
    php: ">=8.2"

relationships:
  related:
    - slug: understanding-multitenancy-patterns
      type: article
      rel: see-also
    - slug: tenant-identification-strategies
      type: knowledgebase
      rel: see-also

dates:
  created_at: 2024-03-15
  started_at: 2024-03-15

lifecycle: active
current_version: "1.1"
image: projects/sprout-for-laravel.png

links:
  website: https://sprout.ollieread.com
  docs: https://sprout.ollieread.com/docs/1.x/installation
  github: https://github.com/sprout-laravel
  packagist: https://packagist.org/packages/sprout-laravel/sprout

projects/sprout-for-laravel/project.md

Sprout is a multitenancy package for Laravel that takes a different approach
to most existing solutions. Rather than forcing a specific tenancy strategy,
it provides the building blocks to implement whichever approach fits your
application best.

## Key Features

- Seamless integration with Laravel's service container
- Support for multiple tenancy strategies (domain, path, header, etc.)
- Database-per-tenant and shared-database support
- Tenant-aware queue jobs and scheduled tasks

projects/sprout-for-laravel/_notes.md

## TODO

- Add migration guide from v1.0 to v1.1
- Screenshot of dashboard for image field
- Link to the devlog series once it's started

The _notes.md file is for personal notes and research — it is not published or processed.


Category

Categories describe what kind of thing an article is — the format or editorial intent, not the subject matter.

An article has exactly one category.

Location

categories.yaml

Schema Reference

<category-id>:
  name: Category Name
  slug: category-slug
  description: What this category represents.
  title: Display Title      # Optional
  sort_order: 0             # Optional
  is_visible: true          # Optional
  icon: icon-name           # Optional
  color: "#hex"             # Optional

Field Reference

Field Type Required Description
name string Display name
slug string URL slug
description string Explanation of what this category represents
title string Optional display title override
sort_order integer Position in ordered listings
is_visible boolean Whether the category appears in listings
icon string Icon identifier for UI
color string Hex colour for UI

Example

meta:
  name: Meta
  slug: meta
  description: Posts about the site, the content, and the process.
  sort_order: 0
  is_visible: true

deep-dive:
  name: Deep Dive
  slug: deep-dive
  description: In-depth, source-driven exploration of framework internals.
  sort_order: 3
  is_visible: true
  icon: microscope
  color: "#6366f1"

Topic

Topics describe what the content is about — the subject matter. Topics are hierarchical (a tree structure) and referenced by their full path.

Content can have multiple topics. Assign the most specific (leaf) topics; parent topics are implicit.

Location

topics.yaml

Schema Reference

Topics are nested objects. Each level can have:

<topic-id>:
  name: Topic Name
  description: What this topic covers.
  children:
    <child-topic-id>:
      name: Child Topic
      children:
        ...

Field Reference

Field Type Required Description
name string Display name
description string Explanation of what this topic covers
children object Nested child topics

Referencing Topics

Topics are referenced by their path from root to leaf, separated by /:

laravel/http/routing/route-model-binding

When assigning topics to content, use the most specific path. The publishing system should automatically associate content with parent topics.

Example

laravel:
  name: Laravel
  description: The Laravel PHP framework.
  children:
    http:
      name: HTTP Layer
      description: Request handling, routing, and responses.
      children:
        routing:
          name: Routing
          description: URL routing and route configuration.
          children:
            route-model-binding:
              name: Route Model Binding
              description: Automatic model resolution from route parameters.
            route-parameters:
              name: Route Parameters
              description: Capturing and constraining URL segments.
            url-generation:
              name: URL Generation
              description: Generating URLs to named routes.
        middleware:
          name: Middleware
          description: Request/response pipeline filters.
          children:
            middleware-priority:
              name: Middleware Priority
              description: Controlling middleware execution order.
            global-middleware:
              name: Global Middleware
              description: Middleware applied to all requests.
        requests:
          name: Requests
          description: HTTP request handling and input.
        responses:
          name: Responses
          description: HTTP response generation.
    container:
      name: Service Container
      description: Dependency injection and service resolution.
      children:
        binding:
          name: Binding
          description: Registering services in the container.
        resolution:
          name: Resolution
          description: Resolving services from the container.
    lifecycle:
      name: Application Lifecycle
      description: Bootstrapping and request lifecycle.
      children:
        bootstrapping:
          name: Bootstrapping
          description: Application initialization process.
        service-providers:
          name: Service Providers
          description: Registering and booting application services.

php:
  name: PHP
  description: PHP language features and patterns.
  children:
    attributes:
      name: Attributes
      description: PHP 8+ attributes and metadata.
    enums:
      name: Enums
      description: PHP 8.1+ enumerations.

Change

A Change represents a single meaningful change to a project (framework, app skeleton, package). It is the atomic unit of "what changed" and can be referenced by multiple changesets.

Changes use the same topics, tags, and sources formats defined in Shared Fields, but have their own schema structure rather than the grouped field objects used by authored content types.

Location

Single-file format:

changes/<org>/<repo>/<change-id>.yaml
changes/<org>/<repo>/<change-id>.brief.md    # Optional short writeup
changes/<org>/<repo>/<change-id>.deep.md     # Optional detailed writeup

For the single-file format, brief and deep writeups are companion files that sit alongside the YAML file, sharing the same base name.

Directory format:

changes/<org>/<repo>/<change-id>/
  change.yaml      # Change metadata
  brief.md         # Optional short writeup (2-6 lines)
  deep.md          # Optional detailed writeup
  _notes.md        # Optional personal notes (not published)

Use the directory format when you want to keep notes alongside the change, or when you prefer the files grouped together.

Examples:

  • changes/laravel/framework/collection-lazy-by-default.yaml
  • changes/laravel/framework/collection-lazy-by-default.brief.md
  • changes/laravel/laravel/public-disk-url-fallback/change.yaml

Schema Reference

# Required
name: collection-lazy-by-default
project: laravel/framework
title: Collections use lazy evaluation by default

# Classification (all required)
type: feature
scope: runtime
impact: high
audience: broad
magic: low

# Recommended
summary: >
  The Collection class now defers evaluation until iteration,
  improving memory usage for large datasets.

# Optional
topics:
  - laravel/collections
tags:
  - laravel-12
  - performance
  - breaking-change
deprecated: false
breaking: true
security: false

# Sources (at least one recommended)
sources:
  - type: pr
    repo: laravel/framework
    ref: "12345"
  - type: commit
    repo: laravel/framework
    ref: abc123def456

Note: On changes, project refers to the repository in org/repo format — this is different from relationships.project on authored content types, which refers to a project slug within this repository.

Field Reference

Field Type Required Description
name string Unique identifier (should be descriptive and stable)
project string Repository in org/repo format
title string Human-readable title of the change
type enum Type of change
scope enum What part of the system it affects
impact enum How significantly it affects users
audience enum How many users it affects
magic enum How "magical" or implicit the behaviour is
summary string One-paragraph explanation
topics array Related topic paths (same format as Shared Fields)
tags array Cross-cutting labels (same format as Shared Fields)
deprecated boolean Marks something as deprecated
breaking boolean Is this a breaking change?
security boolean Is this a security fix?
sources array Links to commits, PRs, comparisons (same format as Shared Fields)

Type Values

Value Description
feature New functionality
bugfix Bug fix
perf Performance improvement
dx Developer experience improvement
refactor Internal refactoring (no behaviour change)
docs Documentation change
security Security fix
deprecation Deprecates existing functionality

Scope Values

Value Description
install Affects fresh installations only
runtime Affects running applications
tooling Affects CLI tools, commands, dev workflow
docs Documentation only
tests Test suite only

Impact Values

Value Description
low Minimal effect, most users won't notice
medium Noticeable improvement or change
high Significant change that many users will want to know about

Audience Values

Value Description
niche Affects a small subset of users
some Affects users of specific features
broad Affects most users

Magic Values

Value Description
low Explicit, predictable behaviour
medium Some implicit behaviour
high Significant implicit/automatic behaviour

Example (Single-file Format)

changes/laravel/laravel/public-disk-url-app-url-fallback.yaml

name: public-disk-url-app-url-fallback
project: laravel/laravel
type: bugfix
scope: install
impact: medium
audience: broad
magic: low

title: Public disk URL now trims APP_URL and falls back safely

summary: >
  Prevents malformed `//storage` URLs and avoids null deprecation
  warnings when APP_URL is missing.

topics:
  - laravel/config/filesystems

tags:
  - laravel-12
  - config
  - filesystems

breaking: false
security: false

sources:
  - type: commit
    repo: laravel/laravel
    ref: abcdef123456
  - type: compare
    repo: laravel/laravel
    ref: v12.11.0...v12.11.2

changes/laravel/laravel/public-disk-url-app-url-fallback.brief.md

The `public` disk now properly handles missing or malformed `APP_URL` values. Previously, an unset `APP_URL` would cause
deprecation warnings, and URLs with trailing slashes would produce `//storage` paths.

Example (Directory Format)

changes/laravel/framework/collection-lazy-by-default/change.yaml

name: collection-lazy-by-default
project: laravel/framework
type: feature
scope: runtime
impact: high
audience: broad
magic: medium

title: Collections use lazy evaluation by default

summary: >
  The Collection class now defers evaluation until iteration,
  improving memory usage for large datasets.

topics:
  - laravel/collections

tags:
  - laravel-12
  - performance
  - breaking-change

breaking: true
security: false

sources:
  - type: pr
    repo: laravel/framework
    ref: "54321"

changes/laravel/framework/collection-lazy-by-default/brief.md

Collections now use lazy evaluation by default. Operations like `map`, `filter`, and `transform` are deferred until the
collection is iterated.

changes/laravel/framework/collection-lazy-by-default/deep.md

## What Changed

Previously, every Collection method that transformed data would execute immediately and allocate a new array. With this
change, transformation methods return a lazy wrapper that only processes elements as they're consumed.

## Why It Matters

For large datasets, this dramatically reduces memory usage. A chain like `$collection->map(...)->filter(...)->take(10)`
now only processes the elements needed to produce 10 results, rather than transforming the entire collection twice.

## Migration Notes

If your code relies on side effects during collection operations, you may need to add `->all()` or `->values()` to force
eager evaluation.

changes/laravel/framework/collection-lazy-by-default/_notes.md

## Research

- Taylor mentioned this was inspired by Rust iterators
- Check if this affects `Collection::macro()` behaviour
- TODO: Benchmark comparison for the deep writeup

The _notes.md file is for personal notes and research — it is not published or processed.


Changeset

A Changeset represents all the changes that shipped in a specific version. It references changes by their IDs rather than duplicating information.

Location

changesets/<org>/<repo>/<version>.yaml

For example:

  • changesets/laravel/framework/12.11.0.yaml
  • changesets/laravel/laravel/12.11.2.yaml

Schema Reference

# Required
name: laravel-framework-12.11.0
project: laravel/framework
version: 12.11.0

# Recommended
published_at: 2026-01-22
links:
  compare: https://github.com/laravel/framework/compare/v12.10.0...v12.11.0
  release: https://github.com/laravel/framework/releases/tag/v12.11.0

# Optional
summary: Performance improvements and new Collection methods.
highlights:
  - collection-lazy-by-default
  - query-builder-upsert-returning

# Change list (order can indicate priority)
changes:
  - collection-lazy-by-default
  - query-builder-upsert-returning
  - middleware-priority-config
  - blade-fragment-caching
  - artisan-make-enum-command

Field Reference

Field Type Required Description
name string Unique identifier for this changeset
project string Repository in org/repo format
version string Version string
published_at date ISO date when the version was released
links object Related URLs
summary string One-line summary of the release
highlights array Change IDs to feature prominently
changes array All change IDs included in this version

Links Object

Field Type Description
compare string GitHub compare URL
release string GitHub release page URL
blog string Official blog post URL
upgrade string Upgrade guide URL

Example

changesets/laravel/framework/12.11.0.yaml

name: laravel-framework-12.11.0
project: laravel/framework
version: 12.11.0
published_at: 2026-01-22

summary: Performance improvements and new Collection methods.

links:
  compare: https://github.com/laravel/framework/compare/v12.10.0...v12.11.0
  release: https://github.com/laravel/framework/releases/tag/v12.11.0

highlights:
  - collection-lazy-by-default
  - query-builder-upsert-returning

changes:
  - collection-lazy-by-default
  - query-builder-upsert-returning
  - middleware-priority-config
  - blade-fragment-caching
  - artisan-make-enum-command
  - validator-sometimes-closure
  - http-client-retry-callback

Range Coverage

Ranges (e.g., "12.10.0 → 12.11.2") should be computed by reading multiple per-version changesets. Only create a dedicated range file if it's editorial/curated content (upgrade guide, cross-repo bundle, weekly digest).


Markdown Blocks

This repository uses custom block syntax (Markdoc-style) for structured content that goes beyond standard Markdown.

Block Syntax

{% blockName attr="value" %}
Content
{% /blockName %}

Some blocks have required child blocks:

{% parent %}
{% child %}...{% /child %}
{% /parent %}

Available Blocks

ref — Inline Reference

Marks inline code as a semantic reference with optional linking.

This resolves via {% ref type="class" value="Illuminate\\Container\\Container" %}.
Attribute Required Description
type Reference type (see below)
value Canonical identifier
text Display text (defaults to value)
href Explicit URL override
title Tooltip text
project Repository hint (e.g., laravel/framework)
version Version hint for resolution
short Boolean to use a short version

Type Values: class, method, function, type, variable, config, env, file, topic, article, knowledgebase, change, pr, commit, compare, colour, key

callout — Notice Box

Semantic notice box for tips, warnings, etc.

{% callout type="warning" title="Order matters" %}
Middleware priority can override group ordering in surprising ways.
{% /callout %}
Attribute Required Description
type note, tip, info, warning, danger
title Heading text
icon Presentation hint

code — Enhanced Code Block

A self-closing tag that adds metadata to a standard markdown code fence.

```php {% code filename="bootstrap/app.php" highlight="12-30" /%}
<?php
// ...
```

The tag appears on the same line as the opening fence, after the language identifier. The tag is self-closing (/%}).

Attribute Required Description
filename Displayed filename/header
highlight Line ranges (e.g., 3-7 or 3,5,8-12)
focus Boolean for "focus mode" presentation
class The class that code originally sits in
method The method that the code was taken from
start The line number start

tabs / tab — Tabbed Content

Side-by-side content alternatives.

{% tabs %}
{% tab title="Laravel 11" %}
Content for Laravel 11…
{% /tab %}
{% tab title="Laravel 12" %}
Content for Laravel 12…
{% /tab %}
{% /tabs %}

tabs attributes:

Attribute Description
style Presentation hint (pill, underline)

tab attributes:

Attribute Required Description
title Tab label
id Stable identifier for deep linking

include — Reusable Snippets

Include content from another file.

{% include file="snippets/container-resolution.md" %}
Attribute Required Description
file Path relative to repo root

changeset — Version Changes

Render changes for a released version.

{% changeset project="laravel/framework" version="12.11.0" mode="highlights" %}
Attribute Required Description
project Repository (org/repo)
version Version string
mode highlights, full, grouped, upgrade, deep

Mode Values:

Mode Description
highlights Top items from highlights list
full All changes
grouped Grouped by type/scope/topic
upgrade Medium/high impact changes only
deep Includes .deep.md writeups where available

For ranges:

{% changesetRange project="laravel/framework" from="12.10.0" to="12.11.2" mode="full" %}

change — Single Change

Render a single change by ID.

{% change id="collection-lazy-by-default" mode="summary" %}
Attribute Required Description
id Change ID
mode brief, summary, deep

correction — Error Correction

Capture corrections without rewriting history.

{% correction title="Model binding timing" date="2026-01-25" %}
{% old %}
I originally said model binding happens *before* middleware.
{% /old %}
{% new %}
Model binding happens *after* the route is matched, during route resolution.
{% /new %}
{% /correction %}

correction attributes:

Attribute Description
title Short label
date When the correction was made
reason Brief justification

Child blocks:

Block Required Description
old The incorrect content
new The corrected content

Writing Conventions

Categories

  • Categories describe format/intent, not subject
  • An article has exactly one category
  • Choose the category that best describes what the article is

Topics

  • Topics describe subject matter
  • Assign the most specific (leaf) topics
  • Parent topics are implicit — don't duplicate them manually
  • Content can have multiple topics

Tags

  • Keep tags flat and minimal
  • Use tags for cross-cutting facets: performance, security, breaking-change, octane, postgres
  • Don't duplicate information already captured by topics

IDs and Slugs

  • IDs should be stable and semantic
  • Prefer descriptive IDs over version-based ones for changes
  • Slugs should be URL-safe (lowercase, hyphens, no special characters)

Source of Truth Rules

  1. Markdown/YAML in this repo is canonical — all other representations are derived
  2. Never store derived data — HTML, search indexes, excerpts are generated downstream
  3. The publishing app consumes this repo — it should not modify it
  4. Git is the history — don't manually track revision history in files

Quick Reference

Adding an Article

Single-file format:

  1. Create articles/<slug>.md
  2. Add front matter with title, content (slug, status, category), classification (topics)
  3. Write content

Directory format:

  1. Create articles/<slug>/
  2. Create article.yaml with metadata
  3. Create article.md with content
  4. Optionally add _notes.md for personal notes

Adding a Series

  1. Create series/<slug>.yaml
  2. Define name, title, content (slug, status, summary)
  3. List article slugs in articles array

Adding a Course

  1. Create courses/<slug>/
  2. Create course.yaml with name, title, content (slug, status, summary), chapters
  3. Create chapter files (01-intro.md, etc.)
  4. Optionally add _notes.md for personal notes

Adding a Knowledgebase Entry

  1. Create knowledgebase/<slug>.md
  2. Add front matter with title, content (slug, status, kind), classification (topics)
  3. Add relationships.related links to related entries
  4. Write content

Adding a Project

  1. Create projects/<slug>/
  2. Create project.yaml with title, content (slug, status, tagline), lifecycle, links
  3. Create project.md with description/overview
  4. Optionally add _notes.md for personal notes
  5. Optionally set relationships.project: <slug> on related articles, series, knowledgebase entries, or courses

Adding a Laravel Release Writeup

  1. Create change entries for new meaningful changes:
    • Single-file: changes/laravel/<repo>/<change-id>.yaml (plus optional .brief.md/.deep.md)
    • Directory: changes/laravel/<repo>/<change-id>/ with change.yaml (plus optional brief.md/deep.md/ _notes.md)
  2. Create changeset in changesets/laravel/<repo>/<version>.yaml
  3. Write release article referencing the changeset via block tags

Adding a New Category

  1. Add entry to categories.yaml
  2. Include name, slug, description

Adding a New Topic

  1. Add to topics.yaml in the appropriate hierarchy
  2. Include name and optional description
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment