Copy‑Paste‑Code sicher entfernen mit gemeinsamen Hilfsfunktionen
Entferne Copy‑Paste‑Code sicher mit einem kleinen Schritt‑Refactorplan, der KI‑generierte Funktionen dedupliziert, das Verhalten beibehält und jede Änderung verifiziert.

Was „Copy‑Paste‑Code" im Laufe der Zeit bricht
Copy‑Paste‑Code liegt vor, wenn dieselbe Logik an mehreren Stellen auftaucht mit kleinen Änderungen: ein umbenanntes Feld, ein anderer Default‑Wert, eine leicht veränderte Fehlermeldung. Anfangs fühlt sich das schneller an als eine wiederverwendbare Funktion zu schreiben. Monate später wird es zur Falle.
In realen Projekten sieht das oft so aus: derselbe Request‑Helper in drei Dateien, fünf Varianten von „Datum formatieren" oder zwei fast identische Auth‑Prüfungen, die sich nicht darauf einigen, was "eingeloggt" bedeutet. Der Code funktioniert — bis eine Kopie gefixt wird und die anderen nicht.
KI‑generierte Apps neigen dazu, Logik zu duplizieren, weil das Modell jedes Mal das Problem vor sich löst. Bitte um eine neue Seite und es kann Helper neu erstellen statt bestehende wiederzuverwenden. Tools, die Code in Stößen erzeugen (Seite für Seite, Feature für Feature), machen Wiederholungen noch wahrscheinlicher. Am Ende hast du eine Codebasis voller Near‑Clones, die zwar konsistent aussehen, es aber nicht sind.
Duplikation erhöht Bugs und verlangsamt Änderungen auf einfache Weise: jede Änderung wird zur mehrdateiigen Suchaktion. Verpasst du eine Kopie, lieferst du inkonsistentes Verhalten. Die Unterschiede sind bei Reviews leicht zu übersehen, weil sie oft klein sind.
Wenn Leute sagen „verhaltensidentisch“, meinen sie meist „Nutzer sollen nichts merken“. Das umfasst die offensichtliche Ausgabe, aber auch Details, die oft vergessen werden:
- Dieselben Inputs führen zu denselben Outputs, inklusive Randfällen.
- Dieselben Fehler treten in denselben Situationen auf, mit denselben Meldungen oder Codes.
- Dieselbe Seiteneffekte passieren (Logging, Caching, Retries, Datenbank‑Schreibvorgänge).
- Zeitkritisches Verhalten bleibt innerhalb der erwarteten Grenzen.
Wenn du Copy‑Paste‑Code sicher entfernst, ist das eigentliche Ziel nicht „schönerer Code“, sondern „keine Überraschungen in Produktion".
Wähle ein gutes erstes Deduplizierungsziel
Beginne mit einem kleinen Bereich, der leicht beobachtbar und schwer misszuverstehen ist. Gute frühe Ziele sind Helper‑Patterns wie Input‑Validierung, Datums‑ oder Währungsformatierung, wiederkehrende Auth‑Prüfungen oder derselbe API‑Call‑Wrapper, der über Screens hinweg benutzt wird.
Wähle etwas, das größtenteils genau eine Aufgabe macht und das du in einem Satz beschreiben kannst. Brauchst du drei Absätze, um es zu erklären, ist es kein erstes Ziel.
Bevor du irgendetwas anfasst, definiere die Grenzen. Schreib auf, was rein darf (Inputs), was rauskommt (Outputs) und was sonst betroffen ist (Seiteneffekte wie Logging, Caching, Env‑Reads oder das Verändern eines geteilten Objekts). KI‑generierte Funktionen verstecken Seiteneffekte oft an kleinen Stellen, wie „hilfreichen" Defaults oder stillem Fehler‑Verschlucken.
Ein solides Kandidatenprofil:
- Taucht in 3+ Stellen mit größtenteils denselben Zeilen auf
- Hat klare Inputs und Outputs (wenige globale Reads)
- Scheitert sichtbar (Fehler, Statuscode, Meldung)
- Liegt nicht auf deinem kritischsten Pfad (Payments, Kern‑Auth, Migrations)
- Hat auflistbare Randfälle ohne Raten
Nimm dann ein Inventar der Duplikate vor. Verlass dich nicht nur auf Suchergebnisse. Öffne jede Datei und bestätige, dass es wirklich dieselbe Aufgabe ist, nicht nur ähnlich aussehender Code.
Entscheide schließlich, was auf keinen Fall geändert werden darf. Sei konkret: exakte Fehlertypen, exakte Meldungen (wenn Nutzer oder Tests davon abhängen), wie leere Werte behandelt werden und seltsame Randfall‑Verhalten. Hier verstecken sich Überraschungen: eine Kopie trimmt Whitespace, die andere nicht — plötzlich schlägt ein Login für eine kleine Nutzergruppe fehl.
Verhalten sichern, bevor du etwas änderst
Du brauchst Belege dafür, was der Code heute tut — inklusive der merkwürdigen Teile, die du vielleicht lieber anders hättest. Ohne diese Basis kann ein „einfacher Cleanup" still und heimlich Ausgaben, Fehlermeldungen oder Randfälle ändern, auf die Caller angewiesen sind.
Beginne damit, reale Beispiele für Inputs und Outputs zu erfassen. Zieh ein paar aus Logs, Support‑Tickets oder eigenen manuellen Durchläufen. Wenn es keine Logs gibt, starte die App und protokolliere ein paar echte Requests oder User‑Flows (was du eingegeben hast, was du gesehen hast und was das System zurückgab).
Schreibe auch erwartete Fehler auf. Viele KI‑generierte Helper unterscheiden sich hauptsächlich darin, wie sie fehlschlagen: eine Version gibt ein leeres Objekt zurück, eine andere wirft, eine dritte liefert 200 mit einem Error‑String. Diese Unterschiede sind wichtig, wenn andere Teile der App darauf aufgebaut sind.
Erstelle eine kleine Menge „goldener" Szenarien, die du nach jeder kleinen Änderung wieder ausführen kannst. Halte es kurz und realistisch:
- Ein häufiger Input, der erfolgreich sein sollte
- Ein Grenzfall (leer, langer String, Null, fehlendes Feld)
- Ein bekannter schlechter Input, der einen spezifischen Fehler auslösen soll
- Ein Szenario, in dem optionale Flags oder Header das Verhalten ändern
- Ein performance‑sensitiver Fall (große Payload oder wiederholte Schleife)
Notiere auch versteckte Abhängigkeiten, die das Verhalten zwischen Runs inkonsistent machen können: Environment‑Variablen, Feature‑Flags, aktuelle Zeit und Timezones, zufällige IDs, globale Caches und Netzwerkanfragen.
Beispiel: Wenn drei duplizierte Request‑Helper alle einen Auth‑Header setzen, prüfe, ob sie sich unterscheiden, wenn das Token fehlt (throw vs return null) und ob sie das Token aus einem Global, localStorage oder einer Env‑Var lesen. Genau dieses Verhalten musst du beim Deduplizieren bewahren.
Duplikate und ihre kleinen Unterschiede kartieren
Copy‑Paste‑Code stimmt selten perfekt überein. Bevor du etwas zusammenführst, erstelle eine klare Karte dessen, was gemeinsam ist und was sich zwischen den Versionen heimlich geändert hat.
Leg die duplizierten Funktionen nebeneinander und vergleiche sie Zeile für Zeile. Nicht nur drüberscrollen. KI‑generierter Code ändert oft eine kleine Sache (Default‑Wert, Header‑Name, fehlende Null‑Prüfung), die erst in Produktion auffällt.
Die meisten Unterschiede fallen in einige Kategorien:
- Naming und Form (Parameter‑Namen, Rückgabefelder, Fehlermeldungen)
- Defaults (Timeouts, Retries, leere String vs null, Fallback‑Werte)
- Randfall‑Handling (fehlende Felder, 204‑Responses, undefined Env‑Vars)
- Seiteneffekte (Logging, Metriken, Caching, Schreiben in Storage)
- Ein‑/Ausgabe‑Formatierung (Trimmen, Encoding, Datums‑Parsing)
Wenn du die Unterschiede gelistet hast, wähle eine Version als Baseline. „Baseline" heißt nicht „am besten geschrieben“, sondern „am meisten von der App genutzt" — oft die Version mit den meisten Aufrufen oder die mit dem Verhalten, das Nutzer aktuell erleben.
Entscheide dann, welche Unterschiede absichtlich und welche zufällig sind. Wenn ein Unterschied dokumentiert, getestet oder klar für einen bestimmten Caller nötig ist, behandel ihn als absichtlich. Sieht es nach zufälliger Variation aus (anderer Default‑Timeout ohne Grund, inkonsistente Error‑Mapping, etwas andere Header‑Casing), geh zunächst davon aus, dass es zufällig ist.
Konkretes Beispiel: Zwei Request‑Helper setzen beide einen Authorization‑Header, aber nur einer sendet zusätzlich Cookies. Diese eine Zeile Unterschied kann in Produktion verändern, wer als „eingeloggt" gilt.
Entwerfe eine gemeinsame Utility, die einfach bleibt
Die gemeinsame Utility sollte fast langweilig wirken. Version eins geht nicht um „besseres Design“, sondern darum, das gleiche Verhalten an einem Ort zu haben.
Halte die Oberfläche klein
Beginne mit der kleinsten Einheit, die sich wiederholt. Wenn fünf Funktionen dieselben Inputs normalisieren oder dasselbe Payload bauen, extrahiere nur diesen Teil. Lass Validierungsregeln, Retries und Sonderfälle dort, bis du bewiesen hast, dass sie wirklich geteilt werden.
Eine kleine Utility ist leichter zu reviewen, weil es weniger Wege gibt, das Verhalten versehentlich zu ändern. Anzeichen, dass du klein geblieben bist:
- Der Name beschreibt eine einzige Aktion.
- Sie nimmt explizite Inputs und gibt einen Wert zurück.
- Sie kennt nichts über Routen, Screens oder Datenbanktabellen.
- Sie vermeidet versteckte Defaults, die raten, was du meintest.
Bevorzuge explizite Parameter statt Globals
KI‑generierter Code greift oft in Globals, Env‑Variablen oder geteilte Singletons. Das macht Deduplizieren riskant, weil jeder Caller auf leicht unterschiedliche versteckte Zustände angewiesen sein kann.
Gib stattdessen alles als Parameter mit. Zum Beispiel baseUrl, headers oder timezone. Das mag anfangs repetitiv wirken, aber es macht Unterschiede sichtbar und hält die gemeinsame Logik ehrlich.
Seiteneffekte beim Caller lassen
Wenn der duplizierte Code logged, in die DB schreibt oder Analytics triggert, halte diese Seiteneffekte möglichst außerhalb der gemeinsamen Utility. Strebe „bei gegebenen Inputs, liefere Outputs" an. Der Caller entscheidet, ob geloggt, gespeichert oder ein Fehler verschluckt wird.
Beispiel: Wenn drei Endpunkte eine Fehlermeldung bauen und gleichzeitig loggen, extrahiere nur buildErrorMessage(details). Jeder Endpunkt behält eigenes Logging, damit du nicht versehentlich Log‑Mengen oder Timing änderst.
Schritt‑für‑Schritt: kleine, verifizierte Refactor‑Züge
Behandle das als Serie kleiner Swaps, nicht als großen Rewrite. Du willst das heutige Verhalten, nur an einem anderen Ort.
-
Wähle eine „Source of truth"‑Duplikat.
-
Kopiere diese Implementierung genau so in eine neue gemeinsame Datei. Bereinige noch nicht. Keine Umbenennungen, kein Formatieren, kein „während ich hier bin"‑Änderungen.
-
Migriere in kleinen Schritten:
- Erstelle die gemeinsame Utility, indem du eine bestehende Funktion 1:1 kopierst, und lass die alten Duplikate an Ort und Stelle.
- Aktualisiere eine einzelne Aufrufstelle, damit sie die neue Utility verwendet.
- Führe deine goldenen Szenarien aus und vergleiche Outputs, Logs und Seiteneffekte.
- Wiederhole: verschiebe eine weitere Aufrufstelle, teste erneut.
- Nachdem jede Aufrufstelle die Utility nutzt, lösche die alten Duplikate und entferne ungenutzte Imports.
Zwischen jedem Schritt halte einen sauberen, reviewbaren Commit. Wenn etwas kaputt geht, kannst du einen kleinen Schritt zurücksetzen statt in einem riesigen Diff nach dem Fehler zu suchen.
Wenn Aufrufstellen leicht unterschiedliche Argumentformen haben, nutze einen temporären Kompatibilitäts‑Wrapper. Halte die unordentliche Konvertierung am Rand (nahe der Aufrufstelle), nicht innerhalb der gemeinsamen Utility.
Wie du verifizierst, dass das Verhalten identisch bleibt
Der sicherste Weg zu beweisen, dass sich nichts geändert hat, ist, beide Pfade kurzfristig parallel zu lassen und mit denselben Inputs zu vergleichen.
Sichere 10–20 reale Inputs, die du wieder abspielen kannst (Fixtures aus Logs sind ideal). Lauf sie durch die alte Funktion und die neue Utility in derselben Reihenfolge und vergleiche die Resultate — inklusive Form und Typen, nicht nur Werte.
Hör nicht beim Happy Path auf. Viele Fehler zeigen sich nur bei Fehlschlägen. Vergleiche:
- Fehlermeldungen und Fehlertypen (inklusive Wortlaut, falls die UI ihn anzeigt)
- Statuscodes für API‑Antworten (200 vs 204 vs 404 ist relevant)
- Behandlung von leer, null und missing ("" vs null vs undefined)
- Reihenfolge von Items (besonders wenn du sortierst oder Keys mapst)
- Seiteneffekte wie Logging, Retries oder Caching
Wenn Unterschiede schwer zu finden sind, füge eine temporäre Debug‑Option hinzu, die beide Pfade laufen lässt und einen kompakten Diff ausgibt, wenn sie abweichen. Entferne die Option, sobald die Migration abgeschlossen ist.
Beobachte schließlich die Performance in den gängigen Pfaden. Zeit ein paar typische Aufrufe vor und nachher und bestätige, dass du keine zusätzlichen DB‑Queries, wiederholtes JSON‑Parsen oder unnötige Netzwerkanfragen eingeführt hast.
Häufige Fallen, die Verhalten ändern, ohne dass man es merkt
Die meisten Refactors scheitern aus einem banalen Grund: Du hast zwei Dinge gleichzeitig geändert. Halte „gleiche Ausgabe für den gleichen Input" als Ziel, nicht „schönerer Code".
Diese Fallen kommen oft vor:
- Defaults unbewusst ändern (
timeout = 0vstimeout = 30, oderundefinedwird anders behandelt alsnull) - Typkonvertierung verlieren (String "0" wird Zahl 0, fehlendes Feld wird leerer String)
- Fehlerbehandlung „aufräumen" und dadurch Fehler verbergen (ein geworfener Fehler wird zu einer geloggten Warnung)
- Eine gemeinsame Utility bauen, die alles macht (sie wächst mit Flags, Sonderfällen und verstecktem Zustand)
- 9 Aufrufstellen migrieren und die 10. vergessen (oft ein Background‑Job, Admin‑Screen oder selten genutzter Endpoint)
Beispiel: Zwei Request‑Helper machen Retries bei 500er Fehlern, aber nur einer retried bei 429 Rate‑Limits. Wenn du ohne Beachtung zusammenführst, kann ein Checkout‑Flow langsamer werden oder bei Traffic‑Spitzen fehlschlagen.
Kurze Checkliste vor dem Merge des Refactors
Bevor du merge, willst du Duplikation entfernen, ohne das Verhalten der App zu verändern.
Checks fürs Utility‑Design
- Der neue Helper macht genau eine klare Aufgabe. Tut er zwei Dinge, teile ihn, solange er noch klein ist.
- Inputs und Outputs sind vorhersehbar. Vermeide magische Defaults, die vom globalen Zustand abhängen.
- Funktionsname und Parameter entsprechen den Begriffen, die Leute bereits verwenden.
- Er liest nicht intern Env‑Variablen, Dateien, Request‑Context oder User‑Session‑Daten. Werte werden reingereicht.
- Fehler werden genauso gehandhabt wie vorher (Typ, Form der Meldung, Statuscode wenn relevant).
Wenn du unsicher bist, vergleiche, was der alte Code tatsächlich produziert hat, nicht das, was du dir wünschst, dass er produziert.
Merge‑Readiness‑Checks
- Jede Aufrufstelle ist migriert. Keine halb verschobenen Dateien verwenden noch ein altes Duplikat.
- Alte Kopien sind entfernt (oder klar zur sofortigen Entfernung markiert), damit sie nicht weiter auseinanderdriften.
- Gold‑Szenarien bestehen, inklusive mindestens eines unhappy path (schlechte Eingabe, fehlendes Feld, Timeout).
- Logs, Metriken und Fehlermeldungen sind nicht lauter oder leiser geworden in einer Weise, die Debugging verwirrt.
- Ein kurzer Diff‑Scan zeigt, dass keine Secrets oder Tokens versehentlich in die gemeinsame Utility gewandert sind.
Beispiel‑Szenario: Deduplizieren von KI‑generierten Request‑Helpern
Du übernimmst eine App, in der ein KI‑Tool drei ähnliche Helper produziert hat: getJson(), postJson() und requestWithRetry(). Jede „funktioniert", aber jeder Endpoint ruft leicht unterschiedliche Varianten auf und Bugs tauchen nur in Produktion auf.
Im Vergleich erkennst du relevante Unterschiede: Header (Bearer vs API‑Key, Groß-/Kleinschreibung), Timeouts (5 Sekunden vs 5000 ms), Retry‑Regeln (nur Netzwerk vs auch 503) und Error‑Mapping ({ ok: false } vs throw vs in message wrappen).
Statt eine „beste" Version durchzudrücken, baue einen gemeinsamen Request‑Builder mit expliziten Inputs wie makeRequest({ method, url, headers, timeoutMs, retryPolicy, mapError }). Der Schlüssel: Defaults müssen das aktuelle Verhalten der ersten migrierten Endpoint‑Version widerspiegeln, nicht das, was du dir wünschst.
Migriere zuerst einen Endpoint, idealerweise einen einfachen, der trotzdem Auth und Error‑Handling trifft. Lass den alten Helper für alles andere stehen.
Deine goldenen Szenarien fangen subtile Änderungen schnell ein. Beispiel: Ein Endpoint, der 204 No Content zurückgibt, lieferte früher null, aber der neue Helper versucht json() und wirft. Das Szenario schlägt sofort fehl und du behebst es, indem du 204 explizit behandelst.
Nächste Schritte, wenn die Codebasis zu verknotet ist
Manchmal kannst du nicht sicher deduplizieren, weil die Duplikate tiefere Probleme verbergen. Wenn jede „ähnliche" Funktion andere Seiteneffekte, Fehlerbehandlung oder stille Korrekturen hat, kann eine gemeinsame Utility echte User‑Flows kaputtmachen.
Eine Pause hilft oft, aber sie muss kein Rewrite sein. Ziel ist ein kurzer Reset, der kleine‑Schritt‑Refactors wieder möglich macht.
Signale, dass du während des Refactors Sicherheit härten solltest
Wenn du gemeinsamen Code anfässt, behandle diese Checks als nicht verhandelbar:
- Authentifizierung ist inkonsistent (einige Routen prüfen Auth, andere vergessen es)
- Secrets sind exponiert (API‑Keys im Code, in Logs oder im Client‑Bundle)
- User‑Input landet in SQL oder Queries ohne strenge Validierung (Injection‑Risiko)
- "Admin"‑Logik hängt von einem Frontend‑Flag oder einer schwachen Rollenprüfung ab
- Fehlermeldungen leaken interne Details (Stacktraces, Tabellennamen)
Duplikate zu fixen, ohne das zu adressieren, kann das Risiko in deine neue Utility tragen.
Momentum halten ohne die ganze App neu zu schreiben
Ziele zuerst eine dünne Stabilisierungsschicht: eine kleine Menge gemeinsamer Helper mit engen Regeln und Tests oder Snapshots für die heißesten Flows. Entferne dann Duplikate Cluster für Cluster (zuerst alle Request‑Helper, dann alle Auth‑Checks). Wenn ein Modul zu chaotisch ist, kapsle es hinter einer einfachen Schnittstelle ab und verschiebe die interne Aufräumarbeit, bis der Rest der App stabil ist.
Wenn du ein kaputtes KI‑generiertes Prototype von Tools wie Lovable, Bolt, v0, Cursor oder Replit übernommen hast, kann ein externer Audit Tage an Rätselraten sparen. FixMyMess (fixmymess.ai) beginnt mit einem kostenlosen Code‑Audit, um Duplikate, versteckte Verhaltensunterschiede und Sicherheitsprobleme aufzudecken und hilft dann, das Prototype produktionsreif zu machen, ohne das Verhalten zu verändern.
Häufige Fragen
What exactly counts as “copy-paste code"?
Copy‑Paste‑Code ist dieselbe Logik, die in mehreren Dateien dupliziert wurde, oft mit kleinen, leicht zu übersehenden Unterschieden. Anfangs funktioniert alles, aber mit der Zeit landen Fixes nur in einer Kopie, die anderen bleiben zurück, sodass das Verhalten sich verschiebt und Fehler auftreten.
Why do AI-generated apps end up with so many duplicates?
Weil das Modell meist das Problem löst, das gerade vor ihm steht, erzeugt es oft Helper neu, statt vorhandene wiederzuverwenden. Wenn Features Seite für Seite generiert werden, entstehen fast identische Kopien, die zwar einheitlich aussehen, aber Randfälle, Defaults oder Fehler unterschiedlich behandeln.
What’s the best first thing to deduplicate?
Beginne mit etwas Kleinem, Sichtbarem und leicht Beschreibbarem, zum Beispiel Input‑Validierung, Formatierung, ein API‑Request‑Helper oder eine wiederkehrende Auth‑Prüfung. Meide zuerst die kritischsten Pfade, damit du die Refactor‑Methode mit geringem Risiko lernen kannst.
What should I document before I touch any duplicated code?
Notiere für jede Duplikatfassung vor Änderungen die Eingaben, Ausgaben und Seiteneffekte. Erwähne dabei oft vergessene Details wie genaue Fehlermeldungen, Statuscodes, Logging, Caching und die Behandlung leerer Werte.
What are “golden scenarios,” and how many do I need?
Das sind wenige reale Szenarien, die du nach jeder kleinen Änderung neu ausführen kannst, um zu beweisen, dass sich nichts geändert hat. Praktisch: ein häufiger Erfolgsfall, ein Randfall, ein bekannter Fehler mit einer konkreten Fehlermeldung und ein Fall, der sich durch Flags, Header oder Umgebung ändert.
How do I choose the “baseline” duplicate to merge into a shared utility?
Wähle die Version, auf die der Rest der App sich am meisten verlässt — nicht die hübscheste. Die Baseline ist oft die, die am häufigsten verwendet wird oder die das Verhalten zeigt, das Nutzer aktuell erleben. Dieses Verhalten erhältst du zuerst.
What’s the safest way to migrate call sites to the new utility?
In kleinen Schritten: Kopiere die Baseline‑Implementierung in eine gemeinsame Datei, bereinige sie noch nicht, migriere eine Aufrufstelle und führe deine Gold‑Szenarien aus. Kleine, rückgängig machbare Commits sind der beste Schutz gegen unbeabsichtigte Verhaltensänderungen.
Why do refactors often break error handling even when output “looks the same"?
Weil Refactors oft das Fehlerverhalten ändern, ohne dass jemand es bemerkt — etwa durch Unterschiede zwischen geworfenen Fehlern und zurückgegebenen Objekten oder durch Unterschiede bei 204/Empty‑Antworten. Dein neuer Helper muss exakt die Fehlerarten, Meldungen und die Behandlung leerer Werte bewahren, von denen die Aufrufer abhängen.
Where should logging, retries, and other side effects live after deduplication?
Bewahre Seiteneffekte wie Logging, Metriken, Speicherzugriffe und Analytics möglichst in der aufrufenden Stelle, damit die gemeinsame Utility vorhersehbar bleibt. So verhinderst du Überraschungen wie plötzlich doppelt so viele Logs oder geänderte Retry‑Zeitpunkte.
When should I stop refactoring and get an outside audit or help?
Wenn die Duplikate Sicherheitsprobleme verbergen — inkonsistente Auth‑Prüfungen, exponierte Secrets oder unsichere Eingaben — kann das Zusammenführen das Risiko bündeln. Hol Hilfe, wenn du solche Probleme findest; ein externes Audit kann viel Zeit sparen.