TL;DR
- One site's blog pulls posts from Airtable; this site's pulls from local MDX.
- Routes, components, types, and tests are identical between them.
- The only meaningful difference is one file — the source module. That's the test of the abstraction, and it echoes Anthropic's "stay simple" guidance for agents.
Two blogs, one shape#
We already had a blog on another site (an Airtable-backed content site). When this site needed a blog, the tempting move was to copy the code and rip out Airtable. The disciplined move was to ask: what actually has to change?
The answer turned out to be one file.
The boundary that did the work#
Everything in the blog depends on a single type, BlogPost, and two functions: listPublishedPosts() and getPostBySlug(). The route that renders an article doesn't know whether the post came from a database, a file, or a fixture. It only knows the shape.
That boundary is the entire trick. As long as the source module returns BlogPost objects, the rest of the system — the index page, the article page, the table of contents, the share-card generator, the RSS feed — neither knows nor cares where they came from.
Why "change one file" beats "build something clever"#
This restraint isn't just our taste. It's the same instinct Anthropic's applied-AI team argues for in Building Effective Agents. Erik Schluntz and Barry Zhang open with a warning that lands just as hard in plain software architecture:
"Success in the LLM space isn't about building the most sophisticated system."
— Erik Schluntz & Barry Zhang, Anthropic, Building Effective Agents
Their prescription is to add machinery only when you've earned it:
"Start with simple prompts, optimize them with comprehensive evaluation, and add multi-step agentic systems only when simpler solutions fall short."
— Erik Schluntz & Barry Zhang, Anthropic, Building Effective Agents
Swap "agentic systems" for "abstractions" and it's the same rule. We didn't build a plugin system or a generic content framework. We built the smallest seam that made the source swappable — one type, two functions — and stopped. Porting then meant rewriting one module, not negotiating with a framework.
What changed, and what didn't#
Swapping Airtable for local MDX touched exactly the concrete source module: instead of an HTTP call to a table, it reads content/blog/*.mdx and parses frontmatter. Same return type. Same contract.
What did not change: the routes, the components, the BlogPost type, the cleaning logic, the tests. We diffed the two source.ts files side by side and that diff is the feature. Everything else was reuse.
The fixtures earned their keep twice#
We ship a bundled fixture post in both sites. It does two jobs. In production it's the fallback — if the content source ever throws (a bad token, a renamed table, a malformed file), the site degrades to bundled content instead of returning a 500. In testing it's the harness — we can test the routes without standing up the real source at all.
A broken source should never take down a marketing page. The fixture is what makes that a guarantee instead of a hope. (Unattended robustness is a recurring theme — see The Stop-hook loop that ran for six hours.)
Why the model makes this matter more, not less#
It's fair to ask whether architecture discipline still matters when the model is this capable. It matters more. On SWE-bench Verified, Claude Opus 4.5 resolves 80.9% of real GitHub issues and Sonnet 4.5 resolves 77.2%. When an agent can implement almost anything you describe, the bottleneck shifts from "can it write the code" to "is the system shaped so a change stays small." A clean boundary is what keeps an extremely fast implementer from making an extremely fast mess.
Why this matters beyond blogs#
This is the same principle behind every skill in the library: write the contract once, keep the swappable part small, and prove the seam by actually swapping it. We didn't believe the abstraction was clean — we changed the source and watched everything else stay still. That's the only proof that counts. The skills themselves encode this discipline so you don't have to rediscover it — that's the whole pitch in Stop re-teaching Claude your coding style.
If your next "we'll just copy and modify" task feels big, look for the boundary first. The honest version of "port it" is often "rewrite one file."
How to start#
- Define the one type your features depend on before you write the features.
- Put every concrete source behind a single module — make it the only thing routes import.
- Ship a fixture fallback so a source outage degrades instead of crashing.
- Want these patterns as installable skills? Grab the starter pack or see the full library, and start with Your first 20 minutes with cyberg7-skills.
More build stories in the Case studies pillar.