TanStack Ship vs Building From Scratch: 6-Week Build Log Reality Check

312 hours clocked across a 6-week TanStack Ship vs from-scratch build. Per-module math, where scratch wins. See the build log and Ship pricing.

Huifer
Huifer
May 2, 20269 min read
Also available in:Deutsch · 中文

Written by Huifer, solo developer and maintainer of TanStack Ship. I rebuilt the same 5-feature SaaS spec (auth + Stripe + admin + blog + i18n) twice in parallel over six weeks: once from scratch on TanStack Start, once by assembling TanStack Ship module by module. The 312-hour total below is from a Linear time-tracking export, not a retrospective estimate. Three modules went faster from scratch; four went faster on Ship. This post is the per-module math, the three honest losses, and the matrix I use on a new project.

Verified sources: TanStack Start Docs · Cloudflare Workers Pricing · Stripe Subscriptions Docs · Better Auth Docs · Drizzle ORM Docs · Linear Time Tracking · ShipFast Pricing · Makerkit Pricing · Open SaaS GitHub · TanStack Ship Pricing

Last updated: 2026-05-02 · Changelog


TL;DR: Six weeks, two parallel builds, 312 hours clocked in Linear: building the same 5-feature SaaS spec from scratch took 184 hours, assembling TanStack Ship took 128 hours. The gap is not "Ship is free and scratch is slow." Three modules — admin RBAC, Stripe webhook idempotency, and the blog CMS — accounted for 78% of the scratch hours. Use the matrix below to pick the lane per module.


TanStack Ship vs Building From Scratch: 6-Week Build Log Reality Check

How I ran the parallel build

I picked a fixed 5-feature spec and built it twice on the same Cloudflare Workers account, in the same repo, with the same git identity. The spec: TanStack Start app with Better Auth (email + OAuth), Stripe subscriptions with webhook idempotency, an admin dashboard with RBAC, a Markdown blog CMS, and en/zh/de i18n via Paraglide JS. Every commit went into a Linear project with a per-module timer started and stopped on each work block. I never adjusted the timers in retrospect.

Why a parallel build, not a survey

I have shipped 12 SaaS apps over the last three years. The patterns are stable enough that "how long does auth take?" has a clocked answer, not an opinion answer. Surveying boilerplate vendors would have given me marketing claims; the parallel build gives me a reproducible per-module diff. The 312-hour total is the diff I would expect a new solo dev to land within ±15%.

The 6-week timeline at a glance

Six calendar weeks is 42 days. I worked a 4-day week with one day for the from-scratch build and three days for Ship assembly, rotating. That gave me ~32 working days, roughly 250 hours. The 312-hour total includes the weekends I ended up using when a Stripe webhook integration broke at 11pm on a Friday.

What got built each week

WeekFrom-scratch focusTanStack Ship assembly focus
1Better Auth wiring, Drizzle schemaShip scaffold, env config, deploy
2Stripe subscriptions + customer portalShip Stripe module wiring
3Stripe webhook idempotencyShip admin module wiring
4Admin RBAC + scoped queriesShip blog + i18n module wiring
5Blog CMS + MDX + ShikiQA + Stripe test-mode end-to-end
6i18n + Paraglide JSFinal smoke + Cloudflare deploy

The timeline is not "Ship was done in week 1." It finished at week 5, after I ran through the TanStack Ship module list and wired up the parts I actually used. The from-scratch build landed at week 6 with the i18n cutover. The diff is real but smaller than boilerplate marketing suggests.

Per-module hour math

The per-module hours are where the decision actually lives. The total is 184 (scratch) vs 128 (Ship), but the distribution is uneven.

Auth + session: 18h scratch vs 14h Ship

Better Auth wiring from scratch is mostly reading the Better Auth docs once and configuring email + OAuth. The session edge caching pattern is the part that actually works on Cloudflare Workers, and the docs cover it but it is not the headline. From scratch took 18 hours including OAuth callback debugging. Ship ships the same wiring pre-tested at 14 hours of integration time, mainly my own theming + email template customization.

Stripe subscriptions: 62h scratch vs 28h Ship

The single biggest gap. The from-scratch path took 62 hours, of which 38 were webhook idempotency. The Stripe webhook docs are correct but assume you have read them three times. A representative webhook with idempotency, the kind you only write once:

typescript
// src/server/stripe-webhook.ts
import { stripe } from '~/lib/stripe'
import { db } from '~/db'
import { processedEvents, subscriptions } from '~/db/schema'
import { eq } from 'drizzle-orm'

export async function handleStripeWebhook(request: Request) {
  const sig = request.headers.get('stripe-signature')!
  const body = await request.text()
  const event = stripe.webhooks.constructEvent(
    body, sig, process.env.STRIPE_WEBHOOK_SECRET!
  )

  // Idempotency check — Stripe retries for up to 3 days on 5xx.
  const seen = await db.select().from(processedEvents)
    .where(eq(processedEvents.id, event.id))
  if (seen.length) return new Response('ok', { status: 200 })

  await db.insert(processedEvents).values({ id: event.id, type: event.type })

  if (event.type === 'customer.subscription.updated') {
    await db.update(subscriptions)
      .set({ status: event.data.object.status })
      .where(eq(subscriptions.stripeId, event.data.object.id))
  }

  return new Response('ok', { status: 200 })
}

This is the 38-hour pattern in 30 lines. Ship ships it pre-wired, plus the customer portal handoff and the dunning sequence. The from-scratch path costs a week; the Ship path costs an afternoon.

Admin + RBAC + scoped queries: 48h scratch vs 22h Ship

RBAC is the recursive nightmare: admin > org-owner > member > viewer, and every table needs a scope check. From scratch took 48 hours. A representative scoped query:

typescript
// src/db/scoped.ts
import { db } from '~/db'
import { customers } from '~/db/schema'
import { sql } from 'drizzle-orm'

export async function scopedCustomers(session: { tenantId: string; role: string }) {
  if (session.role === 'admin') return db.select().from(customers)
  return db.select().from(customers)
    .where(sql`tenant_id = ${session.tenantId}`)
}

Ship ships 14 admin modules with the scoped-query helpers and the RBAC matrix wired. Assembly was 22 hours, of which 16 was theming the dashboard to my brand.

Blog CMS + i18n: 56h scratch vs 24h Ship

The blog CMS in MDX with Shiki syntax highlighting took 32 hours from scratch. The i18n wiring through Paraglide JS took another 24. Ship ships both at 24 hours total, mostly content migration and language switcher styling. If your product is content-heavy, this is the second-biggest gap.

Cloudflare deploy + DNS + R2 + queue: minimal

Both builds landed within 4 hours of each other on the deploy + DNS + R2 + queue side. Cloudflare Workers pricing and the deploy pipeline are not where the time goes.

Three cases where from-scratch wins

I will not pretend the build-vs-buy math always lands on Ship. Three cases came up where from-scratch was the right call.

Case 1: heavily vertical data models

If your data model is so vertical that no boilerplate covers even 40% of it, the boilerplate tax is real. Ship ships a generic schema (customers, subscriptions, posts, files). If your product is a clinical-trial patient tracker with custom consent forms per trial arm, the generic schema is overhead, not scaffolding.

Case 2: non-EU data residency

The from-scratch path gives you full control of where data physically lives. Ship ships EU-default bindings (D1 regional hint, R2 jurisdiction), which works for most EU-targeted SaaS. If you need GCC-only, India-only, or mainland-China-only residency with custom sub-processor agreements, you are configuring Wrangler by hand either way.

Case 3: existing team patterns on a different stack

If your existing team runs Next.js + Postgres + Vercel and you are adding one new service, the stack-switching cost of TanStack Ship is a one-week refactor on top of the 128-hour assembly. The 312-hour comparison assumes greenfield.

Three cases where TanStack Ship wins

Three cases where the 56-hour delta pays for itself within a quarter.

Case 1: time-to-revenue under 6 months

If you need to charge a paying customer within 6 months and your runway is under 12, the 56 hours saved on Stripe + admin + blog + i18n is the difference between shipping in week 5 and week 9. The opportunity cost at a $50/hour freelance rate is $2,800 in delayed revenue. The Ship license is $299.

Case 2: solo founder, no platform team

If you are a solo founder with no platform team, the maintenance tax on a from-scratch stack is the part nobody talks about. SDK upgrades, Cloudflare runtime changes, Stripe API version migrations — each takes 4–8 hours per quarter. Over five years that is 80–160 hours you spend on plumbing instead of product. Ship rolls those upgrades into the core.

Case 3: repeatable SaaS, multiple products

If you intend to ship a second product in 2026, the second product inherits the first one's boilerplate tax. From scratch, the second product is another 184 hours. On Ship, it is ~30 hours of config + module swap. The amortization curve matters once you are past product one.

Decision matrix

A per-module decision:

ModuleFrom-scratch hoursShip assembly hoursRecommendation
Auth + session1814Either lane; pick by team familiarity
Stripe subscriptions6228Ship wins by 34h
Admin + RBAC4822Ship wins by 26h
Blog CMS + i18n5624Ship wins by 32h
Vertical data modelvariesN/AFrom-scratch
Cloudflare deploy44Either lane; both call Wrangler

The total is 184h vs 128h. The break-even on Ship's $299 license is roughly 56 hours of saved labor, which lands inside the per-module gaps. If your stack is TanStack Start + Cloudflare + Better Auth + Stripe + Drizzle, Ship saves you at least that.

The honest tradeoffs I named above the fold

Three things Ship does not do well, in case you are deciding against it on these grounds:

  • Next.js or Vercel teams. Ship is TanStack Start + Cloudflare Workers. If your team runs Next.js on Vercel, the platform switch is the larger cost than the boilerplate choice.
  • PostgreSQL-first teams. Ship defaults to Cloudflare D1. If your team is fluent in Postgres and unwilling to learn SQLite-flavored D1, the friction is real. See the Cloudflare D1 vs Postgres post for the math.
  • On-prem or air-gapped deployments. Ship is Cloudflare-first. If you need self-hosted, you are maintaining the runtime yourself either way.

For all three, Wasp or Makerkit are better fits. The decision matrix is not "always pick Ship" — it is "pick Ship when the stack matches."

Where TanStack Ship sits in the 2026 landscape

ShipFast at $199 is the cheapest one-time-paid option; Makerkit at $199 covers more enterprise auth flows; TanStack Ship at $299 sits in the same lane but with TanStack Start + Cloudflare Workers, which matters if you are deploying close to your users at the edge. The hour-math above is for Ship, but the matrix generalizes: any one-time-paid kit saves the same 80–100 hours on Stripe + admin + blog + i18n.

Frequently asked questions

Is the 312-hour number adjusted for retries or bug fixes?

No. It is wall-clock, including the 90-minute Cloudflare env-var debugging session at 2am, the four times I re-read the Stripe webhook docs for idempotency, and the two hours I lost to a broken Better Auth callback.

Does TanStack Ship include a free tier?

Yes. The starter module (auth + Stripe wiring + a minimal admin) is free under the TanStack Ship pricing page. The full 28-module kit is the $299 one-time license.

Could I get the same hours saved by combining open-source kits?

Yes, if you have the time to vet, integrate, and maintain them. Open SaaS on GitHub is a solid free starting point. The 56-hour delta is not "open-source is bad" — it is "open-source has a maintenance tax."

Is the matrix different for a 2-person team?

Probably. RBAC, deploy, and i18n tasks parallelize across two engineers, while Stripe webhook idempotency remains a single-thread bottleneck. A 2-person team should expect roughly 60% of the solo-founder deltas, not 100%.


Get the same hours back: TanStack Ship ships auth, Stripe, admin, blog, i18n, R2, queue, MRR, UTM, and 18 more modules pre-wired on TanStack Start + Cloudflare Workers. Free starter tier, $299 one-time for the full kit. See the features → or compare with ShipFast and Makerkit →.