Lesson 2 / الدرس 2

The things that differ, and the things nobody may see / ما يختلف، وما لا يجوز أن يراه أحد

Two kinds of value cannot live in your code: what changes between environments, and what must never be published. They are handled the same way, and the day you get it wrong is the day a password is in a public repository forever.

نوعان من القيم لا يمكن أن يعيشا في كودك: ما يتغير بين البيئات، وما لا يجوز نشره أبدًا. ويُعالجان بالطريقة نفسها، ويوم تخطئ فيهما هو يوم تصير كلمة سر في مستودع عام إلى الأبد.

A database password, an API key, the address of a mail server, whether debugging is on: none of these belong in a file you commit. The code is the same everywhere; the configuration is what makes one copy of it your machine and another copy the live site. Keep them separate and deploying becomes copying code, nothing more.

A file, ignored by git, with an example beside it

config.php              <- real values. In .gitignore. Never committed.
config.example.php      <- the same keys, with placeholder values.
                           COMMITTED, so a new developer knows what
                           the application needs without asking.

# .gitignore
/config.php
/storage/logs/
/storage/uploads/
The example file is the part people skip, and it is what stops configuration becoming folklore. Without it, the list of required settings lives only in the head of whoever set the server up, and the next person discovers each one by hitting the error it causes.
<?php
// config.example.php — committed, and readable as documentation.
return [
    'debug' => true,

    'db' => [
        'host'     => '127.0.0.1',
        'name'     => 'training',
        'user'     => 'CHANGE_ME',
        'password' => 'CHANGE_ME',
    ],
];

// And in the application: fail loudly on a missing setting rather than
// defaulting to something that half works.
$config = require __DIR__ . '/../config.php';

foreach (['db.host', 'db.name', 'db.user', 'db.password'] as $key) {
    if (config($key) === null) {
        throw new RuntimeException("config: {$key} is not set");
    }
}
The check at the bottom is worth the six lines. A missing setting that defaults to something harmless produces a site that appears to work and quietly does the wrong thing — mail that goes nowhere, a cache that never caches, debug left on. A crash on the first request is a far cheaper failure.

What to do when a secret gets out

  1. Change the secret first. Before cleaning anything up, before telling anyone, before working out how it happened. A rotated key makes the leak harmless; everything else is tidying.
  2. Assume it was read. Public repositories are scanned continuously by automated tools; a key pushed and deleted four minutes later has been collected. Plan for used, not for probably-fine.
  3. Tell whoever needs to know, the same day. A leaked credential is not an embarrassment to manage quietly — it is an incident, and the cost of naming it early is always smaller than the cost of it being found later.
  4. Then fix the history, knowing it changes nothing about the exposure. Rewriting git history breaks everyone's clone and does not reach forks, caches or anyone who already pulled.

Check yourself / اختبر نفسك

1. What is config.example.php for?

2. A key was pushed to a public repository and deleted four minutes later. What now?

3. Why crash on a missing configuration value rather than defaulting?