حالت تاریک
فصل ۳ — مبانی رویکرد مهندسی
مهندسی در رشتههای مختلف متفاوت است. ساخت پل مثل مهندسی هوافضا نیست و هیچکدام مثل مهندسی برق یا شیمی نیست، اما همهٔ این رشتهها ایدههای مشترکی دارند. همه محکم بر عقلانیت علمی استوارند و رویکردی pragmatیک و تجربی برای پیشرفت میگیرند.
اگر میخواهیم به هدف تعریف مجموعهای از اندیشهها، ایدهها، شیوهها و رفتارهای ماندگار که جمعاً زیر نام مهندسی نرمافزار گرد بیاوریم برسیم، این ایدهها باید fairly بنیادین برای واقعیت توسعهٔ نرمافزار و در برابر تغییر robust باشند.
صنعتی از تغییر؟
در صنعتمان زیاد دربارهٔ تغییر صحبت میکنیم. از فناوریها و محصولات تازه هیجانزده میشویم، اما آیا این تغییرات واقعاً «عقربه را حرکت میدهند» در توسعهٔ نرمافزار؟ به نظر میرسد بسیاری از تغییراتی که ما را بهتپش میاندازند بهاندازهٔ آنچه گاه فکر میکنیم تفاوت ایجاد نمیکنند.
مثال موردعلاقهٔ من در ارائهٔ کنفرانسی زیبا توسط Christin Gorman[^1] نشان داده شد. Gorman نشان میدهد وقتی از کتابخانهٔ open source محبوب آن زمان Hibernate برای object relational mapping استفاده میکردید، در واقع کد بیشتری نسبت به رفتار معادل نوشتهشده در SQL لازم بود — حداقل ذهنی؛ SQL هم قابل فهمتر بود. Gorman بهشکل خندهدار توسعهٔ نرمافزار را با پخت کیک مقایسه میکند: با مخلوط آماده کیک میپزید یا مواد تازه انتخاب میکنید و از صفر میسازید؟
بسیاری از تغییرات در صنعتمان زودگذرند و چیزها را بهتر نمیکنند. بعضی — مثل مثال Hibernate — واقعاً بدتر میکنند.
برداشت من این است که صنعتمان در یادگیری و پیشرفت مشکل دارد. این کمبود پیشرفت نسبی با پیشرفت باورنکردنی سختافزاری که کدمان روی آن اجرا میشود پوشانده شده است.
منظورم این نیست که در نرمافزار پیشرفتی نبوده — برعکس — اما باور دارم سرعت پیشرفت بسیار کندتر از آن است که بسیاری از ما فکر میکنیم. لحظهای فکر کنید چه تغییراتی در حرفهتان اثر قابلتوجهی روی نحوهٔ تفکر و عمل شما در توسعهٔ نرمافزار داشتهاند. چه ایدههایی روی کیفیت، مقیاس یا پیچیدگی مسائلی که میتوانید حل کنید تفاوت گذاشتند؟
فهرست کوتاهتر از آن است که معمولاً فرض میکنیم.
مثلاً در حرفهام حدود ۱۵ یا ۲۰ زبان برنامهنویسی مختلف به کار بردهام. اگرچه ترجیح دارم، فقط دو تغییر زبان بهطور بنیادین نحوهٔ تفکر و طراحیام را عوض کرد.
آن گامها: گام از Assembler به C و گام از برنامهنویسی رویهای به OO. زبانهای فردی برایم کمتر از paradigm برنامهنویسی مهماند. آن گامها تغییر قابلتوجهی در سطح انتزاعی بود که میتوانستم در کد با آن کار کنم. هر کدام گامی در پیچیدگی سیستمهایی بود که میتوانستیم بسازیم.
وقتی Fred Brooks نوشت بهبود یک مرتبهای نیست، چیزی را از قلم انداخت. شاید بهبود ۱۰ برابری نباشد، اما قطعاً زیان ۱۰ برابری هست.
سازمانهایی دیدهام که رویکردشان به توسعهٔ نرمافزار — گاه با فناوری، اغلب با فرایند — آنها را فلج کرده بود. یک بار در سازمان بزرگی مشاوره کردم که بیش از پنج سال هیچ نرمافزاری به production منتشر نکرده بود.
نهتنها یادگیری ایدههای تازه برایمان سخت است؛ تقریباً غیرممکن است ایدههای قدیمی را — هرچقدر هم رد شده — کنار بگذاریم.
اهمیت اندازهگیری
یکی از دلایلی که دور انداختن ایدههای بد برایمان سخت است این است که عملکردمان در توسعهٔ نرمافزار را خیلی مؤثر اندازه نمیگیریم.
بیشتر معیارهای اعمالشده به توسعهٔ نرمافزار یا بیربطاند (velocity) یا گاه آسیبزنندهاند (lines of code یا test coverage).
در حلقههای agile توسعه مدتهاست باور رایجی این است که اندازهگیری عملکرد تیم یا پروژهٔ نرمافزار ممکن نیست. Martin Fowler در ۲۰۰۳ در Bliki پرخواندهاش دربارهٔ یک جنبهٔ این موضوع نوشت.[^2]
نکتهٔ Fowler درست است: معیار دفاعپذیری برای بهرهوری نداریم، اما این همان «نمیتوانیم چیز مفیدی اندازه بگیریم» نیست.
کار ارزشمند Nicole Fosgren، Jez Humble و Gene Kim در گزارشهای «State of DevOps»[^3] و کتاب Accelerate: The Science of Lean Software & DevOps[^4] گام مهمی به سمت تصمیمگیری قویتر و مبتنی بر شواهد است. مدل جالب و قانعکنندهای برای اندازهگیری مفید عملکرد تیمهای نرمافزار ارائه میدهند.
جالب است که سعی نمیکنند بهرهوری را اندازه بگیرند؛ بلکه اثربخشی تیمهای توسعهٔ نرمافزار را بر دو ویژگی کلیدی میسنجند. معیارها بخشی از مدل پیشبینیکنندهاند. نمیتوانند ثابت کنند این معیارها رابطهٔ علّی با عملکرد تیمها دارند، اما میتوانند همبستگی آماری نشان دهند.
معیارها پایداری (stability) و توان عبور (throughput)اند. تیمهای با پایداری و throughput بالا «عملکرد بالا» (high performers) و تیمهای با امتیاز پایین «عملکرد پایین» (low performers) طبقهبندی میشوند.
جالب این است که اگر فعالیتهای این گروههای عملکرد بالا و پایین را تحلیل کنید، بهطور مداوم همبستهاند. بههمین شکل اگر فعالیتها و رفتارهای تیمی را ببینیم، میتوانیم امتیازشان را در این معیارها پیشبینی کنیم — و آن هم همبسته است. بعضی فعالیتها برای پیشبینی عملکرد در این مقیاس قابل استفادهاند.
مثلاً اگر تیم شما test automation، trunk-based development، deployment automation و حدود ده شیوهٔ دیگر را به کار ببرد، مدل پیشبینی میکند continuous delivery را عمل میکنید. اگر continuous delivery را عمل کنید، مدل پیشبینی میکند در عملکرد تحویل نرمافزار و عملکرد سازمانی «عملکرد بالا» خواهید بود.
یا اگر سازمانهایی را که عملکرد بالا دیده میشوند ببینیم، رفتارهای مشترکی مثل continuous delivery و سازماندهی در تیمهای کوچک دارند.
معیارهای stability و throughput پس مدلی به ما میدهند که بتوانیم نتایج تیم را پیشبینی کنیم.
هر کدام با دو معیار ردیابی میشوند.
پایداری با موارد زیر:
- نرخ شکست تغییر (Change Failure Rate): نرخی که یک تغییر در نقطهٔ مشخصی از فرایند نقص معرفی میکند
- زمان بازیابی از شکست (Recovery Failure Time): چقدر طول میکشد از یک شکست در نقطهٔ مشخص فرایند بازیابی شود
اندازهگیری stability مهم است چون واقعاً معیار کیفیت کار انجامشده است. نمیگوید تیم چیز درست میسازد یا نه، اما میسنجد اثربخیشان در تحویل نرمافزار با کیفیت قابل اندازهگیری.
توان عبور با موارد زیر:
- زمان Lead (Lead Time): معیار کارایی فرایند توسعه. چقدر طول میکشد یک تغییر تکخطی از «ایده» به «نرمافزار کارکن»؟
- فراوانی (Frequency): معیار سرعت. چند وقت یکبار تغییرات به production مستقر میشوند؟
Throughput معیار کارایی تیم در تحویل ایدهها به شکل نرمافزار کارکن است. چقدر طول میکشد تغییر به دست کاربران برسد و چند وقت یکبار این اتفاق میافتد؟ این — از جمله چیزهای دیگر — نشانهای از فرصتهای یادگیری تیم است. تیم شاید از آنها استفاده نکند، اما بدون امتیاز خوب در throughput، شانس یادگیری هر تیمی کم میشود.
اینها معیارهای فنی رویکرد توسعهٔ ما هستند. به سؤالهای «کیفیت کار ما چقدر است؟» و «با چه کارایی میتوانیم کار با آن کیفیت تولید کنیم؟» پاسخ میدهند.
ایدههای معنادارند، اما شکافهایی دارند. نمیگویند چیز درست میسازیم یا نه — فقط آیا درست میسازیم — اما فقط بهخاطر کامل نبودن، سودمندیشان کم نمیشود.
جالب است مدل همبستگی که توصیف کردم فراتر از پیشبینی اندازه تیم یا continuous delivery میرود. نویسندگان Accelerate داده دارند که همبستگیهای قابلتوجهی با چیزهای مهمتر نشان میدهد.
مثلاً سازمانهایی از تیمهای عملکرد بالا — بر اساس این مدل — بیشتر از سازمانهایی که نیستند درآمد دارند. داده میگوید همبستگی میان رویکرد توسعه و نتیجهٔ تجاری شرکتی که آن را عمل میکند وجود دارد.
همچنین باور رایجی را که «یا سرعت دارید یا کیفیت، نه هر دو» رد میکند. این سادهسازی درست نیست. سرعت و کیفیت در دادهٔ این پژوهش clearly همبستهاند.
راه به سرعت، نرمافزار باکیفیت است؛ راه به نرمافزار باکیفیت، سرعت بازخورد است؛ و راه به هر دو، engineering عالی است.
بهکارگیری پایداری و توان عبور
همبستگی امتیازهای خوب در این معیارها با نتایج باکیفیت مهم است. فرصتی به ما میدهد از آنها برای ارزیابی تغییرات در فرایند، سازمان، فرهنگ یا فناوری استفاده کنیم.
تصور کنید نگران کیفیت نرمافزارمان هستیم. چگونه بهبود دهیم؟ میتوانیم تصمیم بگیریم فرایند را عوض کنیم. مثلاً هیئت تأیید تغییر (Change Approval Board یا CAB) اضافه کنیم.
واضح است افزودن بررسی و sign-off اضافی روی throughput اثر منفی میگذارد و فرایند را حتماً کند میکند. اما آیا stability را بالا میبرد؟
برای این مثال خاص داده هست. شاید غافلگیرکننده باشد: CABها stability را بهبود نمیدهند. اما کند شدن فرایند خودش stability را بدتر میکند.
یافتیم تأییدهای خارجی با lead-time، deployment frequency و restore-time همبستگی منفی داشتند و با change fail rate همبستگی نداشتند. بهاختصار، تأیید توسط نهاد خارجی (مثل مدیر یا CAB) برای افزایش پایداری سیستمهای production — اندازهگیریشده با زمان بازیابی سرویس و نرخ شکست تغییر — کار نمیکند. اما قطعاً چیزها را کند میکند. در واقع از نداشتن فرایند تأیید تغییر هم بدتر است.[^5]
نکتهٔ واقعی من مسخره کردن CABها نیست؛ بلکه نشان دادن اهمیت تصمیمگیری بر اساس شواهد بهجای حدس است.
واضح نیست CAB ایدهٔ بدی باشد. منطقی به نظر میرسد و در واقعیت احتمالاً بیشتر سازمانها کیفیت را اینطور مدیریت میکنند. مشکل این است که کار نمیکند.
بدون اندازهگیری مؤثر نمیتوانیم بفهمیم کار نمیکند؛ فقط حدس میزنیم.
اگر میخواهیم رویکرد عقلانیتر و مبتنی بر شواهد به تصمیمگیری بیاوریم، نباید حرف من یا Forsgren و همنویسندگانش را — در این یا هر موضوع دیگر — بپذیرید.
بهجای آن میتوانید این اندازهگیری را خودتان در تیمتان انجام دهید. throughput و stability رویکرد فعلیتان — هرچه باشد — را بسنجید. تغییری ایجاد کنید — هرچه باشد. آیا تغییر عقربهٔ هر کدام از این معیارها را حرکت میدهد؟
دربارهٔ این مدل همبستگی در کتاب عالی Accelerate بیشتر بخوانید. رویکرد اندازهگیری و مدلی را که با ادامهٔ پژوهش تکامل مییابد توصیف میکند. نکتهٔ من تکرار آن ایدهها نیست؛ بلکه اثر مهم — شاید عمیق —ی است که این باید روی صنعتمان بگذارد. بالاخره خطکش مفیدی داریم.
میتوانیم از این مدل stability و throughput برای سنجش اثر هر تغییری استفاده کنیم.
میتوانیم اثر تغییرات در سازمان، فرایند، فرهنگ و فناوری را ببینیم. «اگر این زبان تازه را بپذیرم، throughput یا stability من بالا میرود؟»
همچنین میتوانیم بخشهای مختلف فرایند را ارزیابی کنیم. «اگر مقدار زیادی آزمون دستی دارم، قطعاً از آزمون خودکار کندتر است، اما آیا stability را بهبود میدهد؟»
هنوز باید با دقت فکر کنیم. معنای نتایج را در نظر بگیریم. اگر چیزی throughput را کم کند اما stability را بالا ببرد چه معنایی دارد؟
با این حال، داشتن معیارهای معنادار برای ارزیابی اقدامات — حتی حیاتی — برای رویکرد مبتنی بر شواهد به تصمیمگیری است.
بنیادهای رشتهٔ مهندسی نرمافزار
پس بعضی از این ایدههای بنیادین چیست؟ ایدههایی که انتظار داریم ۱۰۰ سال دیگر هم درست باشند و هر مسئله و هر فناوری کاربرد داشته باشند؟
دو دسته داریم: فرایند — یا شاید حتی رویکرد فلسفی — و فن یا طراحی. سادهتر: رشتهٔ ما باید روی دو شایستگیٔ هستهای تمرکز کند.
باید در یادگیری متخصص شویم. باید بپذیریم رشتهٔ ما رشتهٔ طراحی خلاقانه است و ارتباط معناداری با مهندسی تولید ندارد؛ و بهجای آن بر تسلط بر مهارتهای کاوش، کشف و یادگیری تمرکز کنیم. این کاربرد عملی استدلال به سبک علمی است.
همچنین باید بر بهبود مهارتهای مدیریت پیچیدگی تمرکز کنیم. سیستمهایی میسازیم که در سر جا نمیگنجند. در مقیاس بزرگ با گروههای بزرگ مردم روی آنها کار میکنند. باید در مدیریت پیچیدگی — هم در سطح فنی و هم سازمانی — متخصص شویم.
متخصصان یادگیری
علم بهترین فن حل مسئلهٔ بشریت است. اگر میخواهیم در یادگیری متخصص شویم، باید نوع رویکرد علمی-عملی حل مسئله — که جوهرهٔ رشتههای مهندسی دیگر است — را بپذیریم و در آن ماهر شویم.
باید متناسب با مسائل ما باشد. مهندسی نرمافزار — مثل تفاوت مهندسی هوافضا و شیمی — مختص نرمافزار متفاوت خواهد بود. باید عملی، سبک و در همهٔ رویکردمان به حل مسائل نرمافزاری رایج باشد.
توافق قابلتوجهی میان کسانی که بسیاری از ما رهبران فکری صنعت میدانیم در این موضوع وجود دارد. با وجود شهرت، این ایدهها اکنون بهطور جهانی یا حتی گسترده بهعنوان بنیاد نحوهٔ برخوردمان به بخش زیادی از توسعهٔ نرمافزار عمل نمیشوند.
پنج رفتار مرتبط در این دسته وجود دارد:
- کار تکراری (Working iteratively)
- بازخورد سریع و باکیفیت (Employing fast, high-quality feedback)
- کار افزایشی (Working incrementally)
- آزمایشگری (Being experimental)
- تجربهگرایی (Being empirical)
اگر قبلاً دربارهٔ این فکر نکردهاید، این پنج شیوه ممکن است انتزاعی و نسبتاً جدا از فعالیتهای روزمرهٔ توسعهٔ نرمافزار — چه برسد به مهندسی نرمافزار — به نظر برسند.
توسعهٔ نرمافزار تمرینی در کاوش و کشف است. همیشه میخواهیم بیشتر بدانیم مشتریان یا کاربران از سیستم چه میخواهند، چگونه مسائل پیشرو را بهتر حل کنیم و چگونه ابزارها و فنون در اختیارمان را بهتر به کار ببریم.
میآموزیم چیزی را از قلم انداختهایم و باید درستش کنیم. میآموزیم چگونه خودمان را برای کار بهتر سازمان دهیم و عمیقتر مسائلی که روی آنها کار میکنیم را بفهمیم.
یادگیری در قلب همهٔ کارهایمان است. این شیوهها بنیاد هر رویکرد مؤثر به توسعهٔ نرمافزاراند، اما بعضی رویکردهای کممؤثرتر را هم رد میکنند. رویکردهای Waterfall این ویژگیها را نشان نمیدهند، مثلاً. با این حال، همهٔ این رفتارها با عملکرد بالا در تیمهای توسعهٔ نرمافزار همبستهاند و دههها نشانهٔ تیمهای موفق بودهاند.
بخش دوم هر یک از این ایدهها را از منظر عملی عمیقتر کاوش میکند: چگونه در یادگیری متخصص شویم و آن را در کار روزمره به کار ببریم؟
متخصصان مدیریت پیچیدگی
بهعنوان توسعهدهندهٔ نرمافزار، جهان را از لنز توسعهٔ نرمافزار میبینم. در نتیجه، برداشت من از شکستها در توسعهٔ نرمافزار و فرهنگ اطرافش را میتوان تا حد زیادی با دو ایدهٔ علوم اطلاعات توصیف کرد: همزمانی (concurrency) و جفتشدگی (coupling).
اینها بهطور کلی — نه فقط در طراحی نرمافزار — دشوارند. پس از طراحی سیستمهایمان به بیرون میریزند و بر نحوهٔ عمل سازمانهایی که در آنها کار میکنیم اثر میگذارند.
میتوانید با ایدههایی مثل قانون Conway[^6] توضیح دهید، اما قانون Conway بیشتر ویژگی emergent این حقایق عمیقتر است.
میتوانید از منظر فنیتر فکر کنید. سازمان انسانی بهاندازهٔ هر سیستم رایانهای یک سیستم اطلاعاتی است. تقریباً حتماً پیچیدهتر است، اما همان ایدههای بنیادین کاربرد دارند. چیزهایی که اساساً دشوارند — مثل concurrency و coupling — در دنیای واقعی مردم هم دشوارند.
اگر میخواهیم سیستمهایی پیچیدهتر از سادهترین تمرینهای برنامهنویسی اسباببازی بسازیم، باید این ایدهها را جدی بگیریم. باید پیچیدگی سیستمهایی که میسازیم را همانطور که میسازیم مدیریت کنیم. و اگر میخواهیم فراتر از محدودهٔ یک تیم کوچک scale کنیم، باید پیچیدگی سیستمهای اطلاعاتی سازمانی را همزمان با سیستمهای اطلاعاتی نرمافزاری فنیتر مدیریت کنیم.
برداشت من این است که بهعنوان صنعت به این ایدهها کمتر از حد لازم توجه میکنیم — آنقدر که همهٔ ما که زمانی در اطراف نرمافزار بودهایم با نتایج آشنا هستیم: سیستمهای big-ball-of-mud، بدهی فنی کنترلنشده، تعداد باگهای فلجکننده و سازمانهایی که از تغییر سیستمهایی که مالک آنها هستند میترسند.
همهٔ اینها را علامت تیمهایی میبینم که کنترل پیچیدگی سیستمهایی که روی آنها کار میکنند را از دست دادهاند.
اگر روی سیستم نرمافزاری ساده و یکبارمصرف کار میکنید، کیفیت طراحی اهمیت کمی دارد. اگر میخواهید چیز پیچیدهتری بسازید، باید مسئله را تقسیم کنید تا بتوانید بدون غرق شدن در پیچیدگی به بخشهایی از آن فکر کنید.
جایی که این خطوط را میکشید به متغیرهای زیادی بستگی دارد: ماهیت مسئله، فناوریهایی که به کار میبرید و شاید تا حدی هوش شما — اما اگر میخواهید مسائل سختتر را حل کنید باید خط بکشید.
بهمحض پذیرش این ایده، دربارهٔ ایدههایی صحبت میکنیم که اثر بزرگی روی طراحی و معماری سیستمهایی که میسازیم دارد. کمی در پاراگراف قبل از ذکر «هوش» بهعنوان پارامتر محتاط بودم، اما یکی از پارامترهاست. مشکلی که از آن محتاط بودم این است که بیشتر ما توانایی حل مسئله در کد را بیش از حد تخمین میزنیم.
این یکی از درسهای زیادی است که میتوانیم از نگاه غیررسمی به علم بیاموزیم: بهتر است با فرض اشتباه بودن ایدههایمان شروع کنیم. پس باید خیلی محتاطتر نسبت به انفجار احتمالی پیچیدگی در سیستمهایی که میسازیم باشیم و با دقت و مراقبت آن را در حین پیشرفت مدیریت کنیم.
پنج ایده در این دسته هم هست — نزدیک به هم و مرتبط با ایدههای متخصص شدن در یادگیری. با این حال، اگر میخواهیم پیچیدگی را بهشکل ساختیافته برای هر سیستم اطلاعاتی مدیریت کنیم، ارزش فکر کردن به این پنج ایده را دارند:
- مدولاریته (Modularity)
- چسبندگی (Cohesion)
- جداسازی دغدغهها (Separation of concerns)
- پنهانسازی اطلاعات / انتزاع (Information hiding/abstraction)
- جفتشدگی (Coupling)
هر یک را در بخش سوم با عمق بیشتری کاوش میکنیم.
خلاصه
ابزارهای حرفهٔ ما اغلب آنچه فکر میکنیم نیستند. زبانها، ابزارها و frameworkهایی که استفاده میکنیم با زمان و از پروژهای به پروژهٔ دیگر عوض میشوند. ایدههایی که یادگیری ما را تسهیل میکنند و اجازه میدهند با پیچیدگی سیستمهایی که میسازیم کنار بیاییم ابزارهای واقعی حرفهٔ ما هستند. با تمرکز روی اینها، بهتر میتوانیم زبانها را انتخاب کنیم، ابزارها را به کار بگیریم و frameworkها را طوری اعمال کنیم که حل مسائل با نرمافزار مؤثرتر شود.
داشتن «خطکش»ی که بتوانیم اینها را با آن ارزیابی کنیم مزیت عظیمی است اگر میخواهیم بر اساس شواهد و داده — نه مد یا حدس — تصمیم بگیریم. هنگام انتخاب باید از خود بپرسیم: «آیا کیفیت نرمافزاری که میسازیم را بالا میبرد؟» — با معیارهای stability. یا «آیا کارایی ساخت نرمافزار با آن کیفیت را بالا میبرد؟» — با معیار throughput. اگر هیچکدام را بدتر نکند، آنچه را ترجیح میدهیم انتخاب میکنیم؛ وگرنه چرا کاری را انتخاب کنیم که یکی از این دو را بدتر کند؟
[^1]: منبع: «Gordon Ramsay Doesn't Use Cake Mixes» اثر Christin Gorman، https://bit.ly/3g02cWO
[^2]: منبع: «Cannot Measure Productivity» اثر Martin Fowler، https://bit.ly/3mDO2fB
[^3]: منبع: Nicole Fosgren، Jez Humble، Gene Kim، https://bit.ly/2PWyjw7
[^4]: کتاب Accelerate توصیف میکند تیمهایی که رویکرد منضبطتری به توسعه دارند «۴۴٪ زمان بیشتر روی کار جدید» نسبت به تیمهایی که ندارند صرف میکنند. نگاه کنید به https://amzn.to/2YYf5Z8.
[^5]: Accelerate اثر Nicole Forsgren، Jez Humble و Gene Kim، ۲۰۱۸
[^6]: در ۱۹۶۷ Mervin Conway مشاهده کرد: «هر سازمانی که سیستمی (بهمعنای گسترده) طراحی میکند، طراحیای تولید میکند که ساختارش کپی ساختار ارتباطی سازمان است.» نگاه کنید به https://bit.ly/3s2KZP2.