Skip to content
All writing
3 min read· 24 Aug 2026

composer remove deleted a section of my site, and nothing logged an error

The week I was getting this site ready to launch, the skills section on the home page disappeared. Not an error page. Not a broken layout. The section was just...

By Lakshit Suthar

The week I was getting this site ready to launch, the skills section on the home page disappeared. Not an error page. Not a broken layout. The section was just gone, and the rest of the page rendered around it like it had never existed.

Nothing was in the logs. The deploy was green, the tests were green, and I hadn't touched that code in days. It took me an embarrassing amount of time to connect it to the last thing I'd actually done, because the last thing I'd done felt completely unrelated: I ran composer remove tightenco/ziggy to clear out a package I wasn't using anymore.

What actually happened

This site caches its content in the database for an hour. Skills, projects, settings, all of it. At the time, the cached values were whatever the repositories returned, which meant Eloquent collections. Objects, in other words. And PHP stores objects in a cache by serializing them.

A serialized object graph embeds class names. When PHP unserializes it, it autoloads those classes, and if one of them no longer exists — say, because you removed the package that provided it five minutes ago — PHP doesn't throw anything. It hands you an __PHP_Incomplete_Class object and moves on with its day.

One of those was sitting inside my cached skills payload. The frontend gates that section on skills.length > 0, and defensive code downstream read the mangled data as "no skills". So the section removed itself. Every individual layer behaved reasonably. Together they hid a broken site from me for as long as the cache lived.

Why this scared me once I understood it

It wasn't a one-off. Any composer remove on any deploy could have done this to any cached object, and the failure window is exactly the cache TTL. Health checks pass, because the page still returns 200 — it's just missing content. By the time you try to reproduce it, the cache has expired and everything looks fine again. Real for users, invisible to every tool I had.

The fix

Two layers, because I didn't want to rely on either one alone.

The repositories now cache plain arrays instead of objects. If there's no class name in the cache, there's nothing to rot when a class disappears. This also cut the home page payload from 65 KB to 28 KB, since Eloquent's wrapping never gets serialized in — I would have taken that on its own.

And the cache helper checks what it reads. Anything containing an __PHP_Incomplete_Class gets treated as a miss:

if ($value !== null && ! self::isIntact($value)) {
    Cache::forget($key);
    $value = null; // falls through to recompute
}

Worst case now is one extra query per key after a deploy. Before, it was an hour of quietly missing content.

There's also a test that plants a poisoned cache entry on purpose and asserts the page still renders complete. The fix was maybe ten lines. The test is what stops someone — probably me — from reintroducing the problem in a refactor two years from now.

What I'm keeping from this

Cache data, not objects. A serialized object is a promise that your codebase will still look the same when it's read back, and a cache exists specifically to outlive the moment it was written. If a cached value couldn't survive var_export(), it was carrying more structure than it should have been.

Get new writing by email

Occasional notes on Laravel, architecture and the database work nobody demos. No schedule I cannot keep, and nothing sent that is not worth your time.

One confirmation email first — nothing else arrives until you click it. Unsubscribe from any issue in one click. Your address stays on this server and is never sold, shared or used for anything else.