Lead technique chez Greenflex · 2012-2017
BeeGreen, migrer sans tout arrêter
BeeGreen est un SaaS multi-clients pour suivre un parc d’équipements et gérer des achats responsables. Pendant cinq ans, j’ai fait passer son code PHP de 2009 vers Symfony, page par page, pendant que les évolutions demandées par les clients continuaient de sortir.
- 5 ansde migration progressive, sans geler les livraisons
- 1ercontributeur du projet, dans une équipe de 3 à 7 développeurs
- 181tables décrites avec Doctrine, sur une base restée en service
- ≈ 230merge requests relues et intégrées
Le contexte
Un SaaS pour les grandes entreprises
Greenflex accompagne les entreprises dans leur transition environnementale. Son offre logicielle, BeeGreen, réunit plusieurs applications utilisées par de grands comptes : le suivi de milliers d’équipements, de leur achat à leur fin de vie, et un guide d’achats responsables avec ses circuits de validation.
J’ai rejoint le projet en 2012 et j’en suis devenu le lead technique. En plus du développement, j’étais l’interlocuteur direct des clients, presque chaque jour, pour leurs demandes et leurs évolutions.
Une mission de 2012 à 2017 : les outils ont bien changé depuis, la méthode reste celle que j’applique aujourd’hui.
Le point de départ
Un code de 2009 qui avait grandi sans fondations
À mon arrivée, l’application comptait près de 70 000 lignes de PHP, sans framework commun ni test. Les pages faisaient tout à la fois, les identifiants de la base étaient écrits en dur et le schéma évoluait à coups de scripts SQL appliqués à la main.
Tout réécrire aurait gelé les évolutions pendant des mois, avec le risque de casser une application utilisée chaque jour par les clients. J’ai choisi de la transformer progressivement.
Extrait d’une page de 2012
if (!$_acl->isAllowed('item', 1)) {
include('site-403.phtml'); // ① droits vérifiés dans la page
} else {
include('include.php');
if (isset($_GET['i'])) { // ② paramètres lus directement
foreach ($_GET['i'] as $pk_item) {
$item[$pk_item] = $db->getItem(array( // ③ méthode « magique »
'fk_customer_item' => $_SESSION['company']['pk'],
'pk_item' => $pk_item,
'fk_status_item !=' => 283, // ④ identifiant en dur
));
}
}
// … puis 750 lignes de SQL, de logique et de HTMLgetItem n’existait nulle part : une méthode de 450 lignes devinait la table à interroger d’après le nom de la fonction appelée.La méthode
Faire cohabiter l’ancien et le nouveau
Dès 2012, j’ai démarré un projet Symfony à côté de l’existant, en décrivant avec Doctrine la base de données en place. Puis j’ai fait de Symfony la porte d’entrée unique de l’application.
Les pages migrées sont servies par Symfony. Les autres passent par un contrôleur de compatibilité qui exécute l’ancien code et l’affiche dans le cadre commun. Connexion, session et droits sont partagés : un seul identifiant pour toutes les applications, au lieu des appels techniques d’une application à l’autre.
Les pages migraient au fil des besoins : la liste des équipements, leur fiche, l’accueil… Et quand une réécriture était trop risquée, l’ancien code restait en place, nettoyé, jusqu’au bon moment.
Le détail technique, sur le blogLe navigateur demande une page, par exemple /items
Le routeur Symfony cherche une route qui correspond
Page déjà migrée
- Route Symfony dédiée
- Contrôleur, formulaires et Doctrine
- Template Twig
Page pas encore migrée
- Route « attrape-tout », déclarée en dernier
- Contrôleur de compatibilité : exécute l’ancienne page et capture son affichage
- Réponse Symfony, avec le menu commun
Partagé par les deux
- Connexion unique
- Session et droits
- Données de chaque client isolées
- Base PostgreSQL
Ce qui a changé
Le socle transformé, livraison après livraison
Personne ne me demandait de reprendre le code ni l’interface : ce travail de fond n’avait pas de budget à part. Je l’ai mené par conscience professionnelle, en profitant de chaque fonctionnalité livrée pour assainir le code qu’elle touchait et faire évoluer l’interface.
Une base de données décrite et versionnée
En 2012
Des tables génériques, des évolutions de schéma appliquées à la main et des identifiants de la base écrits en dur dans le code.
En 2017
Les tables décrites avec Doctrine, des migrations versionnées, des clés étrangères, et des constantes à la place des identifiants.
Des évolutions de schéma versionnées, rejouées à l’identique sur chaque environnement.
Des pages qui ne font plus tout
En 2012
Des pages de plus de 1 000 lignes qui mêlaient contrôle d’accès, lecture directe des paramètres, SQL, logique métier et HTML.
En 2017
Contrôleurs, formulaires et templates Twig séparés. Dans l’ancien code encore en place, les requêtes extraites dans des classes et les vues découpées.
La liste des équipements passée de 1 293 à 477 lignes, avant même sa migration.
Une interface commune
En 2012
Des tableaux de mise en page, des styles écrits dans le HTML et une interface différente d’une application à l’autre.
En 2017
Un framework CSS, des feuilles de style organisées et bien nommées, un menu et une mise en page partagés, et une interface que j’ai fait évoluer au fil des livraisons.
Les anciennes pages affichées dans le même cadre que les nouvelles : l’utilisateur ne voit pas la différence.
Un projet outillé
En 2012
Aucun framework commun, aucun test, une configuration et des mises en production faites à la main.
En 2017
Symfony, des conventions de code (PSR, PHP CS Fixer), des tests Behat, une configuration par environnement, un environnement de développement reproductible et un déploiement automatisé.
Une mise en production en une commande, avec un environnement de recette par client.
Cinq ans de migration
Une transformation par étapes
2012
Premiers pas
Grand nettoyage des pages les plus utilisées, index en base, migrations de schéma. En juillet, le projet Symfony démarre, sur une version du framework encore en bêta.
2013
Faire cohabiter
Le contrôleur de compatibilité permet de servir l’ancien code depuis Symfony. Le suivi du parc et les alertes passent sur le nouveau socle.
2014
Un seul produit
Connexion unique pour toutes les applications, nouvelle interface graphique, API du catalogue sur Elasticsearch, déploiement automatisé.
2015
Une équipe outillée
Revues de code par merge request, montées de version du framework, suppression des modules devenus inutiles.
2016-2017
Le relais
L’équipe poursuit la migration (nouvelles versions de Symfony, découpage front, back-office et API). De mon côté : intégration de Qlik Sense et documentation.
Ce que j’ai construit
Des applications complètes, de la conception à la mise en production
La migration ne s’est jamais faite au détriment du produit. En parallèle, j’ai conçu et développé plusieurs applications entières de la suite, de la base de données à l’interface, et de nombreuses fonctions pour les autres.
Des achats responsables
Un catalogue de matériel noté sur des critères environnementaux, des kits, un panier et des demandes d’achat avec un circuit de validation propre à chaque client.
Le suivi d’un parc d’équipements
Une fiche par équipement, des mouvements unitaires ou en masse, un historique, des champs personnalisés par client, des imports et des exports.
Un portail commun
Un tableau de bord à widgets et des applications activées client par client, comme dans un magasin d’applications.
Un SaaS multi-clients
Les données de chaque client isolées automatiquement à chaque requête, des droits par fonction et une interface en six langues.
Des alertes sur mesure
Des alertes et des rapports envoyés par e-mail, configurés à partir de requêtes sur les données du client.
Données et tableaux de bord
Une API du catalogue avec recherche à facettes sur Elasticsearch, puis l’intégration de tableaux de bord Qlik Sense avec connexion unique.
PHP · Symfony · Doctrine ORM et Migrations · PostgreSQL · Twig · Elasticsearch · Behat · Qlik Sense
Au-delà du code
Lead technique, entre les clients et l’équipe
Au contact des clients
En lien presque quotidien avec les clients : demandes, réclamations, cadrage des évolutions, et des améliorations que je leur proposais.
Embarquer l’équipe
Conventions, revues de code et migrations outillées, pour une équipe passée de 3 à 7 développeurs, dont certains habitués à d’autres façons de travailler.
Accepter le provisoire
Laisser dans l’ancien code ce qu’il était trop risqué de réécrire, plutôt qu’une migration parfaite qui casse ce qui marche.
Transmettre
Documentation d’installation et de traduction, présentation de l’état de la migration aux nouveaux arrivants, qui l’ont poursuivie.
Et pour vos projets
Ce que j’en retiens aujourd’hui
Pas besoin de tout réécrire
Une migration page par page apporte de la valeur pendant toute la transition, là où une refonte complète fait attendre des mois.
Le socle fait partie de chaque fonctionnalité
Sans budget dédié au travail de fond, j’ai assaini le code touché par chaque livraison. C’est ce qui a permis d’avancer pendant cinq ans.
Les outils changent, la méthode reste
Symfony 2 a bien vieilli depuis, mais j’applique la même approche aujourd’hui à des applications React et Symfony récentes.
À la même époque, chez Greenflex, j’ai aussi reconstruit de zéro un guide d’achats responsables : lire l’étude de cas EcoGuide IT.