Aller au contenu

Style de code

Atlas applique un ensemble unique de règles de style et de typage sur son périmètre applicatif TypeScript (les huit catégories Node : apps, packages, services, cli, ui, config, assets, sandbox). Le code DataOps de dataops/ est en Python natif et relève d’une chaîne d’outillage distincte (ruff, pytest — ADR 0055) : il n’est soumis ni à ESLint, ni à Prettier, ni à TypeScript. Ces règles sont vérifiées automatiquement par les hooks Git (avant chaque commit) et par la CI (à chaque pull request). Git est le système de contrôle de version qui enregistre l’historique du code ; voir le glossaire pour les termes Git de base.

Chaque choix ci-dessous (typage strict, style fonctionnel, Effect) a un coût autant qu’un bénéfice. On les assume volontairement : cette page explique pourquoi Atlas les retient et ce qu’ils coûtent, pour qu’un contributeur sache à quoi s’attendre plutôt que de subir des règles non motivées.

TypeScript ajoute des types statiques à JavaScript ; le compilateur refuse de compiler à la moindre incohérence de types.

Atlas active le mode strictTypeChecked de tseslint, le plus strict disponible.

Pourquoi. Les types attrapent à la compilation une large classe d’erreurs (valeur undefined non gérée, mauvais argument, faute de frappe sur un champ) qui, sans eux, n’apparaîtraient qu’à l’exécution — voire en production. Ils servent aussi de documentation toujours à jour et fiabilisent les refactorisations : changer une signature fait remonter immédiatement tous les appels à corriger.

À quel prix. Le mode le plus strict est plus exigeant à écrire : il faut annoter, gérer explicitement les cas limites et parfois batailler avec le type system pour exprimer une intention pourtant simple. Le compromis assumé : un coût d’écriture en amont contre des erreurs évitées en aval. L’alternative — JavaScript sans types, ou TypeScript en mode laxiste — est plus rapide à écrire mais déplace le coût vers le débogage et les régressions.

Les règles supplémentaires :

RègleEffet
strict-boolean-expressionsInterdit les coercions booléennes implicites (if (value) sur un nombre, par exemple)
no-floating-promisesOblige à gérer toutes les promesses (await ou .then)
no-unnecessary-conditionDétecte les conditions toujours vraies ou toujours fausses
consistent-type-importsForce import type pour les imports purement de types
switch-exhaustiveness-checkVérifie que tous les cas d’un switch sur un type union sont couverts
no-explicit-anyInterdit le type any (erreur, pas avertissement)

La programmation fonctionnelle est un paradigme (une façon de structurer le code) qui privilégie les fonctions sans effet de bord, les données immuables — qu’on ne modifie pas après création — et les erreurs représentées comme des valeurs plutôt que comme des exceptions lancées. Atlas l’adopte via eslint-plugin-functional.

Pourquoi. Un code immuable et sans effet de bord caché est plus prévisible : une fonction qui ne dépend que de ses arguments donne toujours le même résultat, donc se teste et se raisonne isolément. Représenter les erreurs comme des valeurs typées (plutôt que par throw) force à traiter explicitement les cas d’échec — le compilateur ne laisse pas passer un chemin d’erreur oublié.

À quel prix. Le style est plus contraignant et dépayse qui vient d’un JavaScript impératif : interdire if/throw/try/catch (voir le tableau) oblige à réécrire des motifs courants avec des ternaires, du pattern matching ou des combinateurs. La courbe d’apprentissage est réelle, et certains algorithmes s’expriment plus naturellement de façon impérative. Le compromis assumé : une gêne ponctuelle à l’écriture contre des erreurs d’état et des effets de bord cachés en moins. C’est dans ce paradigme que s’inscrit Effect (section suivante), qui en est l’outil principal dans Atlas.

RègleStatutEffet
no-expression-statementserreurInterdit les expressions sans valeur de retour utilisée
no-conditional-statementserreurInterdit if/switch (force l’usage de ternaires ou de pattern matching)
no-throw-statementserreurInterdit throw (force l’usage d’Effect)
no-try-statementserreurInterdit try/catch (force l’usage d’Effect)
immutable-dataerreurInterdit la mutation d’objets et de tableaux
no-classesoffDésactivé (Effect utilise des classes)

Effect est une bibliothèque TypeScript qui met en œuvre concrètement le paradigme fonctionnel décrit ci-dessus : elle est l’outil par lequel Atlas applique « erreurs comme valeurs » et composition. Elle apporte :

  • Gestion d’erreurs typée : les erreurs sont des valeurs avec un type, pas des exceptions
  • Composition : des opérations s’assemblent avec pipe et des combinateurs
  • Observabilité native : tracing (suivi du parcours d’une requête) et métriques compatibles OpenTelemetry
  • Gestion de ressources : acquisition et libération automatiques (équivalent try-with-resources)
  • Concurrence structurée : interruption propre et gestion de fibers (les unités de travail concurrentes d’Effect)

À quel prix. Effect est un investissement : son modèle (le type Effect<A, E, R>, les Layer, le Context) demande un temps d’apprentissage notable et ajoute une dépendance structurante à toute la base de code. Écrit en Effect, un bout de logique simple est plus verbeux qu’en JavaScript direct. Le compromis assumé : une montée en compétence et un peu de cérémonie contre une gestion d’erreurs, de ressources et de concurrence homogène et vérifiée par les types à l’échelle du dépôt. Le pourquoi durable de ce choix est tracé dans l’ADR 0005 ; cette page n’en donne que le résumé opérationnel.

Certains motifs Effect (et le framework HTTP Hono pour les services) sont explicitement autorisés malgré les règles fonctionnelles strictes :

// Effect
Effect.runPromise(...)
pipe(value, Effect.map(...))
Layer.succeed(...)
// Hono (déclaration de routes)
app.get('/path', handler)
records.post('/', handler)

Règles issues de eslint-plugin-security :

RègleEffet
detect-unsafe-regexDétecte les expressions régulières vulnérables au ReDoS
detect-eval-with-expressionInterdit eval() avec une expression
detect-object-injectionAvertit sur les accès dynamiques à des propriétés d’objet
RègleValeurEffet
max-depth4Profondeur d’imbrication maximale
max-lines-per-function60Nombre de lignes par fonction
complexity15Complexité cyclomatique maximale
no-consoleerreurInterdit console.log (autorise warn et error)

Les règles strictes sont assouplies pour *.test.ts et *.spec.ts afin d’autoriser les motifs classiques de test (assertions, mocks, fixtures).

Prettier formate automatiquement le code à chaque commit. Aucun débat de style : la machine décide.

Tous les messages de commit suivent Conventional Commits, vérifié par commitlint :

type(scope): description
[corps optionnel]
[footer optionnel]

Les principaux types : feat, fix, docs, refactor, test, chore, ci, build.

FichierContenu
config/shared-config/ESLint, TypeScript, Prettier
eslint.config.jsComposition ESLint à la racine
.prettierrcOptions Prettier
commitlint.config.jsRègles commitlint