AboutCapabilitiesPortfolioExplore
Projects
Building My Portfolio Into a Publishing System
From Static Site to Publishing System
Inventing a Content Model for Real Projects
Publishing Dense Technical Work Without Watering It Down
Owning the Seams: Plugin Forks and Toolchain Drift
Turning the Site into a Portfolio Editorial Machine
LLM Tooling for Portfolio Writing
The benefits of trad-coding

Inventing a Content Model for Real Projects

2nd article in Building My Portfolio Into a Publishing System
Software Writing Typescript Gatsby Project Solutions Land
2019-11-23

Once I moved to Gatsby, the problem stopped being “how do I have a site?” and became “how do I structure a site that can actually hold the work I want to publish?”

Built a content-driven portfolio model with tags, series, clusters, page-generation rules, and stronger TypeScript-checked structure.

That sounds abstract, but it turned into very concrete repo work: tags, clusters, series, custom page generation, frontmatter rules, generated GraphQL types, and a lot of awkward navigation logic.

This was the point where the repo stopped being mostly about presentation and started becoming about content architecture.

Gatsby gave me power, then immediately asked me to earn it

The initial Gatsby era felt simple only because I stayed on pretty well-trodden paths at first.

But as soon as I wanted the site to reflect real project relationships instead of just a list of pages, the complexity jumped.

The late-2019 commit run tells that story pretty clearly:

  • Adding a post and post handling
  • Creating a tag based taxonomy implemented in frontmatter
  • Adding tag pages
  • Refactor of taxonomy to include parent/children, adding some content backlog
  • Switch to typescript for gatsby-node and generate graphql types
  • Rebuilding site in terms of marketing, pages, series, cluster, articles
  • Creating a secondary nav
  • Cluster layout on cluster pages
  • Attempting another reset, what does it take cmon man

Those are not random feature commits. That is the repo trying to discover its own information model.

I was no longer publishing posts. I was publishing relationships.

The big shift was realizing that a lot of my work did not fit a flat blog structure.

A project can have:

  • a landing page
  • multiple sub-articles
  • tags
  • parent-child relationships
  • cross references
  • series ordering
  • a need for custom navigation that does not feel dumb

In my terms, a series is a set of articles that belong to one project arc and should be read in some order, while a cluster is a grouping page that collects related content without implying a strict sequence.

That is exactly the kind of thing that starts sounding small until you implement it.

My create-pages logic kept getting more complex because the actual content relationships were more complex.

I eventually switched to TypeScript and generated GraphQL types because I wanted the type checker to help me answer very basic but important questions like: what node shape is available here, what fields exist, and what content type am I actually looking at?

A concrete example is that page generation was no longer just “list markdown files and make pages.” It had to branch differently for articles, series, clusters, and standalone pages, while keeping navigation and metadata coherent. That move shows up in the late-2019 TypeScript and GraphQL type-generation work, and I still think it was the right call.

Stronger structure was not academic. It was self-defense.

At some point I stopped wanting “flexible content” and started wanting rules.

The modern src/gatsby/create-pages.ts still shows the outcome of that instinct:

if (o.sort !== null && o.sort !== undefined && !isCommonListSort(o.sort)) {
  throw new FrontmatterError(
    "sort must be one of date+, date-, title+, title-, order+, order-"
  )
}

and more broadly:

const FRONTMATTER_CHECK: Record<
  PostType,
  (o: unknown) => o is NodeFrontmatter
> = {
  article: isMarkdownArticleFrontmatter,
  series: isMarkdownSeriesFrontmatter,
  cluster: isMarkdownClusterFrontmatter,
  page: isMarkdownPageFrontmatter,
}

That is not there because I love bureaucracy. It is there because content systems get slippery fast if you do not make the allowed shapes explicit.

I was building enough page-generation logic that I wanted validation to happen early and loudly.

I also added runtime type guards around some of this logic because taxonomy mistakes are sneaky. They do not always crash in an obvious way. Sometimes they just make the site structure subtly wrong.

A representative shape from the repo looks roughly like this:

---
title: "Some project"
type: "series"
path: "/projects/some-project/"
parent: "/projects/"
sort: "date+"
---

paired with child articles that point back to that parent path. That is the part I wanted to make explicit and machine-checkable instead of relying on memory.

Every Layout helped here too

One detail the repo cannot really capture emotionally is how happy I was with Every Layout during this phase.

Working with those primitives made site building feel much more predictable. That mattered because Gatsby itself was teaching me the opposite lesson.

Gatsby was powerful, but it was also opaque.

I still chose it over alternatives at that point because React familiarity plus Gatsby’s page model felt like the fastest route to custom behavior without having to build the whole publishing layer from scratch.

It likes to own the whole pipeline and expose pieces of it. That can work fine until you want to know exactly what inputs are available for a page, how a node got there, or why a particular transform is happening. Then it starts to feel like you are debugging an ecosystem diagram instead of a program.

So there was a funny split in the experience:

  • layout work started feeling cleaner and more principled
  • content pipeline work started feeling more indirect and weird

That tension is all over the history.

I was also shaping the site around scarcity

A detail I think is worth admitting: part of the early sparse design was meant to make the lack of content less obvious.

That is not the whole reason for the design, but it was part of the psychology.

I wanted the site to feel intentional before it was large.

This is one place where a portfolio repo is more revealing than a finished portfolio. You can see the scaffolding. You can see me trying to make a system that would still look respectable while it was being filled in.

The most honest commit message in this phase

One of the most honest commit messages from that period is Attempting another reset, what does it take cmon man.

That is the kind of commit message I would not manufacture for a retrospective, which is exactly why I like it as evidence.

By that point the repo had accumulated enough content-architecture complexity that resets and rethinks were part of the normal process. The site was no longer a simple arrangement of templates. It had real internal structure, and changes to that structure had consequences everywhere.

Result

This phase produced something much more valuable than a prettier website.

It produced a site that knew the difference between:

  • an article
  • a series
  • a cluster
  • a page
  • a tag view

and could generate navigation and structure from that instead of pretending everything was the same shape.

That gave me a place to publish projects in a way that actually matched how I think about them.

It also set up a lot of the later pain. Once you build a custom content model inside Gatsby, you stop being just a user of the framework. You become the person maintaining the boundary between your real publishing needs and Gatsby’s idea of how content should flow.

That boundary is where a lot of the later fights happened.

Previous Next
Related Building My Portfolio Into a Publishing System:
From Static Site to Publishing System Inventing a Content Model for Real Projects Publishing Dense Technical Work Without Watering It Down Owning the Seams: Plugin Forks and Toolchain Drift Turning the Site into a Portfolio Editorial Machine
Featured Work
Welding PositionerSurface Grinder Retrofit
Company Info
About UsContactAffiliate DisclosurePrivacy Policy
Specific Solutions LLC
Portland, OR