<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ding Xiang</title>
    <description>The latest articles on DEV Community by Ding Xiang (@dingzc2026).</description>
    <link>https://dev.to/dingzc2026</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4233585%2Fceb3bb11-adeb-4401-9ab8-5bc52a35523d.png</url>
      <title>DEV Community: Ding Xiang</title>
      <link>https://dev.to/dingzc2026</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dingzc2026"/>
    <language>en</language>
    <item>
      <title>Running 82 Tiny Websites as One Person</title>
      <dc:creator>Ding Xiang</dc:creator>
      <pubDate>Sun, 08 Nov 2026 13:28:16 +0000</pubDate>
      <link>https://dev.to/dingzc2026/running-82-tiny-websites-as-one-person-40dl</link>
      <guid>https://dev.to/dingzc2026/running-82-tiny-websites-as-one-person-40dl</guid>
      <description>&lt;p&gt;By Ding Xiang&lt;/p&gt;

&lt;p&gt;I run a pool of 82 small independent websites by myself. 73 sit on their own domains; 9 live on pages.dev subdomains. Every one of them is a static site, built with Astro and deployed to Cloudflare Pages.&lt;/p&gt;

&lt;p&gt;That probably sounds like the opposite of focus. Conventional advice says pick one product, pour everything into it, and ignore the rest. I tried to think that way, but my working style kept pulling me somewhere else: I like small, finished things. A tiny site with one job can be designed, built, launched, and understood end to end. A large site asks for a roadmap before it gives you feedback.&lt;/p&gt;

&lt;p&gt;So instead of one big site, I built a pool. The current goal is to reach 100 sites. Along the way, stronger sites cross-link to weaker ones, so the pool behaves a bit like a small ecosystem rather than 82 unrelated side projects.&lt;/p&gt;

&lt;p&gt;This is the engineering log of what that actually takes — and the quota incident that taught me the most expensive lesson so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why lots of small sites?
&lt;/h2&gt;

&lt;p&gt;Each site in the pool has a narrow job. That constraint is the whole point.&lt;/p&gt;

&lt;p&gt;When a site does one thing, the page structure stays obvious. The content model stays small. The failure modes stay contained: if I break a template on one project, I have not taken down some monolith that everything depends on. If an idea turns out to be weak, retiring it is a small decision instead of a funeral.&lt;/p&gt;

&lt;p&gt;There is also a learning effect. Repeating the loop — register the idea, set up the project, write the pages, deploy, submit for indexing, check what happened — turns vague skills into routines. Domain setup, metadata, sitemaps, canonical URLs, redirects, image handling: none of these are hard alone, but doing them carelessly across many sites teaches you exactly where your process leaks.&lt;/p&gt;

&lt;p&gt;The honest trade-off is maintenance surface. 82 of anything is a lot. The pool only works because the sites share as much machinery as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  One template, many small configs
&lt;/h2&gt;

&lt;p&gt;All the sites are static Astro builds on Cloudflare Pages. Under the hood, they lean on a shared-template approach: common layout pieces, shared styling conventions, shared SEO plumbing, shared deployment habits. What differs per site lives in configuration — the site's name, domain, navigation, content collections, feature toggles, and the small bits of identity that keep a site from feeling like a clone of its siblings.&lt;/p&gt;

&lt;p&gt;That separation matters more than any single framework choice. When a fix belongs in the template, I make it once and let it flow to the sites that need it. When a change belongs to one site, it stays in that site's config and content instead of becoming a special case in shared code.&lt;/p&gt;

&lt;p&gt;It also keeps me honest about scope. If a new idea needs a wildly different stack, it probably is not a pool site. The pool is for small, durable, mostly-static things. Experiments that need servers, queues, or user accounts belong somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  The D1 incident
&lt;/h2&gt;

&lt;p&gt;Most of the pool is static, but some pieces use Cloudflare D1, and that is where I got burned.&lt;/p&gt;

&lt;p&gt;D1's free tier counts rows read and rows written. That sounds abstract until you watch a day's quota disappear. I had a table with about 83k rows, and some pages ran full-table GROUP BY aggregations against it — the kind of query that feels harmless in local development, because the answer comes back and nothing complains.&lt;/p&gt;

&lt;p&gt;In production, every one of those page loads scanned the table. Crawler traffic plus normal visits added up fast, and the aggregation queries burned through the whole day's read quota in roughly 3 hours. After that, the database simply refuses to cooperate until the quota resets.&lt;/p&gt;

&lt;p&gt;No users were yelling at me — most of these are tiny sites — but the lesson landed anyway: on a metered free tier, an unindexed full-table scan is not a performance smell. It is a budget fire.&lt;/p&gt;

&lt;h2&gt;
  
  
  The precompute rule
&lt;/h2&gt;

&lt;p&gt;The fix was not "optimize the query a little." The fix was to change the shape of the work.&lt;/p&gt;

&lt;p&gt;If a number does not change between requests, it has no business being computed during a request. Totals, top lists, and grouped counts are now precomputed and stored, so pages read a finished answer instead of scanning raw rows. Static aggregates belong in the build or in a small precomputed table, not in a GROUP BY that runs for every visitor and every crawler.&lt;/p&gt;

&lt;p&gt;The companion rule covers writes: batch writes are planned to use no more than 60–70% of the daily free quota. That buffer sounds wasteful until you need room for a mistake, a retry, or a second run on the same day. Free tiers reward people who leave slack in the system.&lt;/p&gt;

&lt;p&gt;If you take one idea from this post, take that one: on metered infrastructure, design for the counter, not just for the request. The counter is always running, and crawlers are very good at finding your most expensive query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting 82 sites discovered
&lt;/h2&gt;

&lt;p&gt;Building the sites turned out to be the easy half. Getting them discovered is slower, less satisfying work.&lt;/p&gt;

&lt;p&gt;For indexing, I submitted sitemaps to Google Search Console per site, and pushed a batch of 225 URLs across the pool through IndexNow. A concrete example from the pool is &lt;a href="https://getpicforge.com" rel="noopener noreferrer"&gt;PicForge&lt;/a&gt;, a set of image tools that runs entirely in the browser — useful, finished, and exactly the kind of small site the pool exists for.&lt;/p&gt;

&lt;p&gt;Submission, though, is not the same as discovery. A submitted URL is a request, not a result. That is why I have a discoverability retest scheduled for November 19, 2026. The retest will check which of the submitted pages search engines can actually find, whether the per-site sitemaps line up with what is discoverable, and whether the cross-links from stronger sites are helping weaker ones surface at all.&lt;/p&gt;

&lt;p&gt;If the retest shows the submissions did little, that is useful too. A pool of small sites is, among other things, a cheap way to run many small experiments and keep the ones that produce evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintenance is a calendar problem
&lt;/h2&gt;

&lt;p&gt;People imagine the hard part of a site pool is building site after site. It is not. The hard part is remembering that all of them exist.&lt;/p&gt;

&lt;p&gt;My maintenance rule is boring on purpose: template changes are treated as pool changes, not page tweaks. If I adjust shared metadata handling, I expect the change to make sense for a tool page, a content page, and a plain landing page. If it only makes sense for one site, it probably belongs in that site's config. New pages get the same unglamorous checklist as new sites: title, description, canonical URL, sitemap entry, and a quick look at the deployed result rather than the local build. Most pool failures are not dramatic crashes. They are small inconsistencies repeated patiently across many projects.&lt;/p&gt;

&lt;p&gt;That is also why the pool has to stay mostly static. Every dynamic feature multiplies the number of things that can drift, expire, or consume quota while I am working on something else. A static page can wait for me. A neglected database cannot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell another solo builder
&lt;/h2&gt;

&lt;p&gt;Start smaller than your ambition. A finished tiny site teaches more than an unfinished platform.&lt;/p&gt;

&lt;p&gt;Share the machinery, not the identity. Templates and deployment routines should be shared aggressively; each site's purpose and content should stay its own.&lt;/p&gt;

&lt;p&gt;Treat free tiers as systems with counters. Read the quota model before you design the data layer, precompute anything static, and never let a crawler-discoverable page run an unbounded aggregation.&lt;/p&gt;

&lt;p&gt;And keep a date on the calendar to check whether any of it worked. Building is loud; verification is quiet. The pool runs on both.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloudflare</category>
      <category>astro</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
