Philosophy

Opinionated defaults, escape hatches included

goilerplate is opinionated by design. We make strong choices so you can ship fast without decision fatigue.

Our Core Philosophy

“Sane defaults for the 99%, escape hatches for the 1%”

We pick proven, boring technology that works for most SaaS apps. When you outgrow our choices (you probably won’t), we document the migration path.

Boilerplate, Not a Framework

goilerplate is a starting point, not a platform:

  • One database (SQLite) - not an ORM supporting 10 databases
  • One payment provider (Polar) - not a payment abstraction layer
  • One approach (server-rendered) - not a meta-framework

This is code you copy, own, and modify. Not a package you install and configure.

Want Stripe? Replace Polar.
Need PostgreSQL? Swap SQLite.
Want i18n? Add it yourself.

We won’t add support for every tool. We picked one that works. The rest is your code to change.

The Decisions We Made For You

  • SQLite over PostgreSQL - Because you don’t have 10,000 concurrent users
  • Polar.sh as default. Open source, developer first, and Merchant of Record. Stripe Managed Payments is also supported.
  • templ - Type-safe Go templates, no separate JS framework needed
  • HTMX - Progressive enhancement where it makes sense
  • Tailwind over custom CSS - Because utility-first works
  • Vanilla JS - Simple DOM manipulation, no framework overhead
  • Server-side rendering - Full page reloads are fast and simple
  • Freemium/Subscription model - Because it’s the proven SaaS growth path

Each choice optimizes for shipping speed over theoretical scale.

Why SQLite?

SQLite is not a “development” database - it’s production-ready for 99% of SaaS apps.

With WAL mode enabled, SQLite handles:

  • 100,000+ page views per day on a single server
  • 10,000 writes/second on modern SSDs (that’s 864 million writes/day)
  • Unlimited concurrent readers without blocking
  • Up to 281 TB database size

Real examples:

  • Basecamp (37signals) runs on SQLite
  • Expensify processes 10TB+ of SQLite data
  • fly.io uses SQLite for edge apps

We use modernc.org/sqlite (pure Go, no CGO) which means:

  • Cross-compilation works
  • Static binaries
  • No C dependencies

When to switch to Postgres: Multi-server writes, PostGIS, or compliance requirements. Otherwise, stick with SQLite.

Discord stored 120 million messages in SQLite before switching. You’re not Discord yet.

Why Polar?

Polar.sh is our default payment provider because it’s open source, developer-first, and gets you to launch faster than anything else. It also acts as Merchant of Record, so sales tax, VAT, and GST are handled for you.

Polar acts as Merchant of Record:

Customer → Pays Polar → Polar pays taxes → You get clean revenue

What MoR saves you:

  • $500-2000/month in tax compliance software
  • $1000-5000/month in accountant fees
  • 20-40 hours/month of your time
  • Unlimited liability risk from filing mistakes

For indie makers and early-stage SaaS, that alone is worth Polar’s 4-5% fee.

What about Stripe?

Stripe used to mean “you’re on your own for taxes.” That changed. Stripe Managed Payments is now generally available, and goilerplate enables it automatically when you select the Stripe provider. Stripe becomes the merchant of record and handles sales tax, VAT, and GST in 80+ countries, the same compliance benefit as Polar.

So the choice is no longer “compliance vs. no compliance.” It’s:

  • Polar. Default, open source, simpler dashboard, lower fees, developer first.
  • Stripe. Pick it if your team already lives in the Stripe ecosystem or needs deeper billing primitives.

Both are first-class. Switching is one ENV variable:

PAYMENT_PROVIDER=stripe  # We support both

Note: Stripe Managed Payments only supports digital products (SaaS, software, courses, e-books, electronically delivered services). For a typical SaaS, that’s not a constraint.

Why Freemium/Subscription?

We built subscription and free tier support because freemium is the proven SaaS growth model:

  • Larger user base (viral growth)
  • Users try before they buy
  • Lower barrier to entry
  • Recurring revenue when they convert

Not every boilerplate ships with this. We do because most successful SaaS apps use it.

Need a different model? You can adapt the code for paid-only, free trial, or one-time payments. But 99.9% of apps won’t need to - freemium works.

Why Resend?

We chose Resend for email because it’s built for developers:

  • Generous free tier: 3,000 emails/month, 100 daily sends
  • Simple API: Clean, modern REST API with excellent docs
  • Built-in compliance: GDPR/CAN-SPAM handling included
  • Audience management: Newsletter support without extra tools
  • Analytics: Track opens, clicks, bounces out of the box

Alternative email providers? Resend is just an HTTP API call. Swap it out in internal/service/email.go if you need SendGrid, Mailgun, or AWS SES.

Idiomatic Development: Use What You Need, Nothing More

We built this boilerplate with a simple principle: use each technology only where it genuinely adds value.

The Backend Is Your State

Forget client-side state management. The database is your source of truth:

  • No state juggling - The backend already knows the state
  • No complex data syncing - One source of truth: your database
  • No state conflicts - Server renders the current truth, period

When you need interactivity, we use vanilla JavaScript. A dropdown toggle doesn’t need a framework. Keep it simple.

HTMX Where It Makes Sense

We don’t sprinkle HTMX everywhere like fairy dust:

  • Full page reloads are fine - Users understand them, browsers cache them
  • HTMX for specific UX wins - Inline editing, live search, lazy loading
  • Not every click needs to be fancy - Navigation? Just navigate

This isn’t about being anti-modern. It’s about being pro-simple. Every line of JavaScript is a liability. Every HTMX attribute is complexity. Use them when they genuinely improve the user experience, not because you can.

Boring Technology Is Scalable Technology

The “boring” stack scales better than the clever one:

  • Stateless requests scale horizontally without thinking
  • Database-driven state means no session clustering headaches
  • Server-rendered HTML caches at every level (CDN, browser, proxy)
  • Simple JavaScript doesn’t break when frameworks update

We’ve seen too many projects die under the weight of their own cleverness. Microservices that should’ve been functions. Over-engineered frontends that should’ve been simple server-rendered pages. Complex state management that should’ve been database queries.

The Right Tool Philosophy

Each technology in our stack has a specific job:

  • Go - Fast, concurrent backend logic
  • templ - Type-safe HTML generation
  • SQLite - Reliable data storage
  • HTMX - Selective dynamic behavior (not everywhere!)
  • Vanilla JS - Simple DOM manipulation when needed
  • Tailwind - Consistent styling without CSS fights

Notice what’s missing? No state management library. No client-side router. No JavaScript build pipeline. No Node.js dependencies. We use standalone binaries and the platform.

Standard Library First (Backend Too)

This philosophy extends to our backend:

  • net/http Router - Not Chi, Gin, or Echo. The stdlib router does everything we need.
  • 7-line middleware chain - Not Alice or third-party packages. Just a simple for loop.
  • Minimal dependencies - Only add packages when stdlib genuinely can’t do the job.

Most Go web frameworks add complexity you don’t need. The standard library is boring, well-documented, and won’t break when a maintainer loses interest. When you need advanced routing (you probably won’t), switching to Chi takes an hour.

The Bottom Line

We made the decisions so you don’t have to.

git clone && task dev  # You're running. Start building.

Battle-tested, not training wheels:

  • SQLite powers 37signals production apps
  • Polar handles $1M+ ARR businesses
  • HTMX runs enterprise applications

Perfect for: Indie makers, side projects, agencies, startups
Wrong if: Enterprise mandates, Netflix-scale on day one (you’re not)

The deal: You accept our choices (initially) and pragmatism over purity. In return: ship faster, avoid decision fatigue, focus on your business.

Escape hatches exist if you need them. But 99% won’t.


Your users don’t care about your stack. They care if your app solves their problem.

Stop debating. Start shipping.