robust, idempotent und mit eingebauter Qualitätsprüfung
Ein ETL-Skript, das beim ersten Testlauf funktioniert, aber beim zweiten Lauf doppelte Datensätze erzeugt oder bei einem Teilfehler die Zielquelle in einem inkonsistenten Zustand hinterlässt, ist keine Seltenheit. Claude hilft, diese Fallstricke von Anfang an zu vermeiden, indem es Idempotenz, Fehlerbehandlung und Datenqualitätsprüfungen direkt in die generierte Extraktions-, Transformations- und Ladelogik einbaut. Dieser Artikel zeigt den kompletten Entwicklungsprozess an einem realistischen Migrationsbeispiel.
Inhaltsverzeichnis
- 1. Warum ETL-Skripte selten beim ersten Wurf robust sind
- 2. Extraktion generieren lassen: Quellsysteme richtig beschreiben
- 3. Idempotenz von Anfang an mitdenken
- 4. Transformationslogik mit klaren Geschäftsregeln generieren
- 5. Fehlertoleranz: Teilfehler ohne Totalausfall behandeln
- 6. Datenqualitätsprüfungen direkt in die Pipeline einbauen
- 7. Einmalige Datenmigrationen versus wiederkehrende ETL-Jobs
- 8. ETL-Skripte gegen Testdaten verifizieren, bevor sie produktiv laufen
- 9. ETL-Ansätze im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum ETL-Skripte selten beim ersten Wurf robust sind
Ein typisches ETL-Skript entsteht oft unter Zeitdruck: Daten müssen von System A nach System B, die Logik wird schnell geschrieben, ein Testlauf mit einer Handvoll Zeilen funktioniert, das Skript geht in Produktion. Die eigentlichen Probleme zeigen sich erst später, wenn das Skript ein zweites Mal läuft und Duplikate erzeugt, wenn ein Netzwerkfehler mitten im Lauf die Zieltabelle in einem inkonsistenten Zustand zurücklässt, oder wenn ein unerwarteter NULL-Wert die gesamte Transformation zum Absturz bringt.
Claude kann diese Klasse von Problemen deutlich reduzieren, wenn man es nicht nur bittet, "Daten von A nach B zu bringen", sondern explizit nach Idempotenz, Fehlerbehandlung und Wiederanlauffähigkeit fragt. Der Unterschied zwischen einem naiven und einem robusten ETL-Skript liegt selten in der Kernlogik der Transformation, sondern in der Behandlung der Randfälle: Was passiert bei einem doppelten Lauf, was passiert bei einem Teilfehler, was passiert bei unerwarteten Datenformaten. Dieser Artikel zeigt, wie man diese Aspekte systematisch in den Prompt einbringt, statt sie nach einem Produktionsvorfall nachträglich zu reparieren.
2. Extraktion generieren lassen: Quellsysteme richtig beschreiben
Der Extraktionsschritt eines ETL-Skripts muss mit den Eigenheiten der Quelle umgehen können: Paginierung bei APIs, Rate-Limits, inkrementelle versus vollständige Extraktion. Claude generiert brauchbaren Extraktionscode, wenn man die Quelle konkret beschreibt, inklusive Antwortformat, Paginierungsmechanismus und bekannter Limits. Ohne diese Details neigt Claude dazu, eine naive Version zu schreiben, die bei der ersten Seite mit mehr als tausend Datensätzen abbricht oder das Rate-Limit der Quelle ignoriert.
Besonders wichtig bei wiederkehrenden ETL-Jobs ist die inkrementelle Extraktion: Statt bei jedem Lauf die komplette Quelle zu lesen, sollte nur der seit dem letzten Lauf veränderte Datenbestand abgefragt werden. Claude generiert diese Logik zuverlässig, wenn man ein Feld für den letzten erfolgreichen Extraktionszeitpunkt vorgibt und explizit nach einer inkrementellen Strategie fragt, statt implizit eine volle Extraktion zu erwarten.
# ETL extraction step, generated with explicit pagination and rate limit context
import time
import requests
from datetime import datetime, timezone
def extract_orders_incremental(since: datetime, api_key: str) -> list[dict]:
"""
Extracts orders modified since the given timestamp.
@param since Timestamp of the last successful extraction run.
@param api_key Authentication token for the source API.
@return List of order records as returned by the source system.
"""
orders: list[dict] = []
page = 1
while True:
response = requests.get(
"https://source-system.example/api/orders",
params={"modified_since": since.isoformat(), "page": page, "per_page": 200},
headers={"Authorization": f"Bearer {api_key}"},
timeout=30,
)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", "5"))
time.sleep(retry_after)
continue
response.raise_for_status()
payload = response.json()
orders.extend(payload["data"])
if not payload.get("has_more"):
break
page += 1
return orders
# Store the timestamp only after the full pipeline (extract+transform+load)
# succeeds, never right after extraction — a partial failure downstream
# must not advance the incremental watermark.
3. Idempotenz von Anfang an mitdenken
Idempotenz bedeutet, dass ein wiederholter Lauf desselben ETL-Skripts mit denselben Eingabedaten zum selben Endzustand führt, statt Duplikate zu erzeugen. Das ist die wichtigste Eigenschaft, die ein produktionstaugliches ETL-Skript braucht, weil Wiederholungen nach einem Fehler, einem Timeout oder einer manuellen Neuauslösung in der Praxis unvermeidlich sind. Ohne Idempotenz führt jeder erneute Lauf potenziell zu doppelten Zeilen im Ziel, was besonders bei Finanzdaten oder Bestandsdaten gravierende Folgen hat.
Claude setzt Idempotenz zuverlässig um, wenn man es explizit im Prompt einfordert, typischerweise über UPSERT-Operationen (INSERT ... ON DUPLICATE KEY UPDATE oder ON CONFLICT) statt reiner INSERT-Statements, oder über einen eindeutigen Business-Key, der vor dem Einfügen geprüft wird. Ohne diese explizite Anforderung generiert Claude in kurzen Beispiel-Snippets manchmal einfache INSERT-Logik, die bei einem zweiten Lauf Duplikate erzeugt.
-- WRONG: plain INSERT, creates duplicates on every re-run
-- INSERT INTO target_orders (order_ref, customer_id, total)
-- VALUES ('ORD-2026-0512', 4471, 89.90);
-- RIGHT: idempotent UPSERT using the natural business key
INSERT INTO target_orders (order_ref, customer_id, total, updated_at)
VALUES ('ORD-2026-0512', 4471, 89.90, NOW())
ON DUPLICATE KEY UPDATE
customer_id = VALUES(customer_id),
total = VALUES(total),
updated_at = VALUES(updated_at);
-- Requires a UNIQUE constraint on order_ref, established by the schema
-- For PostgreSQL targets, Claude generates the equivalent:
-- INSERT INTO target_orders (order_ref, customer_id, total, updated_at)
-- VALUES ('ORD-2026-0512', 4471, 89.90, NOW())
-- ON CONFLICT (order_ref) DO UPDATE SET
-- customer_id = EXCLUDED.customer_id,
-- total = EXCLUDED.total,
-- updated_at = EXCLUDED.updated_at;
4. Transformationslogik mit klaren Geschäftsregeln generieren
Die Transformationslogik eines ETL-Skripts ist der Ort, an dem fachliche Regeln umgesetzt werden: Preisberechnungen, Statusabbildungen zwischen zwei Systemen mit unterschiedlichen Enum-Werten, oder das Zusammenführen mehrerer Quellfelder in ein Zielformat. Claude generiert diese Logik am zuverlässigsten, wenn die Geschäftsregel als konkrete Beispieltabelle vorliegt, etwa "Status A wird zu Status X, Status B und C werden beide zu Status Y", statt als abstrakte Beschreibung.
Ein häufiger Fehler bei manuell geschriebenen Transformationen ist das Vergessen von Randfällen: Was passiert, wenn ein Quellstatus auftritt, der in keiner der bekannten Mapping-Regeln vorkommt? Claude generiert auf Nachfrage explizite else-Zweige, die einen unbekannten Wert klar protokollieren und in eine definierte Fallback-Kategorie einordnen, statt ihn stillschweigend zu verwerfen oder das Skript abstürzen zu lassen.
5. Fehlertoleranz: Teilfehler ohne Totalausfall behandeln
Bei der Verarbeitung tausender Datensätze führt ein einzelner fehlerhafter Datensatz, etwa ein unerwartetes Datumsformat oder ein fehlendes Pflichtfeld, in einer naiven Implementierung oft zum Abbruch der gesamten Verarbeitung. Ein robustes ETL-Skript fängt solche Einzelfehler ab, protokolliert den problematischen Datensatz mit ausreichend Kontext für eine spätere Analyse, und setzt die Verarbeitung der restlichen Datensätze fort. Claude generiert dieses Muster zuverlässig, wenn man explizit nach "graceful degradation pro Zeile statt Totalausfall" fragt.
Wichtig ist dabei die Balance: Nicht jeder Fehler sollte stillschweigend übersprungen werden. Ein fehlendes optionales Feld ist unkritisch, ein fehlender Primärschlüssel oder eine offensichtlich korrupte Zeile sollte den gesamten Lauf stoppen, weil sie auf ein strukturelles Problem in der Quelle hindeutet, das eine einzelne Fehlerbehandlung nicht lösen kann. Diese Unterscheidung sollte man Claude explizit mitgeben, damit die generierte Fehlerbehandlung weder zu nachsichtig noch zu strikt ausfällt.
# Row-level error tolerance: continue processing, log failures with context
from dataclasses import dataclass, field
@dataclass
class TransformResult:
"""Aggregates successful and failed rows from a transformation pass."""
succeeded: list[dict] = field(default_factory=list)
failed: list[dict] = field(default_factory=list)
def transform_batch(raw_records: list[dict]) -> TransformResult:
"""Transforms each record independently, isolating per-row failures."""
result = TransformResult()
for record in raw_records:
try:
transformed = {
"order_ref": record["order_ref"], # required, hard fail if missing
"customer_id": int(record["customer_id"]), # required
"total": round(float(record.get("total", 0)), 2), # optional, default 0
"notes": record.get("notes"), # optional, may be None
}
result.succeeded.append(transformed)
except (KeyError, ValueError, TypeError) as exc:
result.failed.append({"record": record, "error": str(exc)})
logger.warning(f"Skipping malformed record: {exc}", extra={"record": record})
if len(result.failed) > len(raw_records) * 0.1:
# More than 10% failures indicates a structural source problem —
# stop the whole run instead of silently accepting bad data quality
raise RuntimeError(f"{len(result.failed)} of {len(raw_records)} records failed, aborting run")
return result
6. Datenqualitätsprüfungen direkt in die Pipeline einbauen
Statt Datenqualität nachträglich per separatem Prüfskript zu kontrollieren, lohnt es sich, Qualitätsprüfungen direkt in das ETL-Skript zu integrieren. Claude generiert solche Prüfungen typischerweise als eigenständigen Schritt zwischen Transformation und Ladevorgang: Stimmt die Zeilenzahl mit der Erwartung überein, liegen numerische Werte in einem plausiblen Bereich, sind Pflichtfelder tatsächlich immer gefüllt. Schlägt eine dieser Prüfungen fehl, wird der Ladevorgang blockiert, statt fragwürdige Daten stillschweigend zu übernehmen.
Diese eingebauten Prüfungen sind besonders wertvoll bei wiederkehrenden Migrationen, weil sie Regressionen sofort sichtbar machen. Ändert sich beispielsweise unbemerkt das Antwortformat der Quell-API, bricht die Prüfung mit einer klaren Meldung ab, statt fehlerhafte oder unvollständige Daten in das Zielsystem zu schreiben. Der Aufwand für diese Prüfungen ist mit Claude gering, weil die Regeln aus der ohnehin vorhandenen Transformationslogik meist direkt ableitbar sind.
7. Einmalige Datenmigrationen versus wiederkehrende ETL-Jobs
Eine einmalige Datenmigration, etwa der Umzug eines Legacy-Shops in ein neues System, unterscheidet sich in wichtigen Punkten von einem wiederkehrenden ETL-Job. Bei einer einmaligen Migration ist Idempotenz zwar weiterhin sinnvoll für Testläufe, aber es gibt keinen Bedarf für inkrementelle Extraktion, dafür einen höheren Bedarf an ausführlicher Validierung vor dem finalen Cutover. Claude sollte im Prompt explizit wissen, um welche Art von Aufgabe es sich handelt, weil sich die generierte Struktur deutlich unterscheidet.
Bei einmaligen Migrationen empfiehlt sich zusätzlich ein Trockenlauf-Modus, bei dem alle Transformationen ausgeführt, aber keine Schreiboperationen an das Zielsystem gesendet werden. Claude kann diesen Modus als Flag in das generierte Skript einbauen, sodass ein vollständiger Testlauf mit echten Produktionsdaten möglich ist, ohne das Risiko, das Zielsystem versehentlich zu verändern, bevor der eigentliche Cutover ansteht.
8. ETL-Skripte gegen Testdaten verifizieren, bevor sie produktiv laufen
Jedes von Claude generierte ETL-Skript sollte vor dem produktiven Einsatz gegen eine repräsentative Stichprobe echter Daten getestet werden, inklusive bekannter Randfälle wie NULL-Werten, doppelten Schlüsseln und ungewöhnlichen Zeichen. Ein bewährtes Vorgehen: Claude bitten, zusätzlich zum eigentlichen ETL-Code auch Testfälle mit genau diesen Randfällen zu generieren, sodass die Fehlerbehandlung nicht nur behauptet, sondern nachweislich funktioniert.
Bei Migrationen mit hohem Risiko lohnt sich ein zweistufiges Vorgehen: Erst gegen eine anonymisierte Kopie der Produktionsdaten testen, dann einen begrenzten Teil der echten Daten migrieren und stichprobenartig gegen die Quelle validieren, bevor der vollständige Lauf gestartet wird. Diese Vorsicht kostet Zeit, verhindert aber die deutlich teureren Folgen einer fehlerhaften vollständigen Migration.
9. ETL-Ansätze im direkten Vergleich
Je nach Anforderung unterscheidet sich, welche Eigenschaften ein von Claude generiertes ETL-Skript zwingend haben sollte. Die folgende Übersicht ordnet typische Szenarien ein.
| Szenario | Naive Generierung | Mit expliziten Anforderungen | Kritische Eigenschaft |
|---|---|---|---|
| Wiederkehrender ETL-Job | Duplikate bei Re-Run | UPSERT, inkrementell | Idempotenz |
| Einmalige Migration | Volllauf ohne Trockenlauf | Dry-Run-Modus, Validierung vor Cutover | Sicherheit vor Cutover |
| Große Datenmengen | Abbruch bei einem Fehler | Row-Level-Fehlertoleranz | Fehlertoleranz |
| Kritische Finanzdaten | Stille Datenqualitätsfehler | Eingebaute Qualitätsprüfung | Datenqualität |
| Externe API als Quelle | Ignoriert Rate-Limits | Retry mit Backoff, Paginierung | Robustheit gegen Quelle |
In jedem dieser Szenarien liegt der Unterschied nicht in der Fähigkeit von Claude, den Kerncode zu schreiben, sondern darin, ob die kritische Eigenschaft explizit im Prompt gefordert wurde. Wer diese Anforderungen als feste Checkliste vor jeder ETL-Generierung durchgeht, reduziert die Zahl der Produktionsvorfälle erheblich.
Mironsoft
Datenmigrationen, ETL-Entwicklung und KI-gestützte Automatisierung
Datenmigration ohne böse Überraschungen?
Wir entwickeln robuste, idempotente ETL-Skripte mit eingebauten Qualitätsprüfungen und Fehlertoleranz, testen sie gegen reale Datenstichproben und begleiten den kompletten Cutover von der Extraktion bis zur Validierung im Zielsystem.
Idempotente Pipelines
UPSERT-basierte ETL-Skripte, die bei Wiederholung keine Duplikate erzeugen
Datenmigration
Legacy-System-Umzüge mit Dry-Run und schrittweiser Validierung
Qualitätsprüfungen
Automatisierte Checks direkt in der Transformationslogik
10. Zusammenfassung
ETL-Skripte mit Claude entwickeln funktioniert am zuverlässigsten, wenn kritische Eigenschaften wie Idempotenz, Fehlertoleranz und Datenqualitätsprüfung von Anfang an explizit eingefordert werden, statt implizit erwartet zu werden. UPSERT-Operationen statt reiner INSERTs verhindern Duplikate bei Wiederholungsläufen, row-level Fehlerbehandlung verhindert, dass ein einzelner fehlerhafter Datensatz die gesamte Verarbeitung stoppt, und eingebaute Qualitätsprüfungen machen Regressionen sofort sichtbar statt sie stillschweigend zu übernehmen.
Der Unterschied zwischen einmaligen Datenmigrationen und wiederkehrenden ETL-Jobs sollte im Prompt klar benannt werden, weil sich Anforderungen wie inkrementelle Extraktion oder Dry-Run-Modus grundlegend unterscheiden. Vor jedem produktiven Einsatz bleibt das Testen gegen reale Datenstichproben mit bekannten Randfällen der wichtigste Schritt, um Vertrauen in die generierte Pipeline aufzubauen.
ETL-Skripte mit Claude entwickeln — Das Wichtigste auf einen Blick
Idempotenz einfordern
UPSERT statt INSERT explizit im Prompt verlangen, um Duplikate bei Wiederholungsläufen zu vermeiden.
Row-Level-Fehlertoleranz
Einzelne fehlerhafte Datensätze isoliert behandeln, damit ein Fehler nicht den gesamten Lauf stoppt.
Qualität in die Pipeline
Prüfungen direkt zwischen Transformation und Ladevorgang einbauen, statt sie nachträglich separat auszuführen.
Migration versus Job unterscheiden
Einmalige Migrationen brauchen Dry-Run und Cutover-Validierung, wiederkehrende Jobs brauchen inkrementelle Extraktion.