


Node 22 LTS, pnpm, Vite, Vitest, ESLint flat, Zod, Turborepo : la stack que j'utilise par défaut en 2026 et pourquoi ennuyeuse vaut mieux que révolutionnaire.
Choisir son socle Node.js + TypeScript en 2026, c'est moins une question de mode qu'une question de DX cumulée sur 2 ans. Voici la stack que j'utilise par défaut et pourquoi.
La DX (Developer Experience) n'est pas un confort, c'est un investissement. Une stack qui vous fait perdre 15 minutes par jour à attendre des builds ou décoder des erreurs absconses, c'est 60 heures par an et par développeur. Sur une équipe de 4, c'est l'équivalent d'un mi-temps perdu.
Cet article condense la stack que j'utilise en 2026 par défaut, avec les arbitrages qui m'ont mené là. Pas de "tu dois utiliser ça" — des critères pour décider.
Trois prétendants en 2026, contexte par contexte :
| Critère | Node 22 LTS | Bun 1.x | Deno 2.x |
|---|---|---|---|
| Maturité prod | ★★★★★ | ★★★ | ★★★★ |
| Performance | ★★★ | ★★★★★ | ★★★★ |
| Écosystème npm | ★★★★★ | ★★★★ | ★★★★ |
| Outils intégrés | ★★ | ★★★★★ | ★★★★★ |
| TypeScript natif | Avec --experimental | Oui | Oui |
| Adoption hosting | Toutes plateformes | Limitée | Croissante |
Mon défaut : Node 22 LTS. Raisons :
Bun : pour les workloads très perf-critiques (parseurs, transformateurs, tests massifs). Le démarrage 4x plus rapide change la donne en CI.
Deno : pour les projets edge / serverless courts. Le modèle de permissions et l'imports HTTPS natifs sont élégants, mais la friction de l'écosystème pèse.
Pour une app web : Vite, point. La DX est sans concurrence : démarrage en moins d'1 seconde, HMR instantané, support TypeScript et JSX natifs, écosystème de plugins riche.
Pour un service backend : esbuild, parce qu'on n'a pas besoin du serveur de dev de Vite. esbuild en mode watch rebundle en <200ms, parfait pour les tests rapides.
Pour publier un package npm : tsup (wrapper esbuild) ou tsdown. Génère dual ESM/CJS + types en une commande.
| 1 | { |
| 2 | "scripts": { |
| 3 | "build": "tsup src/index.ts --dts --format esm,cjs", |
| 4 | "dev": "tsup src/index.ts --watch --dts --format esm,cjs" |
| 5 | } |
| 6 | } |
TypeScript en mode strict plus quelques flags supplémentaires :
| 1 | { |
| 2 | "compilerOptions": { |
| 3 | "strict": true, |
| 4 | "noUncheckedIndexedAccess": true, |
| 5 | "noImplicitOverride": true, |
| 6 | "noPropertyAccessFromIndexSignature": true, |
| 7 | "exactOptionalPropertyTypes": true, |
| 8 | "noFallthroughCasesInSwitch": true, |
| 9 | "forceConsistentCasingInFileNames": true, |
| 10 | "isolatedModules": true, |
| 11 | "verbatimModuleSyntax": true, |
| 12 | "target": "ES2022", |
| 13 | "module": "NodeNext", |
| 14 | "moduleResolution": "NodeNext" |
| 15 | } |
| 16 | } |
noUncheckedIndexedAccess : arr[0] devient T | undefined, forçant à gérer le cas videexactOptionalPropertyTypes : { a?: string } n'accepte plus undefined explicite, juste l'absenceisolatedModules + verbatimModuleSyntax : compatibilité parfaite avec esbuild/ViteCes flags coûtent du temps au début mais éliminent une classe entière de bugs. Sur Nexus, le passage à noUncheckedIndexedAccess a révélé 17 bugs latents en une journée de refactor.
TypeScript valide à la compilation. Pour valider à l'exécution (input HTTP, message de queue, payload externe), un schema runtime est indispensable.
Zod : standard de facto, écosystème énorme, intégration avec tRPC, OpenAPI, etc.
Valibot : 7x plus léger, idéal pour les bundles client. API très proche de Zod.
| 1 | // src/validators/deposit.ts (Zod) |
| 2 | import { z } from "zod"; |
| 3 | |
| 4 | export const depositSchema = z.object({ |
| 5 | amount: z.number().int().positive().max(10_000_000), |
| 6 | currency: z.enum(["XOF", "USD", "EUR"]), |
| 7 | gateway: z.enum(["mtn_momo", "orange_money", "stripe"]), |
| 8 | reference: z.string().min(3).max(64), |
| 9 | }); |
| 10 | |
| 11 | export type Deposit = z.infer<typeof depositSchema>; |
Règle : toute donnée venant de l'extérieur passe par un schema runtime. Pas de as Deposit aveugle. Pas de "ça vient du frontend, c'est OK".
ESLint v9 en flat config (eslint.config.js) est désormais standard.
| 1 | // eslint.config.js |
| 2 | import tseslint from "typescript-eslint"; |
| 3 | import unicorn from "eslint-plugin-unicorn"; |
| 4 | |
| 5 | export default tseslint.config( |
| 6 | ...tseslint.configs.strictTypeChecked, |
| 7 | ...tseslint.configs.stylisticTypeChecked, |
| 8 | unicorn.configs.recommended, |
| 9 | { |
| 10 | languageOptions: { |
| 11 | parserOptions: { |
| 12 | projectService: true, |
| 13 | tsconfigRootDir: import.meta.dirname, |
| 14 | }, |
| 15 | }, |
| 16 | rules: { |
| 17 | "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], |
| 18 | "@typescript-eslint/consistent-type-imports": "error", |
| 19 | "no-console": ["warn", { allow: ["warn", "error"] }], |
| 20 | }, |
| 21 | }, |
| 22 | ); |
strictTypeChecked ajoute des règles qui demandent l'analyse de types (lente mais précieuse) : no-floating-promises, no-misused-promises, require-await. Ces trois règles seules attrapent 80 % des bugs async typiques.
Prettier reste le standard mature. Son écosystème de plugins (Tailwind, etc.) est inégalé.
Biome est 10–30x plus rapide. Sur les gros monorepos, ça change la vie. Son linter (Biome inclut son propre linter) est encore en retrait vs ESLint mais comble rapidement.
Mon défaut 2026 : Biome pour les nouveaux projets, Prettier+ESLint pour les projets existants. La migration Biome demande du travail mais le gain de vitesse est tangible.
Vitest : compatible Jest, ESM natif, watch ultra-rapide, snapshot, mocking, coverage v8. C'est le couteau suisse moderne.
Bun test : intégré au runtime Bun, démarrage instantané. Excellent si on tourne déjà sur Bun.
node:test : module natif Node 20+, sans dépendance. Suffit pour des tests simples mais l'écosystème est mince.
| 1 | // src/services/payment/PaymentRouter.test.ts |
| 2 | import { describe, it, expect, vi } from "vitest"; |
| 3 | import { PaymentRouter } from "./PaymentRouter"; |
| 4 | |
| 5 | describe("PaymentRouter", () => { |
| 6 | it("resolves the right gateway for a method", () => { |
| 7 | const router = new PaymentRouter(); |
| 8 | const fakeGateway = { name: "mtn", supports: ["mobile_money_mtn"] }; |
| 9 | router.register(fakeGateway as never); |
| 10 | expect(router.resolve("mobile_money_mtn")).toBe(fakeGateway); |
| 11 | }); |
| 12 | |
| 13 | it("throws on unregistered method", () => { |
| 14 | const router = new PaymentRouter(); |
| 15 | expect(() => router.resolve("card")).toThrow(/no gateway/i); |
| 16 | }); |
| 17 | }); |
pnpm par défaut. Trois raisons :
node_modules (store partagé hardlinks)npm reste OK pour les projets très petits ou pour un monorepo bien structuré avec npm workspaces. Yarn berry est techniquement excellent mais sa courbe d'apprentissage et son écosystème plus petit le rendent moins justifiable en 2026.
Pour un monorepo de 3 à 15 packages : Turborepo. Configuration simple, cache distant Vercel, parfaitement intégré à pnpm.
Pour un monorepo entreprise avec 30+ packages, contraintes architecturales fortes : Nx. Plus puissant, plus complexe, plus de courbe.
| 1 | // turbo.json |
| 2 | { |
| 3 | "$schema": "https://turbo.build/schema.json", |
| 4 | "tasks": { |
| 5 | "build": { |
| 6 | "dependsOn": ["^build"], |
| 7 | "outputs": ["dist/**", ".next/**"] |
| 8 | }, |
| 9 | "lint": { "dependsOn": ["^build"] }, |
| 10 | "test": { "dependsOn": ["^build"] }, |
| 11 | "dev": { "cache": false, "persistent": true } |
| 12 | } |
| 13 | } |
GitHub Actions est devenu le standard pratique en 2026. GitLab CI reste excellent si vous êtes déjà chez GitLab.
Pour un projet TypeScript/Node :
| 1 | # .github/workflows/ci.yml |
| 2 | name: CI |
| 3 | on: [push, pull_request] |
| 4 | jobs: |
| 5 | ci: |
| 6 | runs-on: ubuntu-latest |
| 7 | steps: |
| 8 | - uses: actions/checkout@v4 |
| 9 | - uses: pnpm/action-setup@v3 |
| 10 | with: { version: 9 } |
| 11 | - uses: actions/setup-node@v4 |
| 12 | with: { node-version: 22, cache: pnpm } |
| 13 | - run: pnpm install --frozen-lockfile |
| 14 | - run: pnpm typecheck |
| 15 | - run: pnpm lint |
| 16 | - run: pnpm test |
| 17 | - run: pnpm build |
Cache pnpm bien fait : 2 minutes vs 6 sans cache sur un projet moyen.
Ma stack 2026 pour un nouveau projet Node/TS :
| Catégorie | Choix |
|---|---|
| Runtime | Node 22 LTS |
| Package manager | pnpm |
| TypeScript | strict + flags renforcés |
| Validation runtime | Zod |
| Linter | ESLint flat + typescript-eslint strict |
| Formatter | Biome (nouveau) ou Prettier (existant) |
| Tests | Vitest |
| Bundler app web | Vite |
| Bundler service | esbuild + tsup pour packages |
| Monorepo | Turborepo + pnpm workspaces |
| CI | GitHub Actions |
Cette stack est ennuyeuse, et c'est précisément l'intérêt. Aucun composant n'est révolutionnaire. Tous sont matures, documentés, recrutables. Le temps qu'on ne passe pas sur la stack est du temps qu'on passe sur le produit.
| Piège | Symptôme | Correction |
|---|---|---|
| Stack exotique pour briller | DX dégradée, recrutement difficile | Choix par défaut sauf justification précise |
| TypeScript non-strict | Bugs runtime fréquents | strict: true + flags renforcés |
| Pas de validation runtime | Crash sur input inattendu | Zod ou Valibot sur tous les inputs |
| Linter trop laxiste | Patterns douteux normalisés | typescript-eslint strict + règles async |
| Pas de cache CI | Builds lents qui démotivent | pnpm cache + Turbo cache |
| Monorepo prématuré | Complexité sans bénéfice | Mono-package jusqu'à 2-3 apps |
| Tests sans cible | Coverage symbolique | Tests sur la logique métier, pas sur le framework |
La meilleure stack 2026 n'est pas la plus nouvelle. C'est celle qui fait gagner le plus de minutes par jour sans demander d'effort cognitif.
Mes critères de choix dans l'ordre :
Une équipe de 4 développeurs avec la bonne stack livre 30 à 50 % plus vite qu'avec une stack mal choisie. Le calcul est rarement explicité mais il est massif.
Si vous démarrez aujourd'hui, prenez la stack par défaut. Personnalisez seulement quand un besoin précis et chiffré le justifie. Le reste est cosmétique.
Si ce sujet ressemble à un problème réel dans votre produit, je peux intervenir sur le diagnostic, l'architecture, le backend, l'interface et les automatisations qui rendent une plateforme exploitable.
Réactions des lecteurs
Aucun commentaire pour l'instant
Soyez le premier à réagir à cet article.