Die Produktions-Lücke nach dem Prototyp
Typische Vibe-Codebases leiden unter flachen Strukturen: alles in wenigen großen Dateien, keine klaren Domain-Grenzen, Tests als Nachgedanke. Der Agent hat funktionierenden Code produziert – aber keinen wartbaren. Der erste Production-Bug kommt nicht vom Feature, sondern von fehlender Fehlerbehandlung, SQL-Injection-Risiken oder Race Conditions.
Produktionsreife bedeutet nicht „kein Agent mehr“. Es bedeutet: Der Agent arbeitet innerhalb einer Architektur-Hülle. Jeder Run muss Static Analysis, Unit-Tests und Diff-Limits passieren. Agenticode etabliert diese Hülle schrittweise, oft beginnend mit einem „Production Readiness Gate“ in der CI.
Holistisch betrachten wir vier Säulen: Struktur (Module, Namespaces, Dependency Injection), Verifikation (Tests, PHPStan, Security-Scans), Betrieb (Logging, Health Checks, Feature Flags) und Governance (Code Review, Policy-Updates). Fehlt eine Säule, kippt das System unter Last.
<?php
declare(strict_types=1);
namespace App\ProductionGate;
final class ProductionReadinessChecker
{
public function execute(string $projectRoot): array
{
$violations = [];
if (!$this->hasPhpStanConfig($projectRoot)) {
$violations[] = 'phpstan.neon fehlt';
}
if ($this->countUntestedServices($projectRoot) > 0) {
$violations[] = 'Services ohne zugehörige Tests gefunden';
}
if ($this->hasRawSqlInControllers($projectRoot)) {
$violations[] = 'SQL in Controller-Schicht – Domain-Layer erforderlich';
}
if ($this->maxFileLineCount($projectRoot) > 400) {
$violations[] = 'Dateien > 400 Zeilen – Refactoring-Gate';
}
return $violations;
}
private function hasPhpStanConfig(string $root): bool
{
return is_file($root . '/phpstan.neon') || is_file($root . '/phpstan.neon.dist');
}
// ... weitere Checks
}
Architektur-Gates für Agent-Output
Ohne Gates schreibt ein Agent dort hin, wo der Prompt am wenigsten Widerstand bietet – oft direkt in Controller oder Twig-Templates. Wir definieren Layer-Regeln: Domain-Logik nur in Services, Persistence nur in Repositories, keine Business-Logik in Templates. Diese Regeln werden als Cursor Rules und PHPStan-Custom-Rules codiert.
Ein weiteres Gate ist die Diff-Größe. Große Diffs sind schwer reviewbar und verstecken Fehler. Agenticode setzt typischerweise ein Limit von 300–500 Zeilen pro Agent-Run. Überschreitungen triggern eine Aufteilung in Sub-Tasks – ein Pattern, das Multi-Agent-Orchestrierung vorbereitet.
Security-Gates sind nicht verhandelbar: Keine Secrets im Code, prepared Statements für SQL, Input-Validierung an Systemgrenzen. Der Agent darf Features bauen – aber nicht die Sicherheitsarchitektur umgehen.
#!/usr/bin/env bash
set -euo pipefail
# Agent-Run Post-Check – Teil jeder Agenticode CI-Pipeline
MAX_DIFF_LINES=400
DIFF_LINES=$(git diff --cached --numstat | awk '{s+=$1+$2} END {print s+0}')
if [ "$DIFF_LINES" -gt "$MAX_DIFF_LINES" ]; then
echo "FAIL: Diff hat $DIFF_LINES Zeilen (Limit: $MAX_DIFF_LINES)"
echo "Bitte Run in kleinere Sub-Tasks splitten."
exit 1
fi
composer phpstan -- --error-format=raw
vendor/bin/phpunit --stop-on-failure
composer audit
echo "OK: Agent-Output produktionsreif für Review."
Der Weg von Agenticode: Strangler statt Big Bang
Wir reißen Vibe-Codebases nicht ab. Der Strangler-Fig-Ansatz extrahiert Schritt für Schritt Domain-Module aus dem Monolithen, während neue Features bereits in der Zielarchitektur landen. Parallel bauen wir die Testpyramide von unten: Unit-Tests für extrahierte Services, dann Integrationstests, zuletzt wenige E2E-Tests.
Jeder Strangler-Schritt ist ein abgeschlossener Agent-Run mit klarem Scope: „BillingService extrahieren“, nicht „alles refactoren“. So bleibt das System deploybar und das Risiko kontrollierbar.
Am Ende steht keine perfekte Codebase aus dem Lehrbuch, sondern eine, die unter echten Nutzerlast stabil läuft, von Ihrem Team gepflegt werden kann und weiterhin agentisch erweiterbar ist – weil die Policies und Gates etabliert sind.
strangler_plan:
module: Billing
steps:
- name: extract_domain_models
allowed_paths: ["src/Models/Billing/**"]
verification: ["phpunit --filter=Billing"]
- name: extract_service_layer
allowed_paths: ["src/Services/Billing/**"]
verification: ["phpstan", "phpunit --testsuite=Billing"]
- name: wire_controllers
allowed_paths: ["src/Controllers/Billing*", "config/routes/billing.php"]
verification: ["phpunit", "smoke_test.sh"]
rollback: "git revert HEAD --no-edit"