Jour 8 Day 8 · jeudi 6 août 2026 Thursday 6 August 2026 DevOps Fondamental
CI/CD & l'automatisation des déploiements CI/CD & deployment automation
Pipelines, environnements, stratégies de déploiement : savoir expliquer comment le code passe du push à la prod, une question quasi systématique en entretien de stage. Pipelines, environments, deployment strategies: knowing how code goes from push to production is a near-guaranteed question in internship interviews.
L’essentiel
La CI (Continuous Integration) consiste à intégrer le code de toute l’équipe en continu : à chaque push, un serveur récupère le code, le compile et lance les tests automatiquement. L’objectif est le feedback rapide — savoir en quelques minutes qu’un commit casse quelque chose, plutôt que de le découvrir la veille de la démo pendant une « phase d’intégration » douloureuse.
Le CD recouvre deux pratiques qu’il faut distinguer en entretien. Continuous Delivery : chaque commit qui passe le pipeline produit un artefact déployable en un clic — le déploiement en prod reste une décision humaine. Continuous Deployment : ce clic disparaît, tout commit vert sur la branche principale part en prod automatiquement. La deuxième exige une confiance totale dans les tests ; la première suffit à la plupart des équipes.
Le tout repose sur un principe : si c’est manuel, ça sera oublié ou raté un vendredi soir. On automatise pour rendre les déploiements ennuyeux.
🎤 En entretien — le recruteur ne veut pas la théorie, il veut VOTRE pipeline : « sur mon projet X, chaque push lance lint + tests ; un merge sur
mainconstruit l’image et la déploie ». Trois jobs sur un projet perso valent tous les buzzwords.
Comment ça marche
Un pipeline typique s’exécute à chaque push ou pull request, en étapes ordonnées qui échouent vite :
- Lint & analyse statique — le moins cher en premier : formatage, ESLint, typecheck. Échec en 30 secondes plutôt qu’en 10 minutes.
- Tests — unitaires d’abord, intégration ensuite. Parallélisables entre plusieurs jobs.
- Build — compilation, bundling, construction de l’image Docker.
- Artefact — le produit du build (image taguée par SHA de commit, binaire, bundle) est publié sur un registry. Règle d’or : on construit une fois, on déploie ce même artefact partout — pas de rebuild entre staging et prod.
- Déploiement — automatique vers staging, puis prod (avec ou sans approbation manuelle selon delivery/deployment).
push
│
▼
lint ──✖ stop (30 s)
│
▼
tests ──✖ stop
│
▼
build ──▶ artefact (image:sha) ──▶ registry
│
staging ◀─────┘
│ smoke tests
▼
prod (approbation ou auto)
Côté outils, GitHub Actions et GitLab CI partagent les mêmes concepts sous des noms proches. Un workflow (Actions) ou une pipeline (GitLab) est décrit en YAML versionné avec le code (.github/workflows/ci.yml, .gitlab-ci.yml). Il contient des jobs — unités d’exécution isolées, chacune sur une machine fraîche — regroupés en stages (GitLab) ou ordonnés par needs (Actions). Les jobs tournent sur des runners : machines hébergées par la plateforme ou auto-hébergées (self-hosted, utile pour du GPU ou un réseau privé). Comme chaque job part d’un environnement vierge, le cache (node_modules, ~/.cargo, couches Docker) est ce qui fait la différence entre un pipeline de 3 minutes et un de 15.
Les mêmes concepts, en vrai, dans un workflow Actions minimal :
name: ci
on: [push]
jobs:
test:
runs-on: ubuntu-latest # runner hébergé par GitHub
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm } # le cache qui change tout
- run: npm ci
- run: npm run lint # le moins cher d'abord (fail fast)
- run: npm test
build:
needs: test # ne tourne que si `test` est vert
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# taguée par SHA : le MÊME artefact ira en staging et en prod
- run: docker build -t ghcr.io/org/api:${{ github.sha }} .
- run: docker push ghcr.io/org/api:${{ github.sha }}
env:
GH_TOKEN: ${{ secrets.GHCR_TOKEN }} # chiffré côté plateforme
💡 Réflexe à montrer — le tag par SHA n’est pas un détail : c’est lui qui rend le « build once, deploy everywhere » vérifiable.
api:3f2c1d9est identique bit à bit en staging et en prod ;api:latestne garantit rien.
Les environnements (staging, production) portent leur configuration et leurs secrets : stockés chiffrés côté plateforme, injectés en variables d’environnement à l’exécution, masqués dans les logs. Jamais dans le repo, jamais dans l’image Docker.
Concepts clés à maîtriser
- Delivery vs deployment : la distinction précise (voir ci-dessus) est une question piège classique. La différence tient en une phrase : qui appuie sur le bouton.
- Stratégies de déploiement : trois façons de mettre la nouvelle version face au trafic — les compromis tiennent dans un tableau :
| Rolling | Blue-green | Canary | |
|---|---|---|---|
| Principe | instances remplacées une à une | 2 environnements, bascule totale | 1-5 % du trafic d’abord |
| Rollback | redéployer l’artefact précédent | rebasculer (instantané) | couper le canary |
| Coût infra | aucun surcoût | infra doublée | faible |
| Bug en prod | s’étend au fil de la bascule | 100 % du trafic d’un coup | 1-5 % des utilisateurs |
| Prérequis | rétro-compatibilité (2 versions coexistent) | — | métriques solides + routage fin |
- Rollback : redéployer l’artefact précédent (immutable, donc toujours disponible). Le vrai piège : les migrations de base de données, rarement réversibles — d’où la pratique des migrations rétro-compatibles (expand/contract : ajouter la colonne, migrer, supprimer l’ancienne plus tard).
- Trunk-based development + feature flags : tout le monde merge sur
mainfréquemment (branches courtes, < 1-2 jours), et le code inachevé part en prod désactivé derrière un flag. On sépare ainsi déploiement (mettre le code sur les serveurs) et release (l’activer pour les utilisateurs) — et un flag se coupe en secondes, plus vite que n’importe quel rollback.
En entretien
« Quelle différence entre continuous delivery et continuous deployment ? » — Delivery : chaque commit vert est déployable, un humain décide quand déployer en prod. Deployment : le déploiement en prod est lui aussi automatique, sans intervention. Bonus : préciser que le deployment intégral exige des tests auxquels on fait vraiment confiance et souvent des feature flags pour découpler release et déploiement.
« Décris le pipeline que tu mettrais en place pour une API Node » — Sur chaque push : lint + typecheck, tests unitaires, tests d’intégration contre un Postgres jetable, build de l’image Docker taguée par SHA, push sur le registry. Sur main : déploiement auto en staging, smoke tests, puis prod avec approbation. Mentionner le cache npm et le fail-fast (lint avant les tests).
« Blue-green vs canary, tu choisis quoi ? » — Blue-green : bascule totale et rollback instantané, mais infra doublée et le bug touche 100 % du trafic dès la bascule. Canary : exposition progressive, le bug ne touche que 1-5 % des utilisateurs, mais il faut de bonnes métriques et de quoi router finement le trafic. Canary si l’observabilité suit, blue-green sinon.
« Comment gères-tu les secrets dans un pipeline ? » — Secrets chiffrés de la plateforme (GitHub Environments, GitLab CI/CD variables masquées et protégées), scoping par environnement, injection à l’exécution seulement. Jamais commités, jamais dans les args de build d’une image, jamais affichés — et rotation si un secret a fuité dans un log.
« Un déploiement casse la prod, tu fais quoi ? » — D’abord couper le feature flag si le changement en a un. Sinon rollback vers l’artefact précédent — possible parce qu’il est immuable et que les migrations sont rétro-compatibles. Ensuite seulement : reproduire, corriger, et ajouter le test qui aurait attrapé le bug.
Pièges & idées reçues
⚠️ Secrets dans les logs — un
echo $DATABASE_URLde debug, un outil verbeux qui affiche sa config, et le secret est archivé pour toujours dans les logs du job. Le masquage automatique ne couvre pas un secret transformé (base64, URL-encodé). Un secret leaké se révoque, il ne se supprime pas des logs.
- Tests flaky : un test qui échoue aléatoirement détruit la confiance dans le pipeline — l’équipe se met à relancer les jobs sans lire les logs, et un vrai bug finit par passer. Le retry automatique masque le symptôme ; la seule vraie réponse est de corriger ou quarantainer le test.
- Pipeline lent : au-delà de ~10 minutes, les devs cessent d’attendre le résultat, empilent les commits et contournent le process. Paralléliser les tests, soigner le cache, sortir les jobs lourds du chemin critique.
- « La CI, c’est juste lancer les tests » — non : c’est surtout la pratique d’intégrer fréquemment sur une branche partagée. Une branche de trois semaines avec un pipeline vert n’est pas de l’intégration continue.
- Rebuilder l’image entre staging et prod : deux builds ne sont jamais garantis identiques (dépendances mises à jour entre-temps). On promeut le même artefact d’un environnement à l’autre.
Pour aller plus loin
- GitHub Actions — documentation et GitLab CI/CD
- Martin Fowler — Continuous Integration, l’article de référence
- trunkbaseddevelopment.com : branches courtes et feature flags, avec les schémas qui vont bien
- Exercice concret : ajouter un workflow GitHub Actions (lint + tests + build Docker) sur un de vos projets — c’est le sujet de conversation idéal en entretien
The essentials
CI (Continuous Integration) means integrating the whole team’s code continuously: on every push, a server checks out the code, builds it and runs the tests automatically. The goal is fast feedback — learning within minutes that a commit breaks something, instead of discovering it the day before the demo during a painful “integration phase”.
CD covers two practices you must distinguish in an interview. Continuous Delivery: every commit that passes the pipeline produces an artifact that is deployable in one click — deploying to production remains a human decision. Continuous Deployment: that click disappears, and every green commit on the main branch ships to production automatically. The second requires total confidence in your tests; the first is enough for most teams.
Everything rests on one principle: anything manual will be forgotten or botched on a Friday evening. We automate to make deployments boring.
🎤 In an interview — the recruiter doesn’t want theory, they want YOUR pipeline: “on my project X, every push runs lint + tests; a merge to
mainbuilds the image and deploys it”. Three jobs on a personal project beat any buzzword.
How it works
A typical pipeline runs on every push or pull request, in ordered steps that fail fast:
- Lint & static analysis — the cheapest checks first: formatting, ESLint, typecheck. Failing in 30 seconds beats failing in 10 minutes.
- Tests — unit first, integration next. Parallelizable across several jobs.
- Build — compilation, bundling, building the Docker image.
- Artifact — the build output (image tagged with the commit SHA, binary, bundle) is published to a registry. Golden rule: build once, deploy that same artifact everywhere — no rebuilding between staging and production.
- Deployment — automatic to staging, then to production (with or without manual approval, depending on delivery vs deployment).
push
│
▼
lint ──✖ stop (30 s)
│
▼
tests ──✖ stop
│
▼
build ──▶ artifact (image:sha) ──▶ registry
│
staging ◀─────┘
│ smoke tests
▼
prod (approval or auto)
Tool-wise, GitHub Actions and GitLab CI share the same concepts under similar names. A workflow (Actions) or pipeline (GitLab) is described in YAML versioned with the code (.github/workflows/ci.yml, .gitlab-ci.yml). It contains jobs — isolated execution units, each on a fresh machine — grouped into stages (GitLab) or ordered with needs (Actions). Jobs run on runners: machines hosted by the platform or self-hosted (useful for GPUs or private networks). Since every job starts from a clean environment, caching (node_modules, ~/.cargo, Docker layers) is what separates a 3-minute pipeline from a 15-minute one.
The same concepts, for real, in a minimal Actions workflow:
name: ci
on: [push]
jobs:
test:
runs-on: ubuntu-latest # GitHub-hosted runner
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm } # the cache that changes everything
- run: npm ci
- run: npm run lint # cheapest first (fail fast)
- run: npm test
build:
needs: test # only runs if `test` is green
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# tagged with the SHA: the SAME artifact goes to staging and prod
- run: docker build -t ghcr.io/org/api:${{ github.sha }} .
- run: docker push ghcr.io/org/api:${{ github.sha }}
env:
GH_TOKEN: ${{ secrets.GHCR_TOKEN }} # encrypted platform-side
💡 Reflex to show — the SHA tag is not a detail: it’s what makes “build once, deploy everywhere” verifiable.
api:3f2c1d9is bit-for-bit identical in staging and production;api:latestguarantees nothing.
Environments (staging, production) carry their own configuration and secrets: stored encrypted on the platform side, injected as environment variables at runtime, masked in logs. Never in the repo, never baked into the Docker image.
Key concepts to master
- Delivery vs deployment: the precise distinction (above) is a classic trick question. The difference fits in one sentence: who presses the button.
- Deployment strategies: three ways to put the new version in front of traffic — the trade-offs fit in one table:
| Rolling | Blue-green | Canary | |
|---|---|---|---|
| Principle | instances replaced one by one | 2 environments, full switch | 1-5% of traffic first |
| Rollback | redeploy the previous artifact | switch back (instant) | kill the canary |
| Infra cost | none | doubled infrastructure | low |
| Bug in prod | spreads as the rollout progresses | 100% of traffic at once | 1-5% of users |
| Prerequisite | backward compatibility (2 versions coexist) | — | solid metrics + fine-grained routing |
- Rollback: redeploy the previous artifact (immutable, so always available). The real trap is database migrations, which are rarely reversible — hence backward-compatible migrations (expand/contract: add the column, migrate, drop the old one later).
- Trunk-based development + feature flags: everyone merges to
mainfrequently (short-lived branches, < 1-2 days), and unfinished code ships to production disabled behind a flag. This separates deployment (putting code on servers) from release (turning it on for users) — and a flag switches off in seconds, faster than any rollback.
In an interview
“What’s the difference between continuous delivery and continuous deployment?” — Delivery: every green commit is deployable, a human decides when to ship to production. Deployment: the production deploy is automatic too, with no intervention. Bonus: point out that full deployment requires tests you genuinely trust, and usually feature flags to decouple release from deployment.
“Describe the pipeline you would set up for a Node API” — On every push: lint + typecheck, unit tests, integration tests against a throwaway Postgres, build the Docker image tagged with the SHA, push to the registry. On main: auto-deploy to staging, smoke tests, then production with approval. Mention npm caching and fail-fast ordering (lint before tests).
“Blue-green vs canary — which do you pick?” — Blue-green: full switch and instant rollback, but doubled infrastructure and a bug hits 100% of traffic at switch time. Canary: gradual exposure, a bug only touches 1-5% of users, but you need good metrics and fine-grained traffic routing. Canary if your observability is up to it, blue-green otherwise.
“How do you handle secrets in a pipeline?” — The platform’s encrypted secrets (GitHub Environments, GitLab masked and protected CI/CD variables), scoped per environment, injected only at runtime. Never committed, never in an image’s build args, never printed — and rotated if a secret ever leaked into a log.
“A deployment breaks production — what do you do?” — First, kill the feature flag if the change has one. Otherwise roll back to the previous artifact — possible because it’s immutable and the migrations are backward-compatible. Only then: reproduce, fix, and add the test that would have caught the bug.
Pitfalls & misconceptions
⚠️ Secrets in logs — one debugging
echo $DATABASE_URL, one verbose tool printing its config, and the secret is archived forever in the job logs. Automatic masking doesn’t cover a transformed secret (base64, URL-encoded). A leaked secret gets revoked, not deleted from the logs.
- Flaky tests: a test that fails randomly destroys trust in the pipeline — the team starts re-running jobs without reading logs, and a real bug eventually slips through. Automatic retries mask the symptom; the only real answer is fixing or quarantining the test.
- Slow pipeline: past ~10 minutes, developers stop waiting for the result, stack up commits and work around the process. Parallelize tests, invest in caching, move heavy jobs off the critical path.
- “CI is just running the tests” — no: above all it’s the practice of integrating frequently into a shared branch. A three-week-old branch with a green pipeline is not continuous integration.
- Rebuilding the image between staging and production: two builds are never guaranteed identical (dependencies updated in between). You promote the same artifact from one environment to the next.
Going further
- GitHub Actions — documentation and GitLab CI/CD
- Martin Fowler — Continuous Integration, the reference article
- trunkbaseddevelopment.com: short-lived branches and feature flags, with helpful diagrams
- Hands-on exercise: add a GitHub Actions workflow (lint + tests + Docker build) to one of your projects — the perfect conversation starter in an interview