Anfang 2022 traf ich eine stille Entscheidung: Ich hörte auf, Unit Tests für die meisten WordPress- und Node-Projekte bei Seahawk zu schreiben. Nicht laut. Kein Blogpost darüber. Ich hörte einfach... auf. Die Begründung war solide, dachte ich -- wir shippen 15 bis 20 Client-Seiten pro Monat, ich hatte drei andere Entwickler im Einsatz, und die Tests, die ich schrieb, wirten wie Dokumentation, die niemand las. Manuelle QA fing die echten Bugs. Tests waren Theater.WordPress and Node projects coming through Seahawk. Not loudly. No blog post about it. I just... stopped. The justification was sound, I thought -- we were shipping 15 to 20 client sites a month, I had three other developers on rotation, and the tests I was writing felt like documentation nobody read. Manual QA caught the real bugs. Tests were theatre.
Vorspulen zu Ende 2023. GitHub Copilot war etwa acht Monate in meinem Editor. Ich hatte auch angefangen, Cursor nebenbei für alles Greenfield zu nutzen. Die Geschwindigkeit war wirklich bemerkenswert. Aber etwas passierte. Bugs erschienen an Stellen, die ich nicht angefasst hatte. Logik, die korrekt aussah, war falsch bei Edge Cases, die ich nie zu überprüfen gedacht hätte. Und das Schlimmste -- die AI wusste nicht, dass sie falsch war. Sie schrieb den kaputten Code mit der gleichen selbstbewussten Einrückung wie immer.Cursor on the side for anything greenfield. The speed was genuinely remarkable. But something started happening. Bugs were appearing in places I hadn't touched. Logic that looked correct was wrong in edge cases I'd never thought to check. And the worst part -- the AI had no idea it was wrong. It wrote the broken code with the same confident indentation it always does.
Da nahm ich die Tests wieder auf.
---
Der Zeitraum, in dem ich Tests abgebrochen habe (Und warum es damals sinnvoll war)
Ehrlich gesagt? Bei einer bestimmten Art von Projekt war es richtig, Tests zu überspringen. Wenn du eine fünf-seitige Broschüren-Website in WordPress baust, PHPUnit-Tests für ein Contact-Form-Plugin zu schreiben, ist Theater. Dazu stehe ich.was the right call. If you're building a five-page brochure site in WordPress, writing PHPUnit tests for a contact form plugin is theatre. I stand by that.
Seahawks Geschäftskern war lange Zeit genau diese Arbeit -- hohe Volumen, relativ niedrige Komplexität, gut definierter Umfang. Ein Client gibt dir eine Figma-Datei, du baust sie, du QA-st sie, du shippst sie. Die Feedback-Schleifen waren kurz. Wenn etwas kaputt ging, würdest du es innerhalb von Stunden wissen. Tests für diesen Kontext zu schreiben ist das Entwickler-Äquivalent zum Laminieren einer Post-it-Note.
Aber ich verallgemeinerte diese Lektion zu aggressiv. Ich fing an, alle Projekte wie Broschüren-Seiten zu behandeln. Sogar die mit benutzerdefinierten WooCommerce-Checkout-Flows. Sogar das Fintech-Dashboard, das wir Anfang 2023 für einen Client in Frankfurt gebaut haben -- vollständige benutzerdefinierte REST API, JWT Auth, drei verschiedene User-Permission-Tiers. Keine Tests. Nur "sorgfältige manuelle QA". Das war arrogant, und es hat uns erwischt.all projects like brochure sites. Even the ones with custom WooCommerce checkout flows. Even the fintech dashboard we built in early 2023 for a client in Frankfurt -- full custom REST API, JWT auth, three different user permission tiers. No tests. Just "careful manual QA." That was arrogant, and it bit us.
Das Frankfurt-Projekt shipped mit einem Berechtigungs-Bug, der Editor-Level-Usern erlaubte, Admin-Level-Daten unter einer spezifischen Filterkombination abzufragen. Wir haben es erst bemerkt, als ihr internes Team sechs Wochen nach dem Launch eine Sicherheitsprüfung durchgeführt hat. Peinlich. Reparierbar. Aber genau die Art von Ding, das ein einfacher Integration-Test geflaggt hätte, bevor wir auch nur einen Pull Request gestellt hätten.
---
Was AI-Coding-Tools tatsächlich verändert haben
Hier ist das, was die meisten Leute übersehen, wenn sie über Copilot oder Cursor oder welches Modell gerade heiß ist sprechen: Der Code sieht richtig aus. Das ist das Problem.looks right. That's the problem.
Wenn ein Junior Developer fehlerhaften Code schreibt, sieht man die Unsicherheit oft darin. Seltsame Variablennamen, ein Kommentar, der sagt // bin mir unsicher, eine Funktion, die klar zweimal kopiert wurde. Der Code verrät seine eigene Fragilität. AI-Code tut das nicht. Er ist stilistisch konsistent, gut benannt und strukturiert auf eine Weise, die absichtlich wirkt. Das Selbstbewusstsein ist rein kosmetisch.// not sure about this, a function that's clearly copy-pasted twice. The code telegraphs its own fragility. AI code doesn't. It's stylistically consistent, well-named, and structured in a way that reads as intentional. The confidence is entirely cosmetic.
Studien der Stanford Human-Computer Interaction Group haben gezeigt, dass Entwickler, die AI-Assistants nutzen, dazu neigen, generierten Code beim ersten Lesen zu übervertrauen. Das entspricht meiner eigenen Erfahrung. Ich würde einen 40-Zeilen-Function von Copilot kurz anschauen, denken „ja, genau das hätte ich geschrieben", und weitermachen. Manchmal war es okay. Manchmal hatte er stillschweigend missverstanden, was ich eigentlich brauchte. have flagged that developers using AI assistants tend to over-trust generated code on first read. That tracks with my own experience. I would glance at a 40-line function Copilot had written, think "yeah, that's basically what I'd have written," and move on. Sometimes it was fine. Sometimes it had silently misunderstood what I actually needed.
Der spezifische Fehlermodus, auf den ich immer wieder stieß: bedingte Logik rund um Edge Cases, die die AI keinen Grund hatte, zu antizipieren. Sie würde eine Funktion schreiben, die den Happy Path perfekt handhabt und dann stillschweigend bei Null-Eingaben, leeren Arrays oder nicht-standardisierten Datumsformaten fehlschlägt. Dinge, die mich dreißig Sekunden gekostet hätten zu durchdenken, wenn ich den Code selbst geschrieben hätte, weil ich daran gedacht hätte, während ich tippte.conditional logic around edge cases the AI had no reason to anticipate. It would write a function that handled the happy path perfectly and then quietly fail on null inputs, empty arrays, or non-standard date formats. Things that would have taken me thirty seconds to think about if I'd written the code myself, because I'd have been thinking as I typed.
Die Geschwindigkeitsfalle
Hier lauert eine echte Produktivitätsfalle. Die KI macht dich schnell. Schnelligkeit fühlt sich gut an. Du fängst an, schneller zu liefern, und du überprüfst weniger sorgfältig, weil die Geschwindigkeit wie ein Zeichen für Qualität wirkt. Das ist sie nicht. Geschwindigkeit und Korrektheit sind nicht korreliert, wenn du ein Sprachmodell aufforderst.
Im September habe ich ungefähr 40% mehr Features in ein Kundenprojekt gesteckt, als ich ohne KI-Unterstützung geschafft hätte. Das Projekt hatte aber auch mehr Bugs nach dem Launch als alles, das ich in zwei Jahren ausgeliefert habe. Keine katastrophalen Bugs. Aber nervige. Die Art, die das Vertrauen des Kunden zermürbt.
---
Warum Tests jetzt anders funktionieren (Mit KI im Spiel)
Als ich zu Tests zurückkam, kam ich nicht zur alten Workflow zurück. Tests schreiben, dann Implementation, dann AI-unterstützte Code Review -- das ist die Schleife, auf die ich mich jetzt eingependelt habe.
Das Interessante ist, dass KI eigentlich ausgezeichnet darin ist, Tests zu schreiben, auf eine Weise, wie sie nicht immer ausgezeichnet darin ist, Anwendungslogik zu schreiben. Gib Copilot eine gut definierte Funktionssignatur und fordere es auf, eine Test-Suite zu generieren, und es produziert eine Edge-Case-Abdeckung, die ich 20 Minuten lang manuell geschrieben hätte. Es stellt sich unglückliche Pfade gut vor, wenn die Aufgabe speziell „finde Wege, wie das schiefgehen kann" ist.AI is actually excellent at writing tests, in a way it isn't always excellent at writing application logic. Give Copilot a well-defined function signature and ask it to generate a test suite and it'll produce edge-case coverage I'd have taken twenty minutes to write manually. It imagines unhappy paths well when the task is specifically "find ways this can break."
Ich habe das Ganze sozusagen invertiert. Ich schreibe die Test-Spezifikation. Die KI füllt die Test-Cases aus. Dann schreibt die KI die Implementierung. Dann lese ich die Implementierung durch die Linse dieser Tests, statt nur den Code kalt zu lesen.through the lens of those tests, rather than just reading the code cold.
Es ist langsamer als reines vibe-coding. Aber es ist schneller als der alte alles-manuell-schreiben-einschließlich-tests-Workflow. Und es hat seit Frankfurt null Permissions-Bugs ausgeliefert.
Die Tools, die ich tatsächlich nutze
- [Vitest](https://vitest.dev) für alles JavaScript oder TypeScript. Ersetzte Jest für mich komplett letztes Jahr -- die Config ist sauberer und der Watch Mode ist schnell. for anything JavaScript or TypeScript. Replaced Jest for me entirely last year -- the config is saner and the watch mode is quick.
- PHPUnit weiterhin, für WordPress und benutzerdefinierte PHP-Arbeiten. Nichts hat es ersetzt. still, for WordPress and custom PHP work. Nothing has replaced it.
- Cursors "test this function" Shortcut -- ernsthaft eine der nützlichsten Single Features in jedem Editor, den ich je benutzt habe. -- genuinely one of the most useful single features in any editor I've used.
- GitHub Actions für CI. Tests laufen bei jedem Push zu main. Dauert bei den meisten Projekten etwa 90 Sekunden. for CI. Tests run on every push to
main. Takes about 90 seconds on most projects.
---
Das Argument gegen Tests (Steel-Manned)
Ich möchte dieser Position eine faire Chance geben, weil ich sie fast zwei Jahre lang vertreten habe.
Das echte Argument ist nicht "Tests sind nutzlos". Es ist "Tests haben einen Preis und viele Projekte rechtfertigen diesen Preis nicht". Tests zu schreiben und zu warten dauert Zeit. Bei einem Projekt mit kurzer Lebensdauer -- eine Campaign-Microsite, eine Marketing Landing Page, ein Hackathon-Prototyp -- hat diese Zeitivestition null Return. Das Projekt wird tot sein, bevor die Tests dir irgendetwas sparen.
Und es gibt einen subtileren Punkt: schlechte Tests sind schlimmer als keine Tests. Eine Test-Suite, die besteht, weil die Tests tautologisch sind (du testest im Grunde nur, dass deine Funktion das zurückgibt, was du ihr gesagt hast), gibt dir falsches Vertrauen. Ich habe das in Agenturen gesehen. Entwickler, die Tests schreiben, die immer bestehen, weil niemand hinterfragt hat, was sie wirklich überprüfen.bad tests are worse than no tests. A test suite that passes because the tests are tautological (you're essentially testing that your function returns what you told it to return) gives you false confidence. I've seen this at agencies. Developers writing tests that always pass because nobody challenged what they were actually verifying.
Martin Fowler hat gut über das geschrieben -- Coverage-Prozentsätze sind keine Messgröße für Test-Qualität. Eine 90%-Coverage-Zahl kann eine komplett hohle Suite verstecken. -- coverage percentages are not a measure of test quality. A 90% coverage number can mask a completely hollow suite.
Also: nicht alles testen. Nicht testen, weil es sich professionell anfühlt. Testen, weil du eine Logik identifiziert hast, die tragend ist und teuer zu brechen wäre.
---
Was ich jetzt teste (und was nicht)
Hier ist die tatsächliche Entscheidung, zu der ich in den letzten acht oder neun Monaten gekommen bin:
Ich teste:
- Jede Funktion, die mit Geld, Berechtigungen oder Datentransformation umgeht
- Jeden API-Endpoint, der kein einfacher CRUD-Durchgang ist
- Custom Business Logic, bei der der Klient das exakte Verhalten schriftlich vorgegeben hat
- Alles, was eine KI geschrieben hat und das ich nicht Zeile für Zeile vollständig gelesen habe
Ich teste nicht:
- UI-Rendering (Snapshot-Tests haben mir in neun Jahren nicht ein einziges Mal geholfen. Nicht ein einziges Mal.)
- Wrapper für APIs von Drittanbietern, bei denen das externe Verhalten außerhalb meiner Kontrolle liegt
- One-off-Scripts, die einmal laufen und dann gelöscht werden
- Standard-WordPress-Hooks, es sei denn, sie machen etwas Ungewöhnliches
Das ist alles. Keine große Philosophie. Nur eine Liste basierend darauf, wo ich auf die Nase gefallen bin.
---
Der Workflow, der tatsächlich für mich funktioniert
Da ein paar Leute in Slack-Communities, in denen ich aktiv bin, danach gefragt haben, hier die tatsächliche Abfolge:
- Schreib einen kurzen Spec-Kommentar am Anfang der Datei -- was dieses Modul tut, was es nicht tut, Edge Cases die ich bereits kenne.
- Bitte Cursor, Testfälle aus diesem Kommentar zu generieren, bevor ich irgendeine Implementierung schreibe.
- Überprüfe diese Testfälle. Lösche die dummen. Füge alle hinzu, die die KI übersehen hat.
- Lass Copilot oder Cursor die Implementierung schreiben.
- Führe die Tests aus. Sie werden fehlschlagen. Behebe die Implementierung (nicht die Tests).
- Lies den Diff vor dem Push -- KI-gestützer Code braucht immer noch einen menschlichen Review.
Schritt 6 ist nicht verhandelbar. Ich habe in den letzten vier Monaten drei echte Bugs gefunden, nur indem ich das Diff langsam vor dem Push gelesen habe. Nichts Cleveres. Nur lesen.
Kent Becks ursprüngliche Formulierung von TDD ging nie um 100% Coverage oder perfekte Methodologie. Es ging darum, eine Feedback-Schleife schnell genug aufzubauen, um Fehler zu fangen bevor sie sich aufschaukeln. Diese Idee -- schnelle Feedback-Schleifen -- ist heute relevanter als 2003. Weil die KI Fehler schneller macht als jeder Entwickler den ich je eingestellt habe. was never about 100% coverage or perfect methodology. It was about building a feedback loop fast enough to catch mistakes before they compound. That idea -- fast feedback loops -- is more relevant now than it was in 2003. Because the AI makes mistakes faster than any developer I've ever hired.
---
FAQ
Verlangsamt das deine Delivery-Geschwindigkeit?
Bei komplexen Projekten um etwa 10 bis 15%. Bei einfachen vielleicht gar nicht -- die KI generiert die Tests so schnell, dass der Overhead minimal ist. Bei Projekten wo ein Bug nach dem Launch echtes Geld kostet (und die meisten echten Geldprojekte fallen darunter), sind diese 15% hundertfach wert.
Und TypeScript? Ersetzt starke Typisierung nicht eine Menge Tests?
Teilweise. TypeScript fängt eine ganze Klasse von Fehlern zur Compile-Zeit ab, für die du früher Tests brauchtest. Aber Typen testen nicht die Geschäftslogik. Sie überprüfen nicht, ob deine Rabattberechnungsfunktion die richtigen Regeln für Großkundenkunden anwendet. Das liegt immer noch bei dir.
Sollten Junior-Entwickler KI-Coding-Tools verwenden, wenn sie keine Tests schreiben?
Nein. Das ist eine starke Meinung. Ein Junior-Entwickler, der Copilot ohne Tests nutzt, fliegt im Grunde ein Flugzeug auf Autopilot, ohne zu verstehen, wie der Autopilot funktioniert oder wie man manuell landet. Die KI produziert Code, der aussieht wie Senior-Level, der Junior wird nicht wissen, welche Teile er anzweifeln sollte, und du wirst irgendwann einen Produktionsvorfall haben. Tests geben ihm wenigstens einen Mechanismus, um die Ausgabe zu überprüfen, die er akzeptiert.
Warum hast du überhaupt damit angefangen, nicht mehr zu testen?
Burnout, teilweise. Und eine Phase wo jedes Projekt wirklich einfach war und Tests wirklich keinen Nutzen gebracht haben. Der Fehler war nicht zu bemerken, wann die Projektkomplexität sich ändert und dementsprechend anzupassen. Das ist die echte Lektion -- nicht "immer testen" oder "nie testen" sondern zu wissen in welche Kategorie ein bestimmtes Projekt fällt.
---
Tests zu schreiben hat sich nicht wie Schutz angefühlt. Es hat sich wie Papierkram angefühlt. Die KI hat das geändert. Nicht weil die KI schlecht ist -- sie hat mich bedeutend schneller gemacht -- sondern weil sie eine neue Klasse von selbstbewussten, wohlformatierten, plausibel aussehenden Fehlern eingeführt hat die ich nicht durch Code-Lesen wie früher fangen kann. Die Tests sind nicht für die KI. Sie sind für mich. Ein Zwangsmechanismus um zu überlegen was ich wirklich brauche dass der Code tut bevor ich akzeptiere was mir das Modell in die Hand drückt.
Ich wünschte, ich hätte es vor zwei Jahren so gerahmt.
