Monorepo Best Practices
Overview
The Earna AI platform uses a monorepo structure to manage multiple related applications and services. This guide outlines best practices for working within our monorepo.
Repository Structure
earna-ai/
├── apps/ # Main customer-facing application
│ ├── package.json # App-specific dependencies
│ ├── vercel.json # Deployment configuration
│ └── components/ # App-specific components
├── console/ # Admin console application
│ ├── package.json
│ ├── vercel.json
│ └── app/ # Next.js app directory
├── docs-nextra/ # Documentation website
│ ├── package.json
│ ├── vercel.json
│ └── src/content/ # MDX documentation files
├── credit-engine/ # Credit processing service
│ ├── package.json
│ └── vercel.json
├── turbo.json # Turborepo configuration
├── pnpm-workspace.yaml # Workspace definitions
├── package.json # Root dependencies
└── pnpm-lock.yaml # Lockfile for all workspaces
Workspace Management
1. Dependency Management
Installing Dependencies
# Add to specific workspace
pnpm --filter console add react-query
# Add to root (shared tooling)
pnpm add -w -D eslint
# Add to all workspaces
pnpm add -r lodash
Dependency Rules
- Application dependencies: Install in specific workspace
- Dev tools: Install in root workspace with
-w -D
- Shared utilities: Consider creating a shared package
2. Cross-Workspace Dependencies
Internal Package References
{
"dependencies": {
"@earna/shared": "workspace:*",
"@earna/ui": "workspace:^"
}
}
Import Patterns
// Good: Absolute imports within workspace
import { Button } from '@/components/ui/button'
// Good: Cross-workspace imports
import { utils } from '@earna/shared'
// Bad: Relative imports across workspaces
import { Button } from '../../../console/components/button'
Development Workflow
1. Running Services
Individual Services
# Run specific app
pnpm --filter console dev
pnpm --filter apps dev
pnpm --filter docs-nextra dev
Multiple Services
# Run all development servers
pnpm turbo dev
# Run specific combination
pnpm turbo dev --filter=console --filter=apps
2. Building Projects
Build Order
Turborepo automatically handles build dependencies:
{
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
}
}
Build Commands
# Build everything
pnpm turbo build
# Build specific project and dependencies
pnpm turbo build --filter=console...
# Build without cache
pnpm turbo build --force
3. Testing Strategy
Unit Tests
# Test specific workspace
pnpm --filter console test
# Test all workspaces
pnpm turbo test
Integration Tests
# Run integration tests
pnpm turbo test:integration
# Run E2E tests
pnpm turbo test:e2e
Code Organization
1. Shared Code
When to Share
- Share: Utilities used by 2+ workspaces
- Share: Common UI components
- Share: Type definitions
- Don’t Share: Business logic specific to one app
Creating Shared Packages
# Create shared package
mkdir packages/shared
cd packages/shared
pnpm init
# Configure package.json
{
"name": "@earna/shared",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts"
}
2. Configuration Management
Shared Configurations
packages/
├── eslint-config/ # Shared ESLint rules
├── tsconfig/ # Shared TypeScript configs
└── tailwind-config/ # Shared Tailwind presets
Extending Configurations
// apps/tsconfig.json
{
"extends": "@earna/tsconfig/nextjs.json",
"compilerOptions": {
"baseUrl": "."
}
}
Version Control
1. Commit Conventions
Commit Message Format
type(scope): description
[optional body]
[optional footer]
Examples
feat(console): add user authentication
fix(apps): resolve D3 import issues
docs(infrastructure): add Turborepo migration guide
chore: update dependencies
2. Branch Strategy
Branch Naming
feature/[workspace]-description
fix/[workspace]-issue
docs/topic
chore/description
Examples
feature/console-user-management
fix/apps-d3-imports
docs/turborepo-migration
chore/update-dependencies
CI/CD Practices
1. Selective Builds
Turborepo Filtering
# Only build changed packages
pnpm turbo build --filter=[HEAD^1]
# Build affected by changes
pnpm turbo build --filter=...[origin/main]
2. Deployment Strategy
Independent Deployments
Each workspace deploys independently:
- Apps → app.earna.ai
- Console → console.earna.ai
- Docs → docs.earna.ai
Vercel Configuration
{
"buildCommand": "pnpm turbo build --filter=workspace-name",
"installCommand": "pnpm install",
"outputDirectory": ".next"
}
Performance Optimization
1. Build Caching
Local Caching
# Cache location
.turbo/
# Clear cache
rm -rf .turbo
pnpm turbo build --force
Remote Caching
{
"remoteCache": {
"signature": true
}
}
2. Dependency Optimization
Analyze Dependencies
# Check bundle size
pnpm --filter console analyze
# Find duplicate dependencies
pnpm dedupe
# Prune unnecessary packages
pnpm prune
Common Patterns
1. Environment Variables
Structure
.env.local # Local development (gitignored)
.env.development # Development defaults
.env.production # Production defaults
.env.example # Template for developers
Sharing Variables
// turbo.json
{
"globalEnv": ["NODE_ENV"],
"env": ["NEXT_PUBLIC_*", "DATABASE_URL"]
}
2. Scripts Organization
Root Scripts
{
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"lint": "turbo lint",
"clean": "turbo clean && rm -rf node_modules"
}
}
Workspace Scripts
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}
Troubleshooting
Common Issues
Issue: Dependency Conflicts
# Reset and reinstall
pnpm store prune
rm -rf node_modules pnpm-lock.yaml
pnpm install
Issue: Build Failures
# Clear all caches
rm -rf .turbo .next
pnpm turbo build --force
Issue: Type Errors Across Workspaces
# Rebuild TypeScript references
pnpm turbo typecheck --force
Best Practices Checklist
- Keep workspaces focused and independent
- Use Turborepo for all multi-workspace commands
- Commit pnpm-lock.yaml after dependency changes
- Follow naming conventions for branches and commits
- Test changes across affected workspaces
- Document workspace-specific setup requirements
- Use shared configurations to maintain consistency
- Regular dependency audits and updates
- Monitor and optimize build performance
- Keep documentation up-to-date
Resources
Last updated on