DEV Community

marius-ciclistu
marius-ciclistu

Posted on • Originally published at marius-ciclistu.Medium on

The Silent Killer Slowing Down Your Laravel/Maravelith API (It’s Not Your Database)

After stripping out brick/math dependency from Maravel-Framework 20.0.0-RC10, thinking that it was the source of the slow-down that happened after 20.0.0-RC8, I realized that in that version I bumped nesbot/carbon from 2.x to 3.11.4. I opened an issue on their github page asking if this is normal, only to figure out that they introduced a new updateFallbackLocale logic that increased the memory consumption in Maravelith 20.0.0-RC, from 0.60 to 0.66 MB and that slowed down the RPS by 5.2%.


Maravelith 20.0.0-RC4 + Maravel-Framework 20.0.0-RC7


Maravelith 20.0.0-RC4 + Maravel-Framework 20.0.0-RC8–>10

My solution to this move was https://github.com/macropay-solutions/maravelith/releases/tag/20.0.0-RC5 which removed the autodiscovery of nesbot/carbon and replaced it with a child service provider class that does not call updateLocale();

and updateFallbackLocale(); when the locale and fallback_locale are null or en.

The result:

0.59 MB (drop from 0.60 and 0.66) and +9.8% more RPS for Maravelith 20RC.


Maravelith 20.0.0-RC5 + Maravel-Framework 20.0.0-RC10

I did the same in https://github.com/macropay-solutions/maravelith/releases/tag/20.0.0-RC6 by ignoring auto-discovery for nunomaduro/termwind and registering its service provider only when needed:

  if ($this->app->runningInConsole() && \defined('STDOUT') && \stream_isatty(STDOUT)) {
      $this->app->register(\Termwind\Laravel\TermwindServiceProvider::class);
  }
Enter fullscreen mode Exit fullscreen mode

Resulting in 2.3% RPS increase:


Maravelith 20.0.0-RC6 + Maravel-Framework 20.0.0-RC10

Removing the PreventRequestsDuringMaintenance middleware when not needed increases the RPS by another 5.5% :

protected $middleware = [
    // \App\Http\Middleware\TrustHosts::class,
    \App\Http\Middleware\TrustProxies::class,
    \Illuminate\Http\Middleware\HandleCors::class,
    /** Uncomment PreventRequestsDuringMaintenance only if you need it */
    //\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
    /** Comment out ValidatePostSize if handled by infrastructure
     (e.g., Nginx: client_max_body_size 110M; PHP: post_max_size=105M, upload_max_filesize=100M) */
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    //\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
Enter fullscreen mode Exit fullscreen mode


Maravelith 20.0.0-RC7 + Maravel-Framework 20.0.0-RC10

Notice that the ValidatePostSize middleware could also be removed if the server takes that responsability.

Gemini’s review on this:

When developers complain about a slow PHP framework, the usual suspects are quickly rounded up: heavy ORM queries, complex routing, or lack of caching. But if you spend enough time profiling micro-benchmarks, you’ll discover a silent killer that executes before your controller is even instantiated: The Global Boot Sequence.

Modern frameworks like Laravel (and the monolith template Maravelith) prioritize Developer Experience (DX). Features like Package Auto-Discovery are magical — you install a package, and it just works. But that magic comes with a steep tax.

To understand how easily this hidden bloat creeps in, we don’t need to look at a poorly written third-party package. We can look at one of the most downloaded, high-quality PHP packages in the world: Carbon.

The Carbon Case Study: How One Line Cost 5.2% RPS

While benchmarking Maravelith 20.x against the standard Laravel 10 boilerplate, we noticed a strange regression. Upgrading our dependencies to modern PHP standards (specifically migrating from Carbon 2.x to Carbon 3.11.4) suddenly added 0.06 MB of memory overhead and dropped our Requests Per Second (RPS) by 5.2% on a raw API “Hello World” route.

Why would a date library slow down an API that doesn’t even use dates?

The answer was hiding in Carbon’s Laravel Service Provider. Here is the boot() method in Carbon v2.x:

// Carbon v2.x ServiceProvider
public function boot()
{
    $this->updateLocale();

    // ... event listener logic ...
}
Enter fullscreen mode Exit fullscreen mode

And here is what happened in Carbon v3.11.4:

// Carbon v3.11.4 ServiceProvider
public function boot()
{
    $this->updateLocale();
    $this->updateFallbackLocale(); // <-- The 5.2% RPS Killer
    // ... event listener logic ...
}
Enter fullscreen mode Exit fullscreen mode

The “Helpful” Overhead

That single added line, $this->updateFallbackLocale(), was introduced to solve a front-end UI problem. It ensures that Carbon’s internal date translation dictionary perfectly matches the framework's fallback language configuration.

However, because Laravel auto-discovers this Service Provider, it blindly executes this boot() method on every single request.

If you are building a JSON API that returns standard ISO-8601 timestamps, you do not need localized, human-readable strings like “hace 2 horas”. Yet, because of that one line, your API server is forced to boot up a massive AbstractTranslator engine, load translation arrays into RAM, and merge fallback dictionaries—thousands of times a second.

This single line of “helpful” DX cost the framework 60 KB of memory and an immediate 5.2% drop in throughput. We proved this by neutralizing the auto-discovery, wrapping the boot sequence in an English-only escape hatch, and watching the benchmark instantly recover its lost speed.

Death by a Thousand Cuts

The Carbon example isn’t an isolated incident; it is the exact reason monolithic frameworks feel slow. When you boot a standard Laravel application, you aren’t just paying for Carbon’s translator. You are paying for a dozen other global providers doing things “just in case” the request needs them.

Every package that hooks into the global boot path to synchronize state, register listeners, or boot engines steals a fraction of a millisecond and a few kilobytes of RAM. Multiply that by 15 packages, and suddenly your “lean” API requires 2 MB of memory just to return a 200 OK.

The Takeaway

If you want micro-framework speeds (like Lumen or Maravel), you have to architect like a micro-framework.

Stop letting Vendor packages dictate your framework’s boot sequence. Utilize dont-discover in your composer.json to block heavy Service Providers, and write your own custom, lazy-loaded providers that only boot heavy engines when the route actually demands it.

Your server’s CPU will thank you.

Top comments (0)