How this portfolio is engineered
Why this site treats content as data, validates it at build time, and derives everything else from a single source of truth.
Loading article…
Why this site treats content as data, validates it at build time, and derives everything else from a single source of truth.
Loading article…
Most portfolio sites slowly drift out of sync. The homepage claims one thing, the projects page claims another, and a statistic like "5 projects shipped" quietly becomes wrong the moment a sixth one launches. Nobody notices until a visitor does.
I wanted a site where that kind of drift is structurally impossible: every fact has exactly one place it can be edited, and every page that depends on that fact reads it from the same source.
The first decision was to stop treating content as markup. Projects, skills, experience, and even navigation live as typed data modules, not as JSX sprinkled through components. A component's job is to render data; it has no business also being the place where facts live.
The second decision follows from the first: if content is data, it can be validated like data. Every module is checked against a schema when the site builds, and a build with invalid content simply does not produce a deploy. That distinction matters more than it sounds. A missing field or a malformed date is not a runtime surprise a visitor stumbles into; it is a build failure an editor sees immediately, before anything ships.
content modules
(source of truth)
│
▼
schema validation
(build fails on bad data)
│
▼
derived data
(counts, links, related items)
│
▼
pages
This is the part I'm most proud of, because it is where the "single source of truth" idea actually pays off. Nothing that can be computed is hand maintained. If a fact can be derived from existing data, it is derived, not retyped.
In practice that means:
The practical result is that adding a project is a single edit. The grid, the filters, the search index, the related-content links, and the homepage counters all update themselves, because they were never independently maintained facts to begin with. I no longer have a class of bugs where two pages disagree about the same thing, because there is only one place that thing is written down.
A reasonable question, since "content as data" is exactly what a CMS sells. I chose plain, typed files instead because this portfolio has exactly one editor: me. A CMS earns its complexity when multiple non-technical people need to publish content, or when content changes more often than code does. Neither is true here. Version-controlled TypeScript gives me autocompletion, a diffable history, and compile-time validation for free, and it costs nothing to run. Bringing in a CMS would have added a service to operate without solving a problem I actually have.
That trade-off is representative of how the rest of the site is built.
There is no state-management library, because the state is mostly server data with nowhere interesting to go. There is no runtime syntax highlighter, because code blocks can be highlighted once at build time and shipped as plain HTML. Server-only code exists only where the platform genuinely requires it, not by default.
None of these are framework decisions; they generalize past whatever this site happens to be built with today. The underlying rule is simple: every dependency has an ongoing cost, in bundle size, in upgrade churn, in the number of things that can break. Unless a library removes real complexity, a simpler solution wins. That discipline is what makes the more ambitious parts of the site, like the case-study system, safe to build without the foundation getting harder to reason about.
The goal was never to build the most sophisticated portfolio possible. It was to build one where adding a project, fixing a date, or updating a statistic is a single, safe edit, and where that edit automatically shows up everywhere it should. The architecture is not the interesting part on its own. It exists so that everything built on top of it stays predictable as the site grows.