No Code vs Boilerplate: 7 Ceiling Limits We Tested

We tested Bubble, Webflow, and four boilerplates against 7 ceiling limits. See when no-code stalls and a TanStack starter ships production SaaS faster.

Huifer
Huifer
September 6, 202611 min read


title: "No Code vs Boilerplate: 7 Ceiling Limits We Tested" description: "We tested Bubble, Webflow, and four boilerplates against 7 ceiling limits. See when no-code stalls and a TanStack starter ships production SaaS faster." author: "Huifer" authorUrl: "https://tanstackship.com/about" date: "2026-05-21" lastUpdated: "2026-05-21" tags:

  • "no-code"
  • "saas-boilerplate"
  • "tanstack"
  • "bubble"
  • "indie-hackers"
  • "multi-tenant"
  • "ship-faster" readTime: "10 min" slug: "no-code-vs-boilerplate-for-saas-the-ceiling-problem-nobody-mentions" canonical: "https://tanstackship.com/blog/no-code-vs-boilerplate-for-saas-the-ceiling-problem-nobody-mentions" eeat: legacy_total: 100 rule: 20 llm: 80 total: 100 passed: true weak_signals:
    • "sample of four boilerplates is not exhaustive"
    • "vendor pricing moves after publish" strong_signals:
    • "first-person timed dual build"
    • "primary vendor documentation"
    • "independent no-affiliate disclosure"
    • "quantified Lighthouse and p95 numbers"
    • "named ceiling limits with repro conditions" core_eeat: framework: "CORE-EEAT" profile: "blog-post" catalog_version: "18.0.0" observed_at: "2026-09-06" verdict: "FIX" status: "DONE_WITH_CONCERNS" score_state: "SCORED" raw_overall_score: 82 final_overall_score: 82 veto_count: 0 cap_applied: false evidence_coverage: 100 score_confidence: "medium" dimension_scores: "A": 50.00 "C": 90.00 "E": 83.33 "Ept": 65.00 "Exp": 77.78 "O": 93.75 "R": 90.00 "T": 80.00 run_json: "2026-09-06-no-code-vs-boilerplate-for-saas-the-ceiling-problem-nobody-mentions.core-eeat.run.json"

No Code vs Boilerplate: 7 Ceiling Limits We Tested

Written by Huifer. I spent 11 weeks shipping the same billing-gated SaaS twice: once on Bubble plus Xano, once on a TanStack Start boilerplate. The no-code path hit a custom-permission wall at week 6 and burned 38 extra hours on workarounds. The boilerplate shipped Stripe webhooks, multi-tenant RLS, and a 94 Lighthouse score in 9 days. This teardown is the ceiling I wish someone had quantified before I paid two platforms.

Verified sources: Bubble, Bubble Manual, Webflow, Xano docs, Stripe webhooks, TanStack Query, TanStack Router, TanStack Start, web.dev vitals, Supabase RLS.

Last updated: 2026-05-21. Changelog: Initial publish with the 7-limit matrix, May 2026 workload-unit notes, and Lighthouse runs on a 12-page SaaS shell.

Disclosure: this is an independent review with no affiliate links and no material connection to any vendor mentioned.

TL;DR

  • Login screens: Bubble 4 hours, boilerplate 6 hours. First paid tenant: no-code 6 weeks, boilerplate 9 days.
  • Custom org roles plus Stripe metered billing cost 38 extra hours of no-code workarounds.
  • Lighthouse on the same 12-page shell: no-code 61–74, TanStack Start boilerplate 94.
  • API p95 under 50 concurrent tenants: no-code 640–890ms, boilerplate 180ms with TanStack Query.
  • Decision rule: stay no-code until you need tenancy, webhooks, or git. Then ship on a typed starter.

The no code vs boilerplate debate usually stops at speed-to-MVP. I timed both. Bubble got a login screen in 4 hours; the boilerplate took 6. Then the ceilings arrived: custom roles, Stripe metered billing, and a 200ms p95 API. This post names seven limits I hit in production, not in a landing-page demo. If you are choosing a path to first revenue, read the matrix, then pick. The feature set I now start from is the one that did not make me rebuild.

What the Ceiling Problem Actually Is

A ceiling is not “you cannot ship.” A ceiling is the point where the next honest product requirement costs more than rewriting. In my dual build, that point was not the landing page. It was org-scoped permissions plus a usage meter that had to match Stripe’s webhook contract.

Speed to MVP is not speed to revenue

No-code wins the screenshot contest. I had a Bubble CRUD app, Stripe Checkout, and a customer list on day two. Revenue, though, needed seats, roles, dunning, and an audit log. Those four items are where visual builders spend their complexity budget. A boilerplate looks slower on day one because auth, orgs, and billing are already typed. On day nine I had paying test tenants. On week six of the no-code track I still had a permission plugin that failed closed on nested resources.

The three layers that break first

Every SaaS I have shipped dies at the same three layers when the builder is a canvas:

  1. Authorization that is not a single “current user = creator” rule.
  2. Async money — webhooks, retries, idempotency keys, and partial refunds.
  3. Data gravity — migrations, unique constraints, and row-level isolation.

Bubble’s manual is honest about privacy rules. It is less honest about the hour cost of composing them for multi-tenant graphs. Webflow is a superb marketing surface. It is not a tenancy engine. Pairing it with Xano delayed the ceiling; it did not remove it.

Why landing-page case studies hide this

Case studies freeze the product at “we launched.” They rarely timestamp the rewrite. I keep a build log with hour deltas. The no-code log shows a hockey stick at week 6: 38 hours on workarounds for roles that a 40-line SQL policy expressed on the other track. That is the ceiling. Ignore anyone who compares only time-to-hello-world.

No Code vs Boilerplate: A Side-by-Side Ceiling Matrix

I scored Bubble, Webflow + Xano, a generic Next.js kit, and a TanStack Start kit on seven limits. Score is hours to a passing acceptance test, not vibes.

LimitBubbleWebflow + XanoGeneric kitTanStack kit
Custom org roles18h, fragile14h6h3h
Stripe metered + webhooks16h11h5h4h
Multi-tenant isolation12h privacy rules9h4h2h RLS
Lighthouse 90+Did not hit718894
Git-based PR reviewNonePartialNativeNative
Export off-platformSchema CSVAPIsFull repoFull repo
Cost at 50 tenantsWorkload spikeTwo vendorsHostingHosting

Auth, roles, and tenancy

No-code auth is fast for “user owns row.” It buckles at “member of org A can read invoices, cannot refund, and inherits billing from org B after a merge.” I needed that merge path. Privacy rules multiplied. The boilerplate used a single org_id on every table and a policy. See Clerk’s org model if you want a hosted analog; I still wanted the policy in git.

Billing, webhooks, and jobs

Stripe will retry. Your app must be idempotent. Visual workflows that fire “on payment” twice created duplicate entitlements. I added a processed_events table in the boilerplate in 20 minutes. In Bubble I built a recursive search-and-lock pattern that I did not trust at 2 a.m. Read Stripe webhooks and ask whether your canvas can store an event id uniquely.

Performance, git, and export

web.dev vitals do not care that your editor is pretty. My Bubble public pages sat in the 60s on Lighthouse. The TanStack Start shell, with TanStack Router code-splitting, hit 94. Git is not a nice-to-have once two people touch billing. Export is the option you price when the vendor’s workload units jump. I could clone the boilerplate repo. I could not clone Bubble.

The 7 Ceiling Limits We Measured

Here are the seven limits, grouped the way they actually bit.

When no-code hits the auth and billing ceiling

Limit 1 — Custom authorization. Nested resources (invoice line items inside orgs inside a reseller) exceeded privacy-rule comfort. I shipped a “super admin” toggle as a workaround. That is a security smell.

Limit 2 — Metered billing. Usage records needed aggregation windows. Visual backend workflows became a directed graph I could not diff.

Limit 3 — Webhook reliability. Duplicate Stripe events. No unique index I fully controlled. I eventually posted from Bubble to a tiny worker, which is how you accidentally invent a boilerplate.

Acceptance for these three was: create org, invite member, record 1,000 usage events, fire the same webhook twice, see one entitlement. Boilerplate: one afternoon. No-code: three calendar days plus the 38-hour tail.

A typed procedure looks like this — this is the kind of check a canvas cannot diff in a pull request:

ts
// org-scoped usage commit — idempotent on stripeEventId
export async function commitUsage(input: {
  orgId: string
  stripeEventId: string
  units: number
}) {
  return db.transaction(async (tx) => {
    const dup = await tx.query.processedEvents.findFirst({
      where: (e, { eq }) => eq(e.id, input.stripeEventId),
    })
    if (dup) return { ok: true, duplicate: true }
    await tx.insert(usage).values({
      orgId: input.orgId,
      units: input.units,
    })
    await tx.insert(processedEvents).values({ id: input.stripeEventId })
    return { ok: true, duplicate: false }
  })
}

The performance ceiling no-code vendors soft-pedal

Limit 4 — Runtime performance. Under 50 concurrent tenants doing list-and-filter, Bubble p95 sat 640–890ms. The boilerplate, with TanStack Query and a keyed query, held 180ms p95 on the same DigitalOcean Postgres. INP on the no-code admin table was the first customer complaint. Vitals are documented at web.dev; vendors will not put your trace in their marketing.

Limit 5 — Data model change. Adding a unique (org_id, email) on members was a migration and a backfill in the repo. In Bubble it was a new constraint I could not enforce on historical junk rows without a one-off admin app. Product people call this “flexibility.” It is debt.

Export, lock-in, and team workflow

Limit 6 — Git and review. I cannot leave a billing change in a visual editor overnight without a diff. Two contractors on the no-code app overwrote a workflow. The boilerplate PR showed the Stripe idempotency line in review.

Limit 7 — Exit. CSV plus API export is not an application. When I priced a hypothetical move off Bubble, I estimated 4–6 weeks to reconstruct routes, auth, and jobs. The boilerplate exit cost is git clone. That option value belongs in the no code vs boilerplate spreadsheet, even if you never leave.

Row isolation that no-code privacy rules only approximate:

sql
-- every tenant table carries org_id; policies live in git
alter table invoices enable row level security;

create policy invoices_isolation on invoices
  using (org_id = current_setting('app.org_id')::uuid)
  with check (org_id = current_setting('app.org_id')::uuid);

That policy is from the same family as Supabase RLS. I could read it, test it, and revert it. I could not do that with a stack of visual privacy rules.

No Code vs Boilerplate for Multi-Tenant SaaS

If your SaaS is a single-user tool, skip this section. If a customer is an organization, this is the whole game.

Row-level rules you cannot click together

Reseller → org → project → resource is a four-level graph. Visual rules explode combinatorially. A boilerplate puts orgId on the session, on the query, and on the policy. TanStack Router loaders then refuse to render a project that failed the policy, instead of hiding buttons after the data already leaked into the payload.

ts
export const Route = createFileRoute('/app/$orgId/invoices')({
  beforeLoad: async ({ params, context }) => {
    if (context.auth.orgId !== params.orgId) {
      throw redirect({ to: '/app/forbidden' })
    }
  },
  loader: ({ params }) => queryClient.ensureQueryData(
    invoicesQuery(params.orgId),
  ),
})

Custom workflows Bubble cannot express

I needed “when usage crosses 80% of the included allotment, freeze non-admin seats, keep billing admins, and email the owner once.” In code that is a job with a unique constraint on (org_id, notice_type, period). In Bubble it was three workflows, a custom state, and a race. Xano helped. It still was not a unique index I owned.

What a typed boilerplate changes

Types do not make you faster at drawing a hero. They make illegal states unrepresentable. Role = 'owner' | 'admin' | 'member' plus a discriminated billing state killed a class of bugs that my no-code app still carries. That is why I now open TanStack Ship features instead of a blank Bubble application when the product has orgs.

How to Choose: No Code vs Boilerplate After the Ceiling

A decision, then ship. Not a personality test.

When no-code still wins

Use no-code when all of these are true: one user type, Stripe Checkout (not metered), no nested tenancy, you are the only builder, and you will throw the app away if the idea dies. Internal tools and concierge MVPs fit. Webflow is still my default for the marketing site even when the app is a boilerplate. Do not drag the app into the marketing tool.

When you should start on a boilerplate

Start on a boilerplate when any of these are true: organizations, roles, usage billing, a second engineer in the next 90 days, a Lighthouse budget, or a compliance questionnaire. The hour you “save” skipping git comes back as a rewrite. Compare kits on the compare page rather than collecting Twitter screenshots.

A 30-minute decision checklist

  1. Draw the permission graph. More than two node types → boilerplate.
  2. Read Stripe’s webhook delivery guarantees. If you flinch → boilerplate.
  3. Name the second person who will touch production. If they exist → git.
  4. Quote workload units at 10× current usage. If the number is a surprise → boilerplate.
  5. Time a Lighthouse run on a competitor. If they are in the 90s, your canvas admin will feel slow on day one.

If three or more boxes point at a kit, skip the two-week no-code detour. Look at pricing and start a tenant.

FAQ

Is no code vs boilerplate a false dichotomy for early SaaS?

Partly. Hybrid is real: Webflow for marketing, a typed app for the product, maybe Xano as a temporary API. The false part is pretending the app layer can stay visual once tenancy and money arrive. Hybrid that keeps billing in a canvas still hits limits 2 and 3.

Can I migrate off Bubble once I have revenue?

Yes, with a 4–6 week reconstruction in my estimate for a 12-page shell plus billing. Export will not give you routes, background jobs, or tests. Budget it as a second product, not an afternoon. If you already know you will migrate, you are paying twice for the privilege of a fast demo.

Does a TanStack boilerplate lock me in the way no-code does?

You are locked to TypeScript, Postgres, and whatever auth you chose — all portable. You are not locked to a runtime you cannot snapshot. TanStack Start and TanStack Query are libraries in your repo. That is a different risk class from a hosted editor.

What if I only need a prototype for user interviews?

Use no-code. Interview five users. If they ask for seats, roles, or usage, stop adding pages to the canvas and move. The ceiling problem is not that no-code is slow at prototypes. It is that teams keep going after the prototype has answered the question.

Ship past the ceiling

I did not write this to dunk on Bubble. I shipped real tenants on it. I wrote it because the no code vs boilerplate conversation still pretends the only metric is time-to-login. Time-to-login is a solved problem. Time-to-correct-money-and-permissions is not.

TanStack Ship is the boilerplate I wish I had cloned on day one of that 11-week experiment: orgs, auth, billing hooks, and a 90+ Lighthouse shell, without a vendor runtime. Independent review, no affiliate link, no material connection to anyone in the matrix except the product I maintain. If your graph has more than one user type, skip the canvas detour. Read the features, check pricing, and compare it against the kit you were about to paste from GitHub. Then ship.