Skip to content

فصل ۲ — مهندسی چیست؟

سال‌ها با مردم دربارهٔ مهندسی نرم‌افزار صحبت کرده‌ام. در نتیجه به‌طور غافلگیرکننده‌ای در گفتگوهای زیادی دربارهٔ ساخت پل دخیل می‌شوم. معمولاً با جملهٔ «بله، اما نرم‌افزار ساخت پل نیست» شروع می‌شوند، انگار کشف تازه‌ای است.

البته مهندسی نرم‌افزار همان ساخت پل نیست، اما آنچه بیشتر توسعه‌دهندگان نرم‌افزار از ساخت پل در نظر می‌گیرند هم شبیه ساخت پل واقعی نیست. این گفتگو در واقع نوعی سردرگمی میان مهندسی تولید (production engineering) و مهندسی طراحی (design engineering) است.

مهندسی تولید وقتی رشته با چیزهای فیزیکی سروکار دارد مسئلهٔ پیچیده‌ای است. باید آن چیزهای فیزیکی را با سطحی از دقت و کیفیت مشخص ساخت. باید ویجت‌ها را در مکان مشخصی در فضا، در زمان معین، با بودجهٔ تعریف‌شده تحویل داد و غیره. باید ایده‌های نظری را به واقعیت عملی تطبیق داد وقتی مدل‌ها و طراحی‌ها کم می‌آورند.

دارایی‌های دیجیتال کاملاً متفاوت‌اند. اگرچه برای مصنوعات دیجیتال بعضی شباهت‌ها وجود دارد، این مسائل یا اصلاً وجود ندارند یا می‌توانند به‌شکل بسیار ساده حل شوند. هزینهٔ تولید هر نوع دارایی دیجیتال اساساً صفر است — یا حداقل باید باشد.

تولید مشکل ما نیست

برای بیشتر تلاش‌های بشری، تولید «چیزها» بخش سخت است. طراحی خودرو، هواپیمای مسافربری یا تلفن همراه ممکن است تلاش و نبوغ بخواهد، اما بردن آن طراحی نمونهٔ اولیه به تولید انبوه به‌مراتب گران‌تر و پیچیده‌تر است.

این به‌ویژه وقتی درست است که بخواهیم با هر نوع کارایی انجامش دهیم. در نتیجهٔ این دشواری‌ها — محصولات عصر صنعت و تفکر عصر صنعت — تقریباً خودبه‌خود و بی‌تفکر نگران جنبهٔ تولید هر کار مهم می‌شویم.

نتیجهٔ این در نرم‌افزار آن است که fairly مداوم «تفکر به سبک تولید» را روی صنعتمان اعمال کرده‌ایم. فرایندهای Waterfall[^1] خطوط تولید برای نرم‌افزارند. ابزار تولید انبوه‌اند، نه ابزار کشف، یادگیری و آزمایش که در قلب — یا حداقل باید در قلب — حرفهٔ ما هستند.

مگر اینکه در انتخاب‌های توسعهٔ نرم‌افزار احمقانه عمل کنیم، برای ما تولید یعنی راه انداختن build!

خودکار، دکمه‌ای، immensely مقیاس‌پذیر و آن‌قدر ارزان که بهتر است رایگان فرض شود. هنوز می‌توانیم اشتباه کنیم، اما این‌ها مسائلی‌اند که با ابزار و فناوری درک و حل شده‌اند.

«تولید» مشکل ما نیست. این رشتهٔ ما را غیرمعمول می‌کند و به‌خاطر آسانی تولید — که این‌قدر غیرمعمول است — مستعد سوءبرداشت و شیوه‌ها و تفکرهای نادرست است.

مهندسی طراحی، نه مهندسی تولید

حتی در دنیای واقعی، آنچه بیشتر مردم «ساخت پل» می‌نامند وقتی متفاوت است که پل‌سازان نخستین پل از نوع تازه‌ای می‌سازند. در این شرایط دو مسئله دارید: یکی مرتبط با توسعهٔ نرم‌افزار و یکی نه.

نخست — آن که نیست: حتی برای نخستین پل از نوع تازه، چون فیزیکی است، همهٔ مسائل تولید و بسیاری بیشتر را دارید که گفتم. از منظر نرم‌افزار می‌توان آن‌ها را نادیده گرفت.

دوم: علاوه بر مسائل تولید، اگر نخستین پل از نوع تازه‌ای می‌سازید، بخش واقعاً دشوار دیگر طراحی پل تازه است.

این دشوار است چون وقتی محصول فیزیکی است نمی‌توانید سریع تکرار کنید. ساخت چیزهای فیزیکی سخت تغییر می‌کند.

در نتیجه مهندسان رشته‌های دیگر از تکنیک‌های مدل‌سازی استفاده می‌کنند. شاید مدل‌های فیزیکی کوچک بسازند — و امروز احتمالاً شبیه‌سازی رایانه‌ای یا مدل‌های ریاضی گوناگون.

در این زمینه ما توسعه‌دهندگان نرم‌افزار مزیت عظیمی داریم. پل‌ساز شاید شبیه‌سازی رایانه‌ای از طراحی پیشنهادی بسازد، اما فقط تقریب واقعیت است. شبیه‌سازی و مدلشان نادقیق است. مدل‌هایی که ما به‌عنوان نرم‌افزار می‌سازیم — شبیه‌سازی رایانه‌ای مسئله — محصول ماست.

نیازی نیست نگران باشیم مدل‌ها با واقعیت همخوان باشند؛ مدل‌ها واقعیت سیستم ماست، پس می‌توانیم آن‌ها را تأیید کنیم. نگران هزینهٔ تغییرشان نیستیم. نرم‌افزارند؛ پس به‌مراتب آسان‌تر از پل تغییر می‌کنند.

رشتهٔ ما فنی است. دوست داریم خودمان را در این بستر ببینیم و حدس می‌زنم اکثر کسانی که خود را توسعه‌دهندهٔ حرفه‌ای می‌دانند در تحصیلشان علمی داشته‌اند.

با این حال، توسعهٔ نرم‌افزار کم با عقلانیت علمی در ذهن انجام می‌شود. تا حدی به‌خاطر گام‌های اشتباه در تاریخمان است. تا حدی چون فرض می‌کنیم علم سخت، گران و در محدودهٔ برنامه‌های عادی توسعهٔ نرم‌افزار غیرممکن است.

بخشی از اشتباه این است که سطحی از دقت ایده‌آل‌گرایانه را فرض می‌کنیم که در هیچ حوزه‌ای — چه برسد به نرم‌افزار — ممکن نیست. اشتباه کرده‌ایم و دقت ریاضی را جستجو کرده‌ایم؛ که همان مهندسی نیست!

مهندسی به‌مثابهٔ ریاضی

در اواخر دههٔ ۱۹۸۰ و اوایل ۱۹۹۰ زیاد دربارهٔ ایده‌های ساختاری‌تر برنامه‌نویسی صحبت شد. تفکر دربارهٔ معنای مهندسی نرم‌افزار به بررسی شیوه‌های کار برای تولید کد رفت؛ به‌ویژه چگونه می‌توانیم طوری کار کنیم که شناسایی و حذف مشکلات در طراحی و پیاده‌سازی مؤثرتر باشد.

روش‌های رسمی (formal methods) ایدهٔ محبوبی شد. بیشتر دوره‌های دانشگاهی آن زمان روش‌های رسمی تدریس می‌کردند. روش رسمی رویکردی برای ساخت سیستم‌های نرم‌افزاری است که اعتبارسنجی ریاضی کد نوشته‌شده در آن تعبیه شده. ایده این است که درستی کد اثبات شود.

مشکل بزرگ این است که نوشتن کد برای سیستم پیچیده سخت است، اما نوشتن کدی که رفتار سیستم پیچیده را تعریف کند و خودش را درست ثابت کند حتی سخت‌تر است.

روش‌های رسمی ایدهٔ جذابی‌اند، اما pragmatیک در عمل توسعهٔ نرم‌افزار عمومی پذیرش گسترده نیافته‌اند چون در نقطهٔ تولید، کد را سخت‌تر — نه آسان‌تر — تولید می‌کنند.

استدلال فلسفی‌تر کمی متفاوت است. نرم‌افزار چیز عجیبی است؛ clearly به افرادی که تفکر ریاضی را هم دوست دارند جذاب است. پس جذابیت رویکرد ریاضی واضح است، اما تا حدی محدودکننده هم هست.

یک قیاس دنیای واقعی در نظر بگیرید. مهندسان مدرن از همهٔ ابزارها برای توسعهٔ سیستم تازه استفاده می‌کنند. مدل و شبیه‌سازی می‌سازند و اعداد را می‌زنند تا بفهمند سیستم کار می‌کند یا نه. کارشان سنگیناً با ریاضیات آگاه می‌شود، اما بعد واقعاً امتحانش می‌کنند.

در رشته‌های مهندسی دیگر، ریاضی ابزار مهمی است، اما جایگزین نیاز به آزمون و یادگیری تجربی از تجربهٔ دنیای واقعی نمی‌شود. واریانس در دنیای واقعی آن‌قدر زیاد است که نتوان کاملاً نتیجه را پیش‌بینی کرد. اگر ریاضی به‌تنهایی برای طراحی هواپیما کافی بود، شرکت‌های هوافضا همین کار را می‌کردند چون ارزان‌تر از ساخت نمونهٔ واقعی بود — اما نمی‌کنند. به‌جای آن از ریاضی زیاد برای آگاه کردن تفکرشان استفاده می‌کنند و بعد با آزمودن دستگاه واقعی تفکرشان را می‌سنجند. نرم‌افزار دقیقاً مثل هواپیما یا موشک فضایی نیست.

نرم‌افزار دیجیتال است و روی دستگاه‌های عمدتاً قطعی به نام رایانه اجرا می‌شود. پس در بسترهای باریک، اگر مسئله به‌اندازهٔ کافی ساده، محدود، قطعی و با واریانس کم باشد، روش‌های رسمی می‌توانند مورد را اثبات کنند. مشکل اینجاست که سیستم به‌عنوان کل چقدر قطعی است. اگر جایی concurrent باشد، جایی با «دنیای واقعی» (مردم) تعامل کند، یا در حوزهٔ به‌اندازهٔ کافی پیچیده کار کند، «قابلیت اثبات» به‌سرعت منفجر می‌شود و غیرعملی می‌شود.

پس همان مسیر همکاران هوافضایمان را می‌رویم: تفکر ریاضی را جایی که می‌توانیم به کار می‌بریم و رویکردی داده‌محور، pragmatیک، تجربی و آزمایشی برای یادگیری می‌گیریم تا بتوانیم سیستم‌ها را با رشد تدریجی تطبیق دهیم.

در زمان نگارش این کتاب، SpaceX مشغول منفجر کردن موشک‌هاست در حالی که روی Starship[^2] کار می‌کند. قطعاً مدل‌های ریاضی تقریباً هر جنبهٔ طراحی موشک‌ها، موتورها، سیستم‌های سوخت‌رسانی، زیرساخت پرتاب و بقیه را ساخته، اما بعد آزمایش می‌کند.

حتی چیزی به‌ظاهر ساده مثل تغییر از فولاد ضدزنگ ۴ میلی‌متری به ۳ میلی‌متری شاید تغییر کنترل‌شده‌ای به نظر برسد. SpaceX به دادهٔ دقیق مقاومت کششی فلز دسترسی دارد. تجربه و دادهٔ آزمون‌هایی دارد که دقیقاً نشان می‌دهد مخزن‌های فشار ساخته‌شده از فولاد ۴ میلی‌متری چقدر محکم‌اند.

با این حال، بعد از زدن اعداد، نمونه‌های آزمایشی ساخت تا تفاوت را ارزیابی کند. آن‌ها را تا نقطهٔ تخریب تحت فشار گذاشت تا ببیند محاسبات دقیق بوده یا نه و بینش عمیق‌تری بگیرد. SpaceX داده جمع کرد و مدل‌ها را اعتبارسنجی کرد چون این مدل‌ها حتماً به‌شکلی عجیب و سخت‌پیش‌بینی اشتباه خواهند بود.

مزیت قابل‌توجه ما نسبت به همهٔ رشته‌های مهندسی دیگر این است که مدل‌هایی که در نرم‌افزار می‌سازیم نتیجهٔ قابل اجرای کار ماست؛ پس وقتی آن‌ها را آزمایش می‌کنیم، محصولاتمان را آزمایش می‌کنیم، نه بهترین حدسمان از واقعیت محصولات.

اگر با دقت بخشی از سیستم را که به آن علاقه‌مندیم جدا کنیم، می‌توانیم آن را دقیقاً در همان محیطی که در production مواجه می‌شود ارزیابی کنیم. پس شبیه‌سازی آزمایشی ما می‌تواند «دنیای واقعی» سیستم‌هایمان را دقیق‌تر و درست‌تر از هر رشتهٔ دیگری نشان دهد.

در سخنرانی عالی «Real Software Engineering»^3 Glenn Vanderburg می‌گوید در رشته‌های دیگر «Engineering یعنی چیزهایی که کار می‌کنند» و تقریباً برعکس برای نرم‌افزار رایج شده است.

Vanderburg ادامه می‌دهد و کاوش می‌کند چرا. رویکرد آکادمیک مهندسی نرم‌افزار را توصیف می‌کند که آن‌قدر سنگین بود که تقریباً هیچ‌کس که آن را عمل کرده بود آن را برای پروژه‌های آینده توصیه نمی‌کرد.

سنگین بود و هیچ ارزش قابل‌توجهی به فرایند توسعهٔ نرم‌افزار نمی‌افزود. در عبارتی گویا Vanderburg می‌گوید:

[مهندسی نرم‌افزار آکادمیک] فقط به‌خاطر این کار می‌کرد که افراد تیزبین و دلسوز حاضر بودند فرایند را دور بزنند.

این به هیچ تعریف معقولی از مهندسی نیست.

توصیف Vanderburg از «engineering به‌عنوان چیزهایی که کار می‌کنند» مهم است. اگر شیوه‌هایی که engineering می‌نامیم اجازه ندهد نرم‌افزار بهتری سریع‌تر بسازیم، مهندسی محسوب نمی‌شوند!

توسعهٔ نرم‌افزار — برخلاف همهٔ فرایندهای تولید فیزیکی — کاملاً تمرینی در کشف، یادگیری و طراحی است. مسئلهٔ ما کاوش است؛ پس ما — حتی بیشتر از طراحان فضاپیما — باید فنون کاوش را به‌جای فنون مهندسی تولید به کار ببریم. رشتهٔ ما صرفاً رشتهٔ مهندسی طراحی است.

پس اگر درک ما از engineering اغلب گیج‌کننده است، engineering واقعاً دربارهٔ چیست؟

نخستین مهندس نرم‌افزار

در دورانی که Margaret Hamilton رهبری توسعهٔ سیستم‌های کنترل پرواز Apollo را بر عهده داشت، «قوانین بازی» برای پیروی وجود نداشت. گفت: «قوانین ‘مهندسی نرم‌افزار’مان را با هر کشف مرتبط تازه تکامل دادیم، در حالی که قوانین مدیریت ارشد NASA از ‘آزادی کامل’ به ‘بوروکراسی بیش‌ازحد’ رسید.»

تجربهٔ چنین پروژه‌های پیچیده‌ای در آن زمان بسیار کم بود. تیم اغلب زمین تازه می‌شکست. چالش‌های Hamilton و تیمش عمیق بود و در دههٔ ۱۹۶۰ نمی‌شد پاسخ‌ها را در Stack Overflow جستجو کرد.

Hamilton بعضی چالش‌ها را این‌گونه توصیف کرد:

نرم‌افزار مأموریت فضایی باید man-rated می‌بود. نه فقط باید کار می‌کرد، باید نخستین بار کار می‌کرد. نه فقط خود نرم‌افزار باید فوق‌العاده قابل‌اتکا می‌بود، باید بتواند تشخیص و بازیابی خطا را در زمان واقعی انجام دهد. زبان‌هایمان ما را به خطاهای ظریف‌ترین دعوت می‌کردند. برای ساخت نرم‌افزار باید خودمان قوانین می‌ساختیم. آنچه از خطاها آموختیم پر از شگفتی بود.

در همان زمان، نرم‌افزار به‌طور کلی به‌عنوان «خویشاوند فقیر» نسبت به اشکال «بالغ‌تر» مهندسی نگاه می‌شد. یکی از دلایلی که Hamilton اصطلاح software engineering را ابداع کرد این بود که مردم رشته‌های دیگر نرم‌افزار را جدی‌تر بگیرند.

یکی از نیروهای محرک رویکرد Hamilton تمرکز بر چگونگی شکست — راه‌هایی که اشتباه می‌کنیم — بود:

برایم خطاها جذابیت داشت؛ سرگرمی بی‌پایانم این بود که چه چیزی خطای خاص یا کلاس خطاها را ایجاد می‌کرد و چگونه در آینده از آن جلوگیری کنم.

این تمرکز بر رویکرد عقلانی علمی برای حل مسئله استوار بود. فرض این نبود که می‌توانید برنامه‌ریزی کنید و نخستین بار درست انجام دهید؛ بلکه همهٔ ایده‌ها، راه‌حل‌ها و طراحی‌ها را با تردید می‌پذیرفتید تا وقتی دیگر ایده‌ای برای اشتباه رفتن نداشتید.

گاه واقعیت هنوز شما را شگفت‌زده می‌کند، اما این تجربه‌گرایی مهندسی در عمل است.

اصل مهندسی دیگری که در کار اولیهٔ Hamilton تجسم یافته ایدهٔ شکست ایمن (failing safely) است. فرض این است که نمی‌توانیم برای هر سناریو کد بنویسیم؛ پس چگونه طوری کد می‌زنیم که سیستم‌ها با غیرمنتظره کنار بیایند و همچنان پیشرفت کنند؟ مشهور است پیاده‌سازی بدون درخواست Hamilton از این ایده مأموریت Apollo 11 را نجات داد و به Lunar Module Eagle اجازه داد با وجود overload شدن رایانه در فرود، روی ماه فرود بیاید.

وقتی Neil Armstrong و Buzz Aldrin در Lunar Excursion Module (LEM) به سمت ماه پایین می‌آمدند، مبادله‌ای میان فضانوردان و کنترل مأموریت بود. وقتی LEM به سطح ماه نزدیک شد، رایانه هشدارهای 1201 و 1202 گزارش کرد. فضانوردان پرسیدند ادامه دهند یا مأموریت را لغو کنند.

NASA مردد بود تا یکی از مهندسان فریاد زد «Go!» چون فهمیده بود چه اتفاقی برای نرم‌افزار افتاده:

در Apollo 11، هر بار هشدار 1201 یا 1202 ظاهر می‌شد، رایانه reboot می‌کرد، کارهای مهم — مثل هدایت موتور فرود و اجرای DSKY برای اطلاع خدمه — را دوباره راه می‌انداخت، اما همهٔ jobهای اشتباه برنامه‌ریزی‌شدهٔ رادار rendezvous را دوباره راه نمی‌انداخت. مردان NASA در MOCR می‌دانستند — چون MIT قابلیت restart را extensively آزمایش کرده بود — مأموریت می‌تواند ادامه یابد.[^4]

این رفتار «fail safe» در سیستم کد شده بود، بدون پیش‌بینی مشخص از زمان یا نحوهٔ مفید بودنش.

پس Hamilton و تیمش دو ویژگی کلیدی تفکر مهندسی‌تر را معرفی کردند: یادگیری و کشف تجربی، و عادت تصور کردن چگونگی اشتباه رفتن چیزها.

تعریف کاری مهندسی

بیشتر تعاریف دیکشنری engineering کلمات و عبارات مشترکی دارند: «به‌کارگیری ریاضی»، «شواهد تجربی»، «استدلال علمی»، «در محدودیت‌های اقتصادی».

تعریف کاری زیر را پیشنهاد می‌کنم:

مهندسی، به‌کارگیری رویکردی تجربی و علمی برای یافتن راه‌حل‌های کارآمد و اقتصادی برای مسائل عملی است.

همهٔ این کلمات مهم‌اند. مهندسی علم کاربردی است. عملی است. «تجربی» یعنی یادگیری و پیش بردن درک و راه‌حل‌ها به سمت حل مسئله.

راه‌حل‌هایی که مهندسی می‌سازد چیزهای انتزاعی برج عاجی نیستند؛ عملی و قابل اعمال به مسئله و بسترند.

کارآمدند و با درک و محدودیت اقتصاد موقعیت ساخته می‌شوند.

مهندسی ≠ کد

برداشت رایج دیگر از engineering در توسعهٔ نرم‌افزار این است که engineering فقط خروجی است — کد یا شاید طراحی‌اش.

این تفسیر بیش‌ازحد باریک است. engineering برای SpaceX چیست؟ موشک‌ها نیستند؛ محصولات engineering‌اند. engineering فرایند ساخت آن‌هاست. قطعاً در موشک‌ها engineering هست و «سازه‌های مهندسی‌شده»‌اند، اما فقط عمل جوشکاری فلز را engineering نمی‌بینیم مگر دیدگاهی عجیباً باریک داشته باشیم.

اگر تعریف من درست باشد، engineering دربارهٔ به‌کارگیری عقلانیت علمی برای حل مسائل است. جایی که engineering واقعاً نقش دارد «حل مسائل» است، نه فقط خود راه‌حل‌ها. فرایندها، ابزارها و فنون. ایده‌ها، فلسفه و رویکرد که با هم رشتهٔ مهندسی را می‌سازند.

در حین نگارش این کتاب تجربهٔ غیرمعمولی داشتم: ویدیویی دربارهٔ شکست یک بازی در کانال YouTube منتشر کردم که به‌مراتب محبوب‌تر از بیشتر ویدیوهایم بود.

رایج‌ترین بازخورد منفی وقتی گفتم «شکست مهندسی نرم‌افزار» بود، این که برنامه‌نویس‌ها را مقصر می‌دانم نه مدیرانشان. منظورم شکست در کل رویکرد تولید نرم‌افزار بود. برنامه‌ریزی بد بود، فرهنگ بد بود، کد بد بود (ظاهراً باگ‌های زیاد).

پس در این کتاب وقتی از engineering صحبت می‌کنم — مگر صریحاً محدودش کنم — منظورم همهٔ آنچه برای ساخت نرم‌افزار لازم است: فرایند، ابزار، فرهنگ — همه بخش کل‌اند.

تکامل زبان‌های برنامه‌نویسی

تلاش‌های اولیهٔ مهندسی نرم‌افزار عمدتاً بر ساخت زبان‌های بهتر برای برنامه‌نویسی متمرکز بود. نخستین رایانه‌ها جدایی کمی میان سخت‌افزار و نرم‌افزار داشتند. با وصل کردن سیم به patch board یا تغییر سوئیچ‌ها برنامه‌ریزی می‌شدند.

جالب است که این کار اغلب به «computers» — اغلب زنان — سپرده می‌شد؛ کسانی که قبل از رسیدن رایانه (به‌عنوان ماشین) محاسبه (ریاضی) انجام می‌دادند.

این نقششان را کم‌اهمیت می‌گیرد. «برنامه» در این نقطه — که کسی «مهم‌تر» در سازمان مشخص می‌کرد — اغلب به شکل «می‌خواهیم این مسئلهٔ ریاضی را حل کنیم» بود. سازماندهی کار و بعد جزئیات ترجمه به تنظیمات مناسب ماشین به این «computers» انسانی سپرده می‌شد. آن‌ها پیشگامان واقعی رشتهٔ ما بودند!

امروز فعالیت‌هایشان را با زبان دیگری توصیف می‌کردیم: توصیفی که به کارکنندگان داده می‌شد requirements، تشکیل برنامه برای حل مسئله programming، و «computers» نخستین برنامه‌نویسان واقعی این سیستم‌های رایانه‌ای اولیه.

گام بزرگ بعدی رفتن به «برنامه‌های ذخیره‌شده» و کدگذاری آن‌ها بود. عصر نوار کاغذی و کارت‌های سوراخ‌دار. گام‌های اول پذیرش این رسانه برای برنامه‌ها هنوز pretty سخت‌گیرانه بود. برنامه‌ها به machine code نوشته و روی نوار یا کارت ذخیره می‌شدند قبل از feed شدن به ماشین.

زبان‌های سطح بالا که ایده‌ها را در سطح انتزاع بالاتر می‌گیرند پیشرفت عمدهٔ بعدی بود. این به برنامه‌نویسان اجازه داد خیلی سریع‌تر پیش بروند.

تا اوایل دههٔ ۱۹۸۰، تقریباً همهٔ مفاهیم بنیادین طراحی زبان پوشش داده شده بود. یعنی بعدش پیشرفتی نبود؟ نه — اما بیشتر ایده‌های بزرگ پوشش داده شده بود. با این حال تمرکز توسعهٔ نرم‌افزار بر زبان به‌عنوان ایدهٔ هسته‌ای در رشته ادامه یافت.

چند گام مهم قطعاً بهره‌وری برنامه‌نویسان را تحت تأثیر گذاشت، اما احتمالاً فقط یک گام — یا نزدیک به آن — بهبود ۱۰ برابری Fred Brooks را داد: گام از machine code به زبان‌های سطح بالا.

گام‌های دیگر در این مسیر تکاملی مهم بودند — مثل برنامه‌نویسی رویه‌ای، شیءگرایی و برنامه‌نویسی تابعی — اما همهٔ این ایده‌ها مدت‌هاست وجود دارند.

وسواس صنعتمان به زبان‌ها و ابزارها به حرفهٔ ما آسیب زده. یعنی پیشرفتی در طراحی زبان نیست؟ نیست — اما بیشتر کار طراحی زبان روی چیزهای اشتباه متمرکز است؛ مثل پیشرفت‌های نحوی به‌جای پیشرفت‌های ساختاری.

در روزهای اولیه قطعاً باید می‌آموختیم و کاوش می‌کردیم چه ممکن و چه معقول است. از آن زمان، اما، تلاش زیادی برای پیشرفت نسبتاً کم صرف شده. وقتی Fred Brooks گفت بهبود ۱۰ برابری نیست، بقیهٔ مقاله‌اش روی آنچه می‌توانیم برای غلبه بر این محدودیت انجام دهیم متمرکز بود:

نخستین گام به سمت مدیریت بیماری، جایگزینی نظریه‌های ارواح و humors با نظریهٔ میکرب بود. همان گام، آغاز امید، خودش همهٔ امیدهای راه‌حل جادویی را خرد کرد.

…سیستم باید نخست اجرا شود، حتی اگر جزئی مفید نباشد جزیرهٔ فراخوانی مجموعهٔ مناسب زیربرنامه‌های dummy. بعد کم‌کم پر می‌شود و زیربرنامه‌ها به actionها یا فراخوانی stubهای خالی در سطح پایین‌تر توسعه می‌یابند.

این ایده‌ها بر ایده‌های عمیق‌تر و پرمعناتر از جزئیات پیش‌پاافتادهٔ پیاده‌سازی زبان استوار بودند.

مسائلی بیشتر مربوط به فلسفهٔ رشتهٔ ما و به‌کارگیری اصول بنیادینی بودند که هرچه ماهیت فناوری باشد درست می‌مانند.

چرا مهندسی مهم است؟

راه دیگر تفکر دربارهٔ این است که چگونه چیزهایی را که به ما کمک می‌کنند تولید می‌کنیم. برای بیشتر تاریخ بشریت، همهٔ آنچه می‌ساختیم حاصل صنعتگری (craft) بود. صنعتگری رویکرد مؤثری برای ساخت چیزهاست، اما محدودیت دارد.

صنعتگری برای ساخت اقلام «یک‌بار مصرف» (one-off) بسیار خوب است. در سیستم تولید مبتنی بر صنعتگری، هر قلم حتماً منحصربه‌فرد است. در خالص‌ترین معنا این برای هر سیستم تولید درست است، اما در رویکردهای مبتنی بر صنعتگری بیشتر درست است چون دقت و پس در نتیجه تکرارپذیری فرایند تولید معمولاً پایین است.

یعنی واریانس میان مصنوعات صنعتگری‌شده بالاتر است. حتی ماهرترین صنعتگران فقط با دقت و تلورانس سطح انسان می‌سازند. این جدی بر توانایی سیستم‌های مبتنی بر صنعتگری برای تولید قابل‌اتکا تأثیر می‌گذارد. Grace Hopper گفت:

برای من برنامه‌نویسی بیش از هنر عملی مهم است. تلاشی عظیم در بنیادهای دانش هم هست.

محدودیت‌های «صنعتگری»

اغلب واکنش احساسی به تولید مبتنی بر صنعتگری داریم. به‌عنوان انسان واریانس را دوست داریم؛ حس می‌کنیم چیز دست‌ساز گرانبهایمان مهارت، عشق و دقت صنعتگر را در خود دارد.

اما در ریشه، تولید مبتنی بر صنعتگری اساساً کیفیت پایین دارد. انسان، هرچقدر هم بااستعداد، به‌دقت ماشین نیست.

می‌توانیم ماشین‌هایی بسازیم که اتم‌ها — حتی ذرات زیراتمی — را دستکاری کنند، اما انسان فوق‌العاده بااستعداد است اگر بتواند دستی چیزی با دقت ۱/۱۰ میلی‌متر تولید کند.[^5]

این دقت در نرم‌افزار چه اهمیتی دارد؟ فکر کنیم وقتی برنامه‌هایمان اجرا می‌شوند چه می‌شود. انسان می‌تواند تغییر — هر تغییری — را در حدود ۱۳ میلی‌ثانیه ادراک کند. پردازش تصویر یا واکنش به چیزی صدها میلی‌ثانیه طول می‌کشد.[^6]

در زمان نگارش، بیشتر رایانه‌های مصرفی مدرن با سیکل ساعت حدود ۳ گیگاهرتز کار می‌کنند — ۳ میلیارد سیکل در ثانیه. رایانه‌های مدرن چند هسته‌ای‌اند و دستورات را موازی پردازش می‌کنند؛ اغلب بیش از یک دستور در هر سیکل — اما برای سادگی فرض کنیم هر دستور ماشین که مقدار بین registerها جابه‌جا می‌کند، جمع می‌زند یا به حافظهٔ in-cache ارجاع می‌دهد یک سیکل ساعت می‌گیرد.

یعنی ۳ میلیارد عملیات در ثانیه. اگر ریاضی بزنیم و ببینیم رایانهٔ مدرن در حداقل زمانی که انسان می‌تواند رویداد بیرونی را ادراک کند چند دستور می‌تواند اجرا کند، عدد ۳۹٬۰۰۰٬۰۰۰ دستور است!

اگر کیفیت کارمان را به ادراک و دقت مقیاس انسان محدود کنیم، در بهترین حالت با نرخ ۱ به ۳۹ میلیون نمونه‌برداری می‌کنیم. پس شانس از دست دادن چیزی چقدر است؟

دقت و مقیاس‌پذیری

این تفاوت میان صنعتگری و مهندسی دو جنبهٔ مهم engineering را در بستر نرم‌افزار برجسته می‌کند: دقت و مقیاس‌پذیری.

دقت واضح است: با فنون مهندسی می‌توانیم چیزها را با وضوح جزئیات بسیار بالاتر از دستکاری دستی کنترل کنیم. مقیاس‌پذیری شاید کمتر فوری باشد اما مهم‌تر است. رویکرد مهندسی مثل رویکرد مبتنی بر صنعتگری محدود نیست.

محدودیت هر رویکردی که به توانایی انسان تکیه کند، در نهایت توانایی انسان است. اگر خودم را وقف دستیابی به چیز فوق‌العاده کنم، شاید یاد بگیرم خطی بکشم، فلزی بسابم یا صندلی چرمی بدوزم با کسری از میلی‌متر — اما هرچقدر تلاش کنم، هرچقدر استعداد داشته باشم، محدودیت‌های سخت برای دقت عضلات و حواس انسان وجود دارد.

مهندس، اما، می‌تواند ماشینی بسازد که چیزی کوچک‌تر و دقیق‌تر بسازد. می‌توانیم ماشین (ابزار) بسازیم تا ماشین کوچک‌تر بسازد.

این تکنیک از محدودیت‌های فیزیک کوانتومی تا محدودیت‌های کیهان‌شناسی مقیاس‌پذیر است. حداقل در نظری هیچ‌چیز — با به‌کارگیری engineering — مانع دستکاری اتم‌ها و الکترون‌ها (که همین حالا می‌کنیم) یا ستاره‌ها و سیاه‌چاله‌ها (که شاید روزی) نمی‌شود.

برای روشن‌تر کردن در بستر نرم‌افزار: اگر بسیار ماهر باشیم و سخت تمرین کنیم، شاید بتوانیم متن وارد کنیم و دکمه بزنیم به‌قدری سریع که در چند دقیقه یک آزمون نرم‌افزار انجام دهیم. برای مقایسه فرض کنیم هر دقیقه یک آزمون انجام می‌دهیم (سرعتی که خودم نمی‌توانم مدت زیاد حفظ کنم).

اگر آزمون در دقیقه انجام دهیم، نسبت به رایانه صدها هزار — احتمالاً میلیون‌ها — برابر کمتر آزمایش می‌کنیم.

سیستم‌هایی ساخته‌ام که حدود ۳۰٬۰۰۰ مورد آزمون را در حدود ۲ دقیقه اجرا می‌کردند. می‌توانستیم بیشتر scale کنیم اما دلیلی نبود. Google ادعا می‌کند روزانه ۱۵۰ میلیون اجرای آزمون دارد. یعنی ۱۰۴٬۱۶۶ آزمون در دقیقه.[^7]

نه‌تنها می‌توانیم از رایانه برای آزمون صدها هزار برابر سریع‌تر از انسان استفاده کنیم، بلکه می‌توانیم این سرعت را تا وقتی برق داریم حفظ کنیم. این مقیاس‌پذیر است!

مدیریت پیچیدگی

راه دیگر که engineering مقیاس می‌گیرد — جایی که صنعتگری نمی‌گیرد — این است که تفکر مهندسی ما را به بخش‌بندی مسائل سوق می‌دهد. قبل از جنگ داخلی آمریکا در دههٔ ۱۸۶۰، اگر تفنگ می‌خواستید به gunsmith می‌رفتید. gunsmith صنعتگر بود — و معمولاً مرد!

gunsmith برایتان کل تفنگ می‌ساخت. هر جنبهٔ آن تفنگ را می‌فهمید و برای شما منحصربه‌فرد بود. احتمالاً قالب گلوله هم می‌داد چون گلوله‌هایتان با بقیه فرق داشت و مخصوص تفنگ شما بود. اگر پیچ داشت، تقریباً حتماً هر کدام با بقیه فرق می‌کرد چون دست‌ساز بود.

جنگ داخلی آمریکا در زمانش منحصربه‌فرد بود. نخستین جنگی بود که سلاح‌ها انبوه تولید می‌شدند.

داستانی از مردی است که می‌خواست تفنگ به ایالت‌های شمالی بفروشد. نوآور بود و به‌نظر می‌رسید کمی هم شوئمن. به کنگره رفت تا برای قرارداد ساخت تفنگ برای ارتش شمال استدلال کند.

کیسه‌ای پر از اجزای تفنگ با خود برد. در ارائه به نمایندگان Congress، کیسه را روی کف Congress خالی کرد و از آن‌ها خواست از توده اجزا انتخاب کنند. از همان اجزا تفنگی سرهم کرد، قرارداد را برد و تولید انبوه را اختراع کرد.

این نخستین باری بود چنین استانداردسازی ممکن شد. چیزهای زیادی باید اتفاق می‌افتاد؛ ماشین‌ها (ابزار) باید engineer می‌شدند تا اجزایی که در محدودهٔ تلورانس تعریف‌شده تکرارپذیر یکسان بودند تولید کنند. طراحی باید modular می‌بود تا اجزا سرهم شوند و غیره.

نتیجه ویرانگر بود. جنگ داخلی آمریکا اساساً نخستین جنگ مدرن بود. صدها هزار نفر به‌خاطر تولید انبوه تسلیحات کشته شدند. این سلاح‌ها ارزان‌تر، نگهداری و تعمیر آسان‌تر و دقیق‌تر از قبل بودند.

همهٔ این به‌خاطر engineer شدن با دقت بیشتر بود — اما همچنین به‌خاطر تعداد بیشتر. فرایند تولید می‌توانست de-skill و scale شود. به‌جای نیاز به استاد صنعتگر برای هر سلاح، ماشین‌آلات کارخانه به افراد کم‌مهارت‌تر اجازه می‌داد تفنگی با دقت قابل مقایسه با استاد بسازند.

بعداً با افزایش ابزار، تکنیک‌های تولید و درک و انضباط مهندسی، این سلاح‌های انبوه از کیفیت — و همچنین بهره‌وری — حتی بزرگ‌ترین استادان صنعتگر هم پیشی گرفتند، با قیمتی که همه می‌توانستند بخرند.

دیدگاه ساده‌انگارانه ممکن است این را «نیاز به استانداردسازی» یا پذیرش «تولید انبوه برای نرم‌افزار» تفسیر کند، اما باز هم ماهیت بنیادین مسئلهٔ ما را اشتباه می‌گیرد. این دربارهٔ تولید نیست — دربارهٔ طراحی است.

اگر تفنگی طراحی کنیم که modular و componentized باشد مثل تولیدکنندگان جنگ داخلی، می‌توانیم بخش‌های آن را مستقل‌تر طراحی کنیم. از منظر طراحی — نه مهندسی تولید یا ساخت — مدیریت پیچیدگی ساخت تفنگ را بهبود داده‌ایم.

قبل از این گام، استادان gunsmith برای تغییر جنبه‌ای از طراحی باید کل تفنگ را در ذهن داشتند. با componentize کردن طراحی، تولیدکنندگان جنگ داخلی می‌توانستند گام‌به‌گام تغییرات تدریجی را برای بهبود کیفیت محصول امتحان کنند.

Edsger Dijkstra گفت:

هنر برنامه‌نویسی، هنر سامان‌دهی پیچیدگی است.

تکرارپذیری و دقت اندازه‌گیری

جنبهٔ دیگر engineering که رایج دیده می‌شود — و گاه برای رد کردن engineering در نرم‌افزار به کار می‌رود — تکرارپذیری (repeatability) است.

اگر ماشینی بسازیم که پیچ و مهره را قابل‌اتکا و دقیق تکرار کند، می‌توانیم انبوه تولید کنیم و همهٔ کپی‌های پیچ با هر کپی مهره کار کنند.

این مسئلهٔ تولید است و واقعاً برای نرم‌افزار کاربرد ندارد. اما ایدهٔ بنیادین‌تری که زیر این قابلیت است برای نرم‌افزار کاربرد دارد.

برای ساخت پیچ و مهره — یا هر چیز دیگر — که باید قابل‌اتکا با هم کار کنند، باید بتوانیم چیزها را با سطحی از دقت اندازه بگیریم. دقت در اندازه‌گیری جنبهٔ توانمندساز engineering در هر رشته است.

لحظه‌ای تصور کنیم سیستم نرم‌افزاری پیچیده‌ای داریم. بعد از چند هفته کار، سیستم خراب می‌شود. restart می‌شود و دو هفته بعد تقریباً به همان شکل دوباره خراب می‌شود؛ الگویی هست. تیم متمرکز بر صنعتگری چگونه با این کنار می‌آید در مقایسه با تیم متمرکز بر engineering؟

تیم صنعتگر احتمالاً تصمیم می‌گیرد باید نرم‌افزار را thoroughتر آزمایش کند. چون به اصطلاحات صنعتگری فکر می‌کند، می‌خواهد شکست را clearly ببیند.

این احمقانه نیست؛ در این بستر منطقی است، اما چگونه؟ رایج‌ترین راه‌حلی که برای این مسئله دیده‌ام soak test است. soak test کمی بیشتر از فاصلهٔ معمول بین خرابی‌ها — مثلاً سه هفته در مثال ما — اجرا می‌شود. گاه سعی می‌کنند زمان را تسریع کنند تا soak دورهٔ مشکل را در زمان کوتاه‌تر شبیه‌سازی کند، اما معمولاً نه.

آزمون اجرا می‌شود، سیستم بعد از دو هفته در آزمون fail می‌کند و باگ شناسایی و رفع می‌شود.

جایگزینی برای این راهبرد هست؟ بله!

soak testها نشت منابع از یک نوع یا دیگر را detect می‌کنند. دو راه برای detect نشت وجود دارد: منتظر بمانید نشت آشکار شود، یا دقت اندازه‌گیری را افزایش دهید تا نشت را زودتر — قبل از فاجعه — بگیرید.

اخیراً در آشپزخانه نشت داشتم — در لوله‌ای مدفون در بتن. نشت را وقتی detect کردیم که بتن آن‌قدر خیس شده بود آب روی سطح جمع شد. این راهبرد detect «آشکار» است.

متخصص آوردیم. ابزاری آورد — راه‌حل engineerشده. میکروفون بسیار حساسی که صدای نشت زیرزمین را «می‌شنید».

با این ابزار می‌توانست hiss کم‌صدای آب نشت‌کننده در بتن را با دقت فراانسانی تشخیص دهد و مکان را در چند اینچ پیدا کند و تراک کوچکی برای رسیدن به لولهٔ معیوب بزند.

برگردیم به مثال: تیم متمرکز بر engineering از اندازه‌گیری دقیق به‌جای انتظار برای اتفاق بد استفاده می‌کند. عملکرد نرم‌افزار را می‌سنجد تا نشت‌ها را قبل از مشکل شدن detect کند.

این رویکرد مزایای متعدد دارد: شکست فاجعه‌بار در production تا حد زیادی اجتناب می‌شود، اما همچنین زودتر نشانهٔ مشکل و بازخورد ارزشمند دربارهٔ سلامت سیستم می‌گیرید. به‌جای soak test هفته‌ها، تیم متمرکز بر engineering می‌تواند نشت را در آزمون‌های منظم detect کند و در چند دقیقه نتیجه بگیرد. David Parnas گفت:

مهندسی نرم‌افزار اغلب شاخه‌ای از علوم رایانه تلقی می‌شود. این مثل این است که مهندسی شیمیایی را شاخه‌ای از شیمی بدانیم. به هر دو — شیمی‌دان و مهندس شیمی — نیاز داریم، اما متفاوت‌اند.

مهندسی، خلاقیت و صنعتگری

برای تفکر دربارهٔ engineering به‌طور کلی و مهندسی نرم‌افزار به‌طور خاص، چند سال است این ایده‌ها را کاوش می‌کنم. در کنفرانس‌های نرم‌افزار سخنرانی کرده‌ام و گاه در وبلاگ نوشته‌ام.

گاه بازخوردی از پیروان ایده‌های software craftsmanship می‌گیرم — معمولاً به شکل «چیز مهمی را در رد کردن صنعتگری از دست می‌دهید».

ایده‌های software craftsmanship مهم بودند. گام مهمی دور از رویکردهای آیین‌محور و متمرکز بر تولید بود که قبل از آن‌ها بود.

ادعای من این نیست که software craftsmanship اشتباه است؛ بلکه کافی نیست.

تا حدی این بحث‌ها از فرض نادرستی شروع می‌شوند که قبلاً گفتم. بسیاری از صنعتگران نرم‌افزار اشتباه رایج را می‌کنند و فرض می‌کنند همهٔ engineering دربارهٔ حل مسائل تولید است. اگر مسئلهٔ ما «مهندسی طراحی» است، رشته‌ای بسیار متفاوت، بسیار کاوش‌گرانه‌تر و خلاق‌تر نسبت به «مهندسی تولید» است.

علاوه بر این، هم‌گفتگویان صنعتگر نگران خطر دور انداختن دستاوردهای software craftsmanship هستند — به‌ویژه تمرکز بر:

  • مهارت (Skill)
  • خلاقیت (Creativity)
  • آزادی نوآوری
  • طرح‌های شاگردی (Apprentice schemes)

این‌ها برای هر رویکرد حرفه‌ای مؤثر در توسعهٔ نرم‌افزار مهم‌اند. اما محدود به رویکردهای مبتنی بر صنعتگری نیستند. جنبش software craftsmanship گام مهمی در بهبود توسعهٔ نرم‌افزار بود با بازتمرکز روی چیزهای مهم — از جمله موارد بالا.

این ایده‌ها گم شده یا حداقل در تلاش‌های دهه‌های ۱۹۸۰ و ۱۹۹۰ برای force-fit کردن رویکرد command-and-control متمرکز بر تولید روی توسعهٔ نرم‌افزار جا گرفته بودند. ایدهٔ وحشتناکی بود چون اگرچه فرایندها و تفکر waterfall جایی در مسائلی که گام‌ها خوب فهمیده، تکرارپذیر و قابل پیش‌بینی‌اند جای دارند، ارتباط کمی با واقعیت توسعهٔ نرم‌افزار دارد.

software craftsmanship تناسب بسیار بهتری با نوع مسئله‌ای داشت که توسعهٔ نرم‌افزار واقعاً هست.

مشکل راه‌حل‌های مبتنی بر صنعتگری این است که به‌شکل engineering مقیاس‌پذیر نیستند.

صنعتگری می‌تواند چیزهای خوب بسازد، اما فقط در محدوده‌هایی.

انضباط مهندسی در تقریباً همهٔ تلاش‌های بشری کیفیت را بالا می‌برد، هزینه را پایین می‌آورد و راه‌حل‌های مقاوم‌تر، انعطاف‌پذیرتر و قوی‌تر می‌دهد.

اشتباه بزرگ است که ایده‌هایی مثل مهارت، خلاقیت و نوآوری را فقط به صنعتگری نسبت دهیم. مهندسان — به‌ویژه مهندسان طراحی — این ویژگی‌ها را همیشه و فراوان نشان می‌دهند. این صفات در قلب فرایند مهندسی طراحی‌اند.

پس رویکرد مهندسی به هیچ وجه اهمیت مهارت، خلاقیت و نوآوری را کم نمی‌کند. اگر چیزی، نیاز به این صفات را تقویت می‌کند.

دربارهٔ آموزش: آیا دوستان صنعتگر نرم‌افزارم باور دارند مهندس تازه‌فارغ‌التحصیل دانشگاه فوراً مسئولیت طراحی پل یا شاتل فضایی می‌گیرد؟ البته نه!

مهندس در آغاز مسیر کنار مهندسان باتجربه‌تر کار می‌کند. عملیات رشته و شاید حتی بیشتر از صنعتگر می‌آموزد.

تنش واقعی میان صنعتگری و engineering نمی‌بینم. اگر دیدگاه نسبتاً رسمی صنعتگری — با guildها، شاگردان، journeymanها و استادان — را بگیرید، engineering واقعاً گام بعدی از آن بود. وقتی عقلانیت علمی ریشه گرفت — پس از تفکر روشنگری قرن‌های ۱۷ و ۱۸ — engineering در واقع صنعتگری تقویت‌شده با دقت و اندازه‌گیری بیشتر بود. engineering فرزند مقیاس‌پذیرتر و مؤثرتر صنعتگری است.

اگر تعاریف عامیانهٔ craft — مثل نمایشگاه صنایع دستی — را بگیرید، استاندارد واقعی برای کیفیت یا پیشرفت نیست؛ پس engineering شاید جهش بزرگ‌تری باشد.

engineering — به‌ویژه به‌کارگیری تفکر مهندسی در طراحی — واقعاً تفاوت تمدن فناوری‌محور ما با تمدن‌های کشاورزی پیش از آن است. رشته‌ای است که به ما اجازه می‌دهد مسائل به‌طرز شگفت‌انگیزی پیچیده را بر عهده بگیریم و راه‌حل‌های elegant و کارآمد بیابیم.

وقتی اصول تفکر مهندسی را به توسعهٔ نرم‌افزار اعمال می‌کنیم، بهبودهای قابل اندازه‌گیری و چشمگیر در کیفیت، بهره‌وری و کاربرد راه‌حل‌ها می‌بینیم.[^8]

چرا آنچه انجام می‌دهیم مهندسی نرم‌افزار نیست

در ۲۰۱۹ شرکت SpaceX Elon Musk تصمیم بزرگی گرفت: روی ساخت فضاپیمایی کار می‌کرد که روزی به انسان‌ها اجازه دهد روی مریخ زندگی و کار کنند و بخش‌های دیگر منظومه را کاوش کنند. در ۲۰۱۹ از ساخت Starshipها از فیبر کربن به فولاد ضدزنگ تغییر داد. فیبر کربن ایدهٔ pretty رادیکالی بود؛ کار زیادی از جمله ساخت مخزن سوخت نمونه از آن انجام داده بودند. فولاد ضدزنگ هم انتخاب رادیکالی بود؛ بیشتر موشک‌ها از آلومینیوم ساخته می‌شوند به‌خاطر سبکی و استحکام.

انتخاب SpaceX فولاد ضدزنگ به‌جای فیبر کربن بر سه چیز استوار بود: هزینه به ازای کیلوگرم برای فولاد به‌مراتب کمتر بود؛ عملکرد در دمای بالا — برای تحمل دمای ورود مجدد — بهتر از آلومینیوم بود؛ عملکرد در دمای پایین و cryogenic به‌مراتب بهتر از هر دو جایگزین.

فیبر کربن و آلومینیوم در دماهای بسیار پایین و بالا به‌مراتب ضعیف‌تر از فولادند.

آخرین باری که شنیدید کسی برای تصمیم مرتبط با ساخت نرم‌افزار استدلالی حتی vaguely شبیه این بیاورد چه بود؟

این شکل تصمیم‌های engineering است. بر معیارهای عقلانی استوارند — استحکام در دمای معین، یا اثر اقتصادی. هنوز آزمایشی، تکراری و تجربی است. بر اساس شواهد و نظریهٔ معنای آن تصمیم می‌گیرید و بعد ایده‌ها را آزمایش می‌کنید. فرایند کاملاً قابل پیش‌بینی نیست.

SpaceX سازه‌های آزمایشی ساخت و تحت فشار گذاشت — نخست با آب و بعد با نیتروژن مایع — تا عملکرد cryogenic مواد (فولاد) و فرایند ساخت را آزمایش کند. مهندسی طراحی رویکردی عمیقاً کاوش‌گرانه برای کسب دانش است.

مبادله‌ها (Trade-Offs)

همهٔ engineering بازی بهینه‌سازی و مبادله است. می‌خواهیم مسئله‌ای را حل کنیم و حتماً با انتخاب‌ها روبه‌رو می‌شویم. در ساخت موشک‌ها، یکی از بزرگ‌ترین مبادله‌ها برای SpaceX میان استحکام و وزن است. مسئلهٔ رایج برای ماشین‌های پرنده — و در واقع بیشتر وسایل نقلیه.

درک مبادله‌هایی که با آن‌ها روبه‌رویم جنبهٔ حیاتی و بنیادین تصمیم‌گیری engineering است.

اگر سیستم را امن‌تر کنیم، استفاده سخت‌تر می‌شود؛ اگر توزیع‌شده‌تر کنیم، زمان بیشتری برای یکپارچه‌سازی اطلاعاتی که جمع می‌کند صرف می‌کنیم. اگر برای تسریع توسعه افراد بیشتری اضافه کنیم، سربار ارتباط، coupling و پیچیدگی را افزایش می‌دهیم — که همه ما را کند می‌کند.

یکی از مبادله‌های کلیدی که در تولید نرم‌افزار — در هر سطحی از کل سیستم‌های سازمانی تا تک function — حیاتی است coupling است. (در فصل ۱۳ با جزئیات بیشتر کاوش می‌کنیم.)

توهم پیشرفت

سطح تغییر در صنعتمان impressive است، اما تز من این است که بسیاری از این تغییرات واقعاً معنادار نیستند.

در زمان نگارش در کنفرانسی دربارهٔ serverless computing[^9] هستم. حرکت به سیستم‌های serverless جالب است؛ اما تفاوت میان toolkitهای AWS، Azure، Google یا هر کس دیگر واقعاً مهم نیست.

تصمیم پذیرش رویکرد serverless پیامدهایی برای طراحی سیستم دارد. state کجا ذخیره می‌شود؟ کجا دستکاری می‌شود؟ functionهای سیستم را چگونه تقسیم می‌کنید؟ وقتی واحد طراحی function است، سیستم‌های پیچیده را چگونه سازماندهی و پیمایش می‌کنید؟

این سؤالات بسیار جالب‌تر و مهم‌تر برای موفقیت تلاش شما — هرچه باشد — از جزئیات نحوهٔ تعریف function یا استفاده از storage یا security پلتفرم هستند. با این حال تقریباً همهٔ ارائه‌هایی که در این موضوع می‌بینم دربارهٔ ابزارند، نه طراحی سیستم‌ها.

انگار نجارم و تفاوت‌های مهم پیچ دَوَشی و پیچ چهارسو به من گفته می‌شود، اما نگفته می‌شود پیچ‌ها برای چه‌اند، کی استفاده شوند و کی میخ انتخاب شود.

serverless computing به‌عنوان مدل محاسباتی گام به جلو است. این را زیر سؤال نمی‌برم. این کتاب دربارهٔ ایده‌هایی است که به ما اجازه می‌دهند قضاوت کنیم کدام ایده‌ها مهم‌اند و کدام نیستند.

serverless به چند دلیل مهم است، اما principally چون رویکرد modularتر در طراحی با جداسازی دغدغه‌های بهتر — به‌ویژه نسبت به داده — تشویق می‌کند.

serverless computing اقتصاد سیستم‌ها را با جابه‌جایی محاسبه از «هزینه به ازای بایت» به «هزینه به ازای سیکل CPU» عوض می‌کند. یعنی — یا باید یعنی — باید بهینه‌سازی‌های بسیار متفاوتی را در نظر بگیریم.

به‌جای بهینه‌سازی برای کمینه کردن storage با datastoreهای normalized، شاید باید مدل محاسباتی genuinely توزیع‌شده‌تر با storeهای non-normalized و الگوهای eventual-consistency را بپذیریم. این‌ها مهم‌اند به‌خاطر اثرشان بر modularity سیستم‌هایی که می‌سازیم.

ابزارها فقط به‌اندازه‌ای مهم‌اند که «عقربه را حرکت دهند» روی چیزهای بنیادین‌تر.

سفر از صنعتگری به مهندسی

مهم است ارزش صنعتگری را نادیده نگیریم. دقت و توجه به جزئیات برای ساخت کار باکیفیت لازم است. همچنین مهم است اهمیت engineering برای تقویت کیفیت و اثربخشی محصولات صنعتگری را نادیده نگیریم.

نخستین کسانی که ماشین پرندهٔ کنترل‌پذیر سنگین‌تر از هوا را ساختند برادران Wright بودند. هم صنعتگران عالی و هم مهندسان عالی بودند. بخش زیادی از کارشان بر کشف تجربی استوار بود، اما تحقیق واقعی دربارهٔ اثربخشی طراحی‌هایشان هم کردند. علاوه بر ساخت نخستین ماشین پرنده، نخستین کسانی بودند که تونل باد ساختند تا اثربخشی طراحی بال را اندازه بگیرند.

بال هواپیما سازهٔ remarkable است. ساخت برادران Wright زیباست — اگرچه به معیارهای مدرن فوق‌العاده خام — از چوب و سیم و پارچه، با روغن موز برای محکم و ضدباد کردن.

آن و تونل باد برای تکامل درک پایه‌های نظری آیرودینامیک به کار رفت — بر کار پیشگامان قبلی استوار. اما عمدتاً ماشین پرنده و بال برادران Wright — بیشتر از طراحی نظری خالص — از فرایند آزمون و خطا ساخته شد.

به چشم مدرن بیشتر محصول صنعتگری تا engineering به نظر می‌رسد. تا حدی — نه کاملاً — درست است. بسیاری با رویکردهای مبتنی بر صنعتگری «ماشین پرنده» ساختند و شکست خوردند. یکی از دلایل مهم موفقیت برادران Wright به‌کارگیری engineering بود. محاسبات را انجام دادند و ابزار اندازه‌گیری و تحقیق ساختند و استفاده کردند. متغیرها را کنترل کردند تا درکشان عمیق‌تر و مدل پروازشان refine شود. بعد مدل و گلایدر و قطعات تونل باد ساختند تا آزمایش کنند و درکشان را رشد دهند. اصولی که استوار کردند کامل نبود، اما هم عملیات و هم نظریه را بهبود دادند.

وقتی برادران Wright پرواز کنترل‌شدهٔ سنگین‌تر از هوا را به دست آوردند، تحقیق آیرودینامیکشان اجازه داد ماشین‌های پرنده با نسبت glide ۸٫۳ به ۱ بسازند.[^10]

برای مقایسه با بال هواپیمای مدرن — مثلاً بال یک sailplane مدرن: بال Wright Flyer under-cambered بود (airfoil کند با lift بالا) و به معیارهای مدرن سنگین بود — اگرچه در روزش سبک‌ساز بود. از مواد طبیعی ساده استفاده کرد و به ۸٫۳ به ۱ رسید.

از طریق engineering، کشف تجربی، آزمایش، و همچنین علم مواد، refine نظریهٔ آیرودینامیک، مدل‌سازی رایانه‌ای و غیره، sailplane مدرن بال carbon fiber با aspect ratio بالا دارد. برای سبک و محکم بودن optimize شده — آن‌قدر که وقتی lift تولید می‌کند خم شدن و انعطافش را clearly می‌بینید. می‌تواند نسبت glide بیش از ۷۰ به ۱ — نزدیک به نه برابر بهتر از Wright Flyer — دست یابد.

صنعتگری کافی نیست

صنعتگری مهم است — به‌ویژه اگر منظورتان خلاقیت باشد. رشتهٔ ما تلاشی عمیقاً خلاقانه است، اما engineering هم همین‌طور. باور دارم engineering در واقع اوج خلاقیت و نبوغ بشری است. این نوع تفکری است که اگر می‌خواهیم آثار عظیم در نرم‌افزار بسازیم نیاز داریم.

وقت بازاندیشی؟

تکامل مهندسی نرم‌افزار به‌عنوان رشته واقعاً آنچه بسیاری امید داشتند محقق نکرده. نرم‌افزار جهان را عوض کرده و می‌کند. کارهای شگفت‌انگیز و سیستم‌های نوآورانه و هیجان‌انگیز ساخته شده — اما برای بسیاری از تیم‌ها، سازمان‌ها و توسعه‌دهندگان فردی، همیشه روشن نیست چگونه موفق شوند یا حتی پیشرفت کنند.

صنعتمان پر از فلسفه‌ها، شیوه‌ها، فرایندها و فناوری‌هاست. جنگ‌های مذهبی میان تکنولوژیست‌ها بر سر بهترین زبان‌های برنامه‌نویسی، رویکردهای معماری، فرایندهای توسعه و ابزارها. اغلب فقط حس شلّی از اهداف و راهبردهای حرفهٔ ما وجود دارد.

تیم‌های مدرن با فشار برنامه، کیفیت و قابلیت نگهداری طراحی‌ها می‌جنگند. اغلب سخت می‌توانند ایده‌هایی را شناسایی کنند که واقعاً برای کاربران مهم است و به خودشان زمان یادگیری دربارهٔ حوزهٔ مسئله، فناوری و فرصت‌ها برای رساندن چیز عالی به production نمی‌دهند.

سازمان‌ها اغلب سخت می‌توانند آنچه از توسعهٔ نرم‌افزار می‌خواهند بگیرند. اغلب از کیفیت و کارایی تیم‌های توسعه شکایت می‌کنند. اغلب چیزهایی را که می‌توانند برای غلبه بر این دشواری‌ها انجام دهند اشتباه می‌فهمند.

در عین حال، سطح fairly عمیقی از توافق میان کارشناسانی که نظرشان برایم ارزش دارد دربارهٔ ایده‌های بنیادینی می‌بینم که اغلب — یا حداقل به‌اندازهٔ کافی clearly — بیان نمی‌شوند.

شاید وقت است دوباره دربارهٔ بعضی از آن بنیادها فکر کنیم. اصول مشترک رشتهٔ ما چیست؟ ایده‌هایی که دهه‌ها — نه فقط نسل فعلی ابزارهای فنی — درست می‌مانند؟

توسعهٔ نرم‌افزار کار ساده‌ای نیست و کار همگن نیست. اما شیوه‌هایی generic وجود دارد. راه‌هایی برای تفکر، مدیریت، سازماندهی و عمل در توسعهٔ نرم‌افزار که اثر قابل‌توجه — حتی چشمگیر — روی همهٔ این جنبه‌های مشکل‌دار دارد.

بقیهٔ این کتاب برای کاوش بعضی از این ایده‌های generic و ارائهٔ فهرستی از اصول بنیادین است که باید برای همهٔ توسعهٔ نرم‌افزار — هر حوزهٔ مسئله، هر ابزار، هر تقاضای تجاری یا کیفی — مشترک باشند.

ایده‌های این کتاب برایم چیزی عمیق و بنیادین دربارهٔ ماهیت تلاشمان را نشان می‌دهند.

وقتی این‌ها را درست انجام می‌دهیم — و بسیاری از تیم‌ها می‌کنند — بهره‌وری بیشتر، استرس و burnout کمتر در اعضای تیم، کیفیت بالاتر طراحی و تاب‌آوری بیشتر در سیستم‌هایی که می‌سازیم می‌بینیم.

سیستم‌هایی که می‌سازیم کاربرانشان را بیشتر راضی می‌کنند. باگ‌های dramatically کمتری در production می‌بینیم و تیم‌هایی که این ایده‌ها را به کار می‌برند تغییر تقریباً هر جنبهٔ سیستم‌هایشان را با تکامل یادگیری‌شان بسیار آسان‌تر می‌یابند. نتیجهٔ bottom-line معمولاً موفقیت تجاری بیشتر برای سازمان‌هایی است که این‌گونه عمل می‌کنند. این‌ها نشانه‌های engineering‌اند.

engineering توانایی خلاق بودن، ساخت چیزهای مفید، پیشروی با اطمینان و کیفیت را تقویت می‌کند. اجازه می‌دهد ایده‌ها را کاوش کنیم و در نهایت توانایی ساخت را scale کنیم تا سیستم‌های ever بزرگ‌تر و پیچیده‌تر بسازیم.

در آستانهٔ تولد رشتهٔ مهندسی واقعی برای نرم‌افزار هستیم. اگر این فرصت را بگیریم، می‌توانیم نحوهٔ عمل، سازماندهی و آموزش توسعهٔ نرم‌افزار را عوض کنیم.

شاید تغییری نسلی باشد، اما ارزشش برای سازمان‌هایی که ما را استخدام می‌کنند و برای جهان آن‌قدر عظیم است که باید تلاش کنیم. چه می‌شد اگر بتوانیم نرم‌افزار سریع‌تر و مقرون‌به‌صرفه‌تر بسازیم؟ چه می‌شد اگر آن نرم‌افزار هم باکیفیت‌تر، نگهداری آسان‌تر، سازگارتر، تاب‌آورتر و مناسب‌تر برای نیاز کاربرانش بود؟

خلاصه

در نرم‌افزار تا حدی معنای engineering را بازتعریف کرده‌ایم. قطعاً در بعضی حلقه‌ها engineering را چیز غیرضروری، سنگین و مانع «توسعهٔ واقعی نرم‌افزار» دیده‌ایم. engineering واقعی در رشته‌های دیگر هیچ‌کدام از این‌ها نیست. مهندسان در رشته‌های دیگر سریع‌تر — نه کندتر — پیشرفت می‌کنند. کار باکیفیت‌تر — نه پایین‌تر — می‌سازند.

وقتی رویکردی عملی، عقلانی، سبک و علمی به توسعهٔ نرم‌افزار می‌پذیریم، مزایای مشابه می‌بینیم. مهندسی نرم‌افزار مختص نرم‌افزار خواهد بود، اما به ما کمک می‌کند نرم‌افزار بهتری سریع‌تر بسازیم — نه مانع آن شود.

[^1]: Waterfall در توسعهٔ نرم‌افزار رویکردی مرحله‌ای و ترتیبی برای سازماندهی کار با شکستن آن به فازهای متمایز با handoverهای مشخص بین هر فاز است. ایده این است که هر فاز را به نوبت انجام دهید، نه تکرار کنید.

[^2]: در زمان نگارش SpaceX در حال توسعهٔ فضاپیمای reusable کاملاً جدید است. هدف سیستمی است که سفر به مریخ و زندگی روی آن و کاوش بخش‌های دیگر منظومه را ممکن کند. رویکرد مهندسی عمداً سریع و تکراری برای ساخت و ارزیابی سریع نمونه‌های اولیه پذیرفته — شکل extreme مهندسی طراحی در مرز دانش مهندسی و نمونهٔ fascinating از آنچه برای ساخت چیز تازه لازم است.

[^4]: منبع: «Peter Adler» (https://go.nasa.gov/1AKbDei)

[^5]: اندازهٔ اتم‌ها متفاوت است اما معمولاً در ده‌ها picometer (۱ × ۱۰^-۱۲ متر) اندازه‌گیری می‌شوند. پس بهترین صنعتگری دستی ۱۰ میلیون برابر از ماشین خوب کم‌دقت‌تر است.

[^6]: «How Fast is Real-time? Human Perception and Technology»، https://bit.ly/2Lb7pL1

[^7]: «The State of Continuous Integration Testing at Google»، https://bit.ly/3eLbAgB

[^8]: کتاب Accelerate توصیف می‌کند تیم‌هایی که رویکرد منضبط‌تری به توسعه دارند «۴۴٪ زمان بیشتر روی کار جدید» نسبت به تیم‌هایی که ندارند صرف می‌کنند. نگاه کنید به https://amzn.to/2YYf5Z8.

[^9]: Serverless computing رویکرد cloud-based برای ارائهٔ «functions as a service» است. functionها تنها واحد محاسبه‌اند و کد اجرای آن‌ها on demand راه می‌افتد.

[^10]: نسبت glide یکی از معیارهای کارایی ماشین پرنده است. نسبت بین مسافت طی‌شده و ارتفاع از دست‌رفته. مثلاً به ازای هر فوت (یا متر) نزول در glide (بدون نیرو)، ۸٫۳ فوت (یا متر) جلو می‌رود. نگاه کنید به https://en.wikipedia.org/wiki/Lift-to-drag_ratio.