تیم های توسعه در جریان تلاش برای ارائه Value ها ممکن است ناخواسته معماری هایی ایجاد کنند که در بلندمدت محدودیت هایی را برای آنها ایجاد کند. در ابتدا همه چیز خوب به نظر می رسد، فیچرها به سرعت منتشر می شوند، تست های CI با موفقیت اجرا می شوند و پروداکت بدون مشکل کار می کند. اما در پشت این پیشرفت های ظاهری، ممکن است که پیچیدگی های کنترل نشده شکل بگیرند. ارتباطات ساده بین اجزا می توانند به وابستگی های پیچیده و درهم تنیده تبدیل شوند که چابکی را دچار مشکل و سرعت را کم می کنند. داشتن فلوهای بیش از حد پیچیده، وابستگی های غیرضروری و بدهی فنی مشکلات معماری را تشدید می کنند و تیم ها را در بدترین زمان ها در برابر شکست ها آسیب پذیر می کند.
در ادامه 5 مورد مهم تاثیر گذار در این موضوع رو باهم بررسی می کنیم:
Dependencies Everywhere
یک شبکه پیچیده از وابستگی ها که مقیاس پذیری و به روزرسانی را به یک کابوس تبدیل می کنند.
Your Architecture Isn’t as Clean as Your Code
توهمی از پایه های قوی
Your Codebase Is a Black Box
نداشتن دید کافی برای مدیریت و بهبود سیستم
No Governance, No Control
بدون داشتن قوانین و برنامه هایی برای نظارت بر آن ها، پیچدگی ها به سرعت افزایش پیدا می کنند.
You’re Living in a Complete Mesh
هرج و مرج در ارتباطات که دیباگ و جدا کردن بخش ها را دشوار می کند.
در این مطلب با مقایسه این مشکلات با اصول اثبات شده معماری، یکسری راه حل های عملی مطرح می شود که به شما کمک می کند دوباره کنترل را به دست آورید یا از اول آن را از دست ندهید. همچنین در ادامه نشان خواهیم داد ابزارهایی مانند vFunction, Micrometer یا Dash0 چگونه می توانند نقش مهمی در کنترل معماری داشته باشند.
1. Dependencies Everywhere
معماری بد
وابستگی ها از تله های پنهان در معماری نرم افزار هستند. وقتی سیستم ما پر از وابستگی باشد، چه آنها لایبرری های خارجی باشند، چه ماژول های به شدت مرتبط یا میکروسرویس های وابسته به یکدیگر باشند، یک شبکه پیچیده از وابستگی ها ایجاد می شود که مدیریت آن دشوار است. این وضعیت باعث سخت شدن لوکال دیباگ می شود، چون هر تغییری ممکن است بخش دیگر را ایمپکت کند. و به صورت کلی دیپلوی کردن سخت تر، ترابل شوتینگ طولانی تر و خطر شکست های زنجیرهای بیشتر می شود. در نتیجه تیم ما باید زمان بیشتری را برای حل مشکلات بگذارد و زمان کمتری برای خلاقیت دارد.
یک مثال کلاسیک برای این موضوع، "Domino Effect" است، جایی که به روزرسانی یک وابستگی مشترک باعث ایجاد خطاهای غیرمنتظره در چندین سرویس یا بخشی از کد می شود. یک نمونه دیگر از این مشکل زمانی رخ می دهد که میکروسرویس ها به حدی به یکدیگر وابسته هستند که باید به صورت همزمان دیپلوی شوند و این کار هدف اصلی، یعنی جداسازی آنها را از بین می برد. این سطح از شکنندگی مقیاس پذیری را مختل می کند و سیستم ما را در برابر فشارها آسیب پذیر می کند.
معماری خوب
در یک معماری خوب وابستگی ها را با اولویت دادن به ماژولار بودن و کاهش ارتباطات مستقیم به حداقل می رسانیم. اجزای داخلی سیستم باید ایزوله باشند و از طریق API های تعریف شده با یکدیگر ارتباط برقرار کنند، این رویکرد کدبیس ما را ساده تر، تست ها را سریع تر و دیپلوی را مطمئن تر می کند.
همچنین ماژولار بودن به ما اجازه می دهد وابستگی ها را به راحتی جایگزین یا به روزرسانی کنیم. اصول طراحی خوب مانند DDD یا DIP تضمین می کنند که سیستم ها سازگار باقی بمانند و از درهم تنیدگی جلوگیری می کند. یک معماری تمیز این اعتماد به نفس را به ما می دهد تا بدون نگرانی ازخراب شدن سیستم تغییرات خود را انجام دهیم.
مراقب باشیم...
کاهش وابستگی ها به معنای حذف کامل آنها یا تقسیم سیستم به نانوسرویس ها نیست. شاید با داشتن یک واکنش افراطی، داشتن سرویس های کوچک به شدت جزئی یک راه حل خوب به نظر برسد، اما این کار بیشتر اوقات باعث ایجاد پیچیدگی بیشتر می شود. در این شرایط ما باید ده ها یا صدها بخش مختلف را مدیریت کنیم که هرکدام نیاز به نگهداری، مانیتور کردن و ارتباطات جداگانه دارند.
به جای این کار باید دنبال تعادل باشیم. مرزهایی را برای میکروسرویس های خود تعریف کنیم که Cohesion را تقویت کند و از تکه تکه شدن غیرضروری آنها جلوگیری کند. باید به دنبال معماری باشیم که سرویس ها به صورت کاربردی باهم تعامل داشته باشند اما بیش از حد به یکدیگر وابسته نباشند. این رویکرد باعث افزایش انعطاف پذیری و تاب آوری سیستم می شود.
2. Your Architecture Isn’t as Clean as Your Code
معماری بد
تمرکز بر روی متریک هایی مانند Code Quality یا Deployment Frequencies صرفا توهم موفقیت ایجاد میکنه. ابزارهای AI شاید کدی کاملا تمیز و بدون خطا تولید کنند، اما خروجی آن ها بیشتر اوقات به مشکلات عمیق تر معماری توجهی ندارند. سیستم هایی که پر از Circular Dependency ها، سرویس های Tightly Coupled یا Business Bundry های نامنسجم هستند، بدهی های فنی را انباشته می کنند که ابزارهای AI نمی توانند آن ها را برطرف کنند.
سازمان هایی که به دنبال راه حل های سریع با AI هستند، اغلب متوجه می شوند که سیستم هایشان به مرور زمان غیرقابل نگهداری می شوند. نداشتن حاکمیت و نظارت بر تکامل معماری باعث کاهش سرعت تحویل و افزایش هزینه ها می شود. کد ما ممکن است در حالت جداگانه تمیز به نظر برسد، اما سیستم کلی ای که ایجاد می کنیم به یک هرج و مرج و درهم تنیدگی تبدیل می شود.
معماری خوب
یک معماری خوب فراتر از تمیزی کد می باشد و بر سلامت کلی و مقیاس پذیری سیستم تمرکز دارد. سازمان ها نباید فقط بر معیارهای سطحی تمرکز کنند، بلکه باید به سمت حاکمیت مبتنی بر ابزار حرکت کنند که کیفیت معماری را در زمان واقعی اندازه گیری کنند. ابزارهایی مانند vFunction برای مشاهده معماری به تیم ها امکانات زیر را می دهد:
معماری Clean خود را در مقایسه با طراحی مستند، مشاهده و بررسی کنند.
در هر ریلیز مشکلات معماری و فلوهای بیش از پیچیده را شناسایی کنند.
از وابستگی ها نقشه برداری کنند تا بتوانند آن ها را اصلاح و استانداردسازی کنند.
با مشخص کردن تسک های مشخص و قابل اجرا از از پیچیدگی های غیرضروری جلوگیری کنند.
تیم های موفق سیستم هایی می سازند که در آن کد تمیز با معماری تمیز و حاکمیت مناسب همراه باشد.
مراقب باشیم...
باید از واکنش افراطی خودداری کنیم و حاکمیت سختگیرانه ای را ایجاد نکنیم که خلاقیت تیم را سرکوب کند. معماری باید در کنار نیازهای بیزینس رشد کند و قوانین بیش از حد می توانند توسعه را کند یا از تست ها جلوگیری کنند. در عوض باید حاکمیت به عنوان یک راهنما عمل کند، نه یک محدودیت. باید توجه داشته باشیم که هدف رسیدن به کمال نیست، بلکه حفظ انعطاف پذیری با تحت کنترل نگه داشتن بدهی های فنی و پیچیدگی ها می باشد.
با در نظر گرفتن معماری به عنوان یک جنبه تکاملی و قابل مشاهده، می توانیم تا حدودی نگهداری بلندمدت را تضمین کنیم و نوآوری را بدون قربانی کردن کیفیت افزایش دهیم. کد تمیز و معماری تمیز با یکدیگر از توسعه پایدار حمایت می کنند.
3. Your Codebase Is a Black Box
معماری بد
یک کدبیس بلک باکس، دشمن خاموش پروداکتیویتی است. اگر تیم ما نتواند به راحتی ساختار، وابستگی ها یا رفتار سیستم را تشخیص دهد، هر تغییری به یک قمار پرخطر تبدیل می شود. دیباگ کردن به یک جستجوی بی هدف تبدیل می شود، اضافه کردن فیچرهای جدید نیازمند عبور از یک هزارتوی از ناشناخته ها می باشد و مقیاس پذیری به حدس و گمان وابسته می شود.
دلیل این مشکل، نبود شفافیت در معماری است. گستردگی کد، وابستگی های مستند نشده و رفتارهای پیچیده که در اعماق سیستم های قدیمی دفن شده اند. در چنین شراطی، تیم ها اغلب به "دانش گروهی" متکی هستند، در این شرایط فقط تعداد کمی از افراد ارشد واقعا می دانند سیستم چگونه کار می کند، برای همین وقتی آن ها تیم را ترک می کنند این دانش نیز با آن ها می رود. در نتیجه توسعه کند، سیستم ها شکننده و بدهی فنی به صورت تصاعدی افزایش پیدا می کند.
معماری خوب
در یک معماری خوب معماری خوبشفافیت و قابل درک بودن در اولویت می باشد. ابزارهایی مانند vFunction یا Dash0 می توانند معماری ما را از سایه ها بیرون آورده و آن را شفاف کنند. این ابزارها قابلیت های Architectural Observability را فراهم می کنند که به ما امکان می دهد که کدبیس خود را تجسم کنیم و مرزهای سرویس ها، وابستگی ها و نقاط ناکارآمد را شناسایی کنیم.
با استفاده از vFunction می توانیم معماری پیچیده میکروسرویس ها را به صورت Real-time مستند کنیم که این کار پیدا کردن باگ ها را آسان تر می کند و معماری به صورت کد (Architectural as Code) را با ابزارهایی مانند mermaid برای Sequence Diagram به تصویر بکشیم.
این شفافیت به ما اجازه می دهد که مستقیما با بدهی معماری رو به رو شویم. با شناسایی کردن وابستگی های شدید، ماژول های قدیمی یا پیچیدگی های غیرضروری می توانیم تصمیم های آگاهانه برای بازسازی، ماژولار کردن و مقیاس پذیری بگیریم. علاوه بر آن سیستمی که به خوبی مستند و درک شده باشد، توسعه دهندگان جدید راحت تر می توانند به پروژه اضافه شوند و حتی پروژه های پیچیده نیز با گذشت زمان قابل مدیریت باقی می مانند.
مراقب باشیم...
اما باید توجه داشته باشیم که صرفا داشتن شفافیت مشکل را حل نمی کند. اینکه بدانیم بدهی کجا می باشد ولی اقدامی برای رفع آن نداشته باشیم، مانند تشخیص یک موتور خراب است که از تعمیر آن خودداری می کنیم.
همچنین شفافیت باید با یک چارچوب حاکمیتی همراه شود تا از بازگشت مشکلات مشابه جلوگیری کند. باید دستورالعمل هایی برای معماری تعریف کنیم، استانداردهای کدنویسی را اجرا و تست های خود را پیاده سازی کنیم تا سیستم در حین رشد، قابل نگهداری و مقیاس پذیر باقی بماند. معماری خوب یک فرآیند مداوم است، نه یک تلاش یکباره.
4. No Governance, No Control
معماری بد
بدون داشتن حاکمیت قوی در معماری، حتی بهترین تیم ها می توانند به شکل ناخواسته دچار هرج و مرج شوند. در نبود این استاندارها و نظارت های روشن، یکسری سیلو شکل می گیرد. هر تیم راه حل های خود را توسعه می دهد، پترن ها را به شکل ناسازگار استفاده می کند، تکنولوژی های جدید را بدون هماهنگی و همراستایی به کار می گیرد، بلک باکس ایجاد می کند یا کارها را در پروژه های مختلف تکرار می کند. با گذشت زمان این وضعیت به یک معماری تکه تکه تبدیل می شود که پر از افزونگی ها و پیچیدگی ها است.
نداشتن هماهنگی می تواند باعث رشد بی رویه معماری شود، جایی که میکروسرویس ها بدون کنترل گسترش پیدا می کنند و بدهی فنی مدام افزایش پیدا می کند. بدتر از آن تشخیص و حل مشکلات به چالشی دشوار و عذابآور تبدیل می شود، زیرا هیچ درک مشترکی یا استراتژی منسجمی برای هدایت تیم ها وجود ندارد.
معماری خوب
حاکمیت چسبی است که معماری ما را یکپارچه نگه می دارد. یک برنامه حاکمیتی قوی، استانداردها، دستورالعمل ها و کارآمد از آنها پیروی می کند. این برنامه تضمین می کند که هر تصمیم معماری با اهداف کلی بیزینس همسو باشد و از رشد کنترل نشده میکروسرویس ها جلوگیری می کند.
با استفاده از راهکار حاکمیت معماری vfunction، می توانیم مشکلات معماری را شناسایی کنیم و اطمینان حاصل کنیم که تیم ها با پترن های تعریف شده همراستا هستند. این ابزار با ارائه بینش های Real-time از طراحی سیستم، به ما کمک می کند تا بر استانداردهای معماری نظارت کنیم. برای مثال می توانیم ارتباطات مجاز بین سرویس ها و تعاملات را چک کنیم. همچنین علاوه بر آن، می توانیم با ایجاد ADR تصمیمات و مباحثی که منجر به آنها شده را مستند کنیم.
مراقب باشیم...
درست است که حاکمیت ضروری است، اما نباید به قیمت از دست دادن چابکی یا نوآوری تمام شود. داشتن برنامه های حاکمیتی بیش از حد سختگیرانه می تواند به باتلنک هایی تبدیل شود که توسعه را کند و از تست جلوگیری کند. در این شرایط ممکن است توسعه دهندگان احساس محدودیت کنند و به دنبال راه هایی برای دورزدن قوانین باشند که این مسئله مشکل را بدتر می کند.
کلید موفقیت برای این موضوع در ایجاد تعادل است. حاکمیت باید بدون آنکه خلاقیت را سرکوب کند، باید حاکمیت را به عنوان یک نقشه روشن برای برای توسعه دهندگان در نظر بگیریم، نه بعنوان محدودیتی برای حرکت آن ها، بلکه باید آن را به عنوان ابزاری که به آنها کمک می کند هوشمندتر و سریع تر مسیر خود را پیدا کنند در نظر بگیریم.
5. You’re Living in a Complete Mesh
معماری بد
یک شبکه سرویس که در آن سرویس ها به طور بیش از حد به یکدیگر متصل و وابسته هستند، می تواند توسعه را فلج کند. در این آنتی پترن، مسیرهای ارتباطی بین سرویس ها به قدری پیچیده می شوند که حتی تغییرات کوچک یا خرابی در یک بخش از سیستم می توانند تاثیرات زنجیرهای در کل برنامه ایجاد کنند.
برای مثال، تصور کنید یک سرویس برای تکمیل یک تراکنش باید پنج سرویس دیگر را فراخوانی کند، و هرکدام از آن سرویس ها نیز به فراخوانی های بیشتر در سرویس های پایین دستی وابسته باشند. این کار باعث افزایش تاخیر، افزونگی بیشتر و پیچدگی بالای عملیات می شود.
معماری خوب
در یک معماری خوب از ایجاد چنین شبکه هایی اجتناب می کنیم و بر مرزهای شفاف و تعریف شده سرویس ها و کاهش ارتباطات بین سرویس ها تمرکز داریم. سرویس ها باید وظایف مشخص و منسجمی داشته باشند تا حد امکان مستقل عمل کنند.
برای کاهش وابستگی ها، از مکانیسم هایی مانند message queues و event streams استفاده میکنیم. این روش نه تنها وابستگی های مستقیم را کاهش می دهد، بلکه Fault Tolerance را نیز بهبود می بخشد و به سرویس ها اجازه می دهد در صورت عدم دسترسی سرویس های بالا دستی یا سرویس های پایین دستی به کار خود ادامه دهند. همچنین طراحی فلو ارتباطات به شکل یکطرفه (تا حد امکان) مدیریت وابستگی ها را ساده تر و سیستم را پیش بینی پذیرتر می کند.
مراقب باشیم...
همانطور که ارتباط بیش از حد صحیح نیست، جداسازی بیش از حد نیز می تواند باعث ایجاد مشکل شود. جداسازی کامل سرویس ها بدون نیاز صریح بیزینس می تواند باعث کارهای تکراری، Data Inconsistency و ناتوانی در اشتراک گذاری موثر قابلیت های شود. در اینجا هدف تعادل و کاهش وابستگی های غیر ضروری همراه با اطمینان از ارتباط سرویس ها در مواقع نیاز است. ابزارهایی مانند vFunction و ESLint و موارد مشابه می توانند به ما در رسیدن به این تعادل کمک کنند.
معماری خود را باید مانند شبک های از بزرگراه ها تصویر کنیم: ترافیک باید صورت روان جریان داشته باشد، بدون تقاطع های غیرضروری یا بن بست. با استفاده از ابزارهای مناسب می توانیم از مشکلات یک شبکه کاملا درهم تنیده جلوگیری کنیم و معماریای مقیاس پذیر و Resilience بسازیم.
منبع: John Vester - dzone . com