۰:۰۰ — یک ساخت به پایان میرسد. عامل میگوید تمام شد، که ادعایی دربارهی نوشتن کد است، نه دربارهی اینکه آیا کد کار میکند یا نه. هر کاربر یک سازندهی هوش مصنوعی حداقل یک بار این شکاف بین این دو ادعا را حس کرده: پیشنمایش باز میشود، روی دکمهی سوم کلیک میشود، اتفاقی نمیافتد. دیدهام سالن دموی محصول درست در همان لحظه ساکت شده. پس پیش از اینکه هر انسانی یک ساخت را ببیند، حدود شش دقیقه از یک زنجیرهای میگذرد که با خودش بحث میکند. اینجا آنچه واقعاً اتفاق میافتد را، در قالب یک ساختی که دیدیم کج شد و بعد درست شد، دنبال میکنیم.
۰:۰۲ — بازبینی کد شروع میشود. نه عاملی که کد را نوشته و دارد تکلیف خودش را دوباره میخواند — یک عامل جداگانه، با پرامپتی متفاوت، بدون منفعتی در قبولشدن ساخت. این جداسازی بیشتر از آنچه به نظر میرسد اهمیت دارد. عاملی که ساعت ۲:۱۴ بعدازظهر تصمیم گرفته یک فراخوانی fetch بدون مدیریت خطا اشکالی ندارد، ساعت ۲:۱۵ هم اگر از او بخواهید کار خودش را بررسی کند، همچنان همینطور فکر میکند. یک بازبین تازه که به او گفته شود «آنچه خراب است را پیدا کن، فایل را ذکر کن» مثل همان مهندس ارشد اخمو رفتار میکند که واقعاً برای این کار میخواهید. در یک ساخت گذشته، این بازبین متوجه شد که مجموع سبد خرید بیصدا هیچوقت بهروزرسانی نمیشود — `updateTotal` در `Cart.jsx` تعریف شده بود اما هرگز به کنترلکنندهی تغییر تعداد متصل نشده بود، پس تابع وجود داشت و صرفاً هیچوقت اجرا نمیشد. این همان دستهای است که بازبینی کد برای آن است: چیزهایی که یک کامپایلر به آنها شانه بالا میاندازد.
۰:۰۴ — ممیزی امنیتی. عمداً محدودتر از آن چیزی است که به نظر میرسد — این یک تست نفوذ نیست، بلکه شکاری برای همان چند اشتباهی است که واقعاً در کد تولیدشده با هوش مصنوعی دیده میشوند. SQL ساختهشده با اتصال رشتهای. اعتبارسنجی فقط سمتکلاینت که انگار کل ماجراست. و ویژهی خانه: یک کلید API کدگذاریشده بهصورت ثابت، چون عاملی که این ویژگی را نوشته یک قرارداد متغیر محیطی جلوی خودش نداشته و به سمت چیزی رفته که کار میکرد. این مورد را آنقدر میبینیم که دیگر تقریباً غافلگیرمان نمیکند.
۰:۰۷ — لینکها و سئو. زرقوبرقی ندارد، و چیزی را میگیرد که هیچکس تا وقتی یک مشتری متوجه نشود، نمیبیند: یک لینک ناوبری که به /pricing اشاره میکند درحالیکه صفحه واقعاً در /price، یک ورودی نقشه سایت برای صفحهای که خطای ۴۰۴ میدهد، یک توضیح متا که هنوز متن جایگزین قالب را دارد. هیچکدام از اینها ساخت را خراب نمیکنند. اما همهشان بیسروصدا همان چیزی را نابود میکنند که بیشتر کاربران ما برای آن سایت ساختهاند — دیده شدن، کلیک گرفتن.
۰:۰۹ — دسترسیپذیری. این یک بررسی خودکار با axe-core است، نه یک ممیزی دستی کامل، و ارزش دارد که دربارهی معاملهای که این انتخاب میخرد صادق باشیم. axe-core نسبتهای کنتراست، متن جایگزین تصاویر گمشده، ورودیهای فرم بدون برچسب، و تلههای ترتیب تب را میگیرد — لایهی مکانیکی، چیزی حدود ۳۰ تا ۴۰ درصد از آنچه یک بازبینی کامل WCAG علامتگذاری میکند. تجربهی صفحهخوان که از نظر فنی مطابق است اما استفاده از آن واقعاً گیجکننده است را نمیگیرد. ما فقط خودکار را انتخاب کردیم چون ظرف چند ثانیه اجرا میشود و بیشتر آنچه از اینجا ساخته میشود، سایتهای تبلیغاتی و ابزارهای کوچک است، نه نوع برنامهای که یک ممیزی ناقص برای کسی خطر واقعی باشد.
۰:۱۱ — مطابقت. این لایه نمیپرسد «آیا این خوب است»، میپرسد «آیا با آنچه وعده داده شده مطابقت دارد». برنامه گفته چهار صفحه، ساخت سهتا ارائه داده — مطابقت همان چیزی است که متوجه میشود. برنامه یک فرم تماس کارآمد وعده داده، آنچه ارائه شده فرمی است بدون کنش ارسال — همان لایه، همان تشخیص. این مستقیمترین بررسی مرتبط با کاربر است، چون در برابر قصد بیانشدهی کاربر سنجیده میشود، نه در برابر یک مفهوم انتزاعی از کیفیت.
۰:۱۳ — بررسی درونمرورگری، و اینجا جایی است که ساخت ما واقعاً خراب شد. سختترین لایه برای فریب است چون کد نمیخواند، بلکه یک مرورگر واقعی را هدایت میکند — کلیک میکند، تایپ میکند، صبر میکند، بررسی میکند که DOM آنطور که باید تغییر کرده باشد، تغییر کرده. ساخت مورد بحث یک بازی بیکار (idle game) بود، و بازیها اینجا یک بررسی اضافه میگیرند چون یک بازی میتواند بینقص رندر شود اما غیرقابلبازی باشد — نمایشگر امتیاز میتواند کاملاً بیعیب به نظر برسد درحالیکه کاملاً از منطق امتیازدهی جدا شده. بازبین آن را بازی کرد. امتیاز درست بهروز میشد. صدا هیچ صدایی تولید نمیکرد.
سه دور، بعد یک تشدید
این یافته بهعنوان یک گزارش باگ به ما نرسید — مستقیماً وارد یک دور اصلاح شد، و زنجیره دوباره تأیید کرد، تا سه دور درون ساخت. دور اول: اصلاح به مقداردهی اولیهی میکسر دست زد، که از قبل درست بود، پس صدا همچنان بیصدا ماند. دور دوم: یک اصلاح متفاوت به یک حالت مرزیِ بارگذاری پرداخت که مجاور به نظر میرسید، و — این بیش از آنچه انتظار میرود اتفاق میافتد — یک مشکل کوچک جدید ایجاد کرد بدون آنکه مشکل اصلی را حل کند. دور سوم: همچنان بیصدا، و تا این مرحله معمولاً یا با چیزی واقعاً سخت روبهرو هستید یا با یک هشدار کاذب، و این از نوع سخت بود.
پس پلتفرم خودش تشدید کرد. یک اجرای اصلاح پیگیری را در صف گذاشت که کاملاً به یافتهی باقیمانده محدود بود، و روی یک کپی از ساخت کار میکرد نه خود ساخت — یعنی اجرای تشدید میتوانست شکست بخورد بدون آنکه نسخهی کاری موجودمان را از دست بدهیم. آن اجرا علت واقعی را پیدا کرد: یک پرچم بیصداسازی که در یک دور دیباگ قبلی تنظیم شده و هرگز برگردانده نشده بود، در فایلی کاملاً متفاوت از دو اصلاح قبلی. پرچم پاک شد، دوباره تأیید شد، قبول شد. هیچکس تا زمانی که این ساخت از قبل کار میکرد، به آن نگاه نکرد.
| سفارش | لایه | یافتهها |
|---|---|---|
| 1 | بازبینی کد | منطق خراب، کنترلکنندههای مرده، باگهای حالت |
| 2 | ممیزی امنیتی | سطوح تزریق، اسرار افشاشده، الگوهای ناامن |
| 3 | لینکها و سئو | لینکهای شکسته، فرادادهی گمشده، درستی sitemap و robots.txt |
| 4 | دسترسپذیری | axe-core خودکار: کنتراست، برچسبها، ناوبری با صفحهکلید |
| 5 | انطباق | آیا ساخت شامل چیزی است که برنامه وعده داده |
| 6 | بررسی درونمرورگری | ساخت را بهصورت واقعی اجرا میکند — کلیک میکند، تایپ میکند، واکنشش را میبیند |
چیزی که دفعهی بعد رد میکنم
چند ماه پیش از اجرای آن بازی بیکار، نسخهی نرمتری از کل سیستم را امتحان کردیم — بازبینها اجازه داشتند هر نگرانیای را با هر عبارتی مطرح کنند. نتیجه یافتههایی مثل «بهتر است این را در یک تابع کمکی استخراج کنید» و «نام این متغیر میتواند واضحتر باشد» بود، که بهنظر دقت میرسید اما چیزی را اصلاح نمیکرد. دورهای اصلاح، دورهای کامل را صرف صیقلدادن نثر میکردند بهجای اصلاح آنچه واقعاً خراب بود. قانون را سختتر کردیم به: یک فایل نام ببر، یک شکست را توصیف کن، یا چیزی نگو. خروجی بازبینها تقریباً نصف شد و تقریباً همهی موارد باقیمانده قابلاقدام بودند. اگر میخواستم این را از صفر بسازم، از نسخهی نرم صرفنظر میکردم و مستقیم به قانون شواهد میرفتم — لازم نبود این درس را بهشکل گرانبها یاد بگیریم، اما همین کار را کردیم.
این قانون هزینهی واقعی دارد، و آن را پنهان نمیکنم: یک نگرانی مبهم اما درست مثل «این طراحی API شش ماه دیگر گریبان کسی را میگیرد» حالا کنار گذاشته میشود، چون بازبین نمیتواند آن را به یک شکست مشخص متصل کند. با این معامله کنار آمدهایم. زنجیرهای که بازبینی معماری هم انجام میدهد، بهاندازهی کافی سریع نیست که روی هر ساخت اجرا شود، و سرعت کل هدف اجرای خودکار این کار بهجای درخواست از یک انسان است.
اگر کسی بپرسد، افزودن یک دور چهارم را هم رد میکنم. تعداد دورها را با ساختهای واقعی تنظیم کردیم، و ارزش افزودهی فراتر از دور سوم بهشدت افت میکند — دور اول بیشتر یافتههای قابلاصلاح را حل میکند، دور دوم بیشتر مشکلاتی را پاک میکند که دور اول ایجاد کرده، و تا دور سوم آنچه باقی میماند یا واقعاً سخت است یا اصلاً هیچوقت خراب نبوده. دور چهارم بیشتر زمان انتظار طولانیتری برای همان نتیجه میخرد.
هیچکدام از اینها رایگان نیست، و هیچکدام خطاناپذیر نیست. شش لایه بهعلاوهی هر تعداد دور اصلاحی که لازم است، به هر ساخت زمان واقعی اضافه میکند — تفاوت بین اتمام در کمتر از یک دقیقه و اتمام در چند دقیقه. فکر میکنیم این معاملهی درستی برای هرچیزی است که قرار است جلوی مشتریان خودتان بگذارید، اما «سریع» و «تأییدشده» در جهتهای مخالف میکشند، و ما تأییدشده را انتخاب کردیم. بازبینها هم مدلهای زبانی هستند، پس گاهی چیزی را که واقعاً خراب نیست، علامتگذاری میکنند، یا چیزی خراب را از دست میدهند. قانون شواهد و حلقهی چنددوری، تدابیر احتیاطی در برابر این هستند، نه تضمین.
آنچه در پایان میگیرید، یک سابقه است: کدام لایهها اجرا شدند، چه چیزی پیدا کردند، چه چیزی اصلاح شد، و چه چیزی برای قضاوت خودتان باقی مانده. این سابقه به محصول واقعی نزدیکتر از کد است — این تفاوت بین اعتماد به یک ساخت چون تمامشده بهنظر میرسد و اعتماد به آن چون چیزی خصمانه پیشتر تلاش کرد آن را بشکند و شکست خورد.



