<?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: Justin W Boggs</title>
    <description>The latest articles on DEV Community by Justin W Boggs (@codingcapybaras).</description>
    <link>https://dev.to/codingcapybaras</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%2F4040712%2F513d2696-4a6a-4dcc-9272-85383ea854b8.png</url>
      <title>DEV Community: Justin W Boggs</title>
      <link>https://dev.to/codingcapybaras</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codingcapybaras"/>
    <language>en</language>
    <item>
      <title>Claude Code's Most Underused Features</title>
      <dc:creator>Justin W Boggs</dc:creator>
      <pubDate>Tue, 21 Jul 2026 20:45:50 +0000</pubDate>
      <link>https://dev.to/codingcapybaras/claude-codes-most-underused-features-53am</link>
      <guid>https://dev.to/codingcapybaras/claude-codes-most-underused-features-53am</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@cdr6934" rel="noopener noreferrer"&gt;Chris Ried&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The most underused Claude Code features for non-tech founders are plan mode, the CLAUDE.md memory system, checkpoints with &lt;code&gt;/rewind&lt;/code&gt;, hooks, custom slash commands, subagents, auto memory, and context management with &lt;code&gt;/context&lt;/code&gt; and &lt;code&gt;/compact&lt;/code&gt;. Most of us learn to type a prompt, hit enter, and hope. That works, but it leaves the majority of the tool on the table. I used Claude Code for months before I found half of these, and each one fixed a specific frustration I'd assumed was just the cost of AI coding. This post walks through all eight — what each feature does, when to reach for it, and the mistakes I made before I knew it existed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plan mode makes Claude read and propose before it touches files — the single biggest reliability upgrade for non-devs.&lt;/li&gt;
&lt;li&gt;A CLAUDE.md under 200 lines stops you from re-explaining your project every session.&lt;/li&gt;
&lt;li&gt;Checkpoints (&lt;code&gt;/rewind&lt;/code&gt; or double-Esc) undo Claude's edits without you needing to understand git.&lt;/li&gt;
&lt;li&gt;Hooks enforce rules with code; CLAUDE.md only suggests them.&lt;/li&gt;
&lt;li&gt;Start with plan mode and CLAUDE.md this week. Add the rest as specific pain shows up.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why does plan mode matter so much for non-developers?
&lt;/h2&gt;

&lt;p&gt;Plan mode is a session state where Claude can read your code, search it, and reason about it — but it's not allowed to write to disk or run commands that change anything. You toggle it with Shift+Tab. Claude explores, then presents a plan. You approve it before anything happens.&lt;/p&gt;

&lt;p&gt;If you only adopt one thing from this post, make it this one. Here's why it matters more for non-tech founders than for engineers: an engineer can read a diff and catch a bad change after the fact. Most of us can't, or at least can't reliably. Plan mode moves the checkpoint to &lt;em&gt;before&lt;/em&gt; the change, where the conversation is in plain English. "I'll modify the webhook handler to verify signatures before processing, then add a test" is something I can evaluate. Four hundred lines of changed TypeScript is not.&lt;/p&gt;

&lt;p&gt;My old workflow was: type a request, watch files change, get a bad feeling, ask "wait, why did you change that?" — after the fact, with the damage already on disk. In plan mode, the "wait, why?" conversation happens first. I push back on the plan, Claude revises it, and only then do we execute.&lt;/p&gt;

&lt;p&gt;The habit I'd suggest: default to plan mode for anything touching payments, auth, or your database. Those are the areas where a confident wrong change costs you real money or real user trust. For cosmetic changes — copy edits, styling — normal mode is fine. I wrote more about this judgment call in &lt;a href="https://codingcapybaras.com/blog/when-to-trust-your-ai-assistant" rel="noopener noreferrer"&gt;when to trust your AI assistant&lt;/a&gt;, but the short version is that plan mode is how you buy verification time you don't have the skills to get from reading diffs.&lt;/p&gt;

&lt;p&gt;Plan mode also changes the quality of the work itself. When Claude is forced to survey the codebase before acting, it finds the existing helper function instead of writing a duplicate. It notices the pattern your codebase already uses. Acting first and discovering context later is how you get the drift that makes AI-built codebases messy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CLAUDE.md and why should you set it up first?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A CLAUDE.md file is a markdown file of persistent instructions that Claude Code reads automatically at the start of every session.&lt;/strong&gt; It's the fix for the most annoying thing about AI coding as a non-dev: every new session starts from zero, and you find yourself re-explaining your project — where things live, what commands to run, what not to touch — over and over.&lt;/p&gt;

&lt;p&gt;Per &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;the official memory documentation&lt;/a&gt;, the guidance is to keep it under 200 lines, because longer files consume context and Claude's adherence degrades. The docs also give a rule for what belongs in it that I've found dead-on in practice: add an instruction when Claude makes the same mistake a second time, or when you catch yourself typing the same correction you typed last session.&lt;/p&gt;

&lt;p&gt;Mine includes things like: which folders are off-limits (the boilerplate's &lt;code&gt;/platform/&lt;/code&gt; region), what command creates a new route, and the rule that all email goes through one central helper. Every one of those lines exists because Claude got it wrong once and I never wanted to explain it again.&lt;/p&gt;

&lt;p&gt;Three related features most people miss:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/init&lt;/code&gt; writes your first draft.&lt;/strong&gt; Run it in your project and Claude analyzes the codebase and generates a starting CLAUDE.md with build commands and conventions it discovers. You refine from there instead of staring at a blank file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/memory&lt;/code&gt; shows you what's actually loaded.&lt;/strong&gt; It lists every instruction file in your current session. When Claude ignores a rule, this is the first debugging step — half the time the file isn't where you think it is, so Claude never saw it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.claude/rules/&lt;/code&gt; scopes instructions to file paths.&lt;/strong&gt; Rules files with a &lt;code&gt;paths&lt;/code&gt; frontmatter field only load when Claude works on matching files. Your database conventions load when it touches schema files and stay out of the way otherwise. That's how you keep the main file under 200 lines as your project grows.&lt;/p&gt;

&lt;p&gt;If you're brand new to the tool, my &lt;a href="https://codingcapybaras.com/blog/claude-code-for-non-developers" rel="noopener noreferrer"&gt;Claude Code guide for non-developers&lt;/a&gt; covers the setup basics; CLAUDE.md is the first thing I'd add after install.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto memory: the notes Claude writes for itself
&lt;/h2&gt;

&lt;p&gt;CLAUDE.md is memory you write. Auto memory is memory Claude writes — and most users don't know it's happening. As it works, Claude saves notes about your project into a memory directory on your machine: build commands it figured out, debugging insights, preferences you expressed mid-session. Those notes load at the start of future sessions.&lt;/p&gt;

&lt;p&gt;It's on by default in recent versions (v2.1.59+, per &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;the docs&lt;/a&gt;). The first 200 lines of the memory index load into every session, and Claude pulls detailed topic files on demand.&lt;/p&gt;

&lt;p&gt;Two practical things to know. First, you can just tell Claude to remember something — "remember that the API tests need a local Redis instance" — and it lands in auto memory without you editing a file. For a non-dev, that's the lowest-friction way to build persistent context: correct Claude once, out loud, and it sticks.&lt;/p&gt;

&lt;p&gt;Second, everything is plain markdown you can read. Run &lt;code&gt;/memory&lt;/code&gt;, open the auto memory folder, and audit what Claude has written about your project. I check mine every few weeks. Mostly it's accurate; occasionally there's a stale note from a refactor, and deleting it takes ten seconds. Treating the memory folder as something you can read and prune — rather than an invisible black box — is the difference between memory helping and memory quietly steering you wrong.&lt;/p&gt;

&lt;p&gt;The division of labor that's emerged for me: CLAUDE.md holds the rules I'd put in a team handbook. Auto memory holds the accumulated street knowledge — the stuff you'd only know from having worked in the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkpoints and /rewind: the undo button nobody finds
&lt;/h2&gt;

&lt;p&gt;Claude Code automatically snapshots your files before each edit it makes. Run &lt;code&gt;/rewind&lt;/code&gt; — or press Esc twice with an empty prompt — and you get a menu of every prompt in the session. Pick one, and you can restore your code, your conversation, or both to that moment.&lt;/p&gt;

&lt;p&gt;I went months without knowing this existed, and during those months my recovery strategy for "Claude made things worse" was asking Claude to un-make it worse. Sometimes that worked. Sometimes it compounded — the fix for the bad change introduced its own bad change, and I was three layers deep in a hole with no map. If you've lived that spiral, &lt;code&gt;/rewind&lt;/code&gt; is the feature you were missing. One command, pick the last good point, back to safety.&lt;/p&gt;

&lt;p&gt;The limits matter, per &lt;a href="https://code.claude.com/docs/en/checkpointing" rel="noopener noreferrer"&gt;the checkpointing docs&lt;/a&gt;: checkpoints only track files Claude edited through its own tools, in the current session. Changes you made yourself in another editor aren't covered, and it's not a replacement for git — you still want real commits as your permanent history. I think of checkpoints as session-level undo and git as the save file. My actual workflow: commit before starting a chunk of work (I literally ask Claude to do it), then use &lt;code&gt;/rewind&lt;/code&gt; freely within the session, knowing the commit is the hard floor.&lt;/p&gt;

&lt;p&gt;There's also a subtler use: the rewind menu can restore just the &lt;em&gt;conversation&lt;/em&gt; to an earlier point, or summarize a stretch of it, without touching files. When a session has wandered — you explored three approaches and want to get back to the decision point with your code intact — that's the move. It pairs well with the &lt;a href="https://codingcapybaras.com/blog/vibe-coding-workflow" rel="noopener noreferrer"&gt;vibe coding workflow&lt;/a&gt;: move fast &lt;em&gt;because&lt;/em&gt; the escape hatch exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks, custom commands, and subagents
&lt;/h2&gt;

&lt;p&gt;These three are more advanced, and I'd reach for them in this order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom slash commands&lt;/strong&gt; are prompt templates you save as markdown files in &lt;code&gt;.claude/commands/&lt;/code&gt;. A file at &lt;code&gt;.claude/commands/ship-check.md&lt;/code&gt; becomes &lt;code&gt;/ship-check&lt;/code&gt; in your sessions. Anything you find yourself typing repeatedly — "review this for security issues, check the input validation, confirm no secrets are committed" — belongs in one. I keep a handful for my recurring rituals, and they've replaced a text file of prompts I used to paste from. If you've read my post on &lt;a href="https://codingcapybaras.com/blog/prompt-engineering-for-non-devs" rel="noopener noreferrer"&gt;prompt engineering for non-developers&lt;/a&gt;, commands are where your good prompts go to become reusable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hooks&lt;/strong&gt; are shell scripts that run automatically at fixed points in Claude's lifecycle — before a tool runs, after a file edit, and so on (&lt;a href="https://code.claude.com/docs/en/hooks" rel="noopener noreferrer"&gt;hooks reference&lt;/a&gt;). The key distinction, straight from the docs: CLAUDE.md instructions are context Claude &lt;em&gt;tries&lt;/em&gt; to follow; hooks execute regardless of what Claude decides. A &lt;code&gt;PreToolUse&lt;/code&gt; hook can block any command touching a protected folder — not ask nicely, block. If there's a rule in your project where "Claude usually complies" isn't good enough — never touch the production env file, always run the linter after edits — that rule wants to be a hook. Writing one requires a small shell script, which sounds intimidating, but Claude will write its own hooks if you describe the rule. That still makes me smile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subagents&lt;/strong&gt; are separate Claude sessions spawned to handle a task with their own context window, reporting back only the conclusion. The practical benefit for a founder: verbose work stays out of your main conversation. When Claude searches forty files to answer a question, all that file-reading noise fills up context you'd rather spend on the actual work. Delegating research to a subagent keeps the main thread clean, which meaningfully delays the moment your session degrades. You don't have to configure anything to benefit — you can just ask Claude to "use a subagent to research X."&lt;/p&gt;

&lt;h2&gt;
  
  
  Which feature solves which problem?
&lt;/h2&gt;

&lt;p&gt;Here's the map I wish someone had handed me:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem you have&lt;/th&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Effort to adopt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude changes things I didn't want changed&lt;/td&gt;
&lt;td&gt;Plan mode (Shift+Tab)&lt;/td&gt;
&lt;td&gt;None — it's a keystroke&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I re-explain my project every session&lt;/td&gt;
&lt;td&gt;CLAUDE.md + &lt;code&gt;/init&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;~30 minutes once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude forgot what we figured out last week&lt;/td&gt;
&lt;td&gt;Auto memory&lt;/td&gt;
&lt;td&gt;None — on by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude made it worse and I can't get back&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/rewind&lt;/code&gt; / double-Esc&lt;/td&gt;
&lt;td&gt;None — it's a command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I type the same long prompt every week&lt;/td&gt;
&lt;td&gt;Custom slash commands&lt;/td&gt;
&lt;td&gt;~10 minutes per command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A rule keeps getting ignored&lt;/td&gt;
&lt;td&gt;Hooks&lt;/td&gt;
&lt;td&gt;Small script (Claude can write it)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long sessions get dumb and forgetful&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/context&lt;/code&gt;, &lt;code&gt;/compact&lt;/code&gt;, subagents&lt;/td&gt;
&lt;td&gt;Habit, not setup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last row deserves a word since it's the only one I haven't covered: &lt;code&gt;/context&lt;/code&gt; shows you what's filling Claude's context window, and &lt;code&gt;/compact&lt;/code&gt; compresses the conversation when it gets bloated. Sessions don't fail loudly when context fills up — they just get subtly worse. Checking &lt;code&gt;/context&lt;/code&gt; when a long session starts feeling off is the diagnostic most people never run.&lt;/p&gt;

&lt;p&gt;If you're comparing tools rather than deepening one, my &lt;a href="https://codingcapybaras.com/blog/cursor-vs-claude-code-vs-cowork" rel="noopener noreferrer"&gt;Cursor vs Claude Code vs Cowork comparison&lt;/a&gt; covers where each fits. But the honest takeaway from writing this post: most people don't need a different tool. They need the second half of the one they have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's the difference between CLAUDE.md and auto memory?
&lt;/h3&gt;

&lt;p&gt;CLAUDE.md is written by you and holds instructions and rules; auto memory is written by Claude and holds learnings it picks up from working with you. Both load at the start of every session. Instructions set the standard; memory accumulates the context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do checkpoints replace git?
&lt;/h3&gt;

&lt;p&gt;No. Checkpoints only track files Claude edited in the current session, and they're a session-level safety net, not permanent history. Keep committing to git — or ask Claude to commit for you at sensible points — and use &lt;code&gt;/rewind&lt;/code&gt; for the in-session mistakes between commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are hooks safe for a non-developer to set up?
&lt;/h3&gt;

&lt;p&gt;Reasonably, if you start with read-only or blocking hooks (ones that prevent actions rather than perform them). Describe the rule you want enforced and have Claude write the hook script, then have it explain the script back to you line by line before you enable it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How big should a CLAUDE.md file be?
&lt;/h3&gt;

&lt;p&gt;Under 200 lines, per Anthropic's own guidance — adherence drops as the file grows. If you're past that, move path-specific instructions into &lt;code&gt;.claude/rules/&lt;/code&gt; files that only load when relevant files are touched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do these features exist in Cursor or other AI tools?
&lt;/h3&gt;

&lt;p&gt;Partially. Most AI coding tools have some instructions-file equivalent (Cursor has rules files), and several have plan-like modes. The specific mechanics here — auto memory, &lt;code&gt;/rewind&lt;/code&gt;, lifecycle hooks — are Claude Code features, though the concepts transfer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which feature should a beginner adopt first?
&lt;/h3&gt;

&lt;p&gt;Plan mode, same day you install. Then CLAUDE.md within the first week. Everything else can wait until you feel the specific pain it solves — adopting features before the pain exists just adds complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The pattern across all eight features is the same: Claude Code ships with the guardrails non-tech founders need, but they're opt-in, and nothing forces you to find them. Plan mode gives you review before damage. CLAUDE.md and auto memory give you continuity. Checkpoints give you undo. Hooks give you enforcement. None of them require engineering skill to use — they require knowing they exist, which is the gap this post is meant to close. Start with plan mode and a CLAUDE.md this week; add the rest when the matching pain shows up. These Claude Code features tips are most of the distance between fighting the tool and working with it.&lt;/p&gt;

&lt;p&gt;If you're shipping a SaaS with AI coding tools, &lt;a href="https://codingcapybaras.com/" rel="noopener noreferrer"&gt;Coding Capybaras&lt;/a&gt; is the free boilerplate I built for exactly this workflow — it ships with a CLAUDE.md already written, so the guardrails in this post are configured before your first session.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Stripe Webhook Hell: Every Gotcha (and How to Avoid It)</title>
      <dc:creator>Justin W Boggs</dc:creator>
      <pubDate>Tue, 21 Jul 2026 20:28:56 +0000</pubDate>
      <link>https://dev.to/codingcapybaras/stripe-webhook-hell-every-gotcha-and-how-to-avoid-it-n6i</link>
      <guid>https://dev.to/codingcapybaras/stripe-webhook-hell-every-gotcha-and-how-to-avoid-it-n6i</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@sumup" rel="noopener noreferrer"&gt;SumUp&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-person-sitting-in-a-chair-with-a-laptop-and-a-credit-card-ShB9pI4mpRg" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Stripe webhooks are where most indie SaaS founders meet their first truly nasty bug, and it's almost always one of four things: a signature that won't verify because a framework mangled the request body, a missing idempotency check that double-charges or double-fulfills, a handler that runs too slow and gets retried into a storm, or a dev-vs-prod config mismatch where events silently vanish. None of these throw a loud error. They fail quietly, in production, on the code that touches your customers' money — which is exactly why they're so painful. The good news: every one of them has a known fix, and once you understand &lt;em&gt;why&lt;/em&gt; each gotcha happens, they stop being mysterious. Here's the full tour.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verify the signature, and use the raw body to do it.&lt;/strong&gt; Stripe signs every event; if your framework parses the body to JSON before you verify, verification fails. This is the #1 cause of "my webhook won't work."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make your handler idempotent.&lt;/strong&gt; Stripe guarantees at-least-once delivery and retries for up to 72 hours, so the same event can arrive more than once. Store each &lt;code&gt;event.id&lt;/code&gt; with a unique constraint and skip duplicates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return a 2xx fast, then do the work.&lt;/strong&gt; Stripe expects a quick success response and retries anything else. Verify, enqueue the event, return 200 — don't send email or sync an ERP inline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev and prod are different worlds.&lt;/strong&gt; Separate signing secrets, separate endpoints, and the Stripe CLI for local testing. Mixing them up is how events disappear silently.&lt;/li&gt;
&lt;li&gt;This is the highest-stakes code you own. Treat the webhook handler as core infrastructure, not a glue script.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why are webhooks so error-prone in the first place?
&lt;/h2&gt;

&lt;p&gt;A webhook is just Stripe making an HTTP request to your server when something happens — a payment succeeds, a subscription renews, a card gets declined. Conceptually it's simple. The reason it turns into "hell" is that &lt;strong&gt;a webhook is an event you don't control the timing, ordering, or delivery count of.&lt;/strong&gt; Everything else in your app, &lt;em&gt;you&lt;/em&gt; trigger. A webhook arrives whenever Stripe decides, possibly more than once, possibly out of order, possibly while your server is mid-deploy.&lt;/p&gt;

&lt;p&gt;That inversion breaks the assumptions most founders code with. You write a handler imagining it runs once, cleanly, right after the customer pays. In reality it might run three times, the third arriving eleven hours later, after a retry, while a different event for the same customer is also in flight. Code that's correct for the happy path is quietly wrong for the real one.&lt;/p&gt;

&lt;p&gt;And the failures are silent. A bug in your checkout page throws an error the customer sees. A bug in your webhook handler just means a customer paid and never got access — and you find out days later from a support email. That's what makes this the part of a Stripe integration worth slowing down on. In the boilerplate, all of this lives in one file (&lt;code&gt;/platform/lib/payments/webhook-handler.ts&lt;/code&gt;) on purpose: the money-touching logic is concentrated in one place you can reason about, not scattered across routes.&lt;/p&gt;

&lt;p&gt;The four sections below are the four gotchas, in the order you'll typically hit them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 1: Signature verification and the raw body trap
&lt;/h2&gt;

&lt;p&gt;Stripe signs every webhook so you can prove the request actually came from Stripe and wasn't forged. Per &lt;a href="https://docs.stripe.com/webhooks" rel="noopener noreferrer"&gt;Stripe's webhook documentation&lt;/a&gt;, each event arrives with a &lt;code&gt;Stripe-Signature&lt;/code&gt; header, and you verify it with the Stripe library before trusting a single byte of the payload. Skip this and an attacker can POST a fake &lt;code&gt;payment_intent.succeeded&lt;/code&gt; to your endpoint and unlock paid features for free. Verification is not optional — it's the security boundary of your whole billing system.&lt;/p&gt;

&lt;p&gt;Here's the trap that gets nearly everyone: &lt;strong&gt;signature verification needs the &lt;em&gt;raw&lt;/em&gt; request body, exactly as Stripe sent it.&lt;/strong&gt; Most web frameworks — including Next.js by default — automatically parse incoming request bodies into JSON for you. That convenience destroys the signature. Stripe computed its signature over the original bytes; the moment your framework re-serializes the parsed object, even a whitespace difference makes the computed signature not match, and verification fails with a cryptic error that has nothing to do with the real cause.&lt;/p&gt;

&lt;p&gt;The fix is to tell your framework &lt;em&gt;not&lt;/em&gt; to parse the body for the webhook route, and to hand Stripe the raw buffer. In a Next.js App Router route handler you read &lt;code&gt;await request.text()&lt;/code&gt; and pass that string straight to &lt;code&gt;stripe.webhooks.constructEvent()&lt;/code&gt; along with the signature header and your signing secret. Get those three inputs right — raw body, signature header, correct secret — and verification just works.&lt;/p&gt;

&lt;p&gt;The other half of signature verification is replay protection. Stripe includes a timestamp in the signed payload, and the official libraries reject anything older than a &lt;strong&gt;5-minute default tolerance&lt;/strong&gt; (&lt;a href="https://docs.stripe.com/webhooks" rel="noopener noreferrer"&gt;per Stripe&lt;/a&gt;). That stops an attacker from capturing a valid old event and replaying it later. You rarely need to touch the tolerance — just know it's why a webhook replayed from a log hours later will fail verification, which is by design, not a bug.&lt;/p&gt;

&lt;p&gt;If verification is failing, the debugging order is almost always: (1) am I using the raw body, (2) is the signing secret the right one for &lt;em&gt;this&lt;/em&gt; endpoint, (3) is the signature header being passed through unmodified. Ninety percent of the time it's number one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 2: Idempotency, or why the same event arrives twice
&lt;/h2&gt;

&lt;p&gt;Stripe guarantees &lt;strong&gt;at-least-once delivery.&lt;/strong&gt; Read that carefully — &lt;em&gt;at least&lt;/em&gt; once, not &lt;em&gt;exactly&lt;/em&gt; once. The same event can, and eventually will, be delivered to your endpoint more than one time. This happens because of retries (more on those next), network hiccups, and Stripe's own redundancy. If your handler isn't built for it, the duplicate is a real bug with real money attached.&lt;/p&gt;

&lt;p&gt;Picture a &lt;code&gt;checkout.session.completed&lt;/code&gt; handler that grants a license and emails a download link. It runs once, all good. Then the same event arrives again. Now you've granted two licenses and sent two emails — or worse, if the handler creates a record, you have a duplicate. For a refund or a credit, a double-processed event is money straight out the door.&lt;/p&gt;

&lt;p&gt;The fix is &lt;strong&gt;idempotency: process each event exactly once, no matter how many times it's delivered.&lt;/strong&gt; The standard pattern, &lt;a href="https://docs.stripe.com/webhooks" rel="noopener noreferrer"&gt;recommended across Stripe integration guides&lt;/a&gt;, is to store every processed &lt;code&gt;event.id&lt;/code&gt; in a database table with a UNIQUE constraint. At the top of your handler, try to record the event ID; if it's already there, you've seen this event — return 200 and stop. If it's new, do the work.&lt;/p&gt;

&lt;p&gt;The subtle part most tutorials skip: &lt;strong&gt;the idempotency record and the actual business logic must commit in the same database transaction.&lt;/strong&gt; If you mark the event as processed &lt;em&gt;before&lt;/em&gt; doing the work and your server crashes in between, the retry sees the ID, thinks it's done, and skips it — so the customer paid and never got access. If you do the work first and crash before recording the ID, the retry runs the work again — double-fulfillment. Wrapping both in one transaction is what makes "exactly once" actually hold. This is also why the &lt;a href="https://codingcapybaras.com/blog/supabase-vs-firebase-2026" rel="noopener noreferrer"&gt;database you pick&lt;/a&gt; matters here: you want real transactional guarantees under your billing logic, not eventual consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 3: Retries, timeouts, and the storm you cause yourself
&lt;/h2&gt;

&lt;p&gt;Stripe decides whether a delivery succeeded based purely on your HTTP status code. &lt;strong&gt;Any 2xx response is success; anything else — a 400, a 500, a redirect, or a timeout — is a failure that triggers a retry.&lt;/strong&gt; And Stripe doesn't retry gently. In live mode it retries with exponential backoff for up to &lt;strong&gt;three days, across roughly 16 attempts&lt;/strong&gt; (&lt;a href="https://docs.stripe.com/webhooks" rel="noopener noreferrer"&gt;per Stripe's documentation&lt;/a&gt;): the first retry within an hour, then spacing out to twice a day, then daily until it gives up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flw7gaz3ssuqvx0kwpv4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flw7gaz3ssuqvx0kwpv4c.png" alt="Line chart showing Stripe's webhook retry schedule with exponential backoff: the first retry happens within about an hour, the next a few hours later, then attempts spread out over roughly three days before Stripe stops." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The chart shows why a slow or flaky handler is so dangerous: every failure schedules another delivery, and those stack up. If your handler is slow enough to time out — Stripe waits only about 10 seconds for a response — every single event "fails" from Stripe's view and gets retried, even though your code eventually finished. Now you've got the original event plus a retry running the same expensive work, racing each other, multiplying load exactly when your server is already struggling. That's the self-inflicted storm.&lt;/p&gt;

&lt;p&gt;The fix flips the order of operations. &lt;strong&gt;Verify the signature, immediately persist or enqueue the event, return 200 — and only then do the slow work.&lt;/strong&gt; Sending email, syncing to another system, generating a PDF: none of that belongs inline in the webhook response. Acknowledge fast so Stripe is satisfied, then process the event from a background job or a queue. This is exactly the kind of long-running work that pushed me toward a &lt;a href="https://codingcapybaras.com/blog/vercel-vs-netlify-vs-railway" rel="noopener noreferrer"&gt;backend platform that can run workers&lt;/a&gt; — serverless functions are fine for the fast acknowledgment, but the heavy processing wants somewhere it can take its time.&lt;/p&gt;

&lt;p&gt;If you do hit failures, don't panic-rebuild — Stripe's dashboard shows every delivery attempt and its response, and you can manually re-send any event. That visibility, plus good logging in &lt;a href="https://codingcapybaras.com/blog/adding-sentry-to-nextjs-saas" rel="noopener noreferrer"&gt;your error tracker&lt;/a&gt;, turns webhook debugging from guesswork into reading a clear record of what arrived and what your code did with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 4: The dev-vs-prod traps that swallow events
&lt;/h2&gt;

&lt;p&gt;The last category isn't a coding bug — it's a configuration bug, and it's the one that makes people think Stripe itself is broken. &lt;strong&gt;Dev and prod are completely separate worlds in Stripe, and mixing them up makes events silently vanish.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest offender is the &lt;strong&gt;signing secret.&lt;/strong&gt; Every webhook endpoint has its own secret, and your local testing setup has a &lt;em&gt;different&lt;/em&gt; secret again. Deploy with the test-mode secret in your production environment, or paste your local secret into prod, and every real event fails signature verification. No crash, no alert — just verification failures piling up while customers pay and get nothing. Each environment needs its own endpoint and its own secret, loaded from that environment's config, never hardcoded. (In the boilerplate, like every secret, this lives only in &lt;code&gt;.env.local&lt;/code&gt; and the deploy platform's env vars — never in the code.)&lt;/p&gt;

&lt;p&gt;For local development, the right tool is the &lt;strong&gt;Stripe CLI.&lt;/strong&gt; Running &lt;code&gt;stripe listen --forward-to localhost:3000/api/webhooks&lt;/code&gt; opens a secure tunnel from Stripe to your machine and prints a dedicated webhook signing secret for that session. You can then &lt;code&gt;stripe trigger checkout.session.completed&lt;/code&gt; to fire real test events at your local handler. This is how you test webhooks without deploying — and without it, people resort to clicking through real checkouts on a deployed test build, which is slow and miserable.&lt;/p&gt;

&lt;p&gt;A few more environment traps worth knowing. Test-mode events and live-mode events are entirely separate streams; a test purchase never hits your live endpoint and vice versa, so "it worked in test but not in prod" is usually a secret or endpoint mismatch. And if your webhook route sits behind authentication middleware, Stripe's request — which carries no login session — gets blocked before your handler runs, so the webhook path must be explicitly public. The reliability of these events is the backbone of everything downstream, from access control to receipts to your &lt;a href="https://codingcapybaras.com/blog/lifecycle-email-indie-saas" rel="noopener noreferrer"&gt;lifecycle email sequences&lt;/a&gt; — a dropped &lt;code&gt;subscription.updated&lt;/code&gt; is a customer who keeps access after canceling, or loses it after paying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why does my Stripe webhook signature verification keep failing?
&lt;/h3&gt;

&lt;p&gt;The most common cause is that your framework parsed the request body before you verified it. Signature verification requires the raw, unmodified body exactly as Stripe sent it. In Next.js, read the raw body with &lt;code&gt;await request.text()&lt;/code&gt; and disable automatic body parsing for the webhook route. The second most common cause is using the wrong signing secret for that endpoint or environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does idempotency mean for Stripe webhooks?
&lt;/h3&gt;

&lt;p&gt;It means processing each event exactly once even though Stripe may deliver it multiple times (at-least-once delivery). Store every &lt;code&gt;event.id&lt;/code&gt; in a database table with a unique constraint, and skip events you've already recorded. Critically, commit the idempotency record and the business logic in the same transaction so a crash between them can't cause double-fulfillment or a missed event.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does Stripe retry a failed webhook?
&lt;/h3&gt;

&lt;p&gt;In live mode, Stripe retries with exponential backoff for up to three days, across roughly 16 attempts — the first within an hour, then spreading out to daily. It treats any non-2xx response, or a timeout (it waits about 10 seconds), as a failure. You can also manually re-send any event from the Stripe dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I do work inside the webhook handler or return 200 first?
&lt;/h3&gt;

&lt;p&gt;Return a 2xx response as fast as possible, then do the slow work. Verify the signature, persist or enqueue the event, and respond 200 immediately. Running email sends, external API calls, or heavy processing inline risks a timeout, which Stripe reads as a failure and retries — creating duplicate work and load right when you can least afford it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I test Stripe webhooks locally?
&lt;/h3&gt;

&lt;p&gt;Use the Stripe CLI. Run &lt;code&gt;stripe listen --forward-to localhost:3000/api/webhooks&lt;/code&gt; to tunnel events to your local server; it prints a dedicated signing secret for the session. Then use &lt;code&gt;stripe trigger &amp;lt;event&amp;gt;&lt;/code&gt; to fire test events at your handler. This lets you develop and debug without deploying or clicking through real checkouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why are my production webhooks failing when local ones work?
&lt;/h3&gt;

&lt;p&gt;Almost always a configuration mismatch. Each endpoint and environment has its own signing secret, and test-mode and live-mode events are separate streams. Confirm production is loading its own live signing secret, that the live endpoint URL is registered in Stripe, and that the webhook route isn't blocked by authentication middleware.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Stripe webhook hell isn't really about Stripe — it's about the fact that webhooks invert the assumptions you code with. You don't control when an event arrives, how many times it arrives, or what else is happening when it does. Once you internalize that, the four gotchas become a checklist: verify with the raw body, make the handler idempotent within a transaction, acknowledge fast and process in the background, and keep dev and prod cleanly separated. Get those right and the "hell" mostly evaporates.&lt;/p&gt;

&lt;p&gt;This is the code you least want to get wrong, so treat it like the core infrastructure it is. If you're building a SaaS with AI coding tools, &lt;a href="https://codingcapybaras.com/" rel="noopener noreferrer"&gt;Coding Capybaras&lt;/a&gt; is the free boilerplate I built for exactly this workflow — the Stripe webhook handler ships with signature verification and idempotency already wired in, so you start from the safe version instead of debugging your way to it.&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>webhooks</category>
      <category>nextjs</category>
      <category>payments</category>
    </item>
    <item>
      <title>Next.js + Supabase + Stripe + Resend: The 2026 Indie SaaS Stack</title>
      <dc:creator>Justin W Boggs</dc:creator>
      <pubDate>Tue, 21 Jul 2026 20:23:24 +0000</pubDate>
      <link>https://dev.to/codingcapybaras/nextjs-supabase-stripe-resend-the-2026-indie-saas-stack-1jbi</link>
      <guid>https://dev.to/codingcapybaras/nextjs-supabase-stripe-resend-the-2026-indie-saas-stack-1jbi</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@cgower" rel="noopener noreferrer"&gt;Christopher Gower&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-macbook-with-lines-of-code-on-its-screen-on-a-busy-desk-m_HRfLhgABo" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The four-piece &lt;strong&gt;indie SaaS tech stack&lt;/strong&gt; that dominates 2026 launches — Next.js, Supabase, Stripe, and Resend — is not the only viable stack. It's the one I picked for Coding Capybaras, and the one most first-time founders shipping with AI coding tools end up with after a weekend of research. The reason isn't hype: it's that each piece collapses a category of infrastructure work that used to require its own specialist, and the four pieces compose cleanly without you writing glue code that breaks at 3 a.m. This post is a tour of each layer, what it replaces, and what it costs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 15&lt;/strong&gt; is the framework: front-end, back-end, server actions, deploys to Vercel in one push.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; is the data layer: managed Postgres, authentication, file storage, all behind one console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt; handles payments, subscriptions, tax, and the customer portal; the only file in your repo that talks to Stripe is the webhook handler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend&lt;/strong&gt; is transactional email — React templates, deliverability handled, free up to 3,000 emails per month.&lt;/li&gt;
&lt;li&gt;At zero customers, this stack costs &lt;strong&gt;$0/month&lt;/strong&gt;. At 10K users with paid revenue, it lands around the low hundreds.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The four-piece stack at a glance
&lt;/h2&gt;

&lt;p&gt;Think of an indie SaaS as four problems: hosting code, storing data, taking money, and sending email. Every stack you've seen — MERN, T3, the LAMP throwback — is a different answer to those four problems. The 2026 indie SaaS tech stack is the answer that asks AI coding tools to write the least glue code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2p71ljt60yff5dogw7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2p71ljt60yff5dogw7v.png" alt="Bar chart comparing approximate monthly infrastructure cost across four growth stages, broken down by Vercel, Supabase, Stripe, and Resend" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the at-a-glance mapping:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it replaces&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hosting + framework&lt;/td&gt;
&lt;td&gt;Next.js on Vercel&lt;/td&gt;
&lt;td&gt;Express + EC2, separate front-end repo + S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database + auth + storage&lt;/td&gt;
&lt;td&gt;Supabase&lt;/td&gt;
&lt;td&gt;Postgres on RDS, NextAuth/Clerk, S3 buckets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payments + subscriptions + tax&lt;/td&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;td&gt;Paddle, FastSpring, custom billing schemas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactional email&lt;/td&gt;
&lt;td&gt;Resend&lt;/td&gt;
&lt;td&gt;SendGrid, Postmark, Mailgun&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A working SaaS in this stack has roughly six surfaces: a marketing page, an auth flow, a paid-checkout flow, a dashboard, an admin page, and a small number of webhook handlers. Everything else is content. You don't run a server, you don't pick a queue, you don't pick a CDN. The tools have opinions about those things and they're correct opinions for a one-person company.&lt;/p&gt;

&lt;p&gt;What this stack is &lt;em&gt;not&lt;/em&gt; good at: heavy background processing, large file pipelines, real-time gaming, anything where you need an actual cluster. If your idea needs that, this is the wrong stack. But "founder shipping a paid B2B SaaS in 2026" is exactly what this stack is shaped like.&lt;/p&gt;

&lt;p&gt;The rest of this post takes each piece in turn — what it does, what it doesn't do, the gotchas first-time founders hit, and what it costs as you grow. I'll stay honest about the rough edges; the goal is for you to make this choice with eyes open, not to upsell you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js 15: the front-end and the back-end in one file tree
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app" rel="noopener noreferrer"&gt;Next.js 15 with the App Router&lt;/a&gt; is the framework. Calling it a "front-end framework" undersells it: you write React pages, server components, and server actions in the same file tree, and the framework decides what runs in the browser and what runs on the server. For a non-tech founder, that's the single most important property of the stack — you stop choosing where code "lives" and start writing features.&lt;/p&gt;

&lt;p&gt;Server actions are the unlock. In Next.js 15, you write a function, slap &lt;code&gt;"use server"&lt;/code&gt; on it, and call it from a form on the client. Next.js generates the API endpoint, validates the call, and runs the function on the server with your database credentials in scope. There is no &lt;code&gt;/api/create-thing.ts&lt;/code&gt; file you have to remember to write, no fetch call that gets the parameter shape wrong. The Next.js team has spent the last year hardening that surface — &lt;a href="https://nextjs.org/blog/next-15" rel="noopener noreferrer"&gt;security improvements in 15 include unguessable action endpoints and origin checks for CSRF&lt;/a&gt;, which used to be on your shoulders.&lt;/p&gt;

&lt;p&gt;You deploy this to Vercel with one push to GitHub. The &lt;strong&gt;Hobby (free) plan&lt;/strong&gt; gets you a real production deploy with HTTPS, automatic preview environments per pull request, 100 GB of bandwidth, 100,000 function invocations, and 1 million edge requests per month, per the &lt;a href="https://vercel.com/docs/limits" rel="noopener noreferrer"&gt;official Vercel limits page&lt;/a&gt;. The license restriction matters: Hobby is &lt;strong&gt;non-commercial only&lt;/strong&gt;, so the moment you take a paying customer you graduate to the $20/month Pro plan. Plan for that line item from day one — it's not optional once you're a business.&lt;/p&gt;

&lt;p&gt;Where you'll get bitten: client components vs. server components is a real mental model, not a buzzword. Anything that uses &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, or browser APIs has to be in a file marked &lt;code&gt;"use client"&lt;/code&gt;. Anything that talks to your database, reads secrets from environment variables, or runs trusted logic stays a server component. Mix them up and you'll either leak server data to the browser or get a build error you don't understand. AI coding tools usually get this right if you describe the page in plain English ("this is a form that posts to the server and shows a success message") — but verify the file boundaries before shipping.&lt;/p&gt;

&lt;p&gt;The Next.js docs are the source of truth here, and they're unusually good. Read the &lt;a href="https://nextjs.org/docs/app" rel="noopener noreferrer"&gt;App Router section&lt;/a&gt; once before you start; it'll save you a weekend of confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supabase: Postgres, auth, and storage behind one console
&lt;/h2&gt;

&lt;p&gt;Supabase is the data layer. Three things live behind one URL: a managed Postgres database, an auth service that handles sign-up, sign-in, password reset, and OAuth, and a file storage layer that's S3 underneath. The reason this matters for indie founders is that each of those was a separate vendor in 2019 and is now one console.&lt;/p&gt;

&lt;p&gt;The Postgres database is real Postgres, not a managed key-value store with SQL on top. You write SQL or use an ORM (Drizzle is what I use; &lt;a href="https://makerkit.dev/blog/tutorials/drizzle-vs-prisma" rel="noopener noreferrer"&gt;Drizzle vs. Prisma is its own decision&lt;/a&gt; and both are fine). Row Level Security policies — the rules that say "user A can only read rows where &lt;code&gt;user_id = A&lt;/code&gt;" — live in the database, which means a bug in your application code can't accidentally hand the wrong user's data to the browser. That's the safety property that makes Supabase appropriate for a one-person operation.&lt;/p&gt;

&lt;p&gt;The auth piece is the one most founders underweight when they pick a stack. Wiring email/password, magic link, Google OAuth, GitHub OAuth, password reset, session refresh, and "remember me" by hand is at least two weekends. Supabase Auth gives you all of it behind a configuration screen. You write one callback route at &lt;code&gt;/auth/callback&lt;/code&gt; and you're done.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;free tier&lt;/strong&gt;, per the &lt;a href="https://supabase.com/pricing" rel="noopener noreferrer"&gt;Supabase pricing page&lt;/a&gt;, gives you two projects with 500 MB of database storage, 1 GB of file storage, 50,000 monthly active users for auth, and 500,000 edge function invocations. That's enough to launch and earn your first 1,000 users without paying. The catch — and you will hit it — is that free projects &lt;strong&gt;pause after one week of inactivity&lt;/strong&gt; and the free tier has &lt;strong&gt;no automatic backups&lt;/strong&gt;. For a real product, the $25/month Pro plan is correct from day one of paying customers; the daily backups alone justify it.&lt;/p&gt;

&lt;p&gt;Where you'll get bitten: the three-clients pattern in Next.js 15. You need a Supabase client for browser code, a separate client for server components and server actions, and a third "service role" client for admin tasks that bypass RLS. Get those mixed up and you'll either expose admin powers to the browser or fail to write to the database from a server action. Every Supabase + Next.js boilerplate has its own opinion on this; copy the pattern, don't invent yours.&lt;/p&gt;

&lt;p&gt;If you're choosing between Supabase and the alternatives, &lt;a href="https://supabase.com/blog" rel="noopener noreferrer"&gt;Supabase vs. Firebase is its own decision&lt;/a&gt; worth a separate post — Supabase wins for SaaS with rows, Firebase wins for mobile-first real-time apps. For the indie SaaS shape, Supabase is the safer default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stripe: payments, subscriptions, and the customer portal
&lt;/h2&gt;

&lt;p&gt;Stripe is the payments layer. It is the boring, correct answer for an indie SaaS in 2026, and you shouldn't think too hard about it. &lt;a href="https://stripe.com/pricing" rel="noopener noreferrer"&gt;Stripe's standard US online card rate is 2.9% + 30¢ per successful charge&lt;/a&gt;, with no setup fees and no monthly fees. If you sell subscriptions, the Billing add-on charges an additional 0.7% of billing volume for the recurring infrastructure — subscription schedules, proration, dunning, the customer portal.&lt;/p&gt;

&lt;p&gt;What you actually get for those fees is the part that's hard to value until you've tried to build it yourself: PCI compliance is on Stripe's side, the hosted checkout page handles a hundred currencies and tax forms, the customer portal lets your users cancel or upgrade their own subscriptions without a support ticket, and Stripe Tax (an additional fee) handles VAT and US sales tax registration for you. If you've ever read someone's "I tried to handle EU VAT myself" thread on X, you know what you're buying.&lt;/p&gt;

&lt;p&gt;Three rules for Stripe in a Next.js app, in order of how badly each one will burn you if you ignore it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Stripe SDK gets imported in exactly one file.&lt;/strong&gt; In Coding Capybaras that's &lt;code&gt;/platform/lib/payments/stripe.ts&lt;/code&gt;. Every other file calls a thin abstraction layer. This sounds pedantic until you want to swap Stripe for Lemon Squeezy in two years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify webhook signatures.&lt;/strong&gt; Stripe sends a &lt;code&gt;Stripe-Signature&lt;/code&gt; header on every webhook. If you skip the verification, anyone on the internet can hit your webhook endpoint with a fake "subscription canceled" event and lock real customers out of their accounts. The signature check is six lines of code. Write them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook handlers must be idempotent.&lt;/strong&gt; Stripe will retry events. Your handler will see the same &lt;code&gt;customer.subscription.updated&lt;/code&gt; event twice. Store the event ID, check for duplicates, no-op the second time. If you skip this, you'll grant double credits, send double receipts, and explain it to your first ten paying customers individually.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Coding Capybaras boilerplate gets the webhook plumbing right out of the box — that's specifically the part I wanted to never have to think about again. If you're choosing how to ship payments today, the &lt;a href="https://codingcapybaras.com/blog/saas-boilerplate-comparison-2026" rel="noopener noreferrer"&gt;Coding Capybaras boilerplate comparison post&lt;/a&gt; walks through how the four major options handle this. Stripe vs. Lemon Squeezy vs. Paddle is the other branch of this decision — Stripe wins for the US-centric, dev-experience-first founder; Paddle wins for the EU founder who wants merchant-of-record handling.&lt;/p&gt;

&lt;p&gt;What you'll get bitten by: dev vs. prod webhook secrets. They're different. The Stripe CLI's &lt;code&gt;stripe listen&lt;/code&gt; command generates a fresh secret for your local environment that won't work in production. Keep both in &lt;code&gt;.env.local&lt;/code&gt; and &lt;code&gt;.env.production&lt;/code&gt; respectively, and never let a deploy ship with the dev secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resend: transactional email without the deliverability tax
&lt;/h2&gt;

&lt;p&gt;Resend is the email layer. Welcome emails, password resets, payment receipts, "your subscription is expiring" — all transactional, all going through one provider, all logged in your own database so you can prove what was sent when.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://resend.com/pricing" rel="noopener noreferrer"&gt;Resend free tier&lt;/a&gt; gives you 3,000 emails per month and 100 per day. The Pro plan is $20/month for 50,000 emails. Both include React Email, which is the actual reason Resend won the indie SaaS bracket in 2025 — you write email templates as React components in the same repo as your app, with the same TypeScript types. There is no separate template editor, no "preview in Outlook" tab. The same component you preview in the Next.js dev server is the one that gets sent.&lt;/p&gt;

&lt;p&gt;Why this matters: every founder I know has lost a half-day to a Postmark template that didn't render right in Gmail, or to a SendGrid template that broke when the marketing team renamed a merge tag. React Email moves the templates into the file tree, where your AI coding tool can read them and your linter can yell about typos. The deliverability infrastructure — SPF, DKIM, DMARC, IP warm-up, multi-region sending — Resend handles in the background. You buy a custom domain, paste three DNS records into your registrar, and email starts arriving in inboxes.&lt;/p&gt;

&lt;p&gt;The pattern I use in the boilerplate: every transactional send goes through one helper function (&lt;code&gt;sendEmail()&lt;/code&gt;), and that helper writes a row to a &lt;code&gt;platform_email_log&lt;/code&gt; table before calling Resend. When a customer says "I never got the receipt," you check the log first, see whether Resend accepted the message, and only then dig into the user's spam folder. Founder support gets significantly less frustrating when you can prove what was actually sent.&lt;/p&gt;

&lt;p&gt;What you'll get bitten by: domain verification can take up to 48 hours after you add DNS records (it's usually minutes, but the long tail exists). Set this up the same day you set up your Stripe account, not the morning of your launch.&lt;/p&gt;

&lt;p&gt;If you're comparing Resend to alternatives, the &lt;a href="https://codingcapybaras.com/blog/getting-started" rel="noopener noreferrer"&gt;Coding Capybaras blog post on the file-based blog system&lt;/a&gt; is a good example of how I treat email-template files the same way I treat blog-post files — version-controlled, in the repo, deployable with a &lt;code&gt;git push&lt;/code&gt;. The deeper Resend vs. Postmark vs. Mailgun comparison is its own post; the short version is that Resend wins for an indie SaaS that writes templates in code, Postmark wins for a team that wants a UI editor, and Mailgun wins on raw scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this stack actually costs from $0 to 10K users
&lt;/h2&gt;

&lt;p&gt;The honest cost picture, end-to-end:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Hosting&lt;/th&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Payments&lt;/th&gt;
&lt;th&gt;Email&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pre-launch (0 users)&lt;/td&gt;
&lt;td&gt;Vercel Hobby ($0)&lt;/td&gt;
&lt;td&gt;Supabase Free ($0)&lt;/td&gt;
&lt;td&gt;Stripe ($0)&lt;/td&gt;
&lt;td&gt;Resend Free ($0)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0/mo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First paying customer&lt;/td&gt;
&lt;td&gt;Vercel Pro ($20)&lt;/td&gt;
&lt;td&gt;Supabase Free ($0)&lt;/td&gt;
&lt;td&gt;~3% of revenue&lt;/td&gt;
&lt;td&gt;Resend Free ($0)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$23/mo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000 users, paid&lt;/td&gt;
&lt;td&gt;Vercel Pro ($20)&lt;/td&gt;
&lt;td&gt;Supabase Pro ($25)&lt;/td&gt;
&lt;td&gt;~3% of revenue&lt;/td&gt;
&lt;td&gt;Resend Free ($0)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$74/mo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000 users, paid&lt;/td&gt;
&lt;td&gt;Vercel Pro ($20)&lt;/td&gt;
&lt;td&gt;Supabase Pro ($25)&lt;/td&gt;
&lt;td&gt;~3% of revenue&lt;/td&gt;
&lt;td&gt;Resend Pro ($20)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$355/mo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few honest caveats on that table. Stripe's percentage isn't a fixed cost — it's a fee on revenue, and the line above is a back-of-envelope at $1,000/month MRR scaling to ~$10K MRR. The day you take your first paying customer is the day Vercel Hobby's non-commercial license stops applying — graduate to Pro. The Supabase free tier is genuinely usable for a paid product technically, but the lack of backups is the wrong gamble. None of these numbers include domain registration ($12/year), Stripe Tax (a per-jurisdiction fee that's worth it the day you have international revenue), or Sentry/PostHog/analytics — those are separate marketplace decisions.&lt;/p&gt;

&lt;p&gt;If you want the line-item version with usage caveats, the &lt;a href="https://vercel.com/docs/limits" rel="noopener noreferrer"&gt;Vercel limits documentation&lt;/a&gt; and the &lt;a href="https://supabase.com/pricing" rel="noopener noreferrer"&gt;Supabase pricing page&lt;/a&gt; are the canonical sources. I update the numbers in the &lt;a href="https://codingcapybaras.com/marketplace" rel="noopener noreferrer"&gt;Coding Capybaras integration marketplace&lt;/a&gt; whenever the providers change them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Next.js really the right framework for a non-tech founder?
&lt;/h3&gt;

&lt;p&gt;For a SaaS in 2026, yes. Next.js has the largest community, the most LLM training data, and the best AI-coding-tool support of any React framework. Your AI assistant will produce working Next.js code more reliably than working Remix or SvelteKit code, simply because there are more examples for it to draw from. That community-size effect is decisive when you're not the one debugging at 2 a.m.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I swap Supabase for Firebase later?
&lt;/h3&gt;

&lt;p&gt;You can, but it's a real migration — auth schemas differ, the database models differ, and Firebase's real-time primitives don't map cleanly to Postgres. Pick the one you'll be on in two years and commit. For most indie SaaS shapes — B2B subscription, rows of data, RLS-style permissions — Supabase is the safer pick. Firebase wins if you're building a mobile-first app with heavy real-time sync.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if Stripe isn't available in my country?
&lt;/h3&gt;

&lt;p&gt;Stripe is live in 47+ countries as of 2026. If yours isn't on the list, Paddle or Lemon Squeezy are the merchant-of-record alternatives — they handle the legal entity that takes the money on your behalf, in exchange for a higher fee. You give up Stripe's developer experience and you pick up tax compliance. Whether that's worth it depends entirely on your geography.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I really need Resend if Supabase Auth sends emails?
&lt;/h3&gt;

&lt;p&gt;Supabase Auth sends transactional emails (sign-up confirmation, password reset) on its own infrastructure, which is fine for those specific flows. The moment you want to send anything else — receipts, lifecycle emails, "your trial expires in three days" — you need a real email provider. Resend is the cheapest correct answer; Postmark is the next step up if you want a UI template editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this stack overkill for a side project I haven't validated yet?
&lt;/h3&gt;

&lt;p&gt;It costs $0 to find out. The free tiers of all four services let you build a real, working SaaS, take a real payment, and email a real customer without spending a dollar on infrastructure. The "overkill" question shows up after launch, when you're paying $355/month for 10K users — and at that point you have revenue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I learn this stack myself or have AI write it?
&lt;/h3&gt;

&lt;p&gt;Both, in that order. Have the AI assistant write the first version so you can ship something. Read the code it produces. Ask why it made each decision. By the third feature you'll know enough Next.js to debug what your AI assistant gets wrong — and that's the point at which AI coding stops being a parlor trick and starts being a real engineering multiplier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest take
&lt;/h2&gt;

&lt;p&gt;This stack works because the four pieces have spent the last three years getting better at the same problem from different angles. Next.js made the framework decision easier. Supabase made the database/auth decision easier. Stripe was already the boring correct answer for payments. Resend brought email into the file tree where it belongs. None of them are the only viable choice for their layer — they're the layer-best choices for a founder shipping a paid B2B SaaS with AI coding tools in 2026.&lt;/p&gt;

&lt;p&gt;If you're building exactly that shape of company, &lt;a href="https://codingcapybaras.com/" rel="noopener noreferrer"&gt;Coding Capybaras&lt;/a&gt; is the free boilerplate I built around this stack — every piece wired up, AI rule files in every region, GUI for the configuration most boilerplates make you edit in code. The complete codebase ships free.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>saas</category>
      <category>supabase</category>
      <category>stripe</category>
    </item>
  </channel>
</rss>
