נניח שבניתם אפליקציית משימות בצ'אט במשך כמה שבועות. כרגע זה אתר — React, מסד נתונים, שום דבר מיוחד. אתם מקלידים "תעשה לי גרסת Android" ולוחצים Enter. הנה מה שקורה בפועל בין הקשה הזו לבין קובץ .aab שנוחת בתיקיית ההורדות שלכם, כי רוב הפלטפורמות לא מראות לכם את החלק הזה, והחלק שהן מסתירות הוא בדיוק המקום שבו נהג לגור כל הכאב.
קובץ ה-.aab של Android
Gradle משתלט קודם. המודולים הנייטיביים של האפליקציה שלכם — גישה למצלמה, אחסון מקומי, כל תוסף שהבילד משך — כל אחד מצהיר נגד איזו גרסת NDK הוא הודר, וההצהרות האלה לא תמיד מסכימות ביניהן. ראיתי מודול שנבנה נגד NDK r25 מסרב להתחבר למודול שהניח r26, והשגיאה שהוא זורק לא אומרת "אי-התאמת גרסאות", היא אומרת משהו על סמל חסר שלוש שכבות לתוך קובץ .so. גרסאות Kotlin עושות משהו יותר ערמומי: גרסה שננעלה בתוך מודול Gradle אחד יכולה להאפיל בשקט על זו שהוצהרה בראש סקריפט הבילד שלכם, והבילד מצליח — הוא פשוט מייצר בינארי שקורס בגרסאות Android ספציפיות בשטח. שום דבר מזה לא אקזוטי. זה המס הסטנדרטי של שילוח Android נייטיב, וזו הסיבה שצוותים שוכרים אדם שכל התפקיד שלו הוא לדעת איזה דגל גורם לשגיאה של השבוע הזה להיעלם.
הבילד כאן מריץ את שרשרת הכלים האמיתית ובולע את עבודת הפתרון הזו בעצמו:
- התנגשויות תלויות שנתפסות לפני שהן הופכות לקריסת ריצה
- הגדרת שרשרת כלים שמשתפרת בכל פעם ששגיאה חדשה מלמדת אותה משהו
גרסה מהירה ומזויפת יותר של זה — משהו שמעצב .aab בלי להריץ משימות Gradle אמיתיות — הייתה מושקת תוך פחות מדקה. היא גם הייתה קורסת ברגע שהאפליקציה שלכם צריכה שירות רקע או ספריית קריפטוגרפיה נייטיבית, וביקורת Play Store הייתה מסמנת אותה תוך יום. אנחנו מעדיפים להשקיע את עשרים הדקות.
קובץ ה-.dmg של macOS
שני בילדים נכנסים לקובץ הזה. כלי שורת הפקודה של Xcode מקמפלים בינארי Apple Silicon ובינארי Intel בנפרד, ואז lipo מדביקים אותם יחד לקובץ הרצה אוניברסלי אחד.
זה מפתה — לשלוח בינארי אחד ולסגור עניין — עד שנזכרים שהרבה אנשים מריצים את המחשב הנייד של המעסיק שלהם, לא בחירת חומרה משלהם, ושהמחשב הזה יכול להיות בן שלוש שנים ומבוסס Intel. במקום לגרום למשתמש לברר איזה שבב יש לו (רוב לא יודעים להגיד), אנחנו משלחים את שניהם ונותנים למערכת ההפעלה לבחור בשקט. החלופה, שניסינו בהתחלה, היא קומפילציה צולבת של הכול ממחשב לינוקס באמצעות שרשראות כלים מדומות. זה מהיר יותר. זו גם הדרך שבה מגיעים למקרה קצה בחתימת קוד שמופיע רק על חומרת macOS 12 אמיתית, שישה שבועות אחרי השילוח, ומדווח על ידו משתמש מבולבל שאין לו מושג למה האפליקציה שלו לא נפתחת.
המתקין של Windows
כאן חוויית ההרצה הראשונה קובעת אם המשתמש שלכם בכלל סומך על האפליקציה. Windows SmartScreen עוד לא מכיר את המתקין שלכם — הוא לא בנה מוניטין מול השרתים של Microsoft — אז הוא מציג מסך כחול "Windows הגן על המחשב שלך" עם כפתור שכתוב "אל תריץ" בהדגשה וקישור "מידע נוסף" בקושי נראה, שברגע שלוחצים עליו, חושף "הרץ בכל זאת". macOS עושה גרסה משלו לריקוד הזה: קליק ימני, פתח, אשר, כי גם אפליקציות מחוץ ל-App Store לא מהימנות כברירת מחדל. בהתחלה קישרנו את שני אלה לדף שאלות נפוצות כללי. כרטיסי תמיכה לימדו אותנו שזה לא עובד — מישהו שמביט במסך שאומר שההורדה שלו עשויה להיות תוכנה זדונית לא הולך לקרוא תיעוד, הוא מצלם מסך ושואל אם נפרץ. אז תהליך ההתקנה מזהה את מערכת ההפעלה ומראה בדיוק את שלוש הלחיצות הנדרשות, בלי צורך בשאלות נפוצות. פרט קטן, אבל גם הקובץ עצמו חשוב: ההורדה נקראת על שם המוצר שלכם, לא על שם קובץ בילד. אף אחד לא צריך להסביר בתמיכה בצ'אט שהוא הוריד "app-release-signed-v2-final.exe" ולא יכול לדעת אם זה הקובץ הנכון.
המניפסט של תוסף הדפדפן
זה החריג בכל צנרת הבנייה — אין Gradle, אין NDK, אין שלב קומפילציה במובן הרגיל. מה שיש במקום זה הוא מניפסט, והמניפסט הוא משא ומתן עם בודק של Chrome Web Store שלעולם לא תדברו איתו ישירות.
| הרשאה מבוקשת | תוצאת הביקורת |
|---|---|
| <all_urls> (רחב יותר ממה שהפיצ'ר צריך) | התכתבות של שבועיים עם מישהו שלא אומר בדיוק על מה הוא התנגד |
| activeTab (מוגבל לצורך בפועל) | נסגר באותו יום |
MV3 גם מסבך משהו ש-MV2 הפך לקל: שירותי רקע נטענים מחוץ לזיכרון באמצע משימה בכוונה, החלטת מדיניות של גוגל שמכוונת לחיי סוללה, ופיצ'ר שצריך לשרוד את זה חייב להיבנות סביב זה ולא נגדו. אנחנו מגדירים כברירת מחדל לכל תוסף את קבוצת ההרשאות הצרה ביותר שהפונקציונליות בפועל שלו צריכה, ומרחיבים רק כשפיצ'ר ספציפי דורש זאת.
מפתח החתימה (keystore)
מתחת לבילד ה-Android יושב הפריט היחיד שאתם לעולם לא רואים ולא יכולים להרשות לעצמכם לאבד: מפתח החתימה. תאבדו אותו ולא רק תאבדו את היכולת לעדכן את האפליקציה שלכם — תאבדו את היכולת לעדכן אותה תחת הזהות הקיימת שלה, לצמיתות, בלי שום נתיב שחזור שגוגל תעניק לכם, אף פעם. זו לא תשתית מרשימה. זה קובץ. אבל זה ההבדל בין לשלח את הגרסה השישית בעוד חצי שנה כעדכון חלק לבין לשלח אותה כרישום חדש לגמרי שמתחיל מאפס התקנות ואפס ביקורות. אנחנו מייצרים אחד לכל פרויקט ושומרים עליו כך שכל בילד עתידי יחתום עם אותו המפתח כמו ביום הראשון.
שרשור הצ'אט מתחת לכל זה
שום דבר מהאמור לעיל לא חי ב"פרויקט מובייל" נפרד. זו אותה שיחה שבנתה את אפליקציית האינטרנט. בקשו שינוי ממשק, בילד האינטרנט מתעדכן; בקשו חבילת Android בהמשך, והיא מתקמפלת מאותו מצב נוכחי, לא מפיצול שהתרחק מסנכרון לפני שלושה שבועות. רוב הצוותים שראיתי מנסים להוסיף נייטיב בשלב מאוחר יותר מסיימים עם תחזוקה של שתי בסיסי קוד שמתרחקים זה מזה — אפליקציית אינטרנט שמשתלחת כל יום ועטיפה נייטיבית שמישהו צריך לזכור לעדכן לפני כל שחרור. שם, בפער הזה, גר הבלאי, וזה בדיוק מה שהיסטוריית בילד אחת מסלקת. אבל זה חד-צדדי: אם הצ'אט היה מהיר ורשלני לאחרונה, בילד ה-Android יורש גם את זה. זה לא מעבר ליטוש נפרד, זו קומפילציה ישירה של מה שבאמת שם — מה שבפועל שומר על אנשים ישרים, כי אין "נסדר את זה לפני ההגשה" כמשימת צד לדלג עליה.
כשזה מוכן לחנות, נתיב השילוח מעביר לרישום ה-Play Store שלכם ולחשבון ה-Apple Developer שלכם. לא שלנו. לא רצינו לשבת בינכם לבין ההפצה שלכם.



