Une release de Go pousse les développeurs Go à se disputer sur ce que Go est censé être, et j'avoue que je savoure la vue depuis notre côté de la clôture. Go 1.27 apporte les méthodes génériques, un moteur JSON réécrit glissé sous l'ancienne API, des UUID dans la bibliothèque standard, du SIMD portable expérimental, des signatures post-quantiques ML-DSA, des chemins d'allocation moins coûteux pour les objets de moins de 80 octets, et la détection à l'exécution des goroutines qui ne se réveilleront plus jamais. D'où l'angoisse : le langage qui a bâti sa marque en disant non aurait-il fini par dire oui ? Je pense que l'angoisse vise la mauvaise cible. La simplicité n'a jamais été une propriété de la fiche technique. Elle vit dans le diff que ton collègue relit un mardi après-midi, et à cette aune un langage peut se doter d'internes énormément compliqués tout en restant simple là où ça compte. PHP a compris ça il y a des années, en grande partie par accident, et Go 1.27 se lit pour moi comme l'aveu officiel qu'on tenait quelque chose.

Regarde d'abord le changement JSON, parce que c'est le geste le plus PHP de toute la release. Go a désormais encoding/json/v2 avec des défauts plus stricts et davantage de configuration, mais l'ancien encoding/json continue de fonctionner parce qu'il tourne maintenant au-dessus de la nouvelle machinerie, avec une porte de sortie si la transition casse quelque chose. Vieil import, nouveau moteur. On a vécu exactement ça. PHP 7 a remplacé les entrailles du Zend Engine, retravaillé les zvals et les hashtables, et la plupart des bases de code ont migré en changeant un tag Docker. PHP 8.0 a boulonné un JIT sur opcache et tes contrôleurs n'en ont rien su et s'en fichaient. Personne ne s'est levé en conférence pour accuser PHP de trahir le minimalisme, parce que personne n'a jamais pris le moteur de PHP pour un artefact minimal. Le contrat a toujours été que le noyau fait le sale boulot pour que le userland reste ennuyeux, et un userland ennuyeux, c'est précisément le but.

Les méthodes génériques sont le cas plus épineux, et je veux être honnête avec ceux que ça inquiète. Une méthode qui peut introduire ses propres paramètres de type, indépendamment de son receveur, ce n'est pas de la plomberie de moteur. Ça apparaît en revue de code. Ça fait passer pour du code normal des types Result avec un Map qui transforme vers un Result paramétré différemment, et une fois que ça semble normal, les pipelines typés fluides suivent, puis les API de builders, puis des designs de bibliothèques importés de Rust et de Kotlin. L'argument le plus fort contre les fonctionnalités expressives n'a jamais été qu'elles sont inutiles. C'est qu'elles changent ce que tes coéquipiers utilisent par défaut, et le style par défaut d'une équipe est bien plus dur à relire que n'importe quelle fonction astucieuse isolée. Cet argument mérite le respect. J'ai vu une base de code Laravel où un auteur de macros un peu trop enthousiaste a discrètement redéfini ce que voulait dire idiomatique pour tout le reste de l'équipe.

Et malgré tout je penche du côté des ajouts, en partie à cause de la façon dont Go les a clôturés. Les méthodes d'interface ne peuvent pas déclarer leurs propres paramètres de type dans la 1.27, et les méthodes génériques ne peuvent pas satisfaire des contrats d'interface, ce qui veut dire que le nouveau pouvoir existe mais ne peut pas se propager par le canal d'abstraction principal du langage. C'est du rationnement, et le rationnement fonctionne. PHP mène une version de la même expérience au quotidien : on a des génériques en pratique via les annotations en docblocks et PHPStan ou Psalm qui les font respecter, avec zéro support à l'exécution. C'est bancal, c'est une trêve plutôt qu'un design, mais l'effondrement annoncé dans la soupe d'abstractions n'est jamais arrivé. Les équipes qui voulaient des collections typées les ont eues. Celles qui n'en voulaient pas ont continué à écrire des arrays. L'outillage a porté la complexité pour que le moteur n'ait pas à le faire, ce qui n'est qu'une autre réponse à la même question de qui porte le fardeau.

L'histoire des UUID dans la stdlib, c'est là que les deux écosystèmes divergent vraiment, et je suis moins sûr de qui a raison. Go a rapatrié les UUID dans la bibliothèque standard, puis a dû donner à database/sql une connaissance spéciale du nouveau type pour qu'il se comporte bien avec les conversions de base de données, une verrue qui va à l'encontre de l'éthique des petites interfaces. PHP a pris le chemin exactement inverse : le noyau n'a jamais livré d'UUID, et ramsey/uuid plus symfony/uid sont devenus des standards de fait via Composer. Notre approche garde le noyau propre et refile le choix à chaque projet. La leur bénit une implémentation et accepte une bavure architecturale pour la rendre ergonomique. J'ai tapé composer require ramsey/uuid tellement de fois que mes doigts le font sans surveillance, et je suis toujours incapable de te dire si c'est de la liberté ou juste une taxe que j'ai cessé de remarquer.

Ce que je conteste, c'est le cadrage en pente glissante, l'idée qu'un langage qui apprend à absorber de la complexité continuera d'absorber jusqu'à devenir la chose qu'il fuyait. L'accumulation est réelle, bien sûr. Mais le garde-fou n'est pas le refus, c'est une règle lisible sur l'endroit où la complexité a le droit de vivre. La règle implicite de PHP a mieux tenu qu'on ne veut bien le reconnaître : dépense sans compter sous la surface, dans opcache, le JIT, le preloading et la couche FFI, et sois avare en syntaxe qui change l'allure du code moyen. Les enums, les propriétés readonly, la syntaxe des first-class callables, tout ça a franchi une barre haute et mérité sa place. Le processus RFC est lent et parfois exaspérant, et c'est aussi pour ça que du code PHP 8.4 se lit encore comme du PHP pour quelqu'un qui a quitté l'écosystème en 2016.

Alors voilà ce que je te demande vraiment, parce que le débat côté Go est une répétition générale d'un débat qu'on repousse sans cesse. Si une RFC arrivait demain avec de vrais génériques réifiés en PHP, imposés par le moteur, sans docblocks, au prix d'une sérieuse complexité interne et de la quasi-certitude que chaque bibliothèque majeure ferait pousser des API typées de builders et de pipelines en deux ans, voterais-tu oui ? Ou bien la trêve actuelle, l'analyse statique qui fait le travail des génériques pendant que le runtime reste en dehors, est-elle secrètement la meilleure version du placement de la complexité qu'on aurait pu concevoir exprès ? Je change d'avis chaque semaine. Dis-moi où tu atterris, et dis-moi à quoi ressemblerait ta base de code trois ans après l'une ou l'autre réponse.