pilotage/telaria-style.md

Coding style applicatif — non-négociables Mathieu

Périmètre : règles de code transverses à respecter par toute instance Claude qui édite du code applicatif (PHP/Symfony, Twig, Doctrine, Python/FastAPI) dans l'écosystème telaria / tlr-*. Pas un coding standard exhaustif : juste les non-négociables, validés en pratique sur telaria v0.1 → v0.4.

Réf pilotage/ecosystem.md (rôles & autorité). Compléments transverses : quality.md, accessibility.md, guides/git-conventions.md, markdown.md.

Dernière mise à jour : 2026-05-29 — création (suite inbox codexia #18).


Comment utiliser cette fiche

  • Toute nouvelle instance Claude sur un dĂ©pĂ´t code lit ce fichier avant ses premiers commits.
  • Chaque règle = une phrase + un why d'une ligne + un exemple court quand l'Ă©cart est tentant.
  • En cas de doute sur l'interprĂ©tation : Lead dev (codexia) tranche.
  • Les Ă©carts justifiĂ©s se documentent dans le code (commentaire WHY) ou la PR — pas de zèle silencieux.

1. Sobriété > élégance

Règle : trois lignes répétées valent mieux qu'une abstraction prématurée.

Why : les abstractions créées « au cas où » durcissent le code avant qu'on sache de quoi il a besoin. Coût de revert > coût de duplication tant qu'il n'y a pas 3 cas réels.

Interdits par défaut : feature flags pour code non livré, shims de rétrocompatibilité pour API internes, wrappers « génériques » sans usage prouvé, hiérarchies d'interfaces à une seule implémentation.


2. Tests d'intégration > unitaires mockés

Règle : quand l'intégration est faisable (DB de test, conteneur, fixture), elle bat le mock.

Why : leçon retenue — des tests mockés verts ont masqué une migration cassée en prod. Le mock ment sur le contrat réel.

Mocks acceptables : dépendances coûteuses (modèle ML chargé, API tierce payante, e-mail sortant). Tout le reste passe par une vraie instance.


3. Sécurité par défaut

Règle : Voters Symfony, rate limiter sur surfaces sensibles, headers HSTS/CSP/X-Frame, durcissement systemd en prod.

Why : la sécu ajoutée après coup laisse toujours un trou. Ajoutée d'office, elle coûte ~10 lignes.

Cibles minimales :

  • is_granted('VOTER_NAME', $resource) plutĂ´t que if ($user->getId() === ...) ad-hoc.
  • Rate limiter sur login, reset password, endpoints API publics.
  • Headers via nelmio/security-bundle ou Ă©quivalent, CSP stricte par dĂ©faut.
  • En WSL local : pas de zèle sĂ©cu (HTTPS strict, durcissement systemd) — gardĂ© pour le VPS.

4. Accessibilité RGAA AA non négociable

Règle : toute surface UI livrée respecte RGAA AA.

Why : norme légale + engagement Opquast du projet. Rattraper l'accessibilité après coup = re-design.

Détails : voir accessibility.md (couleurs, contrastes, navigation clavier, ARIA, RGAA AA déjà cadré).


5. i18n par défaut

Règle : aucune chaîne UI en dur ; fr par défaut ; structure prête pour en et autres.

Why : ajouter une langue à un projet hardcodé fr coûte plus cher que d'écrire trans('...') dès le début.

Avant / après (Twig) :

{# ❌ #}
<button>Valider</button>

{# âś… #}
<button>{{ 'action.submit'|trans }}</button>

6. Doctrine — entités fines, repos pour queries

Règle : entités = état + invariants. Queries non triviales dans le Repository. Migrations checkées via doctrine:schema:validate. Jamais de SQL dans un contrôleur.

Why : SQL inline = duplication garantie + sécurité fragile. schema:validate rouge = drift entre code et DB → bugs silencieux en prod.

Avant / après :

// ❌ contrôleur
$rows = $conn->executeQuery('SELECT * FROM user WHERE ...')->fetchAllAssociative();

// âś…
$users = $userRepository->findActiveByOrganization($org);

7. Twig — pas de logique métier

Règle : Twig affiche. La logique vit dans des extensions Twig ou des Value Objects passés depuis le contrôleur.

Why : logique en Twig = intestable + invisible aux outils statiques.

Avant / après :

{# ❌ #}
{% if user.subscriptionEndsAt|date('U') < 'now'|date('U') and user.role != 'admin' %}

{# âś… #}
{% if user.needsRenewalReminder %}

8. Python (FastAPI) — lazy imports, Pydantic strict, lifespan

Règle :

  • Imports lourds (ML, torch, sentence-transformers) lazy dans une fonction d'init, pas au top-level.
  • Validation contrat via Pydantic strict (Field(min_length=1), types prĂ©cis aux frontières).
  • PrĂ©chargement via lifespan, pas @app.on_event (dĂ©prĂ©ciĂ©).

Why : top-level imports lourds → boot lent + tests qui chargent tout pour rien. Pydantic strict aux frontières = un seul point de validation, le reste du code peut faire confiance aux types.

Avant / après :

# ❌ top du module
from sentence_transformers import SentenceTransformer
model = SentenceTransformer(...)

# âś…
async def lifespan(app):
    from sentence_transformers import SentenceTransformer
    app.state.model = SentenceTransformer(...)
    yield

9. Bundles vs app-code — critère = réutilisation prouvée

Règle : on extrait en bundle (Symfony) ou en package (Python) uniquement quand la réutilisation est avérée — pas « parce que ce serait propre ».

Why : un bundle prématuré gèle une API qu'on aurait voulu changer. Voir [[dette-architecture-ia-surfaces]] côté codexia pour le retour d'expérience.

Seuil : ≥ 2 dépôts consommateurs réels, ou besoin de versioning indépendant clair.


10. Commentaires — WHY uniquement

Règle : un commentaire explique le pourquoi non-évident (contrainte cachée, invariant subtil, workaround pour un bug précis). Pas le quoi (les identifiants le disent).

Why : un commentaire WHAT ment dès le prochain rename. Un commentaire WHY reste vrai.

Interdits :

  • Refs au ticket / PR / incident (// fix #123, // pour le flow X) → ça vit dans le message de commit et la PR, pas dans le code.
  • Docstrings multi-paragraphes dĂ©coratives.
  • // updated, // removed, // old code — git log est lĂ  pour ça.

11. Outillage avant push

Règle : avant chaque push (idéalement via hook pre-push) :

  • PHP : PHPStan niveau max raisonnable (currently atteint sur telaria), composer audit = 0 advisory, tests verts.
  • Python : ruff check, mypy --strict sur les modules de contrat, tests verts.
  • Tous : git diff --check (pas de trailing whitespace ni de marqueurs de conflit).

Why : un advisory ou un test rouge en CI coûte 5× le temps qu'il aurait coûté en local. La discipline pre-push tient la barre.


12. Zéro dette, zéro poussière sous le tapis

Règle : on fait toujours tout ce qu'on peut faire au fur et à mesure. Pas de « on verra plus tard », pas de TODO reporté à la session suivante, pas de petite MAJ doc qu'on garde pour « quand on aura le temps ».

Why : un report devient un trou qui se découvre au pire moment. Coût immédiat < coût différé. Mathieu refuse la dette technique et les non-dits.

Conséquence assumée : la doc peut changer contradictoirement plusieurs fois dans la même journée au fil des devs — la cohérence à l'instant T prime sur la stabilité éditoriale.

Exemples concrets :

  • Release voisine qui impacte ecosystem.md (version courante) → MAJ immĂ©diate, mĂŞme pour 1 ligne.
  • Commit qui rend une page de spec obsolète → patch dans la foulĂ©e, pas une ligne de wishlist.
  • Ack rituel dĂ» Ă  une instance qui vient de livrer → postĂ©, mĂŞme si elle dit « rien Ă  patcher ».
  • Trou doc identifiĂ© pendant un dev → corrigĂ© maintenant ou créé en ticket explicite, jamais ignorĂ©.

Voir aussi

Assistant documentaire

Posez une question sur la documentation. Les réponses citent leurs sources — un clic ouvre le document à gauche.

Loading…
Loading the web debug toolbar…
Attempt #