From PHP/Laravel to Go: the honest accounting after more than a decade of PHP
It's been over a decade (in fact, almost two) that I’ve been designing architectures in PHP and a decade in Laravel. Today I maintain Rush CMS, my multi-tenant headless CMS in Laravel + Filament + Octane with RoadRunner, PostgreSQL/JSONB, serving multiple production sites from a single panel. I’ve been testing Go in parallel, planning v2 as a hybrid stack: Go at the API core, Svelte for the front-end, Python/FastAPI for the AI layer.
This post is about what I’ve learned along the way. It’s not "Go is better than Laravel" — if you’re looking for those religious battles, this is not the place, that discussion is laziness from people who’ve never put a serious system into production. I’ll talk about when each tool pays its cost, and why a decision that looks ideological is, in the end, accounting.
I’ll use Laravel as PHP, because, to be honest, I don’t remember the last time I left the Laravel ecosystem to use plain PHP.
About Business Logic
The ease of Laravel
Laravel operates under "convention over configuration" and delivers a ready-made ecosystem: Eloquent, queues, authentication, routing. To validate ideas or maintain systems where time-to-market is the main metric, it eliminates initial friction and delivers extremely high-level products.
In Rush CMS, Filament gave me a complete admin panel in days, not weeks. Eloquent + JSONB in Postgres solves dynamic schemas per tenant without DDL or migration hell. This productivity is not a detail — it’s what makes a niche product sustainable as a solo operation.
There’s no free lunch, and the bill comes at scale. The flexibility of PHP and the framework "magic" become expensive in long-term maintenance. To ensure enterprise-grade security and avoid regressions, it’s strictly necessary to impose external quality barriers:
- Clean Code and SOLID rigorously applied;
- Test suites with hundreds of automated tests and severe static analysis in CI/CD (PHPStan at high levels, Spatie Data + Value Objects to simulate the predictability of a typed language);
- Adopt Octane early, otherwise you’ll have to adapt the entire codebase later.
This costs engineering time and a constant battle for performance.
The pragmatism of Go
Go was designed by Google to solve large-scale engineering problems. It has no traditional inheritance, no classes, uses composition via structs and implicit interfaces. In Go there is no magic. Error handling is explicit, which forces the engineer to deal with failure logic at writing time, not in production at 3 AM. You trade critical incidents for boilerplate.
The absence of a dominant monolithic framework forces a more agnostic System Design. Logic tends to stay isolated from the delivery layer (HTTP, gRPC), facilitating DDD. The resulting code is verbose (not to say... painfully ugly!), but extremely readable — any new engineer can understand a Go handler in minutes. The lack of elegance is paid for with raw performance.
In one of the tests I ran, a flow that in Laravel would require job + worker + Redis + Horizon fit into a Go binary solving everything in the same process. The code got boring, the operation became trivial.
System Design
Concurrency vs. synchronous processing: Go wins easily, which is obvious and the bare minimum to expect, given the nature of each ecosystem.
The traditional PHP model spins up a new state for each request. To deal with asynchronous processing, the architecture necessarily involves external components: workers, Redis for state control, brokers like RabbitMQ for queues, Octane under high demand. This multiplies deployment and monitoring complexity with multiple containers, multiple processes, multiple failure surfaces.
In Go, concurrency is native via Goroutines. You can spawn thousands of simultaneous executions with negligible memory cost. For moderately complex background tasks, Go System Design eliminates the need for external workers. A single binary manages HTTP requests and concurrent processing in the same process.
This doesn’t mean "no bottlenecks" — it means bottlenecks move. You stop fighting worker orchestration and start fighting goroutine design, channels, and backpressure. It’s a different problem, but one that fits within a single process.
In server provisioning, Go dominates in efficiency. A Laravel stack requires PHP-FPM + Nginx (and Redis + worker + supervisor if the system is serious), charging its price in RAM. A Go binary is static, occupies tens of MB on disk, consumes a fraction of the memory, and initializes in milliseconds. A modest VPS that today runs a tight Laravel setup comfortably runs an equivalent Go service — with margin to spare.

About Laravel Octane
Octane changes the fundamental premise of PHP. It’s what currently powers the Rush CMS backend and all my clients’ sites. Instead of initializing the framework, loading dependencies, and destroying memory on every request, it bootstraps once and keeps the application alive in RAM. Initialization latency drops to zero. I/O, Service Container resolutions, and configuration loading stop being bottlenecks. The result is a massive increase in RPS on the same hardware, drastically shortening the gap to compiled binaries.
The cost? State management.
Running PHP in memory requires severe engineering maturity. The shared-nothing model forgave poorly written code — everything died at the end of the request. With Octane, memory leaks and state pollution in singletons become real production risks. PHP was not designed to run indefinitely, and the language’s Garbage Collector was never architected for processes that stay alive for weeks. For Octane to be viable, the codebase demands relentless discipline.
"If Octane does what Go does, why the hell are you testing Go?"
The question is obvious — it always comes up when a senior PHP engineer flirts with compiled languages. The answer is simple: Octane is a brilliant adaptation, Go is native. Octane makes PHP behave similarly to Go, but the language wasn’t born for it. You start fighting the ecosystem to ensure a poorly injected singleton doesn’t leak memory between requests and take down the server. Go doesn’t have this problem — it is this problem, solved.
Knowing how to extract the maximum from PHP pays the bills and delivers fantastic products. That’s what I do every day. But having the architectural maturity to know when to isolate bottlenecks using a compiled language is what separates those who deliver features from those who design systems prepared for real scale. Expanding your mindset to new approaches prepares the engineer to solve problems that the base ecosystem simply does not allow.
Costs, Team, and Return
This is where the decision lives — the one nobody wants to make honestly. I will.
- Labor: Laravel is the most paved entry point in web development. In Brazil, you find junior and mid-level Laravel devs everywhere, with predictable salary ranges and an ecosystem of bootcamps feeding the market. Go is another reality: fewer devs, higher salary ranges, slower hiring processes. In a small team or solo operation, this is decisive. Productivity per experienced Go dev tends to be higher in concurrency and systems domains, but you pay for that in salary and scarcity of replacements.
- Infrastructure: This is where Go shines. A Go service consumes a fraction of the resources of an equivalent Laravel app (even with Octane). Translate that to your provider: fewer vCPUs, less RAM, fewer containers, less orchestration. At scale, the AWS/Hetzner/DigitalOcean bill drops by an order of magnitude for high-concurrency workloads. If the product is I/O-bound or has serious spikes, infra payback comes fast.
- Business velocity: Laravel shines here. From zero to a functional MVP with auth, admin panel, CRUD, queues, and billing, Laravel + Filament get there in weeks. In Go you build that by hand — and every piece you build is a piece you must maintain. For early-stage startups, internal company products, freelance work, or idea validation, this speed difference determines whether the project lives or dies.
- Long-term maintenance: Well-written Laravel lasts years. Poorly written Laravel also lasts years — but every change hurts more than the last. Well-written Go lasts decades with virtually no changes, because the language barely evolves (and that’s a feature, not a bug). Poorly written Go is still more predictable than poorly written PHP, because the compiler blocks a lot of nonsense at the gate.
The final calculation depends on which dimension is constrained in your context. In Rush CMS v2, the constraint became infrastructure and runtime predictability.

Concluding the inconclusive
If you made it this far, you already know there is no "definitive choice" — there is the right tool for the moment of the business.
With PHP, you validate a product infinitely faster. Laravel optimizes human productivity by trading hardware for saved dev hours, and in a world where devs are expensive and hardware is cheap, that’s a winning trade early on. But if the operation doesn’t have extremely tight deadlines and needs, from day one, to handle heavy I/O, massive processing, or high concurrency, Go may be the more profitable decision.
Go enforces discipline through design and rewards you with absolute stability. Laravel delivers extreme business velocity, but demands in return quality rigor, third-party tooling, and team control.
Knowing which one to use, and when, is what separates the engineer from the developer.