TL;DR
- Claude ist (noch) nicht der Experte, der für mich alle meine Probleme löst
- Claude ist eher ein Team-Kollege mit viel Wissen, der Lösungsoptionen findet und mir vor allem die Programmierarbeit abnehmen kann
- Wenn mir die Art der Lösung und deren Struktur wichtig ist
- bin ich weiterhin selber dafür verantwortlich diese auszuwählen bzw. herbeizuführen
- sollte ich den von Claude generierten Code konsequent reviewen und sicherstellen, dass er zu der mir vorschwebenden Lösung passt
- Durch Baby-Steps kann ich Claude zu meiner gewünschten Lösung/Struktur bewegen
- Das ist dann nicht Vibe Coding sondern AI-assisted Programming
Das Headerbild wurde von ChatGPT generiert, und auch das hat nicht so funktioniert, wie ich es mir wünschen würde. Aber das ist ein anderes Thema.
Meine ersten praktischen Erfahrungen mit „Vibe Coding” habe ich vermutlich vor ca. einem halben Jahr gesammelt, auch wenn es den Begriff zur der Zeit noch gar nicht gab. Damals habe ich versucht mir von ChatGPT eine kleine Applikation generieren zu lassen, ohne den entstehenden Code selber anzufassen. Das Ergebnis war ernüchternd: Die ersten Schritte funktionierten ganz gut, aber irgendwann hatte ChatGPT sich bei einem Feature in eine Sackgasse verrannt, aus der wir auch gemeinsam nicht wieder rausgekommen sind (die genaueren Details habe ich leider verdrängt).
Während ich kleine, punktuelle Probleme, häufig sehr gut mit Hilfe von AI-Tools analysieren und lösen konnte, habe ich mich auch im weiteren Verlauf bei größeren Aufgaben mit AI-Unterstützung immer eher schwer getan. Nichts von dem, was ich ausprobiert habe, fühlte sich für mich gut und hilfreich an.
Vor einigen Wochen haben ein paar meiner Kolleg:Innen von Claude Code berichtet. Kurze Zeit später konnte ich auch dieses Tool mal ausprobieren und ich war im ersten Moment ziemlich geflashed. Ich weiß gar nicht mehr, was mich am meisten beeindruckt hat:
- Die Tatsache, dass Claude selbständig meine bestehende Code-Basis analysiert und die relevanten Teile für ein Problem findet, ohne dass ich ihm diese nennen oder bereitstellen muss?
- Ihn dabei zu beobachten, wie er versucht eine Lösungsstrategie zu meiner formulierten Anforderung zu finden?
- Dass er mir nicht einfach „die Lösung” präsentiert und sich zurücklehnt, sondern sein Ergebnis selbständig testet, Fehler aufdeckt und diese sofort korrigiert?
Insgesamt hatte ich auf jeden Fall sofort das Gefühl, dass Claude Code das Tool ist, nach dem ich die ganze Zeit gesucht hatte, und mit dem auch ich endlich auf den AI-Hype aufspringen könnte.
Vor kurzem hatte ich dann eine Problemstellung, die in meinen Augen perfekt geeignet war, um sie mit Vibe Coding zu adressieren: Ich wollte meine Notiz und Personal Knowledge Management App wechseln und musste dazu einen Export meiner bestehenden Notizen aus einem proprietären JSON-Format (Anytype) in ein Markdown-Format konvertieren, welches ich mit meinem neuen Tool (Obsidian) verwenden konnte. Eine, wie ich fand, sehr gut beschreibbare Aufgabe – nicht trivial, aber auch nicht übermäßig komplex. Claude Code sollte dafür wie geschaffen sein.
Eine vollständige Beschreibung des Quell- und Zielsformats würde diesen Blog-Post sprengen (und mich dazu zwingen, mich noch intensiver damit zu beschäftigen) und auch wenig zum eigentlichen Thema des Posts beitragen. Ein grobes Verständnis ist jedoch evtl. hilfreich, um sich besser in die Problematik hineinversetzen zu können. Daher hier eine ganz grobe Übersicht.
Format Anytype-Export
Um Notizen vollständig, mit allen Metadaten, aus Anytype herauszubekommen müssen diese im proprietären Any-Block Format exportiert werden. Dabei handelt es sich um eine Sammlung von zahlreichen JSON-Dateien in verschiedenen Unterverzeichnissen.
Die Dateien für die eigentlichen Notizen liegen im Verzeichnis objects
.
Jede Notiz wird in Anytype als ein Object verwaltet. Die Dateien sind entsprechend der internen Object-IDs benannt, die z. T. auch für Referenzen verwendet werden.
Der Inhalt einer solchen JSON-Datei ist wie folgt aufgebaut:
{
"sbType": "Page",
"snapshot": {
"logHeads": {},
"data": {
"blocks": [
...
],
"details": {
"id": "bafyreiagkehxts3wsklux3fn5eamythzcv72nnhfoguhjv33rq2kxvdht4",
"name": "Blog-Post zum Thema Vibe Coding mit Claude Code",
"createdDate": 1745323501,
"creator": "_participant_bafyreid7fihur7y7rniy34bu4h57bcqhp625p3hevkwwgy3d2tsoiyydxy_8mo9d5jlgiw8_A5kSKuwfBNJZ3TREtXKRauminquGhrwHAQHTvTNprjjNq7w3",
"lastModifiedDate": 1745421501,
"lastModifiedBy": "_participant_bafyreid7fihur7y7rniy34bu4h57bcqhp625p3hevkwwgy3d2tsoiyydxy_8mo9d5jlgiw8_A5kSKuwfBNJZ3TREtXKRauminquGhrwHAQHTvTNprjjNq7w3",
"type": "bafyreid2p54osisremlbmjiyoyb2lh4qoo4d3jfflzjg2pv7ge4oiijdpa"
...
},
"fileKeys": null,
"extraRelations": [],
"objectTypes": [ "ot-652974f6f9e5b75bbadb0f4b" ],
"collections": null,
"removedCollectionKeys": [],
"relationLinks": [
...
],
"key": "",
"originalCreatedTimestamp": "0",
"fileInfo": null
},
"fileKeys": []
}
}
In snapshot.data.blocks
sind die eigentlichen Inhalte der Notiz in Form von einzelnen Elementen (Blocks) enthalten, die untereinander in einer hierarchischen Struktur in Beziehung stehen. snapshot.data.details
enthält die Metadaten der Notiz, die in vielen Fällen lediglich eine weitere interne ID als Referenz zu einem Objekt in einem der anderen Verzeichnisse des Exports enthalten. Die entsprechenden Dateien sind ähnlich aufgebaut.
Format Obsidian Markdown
Dahingegen ist das Obsidian Zielformat nahezu trivial. Im Grunde genommen handelt es dabei lediglich um angereicherte Markdown-Dateien, mit einem YAML-Frontmatter-Block am Anfang.
---
type: Publication
createdDate: 2025-04-22 14:05:01 MEST
lastModifiedDate: 2025-04-23 17:18:21 MEST
---
# Blog-Post zum Thema Vibe Coding mit Claude Code
...
Der erste Versuch – naiv und optimistisch
Ich setzte mich also nach Feierabend hin, erstellte den Export, startete Claude Code (Version 0.2.76) und beschrieb in einem kurzen Prompt, was ich erreichen wollte. Claude legte auch sofort los, analysierte die Export-Dateien und gab seine Erkenntnisse und nächsten Pläne schneller auf der Konsole aus, als ich sie lesen konnte. Nach ein paar Bestätigungen meinerseits fing Claude dann irgendwann an ein Python-Migrationsskript zu schreiben. Ich weiß nicht mehr, ob die erste Version sofort fehlerfrei lief oder ob es dazu noch ein wenig Kommunikation zwischen Claude und mir gebraucht hat, aber relativ schnell war das Skript in einer ersten Version „fertig” und generierte mir in einem zweiten Verzeichnis eine Reihe von Markdown-Dateien, die auf den ersten Blick vielversprechend aussahen. Als ich ein paar Dateien öffnete, sah ich jedoch, dass diese ausschließlich den Titel der Notiz enthielten und ansonsten leer waren. Da hatte ich mir etwas mehr versprochen. Zugegeben, das Quellformat ist alles andere als intuitiv und die eigentlichen Inhalte sind im JSON in zig geschachtelten „Block”-Objekten versteckt. Aber dennoch: ich war etwas enttäuscht.
Ich schrieb Claude, dass ich die Inhalte vermisse, und Claude machte sich sofort eifrig daran mein Problem zu analysieren und zu beheben. Nach der nächsten Iteration waren die Inhalte dann rudimentär da und ich nannte Claude den nächsten Punkt, der mir auffiel, und so weiter. Es dauerte nicht lange, da hatte ich den Überblick verloren, was mir schon alles aufgefallen war, was davon Claude mittlerweile schon behoben hatte, und was ich mir bisher noch gar nicht angeguckt hatte.
Als mein Zeitfenster für diesen Abend abgelaufen war, hatte ich ein gefühlt vielleicht gerade einmal halbfertiges Migrationsskript. Die Inhalte meiner Notizen wurden immer noch nicht vollständig migriert und um den größten Teil der Metadaten hatten wir uns noch gar nicht gekümmert.
Ich wusste dafür nun, dass die Aufgabe doch komplexer war, als ich angenommen hatte. Ich hatte vorher nur kurz in das Export-Format geguckt. Schließlich sollte Claude das für mich analysieren, verstehen und entscheiden was zu tun ist. Daher wusste ich auch nicht, wie weit weg insbesondere das Format der Inhalte einer Notiz vom gewünschten Markdown-Format war. Dennoch war das Python-Skript schon stolze 1.000 Zeilen lang. Da ich keine einzige davon selber geschrieben hatte, fühlte es sich für mich an wie ein Stück Legacy-Code. Ich beschloss, es an einem der nächsten Abende noch einmal von vorne zu probieren, um mit den Erfahrungen, die ich im ersten Versuch gesammelt hatte, hoffentlich besser mit Claude zu agieren.
Der zweite Versuch - Beispiele
Ein paar Tage später setzte ich mich erneut hin und startete wieder mit einem neuen Verzeichnis, welches ausschließlich den Export meiner Notizen enthielt. In den vergangenen Tagen war mir die Idee gekommen, dass ich Claude doch ganz gut eine Hand voll Beispiel-Markdown-Dateien erstellen könnte, die demonstrierten, wie ich mir einzelne meiner Notizen im Ergebnis vorstelle – mit deren vollständigem Inhalt, den gewünschten Verweisen sowie den Metadaten. Damit sollte doch wirklich offensichtlich sein, was getan werden muss. Ich erstellte also entsprechende Beispieldateien, startete Claude und sagte ihm, wiederum in einem kurzen Prompt, was ich erwartete. Wieder stürzte sich Claude auf die existierenden Daten und analysierte was das Zeug hielt. Dieses Mal entschied sich Claude überraschenderweise, das Migrationsskript nicht mit Python, sondern mit JavaScript zu schreiben. Sollte mir recht sein, ich bin mit beiden Sprachen nur so halb vertraut, aber so lange Claude sich auskennt, ist ja alles gut.
Um’s kurz zu machen: auch dieser zweite Versuch lief nicht viel besser. Auch dieses Mal tat sich Claude sehr schwer mit der vollständigen Migration der Inhalte, insbesondere mit deren Formatierung. Ich brach den Versuch ab, mit der Erkenntnis, dass ich vermutlich nicht drum herumkomme, Claude im Detail für jede Art von Inhaltsblock zu erklären, wie dieser zu behandeln sei. Ein erster Gedanke wie „dann kann ich es auch gleich selbst schreiben” formte sich in meinem Kopf.
Wie das häufig so ist, wenn man den Rechner zuklappt und seine Gedanken auf etwas anderes lenkt: man kommt auf neue Ideen. Mir fiel ein, dass Anytype ja noch ein zweites Exportformat unterstützt, nämlich Markdown. Da darin ausschließlich der Inhalt der Notizen enthalten ist, und alle Metadaten, die ich recht intensiv genutzt habe, verloren gehen, hatte ich das Format recht schnell verworfen und mich auf das vollständige JSON-Format konzentriert. Aber die Inhalte in dem Markdown-Export sahen gut aus, zumindest deutlich besser als das, was ich zusammen mit Claude an den zwei gemeinsamen Abenden hinbekommen hatte. Vielleicht konnte ich meine Notizen in beiden Formaten exportieren und die Markdown-Dateien einfach mit den fehlenden Daten aus dem vollen JSON-Export anreichern?
Der dritte Versuch - zweigleisig
Als sich die Gelegenheit für den nächsten Versuch ergab wollte ich Claude außerdem etwas mehr unter die Arme greifen als ich das bisher getan hatte. Ich beschrieb in einer README-Datei ausführlich die Problemstellung sowie die Formate der beiden Exporte.
Und tatsächlich, dieses Mal passten die Inhalte direkt im ersten Versuch (kein Wunder, die Markdown-Dateien waren ja bereits da und mussten nur kopiert werden). Allerdings stellte der Lookup der zugehörigen JSON-Daten ein Problem dar. In den Markdown-Dateien existierten keine eindeutigen Identifier für die Notizen. Es blieb nur die Option über den Titel der Notiz zu mappen, der aber nicht eindeutig war.
Dort, wo das Mapping funktioniert hat, konnten Claude und ich uns um die Metadaten kümmern, die in einigen Fällen auch konvertiert werden sollten. Insbesondere enthielten die JSON-Daten Unix-Timestamps, die ich in den Markdown-Dateien gerne in menschen-lesbarer Form haben wollte. Genau dabei hat sich Claude dann überraschend in eine Sackgasse manövriert. Auf meinen Wunsch hin hatte Claude diesmal auch Tests erstellt und einen dieser Tests hat er dann auch nach mehrfachen Versuchen nicht wieder grün bekommen. Da mein Zeitfenster sich eh wieder dem Ende neigte brach ich auch diesen 3. Versuch ohne fertiges Ergebnis ab. Dabei formten sich erneut Gedanken wie: „so schwer kann es doch nicht sein einen Timestamp zu formatieren” und „mir wäre das sicher nicht passiert”. Dem Impuls, den fehlschlagenden Test „mal eben” selbst zu fixen, folgte ich nur kurz. Ein Blick in den Code des Skripts und in den fehlschlagenden Test brachten wider Erwarten nicht sofort die Erkenntnis, was das Problem ist, und für tiefere Analysen hatte ich an diesem Abend keine mentale Kapazität mehr.
Warum funktioniert das nicht?
Da stand ich also, nach drei fehlgeschlagenen Anläufen, und mein Problem war immer noch nicht gelöst. Um mich herum hörte ich doch immer, wie cool dieses Vibe Coding ist, und dass jeder sich damit ganz einfach Softwarelösungen schreiben lassen kann. Warum funktionierte das bei mir nicht?
Eine erste Erkenntnis kam durch den Gedanken, das Migrationsskript dann halt doch selber schreiben zu müssen. Ich überlegte, wo/wie ich anfangen wollte und ertappte mich dabei, das Grundgerüst aus dem letzten Versuch mit Claude als Ausgangsbasis kopieren zu wollen. Warum wollte ich das tun? Weil ich mich, wie bereits geschrieben, mit Python nur bedingt auskenne, und ich komplett ohne Vorlage erst einmal im Internet recherchieren müsste, wie ich so ein Python-Skript aufsetze.
Warum dann nur das Grundgerüst und nicht das ganze halbfertige Skript? Zum einen, weil das Skript von Claude genau das war: das Skript von Claude, bzw. das Skript, das Claude generiert hatte. Für mich fühlte es sich nach wie vor wie ein Stück Legacy-Code an, weil ich bei der Erstellung zwar zugeguckt, aber nicht wirklich daran mitgewirkt hatte. Ich war in meinen Sessions mit Claude recht schnell der Versuchung erlegen, einfach das abzunicken, was Claude mir vorschlug. Den Code, den Claude erstellt hatte, hatte ich maximal überflogen, oft noch nicht einmal das.
Zum anderen hatte ich durch die durchgeführten Versuche und die unterschiedlichen Lösungsansätze mittlerweile eine grobe Idee, wie ich die Migration strukturieren würde, so dass sich die bisher identifizierten Probleme halbwegs gut adressieren ließen. Ich begann mir Claudes und meiner Stärken und Schwächen bewusst zu werden, und wie sich diese sinnvoll miteinander kombinieren ließen. Ich begann von einer „Ich lasse das von Claude für mich entwickeln” zu einer „Ich entwickle das mit Claude zusammen” Denkweise zu kommen.
Der vierte Versuch – auf meine Weise
Ich startete einen vierten Versuch, mit annähernd der gleichen Ausgangsbasis wie vorher. Genau genommen hatte ich tatsächlich schon ein rudimentäres Python-Skript angelegt, bevor ich Claude startete. Dieses Mal versuchte ich jedoch nicht wieder, Claude das Problem zu schildern und ihn ansonsten selbst machen zu lassen. Dieses Mal versuchte ich die Kontrolle zu behalten, und mir von Claude lediglich dabei helfen zu lassen, meinen Ansatz zu verfolgen.
Im ersten Schritt bat ich Claude lediglich, das Skript um zwei Kommandozeilen-Parameter zu erweitern, über die ich ein Quell- und ein Zielverzeichnis angeben konnte. Mehr nicht. Die Anpassungen, die Claude machte, waren dementsprechend überschaubar und, obwohl ich selber nicht gewusst hätte, wie ich das in Python am besten mache, konnte ich sie gut reviewen, verstehen und bestätigen. Ich konnte sie als „unseren Code” akzeptieren.
Im nächsten Schritt bat ich Claude das Skript so zu erweitern, dass es alle JSON-Dateien in einem bestimmten Unterverzeichnis des Quellverzeichnisses einlesen, parsen und zunächst genau zwei Attribute aus den Daten auslesen sollte (Typ und Name). Weiter sollte zunächst nichts passieren, außer, dass die verarbeiteten Dateien mit den gelesenen Attributen gelogged werden sollten. Auch diese Anpassungen hielten sich wieder in Grenzen. Ich schaute sie mir an um sie zu verstehen und bestätigte Claude, dass er die Änderungen vornehmen durfte.
Weitere Schritte waren:
- JSON-Dateien mit bestimmten Typen ignorieren
- zu jeder JSON-Datei eine Markdown-Datei im Zielverzeichnis anzulegen, zunächst nur mit dem Namen als Inhalt
- zusätzlich zu Typ und Name noch die Anytype ID auslesen und in die Markdown-Datei schreiben
- und so weiter …
Mit dieser Vorgehensweise habe ich Claude dazu gezwungen, meinem Weg zu folgen. Da es mein Weg war und ich bewusst kleine Baby-Steps gewählt habe, blieb wenig Spielraum für Claude sich zu verrennen, und die Anpassungen blieben überschaubar und verständlich. Gleichzeitig fühlte sich der entstehende Code für mich nicht mehr fremd an, sondern wie Code, den ich selber auch geschrieben haben könnte, weil durch die schrittweise Vorgehensweise auch die Struktur des Codes in eine entsprechende Richtung gelenkt wurde.
Wenn Claude sich doch mal verrennen wollte, z. B. weil mein Prompt zu ungenau oder der gewählte Schritt doch etwas zu groß war, habe ich ihn konsequent unterbrochen und meinen Prompt nochmal angepasst. Manchmal hab' ich ihn auch zunächst seinen Änderungsvorschlag durchführen lassen, um diesen dann aber danach nochmal nach meinen Vorstellungen anzupassen.
Im Laufe der Zeit nahm ich Claude nicht mehr als den allwissenden Experten wahr, der jedes Problem schneller lösen kann, als ich in der Lage bin es zu beschreiben, sondern als einen eifrigen aber dennoch eher juniorigen Entwickler, der zwar fließend jede bekannte Programmiersprache spricht, der aber wenig Erfahrung mit der strukturierten Herangehensweise an eine nicht-triviale Programmieraufgabe hat.
Irgendwann im weiteren Verlauf, als der größte Teil der Migration halbwegs funktionierte, und die offenen Punkte immer kleiner wurden und mehr ins Detail gingen, habe ich festgestellt, dass ich die notwendigen Änderungen schneller selbst durchführen konnte, als Claude zu erklären, was als nächstes zu tun ist. Mehrmals habe ich angefangen Claude den nächsten Task zu beschreiben, und während dessen wurde mir schon klar, an welcher Stelle ich welche Änderung machen musste, so dass ich den Prompt wieder verwerfen konnte.
Erst im Nachhinein, während ich diesen Blog-Post hier schrieb, habe ich gelernt, dass das, was ich in diesem vierten Versuch gemacht habe, nicht mehr Vibe Coding, sondern AI-assisted Programming ist. Den Unterschied erklärt Simon Willison in einem Blog-Post [1]. Danach wurde der Begriff Vibe Coding ursprünglich von Andrej Karpathy [2] definiert, als
“a new kind of coding … where you fully give in to the vibes, … and forget that the code even exists.”.
Oder, um es mit den Worten von Simon Willison auszudrücken
When I talk about vibe coding I mean building software with an LLM without reviewing the code it writes.
Das hatte ich in den ersten drei Versuchen ausprobiert und es hat für mich und für das konkrete Problem nicht gut funktioniert.
Meine erfolgreichere Vorgehensweise im vierten Versuch beschreibt Simon im weiteren Verlauf wie folgt
If an LLM wrote the code for you, and you then reviewed it, tested it thoroughly and made sure you could explain how it works to someone else that’s not vibe coding, it’s software development.
Ein versöhnliches Ende
Am Ende habe ich meine Notizen mit Hilfe von Claude doch noch erfolgreich migriert bekommen. Das resultierende Python-Skript ist mit knapp 1.000 Zeilen Code umfangreicher geworden, als ich das zu Anfang erwartet hätte. Zum Teil ist das sicherlich der Komplexität geschuldet, die deutlich höher war als anfangs geschätzt. Auf der anderen Seite ist der entstandene Code nach meiner Einschätzung an vielen Stellen aber auch recht “pragmatisch” und sicherlich nicht perfekt. Wenn es sich um ein Stück Software handeln würde, welches dauerhaft in Betrieb wäre und gewartet werden müsste, wäre ich vermutlich nicht sehr zufrieden damit. Allerdings habe Ich auch nicht versucht den Code zusammen mit Claude zu refakturieren und besser zu machen. Das Skript ist ein Stück Wegwerf-Software, die ich genau einmal benutzt habe. Dafür hat der Code ausgereicht.
Vielleicht hätte ich bessere Ergebnisse erzielt, wenn ich mehr Energie in meine Prompts oder in konkrete Arbeitsanweisungen für Claude Code gesteckt hätte, wie sie z. B. in einem Artikel im Anthropic-Blog [3] beschrieben werden. Eine Garantie ist das allerdings auch nicht, wie die Erfahrungen einer meiner Kolleginnen zeigen:
Claude über den Code, den er selbst (über mehrere Sessions) geschrieben hat…
Ich kann nicht genau beschreiben, warum Vibe Coding für mich und mein Problem nicht funktioniert hat
- Vielleicht habe ich mir nicht genug Mühe gegeben mit meinen Prompts und Arbeitsanweisungen
- Vielleicht habe ich mich nicht hinreichend auf den Ansatz eingelassen, weil ich Programmierer bin und eine zu starke Meinung habe, wie “meine” Lösung auszusehen hat
- Vielleicht war das Problem am Ende tatsächlich zu komplex
- Vielleicht ist Claude Code noch nicht gut genug
- Vielleicht eine Kombination von all diesen Punkten
Tools wie Claude Code sind jedoch aktuell alle noch sehr neu und werden sich vermutlich in den nächsten Wochen, Monaten und Jahren noch stark weiterentwickeln. Was heute noch nicht perfekt funktioniert kann Morgen schon viel besser sein. Es besteht also Hoffnung, dass die Tools irgendwann so gut werden, dass selbst ich damit vibe coden kann.
Vielen Dank an Dimitrij Drus, Markus Harrer, Torben Keller, Isabel König-Wingen, Robert Glaser, Sonja Schalmey und Ole Wendland für Feedback und Input zu diesem Blog-Post.