<?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: Mohd Salahudeen</title>
    <description>The latest articles on DEV Community by Mohd Salahudeen (@salahxd).</description>
    <link>https://dev.to/salahxd</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%2F2486134%2Fd0befe46-62f7-4b25-b6c2-e82f771ad077.jpeg</url>
      <title>DEV Community: Mohd Salahudeen</title>
      <link>https://dev.to/salahxd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/salahxd"/>
    <language>en</language>
    <item>
      <title>Meet Archify</title>
      <dc:creator>Mohd Salahudeen</dc:creator>
      <pubDate>Wed, 01 Jul 2026 19:31:45 +0000</pubDate>
      <link>https://dev.to/salahxd/meet-archify-3j57</link>
      <guid>https://dev.to/salahxd/meet-archify-3j57</guid>
      <description>&lt;p&gt;A couple of weeks ago I wrote that Archify was coming and asked people to join a waitlist. That post ended with "coming soon."&lt;/p&gt;

&lt;p&gt;This one doesn't. Archify is live. It's a Chrome extension, you can install it right now, and it does the thing I promised: you hover over any web app and it tells you how the app is built — without a single byte leaving your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ten seconds, not an afternoon
&lt;/h2&gt;

&lt;p&gt;The old routine for understanding an unfamiliar app is archaeology. Open DevTools, inspect elements, dig through the network tab, hunt for source maps, trace API calls across a dozen tabs until a mental model finally clicks.&lt;/p&gt;

&lt;p&gt;Archify collapses that into a hover. Install it, open any site, and move your cursor over the page. An overlay follows along, and for whatever you're pointing at it shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;framework&lt;/strong&gt; behind it — React, Next.js, Vue, Angular, Svelte&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;component type&lt;/strong&gt; — dialog, menu, button, input, tooltip&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;UI library&lt;/strong&gt; — MUI, Ant, Bootstrap, Chakra, Radix&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;recent API calls&lt;/strong&gt; the page has made — method, path, status, latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click to lock the overlay in place so you can read it properly. Press &lt;code&gt;Escape&lt;/code&gt; to let it follow your cursor again. That's the whole interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every claim comes with a confidence — and it never makes names up
&lt;/h2&gt;

&lt;p&gt;This is the part I care about most, and it's the reason Archify took longer than a weekend.&lt;/p&gt;

&lt;p&gt;Most detection tools guess confidently and are wrong just often enough to waste your time. Archify does the opposite. Every claim it makes carries a confidence score, and when it can't actually prove something, it says &lt;code&gt;unknown&lt;/code&gt; instead of inventing an answer.&lt;/p&gt;

&lt;p&gt;On a development build it can be specific. On a minified production bundle — where the component was named &lt;code&gt;a&lt;/code&gt; and the class is &lt;code&gt;css-1q2w3e&lt;/code&gt; — it will not fabricate a component name to look smart. It tells you what the runtime can actually prove and marks the rest honestly.&lt;/p&gt;

&lt;p&gt;My favorite example is shadcn/ui. At runtime it's just Radix primitives with Tailwind classes pasted in — genuinely indistinguishable from raw Radix once it's shipped. So Archify says &lt;strong&gt;Radix&lt;/strong&gt;, and offers shadcn only as a low-confidence hint with a note explaining why it can't be sure. A tool that admits what it doesn't know is worth more than one that's confidently wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second tab I didn't mention last time: Security
&lt;/h2&gt;

&lt;p&gt;The waitlist post was all about architecture. Here's the part I saved: there's a second tab, and it changes the question from "how is this built?" to "what is this page doing to me?"&lt;/p&gt;

&lt;p&gt;Switch to &lt;strong&gt;Security&lt;/strong&gt; and, for the page you're on, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Third-party scripts&lt;/strong&gt; — how many of the scripts running here come from someone other than the site itself&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound calls&lt;/strong&gt; — how many network requests go out, and to how many third-party domains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form and payment field access&lt;/strong&gt; — and this is the sharp one: which scripts are in a position to read your &lt;strong&gt;password&lt;/strong&gt;, &lt;strong&gt;card number&lt;/strong&gt;, or &lt;strong&gt;CVC&lt;/strong&gt; fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one works by watching which script attaches a listener to a sensitive input, and naming where that script came from. It's not an accusation — a script reading a field can be perfectly legitimate. It just makes visible something that is normally invisible: who is &lt;em&gt;technically able&lt;/em&gt; to see what you type. Point it at a checkout page you don't run and you may find that list longer than you'd like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing leaves your browser
&lt;/h2&gt;

&lt;p&gt;Everything above happens locally. There is no cloud step, no server-side analysis, no source upload, no account, and no telemetry. Archify intercepts &lt;code&gt;fetch&lt;/code&gt; and &lt;code&gt;XHR&lt;/code&gt;, reads the framework globals, and inspects the DOM — all inside your tab — and renders the result right there.&lt;/p&gt;

&lt;p&gt;That isn't a privacy promise stapled onto the marketing. It's the architecture. There is no "other end" to send your code to, because I never built one. The systems you explore stay entirely yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's not in it yet
&lt;/h2&gt;

&lt;p&gt;Honesty cuts both ways, so: v1 is deliberately small. There's no AI "explain this app" mode, no architecture graph, and no exports. It's Chrome-only for now. Those are on the roadmap, not in the box — and I'd rather ship something small and honest than something impressive and vague.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install it
&lt;/h2&gt;

&lt;p&gt;If you've ever opened DevTools and wished the app would just explain itself, this is for you. It's free, it's local, and it installs in a click.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://archify.salahxd.dev" rel="noopener noreferrer"&gt;Install Archify at archify.salahxd.dev&lt;/a&gt; — then open any site, hover, and see what it's actually made of. Software shouldn't be a black box.&lt;/p&gt;

</description>
      <category>developertools</category>
      <category>browserextension</category>
      <category>security</category>
      <category>devtool</category>
    </item>
    <item>
      <title>See Through Any Web App</title>
      <dc:creator>Mohd Salahudeen</dc:creator>
      <pubDate>Mon, 22 Jun 2026 14:36:49 +0000</pubDate>
      <link>https://dev.to/salahxd/see-through-any-web-app-40cg</link>
      <guid>https://dev.to/salahxd/see-through-any-web-app-40cg</guid>
      <description>&lt;p&gt;For most of software's history, writing code was the bottleneck. Today, understanding it is.&lt;/p&gt;

&lt;p&gt;AI can generate thousands of lines in seconds. Frameworks abstract away the hard parts, libraries hide their internals, and a single page pulls in dependencies that almost nobody ever opens and reads. We got very good at producing software, and we did almost nothing to make it easier to understand. That gap is the problem I kept running into — so I built a tool for it: Archify.&lt;/p&gt;

&lt;h2&gt;
  
  
  The questions that should take seconds
&lt;/h2&gt;

&lt;p&gt;Every day, developers lose hours to questions that have exact answers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What component is rendering this?&lt;/li&gt;
&lt;li&gt;Where is this data coming from?&lt;/li&gt;
&lt;li&gt;Which API powers this feature?&lt;/li&gt;
&lt;li&gt;What third-party scripts are running on this page?&lt;/li&gt;
&lt;li&gt;What can actually reach user data?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answers all exist. They're just buried under layers of abstraction — scattered across DevTools panels, source maps, network tabs, and a repository you may not even have access to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding software shouldn't feel like archaeology
&lt;/h2&gt;

&lt;p&gt;When you land on an unfamiliar app, the routine is always the same. Open DevTools. Inspect elements. Dig through network requests. Search the source maps. Trace the API calls. Jump between docs, repos, and a dozen browser tabs until a mental model finally clicks into place.&lt;/p&gt;

&lt;p&gt;The information was never the hard part. Modern tooling is excellent at exposing implementation details — the individual requests, the minified bundles, the raw responses. It's far worse at exposing architecture: how those pieces actually connect. That's the part you end up reconstructing by hand, every single time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Archify does
&lt;/h2&gt;

&lt;p&gt;Archify is a browser extension that shows you what's actually happening behind any web app. Hover over an element and you instantly see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The component rendering it&lt;/li&gt;
&lt;li&gt;The framework or library behind it&lt;/li&gt;
&lt;li&gt;The API connections powering it&lt;/li&gt;
&lt;li&gt;The scripts running on the page&lt;/li&gt;
&lt;li&gt;How those parts relate to each other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of spending an afternoon tracing behavior across tabs and files, you get the architecture directly — not just what exists, but how it fits together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for understanding, not surveillance
&lt;/h2&gt;

&lt;p&gt;Most developer tools start from the assumption that your data should leave your machine. Archify doesn't. It runs entirely inside your browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No cloud processing&lt;/li&gt;
&lt;li&gt;No server-side analysis&lt;/li&gt;
&lt;li&gt;No source code uploads&lt;/li&gt;
&lt;li&gt;No accounts&lt;/li&gt;
&lt;li&gt;No hidden telemetry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The systems you explore stay yours. That isn't a marketing line — it's the architecture. There's nothing on the other end to send your code to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters now
&lt;/h2&gt;

&lt;p&gt;The industry is mid-shift. Generating code is becoming nearly free, but generated code still has to be reviewed, dependencies still have to be trusted, and applications still have to be understood. The ability to build is accelerating. The ability to comprehend isn't. Every day, that gap widens.&lt;/p&gt;

&lt;p&gt;I think the next generation of developer tools won't be judged by how much code they can write. They'll be judged by how quickly they help you understand what already exists. That's what Archify is for: making the act of understanding a piece of software as fast as the act of using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming soon
&lt;/h2&gt;

&lt;p&gt;Archify is getting ready for launch, and I'm inviting early users in now. If you've ever opened DevTools and wished the app would just explain itself, you're exactly who I'm building this for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://archify.salahxd.dev" rel="noopener noreferrer"&gt;Join the waitlist at archify.salahxd.dev&lt;/a&gt; — be among the first to try it, and help shape what it becomes. Because software shouldn't be a black box.&lt;/p&gt;

</description>
      <category>developertools</category>
      <category>extensions</category>
      <category>devtools</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My Site Has No Database, But It Has a CMS, Comments, Likes, and Views</title>
      <dc:creator>Mohd Salahudeen</dc:creator>
      <pubDate>Thu, 11 Jun 2026 18:30:00 +0000</pubDate>
      <link>https://dev.to/salahxd/this-site-has-no-database-but-it-has-a-cms-comments-likes-and-views-317b</link>
      <guid>https://dev.to/salahxd/this-site-has-no-database-but-it-has-a-cms-comments-likes-and-views-317b</guid>
      <description>&lt;p&gt;The title isn't a trick. My &lt;a href="https://salahxd.dev" rel="noopener noreferrer"&gt;site&lt;/a&gt; has a full CMS with an admin panel at &lt;code&gt;/keystatic&lt;/code&gt;, real comments under every post, a like button that remembers you, and view counters that tick up. And there is no database behind any of it — at least, none that I run.&lt;/p&gt;

&lt;p&gt;No server I deploy, no database I provisioned. I've never written a migration for this site, never tuned a connection pool, and there's no disk that can fill up at 3am and page me.&lt;/p&gt;

&lt;p&gt;That last part was the whole design goal. I'm building a startup, and whatever attention I have left over is not going toward babysitting infrastructure for a personal blog. So I gave myself one rule when building this: if a feature needs me to &lt;em&gt;operate&lt;/em&gt; something, it doesn't ship.&lt;/p&gt;

&lt;p&gt;What surprised me is how little I had to give up. The whole thing builds to static files and sits on a CDN — but every piece of state you can touch here still gets saved somewhere durable. The state didn't disappear. I just stopped owning the places where it lands.&lt;/p&gt;

&lt;p&gt;This is not a genius-architecture post. Most of it is me being lazy in a useful direction. But it works well enough that people ask about it, so here's how each piece fits together.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CMS is just git
&lt;/h2&gt;

&lt;p&gt;The blog runs on &lt;a href="https://keystatic.com/" rel="noopener noreferrer"&gt;Keystatic&lt;/a&gt;. When I open &lt;code&gt;/keystatic&lt;/code&gt; in production and hit save, it doesn't write to a database. It makes a commit to this repo through a GitHub App:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// keystatic.config.ts&lt;/span&gt;
&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEV&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Salah-XD/personal-portfolio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In dev it writes files to my local disk. In production it commits straight to GitHub. The post you're reading is a &lt;code&gt;.md&lt;/code&gt; file in &lt;code&gt;src/content/blog/&lt;/code&gt;. My &lt;code&gt;/now&lt;/code&gt; and &lt;code&gt;/uses&lt;/code&gt; pages are JSON files in the same repo, edited through the same admin UI.&lt;/p&gt;

&lt;p&gt;So my "content database" is the git history. Publishing is a commit. Fixing a typo is a commit. Undoing a bad edit is &lt;code&gt;git revert&lt;/code&gt;. Vercel sees the push and rebuilds. I get diffs, blame, and backups for free, from the version control I was going to use anyway.&lt;/p&gt;

&lt;p&gt;The catch: writes are slow in the way a deploy is slow. Hitting save kicks off a rebuild, so "publish" takes about a minute. For a blog, I genuinely don't care. If that minute bothered me, this whole setup would be the wrong choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The comments are GitHub Discussions in a trench coat
&lt;/h2&gt;

&lt;p&gt;The comment box at the bottom of each post is &lt;a href="https://giscus.app/" rel="noopener noreferrer"&gt;Giscus&lt;/a&gt;, a thin shell over GitHub Discussions. Each post maps to a Discussion thread in the repo. When you comment, you're posting to GitHub, signed in as yourself.&lt;/p&gt;

&lt;p&gt;So my "comments table" is a Discussions tab, my spam protection is GitHub's, and my auth is OAuth somebody else built. If the env vars aren't wired up, the component renders a placeholder instead of breaking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{giscusReady ? (
  &amp;lt;Comments ... /&amp;gt;
) : (
  &amp;lt;p&amp;gt;Comments will appear here once Giscus is configured.&amp;lt;/p&amp;gt;
)}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a real downside here and I want to be upfront about it: you need a GitHub account to comment. For a blog where the readers are mostly developers, that filter costs me almost nothing and kills spam dead. If I were writing for a general audience, it would be a terrible choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Okay, confession: the like button does touch a server
&lt;/h2&gt;

&lt;p&gt;The likes and views from the title are the one kind of state on this site I couldn't fake at build time — a view happens when a person shows up, and there's no way around counting it live. So those two features hit &lt;a href="https://upstash.com/" rel="noopener noreferrer"&gt;Upstash Redis&lt;/a&gt; through the only two routes on the site that aren't static:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// this route becomes a Vercel Function&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`likes:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire data model. Two integer keys per post — &lt;code&gt;likes:slug&lt;/code&gt; and &lt;code&gt;views:slug&lt;/code&gt; — and the only operation is &lt;code&gt;INCR&lt;/code&gt;. No schema, no ORM, no tables. Deduping is a cookie, not a query: 30 minutes for views, a year for likes. Someone clearing their cookies can like a post twice. I can live with that.&lt;/p&gt;

&lt;p&gt;And the Redis layer is optional. If the keys aren't set, it degrades instead of crashing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/redis.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KV_REST_API_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPSTASH_REDIS_REST_URL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KV_REST_API_TOKEN&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPSTASH_REDIS_REST_TOKEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;_redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;_redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_redis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isRedisConfigured&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_redis&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So yes — strictly speaking, "no backend" is false. There are two serverless functions and a managed key-value store. But Upstash speaks plain HTTP and bills per request, so there's nothing to keep alive between visitors. I provisioned it by clicking a button on a marketplace page. I have never operated it. That feels meaningfully different from "running a database," even if a pedant would disagree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything else happens before anyone visits
&lt;/h2&gt;

&lt;p&gt;The rest of what looks dynamic is work done once, at build time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt; is &lt;a href="https://pagefind.app/" rel="noopener noreferrer"&gt;Pagefind&lt;/a&gt; — it indexes the built HTML and ships a static index the browser queries. No search server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OG image&lt;/strong&gt; on every post is rendered with satori + resvg during the build. No screenshot service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RSS and the sitemap&lt;/strong&gt; are just generated files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a request comes in, almost none of this code runs. It already ran, once, on my machine and on Vercel's build servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this actually costs me
&lt;/h2&gt;

&lt;p&gt;None of this is free, it's just paid in a different currency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No instant writes.&lt;/strong&gt; Publishing is a deploy. Fine for a blog, useless for anything interactive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No queries.&lt;/strong&gt; I can increment a counter. I can't ask "which posts did people who liked X also read." If I ever want that, Redis with two keys per post isn't the answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More vendors, not fewer.&lt;/strong&gt; Instead of one database I run, I depend on GitHub, Upstash, Vercel, and Buttondown for the newsletter. My bet is that each of them runs their slice better than I'd run all of it — and if one of them dies or gets unbearable, I'm swapping one small piece, not migrating a monolith.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When I'd throw this whole thing out
&lt;/h2&gt;

&lt;p&gt;The moment this site needs user accounts, per-user data, anything transactional, or writes that have to show up in the same second — I'd set up a real database and not feel bad about it. This setup works &lt;em&gt;because&lt;/em&gt; a personal blog is read-heavy, write-rare, and has exactly one author. Those are narrow conditions.&lt;/p&gt;

&lt;p&gt;But they describe most personal sites and content sites. And for those, the best backend is the one you stop thinking about after the first week. Mine has been a rebuild and an &lt;code&gt;INCR&lt;/code&gt; ever since, and I haven't thought about it once.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>serverless</category>
      <category>opensource</category>
      <category>git</category>
    </item>
  </channel>
</rss>
