66 points par GN⁺ 2025-08-18 | 1 commentaires | Partager sur WhatsApp
  • Répertorie les pièges contre-intuitifs dans lesquels les développeurs tombent souvent, en présentant les causes de bugs susceptibles de se produire facilement
  • Traite des problèmes fréquents dans diverses technologies comme HTML, CSS, Unicode/l’encodage de texte, les nombres à virgule flottante et le temps
  • Souligne que des malentendus ou des erreurs peuvent naître de différences subtiles de syntaxe et de comportement selon les langages et les frameworks
  • Explique, à l’aide d’exemples, les pièges pouvant survenir en environnement réel dans des domaines clés du backend comme la concurrence, le réseau et les bases de données
  • À travers divers exemples et liens de référence, présente les situations problématiques, leurs solutions et des améliorations face à des comportements inattendus

HTML et CSS

  • Valeur par défaut de min-width dans Flexbox/Grid

    • min-width vaut par défaut auto
    • min-width: auto est déterminé par la taille du contenu et prévaut sur flex-shrink, overflow: hidden, width: 0, max-width: 100%
    • Recommandation : définir explicitement min-width: 0
  • Différence entre l’horizontal et le vertical en CSS

    • width: auto tente de remplir l’espace du parent, tandis que height: auto s’adapte au contenu
    • Pour les éléments inline, inline-block et float, width: auto ne s’étend pas
    • margin: 0 auto centre horizontalement, margin: auto 0 ne permet pas le centrage vertical (sauf avec flex-direction: column, où le centrage vertical est possible)
    • La fusion des marges ne se produit qu’à la verticale
    • Si la direction de mise en page change avec writing-mode: vertical-rl, par exemple, le comportement s’inverse également
  • Block Formatting Context (BFC)

    • Création d’un BFC avec display: flow-root (d’autres options existent aussi, comme overflow: hidden/auto/scroll ou display: table, mais avec des effets de bord)
    • Un BFC permet d’éviter le chevauchement des marges verticales entre éléments frères adjacents ou les marges des enfants qui débordent du parent
    • Si un parent ne contient que des enfants flottants, sa hauteur s’effondre à 0 → cela peut être corrigé avec un BFC
    • S’il y a une border ou un padding, la fusion des marges ne se produit pas
  • Stacking Context

    • Conditions qui créent un nouveau stacking context
      • Propriétés de rendu comme transform, filter, perspective, mask, opacity, etc.
      • position: fixed ou sticky
      • z-index défini + positionnement absolute/relative
      • z-index défini + élément à l’intérieur d’un conteneur flexbox/grid
      • isolation: isolate
    • Caractéristiques
      • z-index ne s’applique qu’à l’intérieur du stacking context
      • Les coordonnées de position: absolute/fixed sont calculées par rapport à l’ancêtre positionné le plus proche
      • sticky ne fonctionne pas au-delà d’un stacking context
      • Même avec overflow: visible, le contenu peut être rogné par un stacking context
      • background-attachment: fixed est positionné par rapport au stacking context
  • Unités de viewport

    • Sur les navigateurs mobiles, quand la barre d’adresse/la barre de navigation disparaît au défilement, la valeur de 100vh change
    • Solution moderne : utiliser 100dvh
  • Référence pour le positionnement absolu

    • position: absolute se base non pas sur le parent, mais sur l’ancêtre relative/absolute ou le stacking context le plus proche
  • Fonctionnement du blur

    • backdrop-filter: blur ne tient pas compte des éléments environnants
  • Annulation du float

    • Si le parent est en flex ou grid, le float des enfants n’a aucun effet
  • Unités en pourcentage pour width/height

    • Elles ne fonctionnent pas si la taille du parent n’est pas déterminée à l’avance (pour éviter les références circulaires)
  • Caractéristiques des éléments inline

    • display: inline ignore width, height, margin-top et margin-bottom
  • Gestion des espaces

    • Par défaut, les retours à la ligne en HTML sont traités comme des espaces, et les espaces consécutifs sont réduits à un seul
    • `` empêche cette réduction des espaces, mais son comportement au début et à la fin est particulier
    • Dans la plupart des cas, les espaces au début et à la fin du contenu sont ignorés, sauf pour ``
    • Les espaces/retours à la ligne entre éléments inline-block sont affichés comme un espacement réel (ce qui n’arrive pas en flex/grid)
  • text-align

    • S’applique à l’alignement du texte et des éléments inline, mais pas à celui des éléments block
  • box-sizing

    • La valeur par défaut est content-boxpadding et border ne sont pas inclus
    • Avec width: 100% + padding, l’élément peut déborder de la zone du parent
    • Solution : box-sizing: border-box
  • Cumulative Layout Shift

    • Si les attributs width et height ne sont pas définis sur ``, le chargement différé de l’image peut provoquer des décalages de mise en page
    • Recommandation : définir ces attributs pour éviter le CLS
  • Requêtes réseau de téléchargement de fichier dans Chrome

    • Elles n’apparaissent pas dans le panneau réseau de DevTools (elles sont traitées dans un autre onglet)
    • Si une analyse est nécessaire, utiliser chrome://net-export/
  • Problème d’analyse de JavaScript dans le HTML

    • Dans un cas comme console.log(''), le premier `` est interprété comme une balise de fermeture
    • Référence : Safe JSON in script tags

Unicode et encodage de texte

  • Points de code et clusters de graphèmes

    • Un cluster de graphèmes correspond à l’« unité de caractère » dans une interface graphique
    • Un caractère ASCII visible correspond à 1 point de code = 1 cluster de graphèmes
    • Un emoji peut être un seul cluster de graphèmes composé de plusieurs points de code
    • En UTF-8, un point de code occupe 1 à 4 octets, et le nombre d’octets ne correspond pas au nombre de points de code
    • En UTF-16, un point de code occupe 2 ou 4 octets (paire de substituts / surrogate pair)
    • La norme ne fixe pas de limite au nombre de points de code dans un cluster, mais les implémentations imposent souvent des limites pour des raisons de performance
  • Différences de comportement des chaînes selon les langages

    • Rust : les chaînes internes utilisent l’UTF-8, len() renvoie le nombre d’octets, l’indexation directe est impossible, chars().count() donne le nombre de points de code, la validité UTF-8 est vérifiée strictement
    • Golang : une chaîne est en pratique un tableau d’octets, la longueur et l’indexation se font en octets, l’UTF-8 est généralement utilisé
    • Java, C#, JS : basés sur l’UTF-16, la longueur est mesurée en unités de 2 octets et l’indexation se fait aussi sur cette base, avec présence de paires de substituts
    • Python : len() renvoie le nombre de points de code, et l’indexation renvoie une chaîne contenant un seul point de code
    • C++ : std::string n’impose aucune contrainte d’encodage, se comporte comme un vecteur d’octets, et la longueur/l’indexation se font en octets
    • Parmi les langages mentionnés, aucun ne mesure la longueur ni n’indexe au niveau des clusters de graphèmes
  • BOM (Byte Order Mark)

    • Certains fichiers texte contiennent un BOM, par exemple EF BB BF pour indiquer un encodage UTF-8
    • Il est principalement utilisé sous Windows, et des logiciels non-Windows peuvent ne pas le gérer correctement
  • Autres points d’attention

    • Lors de la conversion de données binaires en chaîne de caractères, les parties invalides sont remplacées par � (U+FFFD)
    • Existence de confusable characters (caractères qui se ressemblent)
    • Normalisation (Normalization) : par ex. é peut être représenté par U+00E9 (un seul point de code) ou U+0065+U+0301 (deux points de code)
    • Existence de zero-width characters et de invisible characters
    • Différences de fin de ligne : Windows utilise CRLF \r\n, Linux/MacOS utilise LF \n
    • Unification des Han (Han unification) : des caractères dont la forme diffère légèrement selon la langue utilisent le même point de code
      • Les polices intègrent des variantes par langue pour effectuer un rendu approprié
      • En internationalisation, il faut sélectionner la bonne variante de police

Virgule flottante (Floating point)

  • Propriétés de NaN

    • NaN n’est égal à aucune valeur, y compris lui-même (NaN == NaN est toujours false)
    • NaN != NaN est toujours true
    • Le résultat d’une opération impliquant NaN se propage le plus souvent en NaN
  • Valeurs spéciales

    • Existence de +Inf et -Inf, distincts de NaN
    • -0.0 est une valeur distincte de +0.0
      • Elles sont identiques dans les comparaisons, mais se comportent différemment dans certains calculs
      • Exemple : 1.0 / +0.0 == +Inf, 1.0 / -0.0 == -Inf
  • Compatibilité avec JSON

    • Le standard JSON n’autorise ni NaN ni Inf
      • En JS, JSON.stringify convertit NaN et Inf en null
      • En Python, json.dumps(...) affiche NaN et Infinity tels quels (non conforme au standard)
        • Avec l’option allow_nan=False, la présence de NaN/Inf provoque une ValueError
      • En Golang, json.Marshal renvoie une erreur si NaN/Inf sont présents
  • Problèmes de précision

    • Une comparaison directe de nombres à virgule flottante peut échouer → forme recommandée : abs(a - b) < ε
    • En JS, tous les nombres sont traités comme des nombres à virgule flottante
      • La plage des entiers sûrs est -(2^53 - 1) à 2^53 - 1
      • En dehors de cette plage, la représentation des entiers devient imprécise
      • Pour les grands entiers, l’usage de BigInt est recommandé
      • Si JSON contient des entiers au-delà de la plage sûre, les valeurs renvoyées par JSON.parse peuvent être imprécises
      • Les timestamps en millisecondes sont sûrs jusqu’en l’an 287396, ceux en nanosecondes posent problème
  • Lois de calcul non applicables

    • Selon l’ordre des opérations, la perte de précision fait que l’associativité et la distributivité ne sont pas rigoureusement vérifiées
    • Les calculs parallèles (multiplication de matrices, sommes, etc.) peuvent produire des résultats non déterministes
  • Performances

    • La division est bien plus lente que la multiplication
    • Lorsqu’on divise plusieurs fois par la même valeur, on peut optimiser en calculant d’abord l’inverse puis en multipliant
  • Différences selon le matériel

    • Prise en charge de FMA (Fused Multiply-Add) : certains matériels effectuent les calculs intermédiaires avec une précision plus élevée
    • Traitement de la plage subnormale : pris en charge par le matériel récent, mais parfois traité comme 0 sur certains anciens systèmes
    • Différences de mode d’arrondi
      • Il existe notamment RNTE (arrondi au pair le plus proche), RTZ (troncature vers 0), etc.
      • Sur x86/ARM, cela peut être configuré comme un état mutable local au thread
      • Sur GPU, le mode d’arrondi varie selon l’instruction
    • Différences de comportement des fonctions mathématiques comme les fonctions trigonométriques ou les logarithmes
    • x86 dispose d’un FPU historique en 80 bits et d’un rounding mode par cœur → utilisation déconseillée
    • De nombreux autres facteurs peuvent aussi faire varier les résultats en virgule flottante selon le matériel
  • Méthodes pour améliorer la précision

    • Garder un graphe de calcul peu profond (réduire les chaînes successives de multiplications)
    • Éviter les cas où les valeurs intermédiaires deviennent extrêmement grandes ou extrêmement petites
    • Exploiter des opérations matérielles comme FMA

Temps (Time)

  • Seconde intercalaire (Leap second)

    • Les timestamps Unix ignorent les secondes intercalaires
    • Lorsqu’une seconde intercalaire survient, le temps peut être allongé ou raccourci dans la zone voisine (Leap smear)
  • Fuseau horaire (Time zone)

    • UTC et les timestamps Unix sont communs à l’échelle mondiale
    • L’heure lisible par les humains dépend du fuseau horaire local
    • Il est recommandé de stocker les timestamps en base de données et d’effectuer la conversion dans l’UI
  • Heure d’été (DST)

    • Dans certaines régions, l’horloge est avancée d’une heure en été
  • Synchronisation NTP

    • Pendant la synchronisation, il est possible que le temps « recule »
  • Configuration du fuseau horaire du serveur

    • Il est recommandé de configurer les serveurs en UTC
    • Dans un système distribué, des fuseaux horaires différents entre nœuds peuvent poser problème
    • Après avoir modifié le fuseau horaire du système, il faut réinitialiser ou redémarrer la base de données
  • Horloge matérielle vs horloge système

    • L’horloge matérielle n’a pas de notion de fuseau horaire
    • Linux : traite l’horloge matérielle en UTC
    • Windows : traite l’horloge matérielle comme une heure locale

Java

  • == compare les références d’objet ; pour comparer le contenu d’un objet, il faut utiliser .equals
  • Si equals et hashcode ne sont pas redéfinis, l’identité des objets dans une map/set est évaluée sur la base de la référence
  • Si l’on modifie le contenu d’un objet servant de clé dans une map ou d’élément dans un set, le comportement du conteneur est cassé
  • Une méthode qui retourne List peut, selon le cas, renvoyer un ArrayList mutable ou un Collections.emptyList() immutable ; modifier le second provoque une UnsupportedOperationException
  • Certaines méthodes censées retourner Optional renvoient parfois null (ce qui n’est pas recommandé)
  • Si un return se trouve dans un bloc finally, l’exception survenue dans try ou catch est ignorée et c’est la valeur de retour du finally qui s’applique
  • Certaines bibliothèques ignorent les interruptions, et l’initialisation de classes, y compris avec des E/S, peut être perturbée par une interruption
  • Dans un thread pool, les exceptions d’une tâche passée via .submit() ne sont par défaut pas affichées dans les logs et ne sont consultables qu’au travers de la future ; si la future est ignorée, l’exception l’est aussi
    • Une tâche scheduleAtFixedRate s’arrête silencieusement si une exception se produit
  • Si un littéral numérique commence par 0, il est interprété en octal (0123 → 83)
  • Le débogueur appelle .toString() sur les variables locales ; certaines classes ont un toString() avec effets de bord, ce qui peut modifier le comportement du code pendant le débogage (désactivable dans l’IDE)

Golang

  • append() réutilise la mémoire si la capacité restante le permet ; un append sur un subslice peut donc écraser jusqu’à la mémoire du parent
  • defer s’exécute au retour de la fonction, pas à la fin d’un bloc de portée
  • defer capture les variables mutables
  • À propos de nil
    • un nil slice et un empty slice sont différents
    • une string ne peut pas être nil, seule la chaîne vide existe
    • une nil map peut être lue mais pas modifiée
    • comportement particulier de interface nil : si le pointeur de données est nul mais que les informations de type ne le sont pas, ce n’est pas égal à nil
  • Dead wait : il existe de vrais cas de bugs de concurrence en Go
  • Il existe de nombreux types de timeout, traités en détail dans net/http

C/C++

  • Si vous stockez un pointeur vers un élément de std::vector puis que le vector grandit, une réallocation peut se produire et invalider le pointeur
  • Un std::string créé à partir d’une chaîne littérale peut être un objet temporaire ; appeler c_str() peut alors être dangereux
  • Modifier un conteneur pendant son parcours peut invalider les itérateurs
  • std::remove ne supprime pas réellement les éléments ; il les réorganise, et il faut erase pour les supprimer
  • Si un littéral numérique commence par 0, il est traité comme un octal (0123 → 83)
  • Undefined behavior (UB) : pendant l’optimisation, un UB peut être transformé librement, il est donc dangereux d’en dépendre
    • Accéder à une mémoire non initialisée provoque un UB
    • Convertir un char* en pointeur de struct puis y accéder avant le début de vie de l’objet provoque un UB ; il est recommandé d’initialiser avec memcpy
    • Un accès mémoire invalide (pointeur null, etc.) provoque un UB
    • Un overflow/underflow entier provoque un UB (pour les types unsigned, un underflow en dessous de 0 est possible)
    • Aliasing : si des pointeurs de types différents référencent la même mémoire, la strict aliasing rule peut provoquer un UB
      • Exceptions : 1) types en relation d’héritage 2) conversion via char*, unsigned char*, std::byte* (la conversion inverse n’est pas concernée)
      • Pour les conversions forcées, memcpy ou std::bit_cast sont recommandés
    • Un accès à une mémoire non alignée provoque un UB
  • Alignment mémoire
    • Un entier 64 bits doit se trouver à une adresse divisible par 8
    • Sur ARM, un accès non aligné peut provoquer un crash
    • Interpréter directement un buffer d’octets comme une struct peut créer des problèmes d’alignement
    • L’alignement peut introduire du padding dans une struct et gaspiller de la mémoire
    • Certaines instructions SIMD (AVX, etc.) ne peuvent traiter que des données alignées ; un alignement de 32 octets est généralement nécessaire

Python

  • Les arguments par défaut d’une fonction ne sont pas recréés à chaque appel ; la valeur initiale est conservée telle quelle

SQL Databases

  • Gestion de Null

    • x = null ne fonctionne pas ; il faut utiliser x is null
    • Null n’est pas égal à lui-même (comme NaN)
    • Un index unique autorise les doublons de Null (sauf dans Microsoft SQL Server)
    • Dans select distinct, la manière de traiter Null varie selon la base de données
    • count(x) et count(distinct x) ignorent les lignes où la valeur est Null
  • Comportement général

    • Les conversions implicites de dates peuvent dépendre du fuseau horaire
    • Un join complexe avec distinct peut être plus lent qu’une requête imbriquée
    • Dans MySQL(InnoDB), si un champ texte n’est pas en utf8mb4, l’insertion d’un caractère UTF-8 sur 4 octets provoque une erreur
    • MySQL(InnoDB) est par défaut insensible à la casse
    • MySQL(InnoDB) autorise les conversions implicites : select '123abc' + 1; → 124
    • Les gap locks de MySQL(InnoDB) peuvent provoquer des deadlocks
    • Dans MySQL(InnoDB), si group by et les colonnes de select ne correspondent pas, le résultat peut être non déterministe
    • Dans SQLite, sauf en mode strict, le type des champs a peu d’importance
    • Les foreign keys peuvent créer des verrous implicites et provoquer des deadlocks
    • Le locking peut, selon la base, rompre l’isolation repeatable read
    • Les bases SQL distribuées peuvent ne pas prendre en charge le locking ou avoir un comportement particulier (variable selon la base)
  • Performance / exploitation

    • Le problème des requêtes N+1 n’apparaît pas dans les slow query logs, car chaque requête individuelle est rapide
    • Les transactions longues peuvent provoquer des problèmes de verrouillage ; il est donc recommandé de les terminer rapidement
    • Cas de verrouillage de table entière
      • Dans MySQL 8.0+, l’ajout d’un index unique ou d’une foreign key peut généralement se faire de manière concurrente
      • Les anciennes versions de MySQL peuvent verrouiller la table entière
      • Si mysqldump est utilisé sans l’option --single-transaction, un verrou de lecture sur la table entière est pris
      • Dans PostgreSQL, create unique index ou alter table ... add foreign key provoquent un verrou de lecture sur la table entière
        • Évitement : utiliser create unique index concurrently
        • Pour les foreign keys : utiliser ... not valid puis validate constraint
  • Requêtes par intervalle

    • Intervalles non chevauchants :
      • La condition simple p >= start and p file 2>&1 → stdout+stderr vont tous deux dans le fichier, cmd 2>&1 > file → seul stdout va dans le fichier, stderr reste inchangé
  • Les noms de fichiers sont sensibles à la casse (contrairement à Windows)

  • Les exécutables disposent d’un système de capabilities (vérifiable avec getcap)

  • Danger des variables non définies : si DIR n’est pas défini, rm -rf $DIR/ risque d’exécuter rm -rf / → on peut s’en prémunir avec set -u

  • Application de l’environnement : pour appliquer un script au shell courant, utiliser source script.sh → pour l’appliquer de façon permanente, l’ajouter à ~/.bashrc

  • Bash met les commandes en cache : si un fichier est déplacé dans $PATH, une erreur ENOENT peut se produire → rafraîchir le cache avec hash -r

  • Quand une variable non quotée est utilisée, les retours à la ligne sont traités comme des espaces

  • set -e : quitte immédiatement en cas d’erreur dans un script, mais ne fonctionne pas à l’intérieur des conditions (||, &&, if)

  • Conflit entre livenessProbe K8s et débogueur : un débogueur à points d’arrêt peut stopper toute l’application, empêchant les réponses aux health checks → le Pod peut être tué

React

  • Modifier directement le state dans le code de rendu
  • Utiliser un Hook dans un if/une boucle → violation des règles
  • Oublier des valeurs nécessaires dans le dependency array de useEffect
  • Oublier le code de nettoyage (clean up) dans useEffect
  • Piège des closures : des bugs apparaissent à cause de la capture d’un state obsolète
  • Modifier les données au mauvais endroit → composant impur
  • Oublier d’utiliser useCallback → provoque des rerenders inutiles
  • Passer une valeur non mémorisée à un composant mémorisé annule l’optimisation de memo

Git

  • Rebase réécrit l’historique

    • après un rebase, un push normal entre en conflit → il faut absolument faire un force push
    • si l’historique de la branche distante change, utiliser aussi --rebase pour le pull
    • --force-with-lease peut, dans certains cas, éviter d’écraser les commits d’un autre développeur, mais si on fait seulement fetch sans pull, cette protection ne fonctionne pas
  • Problème du revert de merge

    • un revert de merge a un effet incomplet → si on merge à nouveau la même branche, il n’y a aucun changement
    • solution : faire le revert du revert, ou employer une méthode propre (backup → reset → cherry-pick → force push)
  • Points d’attention liés à GitHub

    • même si un secret comme une clé API est écrasé par un force push après commit, GitHub en conserve une trace
    • si B est un fork privé du dépôt privé A, et que A devient public, le contenu de B devient lui aussi public (et peut rester accessible même après suppression)
  • git stash pop : en cas de conflit, le stash n’est pas supprimé

  • .DS_Store est créé automatiquement par macOS → il est recommandé d’ajouter **/.DS_Store à .gitignore

Networking

  • Certains routeurs et pare-feu coupent silencieusement les connexions TCP inactives → cela peut invalider les pools de connexions des clients HTTP et DB → solution : configurer TCP keepalive
  • Le résultat de traceroute est peu fiable → selon les cas, tcptraceroute peut être plus utile
  • Le TCP slow start peut augmenter la latence → on peut y remédier en désactivant tcp_slow_start_after_idle
  • Problème de sticky packets TCP : l’algorithme de Nagle retarde l’envoi des paquets → on peut y remédier en activant TCP_NODELAY
  • Quand un backend est placé derrière Nginx, il faut configurer la réutilisation des connexions → sinon, en forte charge, l’épuisement des ports internes peut provoquer des échecs de connexion
  • Nginx bufferise les paquets par défaut → cela peut retarder les SSE (EventSource)
  • Le standard HTTP n’interdit pas le body des requêtes GET et DELETE → certains l’utilisent, mais beaucoup de bibliothèques et serveurs ne le prennent pas en charge
  • Il est possible d’héberger plusieurs sites web sur une seule IP → la distinction est assurée par l’en-tête HTTP Host et le SNI de TLS → certains sites ne sont donc pas accessibles via une simple connexion par IP
  • CORS : lors d’une requête vers une autre origin, le navigateur bloque l’accès à la réponse → le serveur doit définir l’en-tête Access-Control-Allow-Origin
    • des réglages supplémentaires sont nécessaires si les cookies doivent être transmis
    • si le frontend et le backend utilisent le même domaine et le même port, il n’y a pas de problème de CORS

Other

  • Points d’attention YAML

    • YAML est sensible aux espaceskey:value est incorrect, key: value est correct
    • le code pays NO, écrit sans guillemets, peut être interprété comme false
    • un hash de commit Git écrit sans guillemets peut être converti en nombre
  • Problèmes de CSV avec Excel

    • Excel effectue des conversions automatiques à l’ouverture d’un CSV
      • conversion de date : 1/2, 1-22-Jan
      • conversion imprécise des grands nombres : 1234567890123456789012345678901234500000
    • la cause est qu’Excel traite les nombres en floating point en interne
    • ce problème a déjà conduit à une modification erronée du nom de gène SEPT1

1 commentaires

 
GN⁺ 2025-08-18
Avis Hacker News
  • Certains routeurs et pare-feu coupent silencieusement des connexions TCP inactives sans envoyer le moindre signal à l’application ; du code qui maintient un pool de connexions TCP, comme les bibliothèques clientes HTTP ou les clients de base de données, peut alors se retrouver avec des connexions invalidées sans aucun avertissement. Pour corriger cela, on peut configurer le TCP keepalive du système ou, pour HTTP, utiliser les en-têtes Connection: keep-alive, Keep-Alive: timeout=30, max=1000. Une fois la connexion TCP établie, les routeurs intermédiaires ne conservent pas d’état ; le vrai problème vient des timeouts de session des pare-feu ou du NAT. Dans ce cas, il n’y a même pas de paquet RST. Dans un environnement K8s, certains ont déjà rencontré un paramétrage du module conntrack trop bas. Même avec HTTP Keep-Alive, cela n’aide qu’à réutiliser la connexion et pas à la maintenir vivante sur le réseau (lien d’explication) ; HTTP Keep-Alive ne génère en réalité aucun paquet et ne fait que retarder la fermeture. À l’inverse, TCP Keep-Alive envoie périodiquement des paquets pour réinitialiser le timer

    • TCP Keep-Alive peut mal fonctionner sur les appareils mobiles ; les OS mobiles ne peuvent suivre ou gérer individuellement que les paquets keep-alive générés au niveau applicatif. Or TCP Keep-Alive fonctionne sous le niveau de l’application, donc il peut parfois être désactivé alors même que l’app reste accessible
  • Une méthode qui renvoie Optional<T> peut quand même retourner null. Cette pratique est vraiment trop confuse. Si j’avais eu plus de marge émotionnelle, j’aurais peut-être proposé une JEP introduisant une annotation comme @java.lang.NonNullReference. J’aimerais qu’en déclarant un type avec cette annotation, le compilateur traite toute affectation de null comme une erreur ; par exemple, Alpha accepterait une affectation de null, mais Beta lèverait une erreur. J’ai aussi l’impression qu’il faudrait regarder la spec de plus près pour voir comment fonctionne réellement la dead code elimination dans javac. Par exemple, dans if (true), la partie qui fait b = null est en pratique élidée, et pourrait donc être légalement autorisée

    • En Kotlin, c’est déjà une erreur de compilation dans ce genre de cas, même sans annotation

    • On peut aussi se demander s’il est vraiment nécessaire d’utiliser Optional<T> dans un langage où null existe déjà. Si l’on doit de toute façon faire une vérification, comme en Python où une valeur de retour s’écrit simplement T | None au lieu d’un objet Optional, la différenciation du framework devient assez floue. À moins d’adopter un style monadique particulier, la vérification reste au fond la même

  • Il était dit que Java, C# et JS encodent les chaînes en mémoire sous une forme apparentée à UTF-16, mais c’est faux pour Java ; et peut-être aussi pour C# et JS. Tant que le type string d’un langage est suffisamment opaque, sa représentation mémoire est un détail d’implémentation. En Java, c’est devenu explicite à partir de la version 9 (JEP correspondant), ce qui explique aussi pourquoi il est parfois difficile de faire évoluer ces détails en présence d’une FFI. Autre point : on lit souvent que l’entier exact maximal en JS est 2^53−1, alors qu’en réalité des entiers bien plus grands, comme 2^100, peuvent aussi être représentés exactement. 2^53−1 signifie en fait que n-1, n et n+1 sont tous représentables exactement en IEEE double ; ainsi n == n-1 et n == n+1 renvoient tous deux false

    • En C#, la représentation en mémoire est très figée ; on accède souvent directement au buffer via ReadOnlySpan<char> ou un char* brut, donc char y est bien un type de point de code UTF-16. Pour JS, il y a peut-être moyen de contourner ça d’une manière ou d’une autre

    • Je préfère l’expression max safe integer à max accurate integer

    • (À propos de l’encodage Base64) Concernant le fait que les chaînes en mémoire de Java, C# et JS ne seraient pas de type UTF-16 ou apparenté, c’est peut-être techniquement vrai, mais il existe en pratique des cas où l’on encode une chaîne en Base64 dans un langage basé sur UTF-8 puis on la décode en Java, et la représentation UTF-16 de Java pose alors problème

  • J’ai l’impression que les conseils ou informations au format manuel s’assimilent surtout vite quand on les connaît déjà, ou presque. La plupart des manuels sont plus efficaces pour structurer et réviser que pour apprendre à partir de zéro, et sont peu efficaces pour enseigner à des gens qui découvrent totalement le sujet

    • Un manuel existe par nature pour laisser une trace écrite au lieu de tout faire reposer sur la mémoire ; la plupart des manuels Unix fonctionnent comme cela. On consulte un manuel quand on sait déjà ce que fait un logiciel, mais qu’on a oublié un détail d’usage. À l’inverse, un vrai débutant a besoin d’un tutoriel ou d’un guide pour acquérir les concepts. Le manuel sert plutôt de préparation à poser de meilleures questions
  • J’ai trouvé l’article « Traceroute Isn’t Real » vraiment fascinant. J’avais depuis longtemps l’impression que les données de traceroute étaient souvent très imprécises ou peu significatives, et ça m’a aidé à comprendre pourquoi (lien vers l’article original). S’il existe des infos plus récentes, je suis preneur

  • Cet article ressemble moins à une liste de vrais pièges qu’à une collection de petits conseils appris empiriquement par son auteur. Beaucoup de points ne s’appliquent qu’à des contextes très étroits, sans que le contexte soit clairement précisé, et certains paraissent même erronés. Mieux vaut donc ne pas tout prendre au pied de la lettre et le lire plutôt comme un fil de pensée ou des notes

  • L’histoire selon laquelle, en Python, une valeur d’argument par défaut n’est pas recréée à chaque appel mais réutilise une valeur stockée, c’est vraiment un point à connaître quand on manipule des variables datetime

    • Je n’étais pas un développeur Python de longue date, mais cette semaine j’ai vraiment souffert à cause de cette histoire de valeur d’argument par défaut conservée. J’essayais d’affecter un set vide quand aucun set n’était passé en argument, mais le set était réutilisé et cela a provoqué un bug. Il m’a fallu un bon moment pour en trouver la cause
  • Dans le premier « piège » de la page, il était dit que min-width: auto détermine une largeur minimale selon le contenu, mais ce n’est pas vrai hors flex/grid. D’après MDN, pour block, inline, table, etc., auto est résolu à 0 (documentation officielle)

    • Le vrai premier piège, c’est qu’on ne peut lire aucune propriété CSS de manière isolée. Comme le nom même de cascading l’indique, les valeurs par défaut et le résultat de plusieurs règles se combinent quelque part, donc le contexte global du document est essentiel

    • La cascade du côté des propriétés textuelles CSS reste encore compréhensible. En revanche, la mise en page CSS est incroyablement difficile à comprendre, que l’on soit designer de page, implémenteur ou utilisateur. On a du mal à voir pour qui ce design a réellement été pensé

  • Dans l’ensemble, c’est une bonne liste. Quelques remarques.

    • Sur l’unification Unicode, le fait que des caractères de même sens partagent le même point de code dans plusieurs langues et s’affichent différemment selon la police n’est pas un piège. Les hanzi/kanji de l’exemple sont utilisés presque à l’identique en chinois et en japonais, et les utilisateurs des deux langues reconnaissent aussi les autres variantes comme le même concept. L’auteur raisonne comme s’il fallait définir la lettre « A » différemment en anglais et en français, alors que ce n’est pas le cas ; voir l’article Han unification
    • -0.0 et +0.0 (zéro négatif et zéro positif) sont considérés égaux dans les comparaisons en virgule flottante, mais on peut les distinguer, soit par leur motif binaire, soit via le résultat de 1.0/-0.0 = -infini et 1.0/0.0 = +infini
    • Je suis tout à fait d’accord avec la recommandation d’utiliser UTC comme fuseau horaire des serveurs : serveurs, logs, stockage de photos et tout ce qui a une valeur de conservation ou exige des timestamps exacts devrait utiliser UTC ; l’heure locale ne devrait servir qu’à la conversation
    • Il était conseillé d’utiliser low + (high - low) / 2 au lieu de (low + high) / 2 pour éviter l’overflow sur les entiers, mais si low ou high peuvent être négatifs, cela peut simplement déplacer la zone de risque ; c’est un vrai sujet dans la recherche binaire classique
    • En C/C++, bien utiliser les types entiers et les opérations entières est aussi un énorme piège ; voir ce guide
    • Il était dit qu’un rebase peut modifier l’historique, mais un rebase est par définition une commande qui réécrit l’historique
    • Même si, conceptuellement, les locuteurs des deux langues reconnaissent le même caractère, il ne faut pas réduire cela à une simple « variante de police ». Le fait d’avoir le même point de code Unicode ne signifie pas qu’une substitution de caractère soit sûre ; du point de vue d’un utilisateur japonais, ce genre de substitution peut suffire à faire rejeter un produit

    • En réalité, on ne distingue pas le A anglais du A français, mais il existe des cas comme А (cyrillique) et A (latin), d’apparence identique mais correspondant à des points de code différents. L’unification Han regroupe aussi souvent sous un même code des caractères dont la forme diffère sensiblement ; pour quelqu’un qui apprend le japonais ou le chinois, cela peut être réellement déroutant. Par exemple, le caractère « » (drink) peut avoir des rendus très différents selon le contexte, comme le montre ce lien, et le simple fait de le copier peut parfois en changer immédiatement la forme tant le traitement est complexe. Han unification est, en pratique, un sujet assez épineux

  • Il était mentionné qu’il existe des différences subtiles entre numpy et pytorch, mais sans exemple concret cela n’apporte pas grand-chose et reste trop vague pour ressembler à un vrai piège utilisable en pratique