5 Stack Decisions I Almost Got Wrong on Wintura.ai (And What I'd Do Differently)
First-person retrospective from shipping a production B2B SaaS solo: ORM vs raw SQL, Tailwind config vs CSS-first, NextAuth v5 beta vs Clerk, Cloudflare R2 vs S3, JS test runner choice.
TL;DR. Five stack decisions on wintura.ai that I either got right by luck or would re-decide today: (1) skipped Prisma for raw SQL via
/lib/db— right call, would do again. (2) Tailwind v4 CSS-first @theme tokens over JS config — right call, narrowly. (3) NextAuth v5 beta over Clerk — defensible, but if I started today I'd reconsider. (4) Cloudflare R2 over S3 — clearly right for egress economics. (5) Playwright over Vitest+React Testing Library for e2e — right, but I would have added a unit-test layer earlier. The lessons map directly to what Soatech locks in the Technical Blueprint — the same decisions made in hours by an Architect instead of discovered in months by trial-and-error.
Why these specific 5 matter
Per Lily Ray's Q2 2026 "authentic SEO" guidance, the content that ranks in 2026 is content with unique first-person specificity — exactly what I can write about Wintura that no other blog can. Generic stack-decision posts (the kind Google's quality classifier is rejecting per Andres Plashal's "crawled not indexed" diagnosis) say "consider scalability." Specific posts say "I chose Neon over RDS for these three reasons, and here's the per-second cost difference at our usage."
These 5 are the specific decisions where I had to weigh real trade-offs on Wintura and where founders building B2B SaaS in 2026 will face the same fork.
Decision 1: Skipped Prisma for raw SQL via /lib/db — RIGHT CALL
The choice
Most Next.js + Postgres tutorials default to Prisma (or Drizzle, or Kysely) as the ORM layer. I considered them seriously. Wintura ships with raw SQL queries in /lib/db/*.ts files, type-checked against Zod schemas at the call site.
Why I went raw
Three reasons stacked:
- Bundle size: Prisma's generated client adds ~2-4MB to the runtime bundle. Raw
@neondatabase/serverlessdriver is ~80KB. For an edge-deployed Next.js app where every kilobyte matters, the bundle difference compounds. - Query control: complex queries (multi-table joins with tenant filtering, row-level pagination with cursor) are easier to write directly in SQL than in an ORM's query-builder DSL. I wrote roughly 60 query functions in Wintura — easier to author and easier to debug than the equivalent ORM-generated SQL would have been.
- Schema evolution: raw SQL migrations in
/migrations/*.sqlare a known-quantity. Prisma's migration tool is fine but adds a layer of indirection when something goes wrong.
What I'd reconsider
For a multi-engineer team where junior developers need ORM-level guard-rails, Prisma is the right answer — it prevents an entire class of typos. For solo or small-team B2B SaaS where the architect-level engineer is writing all the queries, raw SQL wins on bundle + control.
Per the Wintura.ai case study multi-tenancy section, raw SQL + Row-Level Security + required tenantId parameter at the function signature was the architectural pattern that worked. ORM would have made the same pattern possible but added bundle weight without enforcement upgrade.
Decision 2: Tailwind v4 CSS-first @theme tokens — RIGHT CALL (narrowly)
The choice
Tailwind v4 ships a CSS-first configuration model: design tokens go in a @theme block in globals.css, not in a JavaScript tailwind.config.js file. I switched Wintura to this pattern.
Why I went CSS-first
- No build-time JS evaluation: Tailwind v3's JS config required Node.js to evaluate, which added ~200ms to builds. v4 CSS-first reads tokens directly.
- Single source of truth:
globals.css@themeblock +design-tokens.json(the brand reference document) live in the same conceptual layer. JS config introduced a third layer. - CSS Custom Properties: tokens compile to
--token-namevariables natively, making them usable in inline styles, third-party widgets, and runtime theme switching.
What I'd reconsider
Tailwind v4 is still maturing — some shadcn/ui components needed minor patches for v4 compatibility (notably tw-animate-css replacing tailwindcss-animate per the Wintura.ai case study brand section). If your project has heavy reliance on third-party Tailwind plugins that haven't migrated to v4 yet, v3 is the safer 2026 choice.
For greenfield projects launching in 2026, v4 is the right answer narrowly. For 2025-vintage projects in active development, the migration cost is real — consider the Soatech Production Lift if you want it handled.
Decision 3: NextAuth v5 beta over Clerk — DEFENSIBLE BUT WOULD RECONSIDER
The choice
NextAuth v5 beta.30 vs Clerk vs Supabase Auth. I went with NextAuth v5 beta.
Why I went NextAuth
- Cost: NextAuth is free + self-hosted. Clerk's Pro plan is €25/month + per-MAU pricing that scales fast (€0.02/MAU after 10K active users).
- Data residency: NextAuth sessions live in your own database. Clerk hosts them. For EU/GDPR-aligned positioning, self-hosted matters.
- Customization: NextAuth's callback model gives you total control over session shape, JWT contents, tenant resolution. Clerk's customization is good but you're working within their abstractions.
What I'd reconsider
NextAuth v5 has been in beta for the entire Wintura build. Major version bumps occasionally broke production. Clerk would have shipped faster — auth UI is essentially zero-effort with Clerk's drop-in components vs the ~3 weeks I spent on Wintura's custom sign-in flow.
If you're optimizing for shipping speed and €25-€100/month is acceptable, Clerk is the right answer in 2026. If you're optimizing for cost + control + EU data residency, NextAuth v5 (now nearing stable) is still the right answer. For Soatech client engagements, the Blueprint phase makes this decision based on the client's specific constraints — neither is universally correct.
Decision 4: Cloudflare R2 over S3 — CLEARLY RIGHT
The choice
For Wintura's sealed-PDF document storage, I needed S3-compatible object storage. The options: AWS S3, Cloudflare R2, Backblaze B2, Vercel Blob.
Why I went R2
- Egress economics: S3 charges ~€0.09/GB egress; R2 charges €0 egress to authenticated clients via signed URLs. For a document-heavy product where every proposal PDF gets downloaded by the client, the math favored R2 by ~10×.
- S3-compatible API: same
@aws-sdk/client-s3SDK works against R2 by changing the endpoint. Zero learning curve. - EU data residency: R2 supports EU-only buckets. Good for GDPR-aligned positioning.
What I'd reconsider
Nothing. R2 has been entirely solid. If your usage pattern has very low egress (most documents stored, rarely downloaded), S3's lower storage cost at scale (€0.023/GB vs R2's €0.015/GB) could win — but the egress savings dominate for nearly every real-world SaaS usage pattern.
Per Cloudflare's R2 pricing page, the no-egress-fee model is consistent in 2026.
Decision 5: Playwright over Vitest+React Testing Library — RIGHT, BUT INCOMPLETE
The choice
For Wintura's test suite, I went all-in on Playwright e2e tests. 24 spec files, per the Wintura.ai case study test section. No unit-test layer.
Why I went all-Playwright
- E2E catches real bugs: unit tests on a multi-tenant SaaS often pass with mocked tenants while production fails with real tenants. Playwright tests against a real Postgres database + real auth flow catch the integration-layer bugs that matter.
- Solo-founder pragmatism: maintaining two test layers (unit + e2e) doubles the test-maintenance cost. For a 1-person team, e2e-only was the lower-overhead choice.
- a11y matrix integration: axe-core 4.11.3 integrates into Playwright cleanly. Unit-test a11y is harder.
What I'd reconsider
I would have added a unit-test layer for pure-function code (validation, transformers, business logic). Not for components — for the /lib/* files. Unit tests run in ~1 second vs Playwright's ~30 seconds per file. The feedback loop on logic changes would have been faster.
For Soatech engagements, the default is Playwright matrix (per Wintura) for everything except pure-function libraries, where Vitest gets added. Hybrid wins.
What these decisions have in common
All five share the same shape: the right answer depended on the specific context (solo vs team, cost-sensitive vs speed-sensitive, EU-aligned vs US, etc.). Generic blog posts saying "use Prisma" or "use Clerk" miss this entirely.
The lesson for founders: stack decisions are not "what's the best X?" They're "what fits this product, this team, this budget, this jurisdiction?" That's exactly what the Soatech Technical Blueprint (€2,500 / 5 days) produces — written rationale for each stack decision based on your specific constraints, not blanket recommendations.
Per the how-we-build-mvps-at-soatech playbook, the Blueprint phase locks these decisions on Day 1-5 so the remaining Sprint days are pure execution. Wintura took 6 months partly because I discovered these decisions as I went. A paying Soatech client gets them locked upfront.
Frequently asked questions
Should I use Prisma or raw SQL for a new SaaS in 2026?
Depends on team size. Solo or small architect-led team: raw SQL via something like /lib/db wins on bundle size + control. Multi-engineer team with junior developers: Prisma or Drizzle wins on guard-rails. For Wintura (solo), raw SQL was right. For Soatech client engagements, the choice is made in the Technical Blueprint based on the team they're building for.
Is Tailwind v4 production-ready in 2026?
Yes, but with caveats. The CSS-first @theme pattern is stable. Some Tailwind v3 plugins haven't migrated (notably tailwindcss-animate — Wintura switched to tw-animate-css). For greenfield 2026 projects, v4 is the right default. For 2025-vintage projects mid-flight, the migration cost is real.
Should I pick Clerk over NextAuth v5 for auth?
If you value shipping speed and €25+/month is acceptable, Clerk. If you value cost + self-hosted data residency + total customization control, NextAuth v5 (now near-stable). For EU/GDPR positioning, NextAuth tends to win. Per my Wintura experience, I went NextAuth but would consider Clerk if I were starting today.
Is Cloudflare R2 actually cheaper than S3?
Yes, dramatically — but only if your workload has meaningful egress. R2 has zero egress fees to clients via signed URLs; S3 charges ~€0.09/GB. For document-heavy SaaS like Wintura (every PDF downloaded), R2 is ~10× cheaper. For archive workloads with rare access, S3's storage cost (€0.023/GB vs R2's €0.015/GB) can win.
Do I need both unit tests and e2e tests for a SaaS MVP?
Depends on what's in your codebase. Pure-function libraries (validators, transformers, business logic) benefit from unit tests — fast feedback. UI components and integration paths benefit from e2e tests — catch real bugs. Wintura started e2e-only; I'd recommend hybrid for any team larger than solo.
How does Soatech avoid making these "I'd reconsider" decisions in client work?
The Technical Blueprint phase. Five days of architecture sprint locking stack decisions against the client's specific constraints (team size, budget, data residency, time-to-market, performance budget). Same questions I had to answer for Wintura — answered upfront for client work, not discovered mid-Sprint.
Where can I see how these decisions played out in production?
Wintura.ai is the live product. The Wintura.ai case study documents the full architectural picture — every dependency version, every architectural rule, every test pattern. Use the live demo, see the public proposal samples, then read the case study for the depth.
What stack decisions should every founder lock before writing code?
Six: (1) database + ORM-or-not, (2) auth library + multi-tenant pattern, (3) deployment platform + CI/CD, (4) third-party integration map (Stripe, email, CRM, storage), (5) test strategy (unit vs e2e split + a11y coverage), (6) state management + form validation (Zod vs Yup vs Valibot). All six fit in a 5-day Technical Blueprint.
Is there a Soatech engagement that's specifically for stack-decision advisory?
The Blueprint is exactly this. €2,500 / 5 days. Walk away with the architecture document regardless of whether you proceed to a Sprint. If you do proceed, the fee converts toward Sprint One. See the how-we-build-mvps-at-soatech playbook for what's in the deliverable.
Related Articles
Why I Picked Next.js 16 for Wintura.ai — with Verified Q2 2026 Alternatives Considered
First-person stack rationale for shipping Wintura solo: Next.js 16.1.6 + React 19.2.3 vs Remix, SvelteKit, plain Vite + React. Real trade-offs, not framework cheerleading.
What an MVP Actually Cost Me to Ship Wintura.ai in 2026 — Verified Breakdown
Real numbers from shipping a production B2B SaaS solo: €5,800 one-time build cost + €218/month run cost using Claude Sonnet 4.6, Neon Postgres, Vercel, Stripe, Cloudflare R2.
I Built Wintura.ai with Claude as My Pair-Programmer — Here's What AI Can and Can't Do in 2026
First-person breakdown of where Claude Sonnet 4.6 + Haiku 4.5 worked and where they failed across 6 months of shipping a production B2B SaaS solo. Real examples, not benchmarks.
Ready to build something great?
Architect-led, AI-accelerated. Let's turn your idea into a shipped product.