Why Developers Hate Legacy Code? چرا برنامهنویسان از کدهای قدیمی متنفرند؟
تصور کنید وارد یک پروژه نرمافزاری میشوید که چند سال از عمر آن گذشته است. قرار است فقط یک قابلیت ساده به آن اضافه کنید؛ مثلاً امکان پرداخت با کیف پول دیجیتال را به یک فروشگاه اینترنتی اضافه کنید. اما وقتی فایلهای پروژه را باز میکنید، با هزاران خط کد نامرتب، متدهای طولانی، اسمهای نامفهوم و بخشهایی روبهرو میشوید که هیچکس دقیقاً نمیداند چه کاری انجام میدهند!
بدتر از همه اینکه با تغییر یک قسمت کوچک، ناگهان بخش دیگری از برنامه از کار میافتد؛ مثلاً صفحه ثبت سفارش خطا میدهد یا اطلاعات کاربران بهدرستی ذخیره نمیشود.
اینجاست که احتمالاً برنامهنویس با خودش میگوید: «چه کسی این کدها را نوشته؟!»
این اتفاق در دنیای برنامهنویسی بسیار رایج است و یکی از دلایلی است که بسیاری از توسعهدهندگان هنگام کار با پروژههای قدیمی احساس خستگی و کلافگی میکنند.
اما چرا برنامهنویسان از Legacy Code یا کدهای قدیمی متنفرند؟ آیا هر کد قدیمی الزاماً کد بدی است؟
Legacy Code چیست؟
Legacy Code یا کد قدیمی به کدهایی گفته میشود که در پروژههای نرمافزاری قدیمی وجود دارند و معمولاً تغییر، توسعه یا نگهداری آنها دشوار است.
این کدها ممکن است سالها قبل نوشته شده باشند و همچنان در یک سازمان، بانک، فروشگاه اینترنتی یا نرمافزار حسابداری مورد استفاده قرار بگیرند.
برای مثال، فرض کنید یک فروشگاه اینترنتی را در نظر بگیرید که از ۱۰ سال پیش فعالیت میکند. در ابتدا این فروشگاه فقط امکان نمایش محصولات و ثبت سفارش را داشته است؛ اما بهمرور قابلیتهایی مانند پرداخت آنلاین، ارسال پیامک، تخفیف، کیف پول دیجیتال و پیگیری سفارش نیز به آن اضافه شدهاند.
در طول این سالها، برنامهنویسان مختلفی روی پروژه کار کردهاند و هرکدام تغییراتی در کدها ایجاد کردهاند.
نتیجه ممکن است نرمافزاری باشد که همچنان بهدرستی کار میکند، اما ساختار داخلی آن پیچیده و تغییر دادنش بسیار دشوار شده است.
نکته مهم: هر کد قدیمی الزاماً Legacy Code بد نیست. ممکن است یک پروژه ۱۵ ساله ساختاری منظم، مستند و قابل توسعه داشته باشد. مشکل اصلی زمانی ایجاد میشود که کدها بدون توجه به اصول طراحی، مستندسازی و نگهداری توسعه پیدا کنند.
چرا برنامهنویسان از Legacy Code متنفرند؟
دلایل مختلفی وجود دارد که کار با کدهای قدیمی را برای توسعهدهندگان دشوار میکند. بیایید مهمترین آنها را بررسی کنیم.
۱. هیچکس نمیداند کدها دقیقاً چگونه کار میکنند!
یکی از بزرگترین مشکلات پروژههای قدیمی، نبود مستندات مناسب است.
فرض کنید وارد پروژهای میشوید که برنامهنویس اصلی آن چند سال پیش شرکت را ترک کرده است. حالا شما باید بخشی از سیستم را تغییر دهید، اما هیچ توضیحی درباره نحوه عملکرد متدها، ارتباط فایلها یا منطق برنامه وجود ندارد.
حتی ممکن است نام بعضی متغیرها و توابع بهاندازهای نامفهوم باشد که نتوانید از روی آنها متوجه کاربردشان شوید.
مثلاً:
int x = GetData(a, b);
در این کد مشخص نیست x چه اطلاعاتی را نگهداری میکند و متد GetData دقیقاً چه کاری انجام میدهد.
در یک پروژه استاندارد بهتر است نامها هدف و کاربردشان را مشخص کنند.
برای مثال:
int totalPrice = CalculateOrderTotal(price, tax);
حالا خواندن کد و درک عملکرد آن بسیار سادهتر است.
۲. با تغییر یک قسمت، بخشهای دیگر خراب میشوند!
تصور کنید در یک اپلیکیشن فروشگاهی، قرار است روش محاسبه تخفیف محصولات را تغییر دهید.
در یک پروژه اصولی، این قابلیت باید ساختار مشخصی داشته باشد. اما در یک پروژه قدیمی و پیچیده ممکن است محاسبه تخفیف در چندین فایل و متد مختلف تکرار شده باشد.
در نتیجه با تغییر یک قسمت، ممکن است محاسبه قیمت در صفحه سبد خرید، فاکتور نهایی یا گزارش فروش نیز دچار مشکل شود.
به این وضعیت معمولاً وابستگیهای پیچیده (Tight Coupling) گفته میشود.
هرچه بخشهای مختلف برنامه وابستگی بیشتری به یکدیگر داشته باشند، ایجاد تغییرات جدید دشوارتر و پرریسکتر خواهد بود.
۳. تست کردن برنامه تقریباً غیرممکن میشود!
یکی از مهمترین راههای اطمینان از عملکرد صحیح نرمافزار، استفاده از تستهای خودکار (Automated Tests) است.
تستها به برنامهنویسان کمک میکنند بعد از ایجاد تغییرات، بررسی کنند که قابلیتهای قبلی همچنان درست کار میکنند یا خیر.
اما بسیاری از پروژههای قدیمی هیچ تستی ندارند!
فرض کنید قرار است سیستم پرداخت یک فروشگاه اینترنتی را تغییر دهید. اگر تست مناسبی وجود نداشته باشد، چطور مطمئن میشوید که بعد از تغییر کدها، سفارشهای قبلی، محاسبه هزینه ارسال و ثبت تراکنشها همچنان درست کار میکنند؟
در چنین شرایطی برنامهنویس مجبور است بخشهای مختلف برنامه را بهصورت دستی بررسی کند؛ کاری که هم زمانبر است و هم احتمال خطای انسانی در آن وجود دارد.
۴. استفاده از فناوریهای قدیمی و ناسازگار
دنیای برنامهنویسی دائماً در حال تغییر است. زبانهای برنامهنویسی، فریمورکها، کتابخانهها و ابزارهای توسعه مرتب بهروزرسانی میشوند.
اما پروژههای قدیمی همیشه امکان استفاده آسان از فناوریهای جدید را ندارند.
برای مثال، ممکن است یک نرمافزار سازمانی با نسخه قدیمی .NET Framework نوشته شده باشد و به کتابخانههایی وابسته باشد که دیگر پشتیبانی نمیشوند.
حالا اگر تیم توسعه بخواهد قابلیت جدیدی مانند اتصال به سرویسهای ابری، استفاده از APIهای مدرن یا پیادهسازی احراز هویت جدید را به آن اضافه کند، ممکن است با مشکلات سازگاری زیادی روبهرو شود.
گاهی حتی ارتقای یک کتابخانه ساده میتواند بخشهای مختلف برنامه را دچار مشکل کند.
۵. ترس از تغییر کدها!
یکی از عجیبترین مشکلات Legacy Code این است که گاهی برنامهنویسان از تغییر دادن آن میترسند!
فرض کنید متدی را مشاهده میکنید که ۲۰۰ خط کد دارد و چندین بخش مختلف برنامه از آن استفاده میکنند.
شما متوجه میشوید که این متد ساختار مناسبی ندارد و باید اصلاح شود؛ اما نمیدانید تغییر آن چه تأثیری روی قسمتهای دیگر خواهد گذاشت.
در چنین شرایطی ممکن است تصمیم بگیرید اصلاً به آن دست نزنید!
این اتفاق باعث میشود مشکلات قدیمی باقی بمانند و بهمرور زمان، توسعه پروژه سختتر شود.
آیا Legacy Code همیشه چیز بدی است؟
خیر! این یکی از مهمترین نکاتی است که باید درباره کدهای قدیمی بدانیم.
بسیاری از نرمافزارهای مهم و بزرگ دنیا سالهاست فعالیت میکنند و بخشهایی از کد آنها ممکن است قدمت زیادی داشته باشد.
برای مثال، سیستمهای بانکی، نرمافزارهای حسابداری، سامانههای دولتی و سیستمهای مدیریت سازمانی معمولاً سالها مورد استفاده قرار میگیرند.
تعویض کامل چنین سیستمهایی میتواند بسیار پرهزینه، زمانبر و حتی خطرناک باشد.
بنابراین قدیمی بودن کد بهتنهایی مشکل محسوب نمیشود؛ مهم این است که آیا میتوان آن را بهراحتی فهمید، آزمایش کرد، تغییر داد و توسعه داد یا خیر.
گاهی یک کد قدیمی اما منظم، بسیار بهتر از کدی جدید اما نامرتب است!
برنامهنویسان چگونه با Legacy Code کنار میآیند؟
حالا که با مشکلات کدهای قدیمی آشنا شدیم، سؤال مهم این است: آیا باید چنین پروژههایی را از ابتدا بازنویسی کنیم؟
معمولاً نه!
بازنویسی کامل یک نرمافزار قدیمی میتواند هزینه زیادی داشته باشد و حتی مشکلات جدیدی ایجاد کند. به همین دلیل برنامهنویسان حرفهای معمولاً از روشهای تدریجی برای بهبود کدها استفاده میکنند.
مهمترین این روشها عبارتاند از:
۱. Refactoring (بازآرایی کد):
در این روش ساختار داخلی کدها بهبود پیدا میکند، بدون اینکه رفتار اصلی برنامه تغییر کند. برای مثال، یک متد بسیار طولانی به چند متد کوچکتر و قابل فهم تقسیم میشود.
۲. نوشتن تستهای خودکار:
قبل از ایجاد تغییرات مهم، تستهایی نوشته میشوند تا عملکرد بخشهای حساس برنامه بررسی شود و احتمال خراب شدن قابلیتهای قبلی کاهش پیدا کند.
۳. مستندسازی کدها:
برنامهنویسان برای قسمتهای پیچیده توضیحات مناسبی مینویسند تا توسعهدهندگان بعدی راحتتر متوجه منطق برنامه شوند.
۴. حذف کدهای تکراری:
اگر یک منطق مشخص در چندین قسمت برنامه تکرار شده باشد، میتوان آن را در یک بخش مستقل قرار داد و از همان قسمت در بخشهای مختلف استفاده کرد.
۵. مهاجرت تدریجی به فناوریهای جدید:
بهجای تغییر یکباره کل پروژه، میتوان بخشهای مهم را بهمرور به فناوریها و ساختارهای جدید منتقل کرد.
ارتباط Legacy Code با Technical Debt چیست؟
اگر با مفهوم Technical Debt یا بدهی فنی آشنا باشید، احتمالاً متوجه شدهاید که این دو مفهوم ارتباط نزدیکی با یکدیگر دارند.
بدهی فنی زمانی ایجاد میشود که توسعهدهندگان برای تحویل سریعتر پروژه، راهحلهای ساده اما غیراصولی انتخاب کنند و بهبود ساختار کد را به آینده موکول کنند.
برای مثال، فرض کنید یک تیم برای اینکه قابلیت جدید فروشگاه را سریعتر تحویل دهد، چندین شرط تکراری به کد اضافه میکند.
این تصمیم شاید در کوتاهمدت باعث صرفهجویی در زمان شود، اما در آینده توسعه و نگهداری پروژه را دشوارتر میکند.
اگر چنین تصمیمهایی بارها تکرار شوند، ممکن است پروژه بهمرور به مجموعهای از کدهای پیچیده و سختنگهداری تبدیل شود.
البته هر Legacy Code الزاماً نتیجه بدهی فنی نیست؛ اما بدهی فنی یکی از عواملی است که میتواند وضعیت کدهای قدیمی را بدتر کند.
جمعبندی
Legacy Code یکی از واقعیتهای دنیای توسعه نرمافزار است. تقریباً هر برنامهنویسی که در پروژههای واقعی و بزرگ فعالیت کند، دیر یا زود با کدهایی روبهرو میشود که توسط افراد دیگری نوشته شدهاند و درک یا تغییر آنها آسان نیست.
مشکل اصلی کدهای قدیمی، سن آنها نیست؛ بلکه پیچیدگی، نبود مستندات، وابستگیهای زیاد، تستهای ناکافی و دشواری نگهداری آنهاست.
یک برنامهنویس حرفهای فقط کسی نیست که بتواند از ابتدا کد بنویسد؛ بلکه باید بتواند کدهای دیگران را بخواند، مشکلات آنها را تشخیص دهد و بدون آسیب زدن به عملکرد برنامه، آنها را بهبود ببخشد.
بنابراین اگر روزی وارد یک پروژه قدیمی شدید و با دیدن هزاران خط کد نامفهوم احساس کردید کار سختی در پیش دارید، نگران نباشید! این هم بخشی از مسیر واقعی برنامهنویسی است.
فرایانه؛ جایی که مفاهیم پیچیده برنامهنویسی را به زبان ساده، کاربردی و قابل فهم آموزش میدهیم. 💚