Joan Comadran
Field notes

Playbooks

The stack I use to ship an AI product in a weekend

The exact Next.js 16 + Claude + Stripe stack I've used to ship five AI products, and the boring, repeatable playbook that turns an idea into a paid, deployed app in two days.

July 19, 20266 min read

Here's the whole thing, so you can leave early if that's all you wanted: I ship AI products with Next.js 16 on Vercel, the Anthropic SDK for the smart part, Stripe Checkout for the money part, and a database only when something forces me to add one. That's it. I've built five products on this exact setup, and every piece is there because a previous weekend humbled me into adding it, not because it trended on Hacker News.

This isn't a tutorial. It's the boring version, which is the useful version. The deep dives get their own posts (the WhatsApp lead bot, the Stripe email gate, the ongoing war against Claude's token bill), and I'll link them as they show up. Here I just want to hand you the shape of the thing: what goes in the box, in what order, and what to leave on the shelf no matter how good it looks.

Why this stack and not the one with twelve services

Every hour you spend wiring up infrastructure on a Friday night is an hour the product doesn't exist. So the whole stack optimizes for one number, and it's not "developer joy" or "resume keywords". It's time from idea to a URL a stranger can pay on.

ConcernChoiceWhy it survives contact with a weekend
FrameworkNext.js 16, App RouterOne repo, one deploy, server and client in the same place
UITailwind CSS 4No design system to build at 1am
HostingVercelThe deploy is git push. That's the feature.
AIAnthropic SDKOne function call instead of a GPU and a prayer
PaymentsStripe CheckoutHosted page, PCI is their problem, live in an afternoon
DataPostgres / SupabaseOnly when the app genuinely needs to remember something

That last row is the one everyone gets wrong. The classic weekend-project death isn't choosing the wrong database. It's choosing a database at all, on Saturday morning, for an app that has no users and nothing worth persisting. Three of my five products shipped with no database whatsoever. State lived in the request, the model's reply, and Stripe. You add persistence the day a feature demands it, and not one commit before, no matter how grown-up it feels to have a schema.

The weekend, roughly hour by hour

Four phases. They fit a Saturday and a Sunday with time left over to regret starting.

1. Scaffold (30 minutes, most of it staring)

bash
npx create-next-app@latest my-product --typescript --tailwind --app
cd my-product
npm i @anthropic-ai/sdk stripe

One .env.local, three keys at most, and you have something deployable. Push it to Vercel right now, before a single line of product code exists. That way "deploy" is never the villain standing between you and Sunday night. It's just a thing that already works and keeps working while you break everything else.

2. The AI call, which is to say, the product

Most AI products are, at their heart, one server-side function with good taste. Here's the shape of it, close to what runs the tarot readings in Arcaia: the user fills in a short flow, and Claude writes the reading.

ts
// app/api/generate/route.ts
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic();

export async function POST(req: Request) {
  const { context } = await req.json();

  const message = await anthropic.messages.create({
    model: "claude-haiku-4-5",
    max_tokens: 1024,
    system: "You are a tarot reader. Write a warm, specific reading.",
    messages: [{ role: "user", content: context }],
  });

  const text = message.content.find((b) => b.type === "text")?.text ?? "";
  return Response.json({ reading: text });
}

Two things in there worth stealing:

The model is a budget decision wearing a technical costume. Arcaia runs on claude-haiku-4-5 because tarot copy does not require a model that can prove theorems. Haiku is the cheap, fast tier ($1 and $5 per million tokens in and out), and no user has ever emailed to complain their reading wasn't reasoned hard enough. Reach for Opus when the task actually needs Opus. That single line is often the whole difference between a product with margins and a very impressive way to set money on fire.

And the call stays on the server. Your API key never sees a browser. This is not a stylistic preference, it's the difference between a side project and a public incident.

3. The paywall, before you talk yourself out of it

Free tier to earn trust, paid tier to earn rent. Stripe Checkout is a hosted page, so you make a session on the server and send people to it:

ts
// app/api/checkout/route.ts
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST() {
  const session = await stripe.checkout.sessions.create({
    mode: "payment",
    line_items: [{ price: process.env.STRIPE_PRICE_ID, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/unlocked?s={CHECKOUT_SESSION_ID}`,
    cancel_url: process.env.NEXT_PUBLIC_SITE_URL,
  });
  return Response.json({ url: session.url });
}

The move that keeps paying off across my products is the email gate plus payment unlock: give away enough to prove it works, then put the good result behind a checkout. The interesting part (verifying the payment, actually unlocking the thing, not getting scammed) is a whole post of its own. But the skeleton is the twelve lines above, and twelve lines is a suspiciously small amount of code to be standing between you and revenue.

4. Deploy, then physically walk away from the keyboard

You already connected Vercel in phase 1, so shipping is git push. The genuinely hard engineering problem in phase 4 is not opening the editor to add a fifth feature "real quick". A dumb product that takes money beats a gorgeous one still running on localhost:3000, and it beats it by an infinite margin, because one of them exists.

What I leave out on purpose

The stack is defined by its refusals as much as its picks:

  • No CMS. Content is MDX or hardcoded. A CMS is a second app you now also have to run and, eventually, migrate.
  • No auth library until a feature needs someone logged in. Taking their money does not.
  • No database until state has to outlive a request. I know I keep saying this. It keeps being the thing people ignore.
  • No component library. Tailwind plus a dozen hand-rolled components ships faster than an evening spent losing an argument with someone else's abstractions.

Every one of these is a thing you can bolt on later in an afternoon. Not one of them is a thing worth losing your Saturday to.

The tools aren't the point, the sameness is

I've built Arcaia, a WhatsApp bot that qualifies leads with Claude, a mortgage calculator, a rental-price checker, and a few things that deserved to die quietly, all on this same skeleton. The advantage was never any particular tool. It's that the stack doesn't change, so every weekend starts a few hours in instead of at a blank create-next-app. Boring is the strategy.

If you want the layer underneath, the supporting posts pull the pieces apart:

Pick the stack once. Then go spend your weekends on the product instead of the plumbing, which was the whole idea.

Keep reading

02