DEV Community

Roméo DOSsOu
Roméo DOSsOu

Posted on

Comment travailler en équipe avec Git — Workflow complet pour débutants

Branches, Pull Requests, code review, conflits : tout ce que personne ne t'a vraiment expliqué.


Quand tu apprends Git seul, tu maîtrises git add, git commit, git push. Puis tu rejoins une équipe et tu réalises que tu ne sais pas vraiment travailler à plusieurs. Les branches te font peur. Les conflits te paralysent. Les Pull Requests t'intimidaient.

Cet article va changer ça.

On va simuler une vraie session de travail en équipe : deux développeurs (Alice et Bob) qui implémentent deux features en parallèle, se font reviewer mutuellement, tombent sur un conflit, et le résolvent proprement. Chaque commande est expliquée. Chaque décision est justifiée.


Le contexte

On travaille sur une API REST Node.js. L'équipe utilise le GitHub Flow — le workflow le plus répandu dans l'industrie.

Principe fondamental du GitHub Flow :

  • main est toujours stable et déployable
  • Chaque feature vit sur sa propre branche
  • On merge via une Pull Request reviewée par un pair
  • On ne merge que si les tests passent

Deux features sont à développer en parallèle :

  • Alicefeature/PROJ-101-user-authentication (authentification JWT)
  • Bobfeature/PROJ-102-user-profile (profil utilisateur)

Les deux touchent src/routes.js — le conflit est inévitable. Et c'est voulu.


Phase 1 — Poser les fondations

Le lead initialise le projet avec une structure propre et un premier commit :

mkdir api-projet && cd api-projet
git init
mkdir -p src/{api,middleware,models,services} tests
git add .
git commit -m "chore: initialiser la structure du projet API"
Enter fullscreen mode Exit fullscreen mode

Pourquoi ce format de message ?

chore: initialiser la structure du projet API suit la convention Conventional Commits. Le format est type(scope): description. Les types courants :

Type Usage
feat Nouvelle fonctionnalité
fix Correction de bug
chore Maintenance, config
test Ajout/modification de tests
refactor Refactorisation sans changement de comportement

Un historique de commits bien formaté se lit comme un journal de bord. Dans 6 mois, toi ou un collègue pourrez comprendre l'évolution du projet sans lire une seule ligne de code.


Phase 2 — Alice travaille sur l'authentification

Créer une branche, c'est créer un espace de travail isolé

git switch -c feature/PROJ-101-user-authentication
Enter fullscreen mode Exit fullscreen mode

git switch -c crée la branche ET bascule dessus. À ce moment, la branche est une copie exacte de main. Ce que Alice fait ici n'affecte pas main ni le travail de Bob.

Des commits atomiques, pas un gros commit fourre-tout

Alice fait 5 commits distincts — un par responsabilité :

# 1. Le middleware JWT
git commit -m "feat(auth): implémenter le middleware JWT"

# 2. Le service (logique métier pure)
git commit -m "feat(auth): ajouter le service de génération JWT"

# 3. Le controller (pont entre HTTP et service)
git commit -m "feat(auth): ajouter le controller POST /auth/login"

# 4. Les routes
git commit -m "feat(auth): enregistrer les routes d'authentification"

# 5. Les tests
git commit -m "test(auth): ajouter les tests du module authentification"
Enter fullscreen mode Exit fullscreen mode

Pourquoi pas tout en un seul commit ?

Parce que si demain le controller a un bug et qu'on doit faire un git revert, on veut pouvoir annuler seulement le controller — pas le middleware, pas le service, pas les tests. Des commits atomiques = des rollbacks précis.

Une décision sécurité intentionnelle

Dans auth-controller.js, le message d'erreur est volontairement identique dans les deux cas :

// L'email n'existe pas dans la base
return res.status(401).json({ error: 'Identifiants invalides' });

// Le mot de passe est faux
return res.status(401).json({ error: 'Identifiants invalides' });
Enter fullscreen mode Exit fullscreen mode

Pourquoi ? Si les messages étaient différents ("Email inconnu" vs "Mot de passe incorrect"), un attaquant pourrait tester des milliers d'emails pour savoir lesquels sont enregistrés. On appelle ça l'énumération de comptes — une faille de sécurité classique.


Phase 3 — Bob travaille en parallèle

L'isolation des branches en pratique

git switch main          # Bob revient sur main
git switch -c feature/PROJ-102-user-profile
Enter fullscreen mode Exit fullscreen mode

Bob part de main — pas de la branche d'Alice. Il développe GET /users/:id et modifie aussi src/routes.js sans le savoir.

Visualisation de la situation :

main          ──●────────────────────────
                 \              \
Alice             ●──●──●──●──●  (routes.js version Alice)
                 \
Bob               ●──●──●        (routes.js version Bob)
Enter fullscreen mode Exit fullscreen mode

Les deux branches ont divergé depuis le même point. Elles vont devoir se rejoindre.

Ne jamais exposer les données sensibles

// user-controller.js
const { password_hash, ...safeUser } = user;
res.json(safeUser); // password_hash n'est jamais envoyé au client
Enter fullscreen mode Exit fullscreen mode

C'est du JavaScript destructuring : on extrait password_hash pour l'écarter, et on retourne tout le reste. Simple et efficace.


Phase 4 — Pull Requests et code review

Alice ouvre la PR #1. Bob la review.

Ce qu'une bonne PR contient

Titre  : feat(auth): implémenter l'authentification JWT (PROJ-101)
Base   : main ← feature/PROJ-101-user-authentication

## Ce que fait cette PR
- Middleware de vérification des tokens JWT
- Service de génération/vérification
- Endpoint POST /auth/login

## Comment tester
npm test -- --testPathPattern=auth

## Checklist
✅ Tests ajoutés | ✅ Pas de secrets hardcodés
Enter fullscreen mode Exit fullscreen mode

Une bonne description de PR répond à 3 questions : quoi (ce qui change), pourquoi (le contexte), comment (tester).

Bob trouve une faille critique

[SUGGESTION] JWT_SECRET ne devrait pas être optionnel en production.
Si JWT_SECRET n'est pas défini, les tokens sont signés avec
'dev-secret-change-in-prod' — n'importe qui connaissant ce secret
peut forger des tokens valides.
Enter fullscreen mode Exit fullscreen mode

Alice corrige immédiatement :

if (!process.env.JWT_SECRET && process.env.NODE_ENV === 'production') {
    throw new Error('JWT_SECRET est requis en production');
}
Enter fullscreen mode Exit fullscreen mode

C'est ça la valeur d'une code review. Un deuxième regard voit ce qu'on ne voit plus à force de travailler sur le même code.

Bob approuve. Alice merge dans main.


Phase 5 — Le conflit (ne pas paniquer)

Bob tente de mettre à jour sa branche :

git rebase main
Enter fullscreen mode Exit fullscreen mode

Git s'arrête :

CONFLICT (content): Merge conflict in src/routes.js
Enter fullscreen mode Exit fullscreen mode

Comprendre les marqueurs de conflit

<<<<<<< HEAD
const { login } = require('./api/auth-controller');
const { authenticate } = require('./middleware/auth');
=======
const { getUserById } = require('./api/user-controller');
>>>>>>> c8f3b2a
Enter fullscreen mode Exit fullscreen mode
  • Entre <<<<<<< HEAD et ======= : la version de main (le travail d'Alice)
  • Entre ======= et >>>>>>> : la version de Bob

Git ne sait pas laquelle choisir — il n'est pas qualifié pour faire ce jugement métier. C'est toujours une décision humaine.

La résolution intelligente

Bob ne choisit pas entre Alice et lui — il combine les deux :

// Version finale — combine les deux features
const { login } = require('./api/auth-controller');
const { authenticate } = require('./middleware/auth');
const { getUserById } = require('./api/user-controller');

router.post('/auth/login', login);
router.get('/users/:id', authenticate, getUserById); // Bob ajoute authenticate !
router.get('/protected', authenticate, ...);
Enter fullscreen mode Exit fullscreen mode

En voyant le middleware d'Alice, Bob réalise que /users/:id ne devrait pas être public. Le rebase l'a forcé à prendre une décision de sécurité qu'il aurait pu manquer.

git add src/routes.js
git rebase --continue
git push --force-with-lease origin feature/PROJ-102-user-profile
Enter fullscreen mode Exit fullscreen mode

Pourquoi --force-with-lease et pas --force ?

--force écrase la branche distante sans vérification. Si un collègue a poussé quelque chose entre-temps, son travail est perdu.

--force-with-lease vérifie d'abord que personne n'a poussé depuis ta dernière synchronisation. Si c'est le cas, il refuse et t'alerte. C'est la version respectueuse de --force.

Rebase vs Merge — le débat tranché

# Avec merge
* Merge commit
|\
| * commit Bob
| * commit Bob
* Merge pull request d'Alice
|\
| * commit Alice

# Avec rebase
* commit Bob
* commit Bob
* Merge pull request d'Alice
* commit Alice
* commit initial
Enter fullscreen mode Exit fullscreen mode

Le rebase donne un historique linéaire et lisible. On utilise :

  • rebase pour mettre à jour sa branche feature depuis main
  • merge --no-ff pour intégrer une PR dans main (conserve la trace de la branche)

Phase 6 — Merge final et leçon sur le scope des PRs

Alice review la PR de Bob et suggère un refactor du router. Bob répond :

"Excellente suggestion — je crée un ticket PROJ-115. Cette PR reste focalisée sur le profil utilisateur."

C'est la bonne réponse. Une PR qui gonfle en cours de review :

  • Retarde le merge
  • Mélange les responsabilités
  • Complique les rollbacks potentiels
  • Fatigue le reviewer

Une PR = une feature = un ticket. Toujours.


L'historique final : une histoire lisible

*   Merge pull request #2 — feat(users)
|\
| * test(users): tests GET /users/:id
| * feat(users): route GET /users/:id
| * feat(users): controller GET /users/:id
*   Merge pull request #1 — feat(auth)
|\
| * fix(auth): JWT_SECRET obligatoire en prod
| * test(auth): tests authentification
| * feat(auth): routes auth
| * feat(auth): controller login
| * feat(auth): service JWT
| * feat(auth): middleware JWT
|/
* chore: initialiser le projet
Enter fullscreen mode Exit fullscreen mode

N'importe quel développeur qui rejoint l'équipe peut lire cet historique et comprendre comment le projet a évolué. C'est l'objectif.


Ce qu'on a appris

Sur Git :

  • Les branches isolent le travail — on ne pollue pas main
  • Les commits atomiques permettent des rollbacks précis
  • Le rebase nettoie l'historique avant de merger
  • Les conflits ne sont pas des bugs — ce sont des signaux que deux personnes ont travaillé sur la même zone

Sur le travail en équipe :

  • Une PR décrit le quoi et le pourquoi, pas seulement le comment
  • La code review est collaborative, pas confrontationnelle
  • Le scope d'une PR ne grandit pas en cours de review
  • Une deuxième paire d'yeux attrape ce qu'on ne voit plus

Sur la sécurité :

  • JWT_SECRET obligatoire en production
  • Messages d'erreur génériques pour éviter l'énumération de comptes
  • Ne jamais exposer les données sensibles dans les réponses API
  • Les routes avec données personnelles sont toujours protégées

Le code source complet de ce projet est disponible sur GitHub.

N'hésite pas à forker et rejouer les phases toi-même — c'est en faisant qu'on retient.

Top comments (0)