loading icon
בסיס קוד אחד, כל הפלטפורמות: המסע שלנו לבניית אפליקציית אינטרנט מתקדמת

בסיס קוד אחד, כל הפלטפורמות: המסע שלנו לבניית אפליקציית אינטרנט מתקדמת

PWA היא טכנולוגיה חדשה בה נבנה WebApp שלנו, נסביר עליה מעט.

21 בפברואר 2023

לפני כשנתיים ישבנו בחדר ישיבות קטן (נכון יותר לקרוא לו "הפינה הפנויה במשרד") ודנו בבעיה שהטרידה אותנו כבר חודשים: איך בונים מוצר וידאו-צ'אט שעובד טוב באותה מידה על דסקטופ, מובייל, iOS ו-Android - בלי לשרוף את כל התקציב השנתי שלנו על שלוש צוותי פיתוח נפרדים?

התשובה שהגענו אליה, אחרי לא מעט ויכוחים וכמה פרוטוטייפים כושלים, הייתה Progressive Web App - PWA. במאמר הזה נספר את הסיפור האמיתי מאחורי ההחלטה הארכיטקטונית הזו: מה שיקלנו, איפה טעינו, ואיך בסוף הגענו למוצר שאנחנו גאים בו.

נקודת ההתחלה: שלוש אפליקציות או אחת?

כחברה קטנה יחסית, לא היה לנו את הפריווילגיה להחזיק צוות iOS, צוות Android וצוות Web בנפרד - וגם לא רצינו בה, גם אם היה לנו תקציב. ראינו יותר מדי חברות נתקעות בסנכרון פיצ'רים בין שלושה בסיסי קוד שונים, כשכל שינוי קטן הופך לפרויקט של שלושה ספרינטים.

השקלנו כמה כיוונים: React Native, Flutter, ולבסוף - PWA. מה שהטה את הכף לטובת PWA היה שילוב של כמה גורמים: קודבייס אחד שרץ בכל מקום שיש בו דפדפן מודרני, אין צורך לעבור אישור חנויות אפליקציות בכל עדכון (קריטי כשרוצים לשחרר תיקון דחוף), ויכולת להתקין את האפליקציה ישירות ממסך הבית בלי חיכוך של הורדה מה-App Store.

למה לא React או Angular "סתם ככה"?

לא פסלנו פריימוורקים כמו React או Angular - אנחנו משתמשים ברכיבים מהם במקומות שונים במערכת. אבל כשזה הגיע לליבה של ה-PWA עצמה, הגענו למסקנה שבמקרה שלנו, שכבת ה-abstraction שהם מוסיפים דווקא עמדה בדרכנו. היינו צריכים שליטה מדויקת וישירה על Service Worker, על אסטרטגיות קאשינג, ועל ניהול state בין טאבים - וזה דרש מאיתנו לרדת קרוב יותר ל-Web APIs עצמם במקום להתמודד עם עוד שכבת הפשטה שלא תמיד "מבינה" מה אנחנו מנסים לעשות.

ההחלטה הזו לא הייתה קלה, וגם לא הסכמנו עליה פה אחד בצוות. היו מי שטענו שאנחנו "ממציאים מחדש את הגלגל" - ובמובן מסוים הם צדקו. אבל הגלגל הזה היה צריך להתאים בדיוק למערכת ההודעות בזמן אמת שלנו, ולא מצאנו ספרייה קיימת שעשתה את זה בלי overhead מיותר.

האתגר האמיתי: Service Workers וניהול אירועים

אם תשאלו את מי מהמתכנתים אצלנו מה היה הכי מתסכל בפרויקט הזה, התשובה כמעט תמיד תהיה אותו דבר: ניהול אירועים דרך ה-Service Worker. ה-Service Worker רץ בthread נפרד מהדף עצמו, וזה אומר שכל תקשורת בינו לבין הממשק צריכה לעבור דרך ערוצי הודעות (postMessage, BroadcastChannel) - וזה הופך דיבאגינג לחוויה שונה לגמרי ממה שרוב המפתחים רגילים אליה.

בהתחלה ניסינו להשתמש בספריות קיימות לניהול אירועים מול Service Worker, אבל גילינו שאף אחת מהן לא התמודדה טוב עם התרחיש הספציפי שלנו: עדכוני נוכחות בזמן אמת (מי מחובר, מי בשיחה) שצריכים להגיע לכל הטאבים הפתוחים של אותו משתמש בו-זמנית, גם אם רק אחד מהם "פעיל". בסוף בנינו שכבת אירועים משלנו מעל BroadcastChannel API, עם תור הודעות פנימי שמטפל גם במקרים של טאב שנסגר באמצע תקשורת.

IndexedDB: האויב הכי שקט בפרויקט

האתגר השני הגדול היה IndexedDB - מסד הנתונים שרץ בצד הלקוח ומאפשר ל-PWA לשמור מידע גם כשאין חיבור לרשת. ה-API שלו, בלשון המעטה, לא נבנה בשביל נוחות מפתחים: מבוסס על cursors, עם callbacks מקוננים שמזכירים קוד מ-2010. בנינו שכבת wrapper דקה שהפכה את זה ל-Promise-based, וזה שיפר דרמטית את חוויית הפיתוח הפנימית שלנו - אבל עדיין לקח לנו כמה סבבים להבין את ההתנהגות הנכונה של טרנזקציות כשכמה טאבים כותבים לאותו store בו-זמנית, במיוחד בזמן שיחת וידאו פעילה שבה כל שנייה חשובה.

שיקול עיצוב שלא ציפינו שיהיה כזה מורכב: ריבוי טאבים

משהו שלקח אותנו בהפתעה היה כמה חשיבה נדרשה כדי שהמוצר יתנהג נכון כשמשתמש פותח כמה טאבים של האתר במקביל. לא רצינו שמשתמש יצטרך "לחפש" באיזה טאב השיחה שלו פעילה, או שהוא יקבל התראות כפולות מכל טאב בנפרד. הפתרון היה לבחור טאב אחד כ"מוביל" (leader election דרך Web Locks API), כשרק הוא מנהל את חיבור ה-WebSocket בפועל, ומשדר עדכונים לשאר הטאבים דרך אותה שכבת BroadcastChannel שתיארנו קודם.

זה נשמע פשוט כשמתארים את זה במשפט אחד - אבל בפועל, זה דרש כמה שבועות של עבודה, כולל טיפול במקרי קצה כמו טאב מוביל שנסגר בפתאומיות, שבו טאב אחר צריך "לתפוס פיקוד" תוך כדי שהמשתמש עדיין באמצע שיחה.

מה למדנו בדרך

אם יש משהו אחד שהיינו אומרים לעצמנו של לפני שנתיים, זה: תתחילו לבדוק PWA Lighthouse audits הרבה יותר מוקדם בתהליך, לא רק לפני release. גילינו לא מעט בעיות ביצועים - במיוחד סביב גודל ה-bundle הראשוני וזמן טעינת ה-Service Worker - שהיו הרבה יותר זולות לתקן כשזיהינו אותן מוקדם, לעומת מה שקרה כשניסינו לדחוס אופטימיזציות בשבוע האחרון לפני שחרור.

דבר נוסף שלמדנו: תיעוד פנימי חוסך זמן באופן בלתי פרופורציונלי בפרויקט כמו זה. כשבנינו מערכת כה "מותאמת אישית" ולא סטנדרטית, כל מפתח חדש שהצטרף לצוות נזקק להרבה יותר זמן הכוונה מאשר אם היינו עובדים עם React רגיל - אז השקענו זמן בכתיבת README מפורטים לכל מודול, וזה החזיר את עצמו כבר אחרי החודש הראשון.

לסיכום: המסע שעדיין נמשך

המסע לבניית ה-PWA שלנו לא היה קל, ובטח לא ליניארי - היו לא מעט רגעים שבהם שקלנו לוותר ולחזור לפתרון "בטוח" יותר עם שלוש אפליקציות נפרדות. אבל התוצאה - קודבייס אחד שמריץ את החוויה שלנו בכל מקום, מתעדכן ברגע ששחררנו קוד חדש, ונטען מהר גם ברשתות חלשות - שווה כל אחד מהרגעים המתסכלים האלה.

אנחנו עדיין רחוקים מלהיות "מומחי PWA" מושלמים, ואנחנו לומדים דברים חדשים כל חודש. אבל אנחנו גאים להיות חלק מקהילה קטנה יחסית של חברות שמהמרות ברצינות על העתיד הזה של האינטרנט - מוצר אחד, קוד אחד, שרץ בכל מקום שיש בו דפדפן.

תגובות

    הוספת תגובה

    שם מלא
    דוא"ל
    תוכן התגובה

    מאמרים קשורים

    הצטרפו לניוזלטר

    קבלו את הפוסטים החדשים ישירות לאימייל

    כתובת אימייל