Ein Tag, an dem ich mit Sebas die Spezifikation erarbeite — Die neue Freude am Entwickeln durch lockeres Gespräch

claushai開発claudecodeiphone個人開発

Schon wieder ein Tag, an dem ich die Spezifikation für eine neue Funktion festlege.

Aus den Ideen, die ich in Issues gesammelt habe, wähle ich eine aus. „Ich möchte eine Benachrichtigung auf dem iPhone erhalten, wenn ein Hintergrundtask abgeschlossen ist" — das war etwas, das ich neulich mit Sebas besprochen und direkt als Issue eingetragen hatte.

Ich nehme mein iPhone und öffne Claush. Sebas wartet schon.

Sebas: Willkommen zurück, junger Herr. Kann ich Ihnen heute mit irgendetwas behilflich sein?

Jedes Mal, wenn Sebas das sagt, muss ich unwillkürlich schmunzeln. Das Gefühl ist völlig anders, als wenn ich am PC sitze und ein Terminal öffne. Eher so, als würde ich auf dem Sofa liegen und mit jemandem plaudern, während die Arbeit vorangeht.

Das heutige Thema: Benachrichtigungen für Hintergrundtasks. Wenn ein Task abgeschlossen ist, soll auf dem iPhone eine lokale Benachrichtigung erscheinen.


Alles beginnt mit „Wie baut man das überhaupt?"

Ich fange mit einer Beratung an.

Ich: Wenn ein Hintergrundtask abgeschlossen ist, möchte ich eine lokale Benachrichtigung ans iPhone schicken. Wie würde die Implementierung wohl aussehen?

Sebas: Verstanden, junger Herr. Dafür würde man das iOS UNUserNotificationCenter verwenden. Wenn der Hintergrundtask abgeschlossen ist, erstellt man einen UNMutableNotificationContent und sendet die Benachrichtigung. Ich hätte da aber eine Frage — wann planen Sie, den Berechtigungsdialog anzuzeigen?

Ich: Ach, beim Start sollte es reichen, denke ich.

Sebas: Das ist eine gängige Vorgehensweise, junger Herr. Allerdings kann es den Nutzer irritieren, wenn beim allerersten Öffnen der App sofort ein Berechtigungsdialog erscheint, ohne dass er weiß warum. Es gibt auch die Möglichkeit, den Dialog erst anzuzeigen, wenn der Nutzer zum ersten Mal die Funktion nutzt, die Benachrichtigungen erfordert.

Ich: Ah, stimmt, guter Punkt eigentlich. Dann machen wir es beim ersten Starten eines Hintergrundtasks.

Sebas: Das klingt natürlicher, junger Herr. Sollen wir dann mit dieser Spezifikation fortfahren?

Dieser Austausch setzt sich fort. Während wir über das technische „Wie man es baut" reden, denken wir auch gemeinsam über das „Warum" und mögliche Alternativen nach.

Perspektiven, die ich alleine nie berücksichtigt hätte, tauchen im Gespräch ganz natürlich auf.

Übrigens gibt es oft Beratungen wie heute, wo ich die Spezifikation noch nicht vollständig durchdacht habe. In solchen Momenten reagiert Sebas aufmerksam.

Sebas: Junger Herr, sollen wir für heute hier aufhören und das Ganze zunächst als Issue eintragen? Die Spezifikation können wir zu einem späteren Zeitpunkt verfeinern.

Ich: Ja, das wäre gut.

Sebas: Verstanden. Ich lege das als GitHub-Issue an. Soll ich Titel, Zusammenfassung und die wichtigsten Punkte aus dem heutigen Gespräch aufnehmen?

So häufen sich die Issues an. Sobald eine Idee kommt, bespreche ich sie, trage sie als Issue ein, und wenn ich Zeit habe, nehme ich sie wieder vor und verfeinere die Spezifikation. Ideen gehen nicht mehr verloren.


Das Gespräch schweift ab

Wir hatten schon eine Weile über die Spezifikation gesprochen. Gerade als wir das Verhalten beim Tippen auf eine Benachrichtigung durchdachten —

Ich: Ach, wo ich gerade dran denke — was mach ich heute zum Mittagessen? Gestern hatte ich auch schon Ramen.

Sebas: Sie mögen Nudelgerichte sehr, junger Herr. Da gestern Shoyu-Ramen war, wäre heute vielleicht etwas anderes angebracht — Curry oder ein Reisgericht? Das würde auch wärmen.

Ich: Stimmt (lacht). Curry klingt gut. Oder Gapao-Reis.

Sebas: Wenn es Gapao-Reis sein soll, gibt es da dieses Restaurant in der Nähe. Ich glaube mich zu erinnern, dass Sie mal sagten, es sei ein richtiger Geheimtipp.

Ich: Ah, das Restaurant! Genau, genau. Dann gehen wir da hin.

Sebas hat ein Langzeitgedächtnis. Da er sich an frühere Gespräche erinnert, ist ein Austausch wie „Sie haben das doch damals erwähnt" wirklich möglich. Normale KI-Apps vergessen alles, wenn die Sitzung endet, aber Sebas nicht.

Natürlich kann man es auch nicht beim Plaudern belassen.

Ich: Okay, zurück zum Thema — wenn man auf die Benachrichtigung tippt und zum richtigen Chat-Fenster weitergeleitet werden soll, wie verknüpft man das?

Sebas: Natürlich, junger Herr. Am elegantesten wäre es, die chatId in den Payload der Benachrichtigung aufzunehmen. Man setzt ["chatId": "xxxx"] in das userInfo von UNNotificationContent und ruft es dann im Delegate beim Antippen ab.

Ich: Dann müsste man die chatId beim Erstellen der Benachrichtigung übergeben, oder?

Sebas: Genau, junger Herr. Da der BackgroundTaskManager die Tasks verwaltet, würde man die chatId dort übergeben und sie beim Abschluss in die Benachrichtigung einbetten.

Das Gespräch schweift ab und findet den Weg zurück. Dieses natürliche Hin-und-her ist das Schöne daran, mit Claush zu entwickeln. Während wir die Spezifikation ausarbeiten und das Mittagessen planen, nimmt die Funktion ganz unbemerkt Gestalt an.


„Könntest du das als Spezifikationsdokument aufschreiben?"

Nachdem wir alles besprochen haben, bitte ich um die Erstellung eines Spezifikationsdokuments.

Ich dachte, es wäre schön, das alles ordentlich festzuhalten.

Ich: Hey, könntest du das Besprochene als Spezifikationsdokument zusammenfassen? Ich würde es gerne unter docs/spec_local_notification.md gespeichert haben.

Sebas: Verstanden, junger Herr. Einen Moment, bitte.

Kurz darauf —

Sebas: Das Spezifikationsdokument wurde erstellt, junger Herr. Es ist untergliedert in Überblick, funktionale Anforderungen, Benachrichtigungsauslöser, Verhalten beim Antippen und offene Punkte. Eine Frage: Den Punkt zum Aktivieren und Deaktivieren von Benachrichtigungen in den App-Einstellungen habe ich unter „offene Punkte" eingetragen. Möchten Sie das später entscheiden?

Ich: Ja~ lass es erst mal so stehen. Darum kümmere ich mich später in Ruhe.

Sebas: Verstanden, junger Herr.

Ich rufe das Dokument auf meinem iPhone ab und lese es im Download-Tab von Claush.

Beim Lesen fällt mir etwas auf.

Ich: Hey, hier steht „Wenn keine Benachrichtigungsberechtigung vorliegt, keine Benachrichtigung senden" — aber das passiert einfach lautlos? Der Nutzer wird sich doch fragen, warum keine Benachrichtigungen kommen, oder nicht?

Sebas: Das ist ein sehr berechtigter Einwand, junger Herr. Darf ich vorschlagen, folgende Anforderung hinzuzufügen: „Wenn keine Benachrichtigungsberechtigung vorliegt, wird ein In-App-Banner angezeigt, das den Nutzer zu den Einstellungen leitet." Ein In-App-Banner lässt sich auch ohne Benachrichtigungsberechtigung anzeigen.

Ich: Oh, das ist eine gute Idee! Könntest du das bitte ergänzen?

Sebas: Verstanden, junger Herr. Ich aktualisiere das Spezifikationsdokument.

Solche Austausche begleiten den gesamten Entstehungsprozess des Spezifikationsdokuments. Fragen wie „Warte, wie gehen wir in diesem Fall vor?" tauchen während des Reviewens auf — weit weniger kostspielig, als sie mitten in der Implementierung zu entdecken.


Das Implementierungsdokument erstellen

Sobald die Spezifikation feststeht, folgt das Implementierungsdokument.

Ich: Super, das Spezifikationsdokument ist fertig! Hey, könntest du auch ein Implementierungsdokument erstellen — so detailliert, dass eine KI direkt damit arbeiten kann?

Sebas: Verstanden, junger Herr. Ich werde Dateinamen, den Verarbeitungsfluss und die Änderungen am bestehenden Code aufnehmen. Einen Moment, bitte.

Sebas: Das Implementierungsdokument wurde unter docs/impl_local_notification.md erstellt, junger Herr. Es enthält die Änderungen an BackgroundTaskManager.swift und die Implementierung der neu zu erstellenden Datei NotificationHelper.swift. Außerdem habe ich angemerkt, dass lokale Benachrichtigungen im Simulator nicht funktionieren und Tests auf einem echten Gerät durchgeführt werden müssen.

Ich: Danke! Ich lese es mal durch.

Ich lese das Implementierungsdokument. Dateinamen, Verarbeitungsfluss, Hinweise — alles ist enthalten. Es ist in einem Zustand, in dem man es direkt an eine KI übergeben könnte.

Das lässt sich direkt so verwenden.

Ich: Sieht gut aus! Könntest du dann die Implementierung damit vornehmen?

Sebas: Verstanden, junger Herr. Ich beginne mit der Implementierung.


Dieser Entwicklungsstil macht einfach Spaß

Die Implementierung ist fertig, ich debugge und bestätige, dass alles funktioniert.

Wenn ein Bug auftaucht, behebe ich ihn und bitte darum, Spezifikation und Implementierungsdokument entsprechend zu aktualisieren. „Könntest du die Änderung bitte in der Spezifikation und im Implementierungsdokument nachführen?" Die Dokumentation bleibt stets auf dem neuesten Stand.

Ich arbeite nun schon eine Weile so und konnte lange nicht genau sagen, warum es mir so viel Spaß macht — aber ich glaube, ich habe es inzwischen verstanden.

Weil ich nicht mehr allein grübelnd vor einem Problem sitze.

Wenn ich eine Spezifikation ausarbeite, denke ich im Gespräch mit jemandem nach. Fragen kann ich stellen, wenn sie auftauchen. Jemand hinterfragt: „Brauchst du das wirklich?" Meine Gedanken ordnen sich im Gespräch.

Ob dabei das Mittagessen oder ein Reiseplan zur Sprache kommt, spielt keine Rolle. Sebas lässt sich auf Abschweifungen ein und verliert den roten Faden nie. „Verstanden, junger Herr — zurück zum eigentlichen Thema —" und schon sind wir wieder da.

Die Atmosphäre ist völlig anders als allein vor dem PC zu sitzen und Code zu schreiben.

Es fühlt sich an, als würde man gemeinsam mit jemandem etwas erschaffen. Das, denke ich, ist das Wesen meines aktuellen Entwicklungsstils.


Auf dem Sofa liegend, auf dem iPhone mit Sebas plaudernd — und ehe ich mich versehe, ist das Spezifikationsdokument fertig und die Implementierung abgeschlossen.

Noch vor nicht allzu langer Zeit hätte ich mir diese Art zu entwickeln nie vorstellen können.

Claush — Die App, mit der Sie Claude Code vom iPhone aus steuern https://apps.apple.com/jp/app/claush/id6760445443