
در اپیزود هشتم "کدشناسی"، با الهام از زیست یک درخت و مفهوم "بیونیک"، به بررسی یک استراتژی نوین در مهاجرت سیستمهای نرمافزاری قدیمی میپردازیم. این اپیزود به این پرسش میپردازد که چگونه میتوان یک پروژه نرمافزاری بزرگ را، بدون نیاز به بازنویسی کامل از ابتدا یا مواجهه با ریسکهای پرخطر مهاجرت یکپارچه، به تدریج مدرن ساخت. با تمرکز بر الگوی "Strangler Fig" که از فرآیند رشد درخت انجیر خفهکننده الهام گرفته شده است، تحلیل میکنیم که چگونه این رویکرد امکان ایجاد قابلیتهای جدید را به صورت مرحلهای در کنار سیستم موجود فراهم میآورد و مسئولیتها را به آرامی به آنها منتقل میکند. این الگو نه تنها ریسکهای مرتبط با مهاجرتهای کلان را به حداقل میرساند، بلکه تداوم فعالیتهای کسبوکار را در طول فرآیند تحول تضمین میکند. اگر با چالش بهروزرسانی سیستمهای قدیمی مواجه هستید یا به دنبال راهکارهای هوشمندانه برای مهاجرت بدون اختلال میگردید، شنیدن این اپیزود را به شما توصیه میکنم.
منابع این اپیزود :
متن اپیزود
تا حالا به این فکر کردید که ایدهی اولیهی خیلی از چیزهایی که هر روز باهاشون سروکار داریم، مثل ساختمونهایی که توشون زندگی میکنیم، هواپیماهایی که سوارشون میشیم یا حتی الگوریتمهای هوش مصنوعی که ازشون استفاده میکنیم، از کجا اومده؟ طبیعت، یکی از بهترین منابعی بوده که انسان در طول تاریخ برای پیدا کردن ایدههای اولیه یا راهحلهای خیلی از مشکلات تو حوزههای مختلف، بهش رجوع کرده! اصطلاحاً به این نوع ایدهها و راهحلهایی که با تقلید از طرحها و فرآیندهای طبیعی برای نوآوری تو حوزههای مختلف به وجود میان، بایونیک یا بیونیک میگن. ولی میدونید چرا این ایدهها برای ما آدما قابلقبولترن و زودتر پذیرفته میشن؟
اول اینکه رابطهی ما آدما با طبیعت یه ارتباط ذاتیه. وقتی یه ایده از دل طبیعت میاد، ناخودآگاه یه حس آشنایی و آرامش بهمون میده.
دوم اینکه طبیعت خودش یه نمونه واقعی و امتحانشدهس! یه ایدهای که توی طبیعت برای مدتهای طولانی کار کرده و جواب داده، پذیرفتنش برای ما هم سادهتره.
رویکرد بیونیک توی حوزههای مختلفی مثل مهندسی و معماری، پزشکی، علوم کامپیوتر و هوش مصنوعی و کلی فیلد دیگه استفاده میشه.
اما رویکردی که تو این اپیزود میخوام با یه نیمنگاهی به همین موضوع بهش بپردازم، داستان زندگی یه درخت شگفتانگیزه که بهش میگن انجیر خفهکننده یا (Strangler Fig)!
این درخت زیست خیلی عجیبی داره. بذر اولیهش از طریق پرندهها، باد یا چیزای دیگه به درختای دیگه میرسه و از مواد مغذی همون درختا تغذیه میکنه. اولین جوانههاش روی شاخههای اون درخت میزبان زده میشه و بعد آرومآروم و به شکل عجیبی ریشههاش برعکس، یعنی از بالا به سمت پایین رشد میکنن و کمکم دور تنهی درخت میزبان رو میگیرن و به سمت خاک پیش میرن!
این رشد اونقدر ادامه پیدا میکنه که تمام تنهی درخت میزبان رو ریشههای انجیر خفهکننده میپوشونن. تازه، با رشد برگهای این درخت، منبع نوری هم از درخت میزبان گرفته میشه و در نهایت، درخت میزبان شروع به پوسیدن میکنه و آرومآروم از بین میره!
و در نهایت چیزی که باقی میمونه یه ساختار توخالی از درخت انجیر خفهکننده است که شبیه به یه تونل یا ستونه.
چندین سال پیش، مارتین فاولر که یکی از اسمهای تأثیرگذار تو حوزهی مهندسی نرمافزار هم هست، با الهام از فرآیند رشد همین درخت ایدهی یه پترن برای بازسازی و مایگریت تدریجی سیستمهای نرمافزاری قدیمی به سیستمهای مدرن به ذهنش رسید که با نام Strangler Pattern معروف شد.
تو این اپیزود قراره در مورد همین پترن یکم صحبت کنیم!
پس طبق روال هر اپیزود، چند تا سوال کلیدی رو مطرح میکنم و در طول این قسمت بهشون جواب میدیم:
چه روشهایی برای مایگریت کردن پروژههای نرمافزاری وجود داره؟
Strangler Pattern چیه و چطوری میتونه تو مایگریت کردن پروژههای نرمافزاری کمک کنه؟
مزایا و معایب این روش نسبت به سایر روشها چیه؟
و چطور میتوانیم از این الگو تو پروژههای نرمافزاری استفاده کنیم؟
🎙️ بخش اول - معرفی اپیزود
"سلام! من محمدم و این اپیزود هشت از پادکست کُدشناسیه. تو هر اپیزود از این پادکست، معمولاً یه چالش، یه موقعیت یا یه جستار فکری رو از دنیای مهندسی نرمافزار مطرح و کمی روش مکث میکنیم.
موضوعی که توی این اپیزود میخوام بهش بپردازم، یک الگوی نرمافزاری به نام Strangler Fig هست که به عنوان یک رویکرد برای مایگریت کردن تدریجی پروژههای نرمافزاری بزرگ با حداقل ریسک شناخته میشه. سعی میکنم سوالهای مطرح شده تو بخش اول رو با کمک یهسری منابع و تجربههای شخصی، بررسی و اگه شد، کمی روشون عمیق شیم.
✈️ بخش دوم - چرا باید پروژهمون رو مایگریت کنیم؟
قبل از اینکه سراغ اصل ماجرا بریم و در مورد الگوی Strangler Fig صحبت کنیم، بیاین یه لحظه وایسیم و با خودمون فکر کنیم: اصلاً چرا باید یه پروژه نرمافزاری رو مایگریت کنیم؟
معمولاً اصلیترین دلایلی که برای مهاجرت پروژههای نرمافزاری وجود داره، برمیگرده به چالشهایی مثل پیچیدگی تو پیادهسازی ویژگیهای جدید، یا سخت بودن پیادهسازی تغییرات ساده یا مشکلات اسکیلکردن (مقیاسپذیری) پروژه.
این موارد، علاوه بر اینکه روی توسعه کسبوکار تاثیر مستقیم میذارن، باعث کندی و در نهایت افزایش هزینههای کلی پروژه میشن.
یه جورایی، پروژه توی یه بنبست قرار میگیره که عملاً ادامه دادنش، هزینه بیشتری از متوقف کردنش داره! تو این وضعیت، مایگریت سیستم قدیمی به یه سیستم جدید، یکی از راهکارهای خلاص شدن از این چالشهاست. اما خب، این کار یه پروسه پر زحمت و پر ریسکه و متاسفانه تجربههای ناموفق زیادی هم تو شرکتهای نرمافزاری وجود داره.
مثلاً، سیستم پرداخت حقوق شرکت Queensland Health استرالیا تو سال ۲۰۱۰ یکی از فاجعهبارترین پروژههای مایگریشن IT تو تاریخ بود.
اونها بعد از مهاجرت سیستم پرداخت حقوق قدیمیشون، به دلیل عجله تو پیادهسازی و کمبود فرصت برای تست، تجربه وحشتناکی رو پشت سر گذاشتن.
هزاران کارمند یا حقوق نگرفتن، یا کمتر/بیشتر از حد گرفتن که باعث هرجومرج گستردهای شد و در نتیجه، این مهاجرت ناموفق بیشتر از ۱.۲ میلیارد دلار استرالیا ضرر به بار آورد!
یا سیستم انبارداری Target کانادا تو سال ۲۰۱۵؛ که وقتی وارد بازار کانادا شد، یکی از مشکلات بزرگش سیستم مدیریت موجودیش بود.
بعد از اینکه تصمیم گرفتن پروژه رو مایگریت کنن و یه سیستم جدید پیادهسازی کنن، با یک شکست واقعی روبهرو شدن.
این سیستم موجودی غلط میداد و منجر به مشکلات زنجیره تامین شد و در نهایت، شکست کامل و خروج تارگت از بازار کانادا رو به همراه داشت که میلیونها دلار ضرر به بار آورد.
اما خب، تا دلتون بخواد تجربه موفق هم داشتیم که با موفقیت این پروسه رو انجام دادن، مثل مایگریشن پروژههای بزرگی مثل آمازون، نتفلیکس یا شاپیفای.
برای پروسه انتقال یه پروژه قدیمی به یه پروژه مدرن، راهکارهای متفاوتی وجود داره که هر کدوم میتونه بسته به میزان ریسک و حساس بودن پروژه، ابعاد اون، میزان پیچیدگی سیستم و همینطور بودجه، زمان و تعداد افراد تیم، متفاوت باشه.
بذارید یه مختصر در مورد انواع این روشها صحبت کنیم:
اولین روش رویکرد "بیگ بنگ" (Big Bang Rewrite / Greenfield Development) هست.
تو این روش، کل سیستم قدیمی رو کنار گذاشته میشه و یه سیستم کاملاً جدید رو از صفر مینویسین. سیستم قدیمی تا لحظه آخر کار میکنه و بعدش یهو خاموش میشه و سیستم جدید جایگزین سیستم قبلی میشه.
از مزایا این روش میشه به امکان استفاده از جدیدترین تکنولوژیها و معماریها بدون محدودیتهای سیستم قدیمی و پیادهسازی یه کد تر و تمیز و بدون بدهی فنی (Technical Debt) اولیه اشاره کرد.
از طرف دیگه، این روش ریسک بسیار بالایی داره و اگه پروژه به مشکل بخوره یا زمانبر بشه، ممکنه سرمایهگذاری عظیمی هدر بره و در صورتی که یه قسمتی که جدید پیادهسازی شد مشکل داشته باشه امکان برگشت به سیستم قبلی رو هم نداریم! پس کار باید خیلی بینقص دربیاد!
از معروفترین نمونههایی که تو ایران از این روش برای مایگریت پروژه قدیمی استفاده کردن، دیجیکالا بود که پروژه قبلیشون به صورت کامل به یه پروژه مدرن بازنویسی شد.
دومین روش رویکرد "مهاجرت موازی" (Parallel Migration) هست. تو این روش، سیستم جدید به طور کامل پیادهسازی و راهاندازی میشه، اما ترافیک باید بین هر دو سیستم تقسیم بشه یعنی بخشی از ترافیک رو میفرستیم به سرویس جدید و بخشی رو هم به سرویس قدیمی. اینطوری میتونیم نتایج هر دو سیستم رو با هم مقایسه کنیم تا مطمئن بشیم سیستم جدید داره درست کار میکنه و بعد از اون سیستم قدیمی کلاً از دسترس خارج میشه. از مزایا این روش میشه به ریسک بسیار پایینش اشاره کرد. تو این روش اگه هر مشکلی رخ بده میتونیم خیلی راحت برگردیم رو نسخه قبلی یا اصطلاحا Rollback کنیم!
حتی میتونیم سیستم جدید رو با حداقل ریسک تو محیط پروداکشن و زیر بار کاربرای و با دادههای واقعی تست کنیم.
ولی از طرف دیگه، برای این کار مجبوریم برای یه بازه زمانی که میتونه طولانی هم باشه دو تا سیستم رو همزمان نگهداری کنیم و این یعنی هزینههای بالا.
آخرین روش رویکرد "مرحلهای" (Phased Migration) هست.
تو این روش، سیستم به بخشهای کوچکتری تقسیم میشه و هر بخش رو میتونیم به صورت مرحلهای مایگریت کنیم!
یعنی تغییرات رو به صورت کوچیک شده و در طول زمان انجام میدیم.
این روش میتونه شامل ریفکتور کلی پروژه یا بروزرسانی بخشی از پروژه یا حتی انتقال بخشبخش به زیرساخت جدید باشه.
این روش ریسک کمی داره چون تغییرات تدریجی انجام میشه و در نتیجه ریسک کلی انتقال به شدت میاد پایین.
تو این روش میشه هر بخش رو بعد از مایگریت خیلی راحت تست کرد و از کاربران نهایی بازخورد گرفت و اینجوری مدیریت کردن هزینهها و زمان خیلی راحتتر میشه.
ولی از طرف دیگه، این روش هم پیچیدگی نگهداری همزمان دو تا سرویس رو داره و اگه درست مدیریت نشه، خودش میتونه حتی بدهی فنی رو هم زیاد کنه!
حالا فکر میکنم با این مقدمه، میتونیم بریم سراغ بررسی Strangler Pattern.
🌰 بخش سوم - Strangler Fig چیست و چگونه کار میکند؟
Strangler Pattern در واقع یک استراتژی هست!
یعنی ما تصمیم میگیریم که سیستم قدیمی را مرحله به مرحله با چیز جدیدی جایگزین کنیم. نکته مهم اینه که این تصمیمها برای مایگریت کردن بخشهای مختلف، صرفاً تکنیکال نیست و حتماً باید عملکرد کسبوکار هم در نظر گرفته بشه.
با این تفاسیر، این استراتژی زیرمجموعه رویکرد مرحلهای (Phased Migration) قرار میگیره!
حالا برگردیم سر سوال اصلی: Strangler Pattern چیه و چطوری میتونه تو مایگریت کردن پروژههای نرمافزاری کمک کنه؟
همونطور که تو بخش اول در مورد چرخه زندگی درخت انجیر خفهکننده صحبت کردم و متوجه شدیم چطوری ریشههای این درخت از بالا به پایین شروع به رشد میکنن و کمکم درخت میزبان رو در بر میگیرن تا جایی که اون رو خفه میکنن و خودشون به ساختاری مستقل تبدیل میشن، دقیقاً استراتژی که تو این روش برای مهاجرت یک پروژه استفاده میشه، همینه.
ما به جای اینکه یه درخت جدید رو از صفر کنار درخت اصلی بکاریم و بعد از قد کشیدن درخت دوم درخت اولی رو قطع کنیم، میایم یک سیستم جدید رو اطراف سیستم قدیمی میسازیم.
اگه سادهتر بخوام توضیح بدم مثل ریشههای درخت که به دور تنه میپیچن ما هم باید یک لایه رو تنه پروژه اصلی بکشیم و این لایه با یه User Interface جدید یا یه لایه API روی رابط کاربری قبلی یا API ها شروع میشه.
فکر کنید یه سیستم قدیمی بزرگ دارین، مثلاً یه فروشگاه آنلاین. برای پیادهسازی این الگو میایم و یه "نما" یا یه لایه "پروکسی" جدید روی سیستم قدیمی قرار میدیم. حالا تمام درخواستهای جدید یا اونهایی که میخوایم بازنویسی بشن، اول از این لایه باید عبور کنن. بعد، بخش به بخش یا فیچر به فیچر شروع میکنیم به بازنویسی و مایگریت کردن پروژه قدیمی.
مثلاً، اول ماژول پرداخت رو بازنویسی میکنیم. وقتی مشتریای میخواد پرداخت انجام بده، درخواستی که از طریق این نما میاد، به جای اینکه بره سراغ سیستم پرداخت قدیمی، به سیستم پرداخت جدید ما که تازه ساختیم هدایت میشه. تو این حالت، دو تا سیستم همزمان در حال کارن: سیستم قدیمی برای بیشتر بخشهایی که هنوز مایگریت نشده و سیستم جدید برای بخش پرداخت.
این روند همینطوری ادامه ادامه پیدا میکنه. مثلا بعد از پرداخت، میریم سراغ ماژول سبد خرید، بعد مدیریت کاربران و همینطور الی آخر. با هر بخشی که از سیستم قدیمی به سیستم جدید منتقل میشه، مسئولیتها و ترافیک از سیستم قدیمی گرفته میشه. این دقیقاً همون مرحلهای هست که ریشههای انجیر خفهکننده دور درخت میزبان محکم میشن و نور رو ازش میگیرن.
کمکم، بخشهای سیستم قدیمی از کار میافتند و عملاً مسئولیتشون به سیستم جدید منتقل شده. در نهایت، سیستم قدیمی از بین میره و میتونیم بدون هیچ دردسری اون رو از مدار خارج کنیم و چیزی که باقی میمونه، همون سیستم جدید ماست که مثل یه ساختار مستحکم و توخالی شکل گرفته و قابل استفاده است!
🔰 بخش چهارم - مزایا و معایب Strangler Fig در مقایسه با روشهای دیگه
همونطوری که متوجه شدیم تدریجی بودن انتقال مزیت اصلی این رویکرده و به شدت ریسک رو کاهش میده و اگه مشکلی پیش بیاد فقط یک بخش کوچک تحت تاثیر قرار میگیره و میتونیم سریعاً اون بخش رو برگردیم به حالت قبل.
این روش مزیتش اینه که بدون اینکه بخش بیزینس رو درگیر کنه پروژه میتونه به کارش ادامه بده و همون چیزی که خیلی برای پروژههای بزرگ ضروریه!
اما خب، هر روشی چالشهای خودش رو هم داره. یکی از معایب Strangler Fig اینه که برای یه مدت طولانی، شما مجبورید هم سیستم قدیمی و هم سیستم جدید رو کنار هم نگه دارید و همونطور که قبلا این مساله رو بررسی کردیم این یعنی نیازمندی به منابع و در نتیجه هزینههای بالاتر برای پروژه!
یکی از مسائل دیگهای که قبل از پیادهسازی این روش باید در نظر گرفته بشه اولویتبندی بخشهایی هست که باید اول از سیستم جدا بشن و مساله بعدی نحوهی این جداسازی هست!
بر اساس پروژههای مختلف این مساله میتونه متفاوت باشه و نیاز به بررسی دقیق داره و اگه درست برنامهریزی نشه، ممکنه با وجود مزیتهای این الگو، به هم ریختگیهایی تو سیستم ایجاد کنه و در نهایت، شاید زمان کلی مایگریت کردن رو ببره بالا!
اما در مجموع به نظر من طولانی شدن پیادهسازی ریسک کمتری نسبت به خطراتی که تو یه پیادهسازی اشتباه باهاش روبرو میشیم داره!
🚀 بخش پنجم - چطور میتوانیم از این الگو تو پروژههای نرمافزاری استفاده کنیم؟
حالا که با مزایا و معایب این الگو آشنا شدیم، سوال اینجاست که چطور میتونیم از این الگو تو پروژههای نرمافزاری خودمون استفاده کنیم؟
یکم در مورد این مساله تو بخشهای قبل صحبت کردیم ولی بیاید یکم با جزئیات بیشتر ببینیم چطور میشه این کار رو انجام داد.
اولین قدم اولویتبندی هست! یعنی باید بخشهای که اولویت بالاتری دارند رو برای مایگریت کردن شناسایی کنیم و ببینیم کدوم قسمتهای سیستم قدیمی بیشتر مشکلسازن؛
این مشکلا میتونه بر اساس میزان تولید باگ تو پروژه، با کُند کردن سرویس یا سخت بودن توسعه تو این بخشها باشه!
یا شاید هم برعکس، بخشهایی باشه که برای بیزینس اولویت بیشتری داره.
نکته مهم اینه که معمولاً از قابلیتهای پرکاربرد یا اونهایی که وابستگی کمتری به بقیه قسمتها دارن باید شروع کنیم تا هم به اولویتهای بیزینس برسیم و هم اون بخش رو تو مقیاس کوچکتر تست کنیم.
قدم بعدی، ایجاد اون "Proxy" یا پروکسی هست که قبلاً در موردش صحبت کردیم.
ما باید یک لایه جدید رو، مثلاً یک API Gateway یا یک سرویس پروکسی ساده، جلوی سیستم قدیمیمون قرار بدیم.
از حالا به بعد، تمام درخواستها باید اول از این لایه جدید عبور کنن.
بعد از این، میتونیم پیادهسازی بخشهای جدید رو یکی یکی شروع کنیم.
برای هر بخش که انتخاب کردیم، ماژول جدید رو به عنوان یک سرویس مستقل یا بخشی از سیستم مدرنمون پیادهسازی میکنیم. بعدش، از طریق همون Proxy ، درخواستهای مربوط به اون قابلیت خاص رو به سرویس جدید میفرستیم.
مثلاً اگه ماژول پرداخت رو بازنویسی کردیم، وقتی مشتریای میخواد پرداخت انجام بده، Facade اون رو به سیستم پرداخت جدید میفرسته، در حالی که بقیه درخواستها هنوز به سیستم قدیمی میرن.
ممکنه برای سینک دیتا بین سیستم قدیمی و جدید هم، نیاز باشه حرکتهایی انجام بدیم.
همینطور که جلو میریم، تست کردن و مطمئن شدن ۱۰۰ درصدی از کار کردن بخشهای پیادهسازی شده خیلی مهمه.
در کنار اون مانیتور کردن بخشهای جدید هم برای شناسایی هر باگ احتمالی خیلی ضروری و مهمه!
تو این مرحله میشه لاگهایی رو تو بخشهای کد قدیمی اضافه کرد تا مطمئن بشیم هیچ ترافیکی سمتشون نمیاد و عملاً استفاده نمیشن! در نهایت هم وقتی مطمئن شدیم فیچرهای جدید بدون مشکل کار میکنند میتونیم کد یا سرویس قدیمی مربوط به اون قابلیت رو بدون هیچ مشکلی حذف کنیم.
این همون لحظهایه که بخشهای سیستم قدیمی "خفه" میشن و ما از شرشون خلاص میشیم.
پس به طور کلی، الگوی Strangler Fig بهترین گزینه است برای سیستمهای قدیمی و بزرگی که نگهداریشون سخته، یا برای پروژههایی که ریسکپذیری کمی دارن و نمیتونن Downtime طولانی داشته باشن. همینطور اگه تیمها میخوان به سمت معماری میکروسرویس یا یه معماری مدرن حرکت کنن یا بودجه و زمان کافی برای بازنویسی کامل (به روش بیگ بنگ) وجود نداره، این الگو میتونه یه راه مناسب برای این کار باشه!
🏁 بخش پایانی
"خب، بیایید یک جمعبندی در مورد این اپیزود داشته باشیم!
توی این اپیزود، اول با بررسی یک الگو تو طبیعت و مفهوم بیونیک شروع کردیم و رسیدیم به داستان شگفتانگیز درخت انجیر خفهکننده. بعد دیدیم که چطور این داستان به الگوی قدرتمند Strangler Fig Pattern تو دنیای مهندسی نرمافزار تبدیل شده.
تو ادامه بررسی کردیم که اصلاً چرا نیاز به مهاجرت پروژهها پیدا میشه و چه رویکردهای دیگهای مثل بیگ بنگ یا مهاجرت موازی برای این کار وجود داره.
جلوتر بررسی کردیم که Strangler Fig چطور کار میکنه؛ یعنی چطور با استفاده از یک نما یا پروکسی، قابلیتهای جدید روی سیستم قدیمی میسازیم و مسئولیتها رو بخش به بخش به سیستم جدید منتقل میکنیم تا در نهایت سیستم قدیمی آروم آروم از مدار خارج بشه. همچنین در مورد مزایا و معایب این الگو نسبت به روشهای دیگه صحبت کردیم و دیدیم که چطور میتونه ریسک رو کاهش بده و به پروژه اجازه میده با ریسک حداقلی به کارش ادامه بده. در نهایت هم به این پرداختیم که چطور میتونیم این الگو رو تو پروژههای نرمافزاری خودمون پیاده کنیم.