Skip to content

Latest commit

 

History

History
454 lines (304 loc) · 28.7 KB

File metadata and controls

454 lines (304 loc) · 28.7 KB

🔝 Retour au Sommaire

43.3.4 — Stratégie de migration progressive : quand et comment introduire Rust

Module 15 : Interopérabilité · Niveau Expert


Introduction

Les trois sections précédentes ont couvert la mécanique de l'interopérabilité C++/Rust — FFI manuelle, cxx, autocxx. Ce sont des outils. Cette section traite de la stratégie : dans quel contexte introduire Rust dans une base de code C++ existante, par où commencer, comment structurer la frontière, comment gérer le build system mixte, et comment accompagner les équipes dans cette transition.

Ce n'est pas une décision purement technique. Introduire un second langage système dans une organisation a des implications sur le recrutement, la formation, la maintenance, la dette technique et la culture d'ingénierie. Les organisations qui réussissent cette transition sont celles qui l'abordent comme un projet d'ingénierie à part entière — avec une stratégie incrémentale, des critères de décision explicites et des points de mesure.


43.3.4.1 — Évaluer la pertinence : faut-il introduire Rust ?

Questions préalables

Avant toute décision technique, trois questions structurantes :

Le problème est-il réellement lié à la sécurité mémoire ? Si les vulnérabilités du projet sont principalement des erreurs logiques, des problèmes de configuration ou des failles dans les protocoles, Rust n'apporte pas de bénéfice direct. Le borrow checker ne protège pas contre les erreurs de logique métier. Il protège contre les use-after-free, les buffer overflows, les data races et les double-free. Si ces catégories représentent une part significative des bugs et vulnérabilités du projet, l'argument pour Rust est solide.

Le C++ durci suffit-il ? Les sanitizers (AddressSanitizer, ThreadSanitizer), l'analyse statique (clang-tidy), le fuzzing (AFL++, LibFuzzer), les Safety Profiles C++ et les pratiques de codage défensif (section 45) réduisent significativement les risques mémoire sans changer de langage. Pour certains projets, ce durcissement est suffisant et bien plus économique qu'une introduction de Rust. La question honnête est : les outils C++ existants sont-ils appliqués systématiquement, et malgré cela, les vulnérabilités mémoire persistent-elles ?

L'organisation peut-elle absorber le coût ? Introduire Rust implique de former les développeurs, de maintenir un build system mixte, de recruter ou de convertir des ingénieurs, et de gérer deux écosystèmes d'outils. Pour une équipe de 3 développeurs C++ sur un projet stable avec peu de vulnérabilités, le coût est probablement disproportionné. Pour une équipe de 50 développeurs travaillant sur un composant réseau exposé à Internet avec un historique de CVE mémoire, l'investissement se justifie.

Matrice de décision

Facteur Favorable à l'introduction de Rust Favorable au statu quo (C++ durci)
Historique de CVE mémoire Fréquent, récurrent Rare, maîtrisé
Exposition aux entrées non fiables Élevée (réseau, parsing, données utilisateur) Faible (calcul interne, batch)
Contraintes réglementaires CRA, directives NSA/CISA applicables Pas de contrainte spécifique
Taille de l'équipe Suffisante pour absorber la montée en compétence Trop petite pour se disperser
Nouveau composant à écrire Oui — greenfield idéal pour Rust Non — maintenance pure
Base de code existante Modulaire, frontières claires Monolithique, couplage fort
Recrutement Pool Rust accessible Pool Rust inexistant localement
Durée de vie du projet Long terme (5+ ans) Court terme ou fin de vie

L'anti-pattern : la réécriture totale

L'erreur la plus coûteuse est de décider une réécriture complète de la base C++ en Rust. Les réécriture totales échouent pour des raisons bien documentées dans l'industrie logicielle : le nouveau code n'a pas bénéficié des années de corrections de bugs de l'ancien, les fonctionnalités sont perdues ou mal comprises pendant la traduction, et le projet prend inévitablement du retard.

La stratégie qui fonctionne est la cohabitation : le C++ existant reste en place, durci et maintenu ; le Rust est introduit de manière ciblée, composant par composant, aux endroits où il apporte le plus de valeur.


43.3.4.2 — Par où commencer : identifier les composants candidats

Critères de sélection d'un premier composant Rust

Le premier composant Rust dans un projet C++ doit être choisi avec soin. Il doit démontrer la valeur de l'approche sans mettre en danger le projet. Les critères idéaux :

Frontière bien définie. Le composant a une interface claire avec le reste du code — un nombre limité de fonctions, des types de données simples, une sémantique de propriété explicite. Plus la frontière est nette, plus l'interopérabilité est simple.

Surface d'exposition élevée. Le composant traite des données provenant de sources non fiables : parsing de protocoles réseau, décodage de formats de fichiers, traitement d'entrées utilisateur, parsing de certificats ou de tokens. C'est là que les vulnérabilités mémoire ont le plus d'impact.

Taille modérée. Le premier composant ne doit être ni trivial (l'effort ne serait pas justifié) ni massif (le risque serait trop élevé). Quelques milliers de lignes est un bon ordre de grandeur.

Découplage du reste. Le composant ne doit pas nécessiter un accès profond aux structures internes C++. Il reçoit des données, les traite, et retourne un résultat. Ce pattern de "composant de traitement" se prête naturellement à une interface FFI propre.

Testabilité indépendante. Le composant peut être testé de manière autonome — en Rust avec les tests unitaires Rust, et via l'interface FFI depuis les tests d'intégration C++.

Exemples de composants candidats typiques

Composant Pourquoi c'est un bon candidat
Parser de protocole (HTTP, DNS, TLS) Surface d'attaque maximale, données non fiables, bugs mémoire fréquents
Décodeur de format (JSON, XML, images, vidéo) Parsing complexe, historique de CVE, frontière naturelle (bytes → structure)
Module de cryptographie Sensibilité maximale, surface réduite, interface claire
Validation d'entrées utilisateur Premier rempart, pattern entrée → résultat
Composant réseau (buffer management, framing) Manipulation intensive de buffers, timing critique
Bibliothèque de sérialisation/désérialisation Interface claire (bytes ↔ objets), testable indépendamment

Anti-patterns : composants à éviter en premier

Composant Pourquoi c'est un mauvais premier choix
Cœur métier avec couplage profond Frontière FFI trop large, trop de types C++ à traverser
GUI ou couche de présentation Interaction complexe avec les frameworks C++, peu de gain sécurité
Code de performance critique déjà optimisé Le gain sécurité ne justifie pas le risque de régression perf
Code legacy peu compris Impossible de valider la correction de la réécriture
Composant minuscule (< 200 lignes) Effort d'intégration disproportionné

43.3.4.3 — Architecture de la frontière

Le modèle en couches

L'architecture recommandée pour un composant Rust intégré dans un projet C++ suit le modèle en couches décrit dans les sections précédentes, appliqué à l'échelle du projet :

┌──────────────────────────────────────────────────┐
│              Application C++ existante           │
│   (code métier, frameworks, UI, orchestration)   │
├──────────────────────────────────────────────────┤
│              Interface C++ ← → Rust              │
│   (header C-compatible ou bridge cxx)            │
├──────────────────────────────────────────────────┤
│            Wrapper Rust idiomatique              │
│   (API Rust safe, gère UniquePtr/Pin/Result)     │
├──────────────────────────────────────────────────┤
│              Composant Rust pur                  │
│   (logique métier, 100% safe Rust)               │
└──────────────────────────────────────────────────┘

L'objectif est que le composant Rust pur (la couche du bas) ne contienne aucun code unsafe et aucune dépendance à FFI. Il est développé, testé et raisonné comme du Rust standard. Tout le unsafe est concentré dans la couche d'interface — soit dans les wrappers autocxx/cxx générés, soit dans une fine couche manuelle.

Principes de design de la frontière

Minimiser la surface de contact. Chaque fonction à la frontière est un point de risque. Préférer une interface avec 5 fonctions bien définies plutôt que 50 fonctions granulaires. Regrouper les opérations en commandes de haut niveau.

Transférer la propriété clairement. À chaque franchissement de la frontière, il doit être explicite quel côté possède quel objet. Les patterns recommandés : le C++ crée un objet Rust via une factory et le possède via un handle opaque ; le Rust crée un objet C++ via UniquePtr et le possède exclusivement. Éviter la propriété partagée entre les deux langages sauf nécessité absolue.

Copier aux frontières, partager à l'intérieur. Pour les données qui traversent la frontière, la copie est souvent préférable au partage. Elle élimine les questions de durée de vie et de synchronisation. Le coût de la copie est négligeable si la frontière est franchie rarement (une fois par requête, par frame, par message). La performance critique doit être du côté d'un seul langage, pas à cheval sur la frontière.

Erreurs comme valeurs, jamais comme panics/exceptions. La frontière doit toujours retourner des Result côté Rust et des codes d'erreur (ou Result via cxx) côté C++. Aucune exception, aucun panic ne doit traverser.

Exemple d'interface minimaliste

Pour un parser de protocole :

#[cxx::bridge]
mod ffi {
    struct ParseResult {
        success: bool,
        message_type: u32,
        payload_offset: usize,
        payload_length: usize,
        error_message: String,
    }

    extern "Rust" {
        fn parse_message(data: &[u8]) -> ParseResult;
        fn validate_header(data: &[u8]) -> bool;
    }
}

L'interface est de deux fonctions. Les données sont passées par slice (zéro copie en lecture). Le résultat est une struct partagée avec des types primitifs. Le C++ passe un buffer, reçoit un résultat structuré. Pas de handle opaque, pas de callback, pas de propriété partagée.


43.3.4.4 — Gestion du build system mixte

Le défi organisationnel

Le build system est souvent le point de friction principal. Les projets C++ utilisent CMake (ou Meson, Bazel, Make). Les projets Rust utilisent Cargo. Les deux doivent coexister, et l'un doit piloter l'autre.

Stratégie 1 : CMake pilote, Cargo est invoqué

C'est l'approche la plus courante quand le projet C++ est le "propriétaire" et Rust est un composant ajouté. CMake reste le build system principal et invoque Cargo pour compiler le composant Rust.

L'outil corrosion (section 43.3.1.8) est la solution recommandée :

include(FetchContent)  
FetchContent_Declare(Corrosion  
    GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
    GIT_TAG v0.5
    GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(Corrosion)

# Le composant Rust est une target CMake comme les autres
corrosion_import_crate(MANIFEST_PATH components/parser-rs/Cargo.toml)

# L'application C++ lie avec le composant Rust
add_executable(myapp src/main.cpp src/application.cpp)  
target_link_libraries(myapp PRIVATE parser-rs)  

Avantages : les développeurs C++ n'ont pas besoin de connaître Cargo ; le build s'intègre dans les workflows CI existants ; les IDE (CLion, VS Code avec CMake Tools) fonctionnent normalement.

Inconvénient : la recompilation du composant Rust passe par une invocation complète de Cargo à chaque build CMake. Cargo gère son propre cache, et la coordination entre le cache CMake et le cache Cargo peut occasionnellement causer des recompilations inutiles.

Stratégie 2 : Cargo pilote, C++ est compilé par cc/cxx-build

C'est l'approche naturelle quand le composant Rust est le "propriétaire" et intègre du C++ existant. Le build.rs compile le code C++ :

// build.rs
fn main() {
    cxx_build::bridge("src/bridge.rs")
        .files(["../cpp-lib/src/engine.cpp",
                 "../cpp-lib/src/parser.cpp"])
        .include("../cpp-lib/include")
        .std("c++20")
        .compile("cpp_components");
}

Avantages : tout est piloté par cargo build ; l'écosystème Rust (tests, benchmarks, documentation) fonctionne nativement.

Inconvénient : ne convient pas si le projet C++ est volumineux avec un CMakeLists.txt complexe et de nombreuses dépendances — reproduire tout cela dans un build.rs est irréaliste.

Stratégie 3 : Bazel ou Meson comme build system unifié

Pour les organisations disposées à migrer leur build system, Bazel offre un support natif de C++ et Rust dans le même graphe de build. Meson supporte également Rust. Ces approches éliminent le problème de coordination entre deux build systems.

Le coût est la migration du build system existant — un projet à part entière qui ne doit pas être sous-estimé.

Recommandation

Pour la majorité des projets C++ existants, CMake + corrosion est le point de départ le moins disruptif. Le composant Rust est ajouté comme une dépendance CMake ; le reste du workflow reste inchangé.

Structure de répertoires recommandée

myproject/
├── CMakeLists.txt                   # Build system principal (CMake)
├── src/                             # Code C++ existant
│   ├── main.cpp
│   ├── application.cpp
│   └── application.h
├── include/                         # Headers C++ publics
├── components/
│   └── parser-rs/                   # Composant Rust (sous-répertoire dédié)
│       ├── Cargo.toml
│       ├── build.rs                 # Si cxx-build est utilisé
│       ├── src/
│       │   ├── lib.rs               # Point d'entrée Rust
│       │   ├── bridge.rs            # Bridge cxx ou include_cpp! autocxx
│       │   ├── parser.rs            # Logique métier Rust pure
│       │   └── wrapper.rs           # Wrapper idiomatique
│       └── include/
│           └── parser_ffi.h         # Header C pour l'interface (si FFI manuelle)
├── tests/                           # Tests d'intégration C++
└── .github/workflows/               # CI (compile C++ + Rust)

Le composant Rust est un sous-répertoire autonome avec son propre Cargo.toml. Il peut être compilé et testé indépendamment (cd components/parser-rs && cargo test). L'intégration dans le projet C++ se fait uniquement via CMake + corrosion au niveau racine.


43.3.4.5 — Pipeline CI/CD mixte

Les défis spécifiques

Un pipeline CI pour un projet C++/Rust doit :

  • Installer les deux toolchains (compilateur C++ et toolchain Rust).
  • Gérer les caches des deux systèmes de build (cache CMake/ccache + cache Cargo).
  • Exécuter les tests des deux côtés.
  • Appliquer les outils de qualité des deux écosystèmes.

Exemple GitHub Actions

name: Build and Test

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4

      # ── Toolchain C++ ──────────────────────────────────
      - name: Install C++ tools
        run: |
          sudo apt-get update
          sudo apt-get install -y g++-14 cmake ninja-build ccache libclang-dev

      # ── Toolchain Rust ─────────────────────────────────
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt

      # ── Caches ─────────────────────────────────────────
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            components/parser-rs/target
          key: rust-${{ hashFiles('**/Cargo.lock') }}

      - uses: hendrikmuhs/ccache-action@v1

      # ── Build ──────────────────────────────────────────
      - name: Configure
        run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release

      - name: Build
        run: cmake --build build

      # ── Tests C++ ──────────────────────────────────────
      - name: C++ Tests
        run: ctest --test-dir build --output-on-failure

      # ── Tests Rust (indépendants) ──────────────────────
      - name: Rust Tests
        working-directory: components/parser-rs
        run: cargo test

      # ── Qualité Rust ───────────────────────────────────
      - name: Rust Lint
        working-directory: components/parser-rs
        run: |
          cargo fmt --check
          cargo clippy -- -D warnings

      # ── Tests d'intégration (C++ appelant Rust) ────────
      - name: Integration Tests
        run: ./build/integration_tests

Points d'attention

Versionner le Cargo.lock. Pour les bibliothèques Rust intégrées dans un projet C++, le Cargo.lock doit être versionné dans Git. Il garantit la reproductibilité des builds en épinglant les versions exactes des dépendances Rust.

Tester les deux côtés. Les tests Rust (cargo test) valident la logique interne du composant. Les tests C++ valident l'intégration via la frontière FFI. Les deux sont nécessaires — un composant Rust correct peut échouer si la frontière est mal configurée.

Analyse statique des deux côtés. clippy pour Rust et clang-tidy pour C++ doivent être exécutés dans le même pipeline. Les sanitizers C++ (ASan, UBSan) doivent couvrir le binaire complet, y compris le composant Rust lié statiquement.


43.3.4.6 — Formation et montée en compétence des équipes

Le profil du développeur C++ apprenant Rust

Un développeur C++ expérimenté n'est pas un débutant en Rust. De nombreux concepts sont partagés : gestion manuelle de la mémoire, types à taille fixe, compilation native, performances prévisibles, RAII, move semantics. Le développeur C++ comprend déjà pourquoi le borrow checker existe — il a vécu les problèmes que Rust cherche à prévenir.

Les points de friction pour un développeur C++ apprenant Rust sont bien identifiés :

Le borrow checker. C'est le point de friction principal. Le développeur C++ sait écrire du code correct avec des références et des pointeurs, mais le borrow checker impose une discipline plus stricte. Le pattern "un seul propriétaire à la fois" doit devenir un réflexe, pas une contrainte.

L'absence d'héritage de classes. Rust utilise les traits (interfaces) et la composition plutôt que l'héritage. Les patterns comme le polymorphisme dynamique via vtable se traduisent en dyn Trait en Rust, mais la pensée architecturale est différente.

La gestion des erreurs. Le passage de try/catch à Result<T, E> et l'opérateur ? demande un changement d'habitude, mais la plupart des développeurs C++ le trouvent supérieur une fois assimilé.

L'écosystème et les conventions. Cargo, crates.io, rustfmt, clippy, la convention snake_case partout — l'écosystème Rust a ses propres normes que le développeur C++ doit assimiler.

Stratégie de formation recommandée

Phase 1 — Fondamentaux Rust (2-3 semaines). Le Rust Book officiel (The Rust Programming Language), les exercices Rustlings, et quelques petits projets personnels (CLI tool, petit serveur). L'objectif est de maîtriser ownership, borrowing, lifetimes, Result, Option, traits et pattern matching.

Phase 2 — Interopérabilité (1 semaine). La section 43.3 de cette formation, avec manipulation concrète de cxx et autocxx sur un mini-projet. L'objectif est de savoir créer un bridge entre une bibliothèque C++ existante et un composant Rust.

Phase 3 — Premier composant en conditions réelles (2-4 semaines). Développement du premier composant Rust réel dans le projet, avec revue de code par des développeurs Rust expérimentés (internes ou consultants). C'est là que les vraies questions surgissent.

Accompagnement continu. Des sessions de revue de code Rust régulières, un canal de communication dédié (Slack/Discord), et des "office hours" avec un référent Rust accélèrent considérablement la montée en compétence.

L'erreur à éviter : l'expert unique

Ne pas faire reposer toute la compétence Rust sur un seul développeur. Si cette personne quitte l'équipe, le composant Rust devient un point aveugle. L'objectif minimal est que au moins 3 développeurs soient capables de lire, modifier et réviser le code Rust du projet.


43.3.4.7 — Métriques de succès

Mesurer l'impact

L'introduction de Rust doit être évaluée sur des critères concrets, pas sur des convictions :

Réduction des vulnérabilités mémoire. Le critère principal. Comparer le nombre de bugs mémoire (use-after-free, buffer overflow, data race) découverts par les sanitizers, le fuzzing ou en production entre les composants C++ et les composants Rust. L'objectif est une réduction mesurable, pas une élimination totale (les bugs logiques subsistent).

Coût d'intégration. Temps passé à maintenir la frontière FFI, à résoudre les problèmes de build, à former les développeurs. Ce coût doit être proportionné au gain sécurité. S'il dépasse significativement le temps économisé sur les bugs mémoire, la stratégie doit être réévaluée.

Vélocité de développement. La vitesse de développement des nouveaux composants en Rust vs en C++. Après la phase d'apprentissage, la vélocité devrait être comparable. Si elle reste significativement inférieure après 6 mois, c'est un signal d'alerte.

Couverture des tests. Les composants Rust doivent avoir une couverture de test au moins égale aux composants C++. L'écosystème Rust (tests intégrés, cargo test, property testing avec proptest) facilite typiquement l'atteinte d'une couverture élevée.

Satisfaction des développeurs. Un facteur souvent négligé. Si les développeurs trouvent l'interopérabilité trop pénible ou le Rust trop frustrant, l'adoption stagnera indépendamment des métriques techniques. Des sondages réguliers auprès de l'équipe permettent de détecter les problèmes tôt.


43.3.4.8 — Chronologie type d'une migration progressive

Phase 0 — Évaluation (1-2 mois)

  • Audit des vulnérabilités mémoire historiques du projet.
  • Évaluation du durcissement C++ existant (sanitizers, fuzzing, analyse statique).
  • Identification de 2-3 composants candidats pour un premier pilote Rust.
  • Évaluation de la capacité de l'équipe (compétences, disponibilité, motivation).
  • Décision go/no-go documentée avec critères explicites.

Phase 1 — Pilote (2-3 mois)

  • Formation de 2-3 développeurs aux fondamentaux Rust.
  • Développement du premier composant Rust (scope réduit, composant bien isolé).
  • Mise en place du build system mixte (CMake + corrosion).
  • Pipeline CI intégrant les deux toolchains.
  • Revue de code approfondie par un expert Rust (interne ou externe).
  • Bilan : le composant fonctionne-t-il ? Les tests passent-ils ? Le coût est-il acceptable ?

Phase 2 — Consolidation (3-6 mois)

  • Développement de 1-2 composants Rust supplémentaires.
  • Stabilisation des patterns d'interopérabilité (choix entre cxx/autocxx/FFI manuelle).
  • Documentation interne des conventions (style Rust, patterns FFI, organisation des répertoires).
  • Formation de développeurs supplémentaires.
  • Intégration du fuzzing sur les composants Rust (frontière FFI incluse).
  • Mesure des premières métriques d'impact.

Phase 3 — Généralisation (6-12 mois)

  • Tous les nouveaux composants exposés aux entrées non fiables sont développés en Rust (policy organisationnelle).
  • Les composants C++ existants les plus critiques sont progressivement réécrits en Rust (si le coût-bénéfice est favorable).
  • Le C++ existant continue d'être maintenu et durci — pas d'abandon.
  • Revue périodique de la stratégie : ajuster le rythme selon les résultats et la capacité.

Ce que cette chronologie ne contient pas

Elle ne contient pas de "Phase 4 : tout est en Rust". Ce n'est pas l'objectif. L'objectif est un état stable où les deux langages coexistent, chacun appliqué là où il apporte le plus de valeur. Le C++ reste le langage de choix pour les composants existants, le code de performance critique déjà optimisé, et les domaines où l'écosystème C++ est supérieur (certains frameworks, GPU compute, certaines bibliothèques métier). Le Rust est utilisé pour les nouveaux composants à haute exposition sécuritaire et pour les réécritures ciblées à fort impact.


43.3.4.9 — Pièges organisationnels

Le "rewrite-it-in-Rust" enthousiaste

Un développeur découvre Rust, en est enthousiasmé, et propose de réécrire un composant critique de 50 000 lignes. Le résultat prévisible : 6 mois de travail, un composant partiellement fonctionnel, des régressions, et un retour en arrière pénible. La règle : le premier composant Rust doit être petit, isolé et nouveau (ou une réécriture d'un composant de taille modeste bien compris).

La frontière trop large

Si le composant Rust nécessite d'accéder à 30 classes C++ différentes, la frontière FFI devient un projet en soi. Le coût de la plomberie d'interopérabilité dépasse le gain. Règle empirique : si l'interface FFI dépasse 15-20 fonctions, le composant choisi est probablement trop couplé au reste du code C++.

Le build system comme frein

Si chaque modification du composant Rust nécessite 10 minutes de build à cause d'une intégration CMake mal configurée, les développeurs abandonneront. Le build system mixte doit être optimisé dès le départ : caches Cargo et ccache, compilation incrémentale, build parallèle.

L'absence de tests d'intégration à la frontière

Les tests unitaires Rust valident la logique Rust. Les tests unitaires C++ valident la logique C++. Mais sans tests d'intégration qui exercent la frontière FFI réelle (appels C++ → Rust → C++ avec des données réalistes), les bugs d'interopérabilité ne sont découverts qu'en production. Ces tests d'intégration doivent être écrits et maintenus dès le premier composant.

Le manque de documentation de la frontière

La frontière FFI est le contrat entre les deux langages. Ce contrat doit être documenté : qui possède quoi, quels types traversent, comment les erreurs sont propagées, quelles garanties de thread-safety existent. Sans cette documentation, chaque nouveau développeur redécouvre les contraintes par essai-erreur.


Résumé

L'introduction de Rust dans une base de code C++ est une décision stratégique, pas seulement technique. Les clés du succès :

  1. Évaluer honnêtement si le problème justifie l'investissement — le C++ durci peut suffire.
  2. Commencer petit : un composant isolé, bien défini, à haute valeur sécuritaire.
  3. Minimiser la frontière : peu de fonctions, types simples, propriété claire.
  4. Industrialiser le build : CMake + corrosion, CI mixte, caches configurés.
  5. Former l'équipe : au moins 3 développeurs compétents, pas un expert isolé.
  6. Mesurer l'impact : vulnérabilités, coût, vélocité, satisfaction.
  7. Accepter la cohabitation : le C++ ne disparaît pas — il coexiste.

📎 La section suivante (43.3.5) présente les retours d'expérience concrets de l'industrie en 2025-2026 : Google, Microsoft, Meta, le noyau Linux, Android et Chromium — ce qui a fonctionné, ce qui a échoué, et les leçons à retenir.

⏭️ Cas d'usage et retours d'expérience industrie 2025-2026