0

dev dependencies and the --no-dev flag in production

Beginner5 min read·php-12-005

Concept

composer.lock is a JSON file that records the exact version, source URL, and hash of every installed package (direct and transitive). When you run composer install with an existing lock file, Composer installs precisely those versions — no resolution needed, perfectly reproducible across environments.

composer install vs composer update:

  • composer install: Uses composer.lock. Installs exact versions. Use in CI/CD, staging, production.
  • composer update: Ignores composer.lock. Re-resolves from composer.json constraints. Updates lock file. Use locally when you want to upgrade dependencies.

Why commit composer.lock: For applications (not libraries), always commit composer.lock. This ensures dev, CI, staging, and production all run identical code. Prevents "works on my machine" caused by a dependency updating between your test run and your deploy.

Libraries and lock files: Libraries (packages meant to be included by other projects) should NOT commit composer.lock. The library's users will have their own lock files that include the library as a transitive dependency. Committing the library's lock would mislead contributors.

Security auditing: composer audit checks all installed packages against the PHP Security Advisories database for known vulnerabilities. Run in CI: if a vulnerability is found, the build fails.

Code Example

json
// composer.lock (excerpt — never edit manually)
{
    "_readme": [
        "This file is @generated automatically"
    ],
    "content-hash": "a1b2c3d4...",
    "packages": [
        {
            "name": "guzzlehttp/guzzle",
            "version": "7.8.1",
            "source": {
                "type": "git",
                "url": "https://github.com/guzzle/guzzle.git",
                "reference": "41042bc7ab002487b876a0683fc8dce04ddce104"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/...",
                "reference": "41042bc7ab...",
                "shasum": ""
            },
            "require": {
                "php": "^7.2.5 || ^8.0",
                "guzzlehttp/promises": "^1.5 || ^2.0",
                "psr/http-message": "^1.1 || ^2.0"
            }
        }
    ],
    "packages-dev": [...],
    "platform": { "php": "^8.2" }
}
bash
# CI/CD install — uses lock, skips dev, optimizes autoloader
composer install --no-dev --optimize-autoloader --no-interaction --prefer-dist

# Check for security vulnerabilities
composer audit

# Validate that composer.json and lock are in sync
composer validate
# "The lock file is not up to date with the latest changes in composer.json"
# → run composer update or composer install to sync

# Show why a package is installed (dependency chain)
composer why guzzlehttp/guzzle

# Show what requires a package
composer why-not php:8.4  # check if anything is blocking an upgrade