jobsphp

Dette technique : le choix radical d'un CTO chez une startup

Gestion & Stratégie. Dette technique : le choix radical d'un CTO chez une startup

PHP 8.2 ne reçoit plus que des correctifs de sécurité jusqu’au 31 décembre 2026. À cette date, une application métier encore verrouillée sur cette branche n’a pas un problème de « mise à niveau à prévoir ». Elle porte une échéance dans son backlog.

Dette technique: le choix radical d'un CTO chez une startup

La différence est comptable: après la fin de support, chaque vulnérabilité corrigée dans les branches maintenues devient potentiellement une exposition non corrigeable sans migration.

C’est là que la priorisation d’un backlog technique sur un projet PHP complexe cesse d’être une discussion abstraite entre CTO, Product Owner et équipe de développement. Une dette peut ralentir la livraison. Une branche PHP en fin de vie, une dépendance vulnérable ou une Definition of Done incomplète peuvent aussi invalider la livraison elle-même.

Le choix radical d’un CTO n’est donc pas forcément une réécriture. Le vrai choix radical consiste à traiter le passif technique comme du travail produit ordonné, estimé et livrable — pas comme une liste de tickets que l’on repousse jusqu’à l’incident.

La dette technique n’est pas synonyme de mauvais code

Un projet Symfony avec 180 contrôleurs, des services surchargés, des DTO absents et une couverture de tests lacunaire peut fonctionner correctement en production. Pendant un temps. La dette n’est pas mesurée par l’élégance d’un repository ou par le nombre de commentaires TODO. Elle se mesure par les contraintes que le système impose à chaque modification.

Le symptôme observable n’est pas « le code est sale ». C’est plutôt celui-ci:

  • une modification de règle métier nécessite de toucher simultanément un contrôleur, un listener Doctrine, un service applicatif et une commande asynchrone;
  • les tests d’intégration exigent une base de données complète et rendent le cycle CI trop lent pour soutenir le flux de pull requests;
  • une dépendance Composer ne peut pas évoluer parce qu’elle entraîne une rupture de compatibilité en cascade;
  • une montée de version PHP bloque sur des types implicites, des APIs dépréciées ou des extensions non compatibles;
  • les équipes ne savent plus si une migration Doctrine est idempotente, rejouable ou dépendante de l’état exact d’un environnement.

Cette dette n’est pas nécessairement le résultat d’une faute. La grille de Martin Fowler reste utile parce qu’elle évite le jugement moral: une dette peut être prudente ou imprudente, délibérée ou involontaire. Une startup peut choisir un couplage temporaire pour livrer un flux de paiement, puis découvrir six mois plus tard que le modèle de facturation réel ne correspond plus aux hypothèses initiales. Le code devient inadapté après apprentissage du domaine. Ce n’est pas un échec de compétence. C’est une information nouvelle qui arrive dans une architecture ancienne.

Le problème commence quand cette information ne modifie pas l’ordre du backlog.

Une dette technique devient critique lorsqu’elle transforme une évolution ordinaire en opération à risque.

Pour un CTO, l’enjeu n’est pas de classer les anomalies entre « métier » et « technique ». Cette frontière est souvent artificielle. Un endpoint qui explose sa consommation mémoire sous charge est un sujet produit. Un cache incohérent qui affiche un tarif obsolète est un sujet produit. Un worker Messenger non idempotent qui duplique une facture est un sujet produit avec une cause technique.

La bonne unité de décision n’est donc pas la nature du ticket. C’est le coût du report.

Le Product Backlog doit absorber le passif, pas le masquer

Scrum est explicite: le Product Backlog est une liste émergente et ordonnée de tout ce qui est nécessaire pour améliorer le produit. Il est la source unique du travail de la Scrum Team. « Source unique » exclut mécaniquement le tableur technique tenu par le CTO, les notes de dette dans Notion et les alertes de sécurité perdues dans un canal Slack.

Dans une gestion de backlog agile Symfony, le défaut classique consiste à créer deux files séparées:

1. le backlog métier, visible, priorisé, lié aux objectifs commerciaux;

2. le backlog technique, invisible, rarement estimé, traité entre deux sprints.

Ce découpage produit une illusion de vélocité. Le produit semble livrer vite parce que le coût de fiabilisation est sorti du système de mesure. Puis les incidents, les régressions et les migrations urgentes reviennent dans le flux sous une forme plus coûteuse. Ils ne disparaissent pas. Ils changent seulement de libellé.

Le raffinement est le point de contrôle. C’est là qu’un élément flou devient une unité de travail exploitable: description, ordre, taille, dépendances, critères d’acceptation et effets de bord. Les développeurs qui feront le travail l’estiment. Le Product Owner aide à arbitrer la valeur et les compromis. Un CTO qui injecte une priorité sans rendre le risque observable fabrique de la résistance. Un Product Owner qui écarte un sujet technique sans en chiffrer l’impact fabrique de la dette cachée.

Une fiche de backlog technique utilisable doit répondre à des questions concrètes:

  • Quel flux de production est affecté: commande, authentification, synchronisation, facturation, recherche?
  • Quelle contrainte actuelle bloque: CPU, I/O, allocation mémoire, verrouillage SQL, typage faible, dépendance obsolète, absence de test?
  • Quel est le mode de défaillance: indisponibilité, corruption silencieuse, exposition de sécurité, délai de livraison, dette de support?
  • Quelle modification devient possible après traitement?
  • Quel signal permettra de vérifier que le travail a produit son effet: durée CI, taux d’échec, p95, mémoire maximale, nombre de modules concernés, vulnérabilités ouvertes?

L’ordre du Product Backlog n’est pas une estimation. Ce n’est pas non plus une promesse de date. C’est une séquence d’arbitrage. Une migration PHP coûteuse peut être placée avant une feature rentable si le coût de son report est structurellement supérieur: fenêtre de support qui se ferme, incompatibilité d’une dépendance critique, exposition sécurité ou saturation d’un composant.

Situation observéeDette réelleTraitement dans le backlogSignal de sortie
PHP 8.2 encore en productionFin de fenêtre de sécurité procheEpic de compatibilité, découpé par domaine et dépendanceDéploiement validé sur une branche supportée
CI Symfony instableDétection tardive des régressionsIsolation des tests, fiabilisation des fixtures, parallélisation contrôléeExécutions reproductibles et échecs exploitables
Service applicatif monolithiqueCouplage qui rend chaque changement transversalExtraction progressive derrière interfaces et tests de caractérisationModification locale sans régression latérale
Dépendances Composer non gouvernéesSurface d’attaque et compatibilité inconnuesMise à jour, verrouillage, analyse des changements indirectsAucune alerte bloquante à la fusion
Migrations Doctrine opaquesRisque de déploiement et de rollbackNormalisation des migrations et répétition sur environnement représentatifMigration exécutable dans le pipeline de livraison

Le conflit entre priorité métier et technique est mal posé lorsqu’il se limite à « livrer maintenant » contre « nettoyer avant ». La vraie question est: quelle capacité de livraison conserve-t-on après cette décision?

La Definition of Done élimine une partie de la fausse dette

Une équipe PHP accumule parfois de la dette parce que son incrément n’est pas réellement terminé. Le ticket est déplacé en colonne « terminé » dès que le comportement visible existe. Les tests, la migration, l’observabilité, les contrôles de sécurité et les cas limites sont reportés. Le travail non fini se déplace alors dans le futur sous le nom de dette.

La Definition of Done sert précisément à empêcher ce transfert comptable. Dans Scrum, un élément qui ne satisfait pas les mesures de qualité requises ne peut ni être livré ni être présenté à la Sprint Review. Il retourne dans le Product Backlog.

Sur un projet PHP complexe, une DoD utile ne doit pas être un manifeste de vingt pages. Elle doit contenir les contraintes qui empêchent un incrément de se comporter comme une dette fraîchement créée:

  • le code respecte une convention homogène, avec PSR-1 et PSR-12 comme socle de lisibilité;
  • les frontières applicatives sont testées au niveau adapté: unitaire pour la logique isolée, intégration pour les adapters, fonctionnel pour les flux HTTP critiques;
  • les migrations de schéma sont exécutées sur une base représentative, avec estimation de leur coût et de leurs verrous;
  • les messages asynchrones critiques disposent d’une stratégie d’idempotence, de retry et de traitement des échecs;
  • les dépendances modifiées sont analysées, y compris celles transitivement modifiées dans le fichier de verrouillage;
  • les métriques nécessaires au diagnostic existent avant la mise en production: logs structurés, traces, métriques de latence ou de consommation selon le flux.

PSR-12 n’accélère pas un endpoint. Ce n’est pas sa fonction. Son intérêt est plus froid: réduire la friction cognitive quand plusieurs auteurs interviennent sur le même code PHP. Dans un monolithe qui a traversé trois équipes et quatre années de produit, cette réduction est mesurable par le temps de revue, la capacité à localiser un effet de bord et le coût d’onboarding technique.

Si plusieurs équipes Scrum travaillent sur le même produit, elles doivent partager une même Definition of Done. Sinon, une équipe livre un module testé, instrumenté et compatible avec la cible PHP; une autre livre du code exécutable mais sans garantie opérationnelle. Le produit final prend alors la qualité de son maillon le plus faible.

Une fonctionnalité sans conditions de sortie techniques n’est pas livrée: elle est seulement déplacée.

Le cycle de support PHP est une donnée de roadmap

La modernisation PHP échoue souvent parce qu’elle est lancée sous la forme d’un projet massif: « passer à la dernière version ». Cette formulation est faible. Elle ne dit rien sur les extensions, Composer, les frameworks, les règles de typage, les images de conteneurs, les workers, les scripts de maintenance ni les comportements implicites modifiés par le runtime.

Le cycle PHP donne pourtant une contrainte claire. Chaque branche reçoit deux ans de support actif, puis deux ans de correctifs limités aux failles critiques. Après quatre ans, elle atteint sa fin de vie.

Au 17 juillet 2026, les branches soutenues sont PHP 8.2, 8.3, 8.4 et 8.5. Mais elles ne portent pas le même risque:

Branche PHPSupport sécurité jusqu’auLecture stratégique
PHP 8.231 décembre 2026Migration à planifier et engager immédiatement
PHP 8.331 décembre 2027Fenêtre exploitable, mais non confortable pour un SI complexe
PHP 8.431 décembre 2028Cible stable pour une modernisation progressive
PHP 8.531 décembre 2029Cible envisageable si l’écosystème applicatif est compatible

Le CTO ne doit pas décider seulement de la branche cible. Il doit ordonner les incompatibilités. Un audit efficace commence par le chemin d’exécution réel: front HTTP, workers Messenger, commandes planifiées, importateurs, batchs de clôture, processus de génération documentaire. Les scripts oubliés sont souvent ceux qui cassent après la mise à niveau parce qu’ils vivent hors du trafic principal et hors des tests.

La priorisation backlog technique projet PHP complexe devient alors beaucoup plus précise. On ne crée pas un ticket « migration PHP 8.4 ». On crée des éléments traçables:

1. Cartographier les contraintes Composer et les extensions PHP requises dans chaque environnement.

2. Exécuter les suites de tests sur la cible, puis isoler les dépréciations et ruptures par paquet ou domaine.

3. Traiter d’abord les chemins à forte criticité opérationnelle: paiement, identité, données personnelles, synchronisation.

4. Mettre à jour les images de build, les environnements CI et les workers avant de déclarer la migration complète.

5. Déployer de manière progressive avec métriques comparables: erreurs, p95, mémoire, nombre de jobs échoués, volume de retries.

La branche PHP n’est pas un détail d’infrastructure. Elle conditionne le niveau de risque acceptable pour le produit. Repousser la migration jusqu’à la fin du support transforme un programme de modernisation en intervention sous contrainte.

Sécurité: la dette qui ne négocie pas avec la roadmap

Le cadre SSDF du NIST ne demande pas une sécurité ajoutée après coup. Il recommande d’intégrer les pratiques de développement sécurisé dans chaque cycle existant afin de réduire les vulnérabilités livrées, de limiter l’impact de celles qui ne sont pas détectées et de traiter leurs causes racines.

Pour une équipe PHP, cette orientation a une conséquence directe: une vulnérabilité connue dans une dépendance ne relève pas d’un « lot sécurité » optionnel. C’est une condition de livraison. La dépendance indirecte compte autant que la dépendance déclarée explicitement. Composer verrouille un graphe. Le risque est dans le graphe, pas uniquement dans le premier niveau.

Un contrôle de revue de dépendances à chaque pull request permet d’identifier les modifications de packages et les vulnérabilités connues, y compris dans les fichiers de verrouillage. S’il est requis, il peut bloquer la fusion. C’est une bonne friction. Elle coûte quelques minutes au bon endroit plutôt que plusieurs jours d’investigation après déploiement.

Il faut néanmoins éviter le réflexe simpliste: « mettre à jour toutes les dépendances ». Une mise à jour sans stratégie peut déplacer le risque vers une rupture fonctionnelle, un changement de sérialisation, une modification de comportement HTTP ou une régression de performance. Le backlog doit rendre le compromis visible: vulnérabilité, version corrigée, compatibilité applicative, scénario de régression, fenêtre de déploiement.

Les indicateurs de santé du code PHP ne se résument pas à un score de quality gate. Les plus utiles relient une propriété technique à un effet de production:

  • durée médiane et percentile élevé du pipeline CI;
  • taux de flakiness des tests;
  • temps de restauration après incident sur un composant;
  • densité de dépendances obsolètes ou vulnérables;
  • proportion de flux critiques dotés de tests de non-régression;
  • consommation mémoire et latence sur les endpoints ou workers à volume élevé;
  • âge de la branche PHP et distance à sa fin de support;
  • taux d’échec des jobs asynchrones, retries et messages en échec.

Aucune de ces métriques ne produit une priorité automatique. Elles fournissent un signal. La décision reste architecturale: quel composant, quel risque, quelle séquence, quel coût de report.

Refactoriser ou réécrire: le test de la frontière

La réécriture totale est le fantasme récurrent de la dette technique. Elle promet une base saine, un typage strict, une architecture hexagonale nette, une version PHP récente et l’élimination des compromis historiques. Elle promet beaucoup parce qu’elle permet de ne plus regarder le système actuel.

En pratique, une réécriture remplace un passif connu par un risque de livraison moins connu. Les comportements implicites, les correctifs accumulés, les règles métier mal documentées et les exceptions de production ne disparaissent pas. Ils doivent être redécouverts. Parfois, ils sont oubliés.

Le bon critère n’est pas l’âge du code. C’est l’existence de frontières exploitables.

La refactorisation incrémentale est adaptée lorsque le système possède encore des zones isolables: un module de facturation, un adapter de paiement, un moteur de recherche, une couche d’intégration ERP. On peut poser des tests de caractérisation, encapsuler l’existant, extraire progressivement et mesurer la réduction du couplage.

La réécriture devient défendable lorsque l’architecture interdit durablement ces mouvements: modèle de données irréconciliable avec les contraintes actuelles, runtime non maintenable, dépendances fondamentales bloquées, impossibilité de sécuriser ou de tester des flux critiques sans recréer leurs fondations. Même dans ce cas, elle doit être découpée par capacité métier et fonctionner en parallèle de l’existant. Un basculement global n’est pas une stratégie; c’est une concentration de risque.

Le CTO doit formuler cette décision en termes de flux, pas de préférence technologique. « Remplacer Symfony ancien par Symfony récent » n’est pas un objectif produit. « Réduire le temps et le risque de modification du calcul tarifaire en isolant son domaine et en supprimant les dépendances non supportées » en est un.

Le choix radical n’est donc pas de tout jeter. C’est de refuser les tickets techniques sans impact explicité, les fonctionnalités sans DoD et les versions PHP sans horizon de support. Un backlog qui contient réellement le produit contient aussi ses contraintes d’exécution.

Verdict: la gestion de la dette technique doit vivre dans le Product Backlog, avec la même discipline de raffinement que le métier. À utiliser en production.

Questions fréquentes

Pourquoi la dette technique ne doit-elle pas être gérée dans un backlog séparé ?
Gérer deux files séparées crée une illusion de vélocité et masque le coût réel de fiabilisation du produit, ce qui finit par rendre les incidents et migrations plus coûteux à traiter.
Comment savoir si une dette technique est devenue prioritaire ?
La priorité ne dépend pas de la nature du ticket, mais du coût du report, notamment lorsque la dette bloque une montée de version PHP, expose à des vulnérabilités ou rend une évolution métier risquée.
Que doit contenir une Definition of Done pour limiter la dette ?
Elle doit inclure des contraintes concrètes comme le respect de conventions de code, des tests adaptés aux frontières applicatives, la gestion de l'idempotence des messages asynchrones et l'existence de métriques de diagnostic.
Comment aborder une migration PHP complexe ?
Il faut éviter le projet massif et privilégier une approche par étapes : cartographier les dépendances, isoler les ruptures par domaine, traiter les chemins critiques en priorité et déployer progressivement avec des métriques comparables.
Quand est-il justifié de réécrire une application plutôt que de la refactoriser ?
La réécriture est défendable uniquement lorsque l'architecture actuelle interdit toute isolation de modules, rendant impossible la sécurisation ou le test des flux critiques sans reconstruire les fondations.