<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>The Draftbase Example Blog</title><description>A statically generated blog powered by Draftbase.</description><link>https://demo-blog.draftbase.co/</link><item><title>Write the boring version first</title><link>https://demo-blog.draftbase.co/posts/write-the-boring-version-first/</link><guid isPermaLink="true">https://demo-blog.draftbase.co/posts/write-the-boring-version-first/</guid><description>The abstraction you skip today is the one you would have got wrong anyway.</description><pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Nobody has ever been paged because a function was too obvious.&lt;/p&gt;
&lt;h2 id=&quot;two-implementations-then-a-pattern&quot;&gt;Two implementations, then a pattern&lt;/h2&gt;
&lt;p&gt;The rule that survives contact with real codebases: write it inline the first time, write
it inline again the second time, and extract on the third — when you can finally see which
parts actually vary.&lt;/p&gt;
&lt;h2 id=&quot;the-cost-nobody-counts&quot;&gt;The cost nobody counts&lt;/h2&gt;
&lt;p&gt;An abstraction built on one example encodes one example&apos;s assumptions. The second caller
bends to fit. The third adds a flag. Now the shared helper has three modes and every change
to it is a change to everything.&lt;/p&gt;
&lt;h2 id=&quot;what-lazy-actually-means&quot;&gt;What lazy actually means&lt;/h2&gt;
&lt;p&gt;Not fewer features. Fewer moving parts per feature — which is the only kind of simplicity
that is still there in a year.&lt;/p&gt;</content:encoded><category>process</category></item><item><title>Pagination is a content decision</title><link>https://demo-blog.draftbase.co/posts/pagination-is-a-content-decision/</link><guid isPermaLink="true">https://demo-blog.draftbase.co/posts/pagination-is-a-content-decision/</guid><description>Cursor or offset is an implementation detail. How many items belong on a page is not.</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most pagination arguments are about mechanics — cursors versus offsets, stable
sorts, the total count query nobody needs. Those matter, but they are downstream of a
question the engineering team usually skips: what is a page for?&lt;/p&gt;
&lt;h2 id=&quot;cursors-briefly&quot;&gt;Cursors, briefly&lt;/h2&gt;
&lt;p&gt;Offset pagination re-scans rows it has already skipped and shifts under you when something
is inserted. Cursor pagination hands you an opaque marker and continues from there. For an
API that feeds a build, cursors are the right default — you are walking the whole set once,
in order.&lt;/p&gt;
&lt;h2 id=&quot;the-part-that-is-not-mechanical&quot;&gt;The part that is not mechanical&lt;/h2&gt;
&lt;p&gt;Five posts per page is a reading decision. So is whether the archive is paginated at all,
or is one long list that a browser&apos;s find-in-page can search. Ask what a reader is doing on
that screen before picking a number.&lt;/p&gt;</content:encoded><category>architecture</category></item><item><title>Build-time secrets are still secrets</title><link>https://demo-blog.draftbase.co/posts/build-time-secrets/</link><guid isPermaLink="true">https://demo-blog.draftbase.co/posts/build-time-secrets/</guid><description>A static site has no server to leak from, which makes it easy to forget the build had credentials.</description><pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Static output is reassuring: there is no runtime, so there is nothing to
compromise at runtime. The build is a different story.&lt;/p&gt;
&lt;h2 id=&quot;the-three-ways-it-goes-wrong&quot;&gt;The three ways it goes wrong&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;The key gets a public prefix. Every framework has one — &lt;code&gt;PUBLIC_&lt;/code&gt;, &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt;,
&lt;code&gt;VITE_&lt;/code&gt; — and it means &quot;inline this into the browser bundle&quot;. Renaming a variable is
enough to publish a credential.&lt;/li&gt;
&lt;li&gt;The key gets committed. Usually in a &lt;code&gt;.env&lt;/code&gt; that was never added to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The key is over-scoped. A read-only delivery key that leaks is an inconvenience. A
management key that leaks is an incident.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-to-do-about-it&quot;&gt;What to do about it&lt;/h2&gt;
&lt;p&gt;Use the narrowest scope that can build the site. Keep write-capable keys off CI entirely —
if seeding needs one, run it from a laptop. And grep your build output for the key before
you trust the setup, because that check takes five seconds and assumptions do not.&lt;/p&gt;</content:encoded><category>security</category><category>ci</category></item><item><title>Your CMS should not know what your website looks like</title><link>https://demo-blog.draftbase.co/posts/cms-should-not-know-your-layout/</link><guid isPermaLink="true">https://demo-blog.draftbase.co/posts/cms-should-not-know-your-layout/</guid><description>Page builders are a trap. Model the content, then decide separately how it renders.</description><pubDate>Mon, 02 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The fastest way to make content unusable is to store it as a layout.&lt;/p&gt;
&lt;h2 id=&quot;symptoms&quot;&gt;Symptoms&lt;/h2&gt;
&lt;p&gt;You know you have this problem when moving a post to a new design means opening every
entry, or when the answer to &quot;can we put this on the mobile app too?&quot; is no.&lt;/p&gt;
&lt;h2 id=&quot;model-the-shape-not-the-page&quot;&gt;Model the shape, not the page&lt;/h2&gt;
&lt;p&gt;A blog post has a title, a body, an author and a date. That is true whether it renders as
a web page, an RSS item, a newsletter, or a card in an app. The two-column layout with the
pull quote on the right is a rendering decision, and it belongs in the code that renders.&lt;/p&gt;
&lt;h2 id=&quot;the-test&quot;&gt;The test&lt;/h2&gt;
&lt;p&gt;Ask whether you could rebuild the front end from scratch, in a different framework,
without touching a single entry. If the answer is no, the layout has leaked into the
content.&lt;/p&gt;</content:encoded><category>cms</category><category>architecture</category></item><item><title>The static site is back, and it never really left</title><link>https://demo-blog.draftbase.co/posts/static-sites-are-back/</link><guid isPermaLink="true">https://demo-blog.draftbase.co/posts/static-sites-are-back/</guid><description>Rendering pages at build time is not a nostalgia trip. It is the cheapest way to serve content that changes a few times a day.</description><pubDate>Wed, 14 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every few years the industry rediscovers that most pages do not change between
requests. A marketing site, a documentation set, a blog: the content shifts when someone
publishes, not when someone visits.&lt;/p&gt;
&lt;h2 id=&quot;what-you-actually-pay-for&quot;&gt;What you actually pay for&lt;/h2&gt;
&lt;p&gt;A server-rendered page costs you compute on every request, plus the operational surface
that comes with it — scaling rules, cold starts, a database that has to stay up. A static
page costs you one build.&lt;/p&gt;
&lt;h2 id=&quot;where-it-stops-working&quot;&gt;Where it stops working&lt;/h2&gt;
&lt;p&gt;Static rendering falls over the moment content has to be personalised per visitor, or the
catalogue is large enough that a full rebuild takes longer than the publishing cadence.
Both are real limits. Neither applies to a blog.&lt;/p&gt;
&lt;h2 id=&quot;the-practical-setup&quot;&gt;The practical setup&lt;/h2&gt;
&lt;p&gt;Content lives in a CMS. A webhook fires on publish. CI rebuilds and pushes HTML to a CDN.
The whole loop takes a minute or two, and nothing is running in between.&lt;/p&gt;</content:encoded><category>architecture</category><category>performance</category></item></channel></rss>