Aller au contenu
Amine Mekki

Amine Mekki

Développeur Full-Stack TypeScript Senior & Team Lead

Je conçois et construis des systèmes web qui restent maintenables : Next.js et React devant, NestJS et PostgreSQL derrière, et du jugement d'ingénieur partout, y compris sur le code généré par IA.

  • Basé à Sfax, Tunisie
  • Langues de travail Français · Anglais · Arabe

À propos

L'ingénierie avant la production de code

Développeur full-stack JavaScript avec plus de 5 ans d'expérience dans la conception d'applications web et mobiles scalables sur l'écosystème TypeScript moderne : React, Next.js, Node.js et NestJS. J'ai piloté des équipes pluridisciplinaires, architecturé des produits de bout en bout et assuré la livraison en environnement Agile, en Espagne et en Tunisie. Ce qui m'importe n'est pas la liste de frameworks. C'est que le système reste compréhensible, testable et modifiable après mon départ. Cela façonne ma manière de concevoir des APIs, de modéliser les données et de relire du code. Cela façonne aussi ma manière d'utiliser l'IA : comme un accélérateur dont la production subit le même examen que n'importe quelle pull request.

Ce que j'apporte à une équipe

  • Leadership technique : fixer les priorités, garder les parties prenantes alignées, livrer dans les délais
  • Architecture end-to-end : APIs NestJS modulaires, auth JWT avec refresh tokens, RBAC, couches de validation
  • Modélisation de données avec PostgreSQL et Prisma, stratégie de migration comprise
  • Architecture frontend : design systems, atomic design, TanStack Query pour le state serveur
  • Des réflexes de production par défaut : monitoring Sentry, stockage S3, génération de PDF, i18n
  • Des fondations DevOps avec Docker et Docker Compose pour des environnements reproductibles

Philosophie d'ingénierie

Ma façon d'aborder le logiciel

Les frameworks changent. Les raisons qui rendent un logiciel coûteux à faire évoluer, non. Voici les principes auxquels je soumets un codebase, y compris le mien.

Construire pour les humains

Le code est bien plus souvent lu qu'écrit. J'optimise pour le prochain développeur : des noms explicites, des modules courts, des frontières qui correspondent à la façon dont on pense le métier. Si un changement nécessite un guide touristique, l'architecture a déjà échoué.

Une architecture qui a une raison d'être

Chaque couche, pattern ou abstraction doit mériter sa place en résolvant un problème que le projet a réellement. Je préfère livrer une conception simple et directe plutôt que spéculative, puis refactorer délibérément quand de vrais besoins apparaissent.

La qualité ne se limite pas aux tests

Un pipeline vert n'est pas la ligne d'arrivée. La qualité de production inclut l'observabilité, la sécurité, la performance, une gestion d'erreurs claire et la documentation. Les tests donnent confiance dans le comportement ; le reste garde le système honnête face aux vrais utilisateurs.

L'IA est un accélérateur, pas un architecte

J'utilise l'IA tous les jours pour aller plus vite, et je relis tout ce qu'elle produit comme n'importe quelle pull request. L'IA peut générer une implémentation qui fonctionne. Elle ne peut pas décider si l'abstraction est la bonne, si la frontière est correcte, ni si ce code a sa place dans le système. Ce jugement, c'est mon métier.

Comment je juge du code

Qu'il ait été écrit par un collègue, par moi il y a six mois, ou par un modèle, les critères de revue sont les mêmes :

  • Responsabilité unique, appliquée honnêtement
  • Des frontières qui séparent le métier de l'infrastructure
  • Des dépendances explicites plutôt qu'un couplage caché
  • Des noms qui expriment l'intention
  • Des erreurs gérées de façon prévisible, pas décorative
  • Testable par conception, pas en mockant tout
  • Des abstractions introduites quand la duplication fait mal, pas avant
  • Une complexité proportionnelle au problème

Ingénierie assistée par IA

Code qui fonctionne vs code prêt pour la production

Le développement assisté par IA a déplacé le goulot d'étranglement. Produire une implémentation qui fonctionne est devenu rapide ; savoir si elle doit atteindre la production reste la partie difficile. Un code syntaxiquement correct peut cacher de mauvaises frontières, des requêtes N+1, un typage faible, des chemins d'erreur absents et des abstractions dont personne n'a besoin. Voici deux exemples de ce qu'une revue attentive change.

Le N+1 que le compilateur ne voit pas

À la demande « le total des commandes de chaque utilisateur », l’assistant a produit cette fonction. Ça compile, les tests sur trois utilisateurs de fixture passent, et la revue semble triviale. Jusqu’à ce qu’on compte les requêtes.

Généré : fonctionne, ne doit pas partir en prod
// "Get each user's order total", first attempt from the assistant
async function getUserOrderTotals(prisma: PrismaClient) {
  const users = await prisma.user.findMany()

  const results = []
  for (const user of users) {
    const orders = await prisma.order.findMany({
      where: { userId: user.id },
    })
    const total = orders.reduce((sum, o) => sum + o.amount, 0)
    results.push({ name: user.name, total })
  }
  return results
}
Après revue
type UserOrderTotal = { name: string; total: number }

async function getUserOrderTotals(prisma: PrismaClient): Promise<UserOrderTotal[]> {
  const totals = await prisma.order.groupBy({
    by: ['userId'],
    _sum: { amount: true },
  })
  const users = await prisma.user.findMany({
    select: { id: true, name: true },
  })

  const totalByUserId = new Map(totals.map((t) => [t.userId, t._sum.amount ?? 0]))
  return users.map((user) => ({
    name: user.name,
    total: totalByUserId.get(user.id) ?? 0,
  }))
}

Constats de la revue

  • Une requête par utilisateur

    La boucle émet un findMany par utilisateur : 10 000 utilisateurs, c’est 10 001 requêtes. Invisible sur des données de développement, incident de production à l’échelle.

  • Agrégation faite dans le code applicatif

    La base sait calculer des sommes nativement. Rapatrier toutes les commandes en mémoire pour les réduire en JavaScript déplace le travail vers la couche la plus lente.

  • Pas de type de retour explicite

    Les appelants voient une forme anonyme inférée. Un type métier d’une ligne documente l’intention et survit aux refactors internes.

  • Sélection de colonnes non bornée

    findMany() sans select fait transiter toutes les colonnes alors que seuls id et name sont utilisés.

Pourquoi c'est important

Le code généré optimise pour « ça compile et ça a l’air correct ». Les patterns de requêtes sont l’endroit où ça casse en silence : rien dans le système de types ne distingue 2 requêtes de 10 001. Relire les chemins d’accès aux données n’est pas optionnel, quel que soit l’auteur du code.

Le composant client qui n’avait pas de raison d’exister

Une page App Router listant des projets. La version générée ressort la recette SPA familière : composant client, useEffect, fetch, état de chargement. Ça fonctionne, et ça embarque une classe entière de problèmes que le framework avait déjà résolus.

Généré : fonctionne, ne doit pas partir en prod
'use client'

// Project list page, generated with a client-side fetch "to be safe"
export default function ProjectsPage() {
  const [projects, setProjects] = useState<any[]>([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetch('/api/projects')
      .then((res) => res.json())
      .then((data) => {
        setProjects(data)
        setLoading(false)
      })
  }, [])

  if (loading) return <Spinner />
  return <ProjectList projects={projects} />
}
Après revue
// Server component: data is fetched where it lives, typed end to end
export default async function ProjectsPage() {
  const projects: Project[] = await getProjects()

  if (projects.length === 0) {
    return <EmptyState />
  }
  return <ProjectList projects={projects} />
}

Constats de la revue

  • Mauvais côté de la frontière client/serveur

    Rien ici n’est interactif. Rendre côté serveur supprime l’aller-retour réseau, le spinner et le layout shift.

  • any[] efface le contrat

    Le composant accepte ce que l’endpoint renvoie, quoi que ce soit. Un champ renommé casse en production à l’exécution au lieu de casser à la compilation.

  • Pas de chemin d’erreur

    Si le fetch échoue, loading reste vrai pour toujours et l’utilisateur regarde un spinner. Chaque accès aux données doit avoir un comportement d’échec défini.

  • Une route API qui n’existe que pour ce composant

    L’endpoint /api/projects duplique un accès aux données qu’un composant serveur ferait directement. Plus de surface, plus de code à sécuriser et maintenir.

Pourquoi c'est important

Les outils d’IA entraînés sur des années de patterns SPA les reproduisent dans des frameworks qui les ont rendus obsolètes. « Est-ce idiomatique pour la plateforme sur laquelle on est ? » est une question de revue qu’un modèle ne se pose pas de façon fiable.

Travaux choisis

Projets

Une sélection de missions que j'ai pilotées ou auxquelles j'ai contribué. Les études de cas complètes sont sur la page projets.

Team Lead : architecture, backend, livraison

Un produit full-stack qui digitalise l'étude technique et le chiffrage de projets de ventilation, piloté de bout en bout : coordination d'équipe, architecture API et données, fondations frontend et mise en place DevOps.

  • NestJS
  • Next.js
  • TypeScript
  • PostgreSQL
  • Prisma ORM
  • TanStack Query

Lire l'étude de cas

Lead Technique : design system, services backend, livraison

Une plateforme full-stack de gestion agronomique : design system web, services backend NestJS et fonctionnalités front office / back office en Next.js, livrées dans un rôle de leadership technique.

  • Next.js
  • NestJS
  • PostgreSQL
  • Prisma ORM
  • Docker
  • Figma

Lire l'étude de cas

Lead Technique : architecture, roadmaps, qualité de code

Un produit d'optimisation du transport multiplateforme : web en Next.js, mobile en React Native, APIs NestJS derrière les deux. J'y portais la responsabilité des choix d'architecture, des feuilles de route techniques et de la qualité de code.

  • Next.js
  • React Native
  • NestJS
  • PostgreSQL
  • Docker
  • Jira

Lire l'étude de cas

Développeur Full-Stack

Une plateforme de génération de pages publicitaires statiques : Astro.js pour la génération, des blocs de page réutilisables intégrés depuis Figma, et des services NestJS + GraphQL derrière.

  • Astro.js
  • Next.js
  • NestJS
  • GraphQL
  • Docker
  • Figma

Lire l'étude de cas

Expérience

Où j'ai construit

Plus de cinq ans au sein d'équipes produit en Espagne et en Tunisie, souvent comme développeur responsable de l'architecture, de la qualité du code et de la livraison.

  1. Outil de chiffrage et d'étude pour projets de ventilation

    Team Lead & Développeur Full-Stack · Soler & Palau · Barcelone, Espagne

    • Pilotage d'une équipe pluridisciplinaire pour livrer un produit full-stack avec NestJS, Next.js 15 (App Router) et TypeScript : gestion des priorités, alignement des parties prenantes et respect des délais.
    • Conception d'une API REST modulaire avec authentification JWT (refresh tokens), RBAC, couches de validation, observabilité via Sentry, et intégrations pour les données temps réel, le stockage AWS S3, l'email et la génération de PDF via Puppeteer.
    • Modélisation et maintenance du schéma de données PostgreSQL et de la couche Prisma ORM : relations entre entités, stratégie de migration avec Prisma Migrate, et patterns d’accès maintenables.
    • Architecture frontend selon les principes de l'atomic design, avec TanStack Query pour le state serveur, TanStack Form + Zod pour la validation, next-intl pour l'i18n et NextAuth pour les routes protégées.
    • Mise en place des fondations DevOps avec Docker et Docker Compose pour des environnements reproductibles et un onboarding simplifié.
    • NestJS
    • Next.js
    • TypeScript
    • PostgreSQL
    • Prisma ORM
    • TanStack Query
    • TanStack Form + Zod
    • next-intl
    • NextAuth
    • Sentry
    • AWS S3
    • Puppeteer
    • Docker
  2. Plateforme de gestion agronomique

    Lead Technique & Développeur Full-Stack · RAGT · Barcelone, Espagne

    • Rôle de leadership : coordination des parties prenantes, planification et priorisation des tâches, contribution aux décisions d'architecture logicielle et d'orientation technique.
    • Développement du design system web et des services backend principaux avec NestJS, et livraison des fonctionnalités full-stack (front office / back office) avec Next.js.
    • Next.js
    • NestJS
    • PostgreSQL
    • Prisma ORM
    • Docker
    • Figma
  3. Application d'optimisation du transport digital

    Lead Technique · Katalii – Groupe Berto · Barcelone, Espagne

    • Définition des choix architecturaux et des feuilles de route techniques ; suivi de la qualité du code et des performances.
    • Livraison du design system, des APIs backend (NestJS) et du frontend multiplateforme (Next.js / React Native).
    • Next.js
    • React Native
    • NestJS
    • PostgreSQL
    • Docker
    • Jira
  4. Plateforme de génération de pages publicitaires statiques

    Développeur Full-Stack · Le Bon Coin · Barcelone, Espagne

    • Amélioration de l'UI/UX à partir de maquettes Figma et création de nouveaux blocs de pages web.
    • Implémentation de la génération de pages statiques avec Astro.js et développement de fonctionnalités backend avec NestJS et GraphQL.
    • Next.js
    • Astro.js
    • NestJS
    • GraphQL
    • Docker
    • Figma
  5. Produits full-stack B2C et internes

    Développeur Full-Stack · My Insurance / GestiLot / OneWay · Sfax, Tunisie

    • Livraison de plusieurs produits B2C et internes : une plateforme d'assurance, un outil digital pour le marché de l'art et une application de covoiturage interne.
    • Audits d'applications mobiles, développement d'interfaces multiplateformes avec React et React Native, et implémentation de services backend avec NestJS et MongoDB, documentation Swagger incluse.
    • Couverture de tests unitaires complète pour les composants et la logique métier principaux.
    • React
    • React Native
    • NestJS
    • MongoDB / Mongoose
    • Swagger
    • Unit testing
    • Git
    • Redmine
  6. Librairie de composants React interne

    Développeur Frontend · Piximind · Sfax, Tunisie

    • Conception et développement d'une librairie de composants React TypeScript réutilisables avec hooks personnalisés, publiée sous forme de deux packages NPM distincts pour un usage à l'échelle de l'entreprise.
    • Documentation Storybook complète et couverture de tests unitaires pour garantir la fiabilité et la facilité d'intégration.
    • React
    • TypeScript
    • Storybook
    • Unit testing

Stack

Les technologies, en contexte

Pas un mur de logos. Voici la stack avec laquelle je travaille au quotidien, regroupée selon le rôle qu'elle joue dans un système.

Frontend

  • React
  • Next.js · App Router
  • TypeScript
  • Redux / Zustand · state client
  • TanStack Query · state serveur
  • TanStack Form + Zod · formulaires & validation
  • next-intl · i18n
  • NextAuth · auth & routes protégées
  • Astro.js · génération statique

Backend

  • NestJS · APIs REST modulaires, JWT, RBAC
  • Node.js / Express.js
  • GraphQL
  • Puppeteer · génération de PDF

Mobile

  • React Native · apps multiplateformes

Données

  • PostgreSQL
  • Prisma ORM · modélisation & migrations
  • MySQL
  • MongoDB / Mongoose

Qualité & tests

  • Unit testing · couverture de la logique métier
  • Storybook · documentation de composants
  • Swagger · documentation d’API

Observabilité

  • Sentry · suivi des erreurs en production

DevOps & infrastructure

  • Docker · environnements reproductibles
  • Docker Compose
  • CI/CD · pipelines de build & déploiement
  • AWS S3 · stockage de fichiers

Outils & collaboration

  • Git
  • Jira
  • Redmine
  • Figma · intégration de maquettes

Notes d'ingénierie

Articles

Des notes courtes et pratiques sur l'architecture, le clean code et le développement assisté par IA. Le genre de choses que je partagerais dans une revue de code.

L'IA écrit le code. La revue reste la vôtre.

Le développement assisté par IA a déplacé le goulot d'étranglement : écrire le code est devenu rapide, le juger reste lent. Cela change le rôle d'un développeur senior, et rend la compétence de revue plus précieuse, pas moins.

  • Développement assisté par IA
  • Revue de code
  • Clean code

Les frontières avant les abstractions

Les codebases les plus pénibles ne manquent pas d'abstraction. Elles ont des abstractions au mauvais endroit. Les frontières viennent d'abord ; les abstractions se méritent ensuite.

  • Architecture
  • Clean code
  • Refactoring

Contact

Discutons

Le moyen le plus rapide de me joindre est l'email. Je suis ouvert aux discussions d'architecture, de leadership technique, ou à un second avis sur votre codebase.

ou retrouvez-moi sur LinkedIn