Design Choices
Key design decisions and rationale behind the tech stack
Design Choices
Project Generation with better-t-stack.dev
We used better-t-stack.dev to generate the initial project skeleton. This tool was instrumental in bootstrapping our application because:
- Pick and Choose: Allows selective inclusion of TypeScript-based components and libraries
- Scaffolded Structure: Generates a well-organized monorepo structure with best practices
- Modern Stack: Provides opinionated but flexible choices for modern web development
- Time Saving: Eliminates hours of manual configuration and setup
- Type Safety First: All generated code is fully typed with TypeScript
The generator let us quickly assemble our preferred tech stack (Next.js, tRPC, Drizzle, Better Auth, etc.) without the overhead of manual project setup.
Why Better T Stack?
We chose to build on the Better T Stack instead of the traditional T3 Stack for several compelling reasons:
Better Auth vs. NextAuth/Auth.js
Better Auth offers significant advantages over NextAuth:
- Type Safety: Full TypeScript support with better type inference
- Flexibility: More control over authentication flows and customization
- Modern Architecture: Built from the ground up for modern web standards
- Better DX: Cleaner API and easier to understand configuration
- Framework Agnostic: Not tied specifically to Next.js, making it more portable
Drizzle ORM vs. Prisma
Drizzle ORM was chosen for database management:
- SQL-first: Closer to raw SQL, giving developers more control
- Lightweight: Smaller bundle size and faster runtime performance
- Type Safety: Excellent TypeScript support with inferred types
- No Code Generation Step: Types are generated directly from schema
- Better Performance: More efficient queries and less overhead
tRPC for API Layer
tRPC provides end-to-end type safety:
- Full-stack Type Safety: Share types between client and server
- No Code Generation: Types are inferred automatically
- Developer Experience: Autocomplete and type checking everywhere
- Performance: Minimal overhead compared to REST or GraphQL
- Monorepo Friendly: Works seamlessly in turborepo setups
Database Infrastructure
PostgreSQL
PostgreSQL was selected as our database (also picked via the Better T Stack generator):
- Industry Standard: The go-to choice for production-grade applications
- ACID Compliant: Reliable transactions and data integrity
- Rich Feature Set: Advanced indexing, full-text search, JSON support
- Mature Ecosystem: Extensive tooling and community support
- Scalability: Handles high loads and large datasets efficiently
- Open Source: No licensing costs with enterprise-grade features
Docker & Docker Compose
We use Docker and Docker Compose for local PostgreSQL development:
- Consistency: Same environment across all developers' machines
- Isolation: Database runs in a container, no local installation needed
- Easy Setup: One command to start/stop the database
- Version Control: Docker Compose config in the repo ensures reproducibility
- CI/CD Ready: Same container setup can be used in testing pipelines
- Multiple Instances: Easy to run different database versions or configurations
This setup allows developers to run bun run db:start to spin up a PostgreSQL instance instantly.
Tooling Choices
Bun as Package Manager
- Speed: Significantly faster than npm, yarn, or pnpm
- Built-in Tools: Includes test runner, bundler, and more
- Drop-in Replacement: Compatible with Node.js ecosystem
Turborepo for Monorepo Management
- Intelligent Caching: Faster builds with remote caching
- Parallel Execution: Run tasks across packages simultaneously
- Simple Configuration: Minimal setup compared to alternatives
Biome for Code Quality
Biome replaces both ESLint and Prettier:
- Unified Tool: Single tool for linting and formatting
- Performance: Written in Rust, much faster than ESLint/Prettier
- Zero Config: Sensible defaults out of the box
- Better Errors: More actionable error messages