فرض کنید چند هفته است در چت یک اپ لیست کارها میسازید. الان یک وبسایت است — React، یک دیتابیس، چیز خاصی نیست. تایپ میکنید «برام یک نسخه اندروید بساز» و اینتر میزنید. اینجا واقعاً چه چیزی بین آن دکمهفشردن و رسیدن یک فایل .aab به پوشه دانلودهایتان اتفاق میافتد، چون بیشتر پلتفرمها این بخش را نشانتان نمیدهند، و همان بخش پنهانشده جایی است که همیشه درد اصلی در آن بوده.
فایل .aab اندروید
اول Gradle کنترل را به دست میگیرد. ماژولهای بومی اپ شما — دسترسی به دوربین، ذخیرهسازی محلی، هر پلاگینی که در بیلد وارد شده — هر کدام مشخص میکنند با کدام نسخه NDK کامپایل شدهاند، و این اعلامها همیشه با هم همخوانی ندارند. من دیدهام ماژولی که با NDK r25 ساخته شده از لینکشدن با ماژولی که فرض کرده r26 است سر باز میزند، و خطایی که میدهد نمیگوید «عدم تطابق نسخه»، بلکه چیزی درباره یک سیمبل گمشده در عمق سه لایهای یک فایل .so میگوید. نسخههای Kotlin کار خبیثانهتری میکنند: نسخهای که داخل یک ماژول Gradle پین شده میتواند بیصدا نسخه اعلامشده در بالای اسکریپت بیلد شما را سایه بیندازد، و بیلد موفق میشود — فقط باینریای تولید میکند که روی نسخههای خاصی از اندروید در عمل کرش میکند. هیچکدام از اینها عجیب نیست. این هزینه استاندارد شیپکردن اندروید بومی است، و همین است که تیمها یک نفر استخدام میکنند که کارش فقط دانستن این باشد که کدام فلگ خطای این هفته را ناپدید میکند.
بیلد اینجا زنجیره ابزار واقعی را اجرا میکند و خودش این کار حلکردن را انجام میدهد:
- تعارضهای وابستگی پیش از تبدیلشدن به کرش زمان اجرا شناسایی میشوند
- پیکربندی زنجیره ابزاری که هر بار یک خطای جدید چیزی یادش میدهد و بهتر میشود
یک نسخه تقلبی سریعتر از این — چیزی که یک .aab را شکل میدهد بدون اجرای واقعی تسکهای Gradle — در کمتر از یک دقیقه شیپ میشد. اما همان لحظهای که اپ شما به یک سرویس پسزمینه یا کتابخانه رمزنگاری بومی نیاز داشته باشد از کار میافتد، و بازبینی Play Store در عرض یک روز آن را پرچم میکند. ما ترجیح میدهیم آن بیست دقیقه را صرف کنیم.
فایل .dmg مک
دو بیلد در این یک فایل قرار میگیرد. ابزارهای خط فرمان Xcode یک باینری Apple Silicon و یک باینری اینتل را جدا کامپایل میکنند، سپس lipo آنها را در یک اجرایی یونیورسال واحد میچسبانند.
وسوسهکننده است — فقط یک باینری بسازید و تمام — تا وقتی یادتان بیاید که خیلیها لپتاپ کارفرمای خود را استفاده میکنند، نه سختافزار انتخابی خودشان، و آن لپتاپ ممکن است سهساله و اینتل باشد. بهجای اینکه کاربر بفهمد چه چیپی دارد (بیشترشان نمیدانند)، ما هر دو را شیپ میکنیم و میگذاریم سیستمعامل بیصدا انتخاب کند. جایگزین، که اوایل کار امتحانش کردیم، کراس-کامپایل همهچیز از یک ماشین لینوکس با زنجیره ابزار شبیهسازیشده است. سریعتر است. اما همینطور است که به یک باگ حاشیهای امضای کد میرسید که فقط روی سختافزار واقعی macOS 12 بروز میکند، شش هفته بعد از شیپ، توسط کاربری گزارش میشود که سردرگم است و نمیداند چرا اپش باز نمیشود.
نصبکننده ویندوز
اینجا همان لحظهای است که تجربه اولین اجرا مشخص میکند کاربر شما آیا به اپ اعتماد میکند یا نه. Windows SmartScreen هنوز اعتباری نزد سرورهای مایکروسافت برای نصبکننده شما ندارد، پس یک صفحه آبی «Windows protected your PC» نشان میدهد با دکمهای که بهصورت پرمانند نوشته «Don't run» و لینک کمرنگی به نام «More info» که با کلیک روی آن، «Run anyway» ظاهر میشود. macOS نسخه خودش از همین بازی را اجرا میکند: راستکلیک، Open، تأیید، چون اپهای خارج از App Store هم بهطور پیشفرض قابلاعتماد شناخته نمیشوند. اوایل کار، هر دوی اینها را به یک صفحه سؤالات متداول عمومی لینک میدادیم. تیکتهای پشتیبانی نشانمان داد که این روش کار نمیکند — کسی که به صفحهای زل زده که میگوید دانلودش شاید بدافزار باشد، نمیرود سند بخواند، اسکرینشات میگیرد و میپرسد آیا هک شده است. پس مسیر نصب سیستمعامل را تشخیص میدهد و دقیقاً سه کلیک لازم را نشان میدهد، بدون نیاز به سؤالات متداول. جزئیات کوچکی است، اما خود فایل هم مهم است: نام دانلود بر اساس نام محصول شما است، نه بر اساس نام یک artifact بیلد. هیچکس نباید در چت پشتیبانی توضیح دهد که فایلی به نام «app-release-signed-v2-final.exe» دانلود کرده و نمیداند آیا فایل درست است یا نه.
مانیفست افزونه مرورگر
این یکی در کل این خط تولید عجیب و متفاوت است — بدون Gradle، بدون NDK، بدون مرحله کامپایل به معنای معمول. چیزی که بهجای آن دارد یک مانیفست است، و مانیفست یک مذاکره است با بازبین Chrome Web Store که هرگز مستقیم با او صحبت نمیکنید.
| مجوز درخواستی | نتیجه بازبینی |
|---|---|
| <all_urls> (فراتر از نیاز واقعی ویژگی) | دو هفته رفتوبرگشت با کسی که دقیقاً نمیگوید ایراد چه بود |
| activeTab (محدود به نیاز واقعی) | همان روز تسویه میشود |
MV3 همچنین چیزی را که MV2 آسان کرده بود پیچیده میکند: سرویسورکرهای پسزمینه طبق طراحی، وسط کار آنلود میشوند، تصمیم سیاست گوگل برای عمر باتری، و ویژگیای که باید از این وضعیت جان بهدر ببرد باید بهگونهای ساخته شود که با آن هماهنگ باشد، نه در تضاد با آن. ما بهطور پیشفرض هر افزونه را محدود به کوچکترین مجموعه مجوزی که واقعاً برایش لازم است میکنیم و فقط وقتی یک ویژگی خاص لازم دارد آن را گسترش میدهیم.
کیستور
زیر بیلد اندروید، یک artifact نشسته که هرگز نمیبینید و نمیتوانید از دستش بدهید: کلید امضا. اگر آن را از دست بدهید، فقط توانایی بهروزرسانی اپ خود را از دست نمیدهید — توانایی بهروزرسانی آن تحت هویت فعلیاش را برای همیشه از دست میدهید، بدون هیچ راه بازیابیای که گوگل به شما بدهد، هرگز. زیرساخت پرزرقوبرقی نیست. یک فایل است. اما تفاوت بین شیپکردن نسخه شش ماه بعد بهعنوان یک بهروزرسانی بیدرز و شیپکردن آن بهعنوان یک لیستینگ کاملاً جدید که از صفر نصب و صفر نظر شروع میشود، همین است. ما به ازای هر پروژه یکی میسازیم و نگهش میداریم تا هر بیلد آینده با همان کلید روز اول امضا شود.
رشته گفتگویی زیر همه اینها
هیچکدام از موارد بالا در یک «پروژه موبایل» جدا زندگی نمیکند. همان گفتگویی است که وباپ را ساخته. یک تغییر UI بخواهید، بیلد وب بهروز میشود؛ بعد بسته اندروید بخواهید، از همان وضعیت فعلی کامپایل میشود، نه از یک فورک که سه هفته پیش از همخوانی خارج شده. بیشتر تیمهایی که دیدهام بعداً سعی میکنند بومی را وصله کنند، به دو کدبیس ختم میشوند که از هم فاصله میگیرند — یک وباپ که هر روز شیپ میشود و یک رَپر بومی که کسی باید یادش باشد پیش از هر ریلیز بهروزش کند. این فاصله جایی است که کهنگی زندگی میکند، و دقیقاً همان چیزی است که یک تاریخچه بیلد واحد از بین میبرد. البته این قضیه دو طرفه است: اگر چت اخیراً شلخته و سریع بوده، بیلد اندروید همان را هم به ارث میبرد. این یک پاس صیقل جداگانه نیست، یک کامپایل مستقیم از هرچه واقعاً وجود دارد است — که در عمل مردم را صادق نگه میدارد، چون هیچ side quest «قبل از ارسال تمیزش میکنیم» برای رد کردن وجود ندارد.
وقتی برای فروشگاه آماده شد، مسیر ارسال به لیستینگ Play Store خودتان و اکانت Apple Developer خودتان تحویل داده میشود. نه ما. ما نمیخواستیم بین شما و توزیع خودتان قرار بگیریم.



