Retour au blog

Pourquoi migrer de PHP vers Go

PHP mobilise un worker entier pour chaque requête, alors qu'un seul processus Go peut maintenir des milliers de connexions à la fois. C'est cette différence de gestion de la concurrence qui pousse vraiment les équipes à migrer, et vous pouvez déplacer un service à la fois plutôt que de tout réécrire.

Pourquoi migrer de PHP vers Go

Quand PHP peine à monter en charge, la vitesse brute du langage est rarement en cause. Le problème vient de sa façon de traiter les requêtes concurrentes. Avec PHP-FPM, chaque requête occupe un processus worker complet pendant toute sa durée. Toute requête qui passe son temps à attendre, que ce soit une requête SQL lente, un appel à un service tiers ou une connexion qui reste ouverte, bloque un worker qui pourrait servir quelqu'un d'autre. Les fonctionnalités temps réel comme les tableaux de bord en direct, les réponses en streaming et les API à fort fan-out atteignent vite cette limite. Ajouter des workers ou des machines repousse le plafond, mais il est toujours là.

Le modèle de concurrence de Go n'a pas ce plafond, et c'est la principale raison pour laquelle les équipes passent de PHP à Go. Cet article explique pourquoi ce modèle compte, ce qu'il apporte au déploiement et aux coûts, ce que les équipes ont constaté après la migration, et comment migrer un service à la fois plutôt que de miser tout le produit sur une réécriture.

En bref

  • Migrez pour le modèle de concurrence. La conception un-processus-par-requête de PHP mobilise un worker entier pendant toute la durée de chaque requête. Les goroutines de Go permettent à un seul processus de gérer des milliers de connexions concurrentes. L'écart se creuse à mesure que vous ajoutez des fonctionnalités temps réel et gourmandes en connexions.
  • L'écart de mémoire est considérable. Un worker PHP-FPM occupe environ 30 à 60 MB une fois votre framework chargé. Une goroutine démarre à environ 8 KB. Cinquante flux concurrents peuvent saturer un pool FPM alors qu'un seul processus Go les remarque à peine.
  • Les équipes qui ont migré font état de gains importants. Des ingénieurs de Cloudflare et de la fintech Curve décrivent un service passé d'environ 10 requêtes par seconde à plusieurs milliers après la migration vers Go, avec de nouveaux ingénieurs productifs en une à deux semaines (Go Time #316).
  • Le déploiement est plus simple. Go compile vers un binaire statique unique. Pas de runtime à installer, pas de Composer à lancer, pas de FPM à régler, ce qui convient très bien aux microservices et à Kubernetes.
  • Vous n'avez pas à tout réécrire. Utilisez le strangler pattern. Migrez le service qui pose problème, faites-le tourner à côté de votre PHP, puis étendez la démarche une fois qu'il a fait ses preuves.

Table des matières

La vraie raison de migrer : la concurrence

Le PHP classique repose sur une architecture share-nothing, un processus par requête. Une requête arrive, PHP-FPM la confie à un worker, et ce worker exécute votre code en partant d'un état vierge jusqu'à la réponse, puis se réinitialise. La conception est simple et bien isolée. Ce qu'elle vous coûte, c'est de la mémoire.

Un worker PHP-FPM occupe en général 30 à 60 MB de mémoire résidente une fois votre framework chargé. Servir 50 requêtes simultanées exige 50 workers, soit environ 1,5 à 3 GB de RAM consacrés surtout à attendre.

Imaginez maintenant que ces 50 requêtes soient des flux Server-Sent Events ou des connexions WebSocket qui restent chacune ouvertes plusieurs minutes. Cinquante connexions longues peuvent saturer tout un pool FPM, et le 51e utilisateur fait la queue. Résoudre cela à coups de matériel devient vite coûteux, parce que chaque connexion concurrente coûte un processus entier.

Go fonctionne à l'inverse. Une goroutine, l'unité de concurrence de Go, démarre avec une pile d'environ 8 KB, et le runtime en multiplexe des milliers sur un petit pool de threads système. Quand une goroutine se bloque sur une I/O, l'ordonnanceur la met en attente et en exécute une autre sur le même thread. Un seul processus Go peut maintenir des dizaines de milliers de connexions ouvertes en utilisant une fraction de la mémoire qu'exigerait une flotte FPM équivalente. Un handler de connexion n'est qu'une fonction ordinaire :

example.gogo
func (s *Server) handleStream(w http.ResponseWriter, r *http.Request) {
	// Each connection is one cheap goroutine. Blocking here parks
	// this goroutine and frees the thread for others, no pool to exhaust.
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "streaming unsupported", http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "text/event-stream")

	for event := range s.events(r.Context()) {
		fmt.Fprintf(w, "data: %s\n\n", event)
		flusher.Flush()
	}
}

Le serveur net/http exécute automatiquement chaque requête dans sa propre goroutine, si bien que ce code maintient une connexion de streaming active sans réserver un worker lourd. Si votre produit évolue vers des fonctionnalités temps réel, des API gateways à fort fan-out ou des services qui maintiennent de nombreuses connexions concurrentes, cette différence pèse lourd. Le modèle de PHP joue contre ces charges de travail, alors que celui de Go les absorbe sans effort particulier. Si les goroutines et les channels sont nouveaux pour vous, le cours Go Concurrency Fundamentals présente ce modèle de façon pratique.

Ce que les équipes gagnent après la migration

Le meilleur témoignage de première main sur une vraie migration de PHP vers Go est l'épisode 316 du podcast Go Time. Des ingénieurs de Cloudflare et de la fintech Curve y racontent comment ils ont migré des systèmes PHP en production vers Go (Go Time #316, Changelog). Ce sont des praticiens qui décrivent leurs propres systèmes, ce qui rend ce témoignage plus utile qu'une étude de cas d'éditeur ou qu'un article de blog qui devine les chiffres de l'extérieur.

Un service serait passé d'environ 10 requêtes par seconde avec l'ancienne configuration à plusieurs milliers après la migration. Ils ont résumé le runtime par « even bad Go is incredibly performant » (même du mauvais Go est incroyablement performant), autrement dit vous obtenez beaucoup de débit avant d'avoir besoin d'optimiser quoi que ce soit. Les nouveaux ingénieurs étaient productifs en une à deux semaines plutôt qu'en plusieurs mois, ce qui tient à la taille réduite du langage Go.

La filiation Go de Curve remonte à Monzo, une autre fintech qui a beaucoup construit sur Go, et c'est en partie ainsi que ce modèle s'est répandu dans ce secteur.

Il existe aussi d'autres exemples de Go en production. HelloFresh a développé en Go son API gateway, Janus, et l'a publiée en open source (Janus sur GitHub). Une gateway est une couche de routage à fort fan-out, exactement le type de charge où les goroutines aident le plus. Vous trouverez aussi de nombreux ingénieurs qui rapportent qu'un simple batch réécrit de PHP en Go tourne beaucoup plus vite avec une fraction des ressources.

Go vs PHP : les avantages techniques

Voici comment les deux se comparent sur ce qui compte pour un service backend.

CritèrePHPGo
ConcurrenceUn processus par requête, les Fibers ajoutant de la concurrence mais pas de parallélismeGoroutines sur un ordonnanceur M:N, vrai parallélisme sur tous les cœurs
TypageDynamique, type hints progressifsStatique, vérifié à la compilation
DéploiementCode + dépendances Composer + FPM + serveur webUn binaire statique unique, aucun runtime à installer
ExécutionInterprété, assisté par JITCompilé en code machine natif

La concurrence est le point le plus important. PHP 8.1 a introduit les Fibers, une vraie amélioration, mais il faut être précis sur ce qu'elles font. Les Fibers apportent une concurrence coopérative pour structurer les I/O asynchrones. Elles permettent à un worker d'entrelacer des tâches en attente. Elles ne permettent pas à un processus PHP de saturer 16 cœurs avec du travail CPU comme le fait l'ordonnanceur de Go.

Go répartit de nombreuses goroutines sur de nombreux threads système, les exécute en parallèle et les met en attente à moindre coût quand elles se bloquent. La même primitive vous donne une concurrence bon marché et un vrai parallélisme. Un worker pool en est un bon exemple. En Go, vous y recourez sans cesse, et le PHP classique n'a pas d'équivalent propre au sein d'un seul processus :

example.gogo
func process(jobs <-chan Job, results chan<- Result, wg *sync.WaitGroup) {
	defer wg.Done()
	for job := range jobs {
		results <- job.Run() // each worker pulls the next job when free
	}
}

func main() {
	jobs := make(chan Job, 100)
	results := make(chan Result, 100)
	var wg sync.WaitGroup

	// Fan out to 8 workers sharing one queue, all in one process.
	for i := 0; i < 8; i++ {
		wg.Add(1)
		go process(jobs, results, &wg)
	}

	go func() {
		for _, j := range loadJobs() { // pull the batch from your work source
			jobs <- j
		}
		close(jobs)
	}()

	go func() { wg.Wait(); close(results) }()
	for r := range results {
		record(r)
	}
}

Le déploiement reçoit moins d'attention qu'il ne le mérite. Un build Go produit un binaire statique unique, et le livrer peut se résumer à copier ce fichier sur un serveur ou dans un conteneur scratch. Pas de runtime PHP à installer, pas d'étape Composer, pas de réglage de FPM, et pas de configuration de serveur web devant. Si vous faites tourner des microservices dans Kubernetes, vous obtenez cet artefact léger et autonome à chaque déploiement.

Le typage statique aide aussi. Le compilateur détecte au build toute une catégorie d'erreurs que PHP ne révèle qu'à l'exécution, et cela compte davantage à mesure qu'un service grandit et que plus de personnes y travaillent.

Reste l'objection « le PHP moderne est rapide maintenant ». PHP a effectivement comblé une grande partie de l'écart de vitesse brute, avec le JIT de PHP 8 et des runtimes à workers comme FrankenPHP et RoadRunner qui gardent votre application chargée en mémoire d'une requête à l'autre. Mais FrankenPHP et RoadRunner sont tous deux écrits en Go. Quand l'écosystème PHP a eu besoin de serveurs d'applications capables d'une forte concurrence, il les a construits sur le runtime de Go. Si la concurrence est votre problème, vous pouvez placer un runtime propulsé par Go devant PHP, ou bien écrire directement en Go le service gourmand en connexions et vous passer de la couche intermédiaire.

Ce que disent vraiment les benchmarks (et le vrai bénéfice)

Vous verrez des chiffres accrocheurs affirmant que Go est 10 à 30 fois plus rapide que PHP. Ils proviennent de suites synthétiques comme les benchmarks TechEmpower, qui testent les runtimes dans des conditions idéalisées, limitées par le CPU et le framework (TechEmpower Framework Benchmarks). Ces chiffres sont réels, mais ils ne prédisent pas votre latence en production. Ne migrez donc pas en espérant ce multiplicateur.

En production, l'essentiel du temps d'une requête passe dans la base de données et le réseau, et Go comme PHP les attendent de la même manière. Le multiplicateur fond rapidement. Les équipes qui mesurent honnêtement après une migration rapportent souvent des gains allant de quelques pour cent à 10 ou 20 % sur la latence moyenne de bout en bout.

La moyenne masque là où Go l'emporte : la stabilité de la latence de queue et le coût par requête. Les latences p99 et p99.9 de Go restent stables sous charge, car aucune requête n'a à attendre qu'un worker FPM se libère. Vous servez aussi le même trafic avec bien moins de machines, puisqu'un seul processus absorbe une concurrence qu'un pool ne peut pas encaisser. Ce sont ces gains que vous remarquez en production et sur la facture, et ils durent bien après que plus personne ne s'intéresse à un benchmark synthétique.

Recruter plus vite, livrer plus vite

Le recrutement et l'onboarding jouent aussi en faveur de Go. Le langage est petit, avec 25 mots-clés et un formateur imposé, si bien qu'un ingénieur compétent venant de PHP ou de n'importe quel autre langage est productif en une ou deux semaines. Cela correspond au temps d'onboarding décrit par les ingénieurs de Cloudflare et de Curve.

Vous n'avez donc pas besoin de recruter des personnes qui connaissent déjà Go. Elles l'apprennent vite, et le formatage imposé ainsi que le typage statique gardent leur code cohérent dès le premier jour.

Go s'accompagne aussi souvent d'une prime salariale dans les enquêtes de marché, ce qui suggère que la demande dépasse l'offre. Les chiffres exacts cités par des sources comme Glassdoor varient selon la région et le poste. Considérez-les donc comme un vent favorable pour le recrutement, pas comme une ligne d'un business case. L'écosystème Go est le plus solide dans le réseau, l'infrastructure, les CLI et les services à haut débit, c'est-à-dire le travail que vous migreriez. Les goroutines ont été conçues pour ce travail, et les bibliothèques et l'outillage qui l'entourent sont matures.

Comment migrer sans mettre l'entreprise en jeu

Ne réécrivez pas tout d'un coup. Les réécritures complètes sont l'un des moyens les plus sûrs de paralyser un produit, parce que du code qui fonctionne contient des années de corrections de bugs et de cas limites que vous jetteriez (Joel on Software). Les équipes de Cloudflare et de Curve n'ont pas fait de réécriture big bang non plus. Elles ont utilisé le strangler pattern, et il mérite d'être copié.

Commencez par le service qui pose réellement problème : le flux SSE qui sature votre pool, l'API à fort fan-out qui bloque vos workers, ou la gateway incapable de maintenir assez de connexions. Reconstruisez uniquement ce service en Go, placez-le derrière la même couche de routage et faites-le tourner à côté de votre PHP.

Dirigez une partie du trafic vers lui et surveillez la mémoire et la latence de queue avant d'en envoyer davantage. Votre PHP continue de servir tout le reste pendant ce temps, il n'y a donc pas de jour J et rien ne dépend d'une bascule unique. Quand ce premier service a fait ses preuves, ce qui est généralement le cas, vous disposez de chiffres réels et d'un modèle pour le suivant.

La migration devient ainsi une décision service par service. Vous n'avez pas à devenir « une boîte Go ». Gardez PHP là où il a sa place, comme le CMS, les pages de contenu ou l'admin Laravel avec laquelle votre équipe livre vite, et déplacez vers Go les services gourmands en connexions et à haut débit.

Questions fréquentes

Go est-il plus rapide que PHP ?

Sur des benchmarks synthétiques limités par le CPU, oui, souvent d'un facteur important (TechEmpower). En production, l'écart de latence moyenne est généralement plus faible, car l'essentiel du temps d'une requête passe dans la base de données et le réseau, et les deux langages les attendent de la même manière. Go conserve son avance en concurrence et en latence de queue sous forte charge. Il maintient des milliers de connexions dans un seul processus et garde une latence p99 stable là où un pool FPM manquerait de workers, si bien qu'il vous faut moins de machines pour le même trafic.

Dois-je réécrire toute mon application PHP en Go ?

Non. Migrez de façon incrémentale. Les réécritures complètes sont l'un des moyens les plus sûrs de paralyser un produit (Joel on Software). Utilisez le strangler pattern : déplacez vers Go le service problématique à forte concurrence, faites-le tourner à côté de votre PHP, basculez progressivement le trafic, puis étendez la démarche une fois qu'il a fait ses preuves. Vous obtenez les bénéfices sans un jour J risqué.

Quels services migrer en premier ?

Commencez par ceux qui sont gourmands en connexions et à fort fan-out : les endpoints WebSocket et SSE, les fonctionnalités temps réel, les API gateways, les réponses en streaming et tout service où de nombreuses requêtes restent ouvertes en attendant des I/O. Le modèle un-processus-par-requête de PHP atteint ses limites d'abord sur ces services, et les goroutines y aident immédiatement. Ce sont donc eux qui vous donnent le plus vite la preuve que la migration est rentable.

PHP peut-il gérer les WebSockets et le temps réel ?

Oui, avec de l'aide. Le PHP-FPM classique mobilise un worker par connexion, donc de nombreuses connexions longues épuisent le pool. Des runtimes comme Swoole et FrankenPHP gardent les workers en vie et ajoutent une concurrence événementielle pour que cela fonctionne. Go gère nativement la même charge, avec une goroutine bon marché par connexion. Les services gourmands en connexions sont la raison la plus courante pour laquelle les équipes migrent.

Go est-il plus difficile à apprendre que PHP ?

Pas sa syntaxe. Go n'a que 25 mots-clés et un seul style de formatage imposé, et la plupart des ingénieurs sont productifs en une à deux semaines. Ce qui est nouveau pour un développeur PHP, c'est le modèle de concurrence (goroutines et channels) et la gestion des erreurs sous forme de valeurs retournées plutôt que d'exceptions. Cela demande un peu de pratique, et ce sont aussi les compétences que vous utiliserez le plus dans les services que vous migrerez.

Prêt à écrire du Go en production ?

La meilleure façon d'évaluer Go est d'en écrire. Sur LevelUpGo, chaque leçon est un exercice pratique que vous résolvez dans le navigateur. Votre code s'exécute sur une vraie toolchain Go et est vérifié par les tests de l'exercice, vous obtenez donc un retour immédiat sur du Go qui fonctionne au lieu de simplement lire à son sujet.

Un parcours pour un développeur PHP :

  • Go Fundamentals couvre les types, les structs et la gestion des erreurs, les aspects qui paraissent différents quand on vient de PHP. Il est gratuit pour commencer et ne demande aucune carte bancaire.
  • Composite Types couvre les slices, les maps et les structs, les briques de base quotidiennes des données qui transitent par un service.
  • Go Concurrency Fundamentals enseigne les goroutines, les channels et select, les fonctionnalités qui permettent à un seul processus Go de faire ce qu'un pool PHP-FPM entier ne peut pas faire.
  • La feuille de route trace le chemin de votre premier programme jusqu'à un projet final concurrent.

Pour d'autres comparaisons, lisez Passer de Python à Go, Go vs Rust en 2026 et Go vs Node.js et la question de la chaîne d'approvisionnement.

Sources

Sources citées dans cet article, en commençant par les témoignages de praticiens de première main :

Écrivez du Go comme un ingénieur senior

Des leçons interactives dans votre navigateur. Les premières sont gratuites.

Essayer une leçon gratuiteOu créer un compte gratuit