<?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: Shayan Ali</title>
    <description>The latest articles on DEV Community by Shayan Ali (@shayanali).</description>
    <link>https://dev.to/shayanali</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%2F4230397%2Fd71fc49b-862c-4fda-9c67-7dba214a859e.png</url>
      <title>DEV Community: Shayan Ali</title>
      <link>https://dev.to/shayanali</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shayanali"/>
    <language>en</language>
    <item>
      <title>Building a 2,135-Project Static Showcase (No Database, No Framework, No Regrets)</title>
      <dc:creator>Shayan Ali</dc:creator>
      <pubDate>Fri, 06 Nov 2026 12:11:27 +0000</pubDate>
      <link>https://dev.to/shayanali/building-a-2135-project-static-showcase-no-database-no-framework-no-regrets-32mg</link>
      <guid>https://dev.to/shayanali/building-a-2135-project-static-showcase-no-database-no-framework-no-regrets-32mg</guid>
      <description>&lt;h1&gt;
  
  
  Building a 2,135-Project Static Showcase (No Database, No Framework, No Regrets)
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;How LibreAI — a catalogue of 2,135 verified open-source AI projects with install guides, search and favourites — ships as pure static files.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;LibreAI catalogues 2,135 open-source AI projects across 17 categories. Every project has its own page: what it does, who it's for, key features, an install guide derived from the project's README, alternatives, and an FAQ. The whole thing exists in 7 language editions — roughly 15,000 pages. There is no database, no server-side rendering, and no JavaScript framework. A Python build script generates flat HTML, and a static host serves it.&lt;/p&gt;

&lt;p&gt;This post is about why that architecture won, and what it costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data is code
&lt;/h2&gt;

&lt;p&gt;The catalogue lives in Python data modules — lists of dicts, one per project, concatenated in a fixed order at build time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;REPOS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;REPOS_CORE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;EXTRA&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;V3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;EXTRA2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;EXTRA11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each record carries the fields the pages need: name, GitHub URL, category, tagline, description, features, licence, stars, forks, last-push date, the project's own website, alternatives, and — for 1,126 of the projects — a &lt;code&gt;run&lt;/code&gt; block with install steps distilled from the project's README.&lt;/p&gt;

&lt;p&gt;Data-as-code sounds primitive until you list what it buys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validation is free.&lt;/strong&gt; A build either renders a record or throws. There is no "row in a weird state" because there are no rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Derived statistics are trivial.&lt;/strong&gt; Our statistics page (star distributions, licence breakdowns, per-category counts) is computed from the same &lt;code&gt;REPOS&lt;/code&gt; list the pages are built from — one source of truth, zero chance of the stats page disagreeing with the catalogue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diffs are reviewable.&lt;/strong&gt; Adding a weekly batch of projects is a diff in a data file, readable by a human before it ships.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cost is real too: the data files are enormous, and you must never line-edit a generated single-line file by hand. We learned to treat data modules as append-only artefacts with tooling around them, not as documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification is a pipeline step, not a vibe
&lt;/h2&gt;

&lt;p&gt;Every project in the catalogue was verified against GitHub at intake: the repo exists, the licence is what we say it is, the star/fork counts and last-push date are recorded from the repo itself. Those recorded values are a &lt;em&gt;snapshot&lt;/em&gt;, and the site is honest about that — the statistics page dates itself ("October 2026") and the methodology note explains that "pushed in 2026" is a property of the snapshot, not a live query.&lt;/p&gt;

&lt;p&gt;That honesty is an architectural feature. A static site cannot pretend to be live data; designing around a dated snapshot forces you to say exactly what you know and when you knew it. 96.5% of the snapshot's 2,123 projects (the count at snapshot time; the catalogue has since grown to 2,135) had recorded a push in 2026 — a claim we can only make because the verification step stored the field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating 15,000 pages without hating your life
&lt;/h2&gt;

&lt;p&gt;The build is one Python program that walks the data and writes files: a page per project per edition, category hubs, the statistics page, sitemaps, feeds. The engineering rules that kept it sane:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One template function, no template engine.&lt;/strong&gt; Pages are built by a handful of Python functions returning strings. Boring, debuggable, and fast enough — a full build is minutes, not a CI event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never regenerate what you can't verify.&lt;/strong&gt; After each build we check the output tree: page counts per edition, sitemap coverage, and spot-checks that a repo page's install guide matches its record. Static generation fails silently in the worst way (a missing page is just… missing), so the checks are part of the build, not an afterthought.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploys are chunked file uploads, and payloads have hard limits.&lt;/strong&gt; Our deploy route rejects payloads above ~129 KB per file, so the build keeps individual files small and the deployer uploads file-by-file with digests. This constraint quietly improves the site: no giant bundled anything, ever.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The two dynamic features, and how static survived them
&lt;/h2&gt;

&lt;p&gt;A showcase needs favourites and a "request a project" form — the only two genuinely dynamic features. Both live in Supabase: an anonymous-key client talks directly from the browser, with row-level rules doing the guarding. The static pages don't know or care; if Supabase is down, the catalogue still renders perfectly and only the star button sulks. That's the correct failure hierarchy for a content site: reading is the product, interaction is garnish.&lt;/p&gt;

&lt;p&gt;Search is the other obvious question. At 2,135 records, a pre-built static index plus a few kilobytes of vanilla JS beats a search backend on every axis that matters here: latency, cost, and the dignity of not running infrastructure to grep a list.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone building a directory today
&lt;/h2&gt;

&lt;p&gt;Start with the data model and the verification step; the pages are the easy part. Publish your methodology and date your snapshots. Compute your marketing statistics from the same data you render, so you can never accidentally lie. And pick the boring stack on purpose: the interesting problem in a directory is &lt;em&gt;coverage and correctness&lt;/em&gt;, and every hour not spent on framework churn is an hour spent verifying the next hundred projects.&lt;/p&gt;

&lt;p&gt;LibreAI is live at libreaihub.vercel.app — 2,135 projects, 17 categories, 1,126 install guides. The statistics page is generated from the catalogue data itself, if you'd like to check the arithmetic behind this post.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>python</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
