Design Decisions
Key architectural and engineering decisions with rationale
📋 Overview
This document records important decisions made during the development of Izri. Each decision includes context, alternatives considered, and reasoning. This helps current and future team members understand why things are the way they are.
🎯 Decision Framework
When making significant decisions, we consider:
- Technical Merit: Does it solve the problem well?
- Developer Experience: Is it enjoyable to work with?
- Performance: Does it meet our performance goals?
- Maintainability: Can we maintain it long-term?
- Community: Is there good community support?
- Cost: Is it sustainable financially?
🏗️ Architecture Decisions
React Router v7 Over Next.js
Decision: Use React Router v7 for the frontend framework
Context: Needed a modern React framework with SSR capabilities
Alternatives Considered:
- Next.js 14/15
- Remix (now merged with React Router)
- Create React App
- Astro
Reasoning:
Pros of React Router v7:
- ✅ Familiar routing API (used by millions)
- ✅ No vendor lock-in (unlike Vercel/Next.js)
- ✅ File-based routing with excellent DX
- ✅ SSR ready without complexity
- ✅ Smaller bundle sizes
- ✅ More flexible deployment options
- ✅ Evolution of Remix (battle-tested patterns)
Cons of Next.js:
- ❌ Vercel-optimized (lock-in concerns)
- ❌ Heavier bundle
- ❌ App Router complexity
- ❌ More opinionated
- ❌ Harder to deploy outside Vercel
Outcome: React Router v7 gives us the benefits of a modern framework without the vendor lock-in
Status: ✅ Implemented and working well
tRPC Over REST and GraphQL
Decision: Use tRPC for API communication
Context: Needed type-safe API layer between frontend and backend
Alternatives Considered:
- Traditional REST APIs
- GraphQL with Apollo
- gRPC with protobuf
Reasoning:
Pros of tRPC:
- ✅ End-to-end type safety (no codegen)
- ✅ Automatic type inference
- ✅ Minimal boilerplate
- ✅ Built-in validation (Zod)
- ✅ Excellent DX (autocomplete everywhere)
- ✅ No schema files to maintain
- ✅ WebSocket support
Cons of Alternatives:
- REST: No type safety, manual validation, lots of boilerplate
- GraphQL: Code generation overhead, schema-first approach, complexity
- gRPC: Protocol buffers, not web-friendly, overkill
Trade-offs:
- ⚠️ Requires TypeScript monorepo (acceptable for us)
- ⚠️ Less suitable for public APIs (we'll add REST later if needed)
Outcome: Perfect fit for our monorepo architecture
Status: ✅ Implemented, extremely productive
Drizzle ORM Over Prisma
Decision: Use Drizzle ORM for database access
Context: Needed a TypeScript ORM for PostgreSQL
Alternatives Considered:
- Prisma
- TypeORM
- Kysely
- Raw SQL with pg
Reasoning:
Pros of Drizzle:
- ✅ Zero runtime overhead
- ✅ SQL-like API (familiar)
- ✅ Full TypeScript inference
- ✅ No code generation step
- ✅ Edge runtime compatible
- ✅ Drizzle Studio (visual DB tool)
- ✅ Simple migrations
Cons of Prisma:
- ❌ 4MB+ runtime overhead
- ❌ Custom DSL (less familiar)
- ❌ Code generation required
- ❌ Not edge-compatible
- ❌ Slower query performance
Performance:
Drizzle: ~1ms query time
Prisma: ~4ms query time (includes runtime overhead)
Outcome: Drizzle is faster, lighter, and more TypeScript-native
Status: ✅ Implemented, excellent DX
Hono Over Express/Fastify
Decision: Use Hono for the HTTP server
Context: Needed a fast, modern web framework
Alternatives Considered:
- Express.js
- Fastify
- Koa
- Elysia
Reasoning:
Pros of Hono:
- ✅ Fastest Node.js framework (benchmarks)
- ✅ Express-like API (familiar)
- ✅ First-class TypeScript support
- ✅ Edge runtime compatible
- ✅ Tiny bundle (~10KB)
- ✅ Rich middleware ecosystem
Cons of Alternatives:
- Express: Slow, old architecture, poor TypeScript support
- Fastify: Good but more complex, heavier
- Koa: Less active, smaller ecosystem
Benchmarks (requests/sec):
Hono: ~50,000
Fastify: ~45,000
Express: ~15,000
Outcome: Hono provides best performance with excellent DX
Status: ✅ Implemented
Monorepo with pnpm + Turbo
Decision: Use pnpm workspaces with Turbo for monorepo management
Context: Needed to share code between frontend, backend, and packages
Alternatives Considered:
- npm workspaces + Nx
- Yarn workspaces + Lerna
- Separate repositories
Reasoning:
Pros of pnpm + Turbo:
- ✅ Fastest package manager (3x faster than npm)
- ✅ Efficient disk usage (content-addressable storage)
- ✅ Strict dependency resolution
- ✅ Turbo's intelligent caching
- ✅ Simple configuration
- ✅ Great DX
Performance:
pnpm install: ~15s
npm install: ~45s
yarn install: ~30s
pnpm build (cached): <5s
pnpm build (clean): ~20s
Outcome: Significant productivity gains from fast installs and builds
Status: ✅ Implemented
🔐 Authentication Decisions
Lucia Over NextAuth
Decision: Use Lucia for authentication
Context: Needed flexible authentication solution
Alternatives Considered:
- NextAuth.js / Auth.js
- Passport.js
- Custom JWT solution
- Supabase Auth
Reasoning:
Pros of Lucia:
- ✅ Framework-agnostic
- ✅ Full control over database schema
- ✅ TypeScript-first
- ✅ No magic, explicit code
- ✅ Flexible session management
- ✅ Multiple providers supported
Cons of NextAuth:
- ❌ Next.js specific
- ❌ Opinionated database schema
- ❌ Hidden complexity
- ❌ Harder to customize
Outcome: Lucia gives us flexibility without framework lock-in
Status: ✅ Implemented (Better Auth being explored as alternative)
💾 Database Decisions
PostgreSQL Over MySQL/MongoDB
Decision: Use PostgreSQL 15 as primary database
Context: Needed reliable, feature-rich database
Alternatives Considered:
- MySQL
- MongoDB
- SQLite (for simplicity)
Reasoning:
Pros of PostgreSQL:
- ✅ ACID compliance
- ✅ JSONB for flexible schemas
- ✅ Advanced indexing (GiST, GIN)
- ✅ Full-text search
- ✅ Rich extension ecosystem
- ✅ Excellent performance
- ✅ Strong community
Use Cases:
- Relational data (users, projects, test runs)
- JSONB for flexible data (test results, analysis)
- Full-text search (future feature)
Outcome: PostgreSQL provides everything we need
Status: ✅ Implemented
Redis for Caching and Sessions
Decision: Use Redis for caching and session storage
Context: Needed fast session storage and caching layer
Alternatives Considered:
- In-memory sessions (not scalable)
- Database-backed sessions (slower)
- Memcached
Reasoning:
Pros of Redis:
- ✅ Sub-millisecond latency
- ✅ Rich data structures
- ✅ Pub/sub support
- ✅ Persistent storage option
- ✅ Battle-tested at scale
Use Cases:
- Session storage (Lucia)
- API response caching
- Rate limiting (planned)
- Real-time features (planned)
Outcome: Redis is industry standard for these use cases
Status: ✅ Implemented
🤖 AI Service Decisions
Python FastAPI for AI Service
Decision: Use Python with FastAPI for AI/ML service
Context: Needed to integrate AI capabilities for code analysis
Alternatives Considered:
- Node.js with TensorFlow.js
- Go with ML libraries
- Keep everything TypeScript
Reasoning:
Pros of Python:
- ✅ Best AI/ML ecosystem (PyTorch, LangChain, etc.)
- ✅ Rich code analysis tools (tree-sitter, AST parsers)
- ✅ Mature scientific computing stack
- ✅ FastAPI provides excellent performance
- ✅ Async/await support
Why FastAPI:
- ✅ Fast (comparable to Node.js)
- ✅ Automatic OpenAPI docs
- ✅ Type hints with Pydantic
- ✅ Modern Python async
- ✅ Easy to deploy
Communication:
- HTTP REST between TypeScript API and Python service
- Async job-based processing
- Clear service boundaries
Outcome: Python is the right tool for AI, FastAPI makes it web-friendly
Status: ✅ Implemented
🎨 Frontend Decisions
Tailwind CSS Over CSS-in-JS
Decision: Use Tailwind CSS for styling
Context: Needed styling approach for rapid UI development
Alternatives Considered:
- Styled Components
- Emotion
- CSS Modules
- Vanilla CSS
Reasoning:
Pros of Tailwind:
- ✅ Rapid development
- ✅ Consistent design system
- ✅ No naming decisions
- ✅ Purged CSS (small bundle)
- ✅ Excellent tooling
- ✅ Great IntelliSense
Cons of CSS-in-JS:
- ❌ Runtime overhead
- ❌ Larger bundle sizes
- ❌ Hydration issues
- ❌ Complex build setup
Outcome: Tailwind provides best DX and performance
Status: ✅ Implemented
shadcn/ui Over Component Libraries
Decision: Use shadcn/ui (copy-paste components)
Context: Needed accessible, customizable UI components
Alternatives Considered:
- Material UI
- Chakra UI
- Radix UI (raw)
- Build from scratch
Reasoning:
Pros of shadcn/ui:
- ✅ You own the code (no package dependency)
- ✅ Fully customizable
- ✅ Accessible by default (Radix UI)
- ✅ Tailwind-first
- ✅ Modern, beautiful design
- ✅ No bundle bloat
Approach:
# Copy component into your project
npx shadcn@latest add button
# Now it's your code, modify as needed
Outcome: Best of both worlds - pre-built + fully customizable
Status: ✅ Implemented
🚀 Deployment Decisions
Docker for Development and Production
Decision: Use Docker Compose for local dev, containers for production
Context: Needed consistent environments across machines
Alternatives Considered:
- Local PostgreSQL/Redis installation
- Vagrant
- Development-only (skip Docker)
Reasoning:
Pros of Docker:
- ✅ Consistent across machines
- ✅ Easy onboarding (one command)
- ✅ Production parity
- ✅ Service isolation
- ✅ Easy cleanup
Docker Compose for Dev:
services:
postgres:
image: postgres:15-alpine
redis:
image: redis:7-alpine
Outcome: Docker simplifies development significantly
Status: ✅ Implemented
🔧 Development Tool Decisions
Biome for Formatting and Linting
Decision: Transitioning from ESLint+Prettier to Biome
Context: Needed fast formatting and linting
Reasoning:
Pros of Biome:
- ✅ 10x faster than ESLint+Prettier
- ✅ Single tool (replaces two)
- ✅ Written in Rust
- ✅ Compatible with ESLint/Prettier configs
- ✅ Zero config out of the box
Performance:
Biome: ~0.5s
ESLint + Prettier: ~5s
Status: 🚧 In progress (ESLint still used for some rules)
📊 Privacy Principles
Metadata-First Analysis
Decision: Analyze repository metadata, not code contents
Context: Users concerned about code privacy
Principles:
- Metadata First: Collect structure, sizes, languages, frameworks
- Skip Binary Files: No images, videos, compiled code
- Ignore Build Dirs: Skip
node_modules,dist,.git, etc. - Optional Hashing: Size-capped (1MB), can be disabled
- Shallow Clone:
--depth=1by default - User Control: Users can configure what's analyzed
What We Collect:
- ✅ File paths and sizes
- ✅ File extensions
- ✅ Language detection
- ✅ Framework detection
- ✅ Dependency file contents (package.json)
What We Don't Collect:
- ❌ Source code contents (unless explicitly requested)
- ❌ Commit history
- ❌ Author information
- ❌ Large files (>1MB)
Outcome: Privacy-first approach builds trust
Status: ✅ Implemented
🔄 Decision Review Process
When to Document Decisions
Document a decision when:
- ✅ It affects architecture significantly
- ✅ There were multiple viable alternatives
- ✅ It might be questioned later ("Why did we...?")
- ✅ It has performance/cost implications
- ✅ It sets a pattern for future work
Decision Template
### [Decision Title]
**Decision**: [What was decided]
**Context**: [Why we needed to decide]
**Alternatives Considered**:
- Option A
- Option B
- Option C
**Reasoning**:
- Pros
- Cons
- Trade-offs
**Outcome**: [Result of decision]
**Status**: ✅ Implemented / 🚧 In Progress / ❌ Deprecated
🔗 Related Documentation
- System Overview: High-level architecture
- Technology Stack: Technologies in detail
- Monorepo Structure: Code organization
📝 Historical Decisions Log
This document contains the complete history of architectural and engineering decisions made during development.
Standing Rule: Always record significant product/engineering decisions when they are made or implemented.
Proposing a change to these decisions? Open an issue or PR with rationale and discuss with the team.