Es begann mit dem falschen Wort

Manchmal verrät ein einziges Wort, dass man eine Sache noch nicht ganz zu Ende gedacht hat. Bei mir war dieses Wort „CRM". So habe ich Pixelklicker.de anfangs beschrieben – „eine Art CRM". Klang erstmal plausibel: Kontakte verwalten, Anfragen sammeln, irgendwas mit Leuten, die sich melden.

Nur: Was ich eigentlich wollte, war etwas ganz anderes. Eine Heimat für meine Projekte. Eine Plattform, auf der ich zeige, woran ich arbeite – mit lauffähigen Demos unter app.pixelklicker.de – und auf der gleichzeitig Artikel zur Entwicklung erscheinen. Und der Clou: Diese Artikel sollten nicht aus dem Nichts entstehen, sondern KI-gestützt aus meiner eigenen Entwicklerdokumentation. Genau aus den Journal-Einträgen und Devlogs, die ich sowieso schreibe.

Erst als ich anfing, das Datenmodell zu skizzieren, fiel der Groschen: Das ist kein CRM. Das ist ein CMS, das nebenbei auch Leads einsammelt. „CRM vs. CMS" war am Ende nur ein Etikett – die Funktion (Inhalte verwalten plus Leads sammeln) stimmte von Anfang an. Aber es war ein gutes Beispiel dafür, dass man eine Idee erst dann wirklich versteht, wenn man sie in Tabellen und Klassen gießt.

Der Stack stand – das war der einfache Teil

Über die Technik musste ich nicht lange grübeln. Ich baue inzwischen ein gutes Dutzend Projekte mit derselben Kombi, und die hat sich bewährt:

  • Laravel 13 als Fundament
  • Filament 5 fürs Admin
  • Livewire 4 fürs Frontend-Verhalten
  • deutsche UI, englischer Code
  • Pest + PHPStan als Qualitätsnetz

Diese Vorgaben standen fest. Genau deshalb wurde die eigentliche Spannung dieser Session woanders ausgetragen: bei den Detailentscheidungen und – ehrlich gesagt – bei der Tooling-Kompatibilität auf brandneuen Framework-Versionen.

Die drei Weggabelungen

Bevor ich loslegte, gab es drei Stellen, an denen ich mich entscheiden musste.

Erstens: Wie binde ich die KI an? Anthropic-SDK direkt oder Prism PHP? Beim direkten SDK hätte ich Freitext zurückbekommen und müsste den selbst parsen – fehleranfällig und mühsam. Prism gibt mir stattdessen eine einheitliche Laravel-API mit Structured Output. Das heißt: Ich bekomme Titel, Excerpt, Body und Tags als saubere, deterministische Felder zurück, statt aus einem Textblock herauszufischen, was die KI gerade für richtig hielt. Die Entscheidung fiel auf Prism – weniger Fehlerquellen, klar definierte Felder.

Zweitens: das Datenmodell für „Abonnenten/Kontakte". Brauche ich ein eigenes Subscriber-Modell? Oder reicht ein generisches Modell? Ich habe mich für ein einzelnes Lead-Modell entschieden, das als gemeinsamer „Posteingang" für Kontaktanfragen und Newsletter-Anmeldungen dient. Unterschieden wird über ein type-Enum, und ein confirmed_at-Feld hält mir die Tür für ein späteres Double-Opt-in offen. Eine Tabelle, mehrere Zwecke – das hält die Sache schlank, ohne mir Optionen zu verbauen.

Drittens: Wie viel überlasse ich der KI? Hier war mir Kontrolle wichtiger als Bequemlichkeit. Die Generierung läuft als Queued Job, aber die KI erzeugt nur einen Entwurf. Veröffentlicht wird von Hand, nach menschlichem Review. Vertrauen plus Kontrolle – ich will nicht, dass irgendwann ein Artikel live geht, hinter dem ich nicht zu hundert Prozent stehe.

Und dann kamen die Sackgassen

So weit der Plan. Jetzt zu den Stellen, an denen es klemmte – denn die gehören zur ehrlichen Geschichte dazu.

Pest wollte nicht

Mein erster echter Stolperstein: Pest ließ sich nicht installieren. Die Plugins pest-plugin-laravel und pest-plugin-livewire unterstützen Laravel 13 schlicht noch nicht, und Composer quittierte das mit einem Konflikt. Ich hätte mich da reinverbeißen können – Versionen pinnen, Workarounds suchen, downgraden. Aber das wäre der falsche Kampf gewesen. Stattdessen bin ich bewusst auf PHPUnit 12 ausgewichen. Funktional gleichwertig für meine Zwecke, und – wichtiger – der Stack bleibt aktuell, statt dass ich das ganze Projekt ausbremse, nur um eine Test-Syntax zu behalten, die ich gewohnt bin.

Der Seeder, der über Faker stolperte

Der nächste Reibungspunkt war ärgerlich-klein: Mein Seeder brach ab. Schuld war die Faker-Locale de_DE, die schlicht kein catchPhrase() kennt. Schnell umgestellt auf words() – Problem gelöst. Eine dieser Fünf-Minuten-Hürden, die einen trotzdem kurz aus dem Flow reißen.

Die Doku log – der Generator hatte recht

Mein liebster Lerneffekt kam von den Filament-5-Namespaces. Eine Skill-Vorlage behauptete, die Formularfelder lägen unter Filament\Schemas\Components. Klang offiziell, also habe ich es geglaubt. Tatsächlich nutzt der make:filament-resource-Generator dieser Version aber Filament\Forms\Components:

php
// Was die Vorlage behauptete:
use Filament\Schemas\Components\TextInput;

// Was der Generator tatsächlich ausgab – und was stimmte:
use Filament\Forms\Components\TextInput;

Die Lehre daraus war eindeutig: dem echten Generator-Output vertrauen, nicht der Doku-Annahme. Auf brandneuen Versionen ist die Dokumentation der Skill-Vorlagen manchmal eine halbe Version hinterher – der Code, den das Framework selbst erzeugt, ist die Wahrheit.

Was am Ende stand

Trotz – oder gerade wegen – dieser kleinen Umwege stand am Ende der Session ein Fundament, das wirklich läuft. Konkret:

  • ein durchdachtes Datenmodell: Project, Article, DevDocument, PromptTemplate, Lead, Tag und Visit
  • ein Filament-Admin mit Rollen
  • die KI-Generierung end-to-end, inklusive Doku-Import direkt aus den Projektordnern
  • 24 grüne Tests
  • PHPStan Level 5 ohne einen einzigen Fehler

Damit ist der Kreislauf geschlossen, der die ganze Idee trägt: Ich schreibe meine Devlogs, sie wandern als DevDocument ins System, ein Prism-Job macht daraus einen strukturierten Entwurf – und ich entscheide, was davon als Artikel das Licht der Welt erblickt. Genau dieser Text ist im Grunde der erste Beweis, dass die Pipeline funktioniert.

Was ich mitnehme

Drei Dinge bleiben hängen.

Erstens: Etiketten sind unwichtig, Funktionen zählen. Ob ich das Ding „CRM" oder „CMS" nenne, hat keinen Code geändert. Wichtig war, die Funktion sauber zu fassen – Inhalte verwalten, Leads sammeln.

Zweitens: Auf brandneuen Framework-Versionen ist Tooling-Kompatibilität der wahrscheinlichste Reibungspunkt. Die Pest-Plugins waren der beste Beweis. Lieber früh prüfen und pragmatisch ausweichen, als sich tagelang an einem Nebenschauplatz aufzureiben.

Drittens: Strukturiertes KI-Output plus menschliches Review ist die Kombination, mit der ich mich wohlfühle. Die KI nimmt mir die Fleißarbeit ab, aber die Veröffentlichung bleibt meine Entscheidung.

Offen bleibt das eigentliche UI-Design – bewusst. Im Moment trägt Pixelklicker nur ein schlichtes Baseline-Theme. Aber das Fundament steht, und das war der Teil, der zählt.