Chapitre 16 · Semaine 4

Le jour de l'entretien

Savoir n'est pas suffisant : il faut le montrer en une heure, sous le regard de quelqu'un, parfois en anglais, parfois en codant en direct. Ce chapitre donne une méthode pour chaque format : system design mobile, live coding, revue de code, questions comportementales, discussion sur le TJM. Il se termine par un entretien blanc complet.

Durée 3 séances de 2 h Niveau synthèse Prérequis chapitres 01 à 15

Objectifs

  • Connaître le déroulé d'un recrutement en régie et préparer chaque étape.
  • Mener un system design mobile en 30 minutes avec une méthode en étapes.
  • Coder en direct en parlant, sans assistant, et réussir une revue de code orale.
  • Répondre aux questions comportementales avec la méthode STAR et six histoires préparées.
  • Parler de son statut de freelance, de son TJM et poser des questions pertinentes au client.
  • Se présenter en 90 secondes, en français et en anglais.

Le déroulé typique d'une mission en régie

Au Maroc, les banques, opérateurs télécoms et administrations recrutent souvent leurs consultants mobiles par l'intermédiaire d'un intégrateur IT (ESN, société de services). Le processus a généralement trois ou quatre étapes :

[1] Échange avec l'intégrateur (30 à 45 min) profil, disponibilité, TJM, motivation, vérification rapide du niveau │ ▼ [2] Entretien technique client (45 à 90 min) responsable technique ou lead mobile : expérience, questions Flutter, architecture, parfois system design ; parfois une partie en anglais │ ▼ [3] Test technique (selon le client) live coding en partage d'écran, exercice à rendre, QCM, revue de code │ ▼ [4] Validation : manager, achats, parfois habilitation sécurité

L'intégrateur n'est pas un simple intermédiaire : il vous présente au client et engage sa réputation. Soignez cet échange, soyez précis sur vos disponibilités et cohérent sur votre TJM. Demandez-lui ce qu'il sait du client : stack, taille de l'équipe, format de l'entretien, langue, présence d'un test. Ces informations valent beaucoup.

Préparation logistique

  • Visio : outil installé et testé la veille (le client impose souvent le sien), caméra à hauteur des yeux, lumière de face, micro-casque, connexion filaire ou partage de connexion en secours.
  • Partage d'écran : un seul écran partagé, notifications coupées, onglets et fichiers personnels fermés, police de l'IDE agrandie (16 à 18 pt).
  • IDE prêt : un projet Flutter vierge qui compile, avec flutter_riverpod, http ou dio, mocktail déjà ajoutés ; un émulateur ou un appareil déjà lancé ; flutter doctor sans erreur ; SDK à jour (3.47).
  • Assistant IA : désactivé si on vous le demande, et de préférence désactivé par défaut. Une suggestion qui apparaît pendant un live coding sans IA met tout le monde mal à l'aise.
  • Documents : votre CV tel que l'intégrateur l'a envoyé (il est parfois reformaté), la fiche de poste, vos six histoires, vos questions pour le client.

System design mobile : la méthode

Un exercice de system design teste votre capacité à structurer un problème flou, à faire des choix et à les justifier. Il n'y a pas de solution unique. Le piège principal est de dessiner des boîtes tout de suite. Suivez ces étapes et annoncez-les :

ÉtapeQuestions à se poserTemps indicatif
1. Exigences fonctionnellesQuels parcours ? Quels utilisateurs ? Qu'est-ce qui est hors périmètre ?3 à 5 min
2. Exigences non fonctionnellesSécurité, hors ligne, performance, accessibilité, langues (arabe RTL), appareils anciens, conformité3 min
3. EstimationsNombre d'utilisateurs, volumes de données sur l'appareil, fréquence des synchronisations, taille de l'app2 min
4. Découpage en modulesFeatures, core, packages partagés, frontière avec le backend4 min
5. Flux de donnéesState management, sources de vérité, sens des flux4 min
6. APIREST ou autre, pagination, idempotence, versioning, erreurs3 min
7. Stockage et hors ligneCache, base locale, file d'attente, conflits3 min
8. SécuritéAuthentification, stockage des secrets, réseau, intégrité de l'appareil3 min
9. Observabilité, tests, livraisonCrashs, métriques, pyramide de tests, CI/CD, mise à jour forcée3 min
10. CompromisCe que vous avez sacrifié et pourquoi, ce que vous feriez avec plus de temps2 min
À dire en entretien

« Avant de dessiner quoi que ce soit, je voudrais clarifier les exigences : quels parcours sont prioritaires, quel volume d'utilisateurs, et quelles contraintes de sécurité et de hors ligne. Ensuite je proposerai un découpage, le flux de données, puis on verra stockage, sécurité et livraison. Arrêtez-moi si vous voulez que je creuse un point. »

Étude 1 : « Concevez une app e-wallet »

Clarification. Particuliers et commerçants. Parcours : inscription avec KYC, consultation du solde et de l'historique, transfert à un bénéficiaire, paiement par QR code, paiement de factures. Hors périmètre : crédit, épargne. Non fonctionnel : sécurité maximale (argent réel, régulateur), disponibilité, appareils Android d'entrée de gamme, arabe et français, réseau mobile instable. Estimation : un million d'utilisateurs, pic à la fin du mois ; sur l'appareil, quelques centaines de transactions en cache.

┌──────────────────────── App Flutter ────────────────────────┐ │ features/ auth wallet history transfer qr_pay bills │ │ presentation (widgets) ─▶ Notifier / AsyncNotifier │ │ domain (entités, use cases) ◀─ data (repositories) │ │ core/ network (Dio + intercepteurs) storage security i18n │ └──────────────┬──────────────────────────────────────────────┘ │ HTTPS + pinning, access token court, refresh rotatif ▼ API gateway (authN, rate limiting, versioning /v1) │ ┌───────────┼─────────────┬──────────────┬─────────────┐ ▼ ▼ ▼ ▼ ▼ auth comptes transactions paiements notifications (OAuth2) (solde) (historique) (idempotence) (push)
  • Flux de données : Riverpod. Le solde et l'historique sont des AsyncNotifier ; le transfert est un Notifier à états (saisie, confirmation, envoi, résultat). Après un transfert réussi, on invalide le solde et la première page de l'historique.
  • API : REST avec pagination par curseur ; montants en centimes (int) ; Idempotency-Key obligatoire sur les transferts et paiements, conservée tant que l'opération n'est pas confirmée, pour qu'un retry réseau ne débite pas deux fois ; erreurs typées (INSUFFICIENT_FUNDS...).
  • Stockage : tokens dans le stockage sécurisé (Keychain, Keystore) ; cache de l'historique en base locale chiffrée, purgé à la déconnexion ; aucune opération financière hors ligne (on affiche le cache en lecture seule, avec la date de dernière mise à jour).
  • Sécurité : OAuth2 avec PKCE ou login propriétaire, access token de quelques minutes, refresh token rotatif avec file d'attente pour les requêtes concurrentes ; biométrie pour déverrouiller l'app et confirmer un transfert au-delà d'un seuil ; SSL pinning avec plan de rotation ; détection root/jailbreak et attestation (Play Integrity, App Attest) remontées au serveur, qui décide ; masquage de l'écran dans le sélecteur d'apps ; obfuscation ; MASVS comme référentiel.
  • Observabilité : crashs sans PII, métriques de parcours (taux d'échec d'un transfert), corrélation des requêtes avec un identifiant transmis au backend.
  • Tests et livraison : unitaires sur les règles de montant et les états, widget sur le parcours de transfert, intégration contre un backend simulé ; CI avec builds signés par flavor ; déploiement progressif ; version minimale côté serveur pour forcer la mise à jour.
  • Compromis : pas de transfert hors ligne (risque de double dépense, solde non garanti) ; pinning contre souplesse de rotation des certificats ; biométrie seule insuffisante pour les gros montants (ajouter un OTP).

Étude 2 : « Concevez une app terrain hors ligne pour des commerciaux »

Clarification. 500 commerciaux d'une entreprise de distribution, en zones rurales sans réseau pendant des heures. Parcours : tournée du jour, fiche client, prise de commande avec catalogue et prix, encaissement, photos de linéaire, signature. Non fonctionnel : tout doit fonctionner hors ligne ; synchronisation fiable ; appareils fournis et gérés par MDM ; données commerciales sensibles ; batterie sur une journée. Estimation : 2 000 clients et 5 000 références par commercial, quelques dizaines de commandes par jour, photos de 300 Ko.

┌──────────────────────────── App Flutter ───────────────────────────┐ │ UI ─▶ Notifiers ─▶ Repositories ─▶ base locale (drift, chiffrée) │ │ ▲ source de vérité de l'UI │ │ │ │ │ Moteur de synchronisation ───┤ │ │ - pull : delta depuis le dernier curseur (par tenant) │ │ - push : outbox (commandes, encaissements, photos) │ │ - retry avec backoff, Idempotency-Key par opération │ │ - déclenché : retour réseau, premier plan, tâche de fond │ └───────────────────────────┬────────────────────────────────────────┘ │ HTTPS, token d'appareil + utilisateur ▼ API de synchronisation (Spring Boot) /sync/pull?since=cursor /sync/push (lot d'opérations) │ ERP / CRM de l'entreprise
  • Principe : l'UI ne lit que la base locale (offline-first). Le réseau ne sert qu'à synchroniser. Une commande est écrite localement avec un statut pending et une entrée d'outbox dans la même transaction.
  • Pull : synchronisation initiale lourde (catalogue, clients de la zone) en Wi-Fi au dépôt, puis deltas par curseur. Gérer les suppressions (tombstones).
  • Push : opérations envoyées dans l'ordre, par lots, chacune avec une clé d'idempotence ; photos envoyées séparément, en dernier, reprises en cas de coupure.
  • Conflits : les commandes sont des créations (pas de conflit) ; pour une fiche client modifiée des deux côtés, règle explicite par champ (serveur prioritaire pour les données de référence, terrain prioritaire pour les coordonnées relevées), et journal des conflits visible du superviseur. Le prix est recalculé et validé par le serveur, qui peut rejeter une commande : l'app doit afficher ce rejet.
  • Sécurité : base chiffrée (clé dans le Keystore ou le Keychain), effacement à distance par le MDM, verrouillage par code ou biométrie, multi-tenant isolé par une base par compte.
  • Batterie et performance : synchronisation déclenchée par événements plutôt qu'en continu, index sur les colonnes de recherche, pagination locale, parsing des gros lots dans un isolate.
  • Observabilité : âge de la dernière synchronisation réussie, taille de l'outbox, nombre d'échecs, remontés au serveur ; un écran « état de la synchronisation » pour le support.
  • Compromis : prix potentiellement périmés hors ligne (afficher la date du catalogue, validation finale serveur) ; taille de la base contre exhaustivité du catalogue ; complexité de la synchronisation contre simplicité d'un mode en ligne.
Piège d'entretien

« Et si deux commerciaux modifient le même client hors ligne ? » Répondre « le dernier qui synchronise gagne » sans le dire comme un choix conscient est une faiblesse. Expliquez la règle choisie, ce qu'elle fait perdre, et comment le conflit devient visible. Montrer que vous savez que le problème existe vaut plus qu'une solution parfaite.

Live coding : la méthode

  1. Reformuler l'énoncé avec vos mots. Cela évite de coder la mauvaise chose.
  2. Poser des questions : données d'entrée, cas limites, packages autorisés, est-ce qu'on veut des tests, combien de temps.
  3. Annoncer le plan en trois ou quatre étapes avant de taper.
  4. Coder simple d'abord : une version qui fonctionne, puis améliorer. Une solution élégante inachevée vaut moins qu'une solution simple qui tourne.
  5. Parler en codant : ce que vous faites et pourquoi. Le silence est le pire signal. Si vous bloquez, dites-le et dites ce que vous essayez.
  6. Tester : lancer l'app, essayer un cas limite, écrire un test si le temps le permet. Terminer par ce que vous feriez avec plus de temps.
À retenir

Un oubli de syntaxe n'est pas éliminatoire ; un candidat qui ne communique pas l'est souvent. Il est acceptable de dire « je ne me souviens plus de la signature exacte, je vérifie dans la doc » : c'est ce que vous feriez au travail.

Six exercices classiques

ExerciceCe qu'on attendCritères de réussite
Recherche avec debounce sur une APIChamp de recherche, appels limités, résultatsDebounce de 300 ms environ, annulation ou ignorance des réponses obsolètes, états vide / chargement / erreur, pas d'appel pour une chaîne vide, controller libéré
Liste paginéeChargement initial, page suivante au défilementListView.builder, garde contre les appels concurrents, indicateur en bas de liste, erreur sur une page avec bouton réessayer, fin de liste détectée, pull-to-refresh
Formulaire validéPlusieurs champs, validation, soumissionForm et GlobalKey<FormState>, validateurs purs et testables, clavier adapté, bouton désactivé pendant l'envoi, erreurs serveur affichées sur le bon champ
Compteur Riverpod testéNotifier, widget, testNotifier<int>, ref.watch dans le build et ref.read(...notifier) dans le callback, test unitaire avec ProviderContainer.test, test de widget avec ProviderScope
Refactor d'un widget de 300 lignesDécouper et clarifierExtraire des widgets (pas des méthodes qui renvoient des widgets), sortir la logique vers un Notifier, ajouter const, nommer, ne pas changer le comportement, expliquer l'ordre des étapes
Corriger un bug de keysListe réordonnable ou suppression qui garde le mauvais étatDiagnostiquer l'association Element / State par position, ajouter ValueKey(item.id), expliquer pourquoi UniqueKey dans le build est une erreur

Revue de code à faire

Format fréquent : on vous montre un extrait et on vous demande ce que vous changeriez. Lisez l'extrait ci-dessous, chronomètre lancé (10 minutes), et notez tous les problèmes avant d'ouvrir la correction.

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class TransferScreen extends StatefulWidget {
  TransferScreen({required this.userId});
  final String userId;

  @override
  State<TransferScreen> createState() => _TransferScreenState();
}

class _TransferScreenState extends State<TransferScreen> {
  final amountController = TextEditingController();
  double balance = 0;
  List beneficiaries = [];
  bool loading = false;
  static const apiKey = 'sk_live_51H8xQ2eZvKYlo2C';

  Future<void> loadBalance() async {
    final res = await http.get(
      Uri.parse('https://api.example.com/wallet?key=$apiKey'),
    );
    setState(() {
      balance = jsonDecode(res.body)['balance'];
    });
  }

  Future<void> send() async {
    setState(() => loading = true);
    final amount = double.parse(amountController.text);
    try {
      await http.post(
        Uri.parse('https://api.example.com/transfers'),
        body: jsonEncode({'userId': widget.userId, 'amount': amount}),
      );
    } catch (e) {}
    setState(() => loading = false);
    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    loadBalance();
    return Scaffold(
      appBar: AppBar(title: Text('Transfert')),
      body: Column(
        children: [
          Text('Solde : ${balance.toStringAsFixed(2)} MAD'),
          TextField(
            controller: amountController,
            keyboardType: TextInputType.number,
          ),
          ListView(
            children: beneficiaries
                .map((b) => ListTile(title: Text(b['name'])))
                .toList(),
          ),
          ElevatedButton(
            onPressed: loading ? null : send,
            child: loading ? CircularProgressIndicator() : Text('Envoyer'),
          ),
        ],
      ),
    );
  }
}
Correction : les problèmes à trouver
  1. Appel réseau dans build : loadBalance() appelle setState, qui relance build, qui relance l'appel. Boucle infinie de requêtes. Charger dans initState ou, mieux, dans un AsyncNotifier.
  2. Fuite du controller : amountController n'est jamais libéré. Ajouter dispose() avec amountController.dispose().
  3. setState après dispose : après chaque await, l'écran a pu être fermé. Vérifier if (!mounted) return;, et context.mounted avant Navigator.
  4. Clé d'API en dur, et en plus dans l'URL (elle finit dans les logs des proxys). Aucune clé secrète dans l'app ; authentification par token utilisateur dans un en-tête.
  5. double pour l'argent : erreurs d'arrondi. Montants en centimes (int) comme dans le contrat d'API.
  6. Cast implicite dangereux : si l'API renvoie 125000 (un int), l'affectation à un double lève une erreur de type à l'exécution. Pas de parsing typé ni de modèle.
  7. Erreur avalée : catch (e) {}, aucun contrôle du code HTTP, et l'écran se ferme comme si le transfert avait réussi. L'utilisateur croit avoir payé.
  8. double.parse sans validation : exception sur une saisie vide ou avec une virgule. Utiliser un Form avec validateur et tryParse.
  9. Pas d'Idempotency-Key ni d'en-tête Content-Type: application/json : un retry peut débiter deux fois, et le serveur peut ne pas interpréter le corps.
  10. ListView dans une Column sans Expanded : hauteur non bornée, erreur de layout. Et List non typée avec accès dynamique b['name'].
  11. Absence de const : constructeur du widget, Text('Transfert'), CircularProgressIndicator(). Rebuilds évitables.
  12. Architecture : réseau, logique et UI dans le même State, impossible à tester. Repository, Notifier, et un client HTTP injecté.

Commencez par les problèmes de gravité la plus haute (boucle de requêtes, transfert présenté comme réussi, clé en dur, argent en double), puis le reste. L'ordre montre votre jugement.

Questions comportementales : la méthode STAR

Les questions « Parlez-moi d'une fois où... » évaluent votre comportement passé, meilleur indicateur de votre comportement futur. La méthode STAR donne une structure de 90 secondes à 2 minutes :

LettreContenuPoids
SituationContexte en deux phrases : projet, équipe, enjeu15 %
TâcheVotre responsabilité précise10 %
ActionCe que vous avez fait, à la première personne, avec les décisions et leurs raisons55 %
RésultatChiffré si possible, plus la leçon retenue20 %

Erreurs courantes : une situation de deux minutes et une action de vingt secondes ; « nous » partout au lieu de « je » ; une histoire sans résultat ; un échec qui n'en est pas un (« je suis trop perfectionniste »).

Huit questions classiques et la structure attendue

Parlez-moi d'un conflit avec un collègue.

Choisir un conflit professionnel, pas personnel. Montrer que vous avez cherché à comprendre la position de l'autre, ramené la discussion sur des faits ou des critères partagés, et trouvé une issue. Ne jamais dénigrer la personne. Résultat : la relation de travail après le conflit.

Racontez un échec.

Un vrai échec, dont vous portez une part. Décrire ce que vous avez fait pour limiter les dégâts, puis ce que vous avez changé durablement (un process, un test, une habitude). Le recruteur évalue la lucidité, pas l'échec lui-même.

Un désaccord technique avec votre lead ou un architecte ?

Montrer que vous argumentez avec des faits (prototype, mesure, documentation), que vous acceptez une décision contraire une fois prise (disagree and commit), et que vous la mettez en œuvre sérieusement. En régie, le client décide : dites-le.

Une deadline intenable ?

Alerter tôt avec des éléments concrets, proposer des options (réduire le périmètre, livrer en deux temps, renforcer), ne pas sacrifier la sécurité ou les tests en silence. Résultat : ce qui a été livré et quand.

Avez-vous accompagné un développeur moins expérimenté ?

Méthode : revue de code pédagogique, pair programming, tâches de difficulté croissante, documentation. Résultat : l'autonomie gagnée par la personne, mesurée concrètement (elle a livré seule tel module).

Comment priorisez-vous quand tout est urgent ?

Critères explicites : impact utilisateur et métier, risque (sécurité, argent), dépendances, effort. Rendre l'arbitrage visible au responsable plutôt que de choisir seul. Exemple concret avec le résultat.

Une erreur en production dont vous étiez responsable ?

Détection, communication immédiate, mitigation (rollback, arrêt du déploiement progressif, feature flag), correction, puis post-mortem sans recherche de coupable et garde-fou ajouté (test, alerte, étape de CI). Ne pas minimiser.

Racontez une fois où vous avez dû apprendre une technologie très vite.

Méthode d'apprentissage : documentation officielle, petit prototype, lecture de code existant, questions ciblées à un expert. Résultat livré dans le délai. Un changement de state management en cours de projet peut être un bon exemple s'il est réel.

Six histoires réutilisables

Une même histoire bien préparée répond à plusieurs questions. Préparez-en six, vraies, et entraînez-vous à les raconter. Personne ne peut les écrire à votre place : voici le canevas à remplir pour chacune.

HistoireCouvre aussiÀ noter
1. Une réussite technique dont vous êtes fierApprendre vite, prioriserS, T, A, R en quatre lignes, un chiffre
2. Un incident en productionÉchec, pressionComment détecté, temps de résolution, garde-fou ajouté
3. Un désaccord techniqueConflit, influenceArguments des deux côtés, décision, ce que vous avez fait ensuite
4. Une deadline difficilePriorisation, négociationOptions proposées, arbitrage obtenu, résultat
5. Un accompagnementLeadership, communicationSituation de départ de la personne, méthode, résultat
6. Une amélioration de qualité ou de sécuritéInitiative, dette techniqueProblème constaté, proposition, adoption, effet mesuré

Freelance et TJM

Un profil freelance soulève deux inquiétudes chez le client : la stabilité (allez-vous partir au bout de trois mois ?) et l'intégration dans l'équipe. Présentez votre parcours comme une suite de missions choisies, avec des durées, des responsabilités et des résultats. Insistez sur votre engagement pour la durée de la mission.

  • Ne vous dévalorisez pas : pas de « je suis un peu rouillé sur... » non sollicité, pas d'excuse sur une période sans mission. Une phrase factuelle suffit.
  • N'annoncez pas votre plancher : donnez un TJM cible cohérent avec le marché et votre niveau, et laissez la négociation à l'étape prévue (souvent avec l'intégrateur, pas avec le client technique).
  • Avec le client final, le TJM est rarement abordé : si c'est le cas, renvoyez poliment vers l'intégrateur.
  • Cohérence : le TJM annoncé à l'intégrateur doit être le même d'un échange à l'autre.
À dire en entretien

« Je suis indépendant depuis plusieurs années ; mes missions durent en général de X à Y mois, et je m'engage sur la durée prévue. Sur le tarif, mon TJM cible est de Z, que j'ai communiqué à [l'intégrateur] ; je préfère que nous en parlions avec eux pour ne pas mélanger avec la partie technique. »

Questions à poser au client

Vos questions montrent votre niveau autant que vos réponses. Préparez-en cinq et posez-en deux ou trois :

  • Équipe : combien de développeurs mobiles, qui fait le backend, y a-t-il un lead mobile, comment sont réparties les features ?
  • CI et qualité : quelle CI, quels tests automatisés, quelle couverture visée, lints imposés ?
  • Revue de code : obligatoire, combien de relecteurs, qui valide ?
  • Dette : quelle est la plus grosse dette technique de l'app aujourd'hui, et y a-t-il du temps prévu pour la traiter ?
  • Release : à quelle fréquence publiez-vous, qui pilote les stores, y a-t-il un comité de mise en production ?
  • Politique IA : avez-vous une politique sur les assistants de code, et des outils validés ?
  • Travail : présentiel ou hybride, outils de collaboration, horaires de l'équipe, accès VPN depuis l'extérieur ?
  • Succès : à quoi reconnaîtrez-vous dans trois mois que la mission est réussie ?

Réponses clés en anglais

Se présenter en 90 secondes : canevas. Remplissez avec vos propres éléments, puis répétez à voix haute jusqu'à le dire sans lire.

À dire en entretien (anglais)

« I'm a mobile developer with [N] years of experience, the last [N] focused on Flutter in production. Most recently, I worked on [type of app, e.g. a mobile payment app] for [type of client], where I was responsible for [scope: architecture, security, API integration]. A few things I'm proud of: [result 1 with a number], [result 2]. Technically, I'm comfortable with [state management], clean architecture, testing and CI/CD, and I care a lot about security and maintainability. I'm now looking for a mission where I can [what you bring], ideally in [sector]. »

Décrire son dernier projet. Structure : what the app does and for whom, team and your role, architecture in three sentences, one hard problem and how you solved it, the result.

À dire en entretien (anglais)

« The app lets [users] [main action]. We were a team of [N]; I was [role]. The architecture was feature-first with clean layers, [state management], and a REST API behind a gateway. The hardest part was [problem, e.g. handling token refresh with concurrent requests]; I solved it by [solution]. As a result, [measurable outcome]. »

Check-list de la veille et du jour J

La veilleLe jour J
Relire la fiche de poste et noter les mots-clésConnexion 10 minutes avant, outil de visio ouvert
Se renseigner sur le client : activité, apps publiées sur les stores, avis récentsEau, papier, stylo ; téléphone en silencieux
Tester la visio, le partage d'écran, le microIDE ouvert sur le projet vierge, émulateur lancé, assistant désactivé
Vérifier que le projet vierge compile et que les tests passentFiche avec les six histoires et les questions pour le client
Répéter le pitch de 90 secondes en français et en anglaisReformuler chaque question difficile avant de répondre
Relire la rafale de questions ci-dessousPrendre des notes pendant l'entretien (noms, contexte)
DormirAprès : noter les questions posées, envoyer un retour à l'intégrateur

Rafale de révision : 20 questions, 20 réponses

QuestionRéponse en une ligne
Widget, Element, RenderObject ?Configuration immuable, instance dans l'arbre qui garde le State, objet qui fait layout et paint.
StatelessWidget ou StatefulWidget ?Stateful seulement si un état local vit avec le widget (controller, animation).
À quoi servent les keys ?Associer un Element et son State au bon élément quand l'ordre ou la composition d'une liste change.
Pourquoi const ?Instance canonique réutilisée, le framework saute le rebuild de ce sous-arbre.
Future ou Stream ?Une valeur à venir contre une suite de valeurs dans le temps.
Quand un isolate ?Calcul CPU de plus de quelques millisecondes (parsing massif, crypto) ; Isolate.run.
ref.watch ou ref.read ?watch dans build pour réagir, read dans les callbacks pour une action ponctuelle.
Notifier ou AsyncNotifier ?État synchrone contre état initialisé de façon asynchrone, exposé en AsyncValue.
Pourquoi autoDispose ?Libérer l'état quand plus personne n'écoute (écran fermé, family par id).
Comment gérer un refresh token concurrent ?Un seul refresh à la fois, requêtes en attente mises en file puis rejouées.
Où stocker un token ?Keychain ou Keystore via flutter_secure_storage, jamais SharedPreferences.
SSL pinning : risque ?App bloquée à la rotation du certificat sans plan de rotation (plusieurs empreintes).
Pourquoi pas de double pour l'argent ?Arrondis binaires ; on manipule des centimes en int.
Idempotency-Key ?Un retry de la même opération ne la rejoue pas côté serveur.
Offline-first ?L'UI lit la base locale, la synchronisation est un processus à part avec outbox.
Test golden ?Comparaison pixel d'un widget avec une image de référence.
Jank : premier réflexe ?Profiler en mode profile sur appareil réel avec DevTools, pas en debug.
Pigeon ?Générateur de platform channels typés Dart, Kotlin, Swift.
Build number ?Entier strictement croissant par envoi au store, dérivé de la CI.
Dot shorthands (Dart 3.10) ?.center au lieu de MainAxisAlignment.center quand le type est connu.

Questions d'entretien

Présentez-vous.

90 secondes : années d'expérience et spécialité, dernière mission (contexte, rôle, résultat chiffré), deux points forts techniques reliés à la fiche de poste, ce que vous cherchez. Pas de récit chronologique depuis vos études.

Pourquoi cette mission ?

Relier deux éléments précis de la fiche de poste à votre expérience, plus un élément d'intérêt réel (secteur, enjeu technique). Éviter les réponses génériques.

Quel est votre principal point faible technique ?

Un vrai point, sans rapport avec le cœur du poste, avec ce que vous faites pour progresser. Par exemple un domaine que vous avez moins pratiqué, et la formation ou le projet en cours qui le couvre.

Vous ne connaissez pas la réponse à une question technique : que faites-vous ?

Le dire franchement, raisonner à voix haute sur ce que vous savez de proche, indiquer comment vous vérifieriez. Inventer est la pire option : un recruteur technique le voit immédiatement.

Comment arrivez-vous dans une équipe existante ?

Lire le code et la documentation, faire tourner l'app et les tests, prendre une petite tâche pour comprendre le flux de livraison, poser des questions groupées, respecter les conventions avant de proposer de les changer.

Qu'avez-vous appris récemment ?

Un sujet réel et récent (Riverpod 3, primary constructors de Dart 3.13, migration UIScene, Swift Package Manager), ce que vous en avez fait concrètement, et votre avis argumenté.

Votre disponibilité et votre TJM ?

Date de démarrage précise, préavis éventuel. TJM cible cohérent, sans annoncer de plancher ; avec le client final, renvoyer vers l'intégrateur.

Avez-vous des questions ?

Toujours oui. Deux ou trois questions préparées sur l'équipe, la qualité, la release ou les critères de succès de la mission. Jamais « non, tout est clair ».

Exercices

Exercice 1 · Sans IA · 30 min

System design sur papier : « Concevez l'app de suivi de consommation d'un opérateur télécom (forfait, recharge, factures, réclamations) ». Suivez les dix étapes en les annonçant à voix haute. Critères : exigences clarifiées avant tout schéma, un schéma ASCII, au moins trois compromis explicites.

Exercice 2 · Sans IA · 45 min

Live coding enregistré : recherche de bénéficiaires avec debounce sur l'API du projet fil rouge. Filmez votre écran et votre voix. Critères : reformulation et plan annoncés dans les deux premières minutes, aucune période de silence de plus de 30 secondes, cas vide / chargement / erreur gérés, réponses obsolètes ignorées.

Exercice 3 · Oral · 30 min

Rédigez vos six histoires avec le canevas, puis racontez-en trois au chronomètre. Critères : 90 secondes à 2 minutes chacune, « je » majoritaire, un résultat chiffré, une leçon.

Exercice 4 · Anglais · 15 min

Remplissez le canevas de présentation en anglais et enregistrez-vous trois fois. Critères : moins de 90 secondes, sans lire à la troisième prise, un chiffre cité.

Entretien blanc : 60 minutes

Pas d'étape de fil rouge ici : à la place, un entretien blanc complet. Idéalement avec un pair qui joue le recruteur ; sinon seul, en vous enregistrant et en lisant les questions à voix haute. Respectez les durées.

BlocDuréeContenu
1. Présentation5 minPitch de 90 secondes, puis « parlez-moi de votre dernier projet »
2. Questions Flutter15 minSix questions tirées au hasard dans les sections « Questions d'entretien » des chapitres 03, 06, 09, 10, 12 et 13
3. System design15 min« Concevez une app de prise de rendez-vous en agence bancaire »
4. Live coding15 minListe paginée avec états de chargement et d'erreur, sans assistant
5. Comportemental et anglais7 minDeux questions STAR, dont une en anglais
6. Vos questions3 minDeux questions au client

Grille d'auto-évaluation (notez chaque ligne de 1 à 4 ; 1 = insuffisant, 4 = convaincant) :

CritèreCe qu'on observe à 4Note
Clarté de la présentationMoins de 90 s, structurée, un résultat chiffré/4
Exactitude techniqueRéponses justes, API actuelles, aucune invention/4
ProfondeurLe pourquoi et les compromis, pas seulement le comment/4
Méthode de system designExigences clarifiées, étapes annoncées, compromis explicites/4
Live codingPlan annoncé, version simple qui tourne, cas limites traités/4
Communication en codantPas de long silence, pensée rendue visible/4
Histoires STARStructure respectée, « je », résultat, leçon/4
AnglaisCompréhensible, fluide sur les sujets préparés/4
Contexte grand compteSécurité, tests, process et politique IA évoqués spontanément/4
Questions poséesPertinentes, montrent l'intérêt pour la qualité et l'équipe/4

Total sur 40. En dessous de 28, retravaillez les deux critères les plus faibles et refaites l'entretien blanc trois jours plus tard avec d'autres sujets. Au-dessus de 32, vous êtes prêt : gardez le rythme avec un exercice sans IA par jour jusqu'à l'entretien.

Quiz

12 questions sur la méthode. Corrigez, relisez les explications, refaites-le 48 h plus tard.

Pour aller plus loin