Refactoring von Vibe-Codebases

Vibe-Codebases sind nicht wertlos – sie sind ungeschliffen. Der Refactoring-Ansatz darf sie nicht mit Big-Bang-Rewrites zerstören. Agenticode in Köln nutzt Strangler-Fig-Patterns, agentische Multi-Step-Loops und harte Lint-Grenzen, um aus Prototyp-Monolithen wartbare Systeme zu formen – ohne Delivery-Stopp.

Diagnose: Was Vibe-Codebases typischerweise haben

Vibe-Codebases zeigen wiederkehrende Muster: God-Classes mit 800+ Zeilen, SQL in Controllern, Copy-Paste zwischen Features, fehlende Namespaces, Tests nur für „das letzte Feature“. Der Agent hat funktional geliefert – aber ohne Architektur-Intent.

Bevor refactoriert wird, kartieren wir: Hotspots (hohe Änderungsfrequenz + hohe Komplexität), Domain-Grenzen (was gehört zusammen?), und Agent-Risiko (wo würde ein Agent am meisten Schaden anrichten?). Diese Karte steuert die Strangler-Reihenfolge.

Wichtig: Nicht alles muss sofort perfekt sein. Agenticode priorisiert nach Business-Risiko und Änderungsfrequenz – nicht nach akademischer Schönheit.

Hotspot-Analyzer: Zeilen × Churn priorisiert Refactoring-Ziele.
<?php

declare(strict_types=1);

namespace App\Refactoring\Analysis;

final class VibeCodebaseHotspotAnalyzer
{
    public function findHotspots(string $srcDir, int $limit = 10): array
    {
        $files = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($srcDir)
        );
        $scores = [];

        foreach ($files as $file) {
            if ($file->getExtension() !== 'php') {
                continue;
            }
            $path = $file->getPathname();
            $lines = count(file($path) ?: []);
            $churn = $this->gitChurnLast90Days($path);
            $scores[$path] = $lines * log(1 + $churn);
        }

        arsort($scores);
        return array_slice($scores, 0, $limit, true);
    }

    private function gitChurnLast90Days(string $path): int
    {
        $out = shell_exec(
            'git log --since=90.days --numstat -- ' . escapeshellarg($path) . ' 2>/dev/null'
        ) ?: '';
        return substr_count($out, "\n");
    }
}

Strangler-Fig mit agentischen Sub-Loops

Der Strangler-Fig extrahiert Schicht für Schicht: zuerst Domain-Models, dann Services, dann Persistence, zuletzt Controller-Wiring. Jede Extraktion ist ein abgeschlossener Agent-Run mit engem Scope und eigenem Verification-Set.

Während der Extraktion bleibt die alte Implementierung als Fassade erhalten. Neue Aufrufer nutzen die extrahierte Schicht; Legacy-Code wird markiert und später entfernt. So ist das System jederzeit deploybar.

Multi-Agent-Orchestrierung hilft: Planner definiert Extraktions-Reihenfolge, Implementer extrahiert, Tester sichert Verhalten mit Characterization Tests, Reviewer prüft Architektur-Compliance.

Strangler-Fassade: neue Schicht dahinter, Legacy-API nach vorn.
<?php

declare(strict_types=1);

namespace App\Billing;

/**
 * @deprecated Use InvoiceService::create() – Strangler step 3/4
 */
final class LegacyBillingFacade
{
    public function __construct(
        private readonly InvoiceService $invoiceService,
    ) {}

    public function createInvoice(array $legacyPayload): array
    {
        $dto = InvoiceCreateDto::fromLegacyArray($legacyPayload);
        $invoice = $this->invoiceService->create($dto);
        return $invoice->toLegacyArray(); // backward compatible response
    }
}

Harte Lint-Grenzen nach dem Prototyp

Refactoring ohne Grenzen driftet zurück in Vibe-Territorium. Agenticode etabliert PHPStan Level 8, Custom Rules (kein SQL in Controllers, max. Dateigröße), und PHP-CS-Fixer als Pre-Commit-Hook. Agents dürfen nur committen, wenn alle Grenzen grün sind.

Characterization Tests sichern Legacy-Verhalten vor der Extraktion: Sie dokumentieren Ist-Zustand, nicht Soll-Zustand. Sobald grün, kann refactored werden – mit Confidence, dass externe Verhalten gleich bleiben.

Am Ende einer Agenticode-Refactoring-Engagement-Phase haben Sie: modulare Struktur, grüne CI, Cursor Rules die zukünftige Agent-Runs im Gleis halten, und ein Team, das versteht, wie weitergebaut wird.

PHPStan Custom Rules: harte Grenzen gegen Vibe-Regression.
# phpstan-custom-rules/refactoring-gates.neon
parameters:
  agenticodeRules:
    maxFileLines: 400
    forbiddenInControllers:
      - PDO
      - mysqli
      - raw SQL strings
    requiredTestSuffix:
      Service: Test
      Repository: Test

services:
  - class: App\PhpStan\NoSqlInControllerRule
  - class: App\PhpStan\MaxFileLinesRule