Skip to content
Amine Mekki

Amine Mekki

Senior Full-Stack TypeScript Engineer & Team Lead

I design and build web systems that stay maintainable: Next.js and React on the front, NestJS and PostgreSQL behind, and engineering judgment throughout, including over AI-generated code.

  • Based in Sfax, Tunisia
  • Working languages English · French · Arabic

About

Engineering over output

Full-stack JavaScript engineer with over 5 years building scalable web and mobile applications across the modern TypeScript ecosystem: React, Next.js, Node.js and NestJS. I have led cross-functional teams, architected products end to end and driven delivery in Agile environments, in Spain and in Tunisia. What I care about is not the framework list. It is whether the system stays understandable, testable and changeable after I leave the room. That shapes how I design APIs, model data and review code. It also shapes how I use AI: as an accelerator whose output gets the same scrutiny as any pull request.

What I bring to a team

  • Technical leadership: setting priorities, keeping stakeholders aligned, delivering on time
  • End-to-end architecture: modular NestJS APIs, JWT auth with refresh tokens, RBAC, validation layers
  • Data modeling with PostgreSQL and Prisma, migration strategy included
  • Frontend architecture: design systems, atomic design, TanStack Query for server state
  • Production habits by default: Sentry monitoring, S3 storage, PDF generation, i18n
  • DevOps foundations with Docker and Docker Compose so environments stay reproducible

Engineering philosophy

How I approach software

Frameworks change. The reasons software becomes expensive to change do not. These are the principles I hold a codebase to, including my own.

Build for humans

Code is read far more often than it is written. I optimize for the next engineer: explicit names, small modules, boundaries that match how people think about the domain. If a change requires a tour guide, the architecture has already failed.

Architecture with purpose

Every layer, pattern and abstraction has to earn its place by solving a problem the project actually has. I would rather ship a boring, direct design than a speculative one, then refactor deliberately when real requirements show up.

Quality is more than tests

A green pipeline is not the finish line. Production quality includes observability, security, performance, clear error handling and documentation. Tests give you confidence in behavior; the rest keeps the system honest once real users arrive.

AI is an accelerator, not an architect

I use AI every day to move faster, and I review everything it produces the way I review any pull request. AI can generate a working implementation. It cannot decide whether the abstraction is right, whether the boundary is correct, or whether the code belongs in the system at all. That judgment is my job.

How I judge code

Whether it was written by a teammate, by me six months ago, or by a model, the review criteria are the same:

  • Single responsibility, honestly applied
  • Boundaries that separate domain from infrastructure
  • Explicit dependencies over hidden coupling
  • Names that state intent
  • Errors handled predictably, not decoratively
  • Testable by design, not by mocking everything
  • Abstractions introduced when duplication hurts, not before
  • Complexity proportional to the problem

AI-assisted engineering

Code that works vs. code that ships

AI-assisted development moved the bottleneck. Producing a working implementation is fast now; knowing whether it should reach production is the hard part. Syntactically correct code can still hide poor boundaries, N+1 queries, weak typing, missing error paths and abstractions nobody needs. Here are two examples of what a careful review changes.

The N+1 the type checker can’t see

Asked for "each user’s order total", the assistant produced this service function. It compiles, the tests on three fixture users pass, and the review looks trivial. Until you count the queries.

Generated: works, shouldn't ship
// "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
}
After review
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,
  }))
}

Review findings

  • One query per user

    The loop issues a findMany per user: 10,000 users means 10,001 queries. This is invisible in development data and a production incident at scale.

  • Aggregation done in application code

    The database can compute sums natively. Pulling every order into memory to reduce it in JavaScript moves work to the slowest layer.

  • No explicit return type

    Callers see an inferred anonymous shape. A one-line domain type documents intent and survives refactors of the internals.

  • Unbounded field selection

    findMany() with no select drags every column across the wire when only id and name are used.

Why it matters

Generated code optimizes for "compiles and looks right". Query patterns are where that falls apart quietly: nothing in the type system distinguishes 2 queries from 10,001. Reviewing data access paths is not optional, no matter who or what wrote the code.

The client component that didn’t need to exist

A Next.js App Router page for listing projects. The generated version reaches for the familiar SPA recipe: client component, useEffect, fetch, loading state. It works, and it ships a whole class of problems the framework had already solved.

Generated: works, shouldn't ship
'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} />
}
After review
// 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} />
}

Review findings

  • Wrong side of the client/server boundary

    Nothing here is interactive. Rendering on the server removes the fetch round-trip, the loading spinner and the layout shift entirely.

  • any[] erases the contract

    The component accepts whatever the endpoint returns. A renamed field now fails at runtime in production instead of at compile time.

  • No error path

    If the fetch fails, loading stays true forever and the user watches a spinner. Every data access needs a defined failure behavior.

  • An API route that exists only for this component

    The /api/projects endpoint duplicates data access that a server component could perform directly. More surface, more code to secure and maintain.

Why it matters

AI tools trained on years of SPA patterns reproduce them in frameworks that made those patterns obsolete. "Is this idiomatic for the platform we are on?" is a review question a model does not reliably ask itself.

Selected work

Projects

A selection of missions I led or contributed to. Full case studies are on the projects page.

Team Lead: architecture, backend, delivery

A full-stack product that digitizes the technical study and quoting of ventilation projects, led end to end: team coordination, API and data architecture, frontend foundations and DevOps setup.

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

Read case study

Technical Lead: design system, backend services, delivery

A full-stack agronomic management platform: web design system, core NestJS backend services, and front-office / back-office features in Next.js, delivered in a technical leadership role.

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

Read case study

Technical Lead: architecture, roadmaps, code quality

A cross-platform transport optimization product: web with Next.js, mobile with React Native, NestJS APIs behind both. I was responsible for architectural decisions, technical roadmaps and code quality.

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

Read case study

Full-Stack Developer

A platform generating static advertising pages: Astro.js for static generation, reusable page block components built from Figma designs, and NestJS + GraphQL services behind it.

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

Read case study

Experience

Where I've built

Over five years across product teams in Spain and Tunisia, often as the engineer responsible for architecture, code quality and delivery.

  1. Digital study & quoting tool for ventilation projects

    Team Lead & Full-Stack Developer · Soler & Palau · Barcelona, Spain

    • Led a cross-functional team to deliver a full-stack product with NestJS, Next.js 15 (App Router) and TypeScript, managing priorities, stakeholder alignment and on-time delivery.
    • Architected a modular REST API with JWT authentication (refresh tokens), RBAC, validation layers, Sentry observability, and integrations for real-time data, AWS S3 file storage, email and PDF generation via Puppeteer.
    • Designed and maintained the PostgreSQL data model and Prisma ORM layer: entity relationships, migration strategy with Prisma Migrate, and maintainable access patterns.
    • Built the frontend architecture on atomic design principles, with TanStack Query for server state, TanStack Form + Zod for validation, next-intl for i18n and NextAuth for protected routing.
    • Established DevOps foundations with Docker and Docker Compose, enabling repeatable environments and streamlined onboarding and release cycles.
    • NestJS
    • Next.js
    • TypeScript
    • PostgreSQL
    • Prisma ORM
    • TanStack Query
    • TanStack Form + Zod
    • next-intl
    • NextAuth
    • Sentry
    • AWS S3
    • Puppeteer
    • Docker
  2. Agronomic management platform

    Technical Lead & Full-Stack Developer · RAGT · Barcelona, Spain

    • Served in a leadership role: coordinated stakeholders, drove task planning and prioritization, and contributed to key software architecture and technical direction decisions.
    • Developed the web design system and core backend services with NestJS, and delivered full-stack front-office and back-office features with Next.js.
    • Next.js
    • NestJS
    • PostgreSQL
    • Prisma ORM
    • Docker
    • Figma
  3. Digital transport optimization application

    Technical Lead · Katalii – Groupe Berto · Barcelona, Spain

    • Defined architectural decisions and technical roadmaps; monitored code quality and performance metrics.
    • Delivered the web design system, backend APIs with NestJS, and the cross-platform frontend with Next.js and React Native.
    • Next.js
    • React Native
    • NestJS
    • PostgreSQL
    • Docker
    • Jira
  4. Static advertising page generation platform

    Full-Stack Developer · Le Bon Coin · Barcelona, Spain

    • Improved UI/UX from Figma designs and created new web page block components.
    • Implemented static page generation with Astro.js and developed backend features with NestJS and GraphQL.
    • Next.js
    • Astro.js
    • NestJS
    • GraphQL
    • Docker
    • Figma
  5. B2C and internal full-stack products

    Full-Stack Developer · My Insurance / GestiLot / OneWay · Sfax, Tunisia

    • Delivered multiple B2C and internal products: an insurance platform, an art market digital tool, and an internal carpooling app.
    • Conducted mobile application audits, built cross-platform interfaces with React and React Native, and implemented backend services with NestJS and MongoDB, including Swagger API documentation.
    • Provided comprehensive unit test coverage for core components and business logic.
    • React
    • React Native
    • NestJS
    • MongoDB / Mongoose
    • Swagger
    • Unit testing
    • Git
    • Redmine
  6. Internal React component library

    Frontend Developer · Piximind · Sfax, Tunisia

    • Designed and built a reusable React TypeScript component library with custom hooks, published as two separate NPM packages for enterprise-wide adoption.
    • Delivered full Storybook documentation and unit test coverage to ensure reliability and ease of integration.
    • React
    • TypeScript
    • Storybook
    • Unit testing

Stack

Technologies, in context

Not a logo wall. This is the stack I work with daily, grouped by the role it plays in a system.

Frontend

  • React
  • Next.js · App Router
  • TypeScript
  • Redux / Zustand · client state
  • TanStack Query · server state
  • TanStack Form + Zod · forms & validation
  • next-intl · i18n
  • NextAuth · auth & protected routing
  • Astro.js · static generation

Backend

  • NestJS · modular REST APIs, JWT, RBAC
  • Node.js / Express.js
  • GraphQL
  • Puppeteer · PDF generation

Mobile

  • React Native · cross-platform apps

Data

  • PostgreSQL
  • Prisma ORM · data modeling & migrations
  • MySQL
  • MongoDB / Mongoose

Quality & testing

  • Unit testing · coverage of core business logic
  • Storybook · component documentation
  • Swagger · API documentation

Observability

  • Sentry · error monitoring in production

DevOps & infrastructure

  • Docker · reproducible environments
  • Docker Compose
  • CI/CD · build & deployment pipelines
  • AWS S3 · file storage

Tooling & collaboration

  • Git
  • Jira
  • Redmine
  • Figma · design handoff

Engineering notes

Articles

Short, practical notes on architecture, clean code and AI-assisted development. The kind of thing I would share in a code review.

AI writes the code. The review is still yours.

AI-assisted development moved the bottleneck from writing code to judging it. That changes what a senior engineer is for, and it makes review skills more valuable, not less.

  • AI-assisted development
  • Code review
  • Clean code

Boundaries before abstractions

Most painful codebases don't suffer from too little abstraction. They suffer from abstractions in the wrong places. Boundaries come first; abstractions are what you earn afterwards.

  • Architecture
  • Clean code
  • Refactoring

Tests that buy confidence, not coverage

Coverage percentages measure execution, not confidence. A useful test suite is designed around a different question: what would let me refactor without fear?

  • Testing
  • Clean code
  • TypeScript

Contact

Let's talk

The fastest way to reach me is email. Happy to talk about architecture, team leadership, or a codebase you would like a second opinion on.

or find me on LinkedIn