php runtime and server environment

Development Parity With Production

Development parity means the local environment is close enough to production that important bugs can be reproduced before deployment. It does not mean local and production are identical. It means the differences are known, deliberate, and unlikely to hide real runtime problems.

For PHP projects, parity starts with PHP version, extensions, php.ini, web server routing, database/cache versions, queues, scheduled tasks, and environment variables.

A small parity check

PHP example
<?php

declare(strict_types=1);

$expected = [
    'PHP_MAJOR_MINOR' => '8.3',
    'memory_limit' => '128M',
];

$actual = [
    'PHP_MAJOR_MINOR' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
    'memory_limit' => ini_get('memory_limit'),
];

foreach ($expected as $name => $requiredValue) {
    $currentValue = $actual[$name];
    $status = $currentValue === $requiredValue ? 'OK' : 'DIFFERS';

    echo "{$name}: {$currentValue} ({$status})" . PHP_EOL;
}

// Prints:
// PHP_MAJOR_MINOR: 8.3 (OK)
// memory_limit: 128M (OK)

This kind of check is not a replacement for documentation, but it makes hidden runtime differences visible.

What should match closely

These differences commonly cause bugs:

  • PHP major/minor version
  • required PHP extensions
  • important php.ini values such as memory, uploads, timezones, and OPcache behaviour
  • database engine and major version
  • Redis or cache availability
  • queue worker behaviour
  • scheduled task behaviour
  • filesystem paths and writable directories
  • web server document root and rewrite rules
  • environment variable names and required values

If production uses PHP-FPM behind Nginx but local uses only php -S, some routing, upload, header, and FastCGI issues may not appear locally.

What can differ deliberately

Some differences are normal:

  • local debug mode can be enabled
  • Xdebug can exist locally but not in production
  • local passwords can be fake
  • local services can use smaller datasets
  • local mail can go to a mail-catcher
  • local logs can be more verbose

The difference should be documented and understood. Undocumented differences are where surprises come from.

Parity review questions

Ask these during onboarding or before blaming application code:

- Which PHP version runs locally, in CI, and in production?
- Are required extensions loaded in all three places?
- Does local use the same public document root shape as production?
- Are queues and scheduled tasks running locally when the feature depends on them?
- Are database/cache versions close enough to catch real behaviour?
- Are environment variables documented with safe local examples?
- Are long-running workers restarted during deploys?

What you should be able to do

After this lesson, you should be able to explain development parity, identify runtime differences that can hide bugs, write a small parity diagnostic, and decide which differences are acceptable local conveniences versus dangerous mismatches.

Practice

Task: Write A Parity Checklist

Create a small PHP diagnostic or checklist that compares local runtime expectations with the current environment.

Requirements

  • Check PHP major/minor version.
  • Check at least three required extensions.
  • Check one important php.ini value.
  • Include queue, database, cache, and web server routing in the written checklist.
  • Add a short note explaining which local differences are acceptable and which are risky.

Check your work

The result should help a developer spot environment drift before assuming a bug is in application code.

Show solution
PHP example
<?php

declare(strict_types=1);

$expectedPhp = '8.3';
$actualPhp = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;

echo 'PHP: ' . $actualPhp . ($actualPhp === $expectedPhp ? ' OK' : ' DIFFERS') . PHP_EOL;

foreach (['pdo', 'mbstring', 'openssl'] as $extension) {
    echo $extension . ': ' . (extension_loaded($extension) ? 'loaded' : 'missing') . PHP_EOL;
}

echo 'memory_limit: ' . ini_get('memory_limit') . PHP_EOL;

// Prints:
// PHP: 8.3 OK
// pdo: loaded
// mbstring: loaded
// openssl: loaded
// memory_limit: 128M

Checklist:

- Confirm local, CI, and production use the expected PHP major/minor version.
- Confirm required extensions are loaded in the runtime that handles the app.
- Confirm important php.ini settings such as memory, uploads, timezone, and OPcache.
- Confirm local database and cache services are close enough to production behaviour.
- Confirm queue workers and scheduled tasks run locally when the feature needs them.
- Confirm the local web server uses the same public document root and routing shape.

Acceptable local differences include Xdebug, fake passwords, local mail capture, and smaller datasets. Risky differences include missing extensions, different PHP versions, different document roots, disabled queues, and services replaced with mocks when the feature depends on real service behaviour.