کتابهای کلیدواژه: مقیاسپذیری سازمانی · همهٔ کتابها
کتابخانهٔ شنیدنی شبگفتار
کتابهای مقیاسپذیری سازمانی
خلاصههای صوتی فارسی کتابهای مرتبط با مقیاسپذیری سازمانی؛ کتاب و فصل دلخواهتان را برای شنیدن انتخاب کنید.
این مجموعه خلاصهٔ کتابهاست؛ نسخهٔ کامل کتاب اصلی نیست.
1 عنوان · 26 قسمت منتشرشده
راه را پیدا کنید
مسیرهای مطالعاتی
قفسهٔ کتابها
از این کتابها شروع کنید
فصل بیستوپنجم: محاسبات به عنوان سرویس و زیرساخت مقیاسپذیر در گوگل
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که وقتی تعداد کاربران یک سامانه به میلیونها نفر میرسد، کدهای ما چطور روی هزاران سرور مختلف اجرا و مدیریت میشوند؟ چطور میتوانیم مطمئن باشیم که با خرابشدن سختافزارها، سرویس ما از دست نمیرود؟
کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل بیستوپنجم به سراغ یکی از بنیادیترین مفاهیم زیرساختی یعنی محاسبات به عنوان سرویس (Compute as a Service) میرود و نشان میدهد که چطور باید بستر سختافزاری و نرمافزاری را برای مقیاسهای بزرگ طراحی کرد.
در روزهای اولیه، مهندسان نرمافزار مجبور بودند کدهای خود را به صورت دستی روی تکتک ماشینها کپی کنند، آنها را اجرا کنند و نگران خرابی تکتک سیستمها باشند. با رشد شرکت، این روش سنتی به کابوسی از کارهای تکراری تبدیل شد. راهحل اولیه استفاده از اسکریپتهای ساده و ابزارهای مانیتورینگ بود، اما در مقیاسهای کلان، سازمان به برنامهریزی خودکار برای تخصیص ماشینها نیاز دارد. برای نمونه، گوگل سیستم مدیریت خوشهای خود یعنی «بورگ» را ساخت تا وظایف پردازشی را به صورت خودکار روی منابع موجود پخش کند و در صورت خرابی ماشین، کار را به سرعت به سرور دیگری منتقل سازد.
مشکل بعدی این بود که اختصاص دادن یک سرور کامل به یک برنامه، اسراف بزرگی در مصرف منابعی مثل پردازنده و حافظه به حساب میآمد. راهحل این بود که چندین برنامه روی یک ماشین مشترک اجرا شوند، اما این کار خطراتی مثل تداخل منابع به همراه داشت. وقتی برنامهای بیش از حد حافظه مصرف کند، سیستمعامل میتواند کل فرآیند را متوقف کند. استفاده از ماشینهای مجازی هم راهحل سنگینی بود، زیرا سیستمعامل کامل خود را نیاز داشت. بنابراین گوگل به سراغ کانتینرسازی (Containerization) رفت که تکنیکی سبکوزن برای جداسازی فرآیندها روی یک سیستمعامل واحد است.
تغییر زیرساخت به سیستمهای خودکار نیازمند تغییر نگاه ما به نرمافزار هم هست؛ سرویسها نباید مثل حیوانات خانگی نگهداری شوند که اگر بیمار شدند بالاسرشان بنویسیم، بلکه باید مثل دام و گله (Cattle) با آنها رفتار کرد؛ یعنی اگر نمونهای از کار افتاد، خودکار حذف شود و یک نمونهٔ سالم جایگزینش گردد. در این مدل، وضعیت برنامهها باید ناپایدار (Transient) در نظر گرفته شود و دادههای مهم در فضای ذخیرهسازی ماندگار نگهداری شوند. برای مثال، اگر در یک پردازش دستهای (Batch Job) یکی از کارگرها وسط کار بسوزد، سامانه باید کار را به تکههای کوچکتر خرد کرده باشد تا فقط همان تکهٔ کوچک دوباره پردازش شود.
با گذشت زمان، مفهوم کانتینر فراتر از یک ابزار جداسازی رفت و به عنوان یک لایهٔ انتزاعی عمل کرد تا برنامهها نیازی به دانستن جزئیات سختافزار زیر پای خود نداشته باشند. اما قانون هایروم (Hyrum’s Law) یادآوری میکند که وقتی سیستم به اندازهٔ کافی کاربر داشته باشد، حتی رفتارهای ناخواسته یا محدودیتهای سیستمعامل مثل تعداد شناسهٔ فرآیندها به قراردادی پنهان تبدیل میشوند که کاربران به آن وابستگی پیدا میکنند و تغییر دادنشان بسیار پرهزینه خواهد شد.
پیشنهاد من این است که هنگام طراحی سامانههای خود، از همان ابتدا فرض را بر این بگذارید که سختافزار و سرورها ممکن است هر لحظه از دست بروند و نرمافزار خود را به گونهای بنویسید که بدون وابستگی به یک ماشین خاص، به صورت خودکار بازیابی شود.
مدیریت درست زیرساخت محاسباتی ستون فقرات پایداری هر سازمان نرمافزاری بزرگ است و شناخت الگوهای موفق گوگل به ما کمک میکند تا در مقیاسهای بالا دچار بحران نشویم.
فصل بیستوچهارم: تحویل پیوسته و مهندسی نرمافزار در گوگل
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردید که چرا بعضی از نرمافزارها مدام و بدون مشکل بهروزرسانی میشوند، در حالی که انتشار یک نسخه جدید در بعضی شرکتها مثل یک پروژه نجات گروگان است؟ وقتی سرعت تغییرات در دنیای فناوری بالا است، مزیت رقابتی هر محصولی به توانایی آن در ارائه سریع ایدهها به دست کاربران بستگی دارد، اما این سرعت معمولاً پشت سد فرآیند استقرار گیر میکند. کتاب «مهندسی نرمافزار در گوگل» در فصل بیستوچهارم با عنوان تحویل پیوسته (Continuous Delivery) به ما نشان میدهد که چطور سرعت و ایمنی در توسعه نرمافزار در تناقض با هم نیستند، بلکه دست در دست هم دارند. این فصل برای هر کسی که میخواهد چالشهای مقیاسپذیری و انتشار نرمافزار را درک کند، حیاتی است. اولین چالش جدی در این مسیر، هزینهبر بودن انتشارها است که تیمها را وادار میکند انتشارها را عقب بیندازند تا آنها را بیشتر تست کنند. وقتی حجم تغییرات انباشته میشود، فرآیند انتشار پیچیده و پرخطا میشود و کسی جرئت ندارد دکمه انتشار را فشار دهد. راهحل سنتی اضافه کردن نظارت و مدلهای برنامهریزی قدیمی است که فقط سرعت را میگیرد، اما نویسندگان کتاب پیشنهاد میکنند به جای این کار، معماری سیستم را به سمت ریزسرویسها (Microservices) ببرید تا تغییرات کوچک و مستقل شوند. به عنوان مثال، در سیستمهای قدیمی که کدهای بزرگ و به هم ریخته دارند، تیمها هفتهها درگیر تست دستی میشوند، در حالی که معماری ماژولار اجازه میدهد بخشهای مختلف به صورت مستقل توسعه پیدا کنند. چالش دوم مدیریت ویژگیهایی است که هنوز کامل نشدهاند و نباید همه کاربران آنها را ببینند. گزینههای پیش روی تیمها این است که یا منتظر بمانند کل پروژه تمام شود یا از مکانیزم پرچم ویژگی (Feature Flag) برای کنترل نمایش کدها استفاده کنند. فایده پرچم ویژگی این است که کد جدید در کنار کد قدیمی در برنامه وجود دارد اما با یک کلید دینامیک پنهان شده است و میتوان آن را بدون انتشار نسخه جدید فعال یا غیرفعال کرد. مثلاً وقتی قرار است همزمان با یک کمپین مطبوعاتی ویژگی جدیدی رونمایی شود، تیم توسعه نیازی ندارد نگران لو رفتن کدها از قبل باشد؛ کافی است پرچم ویژگی را در لحظه مناسب روشن کنند تا قابلیت برای کاربران نمایان شود. چالش سوم برخورد با باگها و کمالگرایی در انتشار است که میتواند کل چرخه را معطل کند. خیلی از تیمها فکر میکنند برای انتشار نسخه جدید باید همه باگها، حتی کوچکترین موارد برطرف شوند، اما این رویکرد سرعت را کاملاً فلج میکند. نویسندگان این کتاب معتقدند هیچ باینری (Binary) کاملی وجود ندارد و فرآیند ساخت باید قطعی باشد؛ یعنی اگر یک ویژگی به قطار انتشار نرسد، باید بدون تعارف بماند برای قطار بعدی که چند ساعت یا چند روز بعد حرکت میکند. به عنوان مثال، در پروژه جستجوی گوگل، به جای اینکه کل انتشار به خاطر یک باگ در یک گویش محلی بسیار نادر متوقف شود، قطار انتشار حرکت خود را حفظ میکند و تیمها یاد میگیرند که ضربالاجلها غیرقابل مذاکره هستند و سرعت بالا خودش عامل کاهش ریسک است. چالش چهارم این است که رشد محصول معمولاً باعث انباشته شدن کدهای استفادهنشده تورم نرمافزاری میشود که به دستگاه کاربران آسیب میزند. وقتی نرمافزاری برای مدت طولانی توسعه پیدا میکند، حجم زیادی از کدهای قدیمی یا ویژگیهای بدون استفاده روی گوشی کاربر دانلود میشود. راهحل این است که با استقرارهای پویا و تستهای ای/بی (A/B Testing)، یعنی روشی برای مقایسه دو نسخه مختلف نرمافزار روی گروههای کاربری مشابه برای سنجش دقیق عملکرد، فقط کدهایی که واقعاً ارزش دارند روی دستگاه کاربر نهایی ارسال شوند. پیشنهاد عملی من به شما این است که اگر در تیم خودتان مدام نگران انتشار نسخههای بزرگ هستید، به جای سختتر کردن تستها، سعی کنید اندازه دستههای تغییرات را کوچکتر کنید و سیستم خود را طوری بسازید که انتشار مداوم برایتان تبدیل به یک روال عادی شود. در نهایت، سرعت بیشتر به معنای ریسک کمتر است، به شرطی کهdiscipline انضباط تیمی را در تمام مراحل توسعه و استقرار رعایت کنید.
فصل بیستوسوم: یکپارچهسازی پیوسته و ساخت خودکار
رایگانتوضیح و منابع این قسمت
تا به حال برایتان پیش آمده که تغییر کوچکی در برنامه بدهید، اما کل سیستم از کار بیفتد و ساعتها به دنبال پیدا کردن مشکل باشید؟ امروز میخواهیم ببینیم چطور میتوانیم خطاها را خیلی زودتر و پیش از رسیدن به کاربران کشف کنیم.
در کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت»، فصل بیستوسوم به موضوع یکپارچهسازی پیوسته (Continuous Integration) اختصاص دارد که به ما نشان میدهد چطور در مقیاس بزرگ، تغییرات تیمهای مختلف را خودکار و مطمئن کنار هم جمع کنیم.
نخستین ایده، اهمیت چرخههای بازخورد سریع است. مشکل هرچه دیرتر کشف شود، هزینهٔ رفع آن بیشتر و فشار روی توسعهدهنده سنگینتر میشود. گزینههای پیش رو شامل اجرای تستها در زمان محلی، اجرای تستهای پیشثبت presubmit، و در نهایت رسیدن خطا به محیط تولید است. این کتاب نشان میدهد که با ایجاد بازخوردهای سریع، هزینهٔ رفع باگها به شدت کاهش مییابد؛ برای نمونه، اجرای تستهای واحد سریع پیش از ثبت نهایی تغییرات، جلوی ورود بسیاری از خطاهای ساده را میگیرد.
ایدهٔ دوم، تفکیک مرحلهٔ پیشثبت از پسثبت است. ایده آل این است که همهٔ تستها قبل از تایید نهایی اجرا شوند، اما اجرای همهٔ تستها زمانبر و هزینهبر است و بهرهوری مهندسان را پایین میآورد. راه حل این است که تستهای سریع و مطمئن در مرحلهٔ پیشثبت اجرا شوند و تستهای بزرگتر و کندتر به مرحلهٔ پسثبت موکول شوند. به عنوان نمونه، تیمها معمولاً تستهای واحد را در پیشثبت میگذارند تا توسعهدهنده معطل نشود و تستهای سنگینتر سیستم پس از ثبت تغییرات اجرا شوند.
ایدهٔ سوم، استفاده از تستهای هرمتیک hermetic tests است که به تستهایی گفته میشود که در یک محیط کاملاً خودکار و ایزوله بدون وابستگی به سرویسهای واقعی بیرونی اجرا میشوند. گزینههای مختلفی مثل اتصال به پایگاه دادههای واقعی، استفاده از دادههای تقلبی fakes، و استفاده از روشهای ضبط و پخش پاسخها وجود دارد. از آنجا که وابستگی بیرونی وجود ندارد، این تستها بسیار پایدارتر هستند؛ مثلاً گوگل دستیار صوتی خود را به گونهای طراحی کرده که تمام تستهای پیشثبت آن به صورت هرمتیک اجرا شوند تا ناپایداری شبکه روی نتایج اثر نگذارد.
ایدهٔ چهارم، مدیریت خرابیها و حفظ سلامت تستها است. وقتی تستهای بزرگ شکست میخورند، رها کردن آنها باعث انباشت بدهی فنی میشود. گزینهها شامل غیرفعال کردن دائمی تست یا پیوند زدن شکستها به سیستم بلیتها است. نویسندگان نشان میدهند که تگ کردن تستهای خراب با یک بلیت مشخص به تیم اجازه میدهد مسیر انتشار را باز نگه دارد؛ برای نمونه، پروژهٔ گوگل تیکاوت با استفاده از سیستم تگگذاری خودکار، تستهای موقتاً خراب را مدیریت میکند تا هم تیم متوجه مشکل باشد و هم انتشار مسدود نشود.
ایدهٔ پنجم، سنجش و بازخورد شفاف است. اگر گزارش تستها قابل خواندن نباشد، کسی به آنها توجه نمیکند. گزینهٔ دیگر، بازگرداندن گزارشهای مبهم در قالب لاگهای طولانی یا طراحی رابط کاربری صریح است. این کتاب نشان میدهد که بازخورد شفاف کارایی را بالا میبرد؛ مثلاً گوگل تیکاوت با بازطراحی پیامهای خطا و تبدیل آنها به پیوندهای مستقیم جستجو در لاگها، زمان عیبیابی توسعهدهندگان را به شدت کاهش داد.
پیشنهاد من به شما این است که اگر سیستم تست خودکار دارید، ابتدا مطمئن شوید تستهای حیاتی و سریع واقعاً در مسیر توسعهی روزمره قرار دارند و به جای کلافه شدن از تستهای ناپایداری که مرتب هشدار اشتباه میدهند، آنها را ایزوله یا بازنویسی کنید.
امیدوارم این بخش از نگاه ما به فرآیندهای مهندسی نرمافزار برایتان مفید بوده باشد. تا اپیزود بعدی، خدانگهدار.
فصل بیستودوم: تغییرات بزرگ در کدهای عظیم
رایگانتوضیح و منابع این قسمت
تا به حال فکر کردهاید اگر بخواهید همزمان صدها فایل را در پروژه خود تغییر دهید و همه چیز هم درست کار کند، چقدر زمان و انرژی لازم است؟ چه چیزی مانع از این میشود که تغییرات بسیار بزرگ را به صورت یکجا در سیستم ثبت کنید و آیا اصلاً در شرایط اضطراری قادر به انجام چنین کاری هستید؟
این مسائل و روشهای حل آنها در کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل تغییر در مقیاس بزرگ (Large-Scale Change) به تفصیل بررسی شدهاند و اهمیت این موضوع را در نگهداری سیستمهای نرمافزاری نشان میدهند.
اولین ایده کلیدی در این زمینه، مشکل تغییرات اتمی در مقیاس وسیع است. با بزرگتر شدن مخزن کد، انجام تغییرات همزمان در هزاران فایل به دلیل محدودیتهای سیستم کنترل نسخه و احتمال بالای تداخل ویرایشها عملاً غیرممکن میشود. گزینههای پیش روی تیمها این است که یا تغییرات کوچک و پیاپی بسازند یا سیستمهای خود را به تکههای مجزا تقسیم کنند. مشکل اینجاست که تغییرات بزرگ سنتی باعث مسدود شدن سیستم و ایجاد تداخل میشود، و نمونه بارز آن نیاز به ارتقای کتابخانههای قدیمی در کل مخزن است که امکان ارسال یکجای آنها وجود ندارد.
دومین ایده کلیدی متمرکز بر این نکته است که چه کسی باید مسئولیت این تغییرات گسترده را بر عهده بگیرد. تیمهای زیرساخت اغلب مجبورند تغییرات را به صورت سراسری اعمال کنند زیرا تیمهای محصول تخصص یا انگیزه کافی برای این کار را ندارند. گزینه جایگزین این است که هر تیم خودش کدهایش را بهروز کند، اما معاوضه آن در این است که واگذاری این وظیفه به تیمهای مختلف باعث اتلاف وقت و تکرار کارهای تخصصی میشود و در نهایت کار زمین میماند. به عنوان نمونه، انتقال از اشارهگرهای قدیمی سیپلاسپلاس به مدل جدید استاندارد در گوگل، نیازمند متمرکز کردن این فرآیند توسط تیم زیرساخت بود تا صدها هزار مرجع به درستی اصلاح شوند.
سومین ایده کلیدی به چالشهای تست کردن مربوط میشود. اجرای تستها برای تغییرات بزرگ بسیار زمانبر است و سیستمهای یکپارچهسازی پیوسته (Continuous Integration) زیر بار سنگینی میروند. گزینههای موجود شامل اجرای تکتک تستها یا نادیده گرفتن بخشهایی از آنهاست، اما معاوضه اصلی میان دقت تست و سرعت توسعه شکل میگیرد. گوگل برای حل این مسئله از قطاری موسوم به پلتفرم اتوماسیون تست TAP train استفاده میکند که در آن تغییرات تاییدشده با هم ترکیب میشوند تا مجموعه تستهای مرتبط به صورت دستهای و با بهینهسازی زمان اجرا بررسی شوند.
چهارمین ایده کلیدی به ابزار مدیریت تغییرات و فرآیند بازبینی مربوط است. بررسی دستی تغییرات گسترده برای انسان خستهکننده و خطاپذیر است، بنابراین ابزارهایی مانند Rosie به کار گرفته میشوند تا تغییرات بزرگ را به قطعات کوچکتر تبدیل کنند. گزینه دیگر این است که تایید نهایی را به تاییدکنندگان سراسری global approvers بسپاریم که با ابزارهای الگو محور کار میکنند. معاوضه این روش در این است که مسئولیت بازبینی از روی تیمهای محلی برداشته میشود و به افراد متخصصی سپرده میشود که زبانها و کتابخانهها را به خوبی میشناسند تا فرآیند تایید با سرعت و دقت بیشتری جلو برود.
پیشنهاد عملی من به شما این است که اگر در سازمان خود با تغییرات ساختاری بزرگ روبرو هستید، به جای اینکه همه تیمها را به طور همزمان مجبور به ویرایش دستی کدها کنید، یک ابزار خودکار برای ایجاد تغییرات طراحی کنید و فرآیند بازبینی و تست را به صورت قطعهقطعه و مدیریتشده پیش ببرید.
امیدوارم این مرور کوتاه به شما ایده داده باشد که چگونه میتوان کدهای بزرگ را بدون ترس و با کمک ابزارهای مناسب در طول زمان همگام و انعطافپذیر نگه داشت.
فصل بیستویکم: مدیریت وابستگیها در مهندسی نرمافزار
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا وقتی یک کتابخانه یا بسته را در پروژهتان آپدیت میکنید، ناگهان بخشهای کاملاً بیربطی از برنامه از کار میافتند؟ چطور ممکن است کدی که سالها بدون مشکل کار کرده، با یک تغییر کوچک در پشتهٔ فنی زیرین، کل سیستم را زمین بزند؟
در فصل بیستویکم کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت»، به سراغ یکی از پیچیدهترین و کمدرکشدهترین چالشها در دنیای کدنویسی، یعنی مدیریت وابستگی (Dependency Management) رفتهاند و بررسی کردهاند که چطور مدیریت این شبکههای پیچیده میتوانند آیندهٔ پروژههای نرمافزاری را تضمین یا تخریب کنند.
مشکل اصلی اینجاست که برنامهنویسان اغلب به وابستگیها به چشم تکهکدهایی منفرد نگاه میکنند، در حالی که ما با یک شبکهٔ درهمتنیده از کتابخانهها روبرو هستیم که تغییر در یک نقطه از آن، موجی از تغییرات را در سراسر سیستم ایجاد میکند. گزینههای پیش روی تیمها از انجماد نسخهها و تغییر ندادن هیچچیز تا استفاده از مدلهای پیچیده متغیر است، اما معاملهٔ اصلی اینجاست: استفاده از کد دیگران سرعت توسعه را بالا میبرد اما هزینهٔ نگهداری درازمدت و ریسک خرابی را به شدت افزایش میدهد. برای نمونه، اگر دو کتابخانهٔ مجزا در پروژهٔ شما به دو نسخهٔ ناسازگار از یک کتابخانهٔ پایه نیاز داشته باشند، با یک بنبست جدی در ساختار پروژه روبهرو خواهید شد.
وقتی صحبت از مقیاس و زمان میشود، مسئله ابعاد تازهای به خود میگیرد و قانون هایروم (Hyrum's Law) وارد میدان میشود که به این معناست: با داشتن تعداد کافی کاربر، هر رفتار واقعی از سیستم شما، حتا رفتارهای مستندنشده، توسط کسی به عنوان قرارداد استفاده خواهد شد. گزینههای مختلفی مثل نسخهگذاری معنایی (Semantic Versioning) تلاش میکنند با اعداد و ارقام این ریسک را مدیریت کنند، اما این روشها در مقیاس بزرگ اغلب شکست میخورند زیرا نمیتوانند تمام وابستگیهای پنهان و رفتارهای ناخواسته را پیشبینی کنند.
در دنیای مهندسی نرمافزار، وابستگیها کدهای رایگان و بدون هزینهای نیستند که بتوانیم به راحتی آنها را به پروژه تزریق کنیم و فراموششان کنیم. برای مقابله با این چالشها، پیشنهاد من این است که پیش از افزودن هر کتابخانهٔ خارجی، نه تنها به کارایی امروز آن، بلکه به هزینهٔ پنهان آپدیتهای سالهای آینده و توانایی تیم در نگهداری آن فکر کنید و تا جای امکان، ترجیح دهید مسائل را درون سازمان خود حل کنید تا اینکه به کتابخانههای کنترلنشده وابسته شوید.
مدیریت وابستگیها هنرِ تعادل میان استفاده از کدهای آماده و پذیرش ریسک تغییرات در طول زمان است و شناخت این مرز باریک، تفاوت میان یک نرمافزار شکننده و یک سیستم پایدار را رقم میزند.
فصل بیستم: تحلیل ایستا در مقیاس گوگل
رایگانتوضیح و منابع این قسمت
تا حالا برایتان پیش آمده که موقع نوشتن کد، متوجه شوید یک اشتباه ساده مثل خطای تایپی در متن لاگ یا استفاده از یک متغیر دستنخورده، کل برنامهتان را از کار انداخته است؟ چطور میشود این باگها را قبل از اینکه حتی کد اجرا شود و به دست کاربر برسد، شناسایی کرد؟
کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل بیستم به سراغ بررسی کد بدون اجرای آن میرود و نشان میدهد چطور ابزارها میتوانند کیفیت نرمافزار را در مقیاس عظیم شرکت گوگل تضمین کنند.
نخستین چالش در ابزارهای بررسی کد، مقیاسپذیری (Scalability) است؛ یعنی توانایی سازمان و سیستم در رشد بدون افت کیفیت. کدهای گوگل میلیاردها خط هستند و ابزار تحلیل ایستا (Static Analysis) یعنی بررسی کد بدون اجرای آن، باید سریع اجرا شود بدون اینکه توسعهدهنده را معطل کند. راهکار این است که ابزارها به صورت خردپذیر و افزایشی طراحی شوند و تنها روی فایلهای تغییریافته تمرکز کنند. مثلاً به جای بررسی کل پروژه، فقط خطوط ویرایششده در یک تغییر کد تحلیل میشوند تا نتیجه در کسری از ثانیه آماده شود.
چالش دوم، هزینهها و فواید ابزار برای توسعهدهندگان کاربردپذیری است. اگر ابزاری اخطارهای زیادی بدهد که بیارتباط هستند، زمان توسعهدهنده هدر میرود و ممکن است او کدهایی را که درست کار میکنند به خاطر یک اخطار اشتباه دستکاری کند و باگ جدیدی بسازد. گوگل برای حل این مسئله تمرکز خود را روی اخارهای جدید گذاشته و تلاش کرده ابزارها خطاهای نادرست کمی تولید کنند تا اعتماد برنامهنویسان حفظ شود.
مسئلهٔ سوم، لزوم ادغام این ابزارها با جریان کار اصلی مهندسان بازبینی کد (Code Review) یعنی بررسی تغییرات کد توسط همکاران پیش از ادغام است. در گوگل، تحلیل ایستا درست در زمان بازبینی کد و در ابزار مرکزی آن نمایش داده میشود. چون توسعهدهنده در همان لحظه در حال بررسی تغییرات است، این اخطارها کمترین مزاحمت را ایجاد میکنند و از طرفی فشار همکاران باعث میشود این هشدارها جدی گرفته شوند.
چالش چهارم، مشارکت دادن خود توسعهدهندگان در ساخت ابزار است. تیمهای مختلف در گوگل دانش تخصصی خود را دارند و میخواهند قوانین خاص خود را اعمال کنند. گوگل با ساخت پلتفرمی ساده به نام تریکوردر امکان میدهد که مهندسان خودشان چکهای جدید بسازند. به کمک ابزارهایی مثل ریفستر که نمونه کدهای قبل و بعد را میگیرند، نوشتن قوانین جدید بسیار ساده شده و نیازی نیست همه متخصص تحلیل کد باشند.
پیشنهاد من به شما این است که در تیم خودتان، ابزارهای تحلیل ایستا را به جای سیستمهای مزاحم و پر از خطای کاذب، به دستیاران قابل اعتمادی تبدیل کنید که فقط به اشکالات واقعی گیر میدهند. وقتی برنامهنویسان ببینند ابزار واقعاً به آنها کمک میکند و پیشنهاد اصلاح خودکار میدهد، با اشتیاق از آن استفاده خواهند کرد.
امیدوارم این بخش از نگاه گوگل به تحلیل ایستا برایتان مفید بوده باشد و بتوانید از ایدههای آن در پروژههای خودتان استفاده کنید.
فصل نوزدهم: بازبینی کد در مقیاس گوگل با ابزار Critique
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که در شرکتی با هزاران توسعهگر و یک مخزن بزرگ کد، چطور مطمئن میشوند کدهای جدید کیفیت لازم را دارند و سیستم را خراب نمیکنند؟ فرآیند بازبینی کد (Code Review) یکی از ارکان اصلی در مهندسی نرمافزار (Software Engineering) است، اما ابزارهایی که این فرآیند را پشتیبانی میکنند به همان اندازهٔ خود فرآیند اهمیت دارند.
کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل نوزدهم به سراغ ابزار داخلی گوگل به نام Critique میرود و نشان میدهد که چطور یک ابزار ساده و متمرکز میتواند مقیاسپذیری (Scalability) توسعه نرمافزار را تضمین کند.
نخستین ایدهٔ کلیدی در طراحی این ابزار، سادگی افراطی در رابط کاربری است. مشکل اینجاست که ابزارهای پیچیده با گزینههای زیاد، توسعهدهندگان را خسته میکنند و سرعت کار را پایین میآورند. طراحان این ابزار بین افزودن قابلیتهای مختلف و حفظ سادگی، همیشه سادگی را انتخاب کردهاند؛ بارگذاری سریع، استفاده از کلیدهای میانبر و نشانگرهای بصری واضح از ویژگیهای این رویکرد است.
ایدهٔ دوم، تکیه بر بنیاد اعتماد و ارتباطات عمومی است. در فرهنگ کاری گوگل، بازبینی کد ابزاری برای کند کردن دیگران نیست، بلکه راهی برای توانمندسازی آنهاست. ابزار پیامرسان یا پروتکل پیچیدهای برای ارتباطات ندارد و از توسعهدهندگان میخواهد نظرات خود را صریح بنویسند یا پیشنهاد ویرایش ارائه دهند تا از سوءتفاهمها جلوگیری شود.
ایدهٔ سوم مدیریت جریان کار و قابلیت مقایسهٔ تفاوتهاست. درک کدهای تغییریافته در تغییرات بزرگ دشوار است، بنابراین ابزار از الگوریتمهای پیشرفته برای برجسته کردن تغییرات خطی، نادیده گرفتن فضاهای خالی و تشخیص جابهجایی کدها استفاده میکند. همچنین تحلیل ایستا (Static Analysis) به صورت خودکار روی کدها اجرا میشود و خطاهای سبک نگارشی را با تراشههای رنگی نشان میدهد تا نویسنده پیش از بررسی انسانی آنها را اصلاح کند.
ایدهٔ چهارم ویژگی مشخص کردن مسئول فعلی است. در پروژههایی که چندین بازبینیکننده وجود دارد، فهمیدن اینکه الان نوبت چه کسی است که واکنش نشان دهد یک چالش جدی است. این ویژگی مشخص میکند که تغییر فعلی در انتظار پاسخ چه کسی است و نام افراد مسئول را پررنگ میکند تا روند کار متوقف نشود.
ایدهٔ پنجم امتیازدهی و تأیید نهایی تغییرات است که بر اساس تأییدیهٔ مربوط به ظاهر مناسب کد، تأیید رسمی و نبود نظرات حلنشده انجام میشود. این سیستم ساده به نویسنده اجازه میدهد به محض دریافت تأییدیهها، کد را با اطمینان وارد مخزن اصلی کند.
پیشنهاد من این است که در تیمهای خودتان، به جای پیچیده کردن فرآیندهای بازبینی با قوانین دستوپاگیر، روی سادگی ابزارها، شفافیت در تعیین مسئولیت هر مرحله و اعتماد متقابل تمرکز کنید.
امیدوارم از این روایت لذت برده باشید و در قسمتهای بعدی با مباحث بیشتری از مهندسی نرمافزار در گوگل همراه ما باشید.
فصل هجدهم: فلسفه و سیستمهای ساخت در مهندسی نرمافزار
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا در بسیاری از شرکتهای بزرگ، توسعهدهندگان از ابزارهای ساخت کلافهاند و مدام باید منتظر تمامشدن پردازشها بمانند؟ آیا واقعاً میتوان سیستمی داشت که همزمان با رشد تیم، هم سریع باشد و هم جلوی خطاهای انسانی را بگیرد؟
در کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت»، فصل هجدهم به موضوع سیستم ساخت (Build System) و فلسفهٔ پشت آن میپردازد و نشان میدهد چطور گوگل توانسته با بازنگری در این ابزارها، چالش مقیاسپذیری را حل کند.
بسیاری از برنامهنویسان در شروع کار یاد میگیرند که کامپایلرها را مستقیم از طریق خط فرمان صدا بزنند یا برای کارهای پیچیدهتر، اسکریپتهای پوستهای بنویسند. اما با بزرگتر شدن پروژه و افزایش تعداد توسعهدهندگان، این اسکریپتها کند میشود، مدیریت وابستگیها از دست میرود و تفاوتهای کوچک میان سیستم افراد مختلف باعث بروز خطاهای عجیب میگردد.
برای حل این مشکل، صنعت نرمافزار به سراغ سیستم ساخت (Build System) رفت که ابزاری برای تبدیل کد منبع به خروجیهای قابل اجراست. سیستمهای سنتی مبتنی بر وظیفه (Task-based Build System) به برنامهنویسان اجازه میدهند هر اسکریپتی را به عنوان یک وظیفه تعریف کنند؛ اما از آنجا که سیستم نمیداند درون این اسکریپتها چه میگذرد، نمیتواند پردازشها را به صورت موازی اجرا کند یا نتایج را به درستی ذخیره و بازاستفاده نماید.
گوگل برای غلبه بر این محدودیت، رویکرد متفاوتی را انتخاب کرد و به سمت سیستم ساخت مبتنی بر فرآورده (Artifact-based Build System) رفت. در این روش که نمونهٔ متنباز آن Bazel نام دارد، به جای نوشتن اسکریپتهای امری، فایلهای پیکربندی به صورت توصیفی نوشته میشوند تا سیستم دقیقاً بداند چه فرآوردههایی باید تولید شوند؛ در نتیجه، ابزار ساخت میتواند کنترل کامل زمانبندی را در دست بگیرد.
وقتی سیستم ساخت دقیقاً بداند هر ابزار چه ورودی و خروجی دارد، امکانات قدرتمندی مثل موازیسازی و ایجاد کش توزیعشده فراهم میشود. علاوه بر این، با استفاده از روشهایی مانند ایزولهسازی فایلها و اجبار به مدیریت دستی و صریح وابستگیها، جلوی بسیاری از خطاهای پنهان و ناسازگاریها در مخزنهای بزرگ گرفته میشود.
پیشنهاد من به عنوان میزبان این پادکست این است که حتی اگر پروژهٔ کوچکی دارید، از همین امروز ساختار وابستگیهای پروژهتان را دقیق و مستند تعریف کنید تا در آینده با رشد تیم دچار دردسرهای بازبینی و خطایابی نشوید.
امیدوارم این روایت از نگاه گوگل به سیستمهای ساخت برای شما مفید بوده باشد. تا پادکست بعدی، روزگارتان خوش.
فصل هفدهم: جستجوی کد در ابعاد عظیم
رایگانتوضیح و منابع این قسمت
تا حالا برایتان پیش آمده که وسط یک پروژه بزرگ گم شوید و ندانید فلان تابع یا تنظیمات دقیقا کجا تعریف شده است؟ وقتی ابعاد یک پروژه نرمافزاری به اندازه کل کدهای گوگل بزرگ میشود، ابزارهای معمولی دیگر جواب نمیدهند و پیدا کردن یک تکه کد ساده به چالشی بزرگ تبدیل میشود.
کتاب «مهندسی نرمافزار در گوگل» نوشته «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل هفدهم به سراغ ابزاری میرود که دقیقا برای حل همین مشکل طراحی شده است.
اولین چالش در مقیاسهای کلان، سرعت پاسخگویی و تاخیر در جستجوی کد (Code Search) است. وقتی هزاران توسعهدهنده روزانه میلیونها جستجو انجام میدهند، حتی یک ثانیه تاخیر اضافی میتواند زمان زیادی از کل تیم تلف کند. گوگل برای حل این مشکل، به جای روشهای کند قدیمی، از نمایهسازی indexing استفاده میکند که دادهها را از پیش پردازش میکند تا نتایج در کسری از ثانیه ظاهر شوند. اگر قرار بود هر بار جستجو به صورت خام و با دستورات ساده انجام شود، به هزاران پردازنده نیاز بود، اما با یک ساختار بهینه، زمان پاسخ به زیر دویست میلیثانیه میرسد.
چالش دوم این است که در کدهایی با ابعاد عظیم، نتایج جستجو پر از موارد مشابه میشوند و توسعهدهنده در میان هزاران فایل گم میشود. راهحل گوگل استفاده از رتبهبندی ranking بر اساس سیگنالهای مختلف است. این سیستم بررسی میکند که کدام فایلها بیشتر توسط مهندسان دیده شدهاند یا چقدر ارجاع و import دارند، تا در نهایت مرتبطترین نتیجه را در بالای فهرست نشان دهد. مثلا وقتی نام یک تابع را جستجو میکنید، تعریف اصلی آن خیلی بالاتر از یک استفاده تصادفی در یک نظر یا کامنت نمایش داده میشود.
چالش سوم این است که یک جستجوی خوب باید فراتر از تطابق ساده کلمات عمل کند و روابط معنایی بین کدها را درک کند. ابزاری به نام Kythe در اینجا وارد میدان میشود تا ارجاعات متقاطع cross-references را مدیریت کند. این فناوری به توسعهدهنده اجازه میدهد با یک کلیک ساده روی نام یک تابع، مستقیماً به محل تعریف آن بپرسته یا تمام جاهایی که از آن استفاده شده را ببیند، بدون اینکه نیاز باشد کل فایلها را دستی زیر و رو کند.
چالش چهارم مربوط به حفظ تاریخچه و دسترسی به نسخههای مختلف مخزن repository است. در پروژههای بزرگ، گاهی لازم است بدانیم یک کد چرا و چه زمانی تغییر کرده یا حتی فایلهای حذفشده را بررسی کنیم. سیستم جستجوی گوگل تاریخچه تغییرات را به صورت خطی نمایه میکند تا مهندسان بتوانند در وضعیتهای مختلف کدهای گذشته جستجو کنند و بفهمند چه کسی و در چه تاریخی تغییرات را اعمال کرده است.
پیشنهاد من به عنوان میزبان این است که اگر روی پروژههای بزرگ کار میکنید، به جای اتکا به ابزارهای محلی و کند، روی ایجاد یک زیرساخت متمرکز برای جستجو و مرور کد سرمایهگذاری کنید. این کار نه تنها سرعت توسعه را بالا میبرد، بلکه مانع از دوبارهکاری و سرگردانی تیم در میان فایلهای پیچیده میشود.
امیدوارم این بخش از شوگفتار برایتان مفید بوده باشد و بتوانید با ابزارهای بهتری در کدهایتان جستجو کنید.
فصل شانزدهم: کنترل نسخه و مدیریت شاخهها در مهندسی نرمافزار
رایگانتوضیح و منابع این قسمت
تا حالا برایتان پیش آمده که هنگام کار روی یک پروژه، فایلهای مختلفی با نامهای نهایی و اصلاحی بسازید و آخرش هم ندانید کدام نسخه واقعاً بهروز است؟ وقتی بیش از یک نفر روی پروژهای کار میکنند که قرار است ماهها و سالها زنده بماند، کپی کردن فایلها دیگر جواب نمیدهد و به ابزاری جدی نیاز داریم.
این موضوع در کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ تیتوس وینترز، تام مانشریک و هایروم رایت بررسی شده و نشان میدهد که چطور انتخاب ابزار و سیاستهای کنترل نسخه میتواند سرنوشت یک سازمان بزرگ را تغییر دهد.
یکی از چالشهای اصلی، انتخاب بین سیستم کنترل نسخه متمرکز و سیستم کنترل نسخه توزیعشده است. در سیستمهای متمرکز مثل اسویان، یک مخزن مرکزی واحد وجود دارد که همه چیز آنجا ثبت میشود، اما در مدلهای توزیعشده مثل گیت، هر توسعهدهنده کل تاریخچه را روی سیستم خودش دارد. مشکل سیستمهای توزیعشده این است که اگر سیاست روشنی نداشته باشید، هر کس روی شاخهای جدا کار میکند و پیدا کردن نسخهٔ معتبر دشوار میشود. راهحل این است که حتی در سیستمهای توزیعشده، یک مخزن و شاخهٔ مشخص به عنوان منبع حقیقت در نظر گرفته شود تا همه بدانند کد اصلی کجاست.
مسئلهٔ مهم دیگر، تلهای به نام شاخههای توسعه dev branches است که خیلی از تیمها برای حفظ پایداری محصول به آن پناه میبرند. مشکل اینجاست که شاخههای طولانیمدت برای توسعه، در نهایت زمان ادغام شدن با شاخهٔ اصلی به مشکلی بزرگ و پرهزینه تبدیل میشوند که هماهنگیاش وقت تیم را میگیرد. نویسندگان کتاب معتقدند به جای استفاده از شاخههای طولانی، باید از روش توسعه روی شاخهٔ اصلی trunk-based development استفاده کرد و پایداری را با کمک تستهای دقیق و یکپارچهسازی پیوسته (Continuous Integration) به دست آورد. در این روش، تغییرات کوچک به صورت مداوم به شاخهٔ اصلی اضافه میشوند و کدهای ناقص در زمان اجرا غیرفعال میمانند.
بحث جذاب دیگر، قانون تکنسخهای One-Version Rule در گوگل است. در یک سازمان بزرگ، اگر تیمهای مختلف بتوانند نسخههای متفاوتی از یک کتابخانه یا وابستگی را استفاده کنند، پروژهها در زمان ترکیب شدن با خطاهای عجیبی روبهرو میشوند. گوگل با استفاده از مخزن یکپارچه (Monorepo) و ابزارهای داخلی خودش، این قانون را اجرا میکند که برای هر وابستگی، فقط و فقط یک نسخه وجود داشته باشد تا توسعهدهندگان درگیر انتخاب نسخه نشوند. البته اجرای این مدل در مقیاسهای بسیار بزرگ نیاز به زیرساختی دارد که بتواند دهها هزار مهندس و میلیونها خط کد را بدون کندی مدیریت کند.
پیشنهاد من به شما این است که اگر روی پروژهای تیمی کار میکنید که عمرش از چند روز بیشتر است، حتماً از یک سیستم کنترل نسخه استفاده کنید و از ساختن شاخههای موازی و طولانیمدت پرهیز کنید. اجازه ندهید انتخابهای متعدد دربارهٔ نسخههای مختلف کدهای شما را از مسیر اصلی منحرف کند.
امیدوارم این بخش از صحبتها توانسته باشد دید روشنتری نسبت به مدیریت کد و شاخهها در ابعاد بزرگ به شما بدهد. تا اپیزود بعدی، کدها و پروژههایتان پایدار و سرراست باد.
فصل پانزدهم: هنر منسوخ کردن و حذف سیستمهای قدیمی
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا نگهداری از یک نرمافزار قدیمی گاهی وقتها از نوشتن یک نرمافزار جدید هم سختتر میشود؟ سیستمهای قدیمی مثل ساختمانهای فرسودهای هستند که هرچه بیشتر میگذرد، تعمیر و سرپا نگه داشتنشان هزینهبرتر و پیچیدهتر میشود.
کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل پانزدهم به موضوع منسوخسازی (deprecation) میپردازد و نشان میدهد که چگونه باید با برنامهریزی درست، سیستمهای قدیمی را بازنشسته کرد تا پیچیدگی سیستم کاهش یابد.
اولین ایدهٔ کلیدی این است که اساساً کد دارایی نیست، بلکه یک تعهد و بدهی است. مشکل اینجاست که کدهای قدیمی هزینههای پنهانی دارند؛ از منابع مصرفی گرفته تا نیاز به بهروزرسانی مداوم همگام با تغییرات محیط اطراف. گزینهها این هستند که سیستم فرسوده را همین طور رها کنیم یا برای حذف آن زمان بگذاریم. مبادله این است که گرچه کار کردن روی حذف کد دشوار است، اما داشتن همزمان دو سیستم برای یک کار، مانع تکامل سیستم جدید میشود. به عنوان مثال، اگر دو سیستم برای یک کارکرد داشته باشیم، تیمها ناچارند سازگاری با سیستم قدیمی را حفظ کنند که این امر سرعت توسعه سیستم جدید را به شدت پایین میآورد.
ایدهٔ دوم این است که چرا منسوخسازی اصلاً کار سختی است. مشکل قانون هایروم (Hyrum’s Law) است که میگوید وقتی یک سیستم کاربران زیادی دارد، آنها ناخواسته به روشهایی از آن استفاده میکنند که اصلاً پیشبینی نشده است. گزینههای پیش رو یا دست نزدن به سیستم است یا حذف کامل آن. مبادله این است که حذف یک رفتار قدیمی، وابستگیهای پنهان را به هم میریزد. برای نمونه، در گوگل گاهی مهندسان به دلیل تعلق خاطر عاطفی در برابر حذف کدهایی که خودشان نوشتهاند مقاومت میکنند، در حالی که نگهداری کدهای منسوخ هزینهٔ خالص برای سازمان دارد.
ایدهٔ سوم تفاوت میان منسوخسازی توصیهای (advisory deprecation) و اجباری (compulsory deprecation) است. در روش توصیهای، به کاربران اطلاع داده میشود که سیستم جدیدی آمده است اما مهلتی تعیین نمیشود. مشکل این است که «امید، استراتژی نیست» و کاربران به ندرت داوطلبانه مهاجرت میکنند. گزینهها این است که یا به امید کاربران بمانیم یا مهلت مشخصی تعیین کنیم. مبادله این است که روش توصیهای ملایم است اما نتیجه نمیدهد، در حالی که روش اجباری نیازمند اختصاص تیم تخصصی برای کمک به مهاجرت کاربران است. به عنوان مثال، اگر بدون تخصیص نیرو به تیمها بگوییم باید سیستم خود را عوض کنند، فقط اصطکاک و نارضایتی ایجاد کردهایم.
ایدهٔ چهارم اهمیت اخطارها و ابزارهای مناسب است. مشکل این است که انباشت اخطارهای منسوخسازی باعث خستگی کاربران از هشدارها (alert fatigue) میشود؛ پدیدهای که در آن هشدارها آنقدر زیاد میشوند که افراد کلاً آنها را نادیده میگیرند. گزینهها شامل گذاشتن هشدار روی همه چیز یا استفاده هدفمند از تحلیل ایستا (static analysis) است. مبادله این است که ابزارهای خودکار باید هشدارها را دقیقاً در زمان مناسب و فقط به خطوط تازه تغییریافته اعمال کنند. به عنوان مثال، سامانهٔ Tricorder در گوگل هشدار استفاده از کدهای منسوخ را دقیقاً هنگام بازبینی کد نشان میدهد و راه جایگزین را پیشنهاد میکند.
پیشنهاد من به عنوان میزبان این است که وقتی تصمیم به جایگزینی ابزار یا کتابخانهای در تیم خود دارید، از همان ابتدا بازه زمانی مشخصی برای قطع پشتیبانی نسخهٔ قدیمی تعیین کنید و با ابزارهای خودکار، جلوی اضافه شدن استفادههای جدید از کد منسوخ را بگیرید تا کار روی زمین نماند.
امیدوارم این بخش از نگاه شما به چرخهٔ عمر نرمافزار روشنتر کرده باشد. تا اپیزود بعدی، کدهایتان کمخطا و سیستمهایتان همیشه روبهجلو باد.
فصل چهاردهم: تستهای بزرگتر در مهندسی نرمافزار
رایگانتوضیح و منابع این قسمت
تا حالا شده نرمافزاری بنویسید که تمام تستهای کوچک و واحدش سبز باشند، اما وقتی روی سرور واقعی یا محیط اصلی اجرا میشود، ناگهان از هم بپاشد؟ چرا این اندازه فاصله بین کدی که روی لپتاپ ما بینقص کار میکند و سیستمی که کاربران نهایی تجربه میکنند وجود دارد؟
این مبحث از کتاب مهندسی نرمافزار در گوگل (Software Engineering at Google) نوشتهٔ تیتوس وینترز، تام مانشریک و هایروم رایت به ما نشان میدهد که چطور تستهای بزرگتر خطرات پنهان سیستم را کاهش میدهند.
بیایید نگاهی به سناریوی تست واحد (Unit Test) بیندازیم. در پروژههای بزرگ، تستهای کوچک نمیتوانند همهچیز را پوشش دهند چون آنها در یک حباب ایزوله اجرا میشوند. یکی از اولین چالشها در این زمینه، مفهوم وفاداری (Fidelity) است. وفاداری یعنی میزان شباهت محیط تست به رفتار واقعی سیستم در دنیای واقعی. وقتی توسعهدهندگان از جایگزین تست test double استفاده میکنند تا وابستگیهای سنگین را شبیهسازی کنند، این خطر وجود دارد که رفتار این شبیهسازها با کد اصلی هماهنگ نباشد و قرارداد رفتاری بین ماژولها شکسته شود.
مسئلهٔ دوم به پیکربندیها مربوط میشود. بیشتر قطعیهای بزرگ در سیستمهای مقیاسپذیر به خاطر خطای کدنویسی نیست، بلکه به دلیل اشتباه در فایلهای تنظیمات یا ناسازگاری تنظیمات محیطی پیش میآید. تستهای کوچک نمیتوانند این سازگاری را بررسی کنند زیرا معمولاً فایلهای پیکربندی جدا از باینری برنامه نگهداری میشوند و سریعتر تغییر میکنند.
مشکل سوم به رفتارهای پیشبینینشده زیر بار ترافیک مربوط است. مقیاسپذیری (Scalability) و تستهای عملکردی به هزاران درخواست در ثانیه نیاز دارند. این حجم از ترافیک و رفتارهای نوظهور سیستم هنگام ترکیب سرویسها، در تستهای کوچک قابل مشاهده نیست. علاوه بر این، قانون هایروم (Hyrum's Law) یادآوری میکند که کاربران به تمام رفتارهای قابل مشاهده یک سیستم تکیه میکنند، نه فقط به مستندات رسمی آن. بنابراین تستهای واحد نمیتوانند همه این ابعاد را بسنجند.
چالش چهارم به نگهداری و ساختار تستهای بزرگ برمیگردد. برخلاف تستهای واحد که سریع و قابل اعتماد هستند، تستهای بزرگ ممکن است کند، غیرهرمسی و وابسطه به محیط باشند. بدون داشتن مالکیت مشخص برای هر تست و استانداردسازی زیرساخت، این تستها بهمرور فرسوده میشوند و توسعهدهندگان از اجرای آنها سر باز میزنند. گوگل برای حل این مشکل، رویکردهایی مثل رکورد و بازپخش ترافیک را به کار میگیرد تا تستهای بزرگ در پسزمینه اجرا شوند و لاگ ترافیک واقعی را برای تستهای کوچکتر فراهم کنند.
پیشنهاد من به شما این است که در پروژهٔ خودتان، هیچوقت به تستهای واحد اکتفا نکنید و از همان روزهای اول، چند تست یکپارچهسازی متصل به هم طراحی کنید تا رفتار واقعی سیستم حفظ شود.
امیدوارم این اپیزود از شبگفتار به شما کمک کرده باشد دید بهتری نسبت به تست نرمافزار پیدا کنید. تا برنامهای دیگر پیروز و سربلند باشید.
فصل سیزدهم: استفاده مؤثر از جایگزینهای تست در مهندسی نرمافزار
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چطور میتوان هزاران تست نرمافزاری را در چند دقیقه اجرا کرد، بدون اینکه درگیر خطاهای شبکه یا کندی پایگاهداده شد؟ وقتی کدهای ما به سرورهای خارجی یا پایگاههای داده وصل میشوند، نوشتن تستهای سریع و قابلاعتماد کار سادهای نیست و به ابزارهای خاصی نیاز پیدا میکنیم.
کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل سیزدهم به سراغ ابزارهایی میرود که نقش بدل را در فیلمها بازی میکنند و به ما نشان میدهد چطور از آنها برای سرعتبخشیدن به توسعه نرمافزار بهره ببریم.
نخستین ایده کلیدی در این زمینه، مفهوم جایگزین تست (test double) است. جایگزین تست شیء یا تابعی است که به جای پیادهسازی واقعی در تست قرار میگیرد تا وابستگیهای سنگین را شبیهسازی کند. مشکل اصلی این است که استفاده از پایگاهداده واقعی در تمام تستها کندی و خطاهای غیرقابلپیشبینی میآورد، اما گزینه جایگزین کمک میکند تست واحد (unit test) سریع و سبکی داشته باشیم که به شبکه وابستگی ندارد.
دومین ایده مهم، استفاده از نمونههای ساختگی یا فیک (fake) است. مشکل اینجاست که پایگاهداده واقعی برای تست مناسب نیست، و گزینهها شامل استفاده از کدهای پیچیده یا فیکهای درون حافظهای میشوند. مزیت فیک این است که مانند نسخه واقعی رفتار میکند اما سرعت بالایی دارد و معامله اصلی در اینجا هزینه بالای نوشتن و نگهداری فیک توسط تیم سازنده ایپیآی است. برای مثال، یک سامانه فایل سیستم درون حافظهای میتواند جایگزین دیسک سخت شود تا فایلها سریعتر خوانده و نوشته شوند بدون اینکه عملیات واقعی دیسک انجام گیرد.
ایده سوم مربوط به چالشهای استفاده از استابینگ (stubbing) است. مشکل زمانی رخ میدهد که تابع هیچ رفتار ذاتی ندارد و ما مجبوریم مقدار بازگشتی آن را با دست در تست تعیین کنیم. معضل بزرگ این است که افراط در این کار باعث شکننده شدن تستها و لو رفتن جزئیات پیادهسازی میشود. برای مثال، اگر در تست پرداخت بانکی، تمام پاسخهای سرور را به صورت دستی و با مقادیر ثابتی کدگذاری کنیم، با کوچکترین تغییر در کد اصلی، تست ما بیجهت شکست میخورد.
چهارمین ایده کلیدی، بررسی نحوه فراخوانی تابع یا تست تعامل (interaction testing) است. کتاب نشان میدهد که اگرچه این روش برای بررسی تعداد دفعات فراخوانی یک متد مفید است، اما افراط در آن تستها را به کدهای حساسی تبدیل میکند که با هر تغییر جزئی در ساختار داخلی، به خطا میافتند. در این روش، به جای بررسی مقدار خروجی، بررسی میکنیم که آیا متد خاصی مثلاً سه بار صدا زده شده است یا خیر، که این کار وابستگی شدیدی به جزئیات پیادهسازی ایجاد میکند.
پیشنهاد عملی من به شما این است که تا جای امکان از پیادهسازیهای واقعی استفاده کنید و سراغ پیچیدگیهای غیرضروری نروید. هر زمان که اجرای تستها به دلیل وابستگیهای خارجی کند شد، به جای وابستگی کورکورانه به فریمورکهای پیچیده، ساختار کدمان را طوری طراحی کنیم که استفاده از نمونههای ساختگی به سادگی میسر شود.
امیدوارم این روایت از نگاه گوگل به ابزارهای تست، دید تازهای برای بهبود کیفیت کدهایتان به شما داده باشد. تا روایتی دیگر، پیروز و پایدار باشید.
فصل دوازدهم: تستهای واحد پایدار و خوانا در مقیاس گوگل
رایگانتوضیح و منابع این قسمت
تا حالا برایتان پیش آمده که بخواهید یک تغییر خیلی کوچک در برنامهتان بدهید، اما سیستم خودکار تستها با کوهی از خطاهای عجیبوغریب مانع کارتان شود؟ چطور میشود مطمئن بود کدی که امروز مینویسیم فردا با یک تغییر ساختار بیمورد خراب نمیشود و توسعهدهندگان را کلافه نمیکند؟
فصل دوازدهم از کتاب «مهندسی نرمافزار در گوگل» اثر «تیتوس وینترز، تام مانشریک و هایروم رایت» به ما نشان میدهد که چطور تستهای پایدار و کارآمد بنویسیم که به جای اتلاف وقت، به بهرهوری مهندسان کمک کنند.
اولین چالش بزرگ در تستنویسی، تستهای شکننده هستند؛ یعنی تستهایی که با یک تغییر ساختاری بیخطر در کد اصلی که هیچ باگی هم ایجاد نکرده، از کار میافتند. راهحل این است که روی تست واحد (Unit Test) تمرکز کنیم که به تست کردن کوچکترین واحد رفتار کد گفته میشود و باید از طریق رابطهای عمومی (Public APIs) به جای جزئیات پیادهسازی با سیستم تعامل کند. فرض کنید در حال توسعه یک پردازشگر تراکنش هستید؛ اگر به جای دستکاری متدهای داخلی، دقیقا مثل یک کاربر واقعی از درگاه عمومی کلاس استفاده کنید، تغییرات ساختاری داخلی کد، تست شما را نمیشکند و تنها زمانی خطا میگیرید که قرارداد واقعی سیستم نقض شده باشد.
دومین ایدهٔ کلیدی این است که به جای تعاملات، وضعیت (State) سیستم را تست کنید. خیلی از برنامهنویسان با کمک ابزارهای ساخت جایگزین تست (Test Double) که نمونههای شبیهسازیشده برای جایگزینی اشیای واقعی در تست هستند، بررسی میکنند که سیستم چه توابع یا متدهایی را فراخوانی کرده است. این تستهای تعاملی به شدت شکننده هستند چون به نحوه رسیدن سیستم به نتیجه کار دارند، در حالی که تست وضعیت فقط بررسی میکند که نتیجه نهایی و وضعیت سیستم بعد از اجرا چه بوده است.
سومین اصل، تمرکز روی رفتارها به جای متدها است. اگر برای هر متد یک تست بنویسید، با بزرگ شدن متدها، تستها هم پیچیده و گیجکننده میشوند. رویکرد درست این است که هر تست را به یک رفتار مشخص اختصاص دهید؛ رفتارهایی که با ساختار مشخصِ آمادهسازی، اقدام و بررسی نتیجه نوشته میشوند. نام تستها هم باید گویای همین رفتار باشد تا هر زمان تستی شکست خورد، از روی نامش دقیقاً بفهمیم چه چیزی خراب شده است بدون اینکه مجبور باشیم کل کدهای تست را زیر و رو کنیم.
چهارمین نکته مهم این است که از قرار دادن منطق برنامهنویسی مثل حلقهها و شرطها درون کدهای تست خودداری کنید. کدهای تست باید کاملاً خطی، ساده و قابل بازرسی با یک نگاه باشند؛ اگر تست شما خودش نیاز به تست کردن داشته باشد، مسیر را اشتباه رفتهاید. پیامهای خطا هم باید کاملاً صریح باشند و مقادیر مورد انتظار و مقدار واقعی دریافتی را به روشنی نشان دهند تا نیازی به جستجوی دستی در فایلهای تست نداشته باشید.
در نهایت، در اشتراکگذاری کد بین تستها به جای اصل DRY که به پرهیز از تکرار کد اشاره دارد، باید به سمت اصل DAMP برویم که بر عبارات توصیفی و معنادار تمرکز دارد. یعنی کمی تکرار در کدهای تست ایرادی ندارد اگر باعث شود هر تست کامل، مستقل و کاملاً خوانا بماند و خواننده مجبور نباشد برای فهمیدن منطق تست، بین فایلهای مختلف جابهجا شود.
به عنوان پیشنهاد عملی، به شما پیشنهاد میکنم در پروژهٔ بعدیتان قبل از نوشتن تست برای متدهای داخلی، ابتدا رفتار سیستم را از زاویه دید مصرفکننده تعریف کنید و مطمئن شوید کدهای تست شما آنقدر صریح هستند که حتی یک سال بعد هم بدون ابهام خوانده شوند.
امیدوارم این مرور کوتاه به شما کمک کند تستهای پایدارتر و مفیدتری بنویسید و از توسعه نرمافزار لذت ببرید.
فصل یازدهم: تست نرمافزار در مقیاس گوگل
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا وقتی کدهای جدیدی به یک برنامه اضافه میکنید، مدام نگران هستید که مبادا بخش دیگری از سیستم از کار بیفتد؟ چطور ممکن است شرکتی با مقیاس گوگل هر روز تغییرات فراوانی را منتشر کند بدون اینکه کل سیستم فرو بریزد؟
این موضوع در کتاب «مهندسی نرمافزار در گوگل» اثر تیتوس وینترز، تام مانشریک و هایروم رایت بررسی شده است و فصل یازدهم آن نشان میدهد که چگونه تست خودکار، کلید اصلی ایجاد اعتماد به تغییرات در نرمافزارهای مدرن است.
در روزهای اولیه، پروژه سرور وب گوگل با مشکلات بزرگی روبهرو بود زیرا تیمها به هوش برنامهنویسان تکیه میکردند و تست خودکار نداشتند. راهحل این بود که سیاست تستهای خودکار توسعهدهندهمحور اجباری شود، که این کار توانست تعداد انتشارهای اضطراری را به شدت کاهش دهد. معاملهای که اینجا انجام میشود صرف زمان اولیه برای نوشتن تست در برابر خریدن آرامش خاطر و کاهش چشمگیر باگها در طول عمر نرمافزار است؛ مثلاً نوشتن یک تست واحد (Unit Test) برای بررسی یک تابع ریاضی ساده که از بروز خطاهای آتی جلوگیری میکند.
چالش دیگر این است که با رشد کد بیس، تستها ممکن است کند و غیرقابل اعتماد شوند. گزینههای پیش رو شامل نوشتن تستهای سیستممحور بزرگ یا تفکیک آنها بر اساس اندازه و دامنه است. نویسندگان توضیح میدهند که گوگل تستها را بر اساس میزان منابع مصرفی به سه دسته کوچک، متوسط و بزرگ دستهبندی میکند. به عنوان مثال، یک تست کوچک (Small Test) نباید به شبکه یا دیتابیس وصل شود تا بتواند با سرعتی نزدیک به توانایی پردازنده مرکزی اجرا شود.
مسئله دیگر پدید آمدن خطاهای ناپایدار در تستها است که تیمها را کلافه میکند. راه مقابله با این پدیده، ریشهیابی خطاهای تصادفی به جای نادیده گرفتن آنها است تا اعتماد مهندسان به زیرساخت حفظ شود. اگر نرخ این خطاهای تصادفی از حد مشخصی بالاتر برود، توسعهدهندگان دیگر به نتایج تستها اعتماد نخواهند کرد و سیستم ارزش خود را از دست خواهد داد.
پیچیدگی معماری و وابستگیهای زیاد همواره مانع نوشتن تستهای تمیز است. گزینههای موجود شامل استفاده از کدهای ماژولار یا پناه بردن به ابزارهای شبیهساز است. استفاده از جایگزین تست (Test Double) به مهندسان اجازه میدهد وابستگیهای سنگین مثل پایگاه داده را با نمونههای سبک درون حافظه جایگزین کنند تا تستها سریع و ایزوله باقی بمانند.
به عنوان پیشنهاد شخصی، من فکر میکنم شما میتوانید در پروژههای روزمره خود، قانون سادهای بگذارید: هیچ ویژگی جدیدی بدون نوشتن تست متناظر آن وارد کد اصلی نشود تا هر زمان که خواستید کدهایتان را بازفکتور کنید، با خیالی آسوده این کار را انجام دهید.
امیدوارم از این اپیزود لذت برده باشید و نگاه تازهای به اهمیت تستنویسی پیدا کرده باشید.
فصل دهم: مستندسازی در مهندسی نرمافزار
رایگانتوضیح و منابع این قسمت
تا حالا برایتان پیش آمده که روبهروی تکهای از کد بنشینید و از خودتان بپرسید این تابع دقیقا چه کار میکند یا چرا این تصمیم عجیب در طراحیاش گرفته شده است؟ کمبود یا کهنگی مستندات یکی از رایجترین گلایههای برنامهنویسان است، اما چه میشود اگر نگاه ما به نوشتن مستندات تغییر کند و آن را همردهٔ نوشتن خودِ کد بدانیم؟
در کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ تیتوس وینترز، تام مانشریک و هایروم رایت، فصل دهم به موضوع مستندسازی اختصاص دارد و بررسی میکند که چطور میتوان این بخش حیاتی را مقیاسپذیر کرد. مدیریت فرآیندها در مقیاس بزرگ برای تیمهای نرمافزاری اهمیت زیادی دارد، به طوری که مهندسی نرمافزار (Software Engineering) یعنی ساختن و نگهداری نرمافزار در طول زمان، نه فقط نوشتن کدهای اولیه.
یکی از مشکلات بزرگ تیمها این است که مستندات را جدا از چرخهٔ توسعه نگه میدارند. وقتی مستندات در یک سامانهٔ ویکی جداگانه رها شوند، به مرور منسوخ شده و گم میشوند. راه حل این است که مستندات را درست مانند کد، داخل کنترل نسخه قرار دهیم تا با تغییر هر بخش از کد، مستندات مربوط به آن هم بازبینی و بهروز شوند.
بسیاری از برنامهنویسان گمان میکنند مهارت نوشتن با برنامهنویسی کاملاً متفاوت است و آنها نویسندگان خوبی نیستند. اما حقیقت این است که نوشتن مستندات خوب نیاز به ادبیات پیچیده ندارد، بلکه نیازمند درک نیاز مخاطب است؛ دقیقاً مثل فرآیند بازبینی کد (Code Review) که در آن بررسی تغییرات کد توسط همکاران پیش از ادغام انجام میشود تا کیفیت کار بالا بماند.
مشکل دیگر این است که مستندات معمولاً تنها برای خودِ نویسنده یا بر اساس فرضهای ذهنی او نوشته میشوند. نویسنده ممکن است جزئیات فنی را از زاویهٔ دید خودش بدیهی بداند، در حالی که یک کاربر تازه یا فردی از تیمی دیگر کاملاً سردرگم شود. راه حل این است که مخاطب خود را بشناسیم و مستندات را کوتاهتر، شفافتر و متمرکز بر یک هدف مشخص نگه داریم.
برای نمونه، وقتی یک مستند مرجع مینویسیم، باید فرض کنیم مخاطب فردی است که میداند چه میخواهد و فقط به دنبال ساختار مشخصی میگردد. در مقابل، اگر در حال نوشتن یک آموزش یا راهنمای شروع به کار هستیم، باید گامها را صریح و به ترتیب شمارهگذاری کنیم تا خواننده در قدم چهارم دچار خطا نشود.
از نظر من، بهترین راه این است که نوشتن مستندات را نه یک بار اضافهی کاری، بلکه بخشی از وظیفهٔ روزمرهٔ خود بدانیم و برای کدهایی که قرار است بیش از چند ماه عمر کنند، وقت بگذاریم. مستندسازی خوب شاید در روزهای اول زمانبر باشد، اما در بلندمدت هزینههای نگهداری سیستم را به شدت کاهش میدهد.
پس دفعهٔ بعد که کدی را توسعه میدهید، پیش از بستن تغییرات، چند خطی هم برای آگاهی نفر بعدی که قرار است سراغ آن بیاید بنویسید. این کار کوچک، بخش بزرگی از اصطکاک تیم را از بین خواهد برد.
فصل نهم: بازبینی کد: دروازهبان کیفیت یا ابزار رشد؟
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که چرا وقتی چند نفر به یک تکه کد نگاه میکنند، نتیجه کار خیلی بهتر از زمانی میشود که آن را تنها و در سکوت نوشتهاید؟ چطور ممکن است شرکتی با دهها هزار مهندس، میلیاردها خط کد را در یک مخزن واحد مدیریت کند بدون اینکه سیستم زیر پایش خرد شود؟
این موضوعی است که در فصل نهم از کتاب «مهندسی نرمافزار در گوگل» نوشته تیتوس وینترز، تام مانشریک و هایروم رایت به آن پرداخته شده است و نشان میدهد چرا بازبینی کد فراتر از پیدا کردن ساده چند خط باگ است.
اولین مسئلهای که تیمها با آن روبهرو میشوند این است که اصلاً چرا باید هر تکه کدی پیش از ورود به مخزن اصلی بازبینی شود. گزینهها متنوعند؛ از اینکه هر کسی هر چه خواست را مستقیم ثبت کند تا اینکه گروهی معدود به عنوان دروازهبان همهچیز را کنترل کنند. نویسندگان نشان میدهند که اگر بررسی صحت عملکرد تنها هدف باشد، هزینه فرآیند از ارزش آن بیشتر میشود. معاوضه اصلی این است که سرعت اولیه فدای پایداری بلندمدت میشود. به عنوان مثال، در گوگل تقریباً هیچ تغییری بدون تایید ثبت نمیشود و هر مهندسی موظف است در این فرآیند شرکت کند تا همسویی و صحت کد تضمین شود.
دومین چالش، موضوع مالکیت و مقیاسپذیری است. وقتی تعداد افراد زیاد میشود، تعیین اینکه چه کسی مسئول کدام بخش از کد است سخت میشود. راه حل این است که سیستم بر اساس پوشههای ساختاری از فایلهای مالکان اداره شود. معاوضه در اینجا تمرکززدایی در برابر کنترل مرکزی است؛ یعنی هر تیمی مسئولیت دایرکتوری خودش را بر عهده میگیرد بدون اینکه نیازی به تایید مدیران ردهبالا باشد. برای نمونه، فایلهای مالکان در شاخههای مختلف دایرکتوری مشخص میکنند که چه کسانی حق تایید نهایی تغییرات را دارند.
مسئله سوم اهمیت خوانایی و یکدستی در کل پایگاه کد است. خطر اینجاست که هر توسعهدهندهای با سبک شخصی خودش کد بزند و بعد از مدتی هیچکس نتواند کدهای دیگران را بخواند. گزینه گوگل جدا کردن تاییدیه خوانایی (readability) از تاییدیه منطقی است. معاوضه این است که شاید نویسنده مجبور شود کدی را که از نظر منطقی درست است بازنویسی کند تا با استانداردها همخوانی داشته باشد. مثلاً برای نوشتن کدهای جاوا، حتماً باید تأییدیه فردی را بگیرید که دوره تخصصی خوانایی آن زبان را گذرانده است.
چهارمین ایده کلیدی، تمرکز روی تغییرات کوچک است. مشکل اینجاست که توسعهدهندگان دوست دارند پروژههای بزرگ را یکجا تمام کنند و بفرستند. معاوضه این است که خواندن و بررسی تغییرات بزرگ بسیار زمانبر و خطاپذیر است. روش پیشنهادی این است که تغییرات به قطعات کوچکتر از دویست خط تقسیم شوند. مثلاً وقتی تغییرات کوچک میفرستید، همکاران شما میتوانند در کمتر از بیست و چهار ساعت به شما بازخورد بدهند و فرآیند توسعه متوقف نشود.
پنجمین نکته، مزایای فرهنگی و اشتراک دانش در این فرآیند است. بدون یک سیستم مدون، بازبینی کد میتواند به یک جروکمرانی شخصی تبدیل شود. معاوضه اتکا به ابزار در برابر ایجاد تنش انسانی است. به عنوان مثال، ابزار بازبینی کد (Critique) مثل پلیس خوب عمل میکند و انتقادات را از حالت شخصی خارج کرده و به یک گفتگوی سازنده تبدیل میکند.
به عنوان پیشنهاد عملی، به نظرم میرسد بهتر است در تیم خودتان عادت کنید کدهای خود را پیش از ارسال خرد کنید و از همکارانتان بخواهید ابتدا به جای جنگیدن بر سر سلیقه، روی خوانایی و درک منطق تمرکز کنند.
بازبینی کد در نهایت تنها یک مانع در مسیر ثبت کد نیست، بلکه چسبی است که مهندسان را به هم وصل میکند و دانش فنی را در سراسر سازمان جریان میدهد.
فصل هشتم: راهنمای سبکها و قوانین کدنویسی در گوگل
رایگانتوضیح و منابع این قسمت
تا حالا برایتان پیش آمده که هنگام خواندن کدهای نوشته شده توسط همکارانتان، احساس کنید هر کس با زبان خودش حرف میزند و سر درآوردن از منطق برنامهها وقت زیادی از شما بگیرد؟ در یک سازمان بزرگ با هزاران برنامهنویس، بدون داشتن قوانین مشخص، پروژه به سرعت تبدیل به کلافی سردرگم میشود که نگهداریاش غیرممکن است. کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ «تیتوس وینترز، تام مانشریک و هایروم رایت» در فصل هشتم به سراغ همین موضوع حیاتی میرود و بررسی میکند که چگونه راهنماهای سبک و قوانین مدون میتوانند نظم و پایداری را به مخازن عظیم کد هدیه دهند.
مسئلهٔ اصلی در سازمانهای نرمافزاری این است که رفتار خوب و بد برنامهنویسان تعریفنشده باقی میماند و با رشد تیم، سلیقههای شخصی جایگزین انسجام میشود. گزینهها از آزادی کامل توسعهدهندگان در انتخاب الگوها تا داشتن مجموعهای از قوانین خشک متغیر است. نویسندگان کتاب اشاره میکنند که قوانین گوگل قوانین سختگیرانهای هستند که باید رعایت شوند، نه صرفاً پیشنهادهایی دوستانه. به عنوان نمونه، در زبان سیپلاسپلاس، گوگل استفاده از استثناها را ممنوع کرده است؛ تصمیمی که شاید در بیرون از گوگل عجیب به نظر برسد اما برای حفظ سادگی و کنترل جریان خطا در یک کدبانک چند میلیاردی ضروری است.
یکی از چالشهای بزرگ طراحی قوانین، حفظ تعادل بین محدودیت و کارایی است و قانونگذاران باید مطمئن شوند که هر قاعده واقعاً به هدف نهایی کمک میکند. انتخاب اشتباه در اینجا یعنی تحمیل بار ذهنی اضافهکاری به توسعهدهندگان بدون اینکه ارزش واقعی خلق شود. برای مثال، راهنمای سبک پایتون در گوگل توسعهدهندگان را از استفاده از ویژگیهای بسیار پویا مانند بازتاب reflection منع میکند، زیرا این قابلیتها خواندن کد را مبهم و ردیابی خطاها را برای مهندسان دیگر به شدت دشوار میسازند.
یکی دیگر از اصول کلیدی این است که قوانین باید خوانندهٔ کد را بر نویسندهٔ آن ترجیح دهند زیرا کد در طول عمر خود بسیار بیشتر از آنکه نوشته شود، خوانده میشود. راهحل گوگل در این زمینه الزام به درج شواهد صریح از رفتار مورد انتظار در کد است. مثلاً در سیپلاسپلاس، استفاده از ابزار مدیریت مالکیت اشارهگرها یعنی std::unique_ptr به جای اشارهگرهای خام الزامی شده است تا هر زمان که تابعی مالکیت دادهای را میگیرد، خوانندهٔ کد بلافاصله و بدون نیاز به بررسی پیادهسازی داخلی تابع، از انتقال مالکیت باخبر شود.
حفظ سازگاری درونسازمانی مزیتی است که به مهندسان اجازه میدهد بدون اتلاف وقت وارد بخشهای ناآشنای کد شوند و کار را جلو ببرند. گزینههای پیش رو شامل پذیرش استانداردهای بیرون از سازمان یا ساخت استانداردهای داخلی است و معاوضهٔ اصلی در اینجا از دست دادن مقداری از آزادی خلاقانه در ازای کسب بهرهوری تیمی است. به عنوان یک مثال ملموس، اگرچه پایتون در جامعهٔ جهانی از تورفتگی چهاراسپیسی استفاده میکند، گوگل مدتها به دلیل همسویی با کدهای سیپلاسپلاس خود از تورفتگی دواسپیسی استفاده میکرد اما بعدها با بزرگتر شدن پروژهها و تعامل با دنیای خارج، این قانون را اصلاح کرد تا با استانداردهای جامعهٔ پایتون هماهنگ شود.
نکتهٔ قابل توجه این است که نوشتن قوانین تنها نیمی از مسیر است و اعمال دستی آنها در مقیاس وسیع دوام نمیآورد؛ به همین دلیل بخش بزرگی از فرآیندها به کمک ابزارهای خودکار پیش میرود. در این راستا، استفاده از تحلیل ایستا (Static Analysis) یعنی بررسی خودکار کد بدون اجرای آن برای پیدا کردن خطاهای الگو و ساختار بسیار رایج است. ابزارهایی مانند clang-tidy برای زبان سیپلاسپلاس یا فرمتکنندههای خودکار کد کمک میکنند تا بدون نیاز به بحثهای فرساینده در بازبینی کد (Code Review) یعنی بررسی تغییرات کد توسط همکاران پیش از ادغام، تمام فایلها به یک شکل استاندارد و یکدست فرمت شوند.
پیشنهاد من به شما این است که در تیمهای خود به جای تحمیل قوانین سلیقهای و پیچیده، روی چند اصل روشن مانند خوانایی کد، به حداقل رساندن غافلگیری برای خوانندهٔ آینده و اتوماسیون فرمتدهی تمرکز کنید تا انرژی تیم صرف حل مسئله شود نه بحث دربارهٔ فاصله گذاریها. قوانین خوب نباید دستوپاگیر باشند، بلکه باید به عنوان ریلگذاریهایی مطمئن به تیم کمک کنند تا با خیال راحت در مسیر رشد و مقیاسپذیری حرکت کند.
فصل هفتم: سنجش بهرهوری مهندسی
رایگانتوضیح و منابع این قسمت
تا حالا فکر کردید وقتی یک شرکت بزرگ فناوری مثل گوگل توسعه پیدا میکند، چطور میفهمد که برنامهنویسهایش واقعاً دارند سریعتر و بهتر کار میکنند؟ آیا اضافه کردن افراد بیشتر به تیمها همیشه به معنی پیشرفت سریعتر است یا هزینههای پنهان دیگری دارد؟
این مباحث در فصل هفتم از کتاب «مهندسی نرمافزار در گوگل» نوشتهٔ تیتوس وینترز، تام مانشریک و هایروم رایت بررسی شده است و به ما نشان میدهد چطور میتوان بهرهوری نیروی انسانی را با دادههای دقیق ارزیابی کرد.
اولین ایده این است که وقتی سازمانها بزرگ میشوند، هزینههای ارتباطی به صورت غیرخطی رشد میکنند و اضافه کردن نیروی انسانی جدید، لزوماً به همان نسبت کارایی را بالا نمیبرد. برای حل این مشکل، سازمان باید روی افزایش بهرهوری تکتک مهندسان تمرکز کند تا بتواند بدون بزرگ کردن بیش از حد سازمان، مقیاسپذیری (Scalability) را حفظ کند؛ یعنی توانایی سیستم و سازمان در رشد کردن بدون افت کیفیت عملکردی.
ایدهٔ دوم این است که پیش از شروع هر سنجشی، باید با یک فرآیند غربالگری مطمئن شویم که اندازه گیری واقعاً ارزش هزینه کردن وقت و انرژی را دارد. اگر نتایج مثبت یا منفی قرار نباشد تغییری در تصمیمگیریهای مدیریتی ایجاد کند، جمعآوری دادهها تنها اتلاف وقت است و هیچ کمکی به بهبود مهندسی نرمافزار (Software Engineering) نمیکند، که به معنای مهندسی ساختن و نگهداری نرمافزار در طول زمان است.
ایدهٔ سوم استفاده از چارچوب هدف، سیگنال و متریک جیاسام است که جلوی گمراه شدن تیمها را میگیرد. برای نمونه، هدف یک نتیجهٔ مطلوب کلی است، سیگنال نشان میدهد که چطور میفهمیم به آن هدف رسیدهایم و متریک شاخص قابل اندازهگیری دقیقی است که به عنوان نمایندهٔ آن سیگنال عمل میکند.
ایدهٔ چهارم در نظر گرفتن تمام ابعاد پنجگانهٔ بهرهوری با کمک کلمهٔ کلیدی کَنتس است که شامل کیفیت کد، توجه و تمرکز مهندسان، پیچیدگی ذهنی، سرعت و ریتم کاری، و رضایت شغلی میشود. برای نمونه، اگر تیمها فقط روی سرعت کار تمرکز کنند و کیفیت یا رضایت را نبینند، در نهایت با افت شدید کیفیت روبهرو خواهند شد.
ایدهٔ پنجم ترکیب دادههای کمی و کیفی است که کمک میکند خطاهای ابزارهای خودکار شناسایی شوند. مثلاً در گوگل مشخص شد که خیلی از زمانهای ثبتشده برای ساخت پروژه مربوط به ابزارهای خودکار بوده و مهندس اصلاً منتظر آنها نبوده است، موضوعی که با استفاده از بازبینی کد (Code Review) بررسی تغییرات کد توسط همکاران پیش از ادغام، به درک دقیقتر رفتارهای انسانی کمک میکند.
پیشنهاد من به عنوان میزبان این است که وقتی خواستید کارایی تیم فنی خود را بسنجید، هرگز از متریکها برای ارزیابی تکتک افراد استفاده نکنید زیرا این کار فقط باعث دستکاری دادهها میشود، بلکه همیشه تأثیر تجمعی ابزارها و فرآیندها بر کل تیم را بررسی کنید.
امیدوارم این روایت از تجربهٔ گوگل به شما کمک کرده باشد نگاه دقیقتری به سنجش بهرهوری در تیمهای خود داشته باشید.
فصل ششم: رهبری در مقیاس بزرگ در مهندسی نرمافزار
رایگانتوضیح و منابع این قسمت
تا حالا به این فکر کردهاید که وقتی یک تیم کوچک نرمافزاری بزرگ میشود و به چندین تیم تبدیل میشود، دقیقاً چه اتفاقی برای وظایف مدیر آن میافتد؟ آیا قرار است او فقط کدهای بیشتری را بررسی کند یا ماجرا چیز دیگری است؟
کتاب مهندسی نرمافزار در گوگل نوشته تیتوس وینترز، تام مانشریک و هایروم رایت در فصل ششم به ما نشان میدهد که چطور باید از رهبری یک تیم به رهبری سازمانهای بزرگتر گذار کنیم و در مقیاس بالا مؤثر بمانیم.
اولین چالش در مقیاسهای بزرگ، روبرو شدن با مسائل مبهمی است که پاسخ مشخصی ندارند و مدیر باید همیشه در حال تصمیمگیری Always Be Deciding باشد. مشکل اینجاست که افراد قدیمیتر درگیر پیشفرضها و اصطلاحاً کورکنندهها blinders میشوند؛ یعنی فرضهایی که دیگر کسی به آنها شک نمیکند. در گوگل برای سالها، تیمهای جستجوی وب تلاش میکردند کیفیت نتایج را بالا ببرند اما این کار ناخواسته باعث کندتر شدن سایت و افزایش تاخیر latency میشد که نشاندهنده زمان انتظار کاربر برای دریافت پاسخ است. راه حل این بود که مدیران به جای جستجوی پاسخهای مطلق، تعادل میان کیفیت، سرعت و ظرفیت سرور را شناسایی کنند و ماه به ماه تصمیمات خود را بازنگری کنند.
چالش دوم این است که یک رهبر خوب نباید همیشه درگیر جزئیات بماند، بلکه باید همیشه در حال ترک کردن Always Be Leaving باشد. مقصود این است که سازمان را به گونهای بسازید که خودش مشکلات را حل کند و وابسته به شما نباشد تا به نقطه شکست منفرد Single Point of Failure (SPOF) تبدیل نشوید؛ اصطلاحی که به بخشی از سیستم اشاره دارد که با از کار افتادن آن کل مجموعه متوقف میشود. برای نمونه، به جای اینکه خودتان تکتک مشکلات فنی ریز را حل کنید، باید با تفویض اختیار دقیق، تیمها را خودگردان بار بیاورید تا بتوانید روی استراتژیهای کلان متمرکز شوید.
چالش سوم مدیریت انرژی و زمان در مواجهه با کارهای فوری در برابر کارهای مهم است. اگر حواستان نباشد، کارهای فوری تمام وقت شما را میبلعند در حالی که کارهای استراتژیک و مهم روی زمین میمانند. تفکیک کارها به بالاترین سطح اهمیت و رها کردن بخش زیادی از امور کماهمیتتر، ابزاری است که به مدیر کمک میکند تا زیر بار مسئولیتهای رو به رشد غرق نشود.
چالش چهارم حفظ انرژی شخصی در این مسیر فرساینده است. مدیریت در مقیاس بزرگ انرژی زیادی میبرد و مدیران موفق یاد میگیرند که با مرخصیهای واقعی، جدا کردن ابزارهای کاری از گوشی شخصی و حتی اعلام روز سلامت روان، انرژی خود را بازیابی کنند تا بتوانند با دید بازتری به هدایت سازمان ادامه دهند.
پیشنهاد من به شما این است که در کارهای روزمره از خود بپرسید: چه کاری در این سازمان وجود دارد که واقعاً فقط از عهده شخص من ساخته است؟ تمرکز روی همین یک سوال ساده میتواند شما را از افتادن در دام ریزمدیریت نجات دهد.
امیدوارم این روایت از کتاب مهندسی نرمافزار در گوگل برایتان مفید بوده باشد و بتوانید اصول رهبری مقیاسپذیر را در تیمهای خود به کار بگیرید.