Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

It Works on My Machine: A Field Guide to the Developer's Favourite Sentence

About Post

Every developer has said it. Most of us have said it with a straight face, in a meeting, while someone shared a screen showing the exact opposite.

"It works on my machine."

It's the developer's favourite sentence because it's always true. That's the problem. It's true, and it's useless, because your machine is the one place nobody else will ever run your code. So here's a field guide to the usual suspects behind it, and what actually makes them go away.

A field guide to the usual suspects

When "works for me" meets "broken on the server", the cause is almost always one of these. I've met every one of them more than once.

The Case-Sensitive Ghost

❌ Your code does require 'Helpers/StringHelper.php'. The file on disk is helpers/StringHelper.php.

On Windows and on a default macOS setup, the file system doesn't care about case, so it loads fine. Linux cares a lot. Same for class names that don't match file names exactly: Composer's autoloader on Linux will simply not find App\Services\PdfService in PDFService.php. Bonus round: Git on a case-insensitive machine may not even notice you renamed a file from pdfService.php to PdfService.php.

The Version Shapeshifter

❌ You run PHP 8.3 locally. The server runs 8.1. You used a typed class constant, the server has a different opinion about it.

The same story plays out with Node versions, MySQL vs MariaDB, and that PHP extension (intl, gd, imagick) that you installed two years ago and forgot about. Your machine has it, the fresh server doesn't.

The Phantom .env

❌ You added PAYMENT_WEBHOOK_SECRET to your local .env. You did not add it anywhere else, because .env isn't committed (correctly).

Laravel has a special twist here: once you run php artisan config:cache, the .env file is no longer loaded, and env() calls outside your config files return null. Works locally without the cache, breaks on the server with it. Always read env values in config/*.php and use config() everywhere else.

The Time Traveller

❌ Your laptop is in Doha, the server is in UTC, and a report that shows "today's payments" is suddenly missing the first few hours of the day. Or including the last few of yesterday.

Time zones break things quietly, often only for some records and only at certain hours, which makes them a joy to debug. Store in UTC, convert for display, and set the app timezone explicitly instead of trusting the machine.

The Dependency Drifter

❌ Someone ran composer update instead of composer install on the server, and a package you depend on moved a minor version.

The lock file exists so every machine installs the exact same versions. Commit it, and deploy with install, never update.

The Permission Gremlin

❌ Locally, you're the owner of every file. On the server, the web server user can't write to storage/, so logs, cache and uploads fail. Sometimes it can't write the log that would tell you it can't write.

The Leftover Cache

❌ The bug is fixed. The server is still serving the old routes, old config or old compiled views. Or the browser is serving the old JavaScript bundle. "Works on my machine" sometimes means "my machine cleared its cache and yours didn't".

What actually fixes it

The real cure isn't being more careful. It's making "my machine" and "the server" the same thing, as much as you can.

✅ Use Docker for local development. Same PHP version, same extensions, same database engine, and a Linux file system, so the Case-Sensitive Ghost shows up on your laptop instead of in production. Laravel Sail gives you a ready-made setup if you don't want to write your own.

✅ Pin your versions. Set the PHP version in composer.json ("php": "^8.2") and commit composer.lock and package-lock.json. Add an .nvmrc for Node.

✅ Keep .env.example honest. Every new variable goes into it in the same pull request, with a placeholder value. Code review should reject a new config() key without one.

✅ Run CI on the same stack as production. If your pipeline runs tests on Linux with the production PHP version, half of these suspects are caught before anyone says the sentence.

✅ Make deploys scripted, not remembered. composer install --no-dev, migrations, config:cache, route:cache, queue restart: in a script, in the same order, every time.

✅ Be explicit about time. Set the timezone in config, store timestamps in UTC.

The parity rule: every difference between your machine and production is a bug that hasn't happened yet. You don't need to remove all of them, but you should know which ones you're living with.

A better sentence

"It works on my machine" closes a conversation. The better version opens one: "It works on my machine, so the difference is in the environment. Let's compare versions, config and data."

Same humility, but now it's the first step of debugging instead of the last line of defence.

Which suspect has bitten you hardest? My money's on the Case-Sensitive Ghost, but I'd love to hear the strange ones.

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close