Souleymane Diallo

Développeur full-stack

Applications métier et SaaS

Mon but : que ce qui arrive en production soit ce qui avait été demandé.

Sept applications mises en ligne

Le code de six d'entre elles est public. SafeOttawa et ZenGadget restent ouvertes, sans compte.

ProfMatch : les propositions d'affectation classées par score, avec leur rang et les actions de validation.
Analyse d'un CV en 23 secondes, en tâche de fond, sans figer l'interface.

ProfMatch

Défi Informatique · équipe de 4

Affecte les professeurs aux cours à partir de leurs CV, avec une justification lisible pour chaque proposition.

  • Next.js
  • FastAPI
  • PostgreSQL
  • Celery
  • Docker
MediPlan : le flux clinique du jour, chaque rendez-vous avec son heure, son médecin, son motif et son statut.
Double réservation rendue impossible sous requêtes concurrentes, vérifiée par 205 tests.

MediPlan

Projet intégrateur · équipe de 3

Rendez-vous multi-cliniques : la réception et les patients réservent les mêmes créneaux, sans double réservation possible.

  • NestJS
  • Angular
  • PostgreSQL
  • TypeORM
  • Docker
SafeOttawa : un itinéraire piéton calculé entre deux intersections d'Ottawa, sa part de risque, sa distance et le nombre de zones traversées.
94 406 collisions converties en itinéraires classés par risque sur 1 895 zones.

SafeOttawa

Lauréat InnovaCode 2026

Itinéraires les plus sûrs pour piétons et cyclistes, à partir des données ouvertes de la Ville d'Ottawa.

  • Next.js
  • TypeScript
  • Python
  • PostgreSQL
  • Leaflet
  • OSRM

Faites en propre conçues, livrées et exploitées depuis septembre 2024

TechMaint

Gestion d'interventions terrain, multi-locataire, avec une migration SQL Server vers PostgreSQL sans interruption.

  • .NET Core
  • EF Core
  • Angular
  • SignalR
  • PostgreSQL

Devis BTP

Une description dictée à la voix devient un devis PDF structuré, unités et taux de TVA validés.

  • Angular
  • FastAPI
  • PostgreSQL
  • WeasyPrint
  • WebSocket

Growth Engine

dépôt privé

Transforme un échange client en publications à valider ; le fournisseur d'IA se remplace sans réécrire l'application.

  • Next.js
  • FastAPI
  • PostgreSQL
  • Celery
  • Redis

ZenGadget

équipe de 3

Boutique en ligne bilingue, seize pages, sans serveur ni base de données.

  • Next.js
  • React
  • TypeScript
  • Tailwind

Études de cas

Deux plateformes livrées à des clients nommés chez Jant Tech, à Dakar, de l'analyse à la mise en production.

Nouvelle Minoterie du Sénégal

Jant Tech, Dakar · 2022 – 2024 · développeur full-stack · logiciel d'entreprise, non public

Écran de connexion de la plateforme de la Minoterie du Sénégal : entrée par numéro de téléphone et code reçu.
Objet
Plateforme de production et de stock, de l'approvisionnement en blé jusqu'à la livraison de la farine
Périmètre
29 tables · 4 rôles · 8 modules, livrés en entier
Pile
Angular · TypeScript · .NET · C# · SQL Server

Souleymane a rejoint Jant Tech en 2022 et a travaillé trois ans sur nos plateformes clientes.

Sur la plateforme de gestion des opérations de la Nouvelle Minoterie du Sénégal, il a livré l'intégralité du périmètre défini avec le client, de l'approvisionnement en blé jusqu'à la livraison de la farine. Il a modélisé le suivi de production par équipe, une contrainte que peu de développeurs prennent au sérieux : dans une usine qui tourne vingt-quatre heures, un relevé saisi la nuit doit tomber dans le rapport du bon poste.

Saliou Sow
Directeur général, Jant Tech · profil LinkedIn

Plateforme comptable d'un distributeur de carburants

Jant Tech, Dakar · 2022 – 2024 · analyste fonctionnel, puis développeur · logiciel d'entreprise, non public

Espace d'administration de la plateforme comptable : la liste des utilisateurs d'une entreprise et le privilège attaché à chacun, sur poste et sur téléphone.
Objet
Comptabilité d'un réseau de stations-service, en remplacement d'un suivi sur tableurs
Périmètre
5 modules · matrice de droits sur 9 rôles, du directeur général aux agents comptables
Pile
Angular · TypeScript · .NET · C# · SQL Server

J'ai encadré techniquement Souleymane sur la plateforme comptable de notre client distributeur de carburants, une trentaine de stations-service.

Ce qui m'a marqué d'abord, c'est qu'il a commencé par le métier : avant d'écrire une ligne, il a conduit le recueil du besoin auprès du service comptable et rédigé les spécifications fonctionnelles, cinq modules et une matrice de droits couvrant neuf rôles, du directeur général aux agents comptables.

Il ne sépare pas ces deux moments : il spécifie en sachant qu'il devra exécuter, et il développe en sachant pourquoi la règle existe. C'est ce qui manque le plus souvent chez un développeur de son ancienneté.

Mohamed Amar
Lead développeur, Jant Tech · profil LinkedIn
Applications

ProfMatch

Défi Informatique 2026, Collège La Cité · équipe de 4 · 3 semaines

ProfMatch : vingt et une propositions d'affectation pour sept cours, chaque candidat avec son rang et son score.
Vingt et une propositions pour sept cours, chaque candidat avec son rang et son score. Rien n'est affecté tant qu'une personne n'a pas validé : l'application propose, le service RH décide.
ProfMatch : le périmètre d'une génération et les quatre pondérations W1 à W4.
Ce qui décide du score : quatre critères pondérés, compétences 40 %, expérience 30 %, historique 20 %, sémantique 10 %, dont la somme est contrainte à 1. Les poids sont fixés par l'administration, pas par le calcul.
ProfMatch : l'espace du professeur, avec la zone de dépôt du CV en PDF ou DOCX.
Le professeur dépose un PDF. La réponse est immédiate ; l'extraction, elle, part dans une file de tâches et prend vingt-trois secondes.
ProfMatch : le tableau de bord du service des ressources humaines.
Le flux est nommé dans l'ordre où il se pratique : choisir une session, sélectionner les programmes, ajuster les pondérations, générer, puis valider.

Un service des ressources humaines reçoit des CV en PDF et doit affecter des professeurs à des cours. L'application lit les CV, propose les affectations selon quatre critères pondérés, et porte une justification lisible sur chaque recommandation.

Pour qui
Service des ressources humaines, Collège La Cité
Délai
3 semaines
Équipe
4 développeurs
Pile
Next.js · React · FastAPI · PostgreSQL · Celery · Redis · Docker
Voir le code sur GitHubDépôt public · démonstration en pause

Le besoin

Le tri se fait à la main : quelqu'un lit chaque CV, en retient des compétences, les rapproche des cours à pourvoir. C'est long, ce n'est pas reproductible, et personne ne peut expliquer six mois plus tard pourquoi telle personne a obtenu tel cours.

La commande vient du service des ressources humaines du Collège La Cité. Trois semaines, quatre développeurs. J'ai proposé le cadre le premier jour, commits conventionnels, branches protégées, revues obligatoires, parce qu'à quatre sur trois semaines, ce qui n'est pas posé au début ne se pose plus.

Ce que j'ai décidé

Le traitement long part dans une file de tâches.

Écarté
Attendre l'extraction dans le fil de la requête.
Pourquoi
L'extraction d'un CV par le modèle prend vingt-trois secondes. Dans le fil de la requête, c'est une application qui a l'air plantée : l'utilisateur recharge, et le traitement repart une seconde fois.

La justification est rédigée à la consultation, pas à la génération.

Écarté
Rédiger les trente justifications pendant la génération.
Pourquoi
Personne ne lit trente explications. L'enrichissement paresseux garde la génération à trois dixièmes de seconde et ne fait travailler le modèle que sur ce qu'on ouvre vraiment.

Les routes sont montées sans slash final.

Écarté
Laisser la redirection en place et rattraper l'en-tête côté client.
Pourquoi
Un navigateur retire l'en-tête d'autorisation quand une redirection change d'origine, et l'enchaînement hébergeur puis API en changeait. Résultat : 401 sur toutes les listes en production, 200 en appelant l'API directement. Deux chemins ont échappé à ma recherche manuelle ; c'est l'intégration continue qui les a trouvés.

Mise en production

Le front est servi par Vercel sous profmatch.soultaka.com, l'API a tourné en conteneurs sur un serveur que j'administre, derrière son propre nom et son certificat. Migrations de schéma versionnées, file de tâches et cache séparés du service web, intégration continue sur chaque poussée.

La démonstration publique n'était pas une copie d'écran : chaque visiteur obtenait son propre espace, alimenté, effacé au bout d'une heure, et limité à quelques espaces par visiteur. Les données de l'établissement, elles, restaient en lecture. Le serveur est en pause depuis le 27 septembre 2026 ; le code est public.

Dépôt d'un CV
201, en 0,28 s
Extraction par le modèle
23,2 s en tâche de fond : 21 compétences, 3 expériences, 2 formations
Génération des affectations
30 en 0,31 s
Justification narrative
enrichie à la consultation, 12,1 s
Les sept chemins autrefois cassés
200, zéro redirection, 0,25 à 0,52 s
Cloisonnement des rôles
passe de 401 à 403 : l'autorisation arrive enfin
Tests
448, couverture 83,76 %, intégration continue verte

Ce que je referais autrement

Le modèle rate la section « Profil » des CV alors qu'elle est bien présente, et ce champ alimente le critère sémantique, qui est donc calculé sans lui. Une perte de qualité silencieuse, la pire espèce.

Les routeurs de gestion courante sont peu couverts par les tests, malgré les 83 % globaux : la couverture dit la moyenne, pas où sont les trous.

Et l'historique du dépôt me crédite de la quasi-totalité des commits alors que nous étions quatre : j'intégrais le travail de l'équipe, et ça ne se lit pas dans les statistiques.

Une question sur ProfMatch ? Écrire

Applications

SafeOttawa

Lauréat InnovaCode 2026

SafeOttawa : un itinéraire piéton calculé entre deux intersections d'Ottawa.
Deux itinéraires classés entre King Edward et Bank : le recommandé à 13 % de risque, 3,0 km, 68 zones traversées ; l'alternative à 14 % pour un kilomètre de plus. Les corridors rouges sont les zones que les collisions publiées font ressortir.
SafeOttawa : le même trajet demandé avec le profil prudent.
Le même trajet, demandé en profil prudent. La tolérance au risque n'est pas un affichage : elle change le tracé rendu.
SafeOttawa : le formulaire de signalement et les signalements déjà déposés sur la carte.
Ce que la donnée ouverte ne dit pas, les gens le disent : signalement par clic sur la carte, type, description, et les signalements déjà déposés.

Les outils de navigation optimisent le temps ou la distance. Aucun ne dit à un piéton que l'intersection suivante concentre trois fois plus de collisions que la précédente. SafeOttawa transforme 94 406 collisions publiées par la Ville en itinéraires classés par risque.

Pour qui
Piétons et cyclistes d'Ottawa
Données
94 406 collisions, données ouvertes de la Ville
Distinction
Lauréat InnovaCode 2026
Pile
Next.js · TypeScript · Python · PostgreSQL · Leaflet · OSRM
Voir le code sur GitHubOuverte à tous, sans compte : safeottawa.soultaka.com

Le besoin

Un piéton et un cycliste ne cherchent pas le trajet le plus court, mais celui dont ils reviendront. La Ville d'Ottawa publie ses collisions ; personne ne les met entre les mains de qui traverse.

94 406 collisions deviennent 1 895 zones à risque, et ces zones deviennent un coût que le calcul d'itinéraire additionne au kilomètre.

Ce que j'ai décidé

Les signalements passent en base, avec validation stricte.

Écarté
L'écriture sur le disque de la machine, comme en développement.
Pourquoi
L'hébergement du front est en lecture seule : ce qui marchait sur mon poste ne pouvait pas marcher en ligne. Le défaut ne se voyait qu'une fois déployé.

L'appel au calculateur d'itinéraires passe en HTTPS.

Écarté
Laisser l'appel en clair, comme il l'était.
Pourquoi
Une page servie en HTTPS n'a pas le droit d'appeler une ressource en clair : le navigateur bloque, sans rien afficher. Le contrat rendu au client n'a pas changé, la carte n'a pas bougé.

Mise en production

Front servi par Vercel sous safeottawa.soultaka.com, certificat automatique, calcul d'itinéraire par un moteur OSRM et fond de carte OpenStreetMap.

La chaîne a été rejouée en ligne, y compris un signalement rejeté avec sa raison : une validation qui n'a jamais refusé quoi que ce soit n'est pas une validation.

Collisions traitées
94 406
Zones de risque produites
1 895
Itinéraires rendus
classés par profil : prudent, normal, actif
Itinéraire mesuré ci-dessus
3,0 km · 13 % de risque · 68 zones
Signalement invalide
refusé, avec sa raison

Ce que je referais autrement

Ni tests ni analyse statique sur ce projet. C'est un prototype de fin de semaine qui a survécu, et ça se voit : il a gagné un concours, il n'a pas de filet.

Un prix ne rend pas un dépôt propre. Si SafeOttawa devait servir tous les jours, la première semaine passerait à écrire ce qui manque.

Une question sur SafeOttawa ? Écrire

Applications

MediPlan

Projet intégrateur · équipe de 3 · 205 tests automatisés

MediPlan : le flux clinique du jour, chaque rendez-vous avec son statut.
Le flux du jour : chaque rendez-vous avec son heure, son médecin, son motif et son statut, réservé, arrivé, en consultation, terminé. C'est l'écran que la réception garde ouvert.
MediPlan : le tableau de bord de l'administrateur de clinique.
Le tableau de bord d'un administrateur de clinique. Le périmètre de clinique est appliqué par le serveur, pas par l'affichage : l'identité vient du jeton.
MediPlan : les statistiques de la clinique, volume, absences, occupation.
Volume, absences et taux d'occupation par médecin. Le créneau libéré par une annulation redevient réservable : c'est tout l'objet de la décision technique ci-dessous.

Une clinique coordonne ses rendez-vous entre le téléphone, un agenda papier et un tableur. La réception détient le seul agenda qui fait foi. Deux personnes peuvent vendre la même place en même temps.

Pour qui
Cliniques de petite et moyenne taille
Équipe
3, projet intégrateur
Période
Printemps 2026
Pile
NestJS · Angular · PostgreSQL · TypeORM · Docker
Voir le code sur GitHubDépôt public · démonstration en pause

Le besoin

Le médecin ne voit pas sa journée sans la demander. Le patient ne peut rien réserver lui-même. Et une annulation se perd en route : le créneau libéré n'est presque jamais réutilisé.

Projet intégrateur, équipe de trois. J'ai porté le socle technique et intégré les branches des deux autres. Sur un projet d'équipe, l'intégration est souvent la part que personne ne veut prendre.

Ce que j'ai décidé

Un index unique partiel, qui exclut les rendez-vous annulés.

Écarté
Une contrainte d'unicité simple sur le créneau.
Pourquoi
La contrainte simple crée un problème pire que celui qu'elle règle : un rendez-vous annulé occupe toujours la ligne, donc le créneau reste bloqué pour toujours et l'annulation devient inutilisable. Le prédicat partiel est tout l'intérêt.

La réception et le patient empruntent la même transaction, le même verrou, le même index.

Écarté
Deux chemins de réservation, un par canal.
Pourquoi
La garantie ne doit pas dépendre du canal. Ajouter un troisième canal demain ne rouvre pas le trou.

Le périmètre de clinique est appliqué par le serveur.

Écarté
Filtrer à l'affichage, en passant l'identifiant dans la requête.
Pourquoi
L'identité vient du jeton ; le corps de la requête n'expose aucun identifiant de patient. Un patient ne peut donc pas réserver au nom d'un autre, même en fabriquant la requête à la main.

Mise en production

Front sous mediplan.soultaka.com, API en conteneurs sur un serveur que j'administre, migrations de schéma versionnées, intégration continue verte. Serveur en pause depuis le 27 septembre 2026 ; le code est public.

La démonstration donnait à chaque visiteur sa propre clinique, alimentée, effacée au bout d'une heure, avec les trois rôles, administration, médecin, patient, sans se reconnecter.

Trois requêtes simultanées, même créneau
1 × 201, 2 × 409
Annulation, puis nouvelle réservation
le créneau redevient réservable
Patient appelant les statistiques
403
Médecin consultant le rendez-vous d'un autre
404, pas de fuite d'existence
Export CSV
200, 187 lignes, accents intacts
Tests
205, intégration continue verte
Sonde de disponibilité
200 ; 503 quand la base tombe

Ce que je referais autrement

Le jeton vit dans le stockage local du navigateur, sans jeton de rafraîchissement ni liste de révocation : un compte désactivé garde l'accès jusqu'à l'expiration, au plus une heure.

Huit erreurs d'accessibilité subsistent sur des éléments cliquables non atteignables au clavier. Et la police d'icônes pèse 3,9 Mo à elle seule : le paquet initial dépasse le budget que je m'étais fixé.

Une question sur MediPlan ? Écrire

Applications

TechMaint

Projet personnel · 2026

TechMaint : le tableau de bord d'une organisation, interventions par statut.
Une organisation de démonstration, créée pour chaque visiteur : quatre clients, dix interventions, deux techniciens. Aucune autre organisation n'existe, de son point de vue.
TechMaint : la carte des interventions et des techniciens.
Interventions et techniciens sur la carte, en temps réel. Le suivi de position est une simulation, il n'y a pas de source GPS ; le canal temps réel, lui, transporte de vraies diffusions.
TechMaint : le planning des interventions.
Le planning : qui est chez quel client, sur quel équipement, et où en est le travail.

Plusieurs entreprises de maintenance utilisent le même logiciel sans jamais se voir. Le cloisonnement tenait en HTTP et tombait dans le canal temps réel, et c'est le genre de défaut qu'aucun écran ne montre.

Pour qui
Entreprises de maintenance, plusieurs sur le même logiciel
Type
Projet personnel
Année
2026
Pile
.NET Core · Entity Framework Core · Angular · SignalR · PostgreSQL
Voir le code sur GitHubDépôt public · démonstration en pause

Le besoin

Une entreprise de maintenance suit ses interventions au téléphone et sur papier. Le logiciel doit servir plusieurs entreprises à la fois, et aucune ne doit jamais apercevoir les données d'une autre.

C'est un projet personnel, donc le sujet était choisi : je voulais l'isolation multi-locataire, parce que c'est là que les erreurs coûtent le plus cher et se voient le moins.

Ce que j'ai décidé

Le filtre d'isolation passe en fermé par défaut.

Écarté
Le laisser ouvert par défaut, comme il l'était.
Pourquoi
Sans organisation identifiée, il laissait tout passer. En HTTP ce n'était pas visible : un intergiciel renseignait l'organisation avant chaque requête. Un filtre ouvert par défaut trahit exactement au moment où il devrait protéger.

Un filtre dédié aux invocations temps réel, appliqué à chaque appel.

Écarté
Se fier au contexte établi à la négociation initiale.
Pourquoi
Chaque invocation obtient un contexte de dépendances neuf, où l'organisation n'est jamais renseignée : n'importe quel utilisateur authentifié pouvait lire le nom et la position d'un technicien d'une autre entreprise.

C'est l'abonnement lui-même qui est refusé, pas seulement la lecture.

Écarté
Refuser la lecture et laisser l'abonnement.
Pourquoi
L'abonnement se faisait avant la lecture et lui aurait survécu : l'attaquant aurait continué à recevoir les diffusions suivantes.

Mise en production

Front sous techmaint.soultaka.com, API en conteneurs sur le serveur que j'administre, canal temps réel authentifié. Serveur en pause depuis le 27 septembre 2026 ; le code est public.

La démonstration était la preuve de l'architecture : le bac à sable du visiteur n'est pas un dispositif ajouté, c'est le filtre du produit qui le rend possible. Deux bacs simultanés n'ont aucun identifiant commun.

Lecture d'une donnée d'une autre organisation
404, liste vide
Écriture sur un technicien d'une autre organisation
404, position intacte
Technicien tentant quatre actions d'administration
403 × 4
Le même technicien sur ses propres lectures
200
Origine tierce appelant l'API
aucun en-tête d'autorisation renvoyé
Suppression d'un client encore référencé
409 avec identifiant de trace
Négociation du canal temps réel
200 avec jeton, 401 sans

Ce que je referais autrement

Le suivi de position des techniciens est une simulation pilotée depuis le navigateur : il n'y a aucune source GPS réelle. Le canal temps réel, lui, fonctionne et transporte de vraies diffusions ; c'est la donnée qui est simulée.

Le géocodage est hors service depuis que la clé de l'API cartographique a expiré. Il n'y a ni limitation de débit ni jeton de rafraîchissement, la logique métier vit encore dans les contrôleurs, et aucune liste n'est paginée.

Une question sur TechMaint ? Écrire

Applications

Devis BTP

Projet personnel

Devis BTP : le tableau de bord de la démonstration, trois devis avec statut et montant.
Trois devis d'exemple, avec leur statut, accepté, brouillon, envoyé, et leur montant. Le compteur d'essais de l'assistant est affiché en haut : cinq, et une dictée déjà analysée n'en consomme aucun.
Devis BTP : l'écran de création, dictée des travaux à gauche, aperçu du devis à droite.
À gauche, la description des travaux, dictée ou saisie. À droite, l'aperçu du devis qui se remplit : lignes, unités, quantités, taux, totaux. Puis le PDF.

Un artisan du bâtiment rédige ses devis à la main ou dans un tableur : chaque ligne, chaque unité, chaque taux. C'est long, et l'erreur d'arrondi se paie chez le client. Ici, il décrit les travaux à voix haute et relit un devis structuré.

Pour qui
Artisans du bâtiment
Type
Projet personnel
Sortie
Devis structuré, puis PDF
Pile
Angular · FastAPI · PostgreSQL · WeasyPrint · WebSocket
Voir le code sur GitHubDépôt public · démonstration en pause

Le besoin

Le devis est le premier document que le client voit d'un artisan. Il est écrit le soir, après la journée de chantier, et c'est là que les erreurs entrent.

L'application prend la parole comme elle vient, une description parlée, et rend une structure : lignes, unités, quantités, taux, totaux.

Ce que j'ai décidé

L'audio part encodé dans l'appel de génération.

Écarté
Un service de transcription séparé.
Pourquoi
Le fournisseur retenu n'en expose pas. L'audio devait voyager autrement, sans ajouter une seconde dépendance pour une seule fonction.

Une transcription tronquée lève une erreur explicite.

Écarté
La traiter comme une transcription complète.
Pourquoi
Le plafond de jetons inclut la réflexion du modèle : calibré pour un modèle sans raisonnement, il renvoyait du JSON tronqué. Des lignes de devis fausses valent moins qu'une erreur affichée.

La sortie du modèle est bornée par le code, pas par la consigne.

Écarté
Faire confiance au format demandé dans l'invite.
Pourquoi
Quantités strictement positives, taux ramenés à ceux qui existent, unités contraintes. Un modèle qui invente un taux de TVA n'est pas un cas rare, c'est un cas à prévoir.

Mise en production

Front sous devis-btp.soultaka.com, API sur le serveur que j'administre, rendu PDF côté serveur. Serveur en pause depuis le 27 septembre 2026 ; le code est public.

La chaîne complète a été rejouée en ligne : inscription, devis, PDF.

Totaux
1 322,00 HT · 132,20 de TVA · 1 454,20 TTC, exacts au centime
PDF produit
13,9 Ko, douze contrôles de contenu
Transcription tronquée
erreur explicite, aucune ligne inventée
Chaîne rejouée en ligne
inscription, devis, PDF

Ce que je referais autrement

Le canal temps réel fonctionne et il est authentifié, mais aucun écran ne l'utilise : j'ai construit une capacité avant son usage.

Le schéma de base n'est pas versionné par des migrations, ce qui va tant que je suis seul dessus, et plus du tout dès qu'on est deux.

Une question sur Devis BTP ? Écrire

Applications

Growth Engine

Projet personnel · compte requis

Growth Engine : la bibliothèque de gabarits de publication.
Les gabarits de publication, classés par intention. Le style est un objet du système, pas une consigne cachée dans une invite.
Growth Engine : la définition du client idéal, en quatre étapes.
L'installation en quatre étapes commence par le client visé : toutes les générations sont calibrées dessus, plutôt que sur une consigne réécrite à chaque fois.
Growth Engine : la saisie d'une conversation client pour en extraire les objections.
Les objections sont extraites d'une vraie conversation et citées telles quelles, jamais reformulées : c'est la règle du produit, et elle est écrite sur l'écran.
Growth Engine : la liste des publications, vide sur un compte neuf.
L'atelier de publications, sur un compte neuf : brouillon, prêt, planifié, publié. Cette capture est vide, et elle le reste : je ne remplis pas un écran pour la photo.

Transformer un appel client enregistré en publications, sans réécrire ce que le client a dit. Le fournisseur d'IA a changé en cours de route, et l'application n'a pas eu à changer.

Pour qui
Indépendants et petites équipes B2B
Type
Projet personnel
Traitements
Asynchrones, file de tâches
Pile
Next.js · FastAPI · PostgreSQL · Celery · Redis
Demander l'accès au codeDépôt privé · démonstration en pause

Le besoin

Un indépendant qui vend ses services parle mieux qu'il n'écrit. L'appel client contient déjà les mots justes : les objections, les angles, le ton.

L'application ingère la conversation, en extrait ce qui a été dit, et propose des publications, sans jamais présenter comme une citation ce qui n'en est pas une.

Ce que j'ai décidé

Toute la surface d'appel au modèle tient dans une seule fonction.

Écarté
Appeler le fournisseur depuis chaque service qui en a besoin.
Pourquoi
Le fournisseur a changé en cours de route. La bascule s'est faite sans toucher aux appelants : c'est la seule raison pour laquelle elle n'a pas coûté une réécriture.

Une marge de jetons est ajoutée dans la couche d'appel.

Écarté
Garder le plafond calibré pour l'ancien modèle.
Pourquoi
Le nouveau modèle réfléchit, et sa réflexion consomme le budget de sortie : sans marge, la réponse utile était tronquée.

Deux gardes : sortie vide et réponse tronquée lèvent des erreurs explicites.

Écarté
Laisser passer et corriger à l'affichage.
Pourquoi
Une publication à moitié générée ressemble à une publication. C'est exactement ce qu'il ne faut pas laisser passer.

Mise en production

Front sous growth-engine.soultaka.com, API et file de tâches sur le serveur que j'administre, authentification par jeton vérifié côté serveur. Serveur en pause depuis le 27 septembre 2026 ; dépôt privé, accès sur demande.

La chaîne complète a été rejouée en ligne : inscription, connexion, huit gabarits chargés, session tenue par cookie.

Trois variations générées
21,4 s, avec le bon type de publication
Chaîne rejouée en ligne
inscription, connexion, huit gabarits, session
Sortie vide ou tronquée
erreur explicite, rien n'est publié
Changement de fournisseur
aucun appelant modifié

Ce que je referais autrement

La transcription audio n'est pas disponible : la dépendance pèse deux gigaoctets et je ne l'ai pas embarquée. C'est un arbitrage, pas un oubli, et il est écrit dans le dépôt.

Il n'y a pas d'ordonnanceur : les publications planifiées attendent qu'on les déclenche. Une capacité annoncée par l'écran doit exister derrière, ou disparaître de l'écran.

Une question sur Growth Engine ? Écrire

Applications

ZenGadget

Travail de cours · équipe de 3

ZenGadget : le catalogue de la boutique.
Seize pages, toutes pré-rendues. Les données produits sont statiques, écrites en TypeScript, donc vérifiées à la compilation plutôt qu'à l'exécution.
ZenGadget : une fiche produit avec ses variantes.
Une fiche produit et ses variantes. Le contenu est bilingue et le changement de langue n'appelle rien : tout est déjà dans la page.
ZenGadget : le panier, avec le total et la mention « simulation seulement ».
Le panier survit au rafraîchissement, sans serveur : l'état vit dans le navigateur. Et la page dit ce qu'elle est : « simulation seulement, aucun paiement ne sera effectué ».

Une boutique complète, seize pages, deux langues, un panier qui survit au rafraîchissement, sans serveur ni base de données. La contrainte était l'énoncé du cours ; elle est devenue l'exercice intéressant.

Pour qui
Travail de cours, programmation web avancée
Équipe
3
Particularité
Aucun serveur, aucune base
Pile
Next.js · React · TypeScript · Tailwind · react-intl
Voir le code sur GitHubOuverte à tous, sans compte : zengadget.soultaka.com

Le besoin

Travail de cours en programmation web avancée, en équipe de trois : une boutique complète, sans backend ni base de données.

Tout l'état vit donc dans le navigateur. Le panier doit survivre au rafraîchissement, le thème et la langue aussi, sans provoquer d'incohérence au premier rendu, le défaut classique d'une page pré-rendue qui découvre les préférences après coup.

Ce que j'ai décidé

Les données produits sont statiques, en TypeScript.

Écarté
Un fichier JSON chargé à l'exécution.
Pourquoi
Le typage attrape à la compilation ce qu'un JSON laisse découvrir en production, et les seize pages peuvent être pré-rendues.

L'état vit en contexte React, les préférences en stockage local.

Écarté
Un état global chargé au démarrage.
Pourquoi
Sans serveur, il n'y a rien d'autre. Ce qui compte est l'ordre : lire les préférences avant le premier rendu visible, sinon la page clignote de la mauvaise langue.

Mise en production

Servi par Vercel sous zengadget.soultaka.com. Aucune fonction serveur, aucune base : le site entier est un ensemble de pages pré-rendues, installable comme application.

Pages
16, toutes statiques
Fonctions serveur
aucune
Langues
deux, interface complète
Tests
25, de structure

Ce que je referais autrement

Les tests vérifient la présence des choses, pas leur comportement : ils cassent sur un renommage et laissent passer un vrai défaut. C'est le pire rapport entre le coût et la garantie.

Et le contenu produit n'est pas traduit, seule l'interface l'est. Une boutique bilingue dont les fiches ne le sont pas est bilingue à moitié.

Une question sur ZenGadget ? Écrire

Questions fréquentes

Comment travaillez-vous, du besoin à la production ?

Je commence par le métier. Sur la plateforme comptable du distributeur de carburants, j'ai recueilli le besoin auprès du service comptable et rédigé les spécifications, cinq modules et neuf rôles, avant d'écrire une ligne. Puis je développe ce que j'ai spécifié, je mets en production et j'exploite : les sept applications de cette page ont été conteneurisées, servies sous TLS et déployées en intégration continue par moi.

Comment assurez-vous la qualité de ce que vous livrez ?

Par des tests automatisés qui tournent à chaque livraison, 205 sur MediPlan, 448 sur ProfMatch avec 83 % de couverture, et par des revues de code en équipe. Chaque étude de cas se termine par ce que je referais autrement : ce qui a été raté est écrit, pas caché.

Ces applications, seul ou en équipe ?

Quatre des sept sont solo. ProfMatch, MediPlan et ZenGadget ont été livrées en équipe de trois ou quatre, et avant cela j'ai passé deux ans dans l'équipe d'un éditeur à Dakar, sur deux plateformes livrées à des clients nommés.

Sur quelle pile êtes-vous le plus à l'aise ?

C# et .NET avec Angular, trois ans en entreprise. Ensuite React, Next.js, Python et FastAPI sur mes propres applications. Le besoin, le modèle de données et les tests ne dépendent d'aucune pile.

Une autre question ? Écrire

Vingt minutes suffisent pour savoir si mon profil correspond à votre besoin.

Un poste à pourvoir, une question sur une application, ou l'envie de vérifier une chose écrite plus haut : écrivez-moi. Je réponds sous 24 heures.

souleymane.diallo.tech@gmail.comMontréalfrançais et anglais