Go में लीगेसी कोड के रिफैक्टरिंग का परिचय
विरासत (legacy) कोड के साथ काम करना किसी भी Go डेवलपर के जीवन का एक अपरिहार्य हिस्सा है। समय के साथ, प्रोजेक्ट तकनीकी ऋण से भर जाते हैं: मोनोलिथिक फंक्शन, कोड डुप्लिकेशन, टेस्ट और एरर हैंडलिंग की कमी दिखाई देने लगती है। Go में लीगेसी कोड का रिफैक्टरिंग केवल दोबारा लिखना नहीं है, बल्कि सिस्टम के बाहरी व्यवहार को बदले बिना आर्किटेक्चर में सुधार करने की एक रणनीतिक प्रक्रिया है।
इस लेख में, हम Go प्रोजेक्ट्स के रिफैक्टरिंग के मुख्य दृष्टिकोणों पर चर्चा करेंगे, कोड के विशिष्ट उदाहरण देंगे और दिखाएंगे कि पुराने कोडबेस को कैसे साफ, सुरक्षित और अधिक कुशल बनाया जाए।
Go में लीगेसी कोड को विशेष दृष्टिकोण की आवश्यकता क्यों है?
Go अपनी सादगी और सख्ती के लिए जाना जाता है, लेकिन इसमें भी समस्याएं जमा हो जाती हैं:
- इंटरफेस का अभाव — घटकों के बीच कठोर युग्मन;
- एरर को अनदेखा करना — err की जांच किए बिना return;
- ग्लोबल वेरिएबल्स — अंतर्निहित निर्भरताएं;
- लॉजिक का डुप्लिकेशन — कंपोजीशन के बजाय कॉपी-पेस्ट;
- लंबे फंक्शन — कई जिम्मेदारियों वाली 50 से अधिक पंक्तियाँ।
बिना टेस्ट के रिफैक्टरिंग एनेस्थीसिया के बिना सर्जरी करने जैसा है। इसलिए पहला कदम महत्वपूर्ण भागों का टेस्ट कवरेज सुनिश्चित करना है।
रिफैक्टरिंग रणनीति: मोनोलिथ से मॉड्यूल तक
1. इंटरफेस का निर्धारण और डिपेंडेंसी इंजेक्शन
आइए एक विशिष्ट लीगेसी कोड से शुरू करें, जहां बिजनेस लॉजिक स्टोरेज इम्प्लीमेंटेशन से कसकर जुड़ा हुआ है:
// रिफैक्टरिंग से पहलेtype UserService struct { db *sql.DB}
func (s *UserService) GetUser(id int) (*User, error) { row := s.db.QueryRow("SELECT id, name FROM users WHERE id = ?", id) var u User err := row.Scan(&u.ID, &u.Name) return &u, err}समस्या: वास्तविक डेटाबेस के बिना ऐसे कोड का परीक्षण करना असंभव है। समाधान — एक इंटरफ़ेस पेश करना:
// रिफैक्टरिंग के बादtype Storage interface { GetUser(id int) (*User, error)}
type UserService struct { storage Storage}
func NewUserService(storage Storage) *UserService { return &UserService{storage: storage}}
func (s *UserService) GetUser(id int) (*User, error) { return s.storage.GetUser(id)}अब हम टेस्ट में इम्प्लीमेंटेशन को आसानी से मॉक से बदल सकते हैं।
2. बड़े फंक्शन को विभाजित करना (Single Responsibility)
विशिष्ट लीगेसी कोड — एक फंक्शन जो सब कुछ एक साथ करता है:
// रिफैक्टरिंग से पहलेfunc processOrder(order Order) error { // वैलिडेशन if order.Amount <= 0 { return errors.New("invalid amount") } // डिस्काउंट गणना discount := 0.0 if order.CustomerType == "vip" { discount = 0.1 } // डेटाबेस में सेव करना _, err := db.Exec("INSERT INTO orders ...") if err != nil { return err } // ईमेल भेजना return sendEmail(order.Email, "Order confirmed")}रिफैक्टरिंग के बाद, हम इसे छोटे, परीक्षण योग्य फंक्शन में विभाजित करते हैं:
// रिफैक्टरिंग के बादtype OrderProcessor struct { db Database notifier Notifier}
func (p *OrderProcessor) Process(order Order) error { if err := validateOrder(order); err != nil { return err } order = applyDiscount(order) if err := p.db.SaveOrder(order); err != nil { return fmt.Errorf("save order: %w", err) } return p.notifier.SendConfirmation(order.Email)}
func validateOrder(order Order) error { if order.Amount <= 0 { return errors.New("amount must be positive") } return nil}
func applyDiscount(order Order) Order { if order.CustomerType == "vip" { order.Amount *= 0.9 } return order}3. ग्लोबल वेरिएबल्स को DI (Dependency Injection) से बदलना
ग्लोबल वेरिएबल्स — टेस्टिंग के सबसे बड़े दुश्मन हैं।
```