TDD(Test-Driven Development) और LLM का संयोजन
- TDD एक ऐसी development methodology है जिसमें प्रोग्राम लिखना शुरू करने से पहले व्यापक unit tests पहले लिखे जाते हैं
- चूँकि tests व्यवहारतः specification की भूमिका निभाते हैं, इसलिए अंत में सभी tests pass हो जाएँ तो code की शुद्धता कुछ हद तक प्रमाणित की जा सकती है
- परंपरागत रूप से TDD की आलोचना इस रूप में भी होती रही है कि यह productivity को कम करता है या inefficient है
- लेकिन LLM के आने से tests लिखने और code को बार-बार सुधारने की प्रक्रिया कहीं अधिक आसान हो गई है
मैं आम तौर पर LLM का उपयोग कैसे करता हूँ
- Github Copilot जैसे tools का सक्रिय रूप से उपयोग करता रहा हूँ
- LLM दोहराए जाने वाले patterns पहचानने और अगली कुछ lines autocomplete करने में माहिर है, लेकिन पूरे problem को गहराई से समझकर एकदम से पूर्ण module बना देने में अक्सर कठिनाई होती है
- समस्या-समाधान के लिए ज़रूरत से ज़्यादा context देने पर model के topic से भटकने की संभावना बढ़ जाती है
- ज़रूरत के अनुसार जानकारी (जैसे error output) को आंशिक रूप से देकर काम आगे बढ़ाया जाए, तो model debugging में भी बेहतरीन मदद करता है
- IDE, terminal और chat interface के बीच बार-बार copy-paste करने की प्रक्रिया में friction महसूस होता है
क्या इसे automate किया जा सकता है?
- इस प्रक्रिया को automate करने के लिए मैंने खुद event loop की अवधारणा अपनाई
- पहले prompt में implement किए जाने वाले function की specification और function signature स्पष्ट कर दी जाए, तो model unit tests और code का initial draft प्रस्तुत करता है
- इस code को
sandbox directory में सहेजकर अपने-आप go test चलाया जाता है
- अगर test fail हो जाए, तो दूसरे (iterative) prompt में मौजूदा code और test result (compile error या failure information) साथ भेजे जाते हैं
- model इसके आधार पर संशोधित tests और implementation code फिर से सुझाता है
- यह प्रक्रिया तब तक दोहराई जाती है जब तक सभी tests pass न हो जाएँ
- यह approach context को ज़रूरत से ज़्यादा जमा किए बिना incremental improvement संभव बनाती है
- model एक ही test case पर बार-बार fail भी हो सकता है; ऐसे में इंसान सीधे समस्या वाले हिस्से की ओर इशारा कर hint दे सकता है
- यह समझना ज़रूरी है कि LLM द्वारा बनाए गए tests पर्याप्त रूप से सख्त हैं या नहीं, इस पर संदेह करने वाली 'watcher की अनुपस्थिति' की समस्या मौजूद है
- यह संभव है कि code और tests दोनों एक ही error या अधूरे design को साथ-साथ साझा करें
- इसलिए इंसान द्वारा अतिरिक्त test cases के ज़रिए tests को मज़बूत करने की प्रक्रिया महत्वपूर्ण है
- ज़रूरत पड़ने पर, mutation testing जैसी techniques पर भी AI के साथ प्रयोग किया जा सकता है
LLM-आधारित development और cognitive load
- LLM के साथ TDD लागू करने पर, केवल सामान्य algorithm समस्याओं में ही नहीं बल्कि वास्तविक dependencies वाले codebase में भी यह संभव होगा, ऐसा अनुमान है
- हालाँकि, project structure को छोटे units में बाँटकर maintainability बढ़ानी होगी, और हर directory/package स्वतंत्र रूप से test किया जा सकना चाहिए
- cognitive load कम करने के लिए यह सुझाव है कि हर package को मुख्य type definitions (
shared.go), विशिष्ट logic संभालने वाली file (x.go) और tests (x_test.go) में अलग किया जाए
- AI का उपयोग करते समय पूरे code को हर बार model को देने के बजाय, केवल चुनिंदा हिस्से शामिल करके model को केंद्रित रहने के लिए प्रेरित किया जाता है
- इससे test coverage बढ़ाने के साथ modules के बीच coupling कम होती है, जो long-term maintenance में भी लाभ देती है
- बड़े project में भी छोटे और स्पष्ट units में विभाजन करके, हर unit में पर्याप्त logic समाहित रखते हुए उसका scope न्यूनतम रखने वाली संरचना का लक्ष्य रखा जाता है
निष्कर्ष
- AI की प्रगति की गति को देखते हुए, हो सकता है कि कल ही कोई नई architecture आ जाए जो LLM की सीमाओं को पार कर जाए
- इसलिए 100,000 lines से अधिक वाले बड़े legacy code को अचानक refactor करने के बजाय, छोटे स्तर से TDD और LLM के संयोजन की संभावनाएँ तलाशने की सिफारिश की जाती है
- उम्मीद है कि TDD और LLM का सम्मिलन code auto-generation और test quality management दोनों में सकारात्मक बदलाव ला सकता है
5 टिप्पणियां
यह सोचने पर मजबूर करता है कि डेवलपर-विशेष AI services और कौन-सी pipelines इस्तेमाल कर रही होंगी।
(ऐसी चीज़ें देखते-देखते) लगता है थोड़ी ही देर में लोग cyberbrain के लिए दिमाग़ में electric stimulation wires डालने लगेंगे।
टेस्ट कोड जोड़ना अच्छी बात लगती है, लेकिन मुझे नहीं लगता कि इस व्यक्ति के बनाए प्रोग्राम में कोई खास merit है।
cline या aider में भी command line चला कर उसका result लिया जा सकता है, इसलिए उस प्रोग्राम में सिर्फ prompt को अच्छी तरह इस्तेमाल करना, दूसरी सुविधाओं को देखते हुए, ज़्यादा बेहतर लगता है।
BuilderIO का बनाया गया micro-agent भी इसी तरह का एक समान approach है। https://github.com/BuilderIO/micro-agent मैंने भी LLM और TDD के साथ कई बार काम किया है, लेकिन design system वगैरह के ज़रिए abstraction भी अच्छी तरह होना चाहिए। Convention और pattern भी अच्छी तरह स्थापित होने चाहिए। Test case मैं आमतौर पर खुद लिखता हूँ। (चाहे इंसानी भाषा में ही क्यों न हो?) सबसे बढ़कर, जैसा कि इस लेख में भी कहा गया है, low coupling और high cohesion वाले modules को अच्छी तरह design करना ज़रूरी है, तभी सीमित context window में context को ठीक से भरा जा सकता है।
LLM छोटे दायरे के कोड को तो अच्छी तरह देख लेते हैं, लेकिन समग्र डिज़ाइन और बड़े परिप्रेक्ष्य में अभी भी थोड़ी कमी लगती है।
इसे TDD के साथ जोड़कर धीरे-धीरे सुधारने का तरीका अच्छा लगता है।