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 deliveryFee. 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.
function deliveryFee(total) {
if (total >= 100) {
return 0;
}
return 4.9;
}
function banner(total) {
if (total >= 50) {
return "Die Lieferung ist kostenlos!";
}
return `Noch ${(50 - total).toFixed(2)} € bis zur kostenlosen Lieferung`;
}
const total = 60;
console.log(`Warenkorb: ${total.toFixed(2)} €`);
console.log(banner(total));
console.log(`Lieferung: ${deliveryFee(total).toFixed(2)} €`);
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 deliveryFee 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.
function deliveryFee(total) {
if (total >= 100) {
return 0;
}
return 4.9;
}
function banner(total) {
if (total >= 50) {
return "Die Lieferung ist kostenlos!";
}
return `Noch ${(50 - total).toFixed(2)} € bis zur kostenlosen Lieferung`;
}
const total = 60;
console.log(`Warenkorb: ${total.toFixed(2)} €`);
console.log(banner(total));
console.log(`Lieferung: ${deliveryFee(total).toFixed(2)} €`);
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 10 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 10.

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:

const FREE_DELIVERY_FROM = 100;
function deliveryFee(total) {
if (total >= FREE_DELIVERY_FROM) {
return 0;
}
return 4.9;
}
function banner(total) {
if (total >= FREE_DELIVERY_FROM) {
return "Die Lieferung ist kostenlos!";
}
return `Noch ${(FREE_DELIVERY_FROM - total).toFixed(2)} € bis zur kostenlosen Lieferung`;
}
const total = 60;
console.log(`Warenkorb: ${total.toFixed(2)} €`);
console.log(banner(total));
console.log(`Lieferung: ${deliveryFee(total).toFixed(2)} €`);
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 JavaScript 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.
function withVat(net) {
return net * 1.09;
}
console.log(`${withVat(100).toFixed(2)} €`);
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.
const price = 25;
// Die Menge.
const quantity = 3;
// Wir multiplizieren den Preis mit der Menge.
const total = price * quantity;
// Wir geben die Summe aus.
console.log(`Gesamt: ${total.toFixed(2)} €`);
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:

const n = 7; // Anzahl der Bestellungen des Kunden
const s = 420; // Umsatz des Kunden in Euro
// Der Kunde ist VIP: mindestens 5 Bestellungen oder mindestens 500 € Umsatz.
if (n >= 5 || s >= 500) {
console.log("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:

function isVip(orders, spent) {
return orders >= 5 || spent >= 500;
}
const orders = 7;
const spent = 420;
if (isVip(orders, spent)) {
console.log("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 isVip, 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

function confirm(orderId) {
sendEmail(orderId);
// sendSms(orderId);
// if (smsEnabled) {
// sendSms(orderId);
// }
// 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:

function confirm(orderId) {
sendEmail(orderId);
// 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 Math.round.
function courierShareCents(feeCents) {
return Math.floor((feeCents * 30) / 100);
}
const fee = 495; // SHOP-388: Der Kurier schickt die Gebühr in Cent, nicht in Euro.
console.log(`Kurier: ${(courierShareCents(fee) / 100).toFixed(2)} €`);
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 deliveryFee 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:

const FREE_DELIVERY_FROM = 100;
/**
* Die Liefergebühr einer Bestellung.
* @param {number} total die Summe der Waren in Euro, ohne Lieferung
* @returns {number} 0 ab FREE_DELIVERY_FROM, sonst die Gebühr des Kuriers
*/
function deliveryFee(total) {
if (total >= FREE_DELIVERY_FROM) {
return 0;
}
return 4.9;
}
console.log(`Lieferung bei 60 €: ${deliveryFee(60).toFixed(2)} €`);
console.log(`Lieferung bei 120 €: ${deliveryFee(120).toFixed(2)} €`);
Ausgabe
Lieferung bei 60 €: 4.90 €
Lieferung bei 120 €: 0.00 €
Das ist JSDoc: /** … */ über der Funktion, mit @param {number} für jeden Parameter und @returns für das Ergebnis. Node liest es gar nicht. Der Editor (VS Code) zeigt es bei jedem Aufruf an, und mit einer Zeile // @ts-check am Anfang der Datei prüft er auch die Typen aus {number}.

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
Mit // @ts-check unterstreicht der Editor deliveryFee("60"), weil JSDoc {number} sagt. Das ist dieselbe Prüfung, die TypeScript macht, ohne die Sprache der Datei zu ändern.

Ergänze den fehlenden Teil:

const FREE_DELIVERY_FROM = 100;

/**
 * Die Liefergebühr einer Bestellung.
 * @param {} total die Summe der Waren in Euro, ohne Lieferung
 * @returns {number} 0 ab FREE_DELIVERY_FROM, sonst die Gebühr des Kuriers
 */
function deliveryFee(total) {
  if (total >= FREE_DELIVERY_FROM) {
    return 0;
  }
  return 4.9;
}

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.:

const orders = ["2026-10-19 #1057", "2026-10-24 #1063", "2026-10-12 #1041"];
console.log(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%.
function loyaltyTotal(total, orders) {
// wenn der Kunde mehr als 10 Bestellungen hat
if (orders >= 20) {
// total = total * 0.95;
total = total * 0.9; // wir verringern total
}
// TODO: Runden
return total;
}
console.log(`Summe: ${loyaltyTotal(200, 25).toFixed(2)} €`);
console.log(`Summe: ${loyaltyTotal(200, 12).toFixed(2)} €`);
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 oldestFirst 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++; // erhöht count um 1“ falsch?
3. In der Funktion steht eine auskommentierte Zeile „// sendSms(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 ·