Pendant trois ans chez Efipeek, j’ai principalement travaillé sur la plateforme e-commerce de Carter-Cash. Du besoin au suivi en production, j’intervenais sur le backend Symfony et les interfaces.
Avec mon associée, nous avons imaginé un service de location de tenues vintage et upcyclées, principalement sous forme de box par abonnement. J’étais impliqué dans l’ensemble du projet et j’assurais le développement web du premier MVP. Accompagnés par EuraTechnologies et 1Kubator, nous avons mené le projet jusqu’à ce premier MVP, avant de l’arrêter sans mise sur le marché.
Du concept au fonctionnement du service
Construire l’offre à deux
La Bouclup’ était un projet mené à deux autour de la location de tenues vintage et upcyclées, principalement sous la forme d’une box par abonnement. L’idée initiale de louer des vêtements s’est progressivement élargie vers une réflexion plus globale sur notre façon de nous habiller.
Avec mon associée, je travaillais sur l’ensemble du projet : cadrer l’offre, définir le fonctionnement du service et traduire ces décisions dans un premier MVP, dont j’assurais le développement web.
Faire fonctionner le service
Le cycle d’une tenue concentrait une grande partie des questions du projet :
Location
Retour
Remise en stockVérification & nettoyage
Disponibilité
À terme, la date de retour prévue et le temps de préparation devaient permettre d’anticiper la prochaine disponibilité d’une tenue et sa réservation pour une future box.
Cet exemple rendait très concret le lien entre une règle produit et sa mise en œuvre technique.
Structurer le projet avec les incubateurs
Deux programmes d’incubation nous ont accompagnés dans cette maturation :
EuraTechnologiesmai — juillet 2021
1Kubatoravril — juin 2022
Ces programmes nous ont aidés à cadrer le concept, travailler le business model, confronter nos hypothèses et préparer le pitch et le go-to-market.
Un réflexe durable : relier produit et technique
~/efipeek/Carter-Cash
Trois années sur le projet Carter-Cash
Une sélection de mes réalisations chez Efipeek. Choisissez un sujet pour découvrir le besoin, ma contribution et les choix techniques.
Carter-Cash souhaitait permettre à ses clients d’effectuer leurs retours en ligne. Le besoin initial définissait l’objectif et envisageait l’utilisation d’un outil logistique, mais le parcours, ses règles et sa faisabilité technique restaient encore à préciser.
En collaboration avec la cheffe de projet côté Carter-Cash, relais du métier et du service client, j’ai exploré l’API Woop et confronté les premières intentions aux contraintes du site. J’ai identifié ce qui était réalisable, proposé les adaptations nécessaires et complété les règles de gestion avant de concevoir et développer le parcours web.
Rendre simples des retours complexes
Dans le cadre d’un retour, une commande ne correspondait pas toujours à une seule livraison. Les produits pouvaient être expédiés séparément, reçus à des dates différentes ou répartis entre plusieurs colis. Les règles variaient également selon que le client souhaitait effectuer son retour à distance ou directement en magasin.
J’ai traduit ces situations en un parcours capable d’identifier les produits éligibles, de les regrouper selon leur livraison d’origine et de créer autant de retours et d’étiquettes que nécessaire. L’interface adapte chaque étape au choix du client : la sélection d’un magasin apparaît uniquement pour un retour en magasin, tandis que les contraintes liées aux livraisons partielles et aux commandes multi-colis restent prises en charge par le système.
Les choix techniques
Backend Symfony
Les contrôleurs, formulaires, validations personnalisées, services métier et entités Doctrine assurent la création des retours, le contrôle des produits sélectionnés et leur regroupement selon la livraison d’origine.
API et traitements asynchrones
Les échanges avec Woop reposent sur des services HTTP dédiés, avec gestion de l’authentification, des erreurs et de leur journalisation. Lorsque l’étiquette de transport n’était pas immédiatement disponible, Symfony Messenger permettait de poursuivre sa récupération sans bloquer le parcours du client.
Interfaces et documents
Les écrans Twig, complétés par JavaScript, jQuery et SCSS, adaptent dynamiquement les choix présentés. Les bons et étiquettes de retour sont générés à partir de templates, convertis en PDF puis mis à disposition du client ou transmis avec les communications concernées.
Tests et fiabilité
Des tests PHPUnit couvrent notamment les services métier, les règles de regroupement et les objets échangés avec les API. Ils complètent les cahiers de tests, la recette fonctionnelle et le suivi des erreurs après la mise en production.
Un même service, quel que soit le point d’entrée
Le parcours de retour en ligne ne pouvait pas répondre à toutes les situations. Un client pouvait rencontrer un problème sur le site ou avoir besoin d’être accompagné directement par le service client pour créer son retour.
Dans une évolution ultérieure, j’ai conçu des API dédiées à l’outil interne utilisé par les conseillers. Elles permettent de consulter les informations nécessaires et de créer un retour depuis leur propre interface, en réutilisant les services et les règles métier du parcours web. Client et conseiller suivent donc les mêmes règles, même si le retour n’est pas créé depuis la même interface.
Une responsabilité jusqu’en production
J’ai accompagné les évolutions du parcours de retour jusqu’à leur mise en service : préparation des jeux et cahiers de tests, recette côté client et service client, vérification des échanges avec les systèmes concernés et suivi de la mise en production.
Une fois la solution en ligne, les incidents et demandes d’évolution liés aux retours m’étaient systématiquement confiés. J’en analysais les causes, réalisais les corrections nécessaires et adaptais les règles en collaboration avec la gestion de projet Carter-Cash. Je suis ainsi resté le référent technique de ce périmètre jusqu’à mon départ.
Relier les commandes à Datahub
Transformer un besoin d’échange en solution technique
Carter-Cash souhaitait transmettre ses commandes à Datahub. Derrière cet objectif se trouvait un ensemble de données réparties dans le modèle existant : commande, expéditions, client, magasin, produits, tarifs, historique et modes de livraison.
J’ai analysé ces différentes sources, identifié les informations nécessaires et défini leur transformation vers le format attendu par Datahub. J’ai ensuite conçu le parcours complet de l’échange, depuis l’événement déclencheur dans la vie de la commande jusqu’à la construction et à l’envoi authentifié des données.
Faire circuler les données sans bloquer l’application
L’envoi vers Datahub ne devait pas être exécuté directement pendant une action utilisateur ou une transition métier importante. Une réponse lente ou une indisponibilité du service externe aurait alors pu bloquer inutilement le fonctionnement du site.
J’ai choisi une architecture asynchrone basée sur Symfony Messenger. Lorsqu’une commande atteint l’état concerné, un message est placé dans une file dédiée. Un worker le traite ensuite indépendamment, recharge les informations nécessaires et effectue l’envoi vers Datahub. L’action qui a déclenché l’échange peut donc se terminer normalement, même si le service externe répond lentement ou devient indisponible.
Ne jamais laisser un flux échouer silencieusement
Un envoi asynchrone vers Datahub peut échouer loin du parcours de commande qui l’a déclenché. Sans mécanisme dédié, les données d’une commande pourraient ne jamais parvenir au service sans que l’erreur soit immédiatement visible ou facilement reproductible.
J’ai mis en place un suivi persistant des échecs pour chaque expédition concernée : statut retourné, nombre de tentatives et date du dernier essai. Des logs dédiés conservent également l’origine du déclenchement et le contexte utile au diagnostic. Lorsqu’un nouvel envoi aboutit, l’échec précédemment enregistré est automatiquement levé.
Rendre les intégrations visibles et actionnables
Le suivi des envois vers Datahub ne pouvait pas reposer uniquement sur des logs : leur consultation serait restée réservée aux développeurs et les incidents auraient été difficiles à retrouver dans la durée. J’ai donc proposé une interface dédiée dans le back-office, où les traitements en échec deviennent directement visibles.
Cette vue rassemble les informations utiles au diagnostic : commande concernée, expédition, statut, nombre d’échecs et dernier essai. Une action permet de relancer manuellement l’envoi en réutilisant le même service que le traitement automatique. En cas de succès, l’erreur disparaît ; sinon, son suivi est actualisé.
Les choix techniques
Construction et envoi
Un builder transforme les commandes et leurs expéditions dans le format attendu. Un client basé sur Symfony HttpClient obtient un jeton d’accès, envoie les données et interprète la réponse du service externe.
Traitement asynchrone
Une évolution de l’état de la commande déclenche un message Symfony Messenger. Un handler recharge ensuite l’expédition et délègue l’envoi à un service commun, utilisé également lors des reprises manuelles.
Erreurs et exploitation
Doctrine conserve les échecs et leurs tentatives, tandis qu’un canal Monolog dédié fournit le contexte technique. Sonata Admin expose ces informations et permet de relancer un envoi sans dupliquer la logique métier.
Autres intégrations
J’ai également exploré l’API Woop pour remplacer une ancienne source de points relais. J’ai identifié les données nécessaires : la localisation, l’identité du point, les horaires et les catégories de colis acceptées, puis réalisé l’essentiel de leur intégration dans le parcours de livraison.
Reprendre l’authentification et réconcilier les comptes
Devenir référent d’un système existant
Auth0 assurait déjà l’authentification des clients lorsque j’ai rejoint le projet. Je n’avais pas participé à sa mise en place initiale, mais les incidents liés aux comptes et à la connexion m’ont progressivement amené à intervenir régulièrement sur ce périmètre.
J’utilisais la console Auth0 pour examiner les identités, comprendre les blocages et débloquer les situations rencontrées par les utilisateurs. À force de prendre en charge ces demandes et de relier le comportement du service à celui de l’application Symfony, j’en suis devenu le référent opérationnel au sein de l’équipe.
Reprendre et finaliser l’intégration Symfony
À la suite de plusieurs sujets de sécurité, l’équipe avait décidé d’intégrer le bundle Symfony officiel d’Auth0. Un autre développeur avait commencé ce chantier, mais son départ a laissé une première version encore incomplète et partiellement adaptée au fonctionnement du site.
J’ai repris cette base, identifié les parcours et cas particuliers restant à traiter, puis adapté l’intégration au socle existant. J’ai complété les éléments manquants, supprimé les anciens mécanismes devenus inutiles et mené le chantier jusqu’à sa version finale, prête à être déployée.
Sécuriser la création et le cycle de vie des comptes
Ajouter une règle de sécurité à l’authentification ne concernait jamais uniquement la connexion. La validation des comptes, par exemple, pouvait affecter les clients historiques, le parcours après inscription, les messages d’erreur ou encore le renvoi des communications nécessaires.
J’ai étudié ces impacts et formalisé les décisions métier à prendre avant l’activation de nouvelles protections. J’ai ensuite développé les actions et triggers Auth0 ainsi que les points d’entrée Symfony nécessaires aux contrôles retenus lors de l’inscription. J’ai également adapté la gestion des sessions et de la déconnexion afin de conserver un cycle de vie cohérent entre Auth0 et l’application.
Réconcilier les identités sans perdre leur historique
L’évolution de l’authentification nécessitait de traiter des clients possédant plusieurs comptes pour une même identité. Une simple suppression aurait fait disparaître une partie de leur historique ou laissé des relations incohérentes entre l’application et Auth0.
J’ai conçu et développé un traitement capable de détecter les doublons, de sélectionner le compte principal selon son activité et d’y transférer les commandes, paniers, adresses, véhicules et préférences utiles. Les comptes devenus inutiles sont ensuite supprimés localement et dans Auth0 lorsque nécessaire. Des verrous, limites d’exécution, journaux dédiés et synthèses permettent de contrôler chaque fusion.
Les choix techniques
Authentification Symfony
Le bundle s’intègre au composant Security à travers des providers, un authenticator, un point d’entrée et des subscribers dédiés. J’ai adapté ces composants aux différents profils de l’application et aux parcours existants, puis supprimé les anciens mécanismes devenus inutiles.
Cycle de vie Auth0
Les actions et triggers Auth0 communiquent avec des contrôleurs Symfony pour appliquer les contrôles nécessaires lors de l’inscription. La gestion de la déconnexion, des sessions et des identifiants a également été adaptée afin de conserver un état cohérent entre Auth0 et l’application.
Migration des comptes
Une commande Symfony s’appuie sur Doctrine et l’API Auth0 pour fusionner les comptes en doublon, transférer leurs données liées et supprimer les identités devenues inutiles. Des verrous, limites d’exécution et journaux dédiés sécurisent cette opération.
Mise en production
J’ai participé directement à la cellule de mise en production, dans une fenêtre préparée pour limiter l’impact sur les clients. Le nouveau socle Auth0, les actions nécessaires et la fusion des comptes ont été vérifiés avant la réouverture complète du parcours.
Retrouver son panier entre appareils
Retrouver son panier, quel que soit l’appareil
À l’origine, le panier reposait principalement sur la session du navigateur. Après une période d’inactivité, une reconnexion ou le passage sur un autre appareil, un client pouvait perdre son panier en cours et devoir recommencer son parcours.
J’ai refondu son initialisation afin de ne plus dépendre uniquement de cette session. Lorsqu’un client se connecte, le système peut retrouver son dernier panier encore valide dans la base de données et le réinjecter dans son parcours. Si aucun panier ne peut être repris, un nouveau panier est créé selon le contexte de l’utilisateur.
Réconcilier panier, identité et magasin
Lors d’une connexion, plusieurs états pouvaient se rencontrer : un panier anonyme déjà présent dans la session, un ancien panier encore actif dans la base de données ou un magasin différent de celui précédemment associé au client. Les comptes vendeurs nécessitaient également un comportement distinct de celui des clients classiques.
J’ai structuré les règles permettant d’arbitrer ces situations. Un panier anonyme peut être rattaché au client qui vient de se connecter ; en son absence, le dernier panier valide est recherché. Le magasin du panier et celui de la session sont ensuite synchronisés selon le contexte. Un client retrouve ainsi son environnement d’achat, tandis que les usages vendeurs restent isolés dans un panier dédié.
Comprendre les parcours entre appareils
La continuité du panier permettait au client de poursuivre son achat, mais elle ouvrait également une nouvelle question : comprendre comment un parcours commencé sur mobile, desktop ou tablette évoluait jusqu’à la commande.
J’ai conçu un suivi des appareils utilisés aux moments clés du panier : création, changement d’appareil, entrée dans le paiement et finalisation. Des règles garantissent que seules les transitions cohérentes sont conservées, puis une vue agrégée permet d’analyser les principaux parcours entre appareils. J’ai accompagné cette fonctionnalité jusqu’à sa mise en production.
Les choix techniques
Initialisation du panier
Un service Symfony central orchestre les différents cas : panier déjà en session, association d’un panier anonyme après connexion, récupération du dernier panier valide ou création d’un nouveau panier. Un repository Doctrine sélectionne le panier réutilisable selon l’état de la commande associée.
Identité et contexte magasin
Les règles distinguent explicitement les clients et les vendeurs. Le panier conserve également son magasin afin de réconcilier, lors d’une reprise, le contexte enregistré avec celui de la nouvelle session.
Suivi événementiel
Le composant EventDispatcher découple la journalisation des appareils du parcours métier. Des événements sont déclenchés aux étapes significatives, puis un service applique les règles d’intégrité, détecte le type d’appareil et enregistre uniquement les transitions pertinentes. Une vue SQL agrège ensuite les parcours finalisés.
Tests et fiabilité
Les principaux scénarios d’initialisation sont couverts par PHPUnit : création, association après connexion, récupération depuis la base et traitement spécifique des vendeurs. Les dépendances liées à la sécurité, à la session, aux repositories et à Doctrine sont simulées afin d’isoler les règles métier.
Faire évoluer la recherche et la disponibilité
Aider le client à trouver le bon produit
Choisir un pneu ne repose pas uniquement sur ses dimensions. Les indices de charge et de vitesse, la saison ou encore la technologie Runflat déterminent les produits compatibles, avec des règles différentes entre pneus été, hiver et quatre saisons.
Dans le cadre d’une évolution menée en équipe, j’ai contribué à traduire ces règles en filtres Algolia : comparaison des indices, adaptation des critères selon la saison et gestion des produits compatibles malgré certaines différences avec la recherche initiale. J’ai également travaillé sur les informations affichées pour expliquer ces écarts, puis vérifié la cohérence des résultats à l’aide de requêtes Algolia et d’un cahier de tests fonctionnels.
Afficher une disponibilité cohérente
Sur un site relié à des magasins et à plusieurs sources d’approvisionnement, la disponibilité ne se résume pas à une quantité en stock. Elle dépend notamment du magasin sélectionné, des possibilités de commande, des caractéristiques du produit et du choix entre retrait et livraison.
J’ai contribué à faire évoluer ces règles et leur affichage dans les différentes étapes du parcours. J’ai adapté l’utilisation des nouvelles données de stock, de prix et de disponibilité fournies par Algolia. Sur une évolution ultérieure, j’ai également centralisé dans un service Symfony les modes de livraison réellement autorisés et ajouté leur validation côté serveur, afin de conserver des choix cohérents jusqu’au panier.
Faire évoluer un catalogue historique
Le catalogue s’était construit au fil des années autour de plusieurs sources. Selon le type de produit, le pays ou l’écran consulté, les informations pouvaient provenir d’Algolia, de services catalogue externes ou de la base de données. Une évolution d’attribut pouvait donc affecter la recherche, les prix, la disponibilité, le suivi analytique ou l’affichage final.
Mes interventions consistaient souvent à reprendre une évolution déjà engagée, identifier ses répercussions dans le code et adapter les différentes couches concernées. J’ai notamment travaillé sur de nouveaux attributs Algolia, les comportements propres aux différents pays, les tris et la pagination, les messages de disponibilité et les valeurs de repli lorsque certaines données manquaient. Chaque modification demandait donc de vérifier plusieurs sources et plusieurs écrans, pas seulement l’endroit où le besoin avait été signalé.
Les choix techniques
Recherche et filtrage
Les services Symfony construisent les filtres et facettes Algolia selon les caractéristiques du produit, le véhicule, le magasin et le pays. J’ai fait évoluer ces règles ainsi que les mécanismes de tri et de pagination, notamment pour le Tire Selector et les listings produit.
Affichage et interactions
Les résultats, filtres actifs et suggestions d’autocomplétion sont rendus avec InstantSearch.js, JavaScript et Twig. Les composants adaptent les prix, la disponibilité, les informations de compatibilité et les actions proposées selon le contexte utilisateur.
Données et disponibilité
Le parcours combine des données issues d’Algolia, d’API de catalogue et de Doctrine. J’ai adapté leurs correspondances, prévu des valeurs de repli lorsque certains attributs étaient absents et centralisé certaines règles de disponibilité dans des services Symfony réutilisables et contrôlés côté serveur.
Validation et suivi
Les résultats étaient vérifiés à partir de requêtes Algolia, de cahiers de tests fonctionnels et de recettes sur le site. Les logs et le suivi analytique complétaient le diagnostic lorsqu’un listing ou une recherche ne se comportait pas comme prévu.
Anticiper les problèmes de fiabilité
Anticiper un risque avant qu’il ne devienne un incident
Les références des commandes, expéditions et produits reposaient sur une portion d’identifiants globaux. Avec l’augmentation du volume, ce mécanisme pouvait réutiliser une ancienne valeur et provoquer une collision. Un correctif temporaire avait repoussé le problème sans supprimer sa cause.
À partir d’une question soulevée en daily, j’ai analysé le fonctionnement existant et les nombreuses briques dépendantes de ces références. J’ai comparé plusieurs stratégies, présenté leurs compromis puis recommandé une architecture fondée sur des compteurs dédiés par magasin, usage et période. Après validation, j’ai réalisé la solution retenue avec les protections nécessaires contre les créations concurrentes.
Entretenir les données avant qu’elles ne s’accumulent
J’ai constaté que de nombreux paniers devenus inutiles restaient durablement en base de données. J’ai fait remonter ce constat puis proposé et conçu leur nettoyage, sans demande initiale du client. J’ai ensuite conçu un dispositif comparable pour traiter un besoin récurrent concernant les anciennes redirections.
Les deux traitements distinguent les données encore utiles de celles qui peuvent être supprimées, puis interviennent par lots afin de limiter leur impact. Verrous, durées maximales, logs, historiques et synthèses par email permettent de suivre chaque exécution, tandis qu’une planification automatise leur entretien dans la durée.
Faire de la non-régression une responsabilité
Sur une plateforme e-commerce, une régression peut empêcher une recherche, une connexion ou une commande d’aboutir. Les règles étant nombreuses et souvent liées entre elles, la vérification du comportement faisait partie intégrante de mes développements.
Je préparais des cahiers de tests détaillés, réalisais les recettes fonctionnelles et ajoutais des tests PHPUnit sur les services et règles métier sensibles. Avec Vincent, j’ai également participé à la création d’un projet Cypress couvrant de nombreux parcours end-to-end. Cette expérimentation était lancée manuellement selon les besoins ; elle ne constituait pas encore une chaîne de tests systématique ou entièrement automatisée.
Les choix techniques
Génération des références
Pour remplacer l’ancien mécanisme basé sur une portion des identifiants globaux, j’ai créé un service de génération commun aux commandes, aux expéditions et aux produits commandés. Chaque magasin possède ses propres compteurs, séparés par type de référence et par période.
Gestion des créations simultanées
Doctrine garantit l’unicité de chaque compteur. Lors de créations simultanées, un verrou optimiste détecte qu’une valeur vient déjà d’être utilisée : le service recharge alors le compteur et retente la génération avec le numéro suivant.
Purge des données
Les paniers et redirections obsolètes sont supprimés par des commandes Symfony Console. Les traitements utilisent DBAL pour intervenir par lots, des verrous pour empêcher deux exécutions simultanées et une durée maximale pour limiter leur impact sur la base.
Suivi et exploitation
Des logs, alertes et synthèses par email permettent de contrôler les exécutions. Une interface Sonata rend l’utilisation des compteurs consultable, tandis qu’une commande dédiée prépare les références d’une nouvelle période ou initialise celles d’un nouveau magasin.
Tests et outillage
PHPUnit couvre notamment la génération des références, les créations concurrentes et la purge des redirections. Des commandes de génération de données facilitent également les recettes volumineuses hors production.