Moussa Mallé

Ingénieur logiciel.
Je construis des systèmes qui tiennent en production.

Développeur full stack chez Samina à Dakar, hands-on de l'architecture jusqu'au déploiement. En parallèle, je construis ChapChapSend, une plateforme d'envoi de SMS et de messages WhatsApp.

Projets

  • ChapChapSend

    CodeANGEL · toute la partie technique

    Plateforme d'envoi de SMS et de messages WhatsApp pour les PME d'Afrique de l'Ouest : campagnes, paiement mobile dans les messages et API pour les développeurs.

    Ce que fait l'outil

    • Contacts : import, groupes et segments, historique par contact.
    • Campagnes SMS et WhatsApp, à l'unité ou en masse, avec modèles validés, variables (prénom, montant…) et planification.
    • Paiement Wave et Orange Money : lien de paiement dans le message, suivi des transactions.
    • Statistiques de livraison et de campagne, comptes pour toute l'équipe.
    • API pour développeurs : clés de test et de production, permissions, webhooks.
    • Deux passerelles SMS derrière la même API : la passerelle Pro affiche le nom de l'entreprise ; la passerelle personnelle envoie depuis le téléphone Android du client, avec 1 000 SMS offerts par mois.

    Défis et solutions

    Envoyer depuis une SIM grand public sans se faire bloquer
    Un opérateur ne juge pas un SMS, il juge un rythme. L'application impose sa cadence : quelques secondes entre deux messages, une pause toutes les vingt, un plafond d'environ 200 par heure.
    Ne perdre aucun message quand le téléphone s'éteint
    Le serveur fait foi. Les messages attendent dans une file côté serveur ; le téléphone les prend avec un bail et ne confirme qu'après le retour de la SIM. Sans confirmation, le message repart. Un téléphone muet depuis deux minutes est considéré hors ligne : l'API refuse l'envoi tout de suite au lieu de le laisser attendre sans fin.
    Des webhooks fiables, dans les deux sens
    Vers les clients : file persistée, nouveaux essais espacés, rejeu à la demande. Depuis Meta : chaque appel est authentifié avant d'être traité, et les événements en échec sont repris.
    Des crédits et des clés auxquels on peut se fier
    Un SMS en échec rend son crédit. La facturation repose sur un seul événement de paiement, traité une seule fois même s'il arrive deux fois. Une clé d'API n'est affichée qu'une fois ; seule son empreinte est conservée, et la révocation est immédiate.

    Stack · Python, Litestar, PostgreSQL, Kotlin Multiplatform, WhatsApp Cloud API, k3s, ArgoCD

    chapchapsend.com ↗

  • Plateforme de commande, livraison et force de vente

    Samina · distribution de boissons

    Quatre applications mobiles et un portail web métier, orchestrés par un seul backend : de la commande du client à la livraison, en passant par les commerciaux sur le terrain.

    Ce que fait la plateforme

    • App client pour les particuliers et les professionnels : catalogue, commande, suivi de la livraison.
    • App vendeur agréé pour les distributeurs partenaires : réception et préparation des commandes.
    • App livreur : courses proposées aux livreurs disponibles, position suivie automatiquement pendant la tournée.
    • App force de vente : visites, vente directe, prévente et audit des points de vente, autorisés seulement dans la zone du commercial.
    • Portail web métier : carte en temps réel des livreurs et des commerciaux, affectation des commandes, tableaux de bord (chiffre d'affaires, ventes par canal, produit, commercial, livreur), objectifs commerciaux, rôles et permissions par module et par action.

    Défis et solutions

    Suivre automatiquement livreurs et commerciaux, même écran verrouillé
    Les téléphones suspendent une application dès qu'elle quitte l'écran. Le suivi passe par un service de premier plan sur Android et par les mises à jour en tâche de fond sur iOS. Les positions arrivent par WebSocket : la dernière en cache Redis, l'historique dans TimescaleDB. La carte n'affiche plus un point dont la position est trop ancienne.
    Contrôler les actions terrain par zone
    Une visite, une commande ou un audit n'est accepté que si le commercial se trouve dans sa zone, vérifiée avec PostGIS.
    Un backend, cinq publics, des droits fins
    Un monolithe modulaire de 15 domaines métier. Des permissions dynamiques par rôle et par action, appliquées côté serveur et reflétées dans le portail. Des sessions séparées par canal : 24 h sur le portail, 30 jours avec rotation du jeton sur mobile.
    Livrer souvent sans casser
    Typage strict réintroduit en CI : 130 erreurs corrigées et 3 bugs de production découverts. Les quatre applications Android sont signées et versionnées par le pipeline Azure DevOps, qui vérifie l'empreinte du certificat. Le backend tourne sur Azure (App Service, Front Door), avec staging avant la production.

    Stack · FastAPI, PostgreSQL / PostGIS, TimescaleDB, Redis, Celery, WebSocket, Flutter, React, Azure, Azure DevOps

  • Recherche par photo pour l'e-commerce

    Samina · marketplace de produits importés

    Le client photographie un article : la boutique le reconnaît et propose une quarantaine de produits similaires, avec leur prix en FCFA, en quelques secondes.

    Ce que fait l'outil

    • Remplace un circuit manuel : photo reçue sur WhatsApp, recherche d'image, lien copié, fiche créée, réponse au client.
    • Résultats progressifs : le produit reconnu s'affiche d'abord, puis la grille des similaires, puis les prix au fil de l'eau.
    • Rien n'est créé dans la boutique tant que personne n'achète : la fiche naît à l'ajout au panier, en 0,2 à 0,5 s, puis se complète en arrière-plan (images, variantes, synthèse des avis).
    • Plugin WooCommerce installable sur n'importe quelle boutique, avec sa clé d'API, son tableau de bord (conversions, durée de chaque étape) et le déroulé détaillé de chaque recherche.

    Défis et solutions

    Ne jamais afficher un faux prix
    Le poids d'un article fait varier son prix de plusieurs milliers de FCFA. Aucun prix n'apparaît sans fiche complète, et il est calculé à la lecture avec le coefficient de chaque boutique, jamais stocké.
    Plusieurs serveurs, une seule récupération par fiche
    Une fiche peut être demandée en même temps par le pré-chargement, le client et l'achat. Un bail posé en base garantit qu'elle n'est récupérée qu'une fois. L'état de chaque recherche vit en base : n'importe quel serveur peut répondre, et une recherche bloquée après un crash est détectée.
    Maîtriser le coût de chaque recherche
    Une même photo envoyée par dix clients n'est analysée qu'une fois. Les fiches sont partagées entre recherches et entre boutiques. Le traitement des images et la synthèse des avis ne sont faits que pour les articles achetés. Quotas par boutique et par visiteur.
    Un ajout au panier instantané et sans doublon
    Création native minimale avec verrou contre le double clic, réponse immédiate au client, puis enrichissement après la réponse avec quatre essais espacés.

    Stack · Python, FastAPI, SQLAlchemy, Alembic, MySQL, Google Lens (SerpApi), Azure Blob, PHP / WordPress, WooCommerce

  • Publication de catalogue et logistique

    Samina · marketplace de produits importés

    Les outils qui alimentent la marketplace et livrent ses commandes : import de produits en masse, fiches rédigées par IA à partir d'une photo, préparation et suivi des colis.

    Ce que font les outils

    • Import de produits : un lien suffit pour créer une fiche complète dans la boutique, avec images retraitées, variantes, prix en FCFA calculé à partir du poids et délai de livraison. Les doublons sont détectés et l'avancement suit en temps réel.
    • Console de publication en masse : un dossier de pages produits devient des centaines de fiches, envoyées par lots, avec reprise là où la session s'est arrêtée et un rapport final.
    • Fiches par LLM vision : sur une photo, le service détecte tous les produits, rédige des titres et des descriptions e-commerce complets et les publie dans la boutique. Les réponses du modèle sont validées par schéma au fil de l'écriture.
    • Logistique : préparation des commandes, colis, expédition, étiquettes QR, scan et page de suivi publique, avec des rôles préparateur, livreur et logisticien.

    Défis et solutions

    Des données sources qui changent de forme
    Un même champ revient en objet, en liste, en texte ou vide selon le produit. Tous les accès passent par une seule couche de normalisation, testée.
    Un produit, quatorze appels réseau en tout ou rien
    Sur une connexion qui perd une requête sur dix, sans protection, seul un produit sur quatre aboutissait. Chaque appel sortant porte désormais un délai et des réessais, sur une session partagée.
    Ne jamais créer un produit deux fois
    Une création n'est jamais rejouée automatiquement, car une erreur 502 peut cacher un produit bel et bien créé. Les écritures concurrentes sont rejouées sur conflit, et un disjoncteur protège la base.
    Une erreur ne doit jamais arrêter une session de masse
    Chaque résultat est écrit aussitôt ; seuls les résultats définitifs sont mémorisés. Relancer la console reprend exactement les produits en échec.

    Stack · FastAPI, SSE, Azure OpenAI, pydantic-ai, Azure Blob, MySQL, Python CLI (rich), Next.js, Prisma

  • HireBox AI

    Mission · recrutement

    Un service qui lit un CV et génère des questions d'entretien adaptées au candidat.

    • Sorties structurées et validées, prêtes à intégrer dans l'outil du recruteur.
    • Le modèle est isolé derrière une interface : le service se teste sans appeler le LLM.

    Stack · Python, FastAPI, LLM

  • Recherche sémantique pour WooCommerce

    Mémoire de Master 2 · Data Engineering · DIT

    Sortir la recherche produit de WordPress : des résultats trouvés par le sens de la requête, et un back-office qui n'a plus d'index de recherche à maintenir.

    • Le problème : la recherche native fait du LIKE en SQL. « chaussures pour courir » ne trouve pas « basket de running », et une faute de frappe donne une page vide.
    • Capture des changements avec Debezium sur le binlog MySQL, publiée dans Kafka. WordPress ne fait aucun travail en plus quand le vendeur enregistre une fiche.
    • Indexeur Python : il regroupe les changements par produit sur une fenêtre d'une seconde, relit le produit complet, calcule son embedding et l'indexe sans doublon, même en cas de rejeu.
    • Recherche hybride : BM25 pour les noms exacts, vecteurs pour l'intention, fusion des deux classements (RRF). Le modèle tourne en local sur CPU, sans IA générative ni API externe.
    • Premier test de bout en bout : un produit est trouvé 1,1 s après son enregistrement. L'évaluation avant / après est en cours : pertinence, latence, coût d'écriture, fraîcheur de l'index.

    Stack · Debezium, Kafka, Python, multilingual-e5, Elasticsearch, FastAPI, plugin WordPress, Docker

Ma façon de travailler

Comprendre le métier avant de découper

J'organise des ateliers d'event storming et de BDD avec les gens du métier : on déroule les événements réels, on écrit les règles sous forme d'exemples concrets. C'est de là que sortent les domaines et leurs frontières, avant la première ligne de code.

Concevoir pour durer

Architecture hexagonale, monolithe modulaire, domaines qui communiquent par événements. Le métier ne dépend ni du framework ni de la base de données, et les choix importants sont écrits dans des ADR. Le code reste lisible quand le produit grandit et quand l'équipe change.

Monter en charge sans tout réécrire

Je commence par un monolithe modulaire dont chaque module peut être extrait plus tard. Les traitements lourds passent par des files et des workers, les lectures fréquentes par du cache, les données temps réel et géographiques par des bases faites pour ça (TimescaleDB, PostGIS).

Déployer sans surprise

Tout tourne dans Docker, du poste de développement à la production. Chaque environnement a sa place : local sur k3d, staging, puis production. Je déploie en GitOps sur Kubernetes (k3s, ArgoCD) avec des secrets chiffrés dans le dépôt, ou sur Azure (App Service, Front Door). Les serveurs sont décrits en code avec pyinfra et surveillés avec Grafana.

Laisser les outils trouver les bugs

Typage strict, tests et CI bloquante. Sur un projet, remettre le typage strict a révélé trois bugs de production qu'aucun test ne voyait.

Des agents de code, pilotés avec rigueur

J'utilise Claude Code tous les jours. Je le pilote comme un membre de l'équipe : contexte écrit dans le dépôt (conventions, architecture), plan validé avant le code, chaque diff relu. Il passe par les mêmes garde-fous qu'un humain : tests, typage strict, revue et CI.

Faire monter l'équipe

Revues de code, standards écrits, documentation. Chez Samina, l'intégration d'un nouveau développeur a pris environ 40 % de temps en moins.

Parcours

  • Développeur full stack, SaminaDakar · backend, architecture, revues de code, mise en production
  • CodeANGEL, activité indépendanteChapChapSend et missions freelance
  • Master IA & Big Data, Data EngineeringDakar Institute of Technology
  • DUT puis Licence Génie LogicielTechnolab ISTA, Bamako

Backend · Python (FastAPI, Litestar, Django), TypeScript, Kotlin, PostgreSQL, Redis

Architecture · DDD, hexagonale, monolithe modulaire, événements, multi-tenant

Data & IA · Kafka, Elasticsearch, Airflow, dbt, Snowflake, LLM, vision

Infra · Azure, Docker, Kubernetes, ArgoCD, CI/CD

Travaillons ensemble.

Un projet, une question d'architecture ou simplement envie d'échanger ? Écrivez-moi.

moussa@codeangel.pro