Shamim Ahammed
Senior Backend Engineer7 min read
Search for this comparison and you will find benchmarks: requests per second, a bar chart, Go winning by an order of magnitude. Those benchmarks are accurate and almost entirely useless, because they measure a workload nobody runs. Your endpoint is not returning "hello". It is doing four queries, checking permissions, applying business rules and serialising a nested response — and at that point the runtime is a small fraction of the time.
Here is how we actually decide, on systems that have to be delivered on a date and maintained for years.
Where the time goes
Take a representative enterprise endpoint: fetch an order with its lines, customer and payment state, apply visibility rules, return JSON.
Most of that request is spent in the database and on the network. The application runtime's share is real but modest. Swapping the runtime makes the small part smaller and leaves the large part untouched — which is why teams that rewrite for speed are so often disappointed. If your p95 is 400ms and 340ms of it is a missing index, Go will get you to roughly 390ms and a new codebase.
Profile before choosing. If the answer is "the database", the language is not your decision — the query plan is.
The runtime starts to matter when it stops being a small fraction: many concurrent connections, high fan-out, CPU-bound transformation, tight and predictable latency budgets. That is a real and identifiable class of work, and it is usually a minority of the endpoints in an enterprise system.
What each is actually good at
Laravel earns its place on delivery speed for business logic. Eloquent, migrations, validation, queues, scheduling, policies, events, a mature admin ecosystem. For a system whose difficulty is rules rather than throughput — approval chains, pricing, permissions, documents, reporting — the framework has already solved everything that is not your problem. A team that knows it will have a working, testable system in a fraction of the time, and in Bangladesh the hiring pool is deep, which is a genuine engineering consideration and not a soft one.
Go earns its place on concurrency and predictability. Goroutines make tens of thousands of in-flight operations ordinary rather than exotic. Memory footprint is small and stable. Latency does not have PHP's per-request bootstrap. It compiles to a single static binary, which makes deployment and container images dramatically simpler. When something must hold many connections open, or must not be slow at p99, Go is the better tool — not because it is faster in a benchmark, but because it is consistent under load.
The execution models differ in a way worth stating plainly, because it explains most of the practical differences:
| Laravel (PHP-FPM) | Go | |
|---|---|---|
| Concurrency | Process per request, bounded by worker pool | Goroutines, cheap and numerous |
| State between requests | None by default — fresh each request | In-process, shared, and yours to manage |
| Memory | Per-worker, scales with pool size | One process, small and stable |
| Long-lived connections | Awkward; needs a separate server | Native |
| Deploy artefact | Runtime + dependencies + code | One static binary |
| Cold start | Bootstrap per request | Effectively none |
| Time to first feature | Very fast | Slower — more is hand-built |
| CPU-bound work | Weak | Strong |
Notice that the first row explains the third, fourth and fifth. Most "Go is better" and "Laravel is faster to build" arguments are downstream of that one difference.
The failure mode of picking only one
All Laravel, at scale. The system works and ships, then one part of it starts hurting: a webhook endpoint hit thousands of times a minute, a nightly reconciliation that takes hours, a live-tracking feed. The instinct is to scale the whole application horizontally — paying for workers for the entire codebase to fix one endpoint. That works, and it is expensive, and it papers over a shape problem.
All Go, for business logic. Also works, and slowly. You will hand-build validation, an ORM layer or the discipline to live without one, a migration story, a job queue, an admin surface. None is hard; together they are months, on a system whose actual difficulty was never the runtime. We have seen teams spend a quarter rebuilding what a framework gives away, on a service handling traffic a single Laravel node would have absorbed.
What we run: a line, not a side
The architecture we keep arriving at, on system after system:
Laravel owns the system of record. Entities, business rules, permissions, workflow, admin, reporting. Everything a human triggers and a person has to reason about. This is the majority of the code and it changes constantly, which is exactly where the framework's leverage is.
Go owns the hot path. Ingestion endpoints that absorb whatever a third party sends at us. Reconciliation and matching workers that are CPU-bound over large sets. Anything holding connections open — live tracking, streams, device telemetry. These are few, well-specified, and change rarely.
They communicate through a queue and a shared database, not through each other. A Go worker that calls a Laravel endpoint to do its job has inherited Laravel's latency and added a network hop. Give each side ownership of specific tables and pass work as messages. Which service owns which entity gets written down before any code is written — the same rule that decides whether two systems are an architecture or two sources of truth.
A concrete shape: a payment webhook arrives at a Go endpoint, which validates the signature, writes a raw event and returns 200 in single-digit milliseconds — because gateways retry aggressively when you are slow, and a retry storm at the wrong moment is its own outage. A worker then matches events against the ledger. Laravel owns the ledger, the merchant-facing screens and the rules about what a matched payment means.
When to move something across the line
Do not rewrite. Move one thing, for a reason you measured.
- Identify the endpoint or job by data, not by feeling. It is slow at p99, it is CPU-bound, or it is the reason you are scaling everything else.
- Confirm the runtime is actually the constraint. Profile it. If it is the database or an upstream API, moving it to Go changes nothing.
- Give the new service its own tables or a clearly owned slice.
- Run both, compare, then cut over. Shadow the traffic if you can.
- Stop there. The strangler pattern only pays off if you strangle the part that hurts and leave the rest alone.
The most common mistake is step five. A successful migration of one hot endpoint creates enthusiasm for migrating everything, and the parts that follow are precisely the parts with no case for moving.
The part nobody puts in the comparison
Who maintains this in three years? A polyglot system is two ecosystems of dependencies, two build pipelines, two sets of idioms, and a smaller pool of engineers comfortable in both. That is a real cost and it accrues quietly.
We think the split is worth it when the hot path is genuinely distinct — few services, stable, clearly bounded. We think it is not worth it when the "Go part" would be a scattering of endpoints across the codebase. At that point you have two systems and one architecture, and the maintenance cost arrives without the benefit.
The short version
Choose Laravel when the difficulty is business rules and the constraint is delivery time. Choose Go when the difficulty is concurrency, CPU or latency predictability. Choose both when you can draw a clean line between the two and staff both sides — and if you cannot draw the line on a whiteboard in five minutes, you are not ready to have two.
And before any of it: profile. Most of the time the honest answer is that neither language is the problem.
If you want a second opinion on where your system's time is actually going, that is a conversation we are happy to have — including when the answer is "leave it alone".
Get posts like this by email
Occasional engineering notes from the team. No marketing, and easy to leave.
Keep reading
Kubernetes for Scaling SaaS: The Three Signals, and the Five Things Teams Get Wrong
Most SaaS products do not need Kubernetes on day one, and adopting it early buys complexity instead of capacity. Here is how to tell when you actually need it, which multi-tenancy model to pick, and the configuration mistakes that cause the outages.
7 min readEngineeringBuilding AI Agents That Survive Production: A Development Guide
An agent is a model in a loop with tools and a stopping condition. Everything hard about shipping one comes from that sentence — non-determinism, tool design, evaluation, cost, and what happens when retrieved text contains instructions. What we have learned building them.
8 min readNext post
Kubernetes for Scaling SaaS: The Three Signals, and the Five Things Teams Get Wrong
Most SaaS products do not need Kubernetes on day one, and adopting it early buys complexity instead of capacity. Here is how to tell when you actually need it, which multi-tenancy model to pick, and the configuration mistakes that cause the outages.