Skip to content

पोस्ट

आपकी दोष दर कम करने के लिए 4 अभ्यास

17 नवंबर 2015 • 10 मिनट पढ़ना

आपकी दोष दर कम करने के लिए 4 अभ्यास

सॉफ्टवेयर लिखना जटिलता और सरलता के बीच एक लड़ाई है। दोनों के बीच संतुलन बनाना कठिन है। यह व्यापार लंबे अनुरक्षणीय तरीकों और बहुत अधिक अमूर्तता के बीच है। किसी भी दिशा में बहुत अधिक झुकना कोड की पठनीयता को बिगाड़ता है और दोषों की संभावना बढ़ाता है।

क्या दोष टाले जा सकते हैं? NASA कोशिश करता है, लेकिन वे ट्रकलोड टेस्टिंग भी करते हैं। उनका सॉफ्टवेयर शाब्दिक रूप से मिशन क्रिटिकल है - एक शॉट डील। अधिकांश संगठनों के लिए, यह मामला नहीं है और बड़ी मात्रा में परीक्षण महंगा और अव्यावहारिक है। जबकि परीक्षण का कोई विकल्प नहीं है, बिना परीक्षण के दोष प्रतिरोधी कोड लिखना संभव है।

20 साल के कोडिंग और एप्लिकेशन आर्किटेक्टिंग में, मैंने दोषों को कम करने के लिए चार अभ्यासों की पहचान की है। पहले दो अभ्यास दोषों के परिचय को सीमित करते हैं और अंतिम दो अभ्यास दोषों को उजागर करते हैं। प्रत्येक अभ्यास अपने आप में एक विशाल विषय है जिस पर कई पुस्तकें लिखी गई हैं। मैंने प्रत्येक अभ्यास को कुछ पैराग्राफों में संक्षेपित किया है और जब संभव हो तो अतिरिक्त जानकारी के लिए लिंक प्रदान किए हैं।

1. सरल कोड लिखें

सरल आसान होना चाहिए, लेकिन यह नहीं है। सरल कोड लिखना कठिन है।

कुछ लोग इसे पढ़ेंगे और सोचेंगे कि इसका मतलब सरल भाषा सुविधाओं का उपयोग करना है, लेकिन यह मामला नहीं है - सरल कोड मूर्ख कोड नहीं है।

इसे वस्तुनिष्ठ रखने के लिए, मैं साइक्लोमैटिक जटिलता को एक उपाय के रूप में उपयोग कर रहा हूं। जटिलता को मापने के अन्य तरीके और अन्य प्रकार की जटिलताएं हैं, मुझे उम्मीद है कि बाद के लेखों में इन विषयों का पता लगाऊंगा।

Microsoft साइक्लोमैटिक जटिलता को परिभाषित करता है:

साइक्लोमैटिक जटिलता विधि के माध्यम से रैखिक-स्वतंत्र
पथों की संख्या को मापती है, जो सशर्त शाखाओं की संख्या और
जटिलता द्वारा निर्धारित होती है। एक कम साइक्लोमैटिक जटिलता
आम तौर पर एक विधि को इंगित करती है जो समझने, परीक्षण करने और
बनाए रखने में आसान है।

कम साइक्लोमैटिक जटिलता क्या है? Microsoft साइक्लोमैटिक जटिलता को 25 से नीचे रखने की सिफारिश करता है।

ईमानदारी से कहूं तो, मैंने पाया है कि Microsoft की 25 की साइक्लोमैटिक जटिलता की सिफारिश बहुत अधिक है। अनुरक्षणीयता और जटिलता के लिए, मैंने पाया है कि आदर्श विधि आकार 1 से 10 लाइनों के बीच है जिसमें 1 और 5 के बीच साइक्लोमैटिक जटिलता है।

Bill Wagner ने Effective C#, Second Edition में विधि आकार पर लिखा:

याद रखें कि आपके C# कोड को मशीन-निष्पादन योग्य कोड में अनुवाद करना एक दो-चरणीय प्रक्रिया है। C# कंपाइलर IL उत्पन्न करता है जो असेंबलियों में वितरित होता है। JIT कंपाइलर प्रत्येक विधि (या विधियों के समूह, जब इनलाइनिंग शामिल हो) के लिए मशीन कोड उत्पन्न करता है, जैसी आवश्यकता होती है। छोटे फ़ंक्शन JIT कंपाइलर के लिए उस लागत को परिशोधित करना बहुत आसान बनाते हैं। छोटे फ़ंक्शन इनलाइनिंग के लिए उम्मीदवार होने की अधिक संभावना रखते हैं। यह केवल छोटापन नहीं है: सरल नियंत्रण प्रवाह उतना ही महत्वपूर्ण है। फ़ंक्शन के अंदर कम नियंत्रण शाखाएं JIT कंपाइलर के लिए चर को पंजीकृत करना आसान बनाती हैं। यह केवल स्पष्ट कोड लिखने का अच्छा अभ्यास नहीं है; यह रनटाइम पर अधिक कुशल कोड बनाने का तरीका है।

साइक्लोमैटिक जटिलता को परिप्रेक्ष्य में रखने के लिए, निम्नलिखित विधि में 12 की साइक्लोमैटिक जटिलता है।

public string ComplexityOf12(int status)
{
    var isTrue = true;
    var myString = "Chuck";

    if (isTrue)
    {
        if (isTrue)
        {
            myString = string.Empty;
            isTrue = false;

            for (var index = 0; index < 10; index++)
            {
                isTrue |= Convert.ToBoolean(new Random().Next());
            }

            if (status == 1 || status == 3)
            {
                switch (status)
                {
                    case 3:
                        return "Bye";
                    case 1:
                        if (status % 1 == 0)
                        {
                            myString = "Super";
                        }
                        break;
                }

                return myString;
            }
        }
    }

    if (!isTrue)
    {
        myString = "New";
    }

    switch (status)
    {
        case 300:
            myString = "3001";
            break;
        case 400:
            myString = "4003";
            break;

    }

    return myString;
}

एक सामान्यतः स्वीकृत जटिलता परिकल्पना यह मानती है कि जटिलता और दोषों के बीच एक सकारात्मक सहसंबंध मौजूद है।

पिछली पंक्ति थोड़ी जटिल है। सबसे सरल शब्दों में - कोड को सरल रखना आपकी दोष दर को कम करता है।

2. परीक्षणीय कोड लिखें

अध्ययनों से पता चला है कि वास्तविक परीक्षण लिखे बिना परीक्षणीय कोड लिखना दोषों की घटनाओं को कम करता है। यह इतना महत्वपूर्ण और गहरा है कि इसे दोहराने की आवश्यकता है: वास्तविक परीक्षण लिखे बिना परीक्षणीय कोड लिखना दोषों की घटनाओं को कम करता है।

यह सवाल उठाता है, परीक्षणीय कोड क्या है?

मैं परीक्षणीय कोड को ऐसे कोड के रूप में परिभाषित करता हूं जिसका अलगाव में परीक्षण किया जा सकता है। इसका मतलब है कि सभी निर्भरताओं को परीक्षण से मॉक किया जा सकता है। निर्भरता का एक उदाहरण डेटाबेस क्वेरी है। एक परीक्षण में, डेटा मॉक (नकली) किया जाता है और अपेक्षित व्यवहार का एक दावा किया जाता है। यदि दावा सच है, तो परीक्षण पास हो जाता है, यदि नहीं तो यह असफल हो जाता है।

परीक्षणीय कोड लिखना कठिन लग सकता है, लेकिन वास्तव में, Inversion of Control (Dependency Injection) और S.O.L.I.D सिद्धांतों का पालन करते समय यह आसान है। आप आसानी से आश्चर्यचकित होंगे और सोचेंगे कि इस तरह से लिखना शुरू करने में इतना समय क्यों लगा।

3. कोड समीक्षा

एक विकास टीम द्वारा अपनाई जा सकने वाली सबसे प्रभावशाली प्रथाओं में से एक कोड समीक्षा है।

कोड समीक्षा डेवलपर्स के बीच ज्ञान साझाकरण की सुविधा प्रदान करती है। अनुभव से कहते हुए, अन्य डेवलपर्स के साथ कोड पर खुली चर्चा का मेरे कोड लेखन कौशल पर सबसे अधिक प्रभाव पड़ा है।

Steve McConnell द्वारा Code Complete पुस्तक में, Steve कोड समीक्षा के लाभों पर कई केस स्टडी प्रदान करते हैं:

  • 200 से अधिक लोगों के साथ AT&T में एक संगठन के अध्ययन में संगठन द्वारा समीक्षा शुरू करने के बाद उत्पादकता में 14 प्रतिशत की वृद्धि और दोषों में 90 प्रतिशत की कमी की रिपोर्ट की गई।
    • Aetna Insurance Company ने निरीक्षण का उपयोग करके एक प्रोग्राम में 82 प्रतिशत त्रुटियां पाईं और अपने विकास संसाधनों को 20 प्रतिशत तक कम करने में सक्षम थी।
    • एक ही समूह के लोगों द्वारा विकसित 11 प्रोग्रामों के एक समूह में, पहले 5 बिना समीक्षा के विकसित किए गए थे। शेष 6 समीक्षा के साथ विकसित किए गए थे। सभी प्रोग्राम उत्पादन में जारी होने के बाद, पहले 5 में कोड की प्रति 100 लाइनों में औसतन 4.5 त्रुटियां थीं। जिन 6 का निरीक्षण किया गया था, उनमें केवल 0.82 त्रुटियों का औसत था। समीक्षाओं ने त्रुटियों को 80 प्रतिशत से अधिक कम कर दिया।

यदि वे संख्याएं आपको कोड समीक्षा अपनाने के लिए प्रभावित नहीं करती हैं, तो आप Johnny Paycheck’s Take This Job and Shove It गाते हुए एक ब्लैक होल में बहने के लिए अभिशप्त हैं।

4. यूनिट टेस्टिंग

मैं स्वीकार करूंगा, जब मैं समय सीमा के खिलाफ हूं तो परीक्षण सबसे पहली चीज है जो जाती है। लेकिन परीक्षण के लाभों से इनकार नहीं किया जा सकता जैसा कि निम्नलिखित अध्ययन दिखाते हैं।

Microsoft ने यूनिट टेस्टिंग की प्रभावशीलता पर एक अध्ययन किया। उन्होंने पाया कि स्वचालित परीक्षण के साथ संस्करण 2 (संस्करण 1 में कोई परीक्षण नहीं था) को कोडिंग करने से तुरंत दोष 20% कम हो गए, लेकिन अतिरिक्त 30% की लागत पर।

एक अन्य अध्ययन ने Test Driven Development (TDD) को देखा। उन्होंने TDD का उपयोग नहीं करने वाली समान परियोजनाओं की तुलना में कोड गुणवत्ता में दो गुना से अधिक वृद्धि देखी। TDD परियोजनाओं को विकसित करने में औसतन 15% अधिक समय लगा। TDD का एक साइड-इफेक्ट यह था कि परीक्षण लाइब्रेरी और API के लिए दस्तावेज़ीकरण का काम करते थे।

अंत में, Test Coverage and Post-Verification Defects पर एक अध्ययन में:

… हमने पाया कि दोनों परियोजनाओं में परीक्षण कवरेज में वृद्धि
प्री-रिलीज़ परिवर्तनों की संख्या के लिए समायोजित होने पर फील्ड रिपोर्ट की गई
समस्याओं में कमी से जुड़ी है…

एक उदाहरण

निम्नलिखित कोड में 4 की साइक्लोमैटिक जटिलता है।

    public void SendUserHadJoinedEmailToAdministrator(DataAccess.Database.Schema.dbo.Agency agency, User savedUser)
    {
        AgencySettingsRepository agencySettingsRepository = new AgencySettingsRepository();
        var agencySettings = agencySettingsRepository.GetById(agency.Id);

        if (agencySettings != null)
        {
            var newAuthAdmin = agencySettings.NewUserAuthorizationContact;

            if (newAuthAdmin.IsNotNull())
            {
                EmailService emailService = new EmailService();

                emailService.SendTemplate(new[] { newAuthAdmin.Email }, GroverConstants.EmailTemplate.NewUserAdminNotification, s =>
                {
                    s.Add(new EmailToken { Token = "Domain", Value = _settings.Domain });
                    s.Add(new EmailToken
                    {
                        Token = "Subject",
                        Value =
                    string.Format("New User {0} has joined {1} on myGrover.", savedUser.FullName(), agency.Name)
                    });
                    s.Add(new EmailToken { Token = "Name", Value = savedUser.FullName() });

                    return s;
                });
            }
        }
    }

आइए उपरोक्त कोड की परीक्षणीयता की जांच करें।

क्या यह सरल कोड है?

हां, यह है, साइक्लोमैटिक जटिलता 5 से नीचे है।

क्या कोई निर्भरताएं हैं?

हां। 2 सेवाएं हैं AgencySettingsRepository और EmailService

क्या सेवाएं मॉक करने योग्य हैं?

नहीं, उनका निर्माण विधि के भीतर छुपा हुआ है।

क्या कोड परीक्षणीय है?

नहीं, यह कोड परीक्षणीय नहीं है क्योंकि हम AgencySettingsRepository और EmailService को मॉक नहीं कर सकते।

रिफैक्टर्ड कोड का उदाहरण

हम इस कोड को कैसे परीक्षणीय बना सकते हैं?

हम AgencySettingsRepository और EmailService को निर्भरताओं के रूप में इंजेक्ट करते हैं (constructor injection का उपयोग करके)। यह हमें उन्हें परीक्षण से मॉक करने और अलगाव में परीक्षण करने की अनुमति देता है।

नीचे रिफैक्टर्ड संस्करण है।

ध्यान दें कि सेवाएं कंस्ट्रक्टर में कैसे इंजेक्ट की जाती हैं। यह हमें नियंत्रित करने की अनुमति देता है कि कौन सा कार्यान्वयन SendMail कंस्ट्रक्टर में पास किया जाता है। फिर डमी डेटा पास करना और सेवा विधि कॉल को इंटरसेप्ट करना आसान हो जाता है।

public class SendEmail
{
    private IAgencySettingsRepository _agencySettingsRepository;
    private IEmailService _emailService;


    public SendEmail(IAgencySettingsRepository agencySettingsRepository, IEmailService emailService)
    {
        _agencySettingsRepository = agencySettingsRepository;
        _emailService = emailService;
    }

    public void SendUserHadJoinedEmailToAdministrator(DataAccess.Database.Schema.dbo.Agency agency, User savedUser)
    {
        var agencySettings = _agencySettingsRepository.GetById(agency.Id);

        if (agencySettings != null)
        {
            var newAuthAdmin = agencySettings.NewUserAuthorizationContact;

            if (newAuthAdmin.IsNotNull())
            {
                _emailService.SendTemplate(new[] { newAuthAdmin.Email },
                GroverConstants.EmailTemplate.NewUserAdminNotification, s =>
                {
                    s.Add(new EmailToken { Token = "Domain", Value = _settings.Domain });
                    s.Add(new EmailToken
                    {
                        Token = "Subject",
                        Value = string.Format("New User {0} has joined {1} on myGrover.", savedUser.FullName(), agency.Name)
                    });
                    s.Add(new EmailToken { Token = "Name", Value = savedUser.FullName() });

                    return s;
                });
            }
        }
    }
}

परीक्षण उदाहरण

नीचे अलगाव में परीक्षण का एक उदाहरण है। हम मॉकिंग फ्रेमवर्क FakeItEasy का उपयोग कर रहे हैं।

    [Test]
    public void TestEmailService()
    {
        //Given

        //Using FakeItEasy mocking framework
        var repository = A<IAgencySettingsRepository>.Fake();
        var service = A<IEmailService>.Fake();

        var agency = new Agency { Name = "Acme Inc." };
        var user = new User { FirstName = "Chuck", LastName = "Conway", Email = "chuck.conway@fakedomain.com" }

        //When

        var sendEmail = new SendEmail(repository, service);
        sendEmail.SendUserHadJoinedEmailToAdministrator(agency, user);


        //Then
        //An exception is thrown when this is not called.
        A.CallTo(() => service.SendTemplate(A<Agency>.Ignore, A<User>.Ignore)).MustHaveHappened();

    }

समापन

दोष प्रतिरोधी कोड लिखना आश्चर्यजनक रूप से आसान है। मुझे गलत न समझें, आप कभी भी दोष-मुक्त कोड नहीं लिखेंगे (यदि आप पता लगा लेते हैं कि कैसे, तो मुझे बताएं!), लेकिन इस लेख में उल्लिखित 4 अभ्यासों का पालन करके आप अपने कोड में पाए जाने वाले दोषों में कमी देखेंगे।

संक्षेप में, सरल कोड लिखना साइक्लोमैटिक जटिलता को 5 के आसपास और विधि आकार को छोटा रखना है। परीक्षणीय कोड लिखना Inversion of Control और S.O.L.I.D सिद्धांतों का पालन करते समय आसानी से प्राप्त होता है। कोड समीक्षा आपको और टीम को डोमेन और आपके द्वारा लिखे गए कोड को समझने में मदद करती है - केवल अपने कोड को समझाना ही मुद्दों को प्रकट कर देगा। और अंत में, यूनिट टेस्टिंग आपकी कोड गुणवत्ता में भारी सुधार कर सकती है और भविष्य के डेवलपर्स के लिए दस्तावेज़ीकरण प्रदान कर सकती है।

लेखक: चक कॉनवे सॉफ्टवेयर इंजीनियरिंग और जेनेरेटिव AI में विशेषज्ञता रखते हैं। उनसे सोशल मीडिया पर जुड़ें: X (@chuckconway) या उन्हें YouTube पर देखें।

↑ शीर्ष पर वापस

आपको यह भी पसंद आ सकता है