Prompts zur Code-Erklärung: vom Regex bis zum Stacktrace

Sechzehn fertige Aufträge, um Code erklären zu lassen. Jede Karte enthält schon ein Codestück, ein Muster, einen Befehl oder einen Stacktrace und sagt, für wen die Erklärung gedacht ist und wie tief sie geht. Eigenen Code einsetzen und lesen.

Fremden Code verstehen

Eine Funktion ohne einen einzigen Kommentar Zeile für Zeile erklären lassen, fremden Legacy-Code in einer Sprache verstehen, die Sie selbst nicht schreiben, und die Begründung finden, die der Autor mitgenommen hat.

Funktion ohne Kommentare

Übersetzt geerbten Code in einen Satz darüber, was er tatsächlich berechnet.

Prompt

def recalc(items, rate): total = 0 for it in items: if it.status != "cancelled": total += it.price * it.qty if total > 10000: total = total * (1 - rate) return round(total, 2)# stammt vom Vorgänger, kein einziger Kommentar — erkläre, was diese Funktion berechnet und unter welchen Bedingungen

Ergebnis

Die Funktion berechnet die Bestellsumme samt Rabatt.1. Sie multipliziert Preis mal Menge über alle Positionen und überspringt stornierte: eine Position mit status "cancelled" landet nie in der Summe.2. Übersteigt die Summe 10000, wird rate darauf angewendet — 0.1 bedeutet zehn Prozent Nachlass.3. Das Ergebnis wird auf zwei Stellen gerundet, also auf Cent.Das Detail, das nur der Code verrät: der Rabatt gilt für die gesamte Summe, nicht für den Betrag oberhalb der Schwelle. Eine Bestellung über 10001 ist nach dem Rabatt billiger als eine über 9999.

Code in einer fremden Sprache

Erklärt eine unbekannte Konstruktion über die, die Sie in Ihrer eigenen Sprache längst benutzen.

Prompt

import functoolsdef retry(times): def decorator(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): for attempt in range(times): try: return fn(*args, **kwargs) except TimeoutError: continue raise RuntimeError("all attempts failed") return wrapper return decorator# ich schreibe PHP und sehe Dekoratoren zum ersten Mal — erkläre es mit Begriffen, die ich kenne

Ergebnis

Das ist ein Dekorator mit Parameter. Das Nächstliegende aus PHP ist eine Funktion, die eine Closure zurückgibt, welche eine andere Funktion umschließt.Von innen nach außen gelesen:- retry(3) gibt decorator zurück;- decorator bekommt Ihre Funktion und gibt wrapper zurück;- die Zeile @retry(3) über einer Funktion bedeutet genau fn = retry(3)(fn) — danach zeigt der Name auf wrapper.wrapper ruft das Original bis zu dreimal auf und schluckt TimeoutError stillschweigend; nach dem letzten Versuch wirft er RuntimeError. functools.wraps sorgt dafür, dass die Hülle Name und Docstring des Originals behält, sonst steht in jedem Traceback und im Debugger nur noch wrapper.

Zeile für Zeile

Geht das Stück Zeile für Zeile durch — für den Fall, dass nicht das Ganze unklar ist, sondern eine bestimmte Stelle.

Prompt

def top_customers(orders, n): totals = dict() for order in orders: totals[order.customer_id] = totals.get(order.customer_id, 0) + order.total ranked = sorted(totals.items(), key=lambda pair: -pair[1]) return ranked[:n]# bitte Zeile für Zeile, vor allem die Zeile mit sorted und dem Minus vor pair

Ergebnis

1. totals = dict() — ein leeres Wörterbuch, in dem die Summen je Kunde gesammelt werden.2. Die Schleife läuft über die Bestellungen. totals.get(id, 0) liefert das bisher Gesammelte oder null, wenn der Kunde zum ersten Mal auftaucht — deshalb braucht es keine eigene Prüfung auf die erste Bestellung.3. sorted über items() sortiert Paare aus Kunde und Summe. Das Minus vor pair[1] dreht die Reihenfolge um: statt aufsteigend absteigend, die größten Kunden stehen oben.4. ranked[:n] schneidet die ersten n Paare ab.Zurück kommen bewusst die Paare und nicht nur die Kennungen: der aufrufende Code braucht die Summe zur Anzeige und muss sie so kein zweites Mal berechnen.

Warum gerade so geschrieben

Erklärt nicht, was der Code tut, sondern warum der Autor diese Form gewählt hat.

Prompt

_cache = None_lock = threading.Lock()def get_config(): global _cache if _cache is None: with _lock: if _cache is None: _cache = load_config_from_disk() return _cache# warum steht die Prüfung auf None zweimal da, einmal vor der Sperre und einmal darin

Ergebnis

Das ist das Muster double-checked locking, und beide Prüfungen haben unterschiedliche Gründe.Die erste ist der schnelle Weg. Sobald die Konfiguration einmal geladen ist, sehen alle weiteren Aufrufe den fertigen Wert und kehren sofort zurück, ohne die Sperre anzufassen. Eine Sperre zu nehmen kostet bei jedem Aufruf Zeit, gebraucht wird sie aber genau einmal im Leben des Prozesses.Die zweite betrifft das Wettrennen. Während der erste Thread die Sperre hält und die Datei liest, hat ein zweiter die äußere Prüfung längst passiert und wartet dahinter. Ohne die innere Prüfung würde er die Konfiguration ein zweites Mal laden und das fremde Ergebnis überschreiben.Die Zeile global sorgt dafür, dass die Zuweisung die Modulvariable ändert und keine neue lokale anlegt.

Fehler und Stacktrace

Der rote Text, den man erst lesen und dann beheben muss: eine Fehlermeldung verstehen, den Stacktrace nach einem Absturz bis zur Ursache lesen, den fehlgeschlagenen Build entziffern und eine Zeile aus dem Produktions-Log deuten.

Absturz der Anwendung

Liest den Stacktrace: wo es abgebrochen ist, welche Zeile schuld ist und was der Fehler selbst bedeutet.

Prompt

Traceback (most recent call last): File "app/orders.py", line 42, in create_order total = calc_total(items) File "app/pricing.py", line 17, in calc_total return sum(item.price * item.qty for item in items)AttributeError: 'dict' object has no attribute 'price'was ist hier passiert und in welcher Zeile suche ich die Ursache

Ergebnis

AttributeError heißt: das Objekt hat kein Attribut namens price.Einen Trace liest man von oben nach unten für den Aufrufweg und von unten nach oben für die Ursache:- create_order in orders.py, Zeile 42, hat calc_total gerufen;- calc_total in pricing.py, Zeile 17, hat auf item.price zugegriffen und ist gescheitert.Gescheitert ist Zeile 17, die Ursache liegt aber woanders. Die Meldung sagt, dass in items Wörterbücher stehen und keine Objekte — und beim Wörterbuch funktioniert der Punktzugriff nicht. Der Aufrufer hat die Daten also direkt aus JSON weitergereicht, ohne ein Modell daraus zu bauen. Reparieren Sie es dort, wo items entsteht, oder lesen Sie das Feld als item["price"].

Der Build läuft nicht durch

Übersetzt das Installationsprotokoll aus der Sprache des Paketmanagers ins Menschliche.

Prompt

ERROR: Could not find a version that satisfies the requirement pandas==2.9.1 (from versions: 2.0.0, 2.1.4, 2.2.2)ERROR: No matching distribution found for pandas==2.9.1ich bekomme die Abhängigkeiten nicht installiert, was bedeutet diese Meldung

Ergebnis

Diese Version der Bibliothek existiert nicht.pip war im Paketindex, hat pandas gefunden und die verfügbaren Versionen aufgezählt — die neueste davon ist 2.2.2. Sie verlangen 2.9.1, die niemand veröffentlicht, daher «no matching distribution».In der Praxis ist es fast immer eines von drei Dingen: ein Tippfehler in der Versionsnummer, eine requirements.txt aus einem neueren Projekt oder eine Version, die es nur für eine andere Python-Version gibt. Die zweite Zeile ist kein eigenes Problem, sondern das Ergebnis der ersten.

Eine Zeile aus dem Produktions-Log

Erklärt, was der Server wirklich meldet, wenn im Log ein Timeout und drei Zahlen stehen.

Prompt

2026-09-12T03:14:07Z ERROR db pool: timeout acquiring connection after 30s, active=20 idle=0 waiting=1372026-09-12T03:14:07Z ERROR api POST /orders 503 in 30012ms request_id=8c1f2awas will mir der Server mit diesen beiden Zeilen sagen

Ergebnis

Die Datenbankverbindungen sind ausgegangen.Erste Zeile: der Pool hat 30 Sekunden auf eine freie Verbindung gewartet und aufgegeben. active=20 heißt, alle zwanzig sind belegt, idle=0 heißt, keine ist frei, waiting=137 heißt, hundertsiebenunddreißig Anfragen stehen in der Schlange.Die zweite Zeile ist die Folge: POST /orders hing dieselben 30 Sekunden und lieferte 503.Zusammen gelesen: Anfragen kommen schneller herein, als die Datenbank Verbindungen wieder freigibt. Der Pool ist nicht kaputt, er meldet ehrlich, dass er an seiner Obergrenze ist. Weitersuchen sollte man bei den langen Abfragen, die eine Verbindung festhalten, und bei der Poolgröße im Verhältnis zur Zahl der Worker.

Reguläre Ausdrücke und Befehle

Zeilen, die funktionieren, solange niemand sie anfasst: einen regulären Ausdruck erklären lassen, die Regex aus der Formularprüfung und den Ausdruck aus der Konfiguration deuten und den Befehl aus fremder Anleitung prüfen.

Was der reguläre Ausdruck prüft

Zerlegt das Muster in seine Teile und sagt, welche Adressen durchkommen und welche zu Unrecht scheitern.

Prompt

^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z][A-Za-z]+$das steht in unserer Formularprüfung — was genau prüft es und was lässt es fälschlich durch

Ergebnis

Das Muster prüft die Form einer Adresse, nicht ihre Existenz.- ^ und $ verlangen, dass die ganze Zeichenkette passt und nicht nur ein Stück darin.- Der Teil vor dem At-Zeichen: Buchstaben, Ziffern, Punkt, Unterstrich, Prozent, Plus und Bindestrich, mindestens ein Zeichen.- @ — genau ein At-Zeichen.- Die Domain: Buchstaben, Ziffern, Punkte und Bindestriche.- Am Ende ein Punkt und eine Endung aus mindestens zwei Buchstaben.Was unnötig durchrutscht: zwei Punkte hintereinander, ein Punkt am Anfang, eine Domain, die es gar nicht gibt. Was zu Unrecht abgelehnt wird: internationalisierte Adressen und Endungen in nichtlateinischer Schrift. Für ein Formular reicht das meistens — ob es die Adresse gibt, klärt ohnehin erst die Bestätigungsmail.

Ausdruck aus der Konfiguration

Zeigt, welche Teile einer Logzeile in welche Gruppe wandern und in welcher Reihenfolge.

Prompt

^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) - - \[([^\]]+)\] "([A-Z]+) ([^ ]+) [^"]+" ([0-9]+) ([0-9]+)$damit wird eine Zeile aus access.log zerlegt — was landet in welcher Gruppe

Ergebnis

Der Ausdruck zerlegt eine Logzeile des Webservers in sechs Gruppen.1. Die IP-Adresse: vier Zahlen, durch Punkte getrennt. Der maskierte Punkt meint wirklich einen Punkt und nicht «ein beliebiges Zeichen».2. Der Zeitstempel in eckigen Klammern: darin ist jedes Zeichen außer der schließenden Klammer erlaubt — genau das hindert die Gruppe daran, den Rest der Zeile zu verschlucken.3. Die Methode: Großbuchstaben.4. Der Pfad: alles bis zum ersten Leerzeichen.5. Der Statuscode.6. Die Antwortgröße in Bytes.Die Reihenfolge der Gruppen ist die Reihenfolge der Felder im Ergebnis: Gruppe eins ist die Adresse, Gruppe fünf der Status. Eine Zeile, deren Pfad ein Leerzeichen enthält, passt nicht als Ganzes und wird schlicht übersprungen.

Befehl aus einer fremden Anleitung

Nimmt die Befehlskette auseinander, bevor Sie auf dem Produktivserver die Eingabetaste drücken.

Prompt

tar -czf - ./data | ssh backup@nas "cat > /backups/data-$(date +%F).tar.gz"# steht so in unserer Backup-Anleitung — erkläre, was passiert, bevor ich das ausführe

Ergebnis

Der Befehl sichert den Ordner data auf eine andere Maschine, ohne auf dieser hier etwas abzulegen.- tar -czf - baut das Archiv und komprimiert es, schreibt es aber auf die Standardausgabe statt in eine Datei: dafür steht der einzelne Bindestrich.- Der senkrechte Strich reicht diesen Strom an den nächsten Befehl weiter.- ssh verbindet sich als Benutzer backup mit dem Rechner nas und startet dort cat, das den Strom in eine Datei legt.- $(date +%F) setzt das heutige Datum in den Dateinamen ein, es entsteht data-2026-09-13.tar.gz.Das Archiv landet nirgends vollständig — es fließt während des Packens durchs Netz, freier Plattenplatz auf der Quellmaschine ist also nicht nötig. Eine scharfe Kante gibt es: existiert die Datei mit diesem Namen bereits, wird sie kommentarlos überschrieben.

SQL und Datenbankabfragen

Ein SELECT über den halben Bildschirm: eine SQL-Abfrage erklären lassen, ein langes Statement mit mehreren JOINs nachvollziehen, die Fensterfunktion verstehen und herausfinden, woran die Abfrage produktiv scheitert.

Auswertungsabfrage

Übersetzt ein langes SELECT in einen Satz darüber, welche Zeilen im Bericht landen.

Prompt

SELECT c.name, date_trunc('month', o.created_at) AS month, count(*) AS orders, sum(o.total) AS revenueFROM orders oJOIN customers c ON c.id = o.customer_idWHERE o.created_at >= now() - interval '1 year'GROUP BY 1, 2HAVING sum(o.total) > 1000ORDER BY revenue DESC-- erkläre, was diese Abfrage zurückgibt und warum ein Teil der Kunden nie im Bericht auftaucht

Ergebnis

Die Abfrage ermittelt den Umsatz je Kunde, nach Monaten aufgeteilt, für das letzte Jahr.- JOIN verbindet Bestellung und Kunde. Es ist ein gewöhnlicher Join, kein LEFT, deshalb kommen Kunden ohne eine einzige Bestellung überhaupt nicht vor.- WHERE schneidet alles ab, was älter als ein Jahr ist.- date_trunc kürzt das Datum auf den Monatsersten — genau dadurch fallen einzelne Bestellungen in Monatsgruppen.- GROUP BY 1, 2 gruppiert nach den ersten beiden Ausdrücken der Liste: Kundenname und Monat.- HAVING wirft Gruppen unter 1000 weg. Anders als WHERE greift dieser Filter erst, nachdem die Summe gebildet wurde.Eine Ergebniszeile ist ein Kunde in einem Monat, nicht ein Kunde insgesamt.

Fensterfunktion

Erklärt, was OVER berechnet und worin sich ein Fenster von einer gewöhnlichen Gruppierung unterscheidet.

Prompt

SELECT customer_id, created_at, total, sum(total) OVER (PARTITION BY customer_id ORDER BY created_at) AS running_total, row_number() OVER (PARTITION BY customer_id ORDER BY total DESC) AS rnFROM orders-- erkläre, was diese beiden OVER berechnen und warum die Zeilen nicht zusammenfallen

Ergebnis

Eine Fensterfunktion rechnet über eine Gruppe, ohne die Zeilen zusammenzufassen: am Ende stehen genauso viele Zeilen wie am Anfang, jede bekommt nur zwei zusätzliche Spalten.- PARTITION BY customer_id teilt die Tabelle in Stapel je Kunde. Ein Stapel ist das Fenster.- In der ersten Spalte wird die Summe fortlaufend gebildet: ORDER BY created_at legt die Reihenfolge im Fenster fest, und standardmäßig zählen alle Zeilen vom Fensteranfang bis zur aktuellen. Bei der dritten Bestellung eines Kunden ist das die Summe der ersten drei.- Das zweite OVER sortiert anders, nämlich absteigend nach Summe, und row_number vergibt die Plätze: die 1 geht an die größte Bestellung dieses Kunden.Genau das unterscheidet ein Fenster von GROUP BY: die Gruppierung hätte eine Zeile je Kunde übrig gelassen und die einzelnen Bestellungen verloren.

Woran die Abfrage scheitert

Erklärt die Abfrage und zeigt gleich, worüber sie bei echten Daten stolpert.

Prompt

SELECT *FROM orders oLEFT JOIN order_items i ON i.order_id = o.idWHERE DATE(o.created_at) = '2026-09-01' AND o.status != 'cancelled'ORDER BY o.created_at DESC-- auf der Testdatenbank sofort, produktiv hängt sie — erkläre die Abfrage und woran sie hier scheitert

Ergebnis

Die Abfrage holt die Bestellungen vom ersten September samt allen Positionen. LEFT JOIN bedeutet, dass auch eine Bestellung ohne Positionen erscheint, dann mit leeren Spalten aus order_items.Wo sie bei echten Daten stolpert:- DATE(o.created_at) ist eine Funktion über der Spalte, und dadurch ist der Index auf created_at nicht mehr nutzbar: die Datenbank muss für jede Zeile der Tabelle ein Datum ausrechnen. Bei hundert Testzeilen unsichtbar, bei Millionen ein vollständiger Scan.- SELECT * über eine Eins-zu-viele-Verknüpfung liefert jede Bestellung so oft, wie sie Positionen hat. Es kommen deutlich mehr Zeilen heraus als Bestellungen, und die Anwendung muss sie selbst wieder zusammenfassen.- Beide Tabellen haben gleichnamige Spalten (id, created_at); im Ergebnis stehen sie gemischt, und am Namen ist nicht mehr zu erkennen, aus welcher Tabelle ein Feld stammt.

Anfängern und im Bewerbungsgespräch erklären

Wenn die Erklärung wichtiger ist als der Code: Code für Anfänger erklären, eine Programmieraufgabe aus dem Bewerbungsgespräch durchgehen und einem Nicht-Techniker sagen, was das Skript macht.

Das erste Programm

Eine Erklärung für die zweite Woche mit Code: was jede Zeile tut und warum sie so dasteht.

Prompt

nums = [4, 8, 15, 16, 23, 42]total = 0for n in nums: if n % 2 == 0: total += nprint(total / len(nums))# ich lerne gerade erst Python — erkläre Schritt für Schritt, was hier passiert

Ergebnis

Das Programm addiert die geraden Zahlen der Liste und teilt das Ergebnis durch die Anzahl ALLER Zahlen.1. nums ist eine Liste aus sechs Zahlen.2. total = 0 ist die Kiste, in die addiert wird. Die Null ist da, damit es einen Anfang gibt.3. for n in nums ist die Schleife: n wird nacheinander zu jeder Zahl der Liste.4. n % 2 == 0 heißt, der Rest beim Teilen durch zwei ist null, die Zahl ist also gerade.5. total += n addiert das Gefundene zum Gesammelten.6. Geteilt wird durch len(nums), also durch die Länge der Liste: sechs.Das sieht nach einem Denkfehler aus: in der Liste stehen vier gerade Zahlen mit der Summe 70, geteilt wird aber durch sechs. Wer den Durchschnitt der geraden Zahlen will, muss durch deren Anzahl teilen.

Aufgabe aus dem Bewerbungsgespräch

Der Algorithmus so aufbereitet, dass man ihn am Whiteboard laut erklären kann.

Prompt

def search(nums, target): lo, hi = 0, len(nums) - 1 while hi >= lo: mid = (lo + hi) // 2 if nums[mid] == target: return mid if nums[mid] > target: hi = mid - 1 else: lo = mid + 1 return -1# wurde im Bewerbungsgespräch gefragt — erkläre die Idee des Algorithmus und warum er schnell ist

Ergebnis

Das ist eine binäre Suche in einem sortierten Array. Die Idee ist eine einzige: in jedem Schritt fällt die Hälfte des Rests weg.- lo und hi sind die Grenzen des Bereichs, in dem die Antwort noch liegen kann.- mid ist die Mitte. Der doppelte Schrägstrich ist Ganzzahldivision, deshalb kommt ein Index heraus und kein Bruch.- Steht in der Mitte der gesuchte Wert, ist man fertig.- Ist die Mitte größer, liegt die Antwort links, hi rückt nach.- Sonst liegt sie rechts, lo rückt nach.- Die Schleife endet, wenn sich die Grenzen kreuzen: der Wert fehlt, zurück kommt -1.Der Aufwand ist logarithmisch: eine Million Elemente bedeutet etwa zwanzig Schritte statt einer Million. Bedingung ist, dass das Array bereits sortiert ist, sonst liefert der Algorithmus stillschweigend eine falsche Antwort.Die übliche Nachfrage im Gespräch: in Sprachen mit fester Ganzzahlbreite kann lo + hi überlaufen, deshalb schreibt man dort lo + (hi - lo) // 2.

Der Chefin erklären

Eine Nacherzählung des Skripts ganz ohne Fachbegriffe, für das Gespräch mit jemandem, der keinen Code liest.

Prompt

import csv, smtplibrows = list(csv.DictReader(open("orders.csv")))late = [r for r in rows if r["shipped_at"] == ""]body = "Noch nicht versandte Bestellungen: " + str(len(late))smtplib.SMTP("mail.local").sendmail("bot@shop.local", "ops@shop.local", body)# ich muss einer nichttechnischen Führungskraft erklären, was dieses Skript tut

Ergebnis

Das Skript prüft, welche Bestellungen noch nicht versandt sind, und meldet die Zahl per E-Mail.Schritt für Schritt, ohne Fachbegriffe:1. Es öffnet die Bestelltabelle — eine ganz normale Datei, die auch Excel öffnen würde.2. Es sucht die Zeilen heraus, in denen kein Versanddatum steht: bestellt, aber nicht abgegangen.3. Es zählt, wie viele solcher Zeilen zusammenkommen.4. Es schickt eine E-Mail an die Abteilungsadresse, mit dieser einen Zahl im Text.Was eine Führungskraft wissen sollte: das Skript ändert nichts und stört niemanden, es liest nur und berichtet — man kann es stündlich laufen lassen. Seine eine Schwachstelle: es liest eine Datei und zeigt damit den Stand des letzten Exports, nicht den von jetzt.

Prompts für Code-Erklärung: 16 fertige Analysen | iBro