Code-Qualität automatisieren: jscpd gegen Duplikation, cspell gegen Tippfehler

8 Minuten zum Lesen
Code-Qualität automatisieren: jscpd gegen Duplikation, cspell gegen Tippfehler
jscpd erkennt Code-Duplikate in .NET und Web, cspell findet Tippfehler und verbotene Begriffe. So integrierst du beides in deinen Dev-Workflow.

Du behebst einen Validierungsfehler, rollst den Fix aus, und zwei Wochen später meldet sich ein anderes Team: Ihre Version desselben Codes hat den Fehler noch. Copy-Paste statt Abstraktion, und schon steckst du im Maintenance-Albtraum. Ein ähnliches Problem auf einer anderen Ebene: Du öffnest den Kommentar einer Kollegin und siehst blacklist in der offiziellen API-Dokumentation. Nicht mehr zeitgemäß, und in manchen Unternehmensrichtlinien auch nicht mehr zulässig.

jscpd erkennt Code-Duplikate sprachübergreifend, cspell prüft Schreibweise und kann problematische Begriffe als Fehler markieren. Beide lassen sich in unter einer Stunde in einen bestehenden Workflow integrieren.

Wenn Copy-Paste zum Risiko wird

Code-Duplikation ist kein kosmetisches Problem. Identische Blöcke, die an mehreren Stellen gepflegt werden, steigern den Wartungsaufwand proportional zur Anzahl der Kopien. Laut DORA State of DevOps Report ist technische Schuld einer der meistgenannten Faktoren, der Teams in ihrer Liefergeschwindigkeit bremst.

Ein konkretes Szenario: Validierungs-Logik für E-Mail-Adressen ist an vier Stellen dupliziert: Einmal im User-Registration-Modul, einmal beim Checkout, einmal in der Admin-Oberfläche und einmal in einem Batch-Job. Ein RFC ändert die E-Mail-Validierungsregeln, aber nur zwei der vier Implementierungen werden aktualisiert. Das Ergebnis ist inkonsistentes Verhalten und ein Produktionsfehler, bei dem manche User sich nicht registrieren können, während andere bereits registrierte Adressen beim Checkout ablehnt werden.

Neben struktureller Duplikation ist Terminologie ein unterschätztes Thema. Tippfehler in Variablen- und Methodennamen erschweren die Suche und das Lesen. Veraltete oder nicht-inklusive Bezeichner wie blacklist, whitelist, master oder slave in öffentlichen APIs oder Kommentaren sind ein Compliance-Risiko. Die Inclusive Naming Initiative, unterstützt von Microsoft, Google und Red Hat, listet empfohlene Alternativen: blocklist und allowlist statt blacklist und whitelist, primary und replica statt master und slave.

Copy-Paste-Erkennung mit jscpd

jscpd nutzt token-basierte Analyse statt eines abstrakten Syntaxbaums. Das bedeutet, es erkennt Duplikate auch dann, wenn Variablennamen geändert wurden, und ist schnell genug für den CI-Einsatz. Das Tool unterstützt über 40 Sprachen, darunter C#, TypeScript, JavaScript, Python, Java, CSS, HTML und Markdown. Für gemischte Repos mit einem .NET-Backend und einem TypeScript-Frontend ist das ein klarer Vorteil gegenüber sprachspezifischen Tools.

Die wichtigsten Metriken sind der Duplikations-Prozentsatz, die Anzahl der Klone und die betroffenen Zeilen. Über --threshold legst du einen Prozentwert fest, ab dem jscpd mit Exit-Code 1 abbricht, was die Einrichtung eines CI-Blockers vereinfacht. Die Ausgabeformate console, html, json und markdown eignen sich je nach Verwendungszweck: JSON für Dashboards, HTML für lesbare Reports als CI-Artefakt und Markdown für Pull-Request-Kommentare.

jscpd in der Praxis

Für den ersten Test brauchst du keine globale Installation. Ein einmaliger Check für ein gemischtes .NET- und TypeScript-Repo sieht so aus:

# Einmaliger Check (CI oder lokal)
npx jscpd ./src \
  --languages "cs,ts,js,html,css" \
  --min-lines 8 \
  --min-tokens 70 \
  --threshold 5 \
  --reporters "console,html,json" \
  --output "./reports/jscpd" \
  --ignore "**/*.g.cs,**/Migrations/**,**/node_modules/**,**/wwwroot/lib/**,**/obj/**,**/bin/**"

Die Parameter --min-lines und --min-tokens steuern die Mindestgröße eines Duplikats. 8 Zeilen und 70 Token sind sinnvolle Startwerte, die generierte kurze Snippets herausfiltern, ohne echte Duplikate zu verstecken. Ab einer Duplikationsrate von 5 Prozent schlägt jscpd an.

Für den dauerhaften Einsatz empfiehlt sich eine .jscpd.json im Repo-Root, damit alle Entwickler und die CI dieselbe Konfiguration nutzen:

{
  "threshold": 5,
  "minLines": 8,
  "minTokens": 70,
  "reporters": ["console", "html", "json"],
  "output": "./reports/jscpd",
  "gitignore": true,
  "ignore": [
    "**/*.g.cs",
    "**/Migrations/**",
    "**/node_modules/**",
    "**/wwwroot/lib/**",
    "**/obj/**",
    "**/bin/**",
    "**/*.min.js",
    "**/*.min.css"
  ]
}

Generierter Code und minifizierte Assets sollten immer ausgeschlossen werden. In .NET sind das vor allem *.g.cs-Dateien (Source Generator Output) und Migrations-Ordner. Für legitime Duplikate, die aus gutem Grund existieren, gibt es Inline-Suppress-Kommentare: // jscpd:ignore-start und // jscpd:ignore-end klammern Codeblöcke aus der Analyse aus. Diese sollten sparsam verwendet und im Code Review kurz begründet werden.

Über die Einstellung gitignore, die standardmäßig an ist, kannst du sicherstellen, dass alle Pfade in deiner .gitignore automatisch von jscpd ausgeschlossen werden. Das verhindert, dass vergessene Ignorierregeln zu unerwarteten Duplikat-Berichten führen.

Spelling-Checks mit cspell

cspell ist ein Spelling-Checker, der Code versteht. Er prüft nicht nur Markdown und Kommentare, sondern auch Variablennamen, Strings, JSON, YAML und über 25 Programmiersprachen. Die VS Code Extension Code Spell Checker nutzt dieselbe Engine, mit über 10 Millionen Installationen.

Drei zentrale Konzepte sind für den praktischen Einsatz wichtig: words listet Begriffe, die als korrekt gelten sollen, etwa interne Produktnamen oder Fachbegriffe. ignoreWords überspringt Begriffe ohne Fehlermeldung. flagWords ist das Gegenteil: Begriffe auf dieser Liste werden immer als Fehler markiert, unabhängig vom Wörterbuch. Das macht flagWords zum richtigen Werkzeug für nicht-inklusive Terminologie und Compliance-Anforderungen.

Die Konfiguration liegt in einer cspell.json im Repo-Root. Ein Beispiel für ein gemischtes .NET- und TypeScript-Repo:

{
  "version": "0.2",
  "language": "en,de",
  "dictionaries": ["typescript", "dotnet", "company-terms"],
  "dictionaryDefinitions": [
    {
      "name": "company-terms",
      "path": "./cspell/company-words.txt",
      "addWords": true
    }
  ],
  "flagWords": [
    "blacklist",
    "whitelist",
    "master",
    "slave",
    "dummy",
    "sanity check"
  ],
  "ignorePaths": [
    "node_modules/**",
    "obj/**",
    "bin/**",
    "**/wwwroot/lib/**",
    "**/*.min.js",
    "package-lock.json"
  ],
  "overrides": [
    {
      "filename": "**/*.cs",
      "language": "en"
    }
  ],
  "useGitignore": true
}

Die Custom-Dictionary-Datei cspell/company-words.txt enthält einen Begriff pro Zeile: Produktnamen, interne Akronyme, Bibliotheksnamen wie NuGet oder TypeScript. Für Deutsch-Englisch-Mischrepos lohnt sich das Paket @cspell/dict-de-de. Das overrides-Array stellt sicher, dass C#-Dateien nur auf Englisch geprüft werden, was False Positives bei deutschen Kommentaren in reinen Code-Dateien vermeidet.

Für einzelne Ausnahmen gibt es Inline-Suppress: // cspell:ignore SpecialTerm auf Datei-Ebene oder direkt neben der Zeile mit dem unbekannten Begriff. Auch diese Suppressions sollten bewusst eingesetzt und im Review kurz begründet werden.

Von Duplikaten zu Shared Libraries

Der Report von jscpd zeigt, wo die größten Duplikat-Hotspots liegen. Nicht alles davon muss sofort behoben werden. Priorisiere nach Risiko und Änderungsfrequenz: Code, der sich regelmäßig ändert oder sicherheitsrelevant ist, hat die höchste Dringlichkeit.

Die klassischen Refactoring-Muster helfen hier weiter. Extract Method und Extract Class lösen lokale Duplikate innerhalb eines Projekts. Ein gemeinsames NuGet-Package oder ein npm-Scope-Package löst Duplikate über Repo-Grenzen hinweg. Wenn du mehr darüber erfahren möchtest, wie du solche internen Packages sauber aufbaust, schau in unserem Artikel: Private npm- und NuGet-Packages: Naming, Versioning, Feeds und Best Practices.

Manche Duplikation ist akzeptabel und sollte bewusst so bleiben. Test-Setup-Code, der in mehreren Testklassen gleich aussieht, ist oft besser dupliziert als über eine komplexe Basisklasse geteilt. Gleiches gilt für generierten Code und für explizit getrennte Bounded Contexts in Domain-Driven Design, wo dieselbe Datenstruktur in zwei Contexts unterschiedliche Bedeutung hat. Trage diese Ausnahmen im Ignore-Kommentar oder in der Konfiguration explizit ein, damit der Report nicht mit bekannten Einträgen verrauscht.

Fallen, Tuning und der Faktor Mensch

False Positives sind die größte Gefahr für die Akzeptanz beider Tools. Bei jscpd helfen vollständige Ignorierlisten von Anfang an: generierter Code, vendored JavaScript, minifizierte Assets und Migrations-Dateien. Bei cspell gehören alle Produktnamen, Fachbegriffe und Bibliotheksnamen ins Custom Dictionary, bevor der erste CI-Run läuft. Sonst steht ein Entwickler vor einem Report mit 300 Fehlern, von denen 290 False Positives sind.

Performance in CI ist selten ein Problem, lässt sich aber optimieren. Die cache-Konfiguration bei cspell analysiert nur Dateien, die sich seit dem letzten Run geändert haben. In großen Repos kann es sinnvoll sein, die Analyse auf geänderte Dateien oder Module zu beschränken, um die Feedback-Zeit zu verkürzen.

Für lokales Feedback vor dem Push eignen sich Pre-Commit Hooks via husky und lint-staged. So bemerkt ein Entwickler ein flagWord noch bevor der Commit im Remote-Repository landet, was angenehmer ist als ein fehlgeschlagener CI-Run.

Beim Rollout empfiehlt sich ein graduelles Vorgehen: Starte mit nicht-blockierenden Checks. Nach einem Sprint weißt du, wo die häufigsten False Positives liegen, und kannst die Konfiguration bereinigen. Erst danach schaltest du sie als echte Blocker scharf. Kläre außerdem frühzeitig, wer Ausnahmen für flagWords genehmigen darf und wie der Prozess aussieht. Ein undokumentierter Eskalationsweg führt dazu, dass Entwickler die CI schlicht umgehen.

Alternativen und ergänzende Tools

SonarQube bietet Duplikat-Erkennung als Teil einer umfangreicheren Quality-Gate-Plattform mit eigenem Server, Security Hotspots und Code Smells. Es ist deutlich mächtiger als jscpd, erfordert aber auch mehr Setup und Infrastruktur. Wenn du ohnehin eine solche Plattform aufbaust oder bereits hast, ist SonarQube eine natürliche Erweiterung. Für den schnellen Start ohne eigene Server-Infrastruktur ist jscpd die pragmatischere Wahl.

Auf der Spelling-Seite ergänzt Vale cspell sinnvoll, wenn Dokumentationsqualität ein eigenes Ziel ist. Vale ist ein Prose-Linter speziell für Markdown und technische Dokumentation, konfigurierbar mit bekannten Style Guides wie dem Microsoft Writing Style Guide. LanguageTool deckt Grammatik und Stil für Deutsch und Englisch ab, ist aber primär über eine API oder ein IDE-Plugin nutzbar und weniger auf CI ausgelegt.

Die Entscheidung ist oft eine Frage des Aufwands: jscpd und cspell sind in unter 30 Minuten in einen bestehenden Workflow integriert, brauchen keine externe Infrastruktur und liefern ab dem ersten Run verwertbare Ergebnisse. SonarQube und Vale skalieren besser, wenn die Anforderungen wachsen.

Heute anfangen

jscpd und cspell machen sichtbar, was sonst unsichtbar bleibt: Duplikat-Hotspots, die beim nächsten Bugfix wieder zuschlagen, und Bezeichner, die ein Code Review nie kommentiert hätte. Der beste Einstieg ist ein einmaliger Baseline-Report ohne Blocker:

# 1. Baseline-Report generieren (jscpd)
npx jscpd ./src --reporters "console,html,json" --output ./reports/jscpd

# 2. Spelling-Check starten (cspell)
npx cspell "**/*.{ts,js,cs,md}" --show-suggestions

# 3. flagWords sofort aktivieren
echo '{"flagWords":["blacklist","whitelist","master","slave"]}' > cspell.json

Nimm dir die fünf größten Duplikat-Hotspots aus dem jscpd-Report und lege dafür Tech-Debt-Tickets an. Dann richte cspell mit flagWords und einem Custom Dictionary ein und füge beide Tools als nicht-blockierende CI-Jobs hinzu. Nach einem Sprint hast du eine saubere Konfiguration und kannst sie als echten Blocker aktivieren.

Den Duplikations-Prozentsatz und die Anzahl der cspell-Fehler als Trend zu tracken ist wichtiger als auf einen absoluten Zielwert zu zielen. Wenn der Prozentsatz nach einem Refactoring sinkt, ist das ein direktes Signal, dass die Arbeit sich lohnt.

Wie setzt du jscpd und cspell in deinem Projekt ein, und welche Erfahrungen hast du mit Code-Qualitätschecks gemacht? Schau gerne bei uns auf LinkedIn vorbei und diskutiere mit.

Autoren

Florian Bader

Florian Bader

Florian ist Solution Architect und Microsoft Most Valuable Professional (MVP) für Azure IoT und DevOps mit langjähriger Erfahrung im Bereich DevOps, Cloud und Digitalisierung. Er unterstützt Unternehmen dabei, effiziente und effektive Lösungen zu entwickeln, die ihre digitalen Projekte nachhaltig zum Erfolg führen.