tests-and-ai-coding-tools.html
< BACK खराब डेस्क नोटबुक के साथ, खुला लैपटॉप कोड दिखा रहा है, और लंदन की खूबसूरत खिड़की की रोशनी में चाय का कप

मैंने फिर से टेस्ट लिखना क्यों शुरू किया (AI ने मुझे मजबूर किया)

शुरुआती 2022 में, मैंने एक शांत फैसला लिया: मैंने Seahawk में आने वाले ज्यादातर WordPress और Node प्रोजेक्ट्स के लिए यूनिट टेस्ट लिखना बंद कर दिया। जोर से नहीं। इस बारे में कोई ब्लॉग पोस्ट नहीं। मैंने बस... बंद कर दिया। जो तर्क था वह सही था, मेरे ख्याल में -- हम महीने में 15 से 20 क्लाइंट साइट्स डिलीवर कर रहे थे, मेरे पास रोटेशन में तीन अन्य डेवलपर्स थे, और जो टेस्ट्स मैं लिख रहा था वह किसी के पढ़ने के लिए डॉक्यूमेंटेशन जैसा लग रहे थे। मैनुअल QA असली बग्स को पकड़ता था। टेस्ट्स सिर्फ नाटक थे।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.

लेट 2023 तक आते-आते। GitHub Copilot लगभग आठ महीने से मेरे एडिटर में था। मैंने Cursor का भी इस्तेमाल करना शुरू किया था किसी भी greenfield काम के लिए। स्पीड सचमुच लाजवाब थी। लेकिन कुछ शुरू होने लगा। बग्स ऐसी जगहों में दिखने लगे जहां मैंने छुआ भी नहीं था। लॉजिक जो सही दिख रहा था वह edge cases में गलत था जिनके बारे में मैंने सोचा भी नहीं था। और सबसे बुरी बात -- AI को यह नहीं पता था कि यह गलत है। इसने broken code को उसी आत्मविश्वासी indentation के साथ लिखा जैसा यह हमेशा करता है।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.

तब मैंने टेस्ट्स फिर से उठाया।

---

वह समय जब मैंने टेस्टिंग छोड़ दी (और क्यों यह उस समय समझदारी का काम था)

ईमानदारी से कहूं? एक निश्चित तरह के प्रोजेक्ट के लिए, टेस्ट्स छोड़ना सही फैसला था। अगर आप WordPress में पाँच पेज का ब्रोशर साइट बना रहे हैं, तो contact form plugin के लिए PHPUnit टेस्ट्स लिखना सिर्फ दिखावा है। मैं इस फैसले के पीछे खड़ा हूँ।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.

Seahawk की bread and butter लंबे समय से बिल्कुल यह काम ही था -- high-volume, relatively low-complexity, well-defined scope। एक क्लाइंट आपको Figma फाइल देता है, आप इसे बनाते हो, QA करते हो, डिलीवर करते हो। फीडबैक लूप्स छोटे थे। अगर कुछ टूटता था, तो आप घंटों में जान जाते थे। उस कॉन्टेक्स्ट में टेस्ट्स लिखना डेवलपर के लिए Post-it नोट को laminate करने के बराबर है।

लेकिन मैंने उस सीख को बहुत आक्रामक तरीके से generalize कर दिया। मैं सभी प्रोजेक्ट्स को brochure sites जैसा मानने लगा। यहां तक कि custom WooCommerce checkout flows वाले प्रोजेक्ट्स को भी। यहां तक कि उस fintech dashboard को जो हमने शुरुआती 2023 में Frankfurt के एक क्लाइंट के लिए बनाया था -- full custom REST API, JWT auth, तीन अलग-अलग user permission tiers। कोई टेस्ट्स नहीं। बस "सावधानीपूर्वक मैनुअल QA।" यह अहंकार था, और इसने हमें काटा।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.

Frankfurt का प्रोजेक्ट एक permissions bug के साथ ship हुआ जिससे editor-level users, filters के एक specific combination के तहत admin-level data को query कर सकते थे। हमें इसका पता तब चला जब उनकी internal team ने launch के छः हफ्ते बाद एक security review चलाया। शर्मनाक। ठीक करने योग्य। लेकिन यह वह चीज़ है जिसे एक basic integration test ने, pull request raise करने से भी पहले, flag कर दिया होता।

---

AI Coding Tools ने Actually क्या बदला

यहाँ वह चीज़ है जो ज़्यादातर लोग miss करते हैं जब वह Copilot या Cursor या इस महीने का कोई hot model के बारे में बात करते हैं: कोड सही दिखता है। यही तो समस्या है।looks right. That's the problem.

जब कोई junior developer buggy code लिखता है, तो अक्सर आप उसमें uncertainty देख सकते हैं। अजीब variable names, एक comment जो कहता है // not sure about this, एक function जो साफ़ तौर पर दो बार copy-paste किया गया है। कोड अपनी अपनी fragility को broadcast करता है। AI code ऐसा नहीं करता। यह stylistically consistent है, well-named है, और एक तरीके से structure किया गया है जो intentional दिखता है। Confidence पूरी तरह से cosmetic है।// 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.

Stanford के Human-Computer Interaction group के अध्ययन ने flag किया है कि AI assistants का use करने वाले developers, पहली बार में generated code पर over-trust करने की trend रखते हैं। यह मेरे अपने अनुभव के साथ align करता है। मैं एक 40-line function देखता जो Copilot ने लिखा था, सोचता "हाँ, यही वह है जो मैं लिखता," और आगे बढ़ जाता। कभी-कभी यह ठीक था। कभी-कभी इसने silently गलतफहमी की कि मुझे actually क्या चाहिए। 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.

specific failure mode जो मैं बार-बार hit कर रहा था: edge cases के around conditional logic जिसकी anticipate करने का AI को कोई कारण नहीं था। यह एक ऐसा function लिखता जो happy path को perfectly handle करता और फिर quietly null inputs, empty arrays, या non-standard date formats पर fail करता। ऐसी चीज़ें जिन्हें मैं तीस सेकंड में सोच लेता अगर मैंने code ख़ुद लिखा होता, क्योंकि मैं type करते हुए सोच रहा होता।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.

स्पीड ट्रैप

यहाँ एक असली प्रोडक्टिविटी ट्रैप है। AI आपको तेज़ बनाता है। तेज़ होना अच्छा लगता है। आप तेज़ी से शिप करने लगते हैं और कम सावधानी से रिव्यू करते हैं क्योंकि वेलोसिटी क्वालिटी का सबूत जैसी लगती है। यह नहीं है। जब आप लैंग्वेज मॉडल को प्रॉम्प्ट कर रहे हों तो स्पीड और करेक्टनेस आपस में जुड़े नहीं हैं।

मैंने सितंबर में एक क्लाइंट प्रोजेक्ट में तकरीबन 40% ज़्यादा फीचर्स डाले, जो मैं AI असिस्टेंस के बिना नहीं कर पाता। लेकिन प्रोजेक्ट में लॉन्च के बाद जितने बग आए, वे पिछले दो साल में मैंने जो कुछ शिप किया था उससे कहीं ज़्यादा थे। कोई कैटास्ट्रॉफिक बग नहीं थे। पर परेशान करने वाले थे। जो क्लाइंट का विश्वास घिसाते हैं।

---

टेस्ट अब अलग तरीके से काम करते हैं (AI लूप में होने के साथ)

जब मैं टेस्टिंग की ओर लौटा, तो मैं पुरानी workflow की ओर नहीं लौटा। टेस्ट्स पहले लिखना, फिर implementation, फिर AI-assisted code review -- यह वह लूप है जिस पर मैंने settle कर लिया है अब।

दिलचस्प बात यह है कि AI असल में टेस्ट लिखने में बहुत अच्छा है, जिस तरीके से वह हमेशा एप्लिकेशन लॉजिक लिखने में अच्छा नहीं है। Copilot को एक अच्छी तरीके से डिफाइन किया हुआ फंक्शन सिग्नेचर दो और उससे टेस्ट सूट जेनरेट करने के लिए कहो, और यह एज-केस कवरेज निकालेगा जो मुझे मैन्युअली लिखने में बीस मिनट लगते। यह अनहैप्पी पाथ्स अच्छे से कल्पना करता है जब टास्क विशेष रूप से "यह कैसे टूट सकता है इसके तरीके खोजो" हो।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."

तो मैंने चीज़ को उलट दिया है। मैं टेस्ट स्पेक लिखता हूँ। AI टेस्ट केसेज़ भरता है। फिर AI इंप्लिमेंटेशन लिखता है। फिर मैं इंप्लिमेंटेशन को इन टेस्ट्स के लेंस से पढ़ता हूँ, बस कोड को ठंडे दिमाग से पढ़ने की जगह।through the lens of those tests, rather than just reading the code cold.

यह शुद्ध वाइब-कोडिंग से धीमा है। पर पुरानी सब कुछ मैन्युअली लिखना-टेस्ट्स भी शामिल-वर्कफ़्लो से तेज़ है। और यह Frankfurt के बाद से शून्य परमिशन्स बग्स शिप किया है।

जो टूल्स मैं असल में यूज़ कर रहा हूँ

  • [Vitest](https://vitest.dev) किसी भी JavaScript या TypeScript के लिए। Jest को मैंने पूरी तरह replace कर दिया पिछले साल -- कॉन्फिग ज्यादा सेंसिबल है और watch mode तेज है। for anything JavaScript or TypeScript. Replaced Jest for me entirely last year -- the config is saner and the watch mode is quick.
  • WordPress और custom PHP work के लिए अभी भी PHPUnit। इसकी जगह कोई नहीं ले सका है। still, for WordPress and custom PHP work. Nothing has replaced it.
  • Cursor का "test this function" शॉर्टकट -- सचमुच किसी भी एडिटर में सबसे यूजफुल फीचर्स में से एक जो मैंने यूज किया है। -- genuinely one of the most useful single features in any editor I've used.
  • CI के लिए GitHub Actions। हर push पर main को tests चलते हैं। ज़्यादातर projects पर करीब 90 सेकंड लगते हैं। for CI. Tests run on every push to main. Takes about 90 seconds on most projects.

---

Tests के खिलाफ दलील (Steel-Manned)

मैं इस position को fair hearing देना चाहता हूँ क्योंकि मैंने लगभग दो साल इसे माना था।

असली argument यह नहीं है कि "टेस्ट्स बेकार हैं।" यह है कि "टेस्ट्स की एक कीमत है और बहुत सारे प्रोजेक्ट्स उस कीमत को justify नहीं करते।" एक टेस्ट सूट लिखना और maintain करना समय लेता है। एक प्रोजेक्ट पर जिसकी short lifespan है -- एक campaign microsite, एक marketing landing page, एक hackathon prototype -- उस समय का कोई return नहीं है। प्रोजेक्ट death हो जाएगा इससे पहले कि टेस्ट्स आपको कुछ बचाएं।

और एक और सूक्ष्म बात है: बुरे tests कोई tests नहीं होने से ज़्यादा बुरे हैं। एक test suite जो इसलिए pass होता है क्योंकि tests tautological हैं (आप basically यह test कर रहे हैं कि आपका function वह return करता है जो आपने उसे return करने को कहा था) आपको false confidence देता है। मैंने agencies में ऐसा देखा है। Developers ऐसे tests लिख रहे हैं जो हमेशा pass होते हैं क्योंकि किसी ने challenge नहीं किया कि वो actually क्या verify कर रहे हैं।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 ने इस बारे में अच्छे से लिखा है -- coverage percentages टेस्ट quality का measure नहीं हैं। एक 90% coverage नंबर एक बिल्कुल खोखले suite को छुपा सकता है। -- coverage percentages are not a measure of test quality. A 90% coverage number can mask a completely hollow suite.

तो: सब कुछ परीक्षण न करें। परीक्षण इसलिए न करें कि यह पेशेवर लगता है। परीक्षण इसलिए करें क्योंकि आपने ऐसा तर्क पहचाना है जो भार-सहन करने वाला है और जिसे तोड़ना महंगा पड़ेगा।

---

मैं अब क्या परीक्षण करता हूँ (और क्या नहीं)

यह वह वास्तविक निर्णय है जिस पर मैं पिछले आठ या नौ महीनों के बाद पहुँचा हूँ:

मैं परीक्षण करता हूँ:

  1. कोई भी फ़ंक्शन जो पैसे, अनुमतियों या डेटा ट्रांसफॉर्मेशन को संभालता है
  2. कोई भी API endpoint जो सीधी CRUD पास-थ्रू नहीं है
  3. कस्टम बिज़नेस लॉजिक जहाँ क्लाइंट ने लिखित रूप में सटीक व्यवहार निर्दिष्ट किया हो
  4. कोई भी चीज़ जो AI ने लिखी और जिसे मैंने पूरी तरह लाइन-दर-लाइन नहीं पढ़ा

मैं परीक्षण नहीं करता:

  • UI rendering (स्नैपशॉट टेस्ट ने मुझे नौ सालों में एक बार भी नहीं बचाया। एक बार भी नहीं।)
  • Third-party API wrappers जहाँ external behaviour मेरे नियंत्रण से बाहर है
  • One-off scripts जो एक बार चलती हैं और फिर delete हो जाती हैं
  • Standard WordPress hooks जब तक वे कुछ असामान्य न कर रहे हों

बस यही है। कोई बड़ा philosophy नहीं। बस एक list उन जगहों के आधार पर जहाँ मुझे नुकसान हुआ है।

---

The Workflow That Actually Works For Me

चूंकि कुछ लोगों ने Slack communities में मुझसे पूछा है जहाँ मैं हूँ, यहाँ असल sequence है:

  1. फ़ाइल के शीर्ष पर एक संक्षिप्त spec comment लिखें -- यह मॉड्यूल क्या करता है, क्या नहीं करता है, और edge cases जिनके बारे में मुझे पहले से पता है।
  2. कोई भी implementation लिखने से पहले Cursor को उस comment से test cases generate करने के लिए कहें।
  3. उन टेस्ट केसों को देखें। बेकार वालों को डिलीट करें। जो AI ने मिस किए हों, वो add करें।
  4. Copilot या Cursor को implementation लिखने दें।
  5. टेस्ट चलाएं। वो fail होंगे। Implementation को ठीक करें (टेस्ट को नहीं)।
  6. Push करने से पहले diff को पढ़ें -- AI-assisted code को भी human pass की जरूरत है।

Step 6 non-negotiable है। पिछले चार महीने में मैंने तीन genuinely खराब bugs को सिर्फ diff को धीरे-धीरे पढ़कर push करने से पहले पकड़ा है। कुछ clever नहीं। बस पढ़ना है।

Kent Beck की TDD की original framing कभी भी 100% coverage या perfect methodology के बारे में नहीं थी। यह तेजी से feedback loop बनाने के बारे में था ताकि गलतियों को पकड़ा जा सके इससे पहले कि वे compound हो जाएं। वह विचार -- fast feedback loops -- अब 2003 की तुलना में कहीं अधिक relevant है। क्योंकि AI किसी भी developer की तुलना में faster गलतियां करता है जिसे मैंने कभी hire किया है। 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

क्या यह आपकी delivery speed को slow कर देता है?

Complex projects पर लगभग 10 से 15% तक। Simple ones पर, शायद बिल्कुल नहीं -- AI tests को बहुत जल्दी generate करता है कि overhead minimal है। ऐसे projects के लिए जहां एक bug को post-launch में fix करने का real cost होगा (और अधिकांश real-money projects इसमें आते हैं), वह 15% सौ गुना worth है।

TypeScript के बारे में क्या? क्या strong typing बहुत सारे tests की जगह नहीं ले लेता?

आंशिक रूप से। TypeScript compile time पर errors की एक पूरी श्रेणी को पकड़ता है जिसके लिए आपको पहले tests की जरूरत होती थी। लेकिन types business logic को test नहीं करते। वे verify नहीं करते कि आपका discount calculation function wholesale customers के लिए सही rules apply कर रहा है। वह अभी भी आपके ऊपर है।

क्या junior developers को AI coding tools का उपयोग करना चाहिए अगर वे tests नहीं लिख रहे हैं?

नहीं। Strong opinion। एक junior developer जो tests के बिना Copilot का उपयोग कर रहा है वह basically एक plane को autopilot पर fly कर रहा है यह समझे बिना कि autopilot कैसे काम करता है या manually कैसे land करते हैं। AI senior-level दिखने वाला code produce करेगा, junior को पता नहीं चलेगा कि किन हिस्सों पर संदेह करना है, और आपको eventually एक production incident मिलेगा। Tests कम से कम उन्हें output verify करने का एक mechanism देते हैं जो वे accept कर रहे हैं।

आप testing को पहले जगह बंद क्यों किए, सच बताइए?

Burnout, आंशिक रूप से। और एक period जहां हर project genuinely simple था और tests genuinely अपना काम नहीं कर रहे थे। गलती यह थी कि project complexity बदलने पर notice नहीं किया और accordingly adjust नहीं किया। यही real lesson है -- "हमेशा test करो" या "कभी test मत करो" नहीं, बल्कि यह जानना कि एक दिया गया project किस category में आता है।

---

Tests लिखना पहले protection जैसा feel नहीं करता था। यह paperwork जैसा feel करता था। AI ने वह बदल दिया। इसलिए नहीं कि AI बुरा है -- इसने मुझे meaningfully faster बना दिया है -- बल्कि इसलिए कि यह confident, well-formatted, plausible-looking mistakes का एक नया class introduce करता है जिसे मैं code को read करके उसी तरह catch नहीं कर सकता जैसे पहले करता था। Tests AI के लिए नहीं हैं। वे मेरे लिए हैं। एक forcing function यह सोचने के लिए कि मुझे actually code को क्या करना है इससे पहले कि मैं जो भी model हाथ में दे सकता है उसे accept करूं।

मुझे चाहिए था कि मैं इसे दो साल पहले इसी तरह frame करता।

< BACK