Ein Bericht gibt vier Zeilen aus, obwohl in seiner Konfigurationsdatei limit: 6 steht. Sie sollen herausfinden, warum. Dieses Repository ist Ihnen völlig neu. Es enthält eine CLI, einen Konfigurationslader, einige Dienste und Tests. Alles zu lesen würde irgendwann zum Ziel führen. Dem einen Wert zu folgen, der die Zeilenzahl bestimmt, geht deutlich schneller.
Die Frage präzisieren
„Das Berichtsmodul verstehen“ hat keinen klaren Endpunkt. Eine Frage mit einer konkreten Antwort schon:
In der Konfiguration steht
limit: 6, in der UmgebungREPORT_LIMIT=4. Der Bericht gibt vier Zeilen aus. Welcher Wert hatte Vorrang, und was passiert, wennREPORT_LIMITeine leere Zeichenfolge ist?
Notieren Sie den ausgeführten Befehl, das Verzeichnis und den Commit. Prüfen Sie außerdem, dass die Eingabe mehr als vier Datensätze enthält. Hat die Quelle nur vier Zeilen, sagt eine Ausgabe von vier Zeilen nichts über die Begrenzung aus.
Mit dem vorhandenen Hinweis eine Startdatei finden
Die erste nützliche Datei kann ein Starter, ein Formatierer oder ein Test sein – nicht unbedingt die Funktion, die die Grenze auswählt. Nutzen Sie den reproduzierbaren Hinweis, um diese Funktion zu erreichen, und halten Sie fest, welche Verbindungen Sie tatsächlich geprüft haben.
Laden Sie das Übungsprojekt zum Berichtseinstieg herunter, entpacken Sie es und wechseln Sie nach report-entry-lab. Dieses kleine Projekt wurde für das Tutorial erstellt. Es ergänzt die unten verwendeten Prioritätsregeln um einen echten Befehl, eine Dispatch-Tabelle und lokale Fixture-Lader. Sie benötigen Node.js 22 oder neuer, npm und ripgrep (rg); weder eine Installation von Abhängigkeiten noch ein Modellkonto ist nötig. Die Shell-Befehle funktionieren unter macOS und Linux.
REPORT_LIMIT=4 npm run --silent report
Report rows: 4
Aster
Birch
Clover
Dahlia
Die Konfigurations-Fixture enthält limit: 6; die Daten-Fixture hat acht Zeilen. Die erste Ausgabezeile ist eine Kopfzeile, gefolgt von vier Datenzeilen. Die Abweichung ist also real und entsteht nicht durch eine zu kurze Eingabedatei. Das Programm liest ausschließlich die enthaltenen lokalen Fixtures und schreibt ins Terminal; seine Tests starten lokale Node-Prozesse ohne Netzwerkanfragen oder Schreibzugriffe auf Dateien.
| Ihr Hinweis | Hier beginnen | Noch zu prüfen |
|---|---|---|
| Ein Befehl reproduziert das Verhalten | Das aufgerufene Paketskript oder Programm | Welches Argument welchen Handler auswählt |
| Ein markanter Ausgabe- oder Fehlertext | Der Code, der ihn zusammensetzt | Ob ein Treffer Erzeuger, Fixture oder Assertion ist und wer den Erzeuger aufruft |
| Ein benannter Test | Sein Import und der Aufruf im Test | Ob er den echten Befehl startet oder Ersatzabhängigkeiten übergibt |
| Ein Konfigurationsschlüssel oder eine Umgebungsvariable | Das Lesen und Umwandeln des Werts | Welcher Aufrufer dorthin führt und welcher Lader die Konfiguration liefert |
Dasselbe Übungsprojekt in Yenpo lesen
Nutzen Sie das heruntergeladene report-entry-lab für die Frage: „Welcher Einstieg hat diese vier Zeilen erzeugt?“ Das Ergebnis sollte eine kurze Notiz zu Startbefehl, ausgewähltem Handler, Eingabedateien und Ausgabeaufruf sein.
- Öffnen Sie den entpackten Ordner
report-entry-labin Yenpo und wählen Sie das Stammverzeichnis mitpackage.json. Öffnen Sie mit Command-P (Zur Datei…) zunächstpackage.json, dannsrc/cli.mjsundsrc/commands.mjs. Prüfen Sie das Startskript und den fürreportregistrierten Handler. - Suchen Sie über Alle Dateien durchsuchen in der Seitenleiste nach
Report rows:; Regex bleibt ausgeschaltet. Lesen Siesrc/format-report.mjszusammen mit den Testtreffern: Die eine Datei erzeugt den Text, die anderen prüfen das erwartete Ergebnis. Verfolgen Sie den Import und Aufruf des Formatierers insrc/report.mjs, bevor Sie ihn diesem Befehl zuordnen. - Lassen Sie
src/report.mjs,src/limits.mjsund den relevanten Test in Tabs geöffnet. Notieren Sie zu jedem Schritt die belegende Datei und Zeile. Unterscheiden Sie anschließend mit den Terminalprüfungen unten zwischen einem direkten Funktionstest und einem Lauf durch die echte CLI.
Für das Durchsuchen von Dateien und die Textsuche brauchen Sie weder eine Modellverbindung noch einen JavaScript-Sprachserver. Laden Sie Yenpo für Mac herunter und öffnen Sie denselben entpackten Ordner; die Hilfe zum Codelesen erklärt beide Suchwerkzeuge. Die ZIP enthält Quelldateien, aber keine Git-Historie. Diese Übung liest daher Dateien und keinen festgehaltenen Commit-Vergleich.
Sie haben den Terminalbefehl
Prüfen Sie das Skript, bevor Sie den Einstieg aus einem Ordnernamen ableiten:
npm pkg get scripts.report
rg -n 'process.argv|commands\[command\]' src/cli.mjs
rg -n 'report:|runReport|report-config.json' src/commands.mjs
Der erste Befehl gibt "node src/cli.mjs report" aus. In cli.mjs macht process.argv.slice(2) aus report den Befehlsnamen; die restlichen Argumente gehen an den ausgewählten Handler. commands[command] ist ein Tabellenzugriff, der Starter also erst die halbe Antwort. Öffnen Sie commands.mjs: Die Eigenschaft report ruft runReport mit Ladern für report-config.json und rows.json auf. Die benachbarte Eigenschaft help gibt stattdessen Hilfetext zurück.
Ändern Sie nun über denselben Starter genau eine Sache:
REPORT_LIMIT=4 npm run --silent report -- --limit 2
Dies gibt Report rows: 2 aus, danach Aster und Birch. npm leitet die Argumente hinter dem doppelten Bindestrich an das Skript weiter. Lesen Sie Tabellenzugriff und registrierten Handler, um den Pfad festzustellen; die veränderte Ausgabe prüft anschließend, dass dieser Befehl die Begrenzung erreicht. Die Ausgabe allein würde nicht jeden Aufruf auf dem Weg identifizieren.
Sie haben nur einen Ausgabetext
rg -n -F 'Report rows:' src tests
rg -n 'renderReport|runReport' src
Die erste Suche findet format-report.mjs und Test-Assertions. Der Formatierer erzeugt die Kopfzeile; die Tests vergleichen erwarteten Text. Keine dieser Trefferarten ist automatisch ein Befehlseinstieg. Prüfen Sie in report.mjs sowohl den Import von renderReport als auch den Aufruf, der die ausgewählten Namen übergibt. Verfolgen Sie anschließend runReport zurück zur report-Registrierung in commands.mjs und den ausgewählten Handler zurück zur stdout-Ausgabe in cli.mjs.
Damit haben Sie einen Pfad, den der obige Befehl prüfen kann. Wird Ihre echte Ausgabe aus mehreren Teilen zusammengesetzt, suchen Sie nach einem stabilen Fragment und lesen Sie den umgebenden Ausdruck. Hat ein gemeinsamer Formatierer mehrere Aufrufer, bleiben diese Kandidaten, bis Startargumente oder ein gezielter Test einen auswählen. Fassen Sie sie nicht zu einem einzigen Ausführungspfad zusammen.
Sie haben einen Testnamen
rg -n -F 'environment wins over config' tests
node --test --test-name-pattern='environment wins over config' tests/report.test.mjs
Der passende Test importiert runReport direkt und übergibt eine eigene Umgebung sowie eigene Konfigurations- und Zeilenlader. Er bestätigt, dass vier innerhalb dieser Funktion Vorrang vor sechs hat, startet aber weder cli.mjs noch liest er eine Konfigurationsdatei. Welche Datei ein echter Befehl lädt, zeigt er nicht.
Folgen Sie dem Import zum Verhalten und prüfen Sie dann die tatsächliche Bindung commands.report. Dieser separate Test überschreitet die bisher ungeprüfte Grenze:
node --test --test-name-pattern='CLI uses REPORT_LIMIT' tests/cli.test.mjs
Er startet die echte CLI in einem Kindprozess mit REPORT_LIMIT=4 und prüft Exitstatus und stdout. Beide ausgewählten Tests bestehen. Halten Sie den Unterschied fest: Der eine wählt seine Abhängigkeiten selbst, der andere prüft Start und Dateiadapter. Ein bestandener direkter Funktionstest ersetzt Letzteren nicht.
Sie haben den Konfigurationsnamen
rg -n -F 'REPORT_LIMIT' src tests
rg -n 'resolveLimit|loadConfig|report-config.json' src
Der Wert wird in limits.mjs gelesen; andere Treffer bereiten Testeingaben vor. Folgen Sie dem Aufruf von resolveLimit in report.mjs und prüfen Sie dann die von commands.report übergebene Funktion loadConfig. Ihr new URL(..., import.meta.url) wählt die enthaltene Fixture relativ zum Modul. Allein ein passender Dateiname beweist nicht, dass dieser Handler sie verwendet.
Damit haben Sie den Ausgangspunkt für die folgende Untersuchung des Werts. Lassen Sie help und unbeteiligte Tests beiseite: Befehl, ausgewählter Handler, tatsächliche Eingabedateien und Ausgabestelle sind geprüft. Nutzt der echte Befehl einen anderen Checkout, Wrapper oder eine andere Umgebungsquelle, klären Sie das vor einer breiteren Suche. Sonst geht es als Nächstes um die Priorität des Werts, nicht um das gesamte Repository.
Dem ausgewählten Handler folgen
Die Datei report.mjs des Übungsprojekts parst Argumente, lädt die Konfiguration, wählt eine Grenze und lädt Zeilen. Danach ergänzt renderReport die Kopfzeile. Die CLI schreibt die zurückgegebene Zeichenfolge. Das frühere, eigenständige Beispiel unten behält nur Datenzeilen bei, damit Sie den Wert ohne Befehlsverdrahtung untersuchen können. In beiden Versionen werden die Lader als Argumente übergeben; so können Tests die Reihenfolge ohne Dateizugriffe beobachten:
export async function runReport({ args, env, loadConfig, loadRows }) {
const { values } = parseArgs({
args,
options: { limit: { type: 'string' } },
strict: true,
});
const config = await loadConfig();
const limit = resolveLimit(values, env, config);
const rows = await loadRows();
return rows
.slice(0, limit)
.map((row) => row.name)
.join('\n');
}
Noch bevor Sie eine Hilfsfunktion öffnen, zeigt diese Funktion die Reihenfolge: Argumente parsen, Konfiguration laden, Grenze wählen, Zeilen laden, ausgeben. Sie zeigt auch ein leicht übersehenes Detail: resolveLimit() läuft nur, wenn loadConfig() erfolgreich ist. Das wird später wichtig.
Folgen Sie in echtem Code den Imports. Eine Funktion namens loadConfig kann eine JSON-Datei lesen, mehrere zusammenführen oder einen entfernten Dienst aufrufen. Der Name verrät nicht, was zutrifft.
Wie die Grenze gewählt wird
export function resolveLimit(cli, env, config) {
const raw = cli.limit ?? env.REPORT_LIMIT ?? config.limit ?? 10;
const limit = Number(raw);
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
throw new Error('limit must be an integer from 1 to 100');
}
return limit;
}
Hier passieren drei Dinge: Quelle wählen, in eine Zahl umwandeln, Wertebereich prüfen. Halten Sie sie in Ihren Notizen getrennt. Wer nur „Umgebung überschreibt Konfiguration“ notiert, nimmt leicht an, dass ein ungültiger Umgebungswert auf die Konfigurationsdatei zurückfällt. Dieser Code tut das nie.
CLI --limit |
REPORT_LIMIT |
Konfiguration limit |
Ergebnis |
|---|---|---|---|
2 |
4 |
6 |
2, aus der Befehlszeile |
| fehlt | 4 |
6 |
4, aus der Umgebung |
| fehlt | fehlt | 6 |
6, aus der Datei |
| fehlt | fehlt | fehlt | Standardwert 10 |
| fehlt | leere Zeichenfolge | 6 |
Validierungsfehler |
0 |
4 |
6 |
Validierungsfehler |
?? überspringt nur null und undefined. Eine leere Zeichenfolge zählt weiterhin als Wert, also gewinnt REPORT_LIMIT=''. Number('') ergibt 0, die Bereichsprüfung wirft einen Fehler, und die Funktion zieht den Konfigurationswert nicht mehr heran.
Ein Wechsel von ?? zu || würde leere Zeichenfolgen überspringen, aber auch 0 als „nicht gesetzt“ behandeln. Das ändert die Regeln und ist keine bloße Bereinigung. Entscheiden Sie zuerst über das gewünschte Verhalten: Soll ein absichtlich gesetzter ungültiger Wert einen Fehler auslösen oder still auf einen anderen Wert zurückfallen?
Dem Wert bis zur Verwendung folgen
Eine korrekte Grenze garantiert keine korrekte Ausgabe. Folgen Sie limit bis zu rows.slice(0, limit) und prüfen Sie dann, woher rows stammt.
Hier liefert loadRows() alle Zeilen zurück, und der Handler behält die ersten. An der Quelle wird weder sortiert noch gefiltert noch seitenweise geladen. Die Grenze beschränkt also die gedruckte Zeilenzahl, nicht die gelesene oder im Speicher gehaltene Datenmenge.
Das ist bei einer anderen Fehlermeldung entscheidend. Heißt es „Der Bericht verbraucht selbst mit --limit 2 zu viel Speicher“, sind die Prioritätsregeln die falsche Stelle. Sie würden loadRows prüfen und klären, ob die Grenze die Datenquelle erreichen muss. Gleicher Handler, andere Frage, andere nächste Datei.
Absichtlich etwas kaputtmachen
Versuchen Sie nun eine defekte Konfigurationsdatei zusammen mit --limit 2. Die Befehlszeile hat die höchste Priorität – müsste der Befehl dann nicht trotzdem funktionieren?
Tut er nicht. Die Konfiguration wird geladen, bevor die Grenze gewählt wird. Wirft loadConfig() einen Fehler, läuft danach nichts mehr. Priorität bestimmt, welcher Wert verwendet wird. Sie sagt nichts darüber aus, was vor dieser Auswahl erfolgreich sein muss.
Auch der umgekehrte Fall braucht einen Test: Die Konfiguration lädt, aber die gewählte Grenze ist ungültig. Dann dürfen die Zeilen gar nicht gelesen werden. Zählen Sie die Lesezugriffe:
let reads = 0;
await assert.rejects(
runReport({
args: [],
env: { REPORT_LIMIT: '' },
loadConfig: async () => ({ limit: 6 }),
loadRows: async () => {
reads++;
return [];
},
}),
/integer/,
);
assert.equal(reads, 0);
Würden Sie nur auf einen geworfenen Fehler prüfen, bestünde auch eine Variante, die erst alle Zeilen liest und dann die Grenze ablehnt. Erst reads === 0 legt die Reihenfolge fest.
Das Beispiel ausführen
Laden Sie configuration-flow.mjs herunter und führen Sie es mit Node.js 22 oder neuer aus. Es hat keine Abhängigkeiten:
node --test configuration-flow.mjs
Dieser ursprüngliche Download als einzelne Datei bleibt verfügbar. Seine sechs Tests decken Priorität, leere und ungültige Werte, Ausgabe, ausgelassene Zeilenlesevorgänge nach ungültiger Eingabe und die defekte Konfiguration ab. Sie prüfen die Funktionen isoliert. Im eigenen Projekt sollten Sie weiterhin prüfen, wie die echte CLI ihre Umgebung lädt und Fehler ausgibt.
Das neue Einstiegsprojekt prüft auch die Befehlsverdrahtung. Führen Sie in seinem Ordner Folgendes aus:
node --test tests/*.test.mjs
REPORT_LIMIT='' npm run --silent report
Alle neun Tests bestehen. Der zweite Befehl beendet sich absichtlich mit Status 1, schreibt limit must be an integer from 1 to 100 nach stderr und erzeugt keine stdout-Ausgabe. Der Unit-Test zählt zusätzlich die Zeilenlesevorgänge; damit prüft der Fall mit leerem Wert die beschriebene Reihenfolge. Die ZIP enthält ein SHA-256-Manifest; sowohl das Archiv als auch seine entpackten Tests wurden geprüft.
Die Erkenntnisse festhalten
Eine kurze Notiz genügt. Beim nächsten Mal lässt sie sich viel schneller lesen als der Code:
runReport
parseArgs → loadConfig → resolveLimit → loadRows → slice → output
limit: command line > environment > config > 10
invalid selected value: error; no fallback; rows are not loaded
config read failure: stops even when --limit is present
scope: limit bounds output, not the row loader's work
Ergänzen Sie in echtem Code zu jedem Schritt Datei und Zeile und nennen Sie offene Punkte: Welche Datei liest loadConfig, setzt ein Elternprozess REPORT_LIMIT, wie zeigt die CLI Fehler? Sobald Sie die vier Zeilen erklären und diese offenen Fragen benennen können, dürfen Sie vorerst aufhören. Den Rest des Projekts müssen Sie noch nicht verstehen.
Die Übergabe von einer anderen Person prüfen lassen
Eine Lesenotiz wird nützlicher, wenn jemand anderes sie ohne Ihren Gesprächsverlauf nachvollziehen kann. Probieren Sie die poster-desk-Übergabe-ZIP und die kurze Übergabebeschreibung. Dieses kleine Offline-Projekt und seine Aussagen wurden für Lehrzwecke erstellt; sie stammen weder aus einer tatsächlich KI-generierten App noch aus einer echten Übergabe.
Bei der Übernahme sind vier Zusagen anhand eines festen Quellstands zu prüfen: Lokale Karten lassen sich in einem neuen Prozess lesen, identische sequenzielle Wiederholungen verwenden denselben Datensatz, Belege sind nur Fixture-Vorschauen, und die Aufbewahrung beim Ersetzen einer Bereitstellung bleibt ungeklärt. „Implementiert“ gilt für die getesteten Bedingungen; sequenzielle Wiederholungen belegen keine Nebenläufigkeitssicherheit, der Mock sendet nichts, und das Paket liefert keine Belege zum Bereitstellungsspeicher.
Folgen Sie der Beschreibung, kopieren Sie RECEIVER.md außerhalb des entpackten Pakets und ergänzen Sie dort Befehle, Ergebnisse, Quellstellen und offene Fragen. Lassen Sie die gelieferte Vorlage unverändert und prüfen Sie anschließend das Originalpaket erneut.
SHA-256 der ZIP
c39d19b210c05f4667cd74db4934459a079408d14c5e7678c24cd087e1df5185
In Yenpo können Sie mit Zur Datei… und der Projektsuche Handler, Resolver und Tests nebeneinander geöffnet halten. Fragen Sie den KI-Codeleser gezielt nach einem Wert: „Verfolge REPORT_LIMIT vom Lesen bis zur Verwendung, mit Zeilenangaben.“ So erhalten Sie einen Schritt für Schritt prüfbaren Pfad statt einer weiteren Projektzusammenfassung.
Wählen Sie die nächste Leseaufgabe anhand Ihrer Belege:
- Eine überraschende Serverantwort: Verfolgen Sie eine HTTP-Anfrage von der Route bis zur Datenabfrage.
- Grüne Tests nach einem KI-Patch: Prüfen Sie, ob geänderte Assertions das ursprüngliche Verhalten weiterhin schützen.
- Eine scheinbar überflüssige Prüfung: Nutzen Sie Git-Historie und Regressionstests, um ihren Zweck zu verstehen.
- Eine Funktion soll geändert werden: Prüfen Sie, welche Verbraucher ausfallen, welche ihre Ergebnisse behalten und welche noch Tests brauchen.
- Ungetesteter Code soll umgebaut werden: Beobachten und sichern Sie sein aktuelles Verhalten vor Strukturänderungen.