Domaines et cycles de vie - 09 - La dette technique : quand on construit sans lifecycle

7 problèmes concrets causes par l'absence de cycle de vie dans ton code. Avec la solution architecture pour chacun, et du refactoring pragmatique.

09 - La dette technique : quand on construit sans lifecycle

Ce que tu vas apprendre

  • Ce qu'est vraiment la dette technique
  • 7 problèmes concrets causes par l'absence de lifecycle
  • Pour chaque problème, la solution "avec lifecycle"

Prerequisites


Qu'est-ce que la dette technique ?

La dette technique est l'ensemble des raccourcis pris pendant le développement qui coutent plus cher a maintenir qu'a avoir bien fait des le depart. C'est comme une dette financiere : tu empruntes du temps maintenant, mais tu paies des interets plus tard.

Ce n'est pas toujours mauvais : parfois, on prend de la dette technique consciemment pour livrer plus vite. Le problème, c'est quand elle est accidentelle (on ne savait pas qu'on en prenait) ou negligee (on sait mais on ne rembourse jamais).

L'absence de lifecycle est une des sources de dette technique les plus couteuses. Voici pourquoi, avec un cas d'etude réel.


Problème 1 : Pas de Single Source of Truth

Le constat

L'état d'une Place pendant un batch est disperse dans 4 systèmes :

Système Ce qu'il stocke Problème
React state (RAM) L'action en cours Perdu au refresh
IndexedDB (navigateur) Le statut de review Local, pas partageable
Firestore Les paramètres de config Ne connaît pas l'état de la Place
PostgreSQL La Place finale Ne sait pas qu'un batch est en cours

Avec un lifecycle

sql-- Un seul SELECT pour savoir ou en est chaque Place
SELECT place_id, status FROM places WHERE city_id = 42;

Un champ, une table, une vérité.


Problème 2 : Logique de categorisation ad-hoc

Le constat

Le code recalcule l'action a chaque exécution avec des if/else :

typescript// Machine a etats implicite dans du code imperatif
if (place.is_closed && exists) {
  action = "deleted";
} else if (exists) {
  action = "needs_review";
} else {
  action = "pending_images";
}

C'est une state machine deguisee, mais illisible, non testable, et non documentee.

Avec un lifecycle

typescriptconst transitions = {
  SCRAPED: {
    CLOSE_DETECTED: { target: "PENDING_DELETE", guard: existsInDB },
    DIFF_DETECTED: { target: "PENDING_REVIEW", guard: existsInDB },
    NEW_PLACE: { target: "DRAFT", guard: notInDB },
  },
};

Declaratif. Testable unitairement. Lisible par n'importe qui.


Problème 3 : Validation dupliquee

Le constat

Le meme guard est reimplemente dans plusieurs fichiers :

typescript// Fichier A : create-place-panel.tsx
const isValid =
  hasImages && hasAllTops && hasTranslations && hasGptDescriptions && hasCategories && hasMappedData;

// Fichier B : add-place-search.tsx
// ... exactement le meme calcul, copie-colle

Si la regle change, il faut la changer partout. On en oublie un ? Bug.

Avec un lifecycle

typescript// Un guard unique, defini une fois, reutilise partout
function canTransitionTo_READY_FOR_IMAGES(place: PlaceEntity): boolean {
  return (
    place.categories.length > 0 &&
    place.translations.every((t) => t.description !== null) &&
    place.images.length > 0 &&
    place.images.filter((i) => i.top !== null).length >= 3
  );
}

Un seul endroit. Un seul test. Une seule source de vérité pour la regle.


Problème 4 : Duplication de features entières

Le constat

11 fichiers dupliques d'une feature vers une autre : place-type-dialog.tsx, image-scraping-panel.tsx, translations-panel.tsx... Meme logique, meme UI, copiee-collee.

Avec un lifecycle

Les features partagent le meme pipeline de domaine (les transitions). Chaque feature est un point d'entree qui déclenché des transitions, pas une copie du domaine. Pas de duplication, parce que la logique vit dans les transitions, pas dans les features.


Problème 5 : Pas de rollback ni d'idempotence

Le constat

Si le batch plante a la Place 500, il faut tout recommencer. Pas de moyen de savoir quelles Places ont ete traitees et lesquelles non.

Avec un lifecycle

Chaque Place a son état persiste. Si le batch plante, on relance : les Places deja traitees sont en ENRICHED, le batch les ignore (no-op idempotent) et reprend les DRAFT.


Problème 6 : Pas d'audit trail

Le constat

Personne ne sait qui a fait quoi, quand. Une Place est en PUBLISHED mais on ne sait pas qui l'a publiee, quand, ni depuis quel état elle venait.

Avec un lifecycle

Chaque transition est un événement auditable :

typescriptconst auditEntry = {
  entityId: "place_42",
  from: "READY_FOR_PUBLICATION",
  to: "PUBLISHED",
  triggeredBy: "user_123",
  timestamp: "2026-03-28T14:32:00Z",
  event: "PUBLISH",
};

Un historique complet, interrogeable, exportable.


Problème 7 : État et intention melanges dans les types

Le constat

Les types TypeScript melangent "ce que l'entité est" (son état) et "ce qu'on veut en faire" (l'intention) :

typescripttype BatchPlaceResult = {
  place: Place;
  action: "pending_images" | "needs_review" | "deleted"; // intention, pas etat
  reviewStatus?: "approved" | "rejected"; // etat, mais local
};

action n'est pas un état de l'entité, c'est une intention du système. reviewStatus est un état, mais il n'existe que dans un objet local, pas dans la source de vérité.

Avec un lifecycle

typescripttype PlaceEntity = {
  id: string;
  status: PlaceStatus; // l'etat, persiste, autoritaire
  // ... autres champs
};

type PlaceStatus =
  | "DRAFT"
  | "ENRICHED"
  | "READY_FOR_IMAGES"
  | "IMAGES_PROCESSING"
  | "IMAGES_PROCESSED"
  | "READY_FOR_PUBLICATION"
  | "PUBLISHED";

L'état est dans l'entité. L'intention est dans la transition. Les deux ne se melangent pas.


Résumé

  • La dette technique est le coût des raccourcis non rembourses
  • L'absence de lifecycle produit 7 problèmes concrets : pas de SSOT, logique implicite, validation dupliquee, features copiees, pas d'idempotence, pas d'audit, types melanges
  • Chaque problème a une solution directe quand on structure le code autour d'un lifecycle
  • Le lifecycle n'est pas un luxe : c'est ce qui empeche la dette technique de s'accumuler

Article précédent : 08 - Idempotence Article suivant : 10 - Features vs Domaine : la bonne decoupe

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.