Zum Inhalt
wannacode

Kommentare, die lügen, und Kommentare, die helfen

Kurz gesagt: Ein guter Kommentar sagt, warum der Code so ist, wie er ist; ein Kommentar, der den Code nacherzählt, veraltet und fängt an zu lügen, deshalb ersetzt man ihn durch einen guten Namen oder eine Funktion.

Problem

Der Kommentar, dem alle vertrauen

Der Shop liefert ab einem bestimmten Betrag kostenlos. Letztes Frühjahr lag die Grenze bei 50 €, und genau das steht im Kommentar über delivery_fee. Dann hat das Marketing sie auf 100 € erhöht. Die Zahl im Code wurde geändert. Der Kommentar nicht.

In diesem Herbst schreibt ein Kollege das Banner im Warenkorb. Er liest den Kommentar, nicht den Code, und setzt 50 ein:

# Kostenlose Lieferung ab 50 € Bestellwert.
def delivery_fee(total):
if total >= 100:
return 0
return 4.90
def banner(total):
if total >= 50:
return "Die Lieferung ist kostenlos!"
return f"Noch {50 - total:.2f} € bis zur kostenlosen Lieferung"
total = 60
print(f"Warenkorb: {total:.2f} €")
print(banner(total))
print(f"Lieferung: {delivery_fee(total):.2f} €")
Ausgabe
Warenkorb: 60.00 €
Die Lieferung ist kostenlos!
Lieferung: 4.90 €

Ein Kunde mit einem Warenkorb über 60 € liest „Die Lieferung ist kostenlos!“ und zahlt dann 4,90 € für die Lieferung. Der Support schaut sich delivery_fee an und findet keinen Fehler: Die Funktion tut genau das, was ihr Code sagt. Der Fehler steckt in einem Text, den niemand ausführt.

Sieh es dir an

Der Code ging weiter, der Kommentar blieb

So ist es passiert, Schritt für Schritt:

# Kostenlose Lieferung ab 50 € Bestellwert.
def delivery_fee(total):
if total >= 100:
return 0
return 4.90
def banner(total):
if total >= 50:
return "Die Lieferung ist kostenlos!"
return f"Noch {50 - total:.2f} € bis zur kostenlosen Lieferung"
total = 60
print(f"Warenkorb: {total:.2f} €")
print(banner(total))
print(f"Lieferung: {delivery_fee(total):.2f} €")
Schritt für Schritt, in Worten
  1. Die erste Version: Der Kommentar (Zeile 1) und die Prüfung (Zeile 3) sagen dasselbe: 50 €.
  2. Die Grenze wird 100 €. Jemand ändert die Zahl in Zeile 3 und lässt den Kommentar stehen. Der Compiler prüft Code. Kommentare prüft niemand.
  3. Ein Kollege schreibt das Banner. Er liest den Kommentar, nicht den Code, und setzt in Zeile 9 die 50 ein.
  4. Ein Warenkorb über 60 €. Das Banner fragt seine eigene Grenze: 60 ≥ 50, also „Die Lieferung ist kostenlos!“.
  5. Die Gebühr fragt die echte Grenze: 60 < 100, also kostet die Lieferung 4,90 €.
  6. Der Kunde liest „kostenlos“ und zahlt 4,90 €. Die Lüge steht in Zeile 1. Der Fehler, den sie ausgelöst hat, steht in Zeile 9.

Der Compiler prüft jede Zeile Code. Tests prüfen, was der Code tut. Den Kommentar prüft niemand: Er ist Text neben dem Code, und jede Änderung am Code kann ihn zurücklassen. Das nennt man einen veralteten Kommentar (stale comment). Er ist schlimmer als gar kein Kommentar, weil er so sicher klingt.

Die Idee

Lass den Code es sagen

Die Grenze ist eine Regel des Shops, und die Regel lebt an zwei Orten: im Code der Gebühr und im Kopf von jedem, der den Kommentar gelesen hat. Gib ihr einen Namen, und beide Funktionen fragen den Namen:

FREE_DELIVERY_FROM = 100
def delivery_fee(total):
if total >= FREE_DELIVERY_FROM:
return 0
return 4.90
def banner(total):
if total >= FREE_DELIVERY_FROM:
return "Die Lieferung ist kostenlos!"
return f"Noch {FREE_DELIVERY_FROM - total:.2f} € bis zur kostenlosen Lieferung"
total = 60
print(f"Warenkorb: {total:.2f} €")
print(banner(total))
print(f"Lieferung: {delivery_fee(total):.2f} €")
Ausgabe
Warenkorb: 60.00 €
Noch 40.00 € bis zur kostenlosen Lieferung
Lieferung: 4.90 €

Der Kommentar wird nicht mehr gebraucht: Der Name sagt dasselbe. Der Unterschied ist, dass ein Name nicht zurückbleiben kann. Wenn die Grenze 150 € wird, änderst du eine Zeile, und banner bekommt die neue Grenze. Wenn jemand die Konstante löscht, lässt sich das Programm nicht mehr kompilieren (in Python bricht es beim ersten Aufruf ab, mit einem Fehler, dass der Name nicht existiert).

Das ist die Regel aus der Lektion über Namen (Namen im Code): Bevor du einen Kommentar schreibst, prüfe, ob ein Name dasselbe sagen kann.

Probier es aus

Sag die Ausgabe voraus

Ein Hotel verkauft Übernachtungen online. Was gibt das Programm aus?

# Der Preis mit 20% Mehrwertsteuer.
def with_vat(net):
return net * 1.09
print(f"{with_vat(100):.2f} €")
Antwort

Der Kommentar sagt 20%, und der Code multipliziert mit 1.09. Hotelübernachtungen in Bulgarien haben 9% Mehrwertsteuer, und jemand hat die Zahl geändert, ohne den Kommentar anzufassen. Das Programm hört auf den Code.

Sieh es dir an

Kommentare, die den Code wiederholen

Jeder Kommentar hier sagt dasselbe wie die Zeile darunter:

# Der Preis des Produkts.
price = 25.00
# Die Menge.
quantity = 3
# Wir multiplizieren den Preis mit der Menge.
total = price * quantity
# Wir geben die Summe aus.
print(f"Gesamt: {total:.2f} €")
Ausgabe
Gesamt: 75.00 €

Du liest alles zweimal und lernst nichts Neues. Schlimmer noch: Du lernst, Kommentare zu überspringen, und überspringst dann auch den, auf den es ankommt. Lösch sie. Das Programm ist dasselbe, und die Ausgabe auch.

Sieh es dir an

Ein Kommentar statt eines Namens, ein Kommentar statt einer Funktion

Hier erklären die Kommentare Dinge, die der Code verbirgt:

n = 7 # Anzahl der Bestellungen des Kunden
s = 420 # Umsatz des Kunden in Euro
# Der Kunde ist VIP: mindestens 5 Bestellungen oder mindestens 500 € Umsatz.
if n >= 5 or s >= 500:
print("VIP-Rabatt: 10%")
Ausgabe
VIP-Rabatt: 10%

Der Kommentar neben n steht dort, weil der Name nichts sagt. Gib ihm den Namen aus dem Kommentar: orders. Der Kommentar über dem if beschreibt eine Regel. Eine Regel mit einem Namen ist eine Funktion:

def is_vip(orders, spent):
return orders >= 5 or spent >= 500
orders = 7
spent = 420
if is_vip(orders, spent):
print("VIP-Rabatt: 10%")
Ausgabe
VIP-Rabatt: 10%

Die Ausgabe ist dieselbe, und es gibt keine Kommentare. Die if-Zeile liest sich wie ein Satz. Wenn die Regel „mindestens 10 Bestellungen“ wird, änderst du den Rumpf von is_vip, und jede Stelle, die sie aufruft, bekommt die neue Regel. Wie man eine solche Funktion herauslöst, steht in der Lektion Funktionen.

Sieh es dir an

Auskommentierter Code und ein TODO ohne Verantwortlichen

def confirm(order_id):
send_email(order_id)
# send_sms(order_id)
# if sms_enabled:
# send_sms(order_id)
# TODO: reparieren
Ausgabe
E-Mail zur Bestellung #1063

Wird eine SMS gesendet? Niemand weiß, ob sie mit Absicht, für eine Weile oder aus Versehen abgeschaltet wurde, und niemand traut sich, sie zu löschen. So lebt auskommentierter Code jahrelang. Lösch ihn: Der alte Code steht in der Git-Historie und kommt mit einem Befehl zurück.

Ein TODO ohne Namen und ohne Datum ist ein Wunsch, den niemand besitzt. Ein gutes TODO sagt, wer, welches Ticket und wann:

def confirm(order_id):
send_email(order_id)
# TODO(maria, SHOP-412): SMS hinzufügen, sobald der neue Anbieter die Tests besteht.
Ausgabe
E-Mail zur Bestellung #1063

Noch besser: Die Aufgabe lebt im Ticket, und nur der Link bleibt im Code.

Die Idee

Was ein guter Kommentar sagt

Der Code sagt, was er tut. Ein guter Kommentar sagt etwas, das der Code nicht sagen kann:

# Der Kurier bekommt 30% der Gebühr, auf den Cent abgerundet.
# So steht es im Vertrag mit ihm (Punkt 4.2), deshalb gibt es hier kein round().
def courier_share_cents(fee_cents):
return fee_cents * 30 // 100
fee = 495 # SHOP-388: Der Kurier schickt die Gebühr in Cent, nicht in Euro.
print(f"Kurier: {courier_share_cents(fee) / 100:.2f} €")
Ausgabe
Kurier: 1.48 €

Hier sind es drei Dinge:

  • warum: Das Abrunden sieht wie ein Fehler aus, bis du liest, dass der Vertrag es so verlangt. Ohne den Kommentar wird es jemand „reparieren“;
  • wo man mehr erfährt: Das Ticket SHOP-388 erzählt die Geschichte, die nicht in den Code passt;
  • die Einheit, die der Typ nicht sagt: Cent, nicht Euro. In der Lektion über Code-Smells siehst du, wie das in den Typ selbst wandert.

Noch eines: eine Warnung wie „Langsam: Jeder Aufruf ist eine Anfrage an die Bank, etwa 300 ms. Nicht in einer Schleife aufrufen.“ Ein solcher Kommentar erspart dem Nächsten einen Tag Suchen.

Auch ein guter Kommentar kann veralten. Aber das passiert selten, weil er den Code nicht wiederholt: Der Vertrag ändert sich nicht jedes Mal, wenn du die Funktion änderst.

Genauer, für Neugierige

git blame zeigt, wann jede Zeile geschrieben wurde. Ist der Code unter einem Kommentar neuer als der Kommentar selbst, sei vorsichtig: Jemand hat den Code angefasst und den Kommentar vielleicht nicht erreicht.

Sieh es dir an

Dokumentation für den Aufrufer

Wer delivery_fee aus einem anderen Modul aufruft, will den Rumpf nicht lesen. Er will wissen, was er übergeben muss und was er zurückbekommt. Dafür hat jede Sprache einen Dokumentationskommentar, den der Editor anzeigt, wenn du mit der Maus über den Namen fährst:

FREE_DELIVERY_FROM = 100
def delivery_fee(total: float) -> float:
"""Die Liefergebühr einer Bestellung.
total ist die Summe der Waren in Euro, ohne Lieferung. Gibt 0 zurück
ab FREE_DELIVERY_FROM, sonst die Gebühr des Kuriers.
"""
if total >= FREE_DELIVERY_FROM:
return 0
return 4.90
print(delivery_fee.__doc__.splitlines()[0])
print(f"Lieferung bei 60 €: {delivery_fee(60):.2f} €")
print(f"Lieferung bei 120 €: {delivery_fee(120):.2f} €")
Ausgabe
Die Liefergebühr einer Bestellung.
Lieferung bei 60 €: 4.90 €
Lieferung bei 120 €: 0.00 €
Das ist ein Docstring: ein String, der die erste Zeile im Rumpf der Funktion ist. Er ist kein Kommentar, sondern Teil der Funktion: Das Programm liest ihn als delivery_fee.__doc__, und help(delivery_fee) zeigt ihn in der Konsole. total: float und -> float sind Typhinweise (type hints): Python prüft sie beim Ausführen nicht, aber der Editor und Werkzeuge wie mypy tun es.

Die Dokumentation legt den Vertrag fest: was der Parameter bedeutet und in welchen Einheiten, was zurückkommt und wann es einen Fehler gibt. Sie sagt nicht, wie es innen funktioniert. Die Zahlen aus dem Code werden darin nicht wiederholt: Statt „ab 100 €“ nennt sie die Konstante, damit sie nicht zurückbleiben kann.

Genauer, für Neugierige
Typhinweise sind auch Dokumentation, aber Dokumentation, die man prüfen kann: mypy schlägt fehl, wenn du "60" an delivery_fee übergibst. PEP 257 beschreibt, wie man einen Docstring schreibt: Die erste Zeile ist ein Satz, dann kommt eine Leerzeile und die Details.

Ergänze den fehlenden Teil:

FREE_DELIVERY_FROM = 100


def delivery_fee(total: ) -> float:
    """Die Liefergebühr einer Bestellung.

    total ist die Summe der Waren in Euro, ohne Lieferung. Gibt 0 zurück
    ab FREE_DELIVERY_FROM, sonst die Gebühr des Kuriers.
    """
    if total >= FREE_DELIVERY_FROM:
        return 0
    return 4.90

Probier es aus

Finde den Fehler

Der Shop schickt einen Tag nach der Lieferung eine E-Mail „Wie war deine Bestellung?“. Die letzte Bestellung des Kunden ist vom 24.10., und die E-Mail fragt nach der vom 12.10.:

orders = ["2026-10-19 #1057", "2026-10-24 #1063", "2026-10-12 #1041"]
print(reminder(orders))
Ausgabe
Wie war deine Bestellung 2026-10-12 #1041?

Welche Zeile nimmt die falsche Bestellung?

Wähle die Zeile mit dem Fehler.

Übung

Räum die Kommentare auf

Löse die Aufgabe in deinem eigenen Editor.

Rabatt für treue Kunden. Die Funktion hat einen Kommentar, der lügt, einen Kommentar, der den Code wiederholt, eine auskommentierte Zeile und ein TODO ohne Verantwortlichen:

# Rabatt für treue Kunden: 5%.
def loyalty_total(total, orders):
# wenn der Kunde mehr als 10 Bestellungen hat
if orders >= 20:
# total = total * 0.95
total = total * 0.90 # wir verringern total
# TODO: Runden
return total
print(f"Summe: {loyalty_total(200, 25):.2f} €")
print(f"Summe: {loyalty_total(200, 12):.2f} €")
Ausgabe
Summe: 180.00 €
Summe: 200.00 €

Räum auf: Entferne den Lärm und den toten Code, ersetze die Zahlen durch Namen, und erledige das TODO oder gib ihm einen Verantwortlichen und ein Ticket. Die Ausgabe muss dieselbe bleiben.

Weitere Aufgaben

  • Dokumentiere die Reihenfolge. Schreib einen Dokumentationskommentar für oldest_first aus der Aufgabe mit den Bestellungen: was sie bekommt, was sie zurückgibt und in welcher Reihenfolge.
  • Das Banner bei 99,99 €. Führe das Banner aus der Karte „Lass den Code es sagen“ mit einem Warenkorb über 99,99 € und über 100 € aus. Prüfe, dass beide Funktionen genau an der Grenze übereinstimmen.
  • Dein Projekt. Öffne dein letztes Projekt und such dir drei Kommentare. Entscheide für jeden: Wiederholt er den Code, sagt er warum, oder lügt er?

Test

Teste dich

1. Der Kommentar über einer Funktion sagt das eine, und der Code darunter tut etwas anderes. Wem vertraust du?
2. Was ist an „count += 1 # erhöht count um 1“ falsch?
3. In der Funktion steht eine auskommentierte Zeile „# send_sms(order)“. Was tust du?
4. Welcher Kommentar ist es wert, zu bleiben?
5. Welches TODO wird je erledigt?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·