/* Page prose (fr) */
"HardToCatch" = "# Pourquoi c'est si difficile à attraper\n\nLa corruption dépend du *timing*: quel caissier se trouve à lire durant l'intervalle d'un autre. Le timing change avec la charge du CPU, le nombre de cœurs, ce que le périphérique fait d'autre, et pure chance. C'est ce que **non déterministe** veut dire: le même code, les mêmes entrées, des résultats différents. Alors le bug:\n\n* **Pourrait ne pas se reproduire.** Il a passé tes tests parce que tes tests ont eu de la chance.\n* **Pourrait disparaitre sous un débogueur,** qui change le timing.\n* **Pourrait ne mordre qu'en production,** sur un périphérique plus chargé ou plus lent que le tien.\n\nTu ne peux pas te fier à l'exécution pour le trouver. Tu le trouves en le *lisant*. La seule question qui compte:\n\n**Y a-t-il un état qui est partagé entre des tâches concurrentes, et muté, sans que rien ne coordonne l'accès?**\n\nDans l'exemple: `account.balance` est partagé (un `Account`, capturé par chaque closure), il est muté (`account.balance = updated`), et rien ne coordonne la file concurrente `tellers`. Trois sur trois: c'est une data race, démontrable sur le papier, sans qu'aucune exécution de test soit nécessaire.\n\n* callout(Aparté): Dans un projet Xcode complet, il existe un outil appelé le **Thread Sanitizer** qui signale les data races à l'exécution. Il n'existe que sous Xcode, tu ne l'auras pas dans Swift Playground, donc tout au long de ce livre tu apprends à déduire les courses à la main, ce qui est de toute façon la compétence la plus durable.\n\n[Précédent](@previous)  |  page 3 sur 9  |  [Suivant: La correction par file en série](@next)";
