<?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: Muhammad Akbar</title>
    <description>The latest articles on DEV Community by Muhammad Akbar (@makbar89).</description>
    <link>https://dev.to/makbar89</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%2F3929716%2F70ad1a08-af22-4104-98fd-bc91e5011973.jpg</url>
      <title>DEV Community: Muhammad Akbar</title>
      <link>https://dev.to/makbar89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/makbar89"/>
    <language>en</language>
    <item>
      <title>How to build a SaaS for $100/month</title>
      <dc:creator>Muhammad Akbar</dc:creator>
      <pubDate>Mon, 20 Jul 2026 19:11:16 +0000</pubDate>
      <link>https://dev.to/makbar89/how-to-build-a-saas-for-100month-o1f</link>
      <guid>https://dev.to/makbar89/how-to-build-a-saas-for-100month-o1f</guid>
      <description>&lt;p&gt;You can run a production SaaS for &lt;strong&gt;$87/month&lt;/strong&gt; using Neon (Postgres), Vercel (hosting), Supabase (auth), Resend (email), Plausible (analytics), and Polar.sh (payments). Each tool has a generous free tier that covers your first 500–1,000 users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hosting: Vercel — $0/mo (Hobby)
&lt;/h2&gt;

&lt;p&gt;Vercel's free tier includes 100 GB bandwidth, 600 build minutes, and serverless function execution. For a bootstrapped SaaS with fewer than 10k monthly active users, this is genuinely free. You only pay when you need team features or dedicated infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database: Neon — $0/mo (Free tier)
&lt;/h2&gt;

&lt;p&gt;Neon's free tier gives you 0.5 GB storage, 100 compute hours/month, and branching. The branch feature is a killer — spin up a copy of your production DB for every PR. At $25/mo (Scale), you get 10 GB storage and 500 compute hours, enough for most early-stage SaaS products.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth: NextAuth v5 — $0/mo
&lt;/h2&gt;

&lt;p&gt;NextAuth v5 is open source and free. Host your own database sessions, or use Supabase Auth (also free up to 50k monthly active users).&lt;/p&gt;

&lt;h2&gt;
  
  
  Email: Resend — $0/mo (Free tier)
&lt;/h2&gt;

&lt;p&gt;3,000 emails/month free. Transactional emails (welcome, password reset, invoice) typically consume 100–500 emails/month for early-stage SaaS. The $20/mo tier covers 50k emails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analytics: Plausible — $0/mo (Self-hosted)
&lt;/h2&gt;

&lt;p&gt;Plausible is open source with a self-hosted option. Their cloud tier starts at $9/mo for 10k pageviews. Privacy-first, no cookie banners needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Payments: Polar.sh — 5% + $0.50/txn
&lt;/h2&gt;

&lt;p&gt;Polar is the best option for developers outside Stripe-supported countries. They act as Merchant of Record, handle tax compliance, and pay out to your bank account.&lt;/p&gt;

&lt;h2&gt;
  
  
  The total
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Monthly Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vercel (Hobby)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neon (Free)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NextAuth (Open source)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resend (Free)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plausible (Self-hosted)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Polar.sh (5% fee)&lt;/td&gt;
&lt;td&gt;~$5 on $100 revenue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Domain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1/mo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~$6/mo&lt;/strong&gt; until you scale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you outgrow free tiers, expect ~$87/mo for production scale (10k+ MAU).&lt;/p&gt;

&lt;h2&gt;
  
  
  When to upgrade
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;500+ users&lt;/strong&gt;: Upgrade Resend ($20/mo) for higher send limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10k+ MAU&lt;/strong&gt;: Upgrade Neon ($25/mo) for more compute hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;50k+ MAU&lt;/strong&gt;: Upgrade Vercel Pro ($20/mo) for team features and higher limits&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>saas</category>
      <category>webdev</category>
      <category>nextjs</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Built a Block Puzzle Game with React Native and Expo</title>
      <dc:creator>Muhammad Akbar</dc:creator>
      <pubDate>Sat, 18 Jul 2026 00:48:14 +0000</pubDate>
      <link>https://dev.to/makbar89/how-i-built-a-block-puzzle-game-with-react-native-and-expo-38ja</link>
      <guid>https://dev.to/makbar89/how-i-built-a-block-puzzle-game-with-react-native-and-expo-38ja</guid>
      <description>&lt;p&gt;How I Built a Block Puzzle Game with React Native and Expo&lt;/p&gt;

&lt;p&gt;A few weeks ago, I launched my first mobile game — a block puzzle called Blockbeam. It's an 8x8 grid where you drag colorful blocks, fill rows and columns, and chase high scores. Simple concept, but building it taught me a lot about React Native's capabilities beyond typical CRUD apps.&lt;/p&gt;

&lt;p&gt;Here's what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why React Native for Games?
&lt;/h2&gt;

&lt;p&gt;Most mobile games are built with Unity or native code. But for a 2D puzzle game, React Native is surprisingly capable. The game doesn't need 60fps 3D rendering — it needs gesture handling, state management, and smooth animations. React Native handles all three well.&lt;/p&gt;

&lt;p&gt;The key stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expo SDK 54&lt;/strong&gt; — managed workflow, over-the-air updates, zero native config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;react-native-reanimated&lt;/strong&gt; — 60fps drag animations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;react-native-gesture-handler&lt;/strong&gt; — PanResponder for drag-and-drop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;react-native-svg&lt;/strong&gt; — block rendering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AsyncStorage&lt;/strong&gt; — game state persistence&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Puzzle Engine
&lt;/h2&gt;

&lt;p&gt;The core of any block puzzle is the board state. I used a flat 64-element array for the 8x8 grid:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&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;CELLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 64&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// 0 = empty, 1-7 = block colors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Piece placement is straightforward: check if all target cells are empty, fill them, then scan for complete rows and columns to clear.&lt;/p&gt;

&lt;p&gt;The interesting part was the "greedy solver" I built for the auto-play bot. It tries every piece in every position and picks the move that clears the most lines:&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;for &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;piece&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &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;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;canPlace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&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;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;simulateClear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&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;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bestScore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* pick this move */&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Drag and Drop with PanResponder
&lt;/h2&gt;

&lt;p&gt;The trickiest part was the drag mechanic. Each tray piece has a PanResponder that tracks touch position and renders a floating ghost. On release, it calculates the nearest cell position:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;col&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;drag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;boardLayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;drag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;LIFT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;boardLayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LIFT offset accounts for the finger being above the piece — a subtle UX detail that makes dragging feel natural.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with TypeScript&lt;/strong&gt; — Caught dozens of bugs at compile time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test on real devices early&lt;/strong&gt; — Emulators don't reveal performance issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan the theme system earlier&lt;/strong&gt; — Adding palette switching after the fact was messy&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;The game is live on Google Play, completely free with no paywalls. 100% offline, 6 color themes, daily rewards, and power-ups.&lt;/p&gt;

&lt;p&gt;If you're building a casual game, don't overthink the tech stack. React Native + Expo can take you surprisingly far.&lt;/p&gt;

&lt;p&gt;Check it out: &lt;a href="https://play.google.com/store/apps/details?id=com.magstudios.blockpuzzle" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.magstudios.blockpuzzle&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Built with React Native, Expo, and way too much coffee. Solo dev from Pakistan.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>gamedev</category>
      <category>expo</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Postman now limits its free tier to 1 user. Here's what I switched to.</title>
      <dc:creator>Muhammad Akbar</dc:creator>
      <pubDate>Sun, 07 Jun 2026 14:35:21 +0000</pubDate>
      <link>https://dev.to/makbar89/postman-now-limits-its-free-tier-to-1-user-heres-what-i-switched-to-3om4</link>
      <guid>https://dev.to/makbar89/postman-now-limits-its-free-tier-to-1-user-heres-what-i-switched-to-3om4</guid>
      <description>&lt;p&gt;If you opened Postman recently and hit a wall sharing a collection with a teammate, you're not imagining it — as of March 2026, Postman's free tier is capped at &lt;strong&gt;1 user&lt;/strong&gt;. Solo, that's survivable. For a two-person side project it's an instant "upgrade to Team at $19/user/mo" prompt.&lt;/p&gt;

&lt;p&gt;I write about cheap dev stacks, so I went through the actual alternatives and itemized what each gives you for free. Quick rundown:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bruno&lt;/strong&gt; — my pick if you live in Git. Collections are plain-text &lt;code&gt;.bru&lt;/code&gt; files on disk (MIT-licensed, offline-first), so "sharing" is just committing to your repo. No accounts, no sync tier, secrets stay local. Postman, but you own your data. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Insomnia&lt;/strong&gt; — the easiest like-for-like switch after the cutback. Kong-owned, open-source, and the free tier includes &lt;strong&gt;Git Sync for up to 3 users&lt;/strong&gt; — actual free small-team collaboration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hoppscotch&lt;/strong&gt; — web-based, MIT, with the widest protocol set (REST, GraphQL, WebSocket, SSE, Socket.IO, MQTT). Unlimited free real-time workspaces, and you can self-host the whole thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thunder Client&lt;/strong&gt; — if you basically live in VS Code and want something lightweight. Heads up: the free tier is single-device and paywalls environments, so it's more "personal" than "team."&lt;/p&gt;

&lt;p&gt;The honest summary: want &lt;strong&gt;free team collaboration&lt;/strong&gt;? Insomnia or Hoppscotch. Want to &lt;strong&gt;own your data / go Git-native&lt;/strong&gt;? Bruno. Want &lt;strong&gt;lightweight in-editor&lt;/strong&gt;? Thunder Client.&lt;/p&gt;

&lt;p&gt;Full feature-by-feature breakdown (collections, environments, scripting, CLI/CI, self-host) here if you want the details: &lt;a href="https://cheapstack.dev/comparisons/postman-alternatives" rel="noopener noreferrer"&gt;Postman alternatives compared →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What did you switch to after the free-tier change?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tooling</category>
      <category>api</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I built a dev review site in 2 weeks — here's the stack, the rejections, and what I'd do differently</title>
      <dc:creator>Muhammad Akbar</dc:creator>
      <pubDate>Wed, 13 May 2026 16:20:03 +0000</pubDate>
      <link>https://dev.to/makbar89/i-built-a-dev-review-site-in-2-weeks-heres-the-stack-the-rejections-and-what-id-do-differently-14oa</link>
      <guid>https://dev.to/makbar89/i-built-a-dev-review-site-in-2-weeks-heres-the-stack-the-rejections-and-what-id-do-differently-14oa</guid>
      <description>&lt;p&gt;A few months ago I tried to find a "best stack under 100 USD a month" article for a side project. Every result was either an enterprise list with line items at thousands per month, or a vague "use Vercel and Supabase" post with no actual numbers.&lt;/p&gt;

&lt;p&gt;So I built one. The result is &lt;a href="https://cheapstack.dev" rel="noopener noreferrer"&gt;cheapstack.dev&lt;/a&gt; — an editorial site that publishes itemized reference stacks for indie SaaS infrastructure on 50–200 USD a month, with every line item, the date prices were verified, and what to swap when traffic crosses specific thresholds.&lt;/p&gt;

&lt;p&gt;I'm not going to pretend this is a masterpiece. I want to write this post because the things I learned along the way will probably save someone else two weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build
&lt;/h2&gt;

&lt;p&gt;Stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16&lt;/strong&gt; with &lt;code&gt;output: "export"&lt;/code&gt; for static export&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers Static Assets&lt;/strong&gt; for hosting (free, unlimited bandwidth)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IBM Plex Sans + Plex Mono&lt;/strong&gt; for typography&lt;/li&gt;
&lt;li&gt;Markdown-driven content layer (no CMS, no backend, no database)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two-week evening build, mostly weekends. Lighthouse 100/100/100/100 across all pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Cloudflare Workers, not Vercel
&lt;/h2&gt;

&lt;p&gt;Cheapstack's whole pitch is "real numbers for cheap stacks." Hosting it on Vercel — whose Hobby tier is explicitly non-commercial, and whose Pro tier starts at 20 USD a month — would have been hypocritical the moment I added an affiliate link.&lt;/p&gt;

&lt;p&gt;Cloudflare Workers Static Assets bills 0 USD a month, even with commercial use, and edges out Vercel on first-byte latency in 60% of regions. The DX is rougher (you can't just &lt;code&gt;next build&lt;/code&gt; and deploy — you need &lt;code&gt;output: "export"&lt;/code&gt; and a wrangler config), but for a site that's truly static the trade is worth it.&lt;/p&gt;

&lt;p&gt;I tried &lt;code&gt;@opennextjs/cloudflare&lt;/code&gt; first because Cheapstack started life as a dynamic Next.js app with edge runtime OG images. Two days of debugging dynamic-route 404s later, I realized I didn't need any of that. Pre-generating the OG image as a 1200x420 PNG and switching to plain static export resolved everything in 30 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; if your site has no per-user state and no per-request logic, static export beats every adapter. Adapter complexity buys you flexibility you don't have.&lt;/p&gt;

&lt;h2&gt;
  
  
  The content layer
&lt;/h2&gt;

&lt;p&gt;I went into this thinking I'd write everything from scratch. Then I realized the actual work was structuring the data so the prose was a thin layer on top.&lt;/p&gt;

&lt;p&gt;Every tool on the site lives in one file:&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;// src/lib/tools.ts&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ToolCategory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;usd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;verified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}[];&lt;/span&gt;
  &lt;span class="nl"&gt;affiliate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="c1"&gt;// ... more fields&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;TOOLS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Tool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;digitalocean&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;postmark&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="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// ~70 tools&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stack guides, comparison pages, and tool roundups all pull from this single registry. When DigitalOcean changes their pricing, I update one entry and 11 pages reflect it. When an affiliate program approves me, I drop the tracking URL into the &lt;code&gt;affiliate&lt;/code&gt; field and every link to that tool across the site updates.&lt;/p&gt;

&lt;p&gt;This is mundane infrastructure but it's the difference between a site that decays in 3 months and a site that stays accurate for years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Affiliate programs: the reality of being new
&lt;/h2&gt;

&lt;p&gt;I applied to 12 affiliate programs in week one. Here's the score so far:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Program&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DigitalOcean (via Awin)&lt;/td&gt;
&lt;td&gt;Declined — "site too new"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Squarespace (via Impact / Acceleration Partners)&lt;/td&gt;
&lt;td&gt;Declined — "site not yet live or traffic levels below necessary"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hostinger (direct)&lt;/td&gt;
&lt;td&gt;Pending review, day 4 of 3–7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 other Impact-network brands&lt;/td&gt;
&lt;td&gt;Auto-deferred 30–60 days&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So far: 0 approved, 2 declined, 1 pending, 9 deferred. The "site too new" pattern across every network was demoralizing — I'd built the whole monetization architecture on the assumption that applying with a live, polished site would be enough.&lt;/p&gt;

&lt;p&gt;It's not. Affiliate networks gate on traffic signal, not site quality. The site needs to show up in Google for 30+ days with consistent search clicks before most programs unlock.&lt;/p&gt;

&lt;p&gt;This was a real lesson: &lt;strong&gt;the FTC disclosures on cheapstack are mostly aspirational at the moment.&lt;/strong&gt; Every tool link has &lt;code&gt;rel="sponsored nofollow noopener"&lt;/code&gt; and a clear disclosure — even though most of them don't pay me anything. I left the architecture in because (a) when programs do approve me, the wiring is already there, and (b) the credibility hit of &lt;em&gt;adding&lt;/em&gt; affiliate disclosures later would be worse than declaring intent on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually on the site
&lt;/h2&gt;

&lt;p&gt;For the curious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6 stack guides&lt;/strong&gt; — itemized monthly bills for SaaS-100 (~87 USD/mo), MVP-50, marketplace-150, course-100, newsletter-25, landing-15&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 comparisons&lt;/strong&gt; — Firebase vs Supabase, Postmark vs Resend, Beehiiv vs Ghost, Neon vs PlanetScale, Clerk vs Auth.js, Vercel vs Cloudflare Pages, Stripe vs Lemon Squeezy, Stripe vs Paddle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;12 tool roundups&lt;/strong&gt; — hosting, databases, transactional email, newsletter platforms, domain registrars, SEO tools, CRM, landing builders, payments, analytics, headless CMS, auth services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 framework deep-dives&lt;/strong&gt; — Next.js, Astro, SvelteKit, Remix, Nuxt, SolidStart, Qwik, RedwoodSDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything's at &lt;a href="https://cheapstack.dev" rel="noopener noreferrer"&gt;cheapstack.dev&lt;/a&gt; if you want to see how it shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Early signal
&lt;/h2&gt;

&lt;p&gt;Four days post-launch, before any external traffic push:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Search Console: 41 impressions, position 12.1 average&lt;/li&gt;
&lt;li&gt;Indexed: 8 of 47 pages (normal week-1 curve)&lt;/li&gt;
&lt;li&gt;Most interesting query: "affordable node.js headless cms under 50/month" — exactly the kind of long-tail commercial-intent query the site was designed for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Position 12 means page 2 of Google. With backlinks and another month of indexing it could realistically move to page 1 for that query. That's the whole game.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Skip the OpenNext adapter entirely.&lt;/strong&gt; Static export from day one. Two days saved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply to affiliate programs only after 30 days of traffic.&lt;/strong&gt; Day-one applications are a coin flip and rejection cool-down windows are 60–90 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write less, structure more.&lt;/strong&gt; The articles that are working are the ones with strong data tables and clean comparisons. Prose-heavy pieces sit in the middle of the rankings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start the newsletter on day one.&lt;/strong&gt; I held off until "the site was ready." That was wrong. The newsletter is the only thing you actually own — everything else (Google rank, affiliate approvals, social reach) is rented from someone else.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More long-tail comparison pages targeting the queries already showing up in GSC&lt;/li&gt;
&lt;li&gt;A weekly newsletter when I cross 100 organic visits/day&lt;/li&gt;
&lt;li&gt;Real receipts from working indie builds (if you're running profitable on a 50–200 USD/mo stack, I'd love to feature your build — with permission)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've built anything similar — editorial site, affiliate engine, programmatic SEO play — I'd love to hear what worked and what didn't. The two-week timeline only worked because I made a lot of decisions fast; some of them are probably wrong.&lt;/p&gt;

&lt;p&gt;Site: &lt;a href="https://cheapstack.dev" rel="noopener noreferrer"&gt;cheapstack.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>indiehackers</category>
      <category>webdev</category>
      <category>saas</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
