Vorbereitung auf PHP Interviews

PHP Interviewfragen

15 ausgewählte PHP Backend-Interviewfragen, gruppiert nach Erfahrungsstufe. Nutzen Sie diese, um Grundlagen, praktische Kompromisse und Überlegungen für die Produktionsumgebung auf Senior-Niveau zu überprüfen.

PHP KI-Interview startenKeine Kreditkarte erforderlich. 1 kostenlose Sitzung verfügbar.
Technisches Interview-Training auf EnglischEin Modus, in dem Nicht-Muttersprachler technische Interviews üben können.

Junior Fragen

1Wie unterscheidet das Typsystem von PHP schwache und strikte Typisierung, und was ändert sich durch declare(strict_types=1)?

PHP erlaubt normalerweise kompatible skalare Typumwandlungen bei deklarierten Parameter- und Rückgabetypen, etwa eine numerische Zeichenkette für einen int-Parameter. declare(strict_types=1) aktiviert strikte skalare Prüfungen: Ein inkompatibles Argument oder ein inkompatibler Rückgabewert löst TypeError aus, wobei int weiterhin für float akzeptiert wird. Für Argumente benutzerdefinierter Funktionen bestimmt die aufrufende Datei die Striktheit; für Rückgabeprüfungen ist die Datei maßgeblich, die die Funktion definiert. Aufrufe aus internen PHP-Funktionen haben eigene Besonderheiten und unterliegen nicht einfach der strict_types-Einstellung des äußeren Aufrufers. Die Direktive muss am Anfang ihrer PHP-Datei stehen. Sie macht PHP weder statisch typisiert noch deaktiviert sie sprachweit Typumwandlungen durch Operatoren.

// THREE SEPARATE FILES: save each PHP block under its indicated filename.
// File: lib.php
<?php
function takesInt(int $x): int { return $x; }
?>

// File: weak.php
<?php
require __DIR__ . '/lib.php';
echo takesInt('123'); // 123: coercive caller
?>

// File: strict.php
<?php
declare(strict_types=1);
require __DIR__ . '/lib.php';
try {
    echo takesInt('123');
} catch (TypeError $e) {
    echo 'TypeError';
}
?>
Diese Frage mit einem KI-Coach beantworten

2Erkläre den Unterschied zwischen == und === in PHP, einschließlich Fallstricken bei numerischen Zeichenketten, Arrays, Objekten und in_array().

PHP verwendet bei == einen losen Vergleich, gegebenenfalls mit Typumwandlung; === verlangt übereinstimmende Typen und Werte ohne skalare Typanpassung. Numerische Zeichenketten können numerisch verglichen werden. Verwende daher keinen losen Vergleich, wenn eine exakte Übereinstimmung unter Berücksichtigung des Typs erforderlich ist. Arrays sind mit == gleich, wenn sie nach den Vergleichsregeln dieselben Schlüssel-Wert-Paare enthalten, unabhängig von deren Reihenfolge. Array-=== verlangt zusätzlich dieselbe Reihenfolge und übereinstimmende Schlüssel- und Werttypen. Objekt-== vergleicht Eigenschaften von Objekten derselben Klasse; Objekt-=== verlangt dieselbe Instanz. in_array vergleicht standardmäßig lose; übergib true als drittes Argument für strikte Prüfungen. Das Verhalten loser Vergleiche zwischen Zahlen und Zeichenketten hat sich mit PHP 8 geändert. Verlasse dich daher nicht ohne Versionsangabe auf historische Beispiele für Typumwandlungen.

<?php
var_dump(123 == '123');      // loose: numeric string is converted
var_dump(123 === '123');     // strict: int !== string

$values = [0, 1, 2];
var_dump(in_array('0', $values));       // loose
var_dump(in_array('0', $values, true)); // strict
Diese Frage mit einem KI-Coach beantworten

3Wie unterscheiden sich mixed, void und never als PHP-Rückgabetypen, und welche Garantien bieten sie?

mixed erlaubt jeden Rückgabewert, einschließlich null. void erklärt, dass eine Funktion keinen bedeutungsvollen Wert zurückgibt: Sie darf ihr Ende erreichen oder return; verwenden, aber keinen Ausdruck zurückgeben. Ein normaler Aufruf einer void-Funktion ergibt null. never ist seit PHP 8.1 verfügbar und bedeutet, dass die Funktion niemals regulär zum Aufrufer zurückkehrt. Sie kann eine Ausnahme auslösen, die Ausführung mit exit beenden oder in einer Endlosschleife bleiben; sie muss den Prozess nicht irgendwann beenden. Eine never-Funktion darf keine return-Anweisung enthalten. Der entscheidende Unterschied: Eine void-Funktion kann normal enden, sodass ihr Aufrufer fortfährt; eine never-Funktion hat keine normale Fortsetzung.

<?php
function parse(string $raw): mixed {
    return json_decode($raw, true);
}

function logMessage(string $message): void {
    error_log($message);
    return; // allowed
}

function fail(string $message): never {
    throw new RuntimeException($message);
}

logMessage('starting');
fail('stop');
echo 'unreachable';
Diese Frage mit einem KI-Coach beantworten

4Was bedeutet iterable in PHP, und wie hängt es mit array, Traversable, Iterator und Generator zusammen?

iterable bedeutet array|Traversable. Generator implementiert Iterator, das Traversable erweitert, und erfüllt daher iterable. Benutzerdefinierte Klassen stellen Traversable-Verhalten über Iterator oder IteratorAggregate bereit, statt Traversable direkt in einer konkreten Klasse zu implementieren. Iterator bietet Methoden zur Steuerung der Iteration; IteratorAggregate liefert einen Iterator. Eine Generatorfunktion mit yield gibt einen Generator zurück. Wichtig ist die Unterscheidung: foreach kann auch sichtbare Eigenschaften eines gewöhnlichen Objekts durchlaufen, das Traversable nicht implementiert. Dadurch erfüllt dieses Objekt jedoch keine iterable-Parameterdeklaration.

function printItems(iterable $items): void {
    foreach ($items as $item) {
        echo $item, PHP_EOL;
    }
}

printItems(['a', 'b']);

function letters(): Generator {
    yield 'x';
    yield 'y';
}

printItems(letters());
Diese Frage mit einem KI-Coach beantworten

5Welche Sichtbarkeitsregeln gelten für public, protected und private in PHP, und wie wirken sie mit Vererbung und Traits zusammen?

public-Mitglieder sind überall zugänglich, wo auf das Objekt oder die Klasse zugegriffen werden kann. protected-Mitglieder sind innerhalb der deklarierenden Klasse und der zugehörigen Eltern-Kind-Klassenhierarchie zugänglich, nicht jedoch für beliebigen externen Code. private-Mitglieder sind innerhalb ihrer deklarierenden Klasse zugänglich; eine Unterklasse kann ein privates Mitglied der Elternklasse nicht als geerbte Schnittstelle verwenden. Eine gewöhnliche überschreibende Methode darf die geerbte Sichtbarkeit nicht einschränken. Konstruktoren haben jedoch besondere Vererbungsregeln und dürfen nicht als uneingeschränktes Beispiel für diese Regel gelten. Trait-Mitglieder werden in die verwendende Klasse eingebunden und folgen deren Sichtbarkeitsregeln. Die Anpassung mit as kann einen Alias erzeugen, die Sichtbarkeit einer Trait-Methode ändern oder beides. Das Hinzufügen eines Alias entfernt den ursprünglichen Namen nicht.

<?php
class Base {
    public function a() {}
    protected function b() {}
    private function c() {}
}

trait Logs {
    public function log(string $message): void {}
}

class Child extends Base {
    use Logs { log as protected writeLog; }

    public function test(): void {
        $this->a();              // OK
        $this->b();              // OK
        // $this->c();           // Not accessible: private to Base
        $this->writeLog('ok');   // OK inside class, protected alias
    }
}

$child = new Child();
$child->a();
// $child->b();           // Not accessible from outside
// $child->writeLog('x'); // Not accessible from outside
Diese Frage mit einem KI-Coach beantworten

6Wie funktionieren Traits in PHP, und wie werden Konflikte gelöst, wenn mehrere Traits dieselbe Methode definieren?

Traits ermöglichen horizontale Codewiederverwendung: Eine Klasse importiert Trait-Mitglieder mit use; ein Trait selbst lässt sich nicht instanziieren. Eine in der Klasse deklarierte Methode hat Vorrang vor Trait-Methoden, die wiederum Vorrang vor geerbten Methoden der Elternklasse haben. Liefern mehrere Traits dieselbe Methode und wird der Konflikt nicht bereits durch eine Klassenmethode aufgelöst, wählt insteadof die Implementierung aus. Mit as erhält eine Trait-Methode einen zusätzlichen Namen und optional eine andere Sichtbarkeit. Ein Alias benennt die ursprüngliche Methode weder um noch entfernt er sie. Namenskonflikte aufzulösen und die entstehende öffentliche Schnittstelle verständlich zu halten, sind getrennte Aufgaben.

<?php
trait JsonLogger {
    public function log(string $message): void {
        echo "json:$message\n";
    }
}

trait FileLogger {
    public function log(string $message): void {
        echo "file:$message\n";
    }
}

class Service {
    use JsonLogger, FileLogger {
        JsonLogger::log insteadof FileLogger;
        FileLogger::log as logToFile;
        JsonLogger::log as protected logJson;
    }
}

$service = new Service();
$service->log('hello');
$service->logToFile('hello');
// $service->logJson('hello'); // not accessible from outside; protected alias
Diese Frage mit einem KI-Coach beantworten

7Was sind PHP Enums, einschließlich reiner (pure) und Backed Enums, und wie verbessern sie die Domänenmodellierung?

PHP Enums definieren einen geschlossenen Satz benannter Fälle für einen Typ, wie z.B. Bestellstatus oder Benutzerrollen. Reine Enums (Pure Enums) haben nur benannte Fälle; Backed Enums weisen jedem Fall einen eindeutigen String- oder Int-Wert zu, was für die Speicherung, APIs (Application Programming Interfaces) oder Interoperabilität nützlich ist. Enums verbessern die Domänenmodellierung, indem sie anfällige rohe Strings, Ganzzahlen oder Konstanten durch typensichere, selbstdokumentierende Werte ersetzen. PHP Enums können auch Methoden haben und Schnittstellen implementieren; alle Enums unterstützen das Auflisten ihrer Fälle, und Backed Enums unterstützen die Konvertierung zu und von ihren skalaren Werten.

enum OrderState
{
    case Draft;
    case Paid;
    case Shipped;
}

enum PaymentStatus: string
{
    case Pending = 'pending';
    case Complete = 'complete';
    case Failed = 'failed';
}

$status = PaymentStatus::tryFrom('complete');
var_dump($status === PaymentStatus::Complete);
Diese Frage mit einem KI-Coach beantworten

Middle Fragen

8Wie drücken skalare, nullable, Union-, Intersection- und DNF-Typen Schnittstellenverträge in PHP aus, und wann sollte welcher Typ verwendet werden?

PHP-Deklarationen beschreiben akzeptierte und zurückgegebene Werte. Skalare Deklarationen wie int, float, string und bool wählen eine Wertkategorie; ob kompatible skalare Typumwandlungen erlaubt sind, hängt auch von strict_types und dem Kontext ab. ?T oder T|null lässt null zu. Union-Typen, verfügbar seit PHP 8.0, erlauben Alternativen wie int|string. Reine Intersection-Typen, verfügbar seit PHP 8.1, verlangen, dass ein Objekt alle aufgeführten Klassen- oder Interfacetypen erfüllt, etwa Cacheable&JsonSerializable; int&string ist keine gültige Intersection. DNF-Typen (disjunktive Normalform), verfügbar seit PHP 8.2, verbinden Alternativen mit eingeklammerten Intersections, etwa (Cacheable&JsonSerializable)|string. Wähle den engsten verständlichen Vertrag, der gültige Eingaben beschreibt, statt unnötige Alternativen hinzuzufügen.

<?php
interface Cacheable {}

class Report implements Cacheable, JsonSerializable {
    public function jsonSerialize(): mixed { return []; }
}

function findName(int $id): ?string {
    return $id > 0 ? 'Alice' : null;
}

function normalize(int|string $id): string {
    return (string) $id;
}

function store(Cacheable&JsonSerializable $item): void {
    // must satisfy both interfaces
}

function handle((Cacheable&JsonSerializable)|string $input): void {
    // either an object satisfying both contracts, or a string
}
Diese Frage mit einem KI-Coach beantworten

9Was unterscheidet callable, Closure, First-Class-Callables, aufrufbare Objekte und Callable-Arrays in PHP?

callable beschreibt aufrufbare Werte, darunter Zeichenketten mit Funktionsnamen, Closure-Objekte, Verweise auf zugängliche Methoden und Objekte mit __invoke. Closure ist ein konkretes aufrufbares Objekt: Es kann aus einer anonymen Funktion oder aus einer benannten Funktion beziehungsweise Methode entstehen, die in eine Closure umgewandelt wird. Die First-Class-Callable-Syntax wie strlen(...) oder $object->method(...) erzeugt eine Closure, ohne das Ziel aufzurufen. Das Callable-Array [$object, 'method'] verweist auf eine zugängliche Instanzmethode; [ClassName::class, 'method'] setzt in modernem PHP eine passende zugängliche statische Methode voraus. is_callable prüft die Aufrufbarkeit im jeweiligen Gültigkeitsbereich. callable ist ein weiter Vertrag, Closure eine bestimmte Darstellungsform.

class Greeter {
    public function hello(string $name): string { return "Hello $name"; }
    public function __invoke(string $name): string { return "Hi $name"; }
}

$g = new Greeter();

$closure = fn(string $name) => "Hey $name";
$arrayCallable = [$g, 'hello'];
$firstClass = $g->hello(...);
$invokable = $g;

foreach ([$closure, $arrayCallable, $firstClass, $invokable] as $cb) {
    echo $cb('Sam'), PHP_EOL;
}
Diese Frage mit einem KI-Coach beantworten

10Wie geht PHP mit Integer-Überlauf, Gleitkomma-Genauigkeit und exakten Dezimalberechnungen um?

PHP-Integer sind plattformabhängig dimensioniert, mit Grenzen, die durch Konstanten wie `PHP_INT_MAX` angezeigt werden. PHP behält native Integer-Ergebnisse nach einem Überlauf nicht automatisch als Integer beliebiger Genauigkeit bei; überlaufende Integer-Arithmetik kann zu einem `float` werden oder anderweitig die exakte Integer-Semantik verlieren. PHP-Floats sind binäre Gleitkommawerte, daher sind viele Dezimalbrüche, wie `0.1`, nur annähernd genau. Für exakte Dezimal- oder Geldberechnungen sollten Floats vermieden und stattdessen ein exakter Ansatz wie Integer-Untereinheiten (minor units) verwendet werden, wo angebracht, oder Tools für Festkomma-/beliebige Genauigkeit bei Dezimalzahlen wie BCMath. GMP ist nützlich für Integer-Arithmetik beliebiger Genauigkeit.

var_dump(0.1 + 0.2 === 0.3);
printf('%.17f\n', 0.1 + 0.2);
Diese Frage mit einem KI-Coach beantworten

11Erklären Sie PHP-Referenzen (`&`), Copy-on-Write und wie Referenzen unerwartete Nebenwirkungen erzeugen können.

PHP-Variablen werden intern durch zvals repräsentiert. Für Werte wie Arrays und Strings verwendet PHP Copy-on-Write: Die Zuweisung `$b = $a` teilt normalerweise den gleichen zugrunde liegenden Wert, bis eine Variable geändert wird; zu diesem Zeitpunkt trennt/kopiert PHP sie. Eine mit `&` erstellte Referenz macht Variablen zu Aliasen desselben Containers, sodass eine Änderung an einer Variable die andere ändert und das Trennverhalten umgehen kann, das man von normalen Zuweisungen erwartet. Die Übergabe per Referenz ermöglicht es einer Funktion, die Variable des Aufrufers zu ändern. Referenzen können überraschende Nebenwirkungen verursachen, insbesondere bei `foreach ($array as &$value)`: Die Schleifenvariable bleibt nach der Schleife eine Referenz auf das letzte Element, sodass eine spätere Wiederverwendung dieses Elements versehentlich überschreiben kann, es sei denn, Sie führen ein `unset($value)` durch.

$a = [1, 2];
$b = $a;      // shared until modification
$b[0] = 99;   // separates; $a is unchanged
var_dump($a, $b);

$c =& $a;     // alias/reference
$c[0] = 42;   // modifies $a too
var_dump($a, $c);
Diese Frage mit einem KI-Coach beantworten

12Wie funktionieren Objektreferenzen, Zuweisung, `clone`, `__clone`, flache Kopie (shallow copy) und tiefe Kopie (deep copy) in PHP?

In PHP speichert eine Objektvariable eine Referenz auf ein Objekt, nicht eine vollständige Kopie des Objekts. Die Zuweisung `$b = $a` kopiert die Referenz, sodass beide Variablen auf dasselbe Objekt verweisen. Änderungen am Objekt über eine der Variablen sind auch über die andere sichtbar, ohne die Verwendung von `&`. `clone $a` erstellt eine neue Objektinstanz mit einer flachen Kopie der Eigenschaften des ursprünglichen Objekts. Wenn eine Eigenschaft selbst ein Objekt ist, verweist die Eigenschaft des geklonten Objekts immer noch auf dasselbe verschachtelte Objekt, es sei denn, Sie klonen es explizit. Die Methode `__clone()` wird nach der flachen Kopie auf dem neuen Objekt aufgerufen und ist der Ort, um IDs zurückzusetzen, Ressourcen zu trennen oder eine tiefe Kopie von verschachtelten Objekten durchzuführen. Eine tiefe Kopie bedeutet das rekursive Kopieren der verschachtelten, veränderlichen Objekte, die nicht geteilt werden sollen.

class Address {
    public function __construct(public string $city) {}
}

class User {
    public function __construct(public string $name, public Address $address) {}

    public function __clone() {
        $this->address = clone $this->address; // make nested object independent
    }
}

$u1 = new User('Ann', new Address('Paris'));
$u2 = $u1;          // same object handle
$u2->name = 'Beth';
echo $u1->name, PHP_EOL;

$u3 = clone $u1;    // new User object; __clone deep-clones address
$u3->address->city = 'Rome';
echo $u1->address->city, PHP_EOL;
echo $u3->address->city, PHP_EOL;
Diese Frage mit einem KI-Coach beantworten

Senior Fragen

13Wie würden Sie einen Mangel an Datenbankverbindungen beheben und das Verbindungsmanagement für PHP-FPM (PHP FastCGI Process Manager) und langlebige Worker gestalten?

Um einen Mangel an Datenbankverbindungen zu beheben, würde ich Datenbankverbindungslimits und aktuelle Sessions mit der gesamten PHP-Parallelität vergleichen: Jeder aktive PHP-FPM-Child-Prozess kann seine eigene Datenbankverbindung halten, und mehrere Anwendungspools, Hosts, Cron-Jobs, Warteschlangen-Worker, Migrationen und Deployments vervielfachen den Bedarf. Ich würde Datenbank-Session-Ansichten, Verbindungsstatus, Sessions mit Leerlauf in Transaktionen, langsame Abfragen, Sperrwartezeiten, Fehlerprotokolle, Pool-Metriken und aktuelle Verkehrs- oder Worker-Anzahländerungen überprüfen. Für PHP-FPM sollte man `max_children` und die Parallelität pro Host innerhalb des Datenbankbudgets halten, kurze, an den Request gebundene Transaktionen/Verbindungen verwenden, umgehend committen oder zurückrollen und das Halten von Verbindungen während langsamer externer Aufrufe vermeiden. Persistente PDO (PHP Data Objects)-Verbindungen können den Verbindungs-Overhead reduzieren, aber sie können viele inaktive Verbindungen pro FPM-Prozess belegen und den Session-Status beibehalten, daher ist Vorsicht geboten. Ein Pooler oder Proxy wie PgBouncer kann viele PHP-Clients auf weniger Datenbankserver-Verbindungen multiplexen, wenn die Anwendung kompatibel ist. Langlebige Worker benötigen eine explizite Handhabung des Verbindungslebenszyklus: Gesundheitsprüfungen, Neuverbindung nach Inaktivitäts-Timeouts/Neustarts/Failover/Forks, Zurücksetzen des Session-Status und Wiederholung nur sicherer oder idempotenter Arbeit.

Connection-budget example:
- 6 application servers
- 2 PHP-FPM pools on EACH server
- pm.max_children = 40 for EACH pool
- Assume at most 1 database connection per child

Potential request connections = 6 * 2 * 40 = 480.
If 40 were the TOTAL children per server across both pools, it would be 240.
Budget separately for queue workers, cron, deployments and administrators.
A limit of 300 database connections cannot accommodate 480 simultaneous connections.
Diese Frage mit einem KI-Coach beantworten

14Wie würdest du Lesereplikate, die Trennung von Lese- und Schreibzugriffen, Replikationsverzögerung und den Ausfall der primären Datenbank in PHP behandeln?

Trenne Verbindungen zur primären Datenbank und zu Replikaten nach der Bedeutung der Operation, nicht bloß danach, ob SQL mit SELECT beginnt. Sperrende Lesezugriffe, Schreibzugriffe und Transaktionen, die die Konsistenz der primären Datenbank benötigen, müssen diese verwenden. Rein lesende Transaktionen dürfen ein geeignetes Replikat nutzen, wenn ihre Konsistenzanforderungen es erlauben. Asynchrone Replikation kann verzögert sein. Laravels sticky-Einstellung leitet nach einem Schreibzugriff spätere Lesezugriffe derselben Anfrage zur Schreibverbindung. Eine feste Bindungsdauer über mehrere Anfragen hinweg mildert das Problem nur und garantiert nicht uneingeschränkt, eigene Schreibzugriffe anschließend lesen zu können. Für diese Garantie lies von der primären Datenbank oder warte, bis ein Replikat die erforderliche Schreibposition übernommen hat, mit Zeitlimit und Rückfall auf die primäre Datenbank. Proxys unterscheiden sich: RDS Proxy verwendet getrennte Lese- und Schreibendpunkte und teilt nicht allgemein automatisch jede SQL-Anweisung in Lese- oder Schreibverkehr auf. Verwirf beim Failover veraltete Verbindungen und verbinde dich mit begrenzten, zunehmenden Wartezeiten neu. Geht eine Commit-Antwort verloren, ist das Ergebnis ungewiss: Wiederhole nichtidempotente Schreibzugriffe nicht blind; verwende Idempotenzschlüssel oder kläre zuerst das Ergebnis.

<?php
// config/database.php
return [
    'mysql' => [
        'read' => [
            'host' => [env('DB_REPLICA_HOST_1'), env('DB_REPLICA_HOST_2')],
        ],
        'write' => [
            'host' => [env('DB_PRIMARY_HOST')],
        ],
        'sticky'    => true,
        'driver'    => 'mysql',
        'database'  => env('DB_DATABASE'),
        'username'  => env('DB_USERNAME'),
        'password'  => env('DB_PASSWORD'),
        'charset'   => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
    ],
];
Diese Frage mit einem KI-Coach beantworten

15Wie lassen sich Datenbankmigrationen und Schemaänderungen in PHP-Anwendungen sicher ohne oder mit minimaler Ausfallzeit ausrollen?

Verwende Erweiterungs- und Rückbauänderungen, die während eines schrittweisen Rollouts sowohl die alte als auch die neue Anwendungsversion vertragen. Füge zuerst kompatible Strukturen hinzu und rolle dann Code aus, der beide Darstellungen nutzen kann. Ist doppeltes Schreiben nötig, halte es konsistent und plane wiederaufnehmbare, gedrosselte Nachbefüllungen, die neuere Schreibzugriffe nicht überschreiben. Prüfe die kopierten Daten und stelle Lesezugriffe um, bevor du alte Codepfade entfernst. Online-DDL (Datendefinitionssprache) reduziert bestimmte Blockierungsrisiken, ist aber nicht grundsätzlich sperrfrei: Prüfe datenbank- und versionsspezifische Sperren, Transaktionseinschränkungen, Ressourcenverbrauch und Auswirkungen auf die Replikation. PostgreSQLs CREATE INDEX CONCURRENTLY hat beispielsweise Einschränkungen und kann nach einem Fehler einen ungültigen Index hinterlassen. Entferne alte Spalten erst, wenn keine alten Anwendungsinstanzen mehr laufen und das erforderliche Rückrollfenster abgelaufen ist. Nach destruktiven Rückbauänderungen ist die Rückkehr zu Code, der gelöschte Daten benötigt, nicht mehr automatisch sicher.

<?php
// Step 1: Migration (Expand) -> Add new column as nullable
Schema::table('users', function (Blueprint $table) {
    $table->string('full_name')->nullable()->after('name');
});

// Step 2: Code deployment -> Write to both, read with fallback
class User extends Model {
    public function setNameAttribute($value) {
        $this->attributes['name'] = $value;
        $this->attributes['full_name'] = $value; // Dual write
    }
}

// Step 3: Background worker backfills existing rows in small batches
// Step 4: Code deployment -> Read/write exclusively from 'full_name'
// Step 5: Migration (Contract) -> Drop legacy 'name' column
Diese Frage mit einem KI-Coach beantworten