جزئیات پست

چطور بدون دست زدن به کدهای قدیمی، سیستم رو بروز کنیم؟

شروع ماجرا؛ وقتی یک تغییر ساده تبدیل به کابوس شد

چند وقت پیش، یک تغییر ساده به ذهنم رسید: سیستم جستجوی سایت را بروزرسانی کنم. تعداد پارامترهای متد اصلی داشت بالا می رفت. نیاز بود که یک فکری براش بکنم.

با خودم گفتم: "خب، یک تغییر معمولی. چند روزی بیشتر وقت نمی‌گیره."

اما وقتی کد را باز کردم، همه‌چیز عوض شد...


کد قدیمی؛ یک آشفتگی واقعی

سیستم جستجوی ما یک کلاس به نام SearchEngine داشت که حدود ۱۵۰۰ خط کد بود. این کلاس به‌شدت اسپاگتی (Spaghetti Code) بود و هیچ تفکیک مسئولیتی در آن دیده نمی‌شد.

ساختار کد قدیمی چیزی شبیه این بود:

// =============================================
// کلاس قدیمی - 1500 خط کد آشفته
// =============================================
public class SearchEngine
{
    // متد اصلی جستجو با 15 پارامتر
    public object Search(
        string keyword,
        int? categoryId,
        string sortBy,
        bool? inStock,
        DateTime? fromDate,
        DateTime? toDate,
        int page,
        int pageSize,
        string culture,
        string currency,
        bool includeInactive,
        int? brandId,
        decimal? minPrice,
        decimal? maxPrice,
        string searchType)
    {
        // مرحله 1: بررسی 15 پارامتر با شرط‌های تو در تو (50 خط)
        if (string.IsNullOrEmpty(keyword) && !categoryId.HasValue) return null;
        if (page < 1) page = 1;
       if (string.IsNullOrEmpty(sortBy)) sortBy = "Name";
        // ... و ده‌ها شرط دیگر
       
        // مرحله 2: ساخت کوئری با رشته‌سازی خطرناک (150 خط)
        string query = "SELECT * FROM Products WHERE 1=1";
        if (!string.IsNullOrEmpty(keyword))
           query += " AND Name LIKE '%" + keyword + "%'";  // خطر SQL Injection!
        if (categoryId.HasValue)
           query += " AND CategoryId = " + categoryId.Value;
        if (minPrice.HasValue)
           query += " AND Price >= " + minPrice.Value;
        // ... و ده‌ها شرط دیگر با رشته‌سازی
       
        // مرحله 3: اجرای کوئری و تبدیل نتایج (100 خط)
        // اتصال به دیتابیس، اجرا، تبدیل به آبجکت، ...
       
        // مرحله 4: کش کردن با کلیدهای عجیب (80 خط)
        string cacheKey = keyword + "_" + categoryId + "_" + sortBy + "_" + page;
        _cache.Set(cacheKey, results, 600);
       
        // مرحله 5: لاگ‌گیری (30 خط)
        _logger.Log("جستجو انجام شد - کلمه: " + keyword);
       
       return results;
    }
   
    // 10 متد خصوصی دیگر با 50-100 خط کد
}

مشکلات این کد:

❌ ۱۵ پارامتر ورودی - مدیریت آنها به‌تنهایی یک چالش بزرگ است
❌ رشته‌سازی کوئری - هم خطرناک، هم غیرقابل‌خواندن
❌ منطق در هم آمیخته - جستجو، کش، لاگ و گزارش همه در یک متد
❌ بدون تست - هیچ تستی برای این کلاس نوشته نشده
❌ تغییر یعنی فاجعه - کوچک‌ترین تغییری در این متد، ۵۰ صفحه سایت را تحت تأثیر قرار می‌دهد


ترس واقعی؛ وقتی فکر کردم باید بیخیال شوم

بعد از چند ساعت بررسی این کد، به خودم گفتم:

💭 "من واقعا نمی‌توانم از این کد سر در بیاورم..."

💭 "اگر اشتباه کنم و تغییرم باگ داشته باشد، خیلی برام گران تمام می‌شود..."

💭 "انگاری بهتر است که از این تغییر صرف‌نظر کنم..."

واقعاً به این فکر کردم که این تغییر را فعلا بیخیال بشم و بعدا سرفرصت انجامش بدم.


💡 اصل طلایی: به کدهای قدیمی دست نزن!

همان شب، وقتی نشسته بودم و به مشکل فکر می‌کردم، ناگهان یک ایده ساده به ذهنم رسید:

"چرا باید به این کدهای قدیمی دست بزنم؟"

💭 "بیا یک متد جدید با نام AdvancedSearch به کلاس اضافه کنم و به کدهای قبلی هیچ دست نزنم..."


🎯 قانون شماره ۱: هرگز به کدهای قدیمی دست نزنید

این اصل اساسی روش تغییر تدریجی است:

✅ کد قدیمی را بدون تغییر نگه دارید
✅ هیچ خطی از کدهای قبلی را تغییر ندهید
✅ فقط یک متد جدید به کلاس اضافه کنید
✅ متد جدید را در چند صفحه محدود تست کنید
✅ بعد از اطمینان، آرام‌آرام متد جدید را جایگزین کنید

چرا اینقدر مهم است؟

  • 🛡️ سیستم همیشه پایدار می‌ماند - چون کد قدیمی که کار می‌کند، دست نخورده باقی می‌ماند
  • 🛡️ ریسک به حداقل می‌رسد - فقط صفحاتی که متد جدید را دارند، ممکن است دچار مشکل شوند
  • 🛡️ برگشت به حالت قبل بسیار ساده است - فقط کافی است متد جدید را غیرفعال کنید
  • 🛡️ بدون استرس و نگرانی - چون همیشه یک راه بازگشت دارید

راه‌حل عملی؛ افزودن متد جدید به کلاس قدیمی

کلاس قدیمی با متد جدید اضافه شده:

// =============================================
// کلاس قدیمی با متد جدید اضافه شده
// =============================================
public class SearchEngine
{
    // =============================================
    // ⛔ متد قدیمی - بدون تغییر و کاملاً سر جای خودش
    // ما به این کدها هیچ دستی نمی‌زنیم!
    // =============================================
    public object Search(/* 15 پارامتر */)
    {
        // ... کدهای قدیمی (بدون تغییر)
    }
   
    // =============================================
    // ✅ متد جدید با نام واضح - اضافه شده به کلاس
    // این متد را تست می‌کنیم و بعد جایگزین می‌کنیم
    // =============================================
    public SearchResult AdvancedSearch(SearchRequest request)
    {
        // 1. اعتبارسنجی ورودی
        if (request == null) return emptyResult;
       
        // 2. بررسی کش
        var cached = cache.Get(cacheKey);
        if (cached != null) return cached;
       
        // 3. ساخت کوئری با پارامتر (امن و خوانا)
        var query = db.Products
                      .Where(p => p.Name.Contains(request.Keyword))
                      .Where(p => p.Price >= request.MinPrice)
                      .Where(p => p.CategoryId == request.CategoryId);
       
        // 4. اعمال صفحه‌بندی و مرتب‌سازی
        var items = query.OrderBy(request.SortBy)
                         .Skip((request.Page - 1) * request.PageSize)
                         .Take(request.PageSize)
                         .ToList();
       
        // 5. کش کردن و لاگ‌گیری
        cache.Set(cacheKey, result);
        logger.Log("جستجوی پیشرفته انجام شد");
       
        return result;
    }
}
// مدل جدید - تمیز و ساده
public class SearchRequest
{
    public string Keyword { get; set; }
    public int? CategoryId { get; set; }
    public decimal? MinPrice { get; set; }
    public decimal? MaxPrice { get; set; }
    public int? BrandId { get; set; }
    public string SortBy { get; set; }
    public int Page { get; set; } = 1;
    public int PageSize { get; set; } = 20;
}
public class SearchResult
{
    public List<Product> Items { get; set; }
    public int TotalCount { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
}

🎯 قانون شماره ۲: متد جدید را تست کنید

بعد از افزودن متد جدید، نوبت تست است:

✅ تست در محیط توسعه - مطمئن شوید که متد جدید درست کار می‌کند
✅ تست با داده‌های واقعی - عملکرد را با داده‌های مشابه تولید بررسی کنید
✅ تست سرعت و کارایی - مطمئن شوید که کندتر از متد قدیمی نیست
✅ تست لبه‌ها (Edge Cases) - ورودی‌های غیرعادی را نیز تست کنید

فقط وقتی از عملکرد متد جدید کاملاً مطمئن شدید، به مرحله بعد بروید.


🎯 قانون شماره ۳: آرام‌آرام و بخش‌بخش جایگزین کنید

پیاده‌سازی در صفحات؛ جایگزینی تدریجی:

صفحه محصولات (قبل از تغییر):

// =============================================
// استفاده از متد قدیمی با 15 پارامتر
// =============================================
public class ProductController
{
    public IActionResult Index(string keyword, int? categoryId, int page = 1)
    {
        // فراخوانی متد قدیمی
        var result = searchEngine.Search(
            keyword, categoryId, null, null, null, null,
            page, 20, "fa-IR", "IRR", false, null, null, null, "default"
        );
        return View(result);
    }
}

صفحه محصولات (بعد از تغییر - پایلوت):

// =============================================
// متد قدیمی کامنت شده - متد جدید جایگزین
// =============================================
public class ProductController
{
    public IActionResult Index(string keyword, int? categoryId, int page = 1)
    {
        // ⛔ متد قدیمی - کامنت شده (دست نمی‌زنیم به کد قبلی!)
        // var result = searchEngine.Search(
        //     keyword, categoryId, null, null, null, null,
        //     page, 20, "fa-IR", "IRR", false, null, null, null, "default"
        // );
        // ✅ متد جدید - جایگزین شده
        var request = new SearchRequest
        {
            Keyword = keyword,
            CategoryId = categoryId,
            Page = page,
            PageSize = 20,
            SortBy = "Name"
        };
       
        var result = searchEngine.AdvancedSearch(request);
        return View(result);
    }
}


روند مهاجرت تدریجی گام‌به‌گام

🚀 گام ۱: فقط یک صفحه را به‌عنوان پایلوت انتخاب کنید

  • صفحه محصولات را انتخاب کردیم
  • متد قدیمی را کامنت کردیم
  • متد جدید را جایگزین کردیم

🚀 گام ۲: تست در محیط توسعه و تست

  • تست‌های مختلف انجام شد
  • مشکلات احتمالی رفع شد
  • از عملکرد درست اطمینان حاصل شد

🚀 گام ۳: انتشار در محیط تولید (فقط ۲ صفحه)

  • دو صفحه اول با متد جدید به تولید رفت
  • مانیتورینگ دقیق انجام شد
  • بازخورد کاربران بررسی شد

🚀 گام ۴: گسترش تدریجی

  • هر روز چند صفحه جدید به متد جدید منتقل شد
  • اگر مشکلی بود، فقط همان چند صفحه تحت تأثیر قرار می‌گرفت

🚀 گام ۵: تکمیل مهاجرت

  • همه ۵۰ صفحه به متد جدید منتقل شدند
  • متد قدیمی به‌طور کامل حذف شد

خلاصه طلایی روش تغییر تدریجی

مرحله

کاری که انجام می‌دهیم

کاری که انجام نمی‌دهیم

۱

یک متد جدید به کلاس اضافه می‌کنیم

به کدهای قدیمی دست نمی‌زنیم

۲

متد جدید را در محیط توسعه تست می‌کنیم

کدهای قدیمی را تغییر نمی‌دهیم

۳

چند صفحه را به‌عنوان پایلوت انتخاب می‌کنیم

به کدهای قدیمی دست نمی‌زنیم

۴

متد جدید را در صفحات پایلوت جایگزین می‌کنیم

به کدهای قدیمی دست نمی‌زنیم

۵

از عملکرد درست مطمئن می‌شویم

به کدهای قدیمی دست نمی‌زنیم

۶

آرام‌آرام بقیه صفحات را جایگزین می‌کنیم

به کدهای قدیمی دست نمی‌زنیم

۷

بعد از اطمینان کامل، متد قدیمی را حذف می‌کنیم

-


نتیجه‌گیری

این تجربه به من ثابت کرد که:

💭 "روش تغییر یکباره چقدر احمقانه بود!"

با رعایت این سه قانون طلایی:

  • ✅ قانون ۱: به کدهای قدیمی دست نزنید
  • ✅ قانون ۲: متد جدید را تست کنید
  • ✅ قانون ۳: آرام‌آرام جایگزین کنید

نتایج فوق‌العاده بود:

  • ✅ سیستم هیچ‌وقت متوقف نشد
  • ✅ هیچ استرسی نداشتم
  • ✅ ریسک به حداقل رسید
  • ✅ برگشت به حالت قبل، فوق‌العاده ساده بود

💭 "وای چقدر خوب شد این راه را پیدا کردم!"


پیام نهایی به شما

به کدهای قدیمی دست نزنید!

فقط کافی است یک متد جدید بنویسید، آن را تست کنید و بعد از اطمینان، آرام‌آرام آن را با متد قبلی جایگزین کنید.

دفعه بعد که با یک کد اسپاگتی و پیچیده مواجه شدید و ترس از تغییر به سراغتان آمد، به یاد داشته باشید:

شما مجبور نیستید همه‌چیز را یک‌جا تغییر دهید.

می‌توانید قدم‌به‌قدم، با افزودن متدهای جدید و جایگزینی تدریجی، سیستم خود را متحول کنید.