کتابهای دستهبندی: مهندسی نرمافزار · همهٔ کتابها
کتابخانهٔ شنیدنی شبگفتار
کتابهای مهندسی نرمافزار
خلاصههای صوتی فارسی کتابهای مرتبط با مهندسی نرمافزار؛ کتاب و فصل دلخواهتان را برای شنیدن انتخاب کنید.
این مجموعه خلاصهٔ کتابهاست؛ نسخهٔ کامل کتاب اصلی نیست.
12 عنوان · 122 قسمت منتشرشده
راه را پیدا کنید
مسیرهای مطالعاتی
قفسهٔ کتابها
از این کتابها شروع کنید
بخش اول: الگوهای معماری در سیستمهای توزیعشده
نمونهٔ رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که وقتی تعداد کاربران یک سرویس آنلاین به طور ناگهانی چند برابر میشود، زیرساخت آن چطور زیر بار دوام میآورد؟ چطور ممکن است درخواستهای میلیونها نفر در سراسر دنیا بدون از کار افتادن سیستم پردازش شوند؟
این موضوع در فصلهای ابتدایی کتاب «الگوهای سیستمهای توزیعشده» نوشتهٔ اونمش جوشی بررسی میشود که به ما نشان میدهد چطور میتوان چالشهای مقیاسپذیری و پایداری را با استفاده از الگوهای معماری مدیریت کرد.
اولین ایده برای مدیریت دادههای حجیم، بخشبندی کردن اطلاعات روی چندین ماشین است. مسئله این است که وقتی دادهها تقسیم میشوند، احتمال خرابی سختافزار افزایش مییابد و گزینههای پیش رو شامل تکثیر دادهها برای پوشش این خرابیهاست؛ تریدآف این روش در پیچیدگی هماهنگی میان گرههاست. برای نمونه، در یک سامانهٔ خردهفروشی بزرگ، اطلاعات سبد خرید مشتریان روی چندین گره مختلف کپی میشود تا با از دست رفتن یکی از آنها، سیستم همچنان پاسخگو بماند.
برای حفظ ثبات و پایداری دادهها روی یک گره، استفاده از Write-Ahead Log (لاگ نوشتن پیشرو) به کار گرفته میشود. مشکل اینجاست که اگر سرور هنگام بهروزرسانی فایلهای متعدد دچار نقص شود، دادهها ناسازگار میشوند. راهحل این است که سیستم پیش از اعمال تغییرات، جزئیات را در یک فایل ترتیبی ثبت کند تا در صورت راهاندازی مجدد، وضعیت قبلی قابل بازیابی باشد. پایگاههای داده از این الگو برای ثبت و مدیریت تراکنشها استفاده میکنند.
مشکل بعدی بهروزرسانیهای رقابتی است؛ وقتی دو کاربر همزمان درخواستهای متفاوتی میفرستند، سیستم به مدیریت هماهنگ نیاز دارد. الگوی Leader and Followers (رهبر و پیروان) این مسئله را حل میکند به این شکل که یک گره به عنوان رهبر تمام تغییرات را میپذیرد و به بقیه منتقل میکند. تریدآف آن تمرکز بار روی یک گره است و برای حل خطاهای ناشی از قطع ارتباط آن، Quorum (حد نصاب) به کار میرود که یعنی یک تصمیم تنها زمانی معتبر است که اکثریت گرهها آن را تأیید کرده باشند.
گاهی رهبر از کار میافتد و گرههای دیگر باید رهبر جدیدی انتخاب کنند که این امر با خطراتی در ناسازگاری لاگها همراه است. علامت سطح بالا یا High-Water Mark روشی است که نشان میدهد آخرین بهروزرسانی تا کدام نقطه توسط اکثریت گرهها تأیید شده است. همچنین برای اینکه ارسال دوبارهٔ درخواست توسط مشتریان باعث اجرای تکراری عملیات نشود، سامانه از Idempotent Receiver (گیرندهٔ خودتوان) استفاده میکند تا با بررسی شناسهٔ یکتای درخواست، پاسخهای قبلی را بدون اجرای مجدد برگرداند.
پیشنهاد من این است که هنگام طراحی سامانههای توزیعشده، از همان ابتدا به چالشهای همگامسازی زمان و خطاهای جزئی شبکه فکر کنید و به جای تلاش برای پیادهسازی دستی الگوریتمهای پیچیده، از الگوهای اثباتشده و ابزارهای استاندارد بهره بگیرید.
امیدوارم این بررسی کوتاه از الگوهای سیستمهای توزیعشده برایتان مفید بوده باشد و در قسمتهای بعدی همراه ما بمانید.
مقدمه: ریشهیابی شکست پروژههای نرمافزاری
نمونهٔ رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا با وجود پیشرفت ابزارها و متدولوژیها، هنوز هم درصد بالایی از پروژههای نرمافزاری به موقع تحویل داده نمیشوند یا شکست میخورند؟ مشکل اصلی این نیست که یاد گرفتن فریمورکهای جاوااسکریپت سخت است، بلکه چالش اصلی درک درست دنیای پیچیده و دامنههای مختلف کسبوکار است.
در کتاب «یادگیری طراحی دامنهمحور» نوشتهٔ «ولاد خونونوف»، در فصل مقدمه بررسی میشود که ریشهٔ بحران نرمافزار در ضعف ارتباطات است و طراحی دامنهمحور تلاش میکند این مشکل را از ریشه حل کند.
اولین مسئله، ناتوانی در درک صحیح دامنه Domain یعنی حوزهٔ کاری و دانشی که نرمافزار برای آن ساخته میشود است که باعث پیادهسازی نامطلوب سیستم میشود. گزینههای موجود این است که یا بدون شناخت کافی کدنویسی را شروع کنیم یا زمان کافی برای فهم قوانین کسبوکار بگذاریم که معاوضهٔ آن، صرف وقت بیشتر در ابتدا در برابر نجات پروژه در بلندمدت است؛ مثلاً در یک سیستم مالی، اگر بخش حسابداری را درست درک نکنیم، کل ساختار نرمافزار با شکست مواجه میشود.
دومین مسئله، ریشهدار بودن بحران نرمافزار در طول پنجاه سال گذشته است که نشان میدهد متدهایی مثل اجایل یا برنامهنویسی تستمحور به تنهایی نتوانستهاند مشکل را حل کنند. گزینههای جایگزین تمرکز بر فرآیندها یا تمرکز بر حل ریشهای مشکلات ارتباطی بین تیمهاست که نویسنده معتقد است ابزارهای استراتژیک و تاکتیکی طراحی دامنهمحور روی همین بهبود ارتباط تمرکز دارند.
سومین مسئله، مبهم بودن نیازمندیها و اهداف پروژه است که به دلیل نبود درک مشترک بین ذینفعان رخ میدهد. گزینهها شامل ادامهٔ روشهای سنتی ارتباطی یا استفاده از ابزارهای استراتژیک این کتاب برای آنالیز کسبوکار و ساختن یک درک مشترک است که هزینهٔ هماهنگی را به شدت کاهش میدهد.
چهارمین مسئله، فاصله گرفتن کدها از زبان واقعی کسبوکار است که ارتباط بین برنامهنویسان و افراد غیرفنی را دشوار میکند. گزینههای پیش رو نوشتن کد با اصطلاحات فنی پیچیده یا استفاده از ابزارهای تاکتیکی است تا کد نرمافزار مستقیماً اهداف کسبوکار و زبان آن را بازتاب دهد.
به عنوان پیشنهاد شخصی، به نظرم بهتر است پیش از درگیر شدن با جزئیات فنی و نوشتن کدهای پیچیده، زمان بیشتری را به گفتگو با افراد متخصص در زمینهٔ کسبوکار اختصاص دهید تا مطمئن شوید ساختار نرمافزار شما دقیقاً روی نیازهای واقعی همان حوزه بنا شده است.
امیدوارم این نگاه تازه به شما کمک کند تا در مسیر توسعه نرمافزار، ارتباط بهتری با اهداف کسبوکار برقرار کنید و پروژههای پایدارتری بسازید.
بخش چهارم: همروندی، پایش از راه دور، احراز هویت و مفهوم برنامههای بومی ابری
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که وقتی نرمافزاری را روی بسترهای ابری مستقر میکنید، چطور میتوانید از سلامت و عملکرد آن مطمئن باشید در حالی که دسترسی فیزیکی به سرورهای آن ندارید؟ چه چالشهایی در انتظار تیمهای توسعهای است که میخواهند از روش سنتی فاصله بگیرند و برنامههای خود را مقیاسپذیر کنند؟
این مباحث در بخش چهارم کتاب «فراتر از اپلیکیشن دوازدهعاملی» نوشتهٔ «کوین هافمن» بررسی شدهاند که به ما کمک میکند نگاه عمیقتری به معماریهای مدرن داشته باشیم.
یکی از موضوعات کلیدی، همروندی (Concurrency) است که به ما میگوید برنامههای بومی ابر (Cloud Native) باید به جای بزرگ کردن یک پردازش واحد، مقیاس افقی داشته باشند و بار کاری را بین چند پردازش توزیع کنند. مشکل این است که افزودن منابع به یک پردازش منفرد که مقیاسپودی عمودی خوانده میشود، پاسخگوی نیازهای مدرن نیست و انتخاب مناسب این است که چندین نمونه برنامه ایجاد کنیم و بار کاری را بین آنها تقسیم نماییم؛ مانند استفاده از قوانین مقیاسدهی خودکار که تعداد نمونههای برنامه را بر اساس بار کاری تنظیم میکنند.
مسئلهٔ مهم دیگر، پایش از راه دور (Telemetry) است که به ما امکان میدهد رفتار سیستم را زیر نظر بگیریم. در محیطهای ابری، شما نمیتوانید مثل زمان توسعه محلی به درون برنامه سرک بکشید و نمونههای برنامه ممکن است مدام جابهجا شوند، بنابراین نویسنده پیشنهاد میکند برنامهها را مانند کاوشگرهای فضایی ببینیم که به ابزارهای دقیق سنجش نیاز دارند. در این میان، دستههای مختلفی از دادهها وجود دارند که پایش عملکرد اپلیکیشن (Application Performance Monitoring) یکی از آنهاست و جریانی از رویدادها را برای بررسی عملکرد فراهم میکند؛ در حالی که پایشهای مختص دامنه تجاری به گزارش رویدادهای مرتبط با کسبوکار مانند تعداد اقلام فروختهشده میپردازند.
امنیت، احراز هویت و کنترل دسترسی از دیگر ستونهای حیاتی هستند که نباید به عنوان یک کار پس از اتمام پروژه دیده شوند. مشکل اینجاست که تمرکز روی قابلیتهای اولیه باعث غفلت از امنیت میشود، در حالی که کدهای ما در شبکهها و کانتینر (Container) در حال گردش هستند و توسط کاربران مختلفی درخواست میشوند. بهترین گزینه این است که کنترل دسترسی مبتنی بر نقش (Role-Based Access Control) در تمام نقاط پایانی برنامه پیادهسازی شود تا هر درخواستی اعتبارسنجی گردد و این کار با ابزارهایی مانند استانداردهای مدرن از روز اول توسعه انجام گیرد.
در نهایت، مفهوم بومی ابر به برنامههایی اشاره دارد که برای اجرا روی سکو به عنوان خدمت (Platform as a Service) طراحی شدهاند تا تیمها به جای درگیر شدن با زیرساخت، روی هسته اصلی کسبوکار خود که نویسنده آن را به ایده تمرکز بر یک کار بزرگ تشبیه میکند، تمرکز کنند. چالش همیشگی در این میان، توازن میان اصول آرمانگرایانه و نیازهای واقعگرایانه و بودجه تیمها است که نشان میدهد سازش با واقعیتهای توسعه چقدر اهمیت دارد.
پیشنهاد من به شما این است که هنگام طراحی یا بازنگری پروژهها، ساختار برنامههای خود را با این اصول بسنجید تا بدون وابستگی به سختافزار مشخص، آماده مواجهه با بارهای سنگین ترافیکی باشند.
امیدوارم این مرور کوتاه توانسته باشد دید روشنتری نسبت به معماری مدرن نرمافزار به شما بدهد و در مسیر توسعه برنامههای مقیاسپذیر یاریتان کند.
بخش دوم: پیکربندی، لاگها، قابلیت دور انداختن و سرویسهای پشتیبان
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا گاهی برنامهای که روی کامپیوتر شخصی شما بدون هیچ مشکلی کار میکند، وقتی روی سرور اصلی قرار میگیرد دچار اختلال میشود؟ چه چیزی میان کد برنامهنویسی و محیط اجرای آن پنهان است که میتواند همهچیز را به هم بریزد؟
کتاب «فراتر از اپلیکیشن دوازدهعاملی» نوشتهٔ کوین هافمن به ما نشان میدهد که برای ساخت نرمافزارهای مدرن در دنیای ابر، رعایت اصول سنتی دیگر کافی نیست و باید نگاه تازهای به ساختار برنامههای خود داشته باشیم.
نخستین چالش جدی، مدیریت تنظیمات برنامه است که نویسنده پیشنهاد میکند تنظیمات و اعتبارنامهها را به عنوان مواد فراری در نظر بگیریم که با ترکیب شدن منفجر میشوند. مشکل اینجاست که قرار دادن اطلاعاتی مانند آدرس پایگاه داده یا رمزهای عبور در داخل کدهای برنامه، انعطافپذیری را از بین میبرد و امنیت را به خطر میاندازد. در روش سنتی، توسعهدهندگان از فایلهای تنظیمات محلی استفاده میکنند، اما این کار مانع از داشتن build (ساخت) یکتا میشود. راهحل این است که از Configuration (پیکربندی) خارج از کد استفاده کنیم و مقادیر را به وسیله متغیرهای محیطی یا ابزارهای تخصصی به برنامه برسانیم، به طوری که اگر روزی سورسکد روی گیتهاب قرار گرفت، هیچ اطلاعات حساسی افشا نشود.
موضوع مهم بعدی نحوهٔ مدیریت اطلاعات مربوط به Credentials (اعتبارنامه) است که شامل نامهای کاربری، رمزهای عبور و کلیدهای دسترسی به سرویسهای شخص ثالث میشود. اگر این موارد حساس در فایلهای منابع همراه برنامه بستهبندی شوند، همچنان بخشی از سورسکد محسوب میشوند و قانون جداسازی را نقض میکنند. در روش اصولی، این اطلاعات هرگز نباید در مخزن کد وجود داشته باشند و باید در زمان اجرا از محیط ابری دریافت شوند تا برنامهها کاملاً مستقل بمانند.
در بخش ثبت رویدادها، چالش اصلی کنترل مقصد و نحوهٔ ذخیرهسازی Logs (لاگها) است. برنامههای سنتی عادت دارند فایلهای لاگ را روی دیسک ذخیره کنند و خودشان مسئولیت چرخش و مدیریت حجم آنها را به عهده بگیرند، اما در زیرساختهای ابری، سیستمعامل موقتی است. راهحل بومی ابر این است که برنامه تمام خروجیهای خود را صرفاً روی stdout و stderr بنویسد و جمعآوری، پردازش و ذخیرهسازی آنها را به ابزارهای جانبی واگذار کند. این کار باعث سادهتر شدن کد و تمرکز بیشتر روی ارزش تجاری اصلی میشود.
چالش چهارم مربوط به Disposability (قابلیت دور انداختن) است که به سرعتِ آغاز به کار و توقف نرمافزار مربوط میشود. اگر برنامهای برای بالا آمدن به چند دقیقه زمان نیاز داشته باشد، در زمان اوج ترافیک یا مقیاسپذیری خودکار دچار مشکل میشود و درخواستهای کاربران را از دست میدهد. ترفند کار در این است که فعالیتهای طولانیمدت مانند بارگذاری کش را از مرحلهٔ راهاندازی اولیه خارج کنیم تا نمونههای جدید برنامه در کسری از ثانیه آماده سرویسدهی یا متوقف شدن باشند.
در نهایت، برنامهها برای انجام کارهای خود به Backing Service (سرویس پشتیبان) نیاز دارند که شامل پایگاههای داده، سیستمهای پیامرسان و فضاهای ذخیرهسازی میشود. مشکل اینجاست که اتصال مستقیم و سختکدشده به این سرویسها، برنامه را در برابر خرابی آنها آسیبپذیر میکند. راهحل درست این است که سرویسهای پشتیبان به عنوان منابع متصل در نظر گرفته شوند تا اگر پایگاه دادهای از کار افتاد، مدیر سیستم بتواند بدون نیاز به تغییر کد برنامه، اتصال آن را به یک نمونه سالم دیگر تغییر دهد.
پیشنهاد شخصی من این است که هنگام توسعه برنامهها، همیشه فرض کنید سرور شما هر لحظه ممکن است خاموش شود و از بین برود؛ این فرض ساده کمک میکند تا وابستگیهای پنهان را حذف کنید و نرمافزاری انعطافپذیر بسازید.
امیدوارم از این بررسی کوتاه لذت برده باشید و این نکات در طراحی سیستمهای بعدی به کار شما بیاید.
بخش اول: مبانی: پایگاه کد، API-محور، وابستگیها، طراحی/ساخت/انتشار/اجرا
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا بعضی از نرمافزارها به محض قرار گرفتن روی ابر به مشکل برمیخورند، در حالی که بعضی دیگر به راحتی مقیاسپذیر میشوند و رشد میکنند؟ تفاوت اصلی در نحوه پیریزی و رعایت استانداردهایی است که به سیستم اجازه میدهد در دنیای مدرن ابری دوام بیاورد و عملکرد روانی داشته باشد.
در کتاب «فراتر از اپلیکیشن دوازدهعاملی» نوشته «کوین هافمن»، اصول پایهای و ساختاری بررسی میشود که برای ساخت اپلیکیشن دوازدهعاملی (Twelve-Factor App) به عنوان مجموعهای از اصول برای ساخت اپلیکیشنهای ابری قابلحمل و مقیاسپذیر، به شدت به آنها نیاز داریم.
اولین چالش در این زمینه، بینظمی در ساختار کد و داشتن مخازن متعدد برای یک اپلیکیشن واحد است. برخی تیمها کدهای یک محصول را در چندین جای مختلف پخش میکنند که این کار خودکارسازی ساخت و انتشار را تقریباً غیرممکن میسازد. راه حل این است که کل سیستم را به عنوان یک مخزن کد واحد در کنترل نسخه ثبت کنیم و اگر چند بخش مجزا داریم، آنها را به عنوان میکروسرویسهای مستقل از هم جدا کنیم. به عنوان نمونه، داشتن یک اپلیکیشن یکپارچه بزرگ با اسکریپتهای اجرایی متعدد نشاندهنده به هم ریختگی تیمهاست که مستقیماً در معماری نرمافزار هم منعکس میشود.
چالش بعدی در محیطهای ابری پیچیده، نامشخص بودن قراردادهای ارتباطی بین تیمهای مختلف است که باعث خطاهای یکپارچهسازی میشود. انتخاب بهینه این است که رویکرد API First در پیش بگیریم تا هر بخش از سیستم قبل از پیادهسازی کامل، قرارداد مشخصی برای تعامل داشته باشد. این رویکرد همکاری و تست را ساده میکند، اما نیازمند انضباط بالایی در مستندسازی است. برای مثال، استفاده از فرمتهایی برای تعریف این قراردادها به تیمها اجازه میدهد بدون وابستگی به کدهای داخلی، همزمان روی بخشهای مختلف کار کنند.
مدیریت وابستگیها نیز یکی از نقاط عطف در توسعه نرمافزار است که نباید به سرورهای سنتی و وابستگیهای پنهان سیستم تکیه کند. در روش سنتی، برنامهها متکی به پکیجهای نصبشده روی سرور میزبان هستند که خطاهای عجیبی را به دنبال دارد، اما راهکار مدرن این است که وابستگیها صریحاً اعلام و ایزوله شوند. کتاب اشاره میکند که ابزارهای مدیریت وابستگی، تمام کتابخانههای مورد نیاز را مستقیماً درون پکیج اجرایی اپلیکیشن بستهبندی میکنند تا اجرا در هر محیطی کاملاً قابل تکرار باشد.
چهارمین ایده کلیدی، جداسازی دقیق مرحلههای طراحی، ساخت، انتشار و اجرا در چرخه حیات نرمافزار است. خیلی از تیمها با مشکل مواجه میشوند چون تفاوت محیط توسعه و تولید را نادیده میگیرند و تستها را روی نسخههای متغیر انجام میدهند. راهکار اصولی این است که یک کد واحد از طریق خط لوله (Pipeline) که زنجیرهٔ خودکار ساخت، آزمون و انتشار است، به یک بسته اجرایی تغییرناپذیر تبدیل شود. برای نمونه، فایلی که در محیط آزمون تأیید میشود، دقیقاً همان فایلی است که بدون هیچگونه دستکاری دستی به محیط تولید ارسال میگردد.
پیشنهاد عملی من این است که پیش از افزودن هرگونه پیچیدگی به معماری سیستم، ابتدا مخزن کد و وابستگیهای آن را کاملاً پاکسازی کنید و مطمئن شوید که فرآیند ساخت و انتشار شما به صورت خودکار انجام میشود.
امیدوارم این مرور کوتاه به شما کمک کرده باشد نگاه دقیقتری به ساختار نرمافزارهای ابری داشته باشید.
بخش سوم: همسانی محیطها، فرایندهای مدیریتی، اتصال پورت و فرایندهای بیحالت
رایگانتوضیح و منابع این قسمت
آیا تا به حال شده که برنامهای در محیط توسعه یا تست شما بینقص کار کند، اما به محض رسیدن به محیط عملیاتی، با مشکلات غیرمنتظرهای روبرو شوید؟ یا شاید در سازمانی کار کردهاید که انتشار یک تغییر کوچک، هفتهها یا ماهها طول میکشد؟ این تجربهها چقدر برایتان آشناست؟
امروز در ادامه بحث از کتاب «فراتر از اپلیکیشن دوازدهعاملی» نوشته «کوین هافمن»، به سراغ چالشهای «همسانی محیطها» و چند مفهوم کلیدی دیگر در طراحی برنامههای بومی ابر میرویم که به ما کمک میکنند تا به عملکرد اپلیکیشنهایمان در هر محیطی اعتماد بیشتری داشته باشیم.
یکی از مهمترین عواملی که شکاف بین محیطها را ایجاد میکند، «زمان» است. در بسیاری از سازمانها، از لحظهای که یک توسعهدهنده کدی را ثبت میکند تا زمانی که آن کد به تولید میرسد، ممکن است هفتهها یا ماهها طول بکشد. این فاصله زمانی طولانی باعث میشود که افراد تغییرات را فراموش کنند و حتی توسعهدهندگان هم از یاد ببرند که کدشان در زمان ثبت چگونه بوده است. راهکار مدرن این است که این فاصله زمانی را از هفتهها و ماهها به دقیقه یا ساعت کاهش دهیم. این کار با استفاده از یک «خط لوله (Pipeline)» استقرار مداوم مناسب انجام میشود که در آن، تغییرات پس از اجرای تستهای خودکار در محیطهای مختلف، به صورت خودکار به تولید منتقل میشوند. شاید این ایده در ابتدا ترسناک به نظر برسد، اما وقتی توسعهدهندگان عادت کنند که کدشان همان روز به تولید میرسد، نظم و کیفیت کد به شدت افزایش مییابد. عامل انسانی هم مهم است: وقتی استقرار به دست افراد انجام میشود، هر فرد ممکن است مراحل را متفاوت اجرا کند؛ خودکارسازی این تفاوتها را کم میکند و هر کامیت را به یک کاندید انتشار تبدیل میکند.
عامل دیگری که به همسانی محیطها آسیب میزند، «منابع» هستند. وقتی برای راهاندازی سریع چیزی عجله داریم، اغلب سازشهایی میکنیم. مثلاً، ممکن است اپلیکیشن ما در تولید به یک پایگاه داده قوی مثل اوراکل نیاز داشته باشد، اما برای توسعه محلی، از یک پایگاه داده در حافظه استفاده کنیم. هر بار که چنین سازشی میکنیم، شکاف بین محیطهای توسعه و تولید را افزایش میدهیم و هر چه این شکاف بیشتر باشد، پیشبینیپذیری رفتار اپلیکیشن کمتر میشود و قابلیت اطمینان آن کاهش مییابد. امروزه ابزارهای زیادی در دسترس توسعهدهندگان است. میتوان از همان نوع «سرویس پشتیبان (Backing service)» در همه محیطها استفاده کرد، یا ابزارهایی مانند داکر (Docker) به ایجاد محیطهای شبیه تولید روی ایستگاههای کاری توسعهدهندگان کمک میکنند. هر تصمیمی که شکاف عملکردی بین محیطهای استقرار شما را افزایش میدهد، باید مورد سؤال قرار گیرد و باید در برابر وسوسه تفاوت قائل شدن بین محیطها مقاومت کرد.
حالا به سراغ «فرایندهای مدیریتی (Admin process)» میرویم. این فرایندها کارهای یکباره یا نگهداری هستند که جدا از فرایند اصلی اپلیکیشن اجرا میشوند. توصیه اصلی اپلیکیشن دوازدهعاملی این بود که این کارها به عنوان فرایندهای یکباره اجرا شوند، اما کوین هافمن معتقد است که این توصیه میتواند گمراهکننده باشد. مشکل اینجاست که استفاده از زمانبندهای خارجی مثل کرون (Cron) یا حتی اجرای اسکریپتهای تعاملی، در محیط ابری میتواند منجر به هرج و مرج و خطاهای دادهای شود، به خصوص وقتی چندین نمونه از اپلیکیشن در حال اجرا هستند. مثلاً، اگر یک کار دستهای شبانه را با یک زمانبند داخلی در هر نمونه از اپلیکیشن اجرا کنید، ممکن است این کار چندین بار اجرا شود و دادهها را خراب یا تکراری کند.
برای حل این مشکل، راهحلهای بهتری وجود دارد. یکی از جذابترین آنها، این است که قابلیتهای موقت و مدیریتی را از طریق یک «پایانه REST (REST endpoint)» در اپلیکیشن خود ارائه دهید. این کار به شما اجازه میدهد که این قابلیتها را به صورت کنترلشده و امن فراخوانی کنید. راهکار دیگر این است که کد مربوط به کارهای دستهای را از اپلیکیشن اصلی جدا کرده و به یک «میکروسرویس (Microservice)» جداگانه تبدیل کنید. این میکروسرویس میتواند به صورت مستقل مقیاسپذیر باشد و از مزایای دیگر ابر نیز بهرهمند شود. حتی میتوانید از توابع بومی ابر مانند AWS Lambda استفاده کنید که فقط در صورت نیاز فراخوانی میشوند و نیازی به سرورهای همیشه روشن ندارند.
در نهایت، به «اتصال پورت (Port binding)» میرسیم. اپلیکیشنهای بومی ابر باید سرویسهای خود را از طریق اتصال پورت ارائه دهند. در محیطهای غیرابری، به خصوص در سازمانهای بزرگ، معمولاً چندین اپلیکیشن در یک «کانتینر (Container)» سرور وب میزبانی میشوند و با شماره پورتهای مختلف از هم جدا میشوند. این کار مدیریت پورتها را پیچیده میکند. اما در محیط ابری، ارائهدهنده خدمات ابری مسئولیت مدیریت پورتها، مسیریابی، مقیاسگذاری و دسترسی بالا را بر عهده دارد.
و در نهایت، فصل دوازدهم به «فرایندهای بیحالت (Stateless Processes)» میپردازد. یک فرایند بیحالت هیچ فرضی دربارهٔ محتوای حافظه پیش یا پس از پردازش درخواست ندارد؛ میتواند در میانهٔ کار وضعیت موقت بسازد، اما پس از پاسخ دادن، آن وضعیت باید از بین برود. هر وضعیت ماندگار باید به یک سرویس پشتیبان مثل پایگاه داده منتقل شود. به همین دلیل، یک میکروسرویس مدیریت کاربران بیحالت است و فهرست کاربران در پایگاه داده نگهداری میشود. هافمن الگوی «هیچچیز مشترک (Share-Nothing)» را هم توصیه میکند: هر دادهٔ مشترک بین فرایندها یک نقطهٔ شکست است، و چون فرایندها در ابر هر لحظه ممکن است حذف یا جابهجا شوند، اشتراک وضعیت میتواند باعث شکست زنجیرهای شود. حتی فایلسیستم هم سرویس پشتیبان محسوب نمیشود.
فصل بیستوپنجم: محاسبات به عنوان سرویس و زیرساخت مقیاسپذیر در گوگل
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که وقتی تعداد کاربران یک سامانه به میلیونها نفر میرسد، کدهای ما چطور روی هزاران سرور مختلف اجرا و مدیریت میشوند؟ چطور میتوانیم مطمئن باشیم که با خرابشدن سختافزارها، سرویس ما از دست نمیرود؟
کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل بیستوپنجم به سراغ یکی از بنیادیترین مفاهیم زیرساختی یعنی محاسبات به عنوان سرویس (Compute as a Service) میرود و نشان میدهد که چطور باید بستر سختافزاری و نرمافزاری را برای مقیاسهای بزرگ طراحی کرد.
در روزهای اولیه، مهندسان نرمافزار مجبور بودند کدهای خود را به صورت دستی روی تکتک ماشینها کپی کنند، آنها را اجرا کنند و نگران خرابی تکتک سیستمها باشند. با رشد شرکت، این روش سنتی به کابوسی از کارهای تکراری تبدیل شد. راهحل اولیه استفاده از اسکریپتهای ساده و ابزارهای مانیتورینگ بود، اما در مقیاسهای کلان، سازمان به برنامهریزی خودکار برای تخصیص ماشینها نیاز دارد. برای نمونه، گوگل سیستم مدیریت خوشهای خود یعنی «بورگ» را ساخت تا وظایف پردازشی را به صورت خودکار روی منابع موجود پخش کند و در صورت خرابی ماشین، کار را به سرعت به سرور دیگری منتقل سازد.
مشکل بعدی این بود که اختصاص دادن یک سرور کامل به یک برنامه، اسراف بزرگی در مصرف منابعی مثل پردازنده و حافظه به حساب میآمد. راهحل این بود که چندین برنامه روی یک ماشین مشترک اجرا شوند، اما این کار خطراتی مثل تداخل منابع به همراه داشت. وقتی برنامهای بیش از حد حافظه مصرف کند، سیستمعامل میتواند کل فرآیند را متوقف کند. استفاده از ماشینهای مجازی هم راهحل سنگینی بود، زیرا سیستمعامل کامل خود را نیاز داشت. بنابراین گوگل به سراغ کانتینرسازی (Containerization) رفت که تکنیکی سبکوزن برای جداسازی فرآیندها روی یک سیستمعامل واحد است.
تغییر زیرساخت به سیستمهای خودکار نیازمند تغییر نگاه ما به نرمافزار هم هست؛ سرویسها نباید مثل حیوانات خانگی نگهداری شوند که اگر بیمار شدند بالاسرشان بنویسیم، بلکه باید مثل دام و گله (Cattle) با آنها رفتار کرد؛ یعنی اگر نمونهای از کار افتاد، خودکار حذف شود و یک نمونهٔ سالم جایگزینش گردد. در این مدل، وضعیت برنامهها باید ناپایدار (Transient) در نظر گرفته شود و دادههای مهم در فضای ذخیرهسازی ماندگار نگهداری شوند. برای مثال، اگر در یک پردازش دستهای (Batch Job) یکی از کارگرها وسط کار بسوزد، سامانه باید کار را به تکههای کوچکتر خرد کرده باشد تا فقط همان تکهٔ کوچک دوباره پردازش شود.
با گذشت زمان، مفهوم کانتینر فراتر از یک ابزار جداسازی رفت و به عنوان یک لایهٔ انتزاعی عمل کرد تا برنامهها نیازی به دانستن جزئیات سختافزار زیر پای خود نداشته باشند. اما قانون هایروم (Hyrum’s Law) یادآوری میکند که وقتی سیستم به اندازهٔ کافی کاربر داشته باشد، حتی رفتارهای ناخواسته یا محدودیتهای سیستمعامل مثل تعداد شناسهٔ فرآیندها به قراردادی پنهان تبدیل میشوند که کاربران به آن وابستگی پیدا میکنند و تغییر دادنشان بسیار پرهزینه خواهد شد.
پیشنهاد من این است که هنگام طراحی سامانههای خود، از همان ابتدا فرض را بر این بگذارید که سختافزار و سرورها ممکن است هر لحظه از دست بروند و نرمافزار خود را به گونهای بنویسید که بدون وابستگی به یک ماشین خاص، به صورت خودکار بازیابی شود.
مدیریت درست زیرساخت محاسباتی ستون فقرات پایداری هر سازمان نرمافزاری بزرگ است و شناخت الگوهای موفق گوگل به ما کمک میکند تا در مقیاسهای بالا دچار بحران نشویم.
فصل بیستوچهارم: تحویل پیوسته و مهندسی نرمافزار در گوگل
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردید که چرا بعضی از نرمافزارها مدام و بدون مشکل بهروزرسانی میشوند، در حالی که انتشار یک نسخه جدید در بعضی شرکتها مثل یک پروژه نجات گروگان است؟ وقتی سرعت تغییرات در دنیای فناوری بالا است، مزیت رقابتی هر محصولی به توانایی آن در ارائه سریع ایدهها به دست کاربران بستگی دارد، اما این سرعت معمولاً پشت سد فرآیند استقرار گیر میکند. کتاب «مهندسی نرمافزار در گوگل» در فصل بیستوچهارم با عنوان تحویل پیوسته (Continuous Delivery) به ما نشان میدهد که چطور سرعت و ایمنی در توسعه نرمافزار در تناقض با هم نیستند، بلکه دست در دست هم دارند. این فصل برای هر کسی که میخواهد چالشهای مقیاسپذیری و انتشار نرمافزار را درک کند، حیاتی است. اولین چالش جدی در این مسیر، هزینهبر بودن انتشارها است که تیمها را وادار میکند انتشارها را عقب بیندازند تا آنها را بیشتر تست کنند. وقتی حجم تغییرات انباشته میشود، فرآیند انتشار پیچیده و پرخطا میشود و کسی جرئت ندارد دکمه انتشار را فشار دهد. راهحل سنتی اضافه کردن نظارت و مدلهای برنامهریزی قدیمی است که فقط سرعت را میگیرد، اما نویسندگان کتاب پیشنهاد میکنند به جای این کار، معماری سیستم را به سمت ریزسرویسها (Microservices) ببرید تا تغییرات کوچک و مستقل شوند. به عنوان مثال، در سیستمهای قدیمی که کدهای بزرگ و به هم ریخته دارند، تیمها هفتهها درگیر تست دستی میشوند، در حالی که معماری ماژولار اجازه میدهد بخشهای مختلف به صورت مستقل توسعه پیدا کنند. چالش دوم مدیریت ویژگیهایی است که هنوز کامل نشدهاند و نباید همه کاربران آنها را ببینند. گزینههای پیش روی تیمها این است که یا منتظر بمانند کل پروژه تمام شود یا از مکانیزم پرچم ویژگی (Feature Flag) برای کنترل نمایش کدها استفاده کنند. فایده پرچم ویژگی این است که کد جدید در کنار کد قدیمی در برنامه وجود دارد اما با یک کلید دینامیک پنهان شده است و میتوان آن را بدون انتشار نسخه جدید فعال یا غیرفعال کرد. مثلاً وقتی قرار است همزمان با یک کمپین مطبوعاتی ویژگی جدیدی رونمایی شود، تیم توسعه نیازی ندارد نگران لو رفتن کدها از قبل باشد؛ کافی است پرچم ویژگی را در لحظه مناسب روشن کنند تا قابلیت برای کاربران نمایان شود. چالش سوم برخورد با باگها و کمالگرایی در انتشار است که میتواند کل چرخه را معطل کند. خیلی از تیمها فکر میکنند برای انتشار نسخه جدید باید همه باگها، حتی کوچکترین موارد برطرف شوند، اما این رویکرد سرعت را کاملاً فلج میکند. نویسندگان این کتاب معتقدند هیچ باینری (Binary) کاملی وجود ندارد و فرآیند ساخت باید قطعی باشد؛ یعنی اگر یک ویژگی به قطار انتشار نرسد، باید بدون تعارف بماند برای قطار بعدی که چند ساعت یا چند روز بعد حرکت میکند. به عنوان مثال، در پروژه جستجوی گوگل، به جای اینکه کل انتشار به خاطر یک باگ در یک گویش محلی بسیار نادر متوقف شود، قطار انتشار حرکت خود را حفظ میکند و تیمها یاد میگیرند که ضربالاجلها غیرقابل مذاکره هستند و سرعت بالا خودش عامل کاهش ریسک است. چالش چهارم این است که رشد محصول معمولاً باعث انباشته شدن کدهای استفادهنشده تورم نرمافزاری میشود که به دستگاه کاربران آسیب میزند. وقتی نرمافزاری برای مدت طولانی توسعه پیدا میکند، حجم زیادی از کدهای قدیمی یا ویژگیهای بدون استفاده روی گوشی کاربر دانلود میشود. راهحل این است که با استقرارهای پویا و تستهای ای/بی (A/B Testing)، یعنی روشی برای مقایسه دو نسخه مختلف نرمافزار روی گروههای کاربری مشابه برای سنجش دقیق عملکرد، فقط کدهایی که واقعاً ارزش دارند روی دستگاه کاربر نهایی ارسال شوند. پیشنهاد عملی من به شما این است که اگر در تیم خودتان مدام نگران انتشار نسخههای بزرگ هستید، به جای سختتر کردن تستها، سعی کنید اندازه دستههای تغییرات را کوچکتر کنید و سیستم خود را طوری بسازید که انتشار مداوم برایتان تبدیل به یک روال عادی شود. در نهایت، سرعت بیشتر به معنای ریسک کمتر است، به شرطی کهdiscipline انضباط تیمی را در تمام مراحل توسعه و استقرار رعایت کنید.