Scaler une startup tech : De 10 à 100 utilisateurs sans exploser
Guide pratique pour scaler infrastructure, équipe et processus de 10 à 100 utilisateurs. Architecture, monitoring, dette technique, budget. Évitez les pièges.
Scaler une startup tech : De 10 à 100 utilisateurs sans exploser
80% des startups crashent entre 50 et 200 utilisateurs. Cause : infrastructure non préparée. Voici le plan étape par étape pour scaler de 10 à 100 users sans tout casser.
Les 3 seuils critiques
Seuil 1 : 10 → 50 utilisateurs (MVP → Early traction)
Symptômes de croissance :
- ⚠️ App ralentit aux heures de pointe
- ⚠️ Bugs remontés quotidiennement
- ⚠️ Support client prend 4h/jour
- ⚠️ Déploiements = stress (downtime possible)
Problèmes techniques :
- Database queries non optimisées (N+1)
- Pas de cache
- Logs en console.log
- Pas de monitoring
- Déploiement manuel
Budget tech : 500-1K€/mois Temps setup : 2-3 semaines
Seuil 2 : 50 → 100 utilisateurs (Product-market fit)
Symptômes de croissance :
- 🔥 Site down 2-3x/semaine
- 🔥 Database timeout fréquents
- 🔥 Features prennent 2x plus de temps
- 🔥 Onboarding nouveau dev = 2 semaines
Problèmes techniques :
- Architecture monolithique
- Pas de tests automatisés
- Dette technique = 30% du temps dev
- Pas de CI/CD
- Database migrations = panique
Budget tech : 2-5K€/mois Temps setup : 4-6 semaines
Seuil 3 : 100+ utilisateurs (Scale)
Symptômes de croissance :
- 💥 Load balancing nécessaire
- 💥 Multi-région envisagée
- 💥 Équipe 5+ devs
- 💥 Compliance (RGPD, SOC2)
Non couvert ici : Cet article focus 10 → 100 users
Phase 1 : 10 → 50 users (4 semaines)
Semaine 1 : Setup monitoring (CRITICAL)
Objectif : Voir avant que ça pète
Tools à installer :
| Outil | Usage | Prix/mois | Setup |
|---|---|---|---|
| Sentry | Error tracking | 0-26€ | 30min |
| Uptime Robot | Site monitoring | 0€ | 10min |
| Vercel Analytics | Performance | 0-20€ | 5min |
| PostgreSQL stats | DB slow queries | 0€ | 1h |
Configuration Sentry :
// next.config.js
const { withSentryConfig } = require('@sentry/nextjs');
module.exports = withSentryConfig({
// Config Next.js
}, {
org: "your-org",
project: "your-project",
silent: true,
});
// pages/_app.tsx
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1, // 10% des requêtes
environment: process.env.NODE_ENV,
});
Alertes à configurer :
- Error rate >5% → Email immédiat
- Site down >1min → SMS
- API latency >2s → Slack
- Database CPU >80% → Email
Budget : 0-50€/mois
Semaine 2 : Optimiser database (80% impact)
Audit queries lentes :
-- PostgreSQL : top 10 queries lentes
SELECT
query,
mean_exec_time,
calls
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
Problèmes classiques :
1. N+1 queries
Avant (50 queries) :
// ❌ Mauvais
const users = await prisma.user.findMany();
for (const user of users) {
user.posts = await prisma.post.findMany({
where: { userId: user.id }
});
}
Après (2 queries) :
// ✅ Bon
const users = await prisma.user.findMany({
include: { posts: true }
});
Gain : -96% requêtes, -80% latency
2. Indexes manquants
Avant (3s query) :
-- Scan full table (100K rows)
SELECT * FROM posts WHERE user_id = 123;
Après (20ms query) :
-- Créer index
CREATE INDEX idx_posts_user_id ON posts(user_id);
-- Même query = 150x plus rapide
SELECT * FROM posts WHERE user_id = 123;
Indexes critiques :
-- Foreign keys
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_comments_post_id ON comments(post_id);
-- Colonnes filtrées souvent
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_created_at ON posts(created_at);
-- Recherche texte
CREATE INDEX idx_posts_title_trgm ON posts USING gin(title gin_trgm_ops);
3. Pas de pagination
Avant (10s, 50MB transferred) :
// ❌ Load 10K posts
const posts = await prisma.post.findMany();
Après (200ms, 500KB transferred) :
// ✅ Load 20 posts
const posts = await prisma.post.findMany({
take: 20,
skip: page * 20,
orderBy: { createdAt: 'desc' }
});
Gain : -95% latency, -99% bandwidth
Semaine 3 : Ajouter cache stratégique
Caching levels :
Browser Cache (3600s)
↓
CDN Cache (Vercel, 86400s)
↓
App Cache (Redis, 300s)
↓
Database
Redis setup (Upstash gratuit) :
// lib/redis.ts
import { Redis } from '@upstash/redis';
export const redis = new Redis({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
});
// Wrapper avec TTL
export async function getCached<T>(
key: string,
fetcher: () => Promise<T>,
ttl = 300 // 5min par défaut
): Promise<T> {
// Check cache
const cached = await redis.get<T>(key);
if (cached) return cached;
// Fetch fresh
const fresh = await fetcher();
await redis.setex(key, ttl, fresh);
return fresh;
}
Usage :
// Sans cache : 500ms/requête
const user = await prisma.user.findUnique({ where: { id } });
// Avec cache : 20ms/requête
const user = await getCached(
`user:${id}`,
() => prisma.user.findUnique({ where: { id } }),
3600 // 1h
);
Ce qu'il faut cacher :
- ✅ User profiles (1h TTL)
- ✅ Config app (24h TTL)
- ✅ Listes paginated (5min TTL)
- ❌ Real-time data (messages, notifs)
- ❌ User-specific data
Budget : 0€ (Upstash free tier = 10K reqs/day)
Semaine 4 : CI/CD basique
Objectif : Déployer sans stress
GitHub Actions setup :
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- run: npm ci
- run: npm test
- run: npm run lint
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID }}
vercel-args: '--prod'
Bénéfices :
- ✅ Tests auto avant déploiement
- ✅ Déploiement 1-click (git push)
- ✅ Rollback facile (revert commit)
- ✅ Preview branches (Vercel)
Budget : 0€ (GitHub Actions gratuit <2000 min/mois)
Résultat Phase 1 :
- ✅ Monitoring en place
- ✅ Database optimisée (-80% latency)
- ✅ Cache stratégique (-90% load DB)
- ✅ Déploiement automatisé
- 💰 Budget : 50-100€/mois
- ⏱️ Setup : 40-60h dev
Phase 2 : 50 → 100 users (6 semaines)
Semaine 1-2 : Architecture modulaire
Problème : Monolithe 20K lignes = impossible à maintenir
Solution : Découper en modules
Avant :
src/
├── pages/
│ ├── api/
│ │ ├── users.ts (500 lignes)
│ │ ├── posts.ts (800 lignes)
│ │ └── comments.ts (300 lignes)
Après :
src/
├── modules/
│ ├── users/
│ │ ├── user.service.ts
│ │ ├── user.repository.ts
│ │ └── user.types.ts
│ ├── posts/
│ │ ├── post.service.ts
│ │ ├── post.repository.ts
│ │ └── post.types.ts
├── pages/
│ └── api/
│ ├── users/[id].ts (50 lignes)
│ └── posts/[id].ts (50 lignes)
Pattern : Service → Repository → Database
// modules/users/user.repository.ts
export class UserRepository {
async findById(id: string) {
return prisma.user.findUnique({ where: { id } });
}
}
// modules/users/user.service.ts
export class UserService {
constructor(private repo: UserRepository) {}
async getUser(id: string) {
const user = await getCached(
`user:${id}`,
() => this.repo.findById(id),
3600
);
if (!user) throw new NotFoundError();
return user;
}
}
// pages/api/users/[id].ts (50 lignes)
export default async function handler(req, res) {
const service = new UserService(new UserRepository());
const user = await service.getUser(req.query.id);
res.json(user);
}
Bénéfices :
- ✅ Code testable (mock repository)
- ✅ Réutilisable (service partagé)
- ✅ Maintenable (1 fichier = 1 responsabilité)
Semaine 3 : Tests automatisés (critiques)
Objectif : Déployer sans peur de tout casser
Coverage cible :
- 80% units tests (fonctions business)
- 20% integration tests (API endpoints)
- 0% E2E (trop lent pour early stage)
Setup Vitest :
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
thresholds: {
lines: 80,
functions: 80,
branches: 70,
}
}
}
});
Test exemple :
// modules/users/user.service.test.ts
import { describe, it, expect, vi } from 'vitest';
import { UserService } from './user.service';
describe('UserService', () => {
it('should get user from cache', async () => {
const mockRepo = {
findById: vi.fn().mockResolvedValue({ id: '1', name: 'John' })
};
const service = new UserService(mockRepo);
const user = await service.getUser('1');
expect(user.name).toBe('John');
expect(mockRepo.findById).toHaveBeenCalledOnce();
});
it('should throw if user not found', async () => {
const mockRepo = {
findById: vi.fn().mockResolvedValue(null)
};
const service = new UserService(mockRepo);
await expect(service.getUser('999')).rejects.toThrow();
});
});
Run tests :
npm test # Run all
npm test -- --watch # Watch mode
npm test -- --coverage # Coverage report
CI integration (GitHub Actions) :
- run: npm test -- --coverage
- uses: codecov/codecov-action@v3 # Upload coverage
Budget : 0€
Semaine 4 : Database migrations sécurisées
Problème : ALTER TABLE = downtime
Solution : Migrations zero-downtime
Prisma migrations :
# Créer migration
npx prisma migrate dev --name add_user_role
# Review SQL généré
cat prisma/migrations/XXX_add_user_role/migration.sql
Checklist zero-downtime :
- Additive only : Ajouter colonnes (pas supprimer)
- Default values : Toujours un défaut
- Nullable first : Rendre nullable → populate → NOT NULL
- Indexes online : CONCURRENT (Postgres)
- Backward compatible : App v1 doit fonctionner avec schema v2
Exemple migration sécurisée :
-- ❌ MAUVAIS (downtime)
ALTER TABLE users ADD COLUMN role TEXT NOT NULL;
-- ✅ BON (zero downtime)
-- Step 1 : Ajouter nullable avec défaut
ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user';
-- Step 2 : Populate existant (background job)
UPDATE users SET role = 'admin' WHERE email LIKE '%@company.com';
-- Step 3 : Rendre NOT NULL (après deploy)
ALTER TABLE users ALTER COLUMN role SET NOT NULL;
Rollback strategy :
-- Toujours avoir un DOWN migration
-- migration_down.sql
ALTER TABLE users DROP COLUMN role;
Semaine 5 : Performance budget
Objectif : Garantir perfs
Core Web Vitals cibles :
| Métrique | Seuil | Current | Action |
|---|---|---|---|
| LCP (Largest Contentful Paint) | <2.5s | 3.2s | ❌ Image lazy load |
| FID (First Input Delay) | <100ms | 50ms | ✅ OK |
| CLS (Cumulative Layout Shift) | <0.1 | 0.15 | ❌ Reserve space images |
Lighthouse CI :
# .github/workflows/lighthouse.yml
- uses: treosh/lighthouse-ci-action@v9
with:
urls: |
https://preview-${{ github.sha }}.vercel.app
uploadArtifacts: true
temporaryPublicStorage: true
Performance budget :
// lighthouserc.json
{
"ci": {
"assert": {
"assertions": {
"first-contentful-paint": ["error", {"maxNumericValue": 2000}],
"interactive": ["error", {"maxNumericValue": 3000}],
"total-byte-weight": ["error", {"maxNumericValue": 500000}]
}
}
}
}
CI bloque si perf < seuil ✅
Semaine 6 : Documentation technique
Objectif : Onboarder dev #2 en <2 jours
Docs critiques :
1. README.md
# Project Name
## Quick start
\`\`\`bash
git clone ...
npm install
cp .env.example .env
npm run dev
\`\`\`
## Architecture
- Frontend : Next.js 15 + React 19
- Backend : tRPC + Prisma
- Database : PostgreSQL (Supabase)
- Cache : Redis (Upstash)
## Deploy
\`\`\`bash
git push origin main # Auto-deploy Vercel
\`\`\`
2. ARCHITECTURE.md
## System design
[Diagram here]
## Data flow
1. User → Next.js (Vercel)
2. Next.js → tRPC API
3. tRPC → Service Layer
4. Service → Repository
5. Repository → Prisma
6. Prisma → PostgreSQL
## Key decisions
- **Why Next.js?** : SEO + React
- **Why tRPC?** : Type-safe API
- **Why Supabase?** : Managed Postgres
3. CONTRIBUTING.md
## Workflow
1. Create branch `feat/feature-name`
2. Code + tests
3. Push → Auto-preview Vercel
4. PR → Code review
5. Merge → Auto-deploy prod
## Standards
- ESLint + Prettier
- Conventional commits
- Test coverage >80%
Budget : 8-16h rédaction
Résultat Phase 2 :
- ✅ Architecture modulaire
- ✅ Tests 80% coverage
- ✅ Migrations zero-downtime
- ✅ Performance budget
- ✅ Documentation complète
- 💰 Budget : 100-200€/mois
- ⏱️ Setup : 120-150h dev
Infrastructure : coûts réels 10 → 100 users
Phase 1 : 10-50 users
| Service | Usage | Prix/mois |
|---|---|---|
| Hébergement (Vercel Pro) | Unlimited bandwidth | 20€ |
| Database (Supabase Free) | 500MB, 2GB bandwidth | 0€ |
| Cache (Upstash Free) | 10K requests/day | 0€ |
| Monitoring (Sentry) | 5K errors/month | 0€ |
| Analytics (Vercel) | Unlimited | 0€ |
| Email (Resend Free) | 3K emails/month | 0€ |
| TOTAL | 20€/mois |
Phase 2 : 50-100 users
| Service | Usage | Prix/mois |
|---|---|---|
| Hébergement (Vercel Pro) | Unlimited | 20€ |
| Database (Supabase Pro) | 8GB, 50GB bandwidth | 25€ |
| Cache (Upstash Pay-as-you-go) | 100K req/day | 10€ |
| Monitoring (Sentry Team) | 50K errors/month | 26€ |
| Analytics (Vercel) | Unlimited | 0€ |
| Email (Resend) | 10K emails/month | 20€ |
| Storage (S3) | 10GB, 50GB transfer | 5€ |
| TOTAL | 106€/mois |
Projection 100+ users : 200-500€/mois
Checklist scaling readiness
Infrastructure ✅
- Monitoring setup (Sentry + Uptime)
- Database optimisée (indexes, N+1 fixed)
- Cache layer (Redis)
- CI/CD automatisé (GitHub Actions)
- Backup database (daily)
Code ✅
- Architecture modulaire (services + repos)
- Tests coverage >80%
- Documentation technique à jour
- Performance budget configuré
- Error handling standardisé
Process ✅
- Migrations zero-downtime
- Rollback strategy
- Incident response plan (qui appeler ?)
- Feature flags (déployer sans activer)
Conclusion
Scaler de 10 à 100 utilisateurs nécessite 10-12 semaines de travail technique et 120-200€/mois d'infrastructure.
ROI : Investir 15K€ dev maintenant évite 50-100K€ de refonte à 500 users.
Audit infrastructure scaling : Évaluez votre readiness et obtenez un plan d'action.
À propos : Jérémy Marquer a accompagné 20+ startups dans leur scaling. Zéro crash majeur à ce jour.
Articles similaires
Choisir sa stack technique startup 2025 : Guide décision (Next.js, React, Python)
Framework complet pour choisir stack tech startup : Next.js vs React, Node vs Python, PostgreSQL vs MongoDB. Critères, benchmarks, coûts, erreurs à éviter.
Product-Market Fit : Guide technique pour valider votre startup
Méthodologie complète pour atteindre le Product-Market Fit : métriques, signaux, expérimentations, stack analytics. Framework Sean Ellis + exemples concrets.
Automatiser ses process en startup : IA, No-Code et gains de productivité en 2025
Comment automatiser les process d'une startup grâce à l'IA et au No-Code ? Guide complet pour CTOs et fondateurs : outils, ROI, cas d'usage, pièges à éviter.
