עשה זאת בעצמך: NoSQL

מעוניינים לשדרג משהו? לעתים זול יותר ופשוט יותר לבצע שיפוץ קטן בעצמכם, במקום להשתמש בבעל מקצוע. ייתכן והתוצאה תהיה מוצלחת לא-פחות.
בפוסט זה אני רוצה לשתף במימוש מוצלח של "טכניקת NoSQL BIG DATA" שביצענו על גבי מערכת קיימת, מבלי לשנות אותה באופן מהותי ומבלי להחליף את בסיס הנתונים הרלציוני הקיים.המסר המעניין מבחינתי, הוא שניתן ליישם ״בקטנה״ רעיונות של בסיסי הנתונים NoSql בעצמכם – ולהשיג תוצאות יפות.

הבעיה

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

בדיקות שעשינו למערכת הראו שבסביבות 30-אלף פרויקטים, המערכת מתחילה להראות סימנים של שבירת הלינאריות ב scalability. כלומר: עד נקודה זו – אם רצו לנהל עוד פרויקטים היה ניתן להוסיף עוד חומרה ביחס ישר לגדילה בכמות הפרויקטים / הפעילות. מעבר לנקודה זו היה צריך להוסיף x וקצת חומרה ל x פעילות נוספת, וככל שהמספר גדל – העלות השולית הלכה וגדלה.
הבנו שהמערכת תוכל לטפל במשהו כמו 50 אלף עד 100 אלף פרויקטים, תלוי בכמות החומרה שהלקוח יסכים להקצות. באופן מעשי זהו בערך גבול ה Scalability שלנו ולכן ההמלצה ללקוחות הייתה לא ליצור מעל 50 אלף פרויקטים.חשוב להבהיר שמדובר במערכת בת כ3 שנים – שעברה לאורך חייה לא מעט שיפורי performance ו scalability. בשלבים הראשונים של המערכת הצלחנו לבצע שיפור יחיד שהגדיל את ה Scalability ב 30% – אך ככל שהזמן עבר שיפרנו אלמנטים פחות משמעותיים (כיוון שהמשמעותיים כבר שופרו) והבנו שאנו מגיעים לקצה ה Scalability של הארכיטקטורה הקיימת.

פתרון אפשרי אחד היה לנסות לעבור לבסיס נתונים NoSQL, נוסח MongoDB או CouchDB – המתאימים יותר לשימוש הספציפי של המערכת, והיו יכולים בהחלט לשפר את המצב. הבעיה: שאר האלמנטים במערכת (מלבד הפרויקטים) התנהלו בצורה משביעת-רצון בבסיס הנתונים הרלציוני. מה עושים? עושים הסבה לכל הקוד לעבוד מול בסיס נתונים NoSql או דורשים מלקוחות לנהל 2 בסיסי-נתונים שונים במקביל?!

התוצאות

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

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

מקור הבעיה

ישנן סיבות שונות המצדיקות מעהר לNoSql Databases:

  • הרצון בסכמה גמישה, שלא דורשת migration בין גרסאות.
  • כמות נתונים (ב TB) שדורשת מעבר משרת אחת לכמה שרתים – מה שנקרא Scale Out.
  • בעיית Scalability מקומית. כלומר: מעל כמות נתונים מסוימת, זמן התגובה למשתמש הקצה הופך ללא-סביר.
    זו הבעיה איתה התמודדנו במערכת שלנו.

איך נוצרת בעיית Scalability?
בואו נביט על (הפשטה של) סכמת הנתונים של מערכת הפרויקטים:

האובייקטים החשובים, הבנויים בהיררכיה הם: פרויקט, נושא, דיון, תגובה ותכונה-של-תגובה.

אני רוצה להתמקד לרגע באובייקט קצת פחות טריוויאלי במודל הנתונים: Comment Attribute.
תכונה-של-תגובה (Comment Attribute) יכולה להיות דבר כמו: תאריך, שם המגיב, קישור לתמונה וכו'

בבסיס נתונים רלציוני ניתן לשמור תכונות כאלו ב-2 אופנים:

  • סכמה קשיחה: כעמודה (column) בטבלה. על כל תכונה אפשרית יוצרים עמודה חדשה.
    יתרונות: פשטות
    חסרונות: יש תכונות (כגון "deleted by admin") שמתרחשות לעתים נדירות – אך עדיין יש לשמור עבורן מקום בכל רשומה, הוספת שורה = הוספת סכמה.
  • סכמה גמישה: כטבלה נוספת, בתצורת master-detail, בה כל מפתח וערך של תכונה היא שורה נוספת.
    יתרונות: גמישות רבה ללא שינויי סכמה
    חסרונות: עוד טבלה לנהל, עוד קצת סיבוך.

המערכת הנ"ל השתמשה בסכמה גמישה.

נתונים לדוגמה, בסכמה גמישה של תכונות אובייקט (לחצו להגדלה)

שתי הגישות, הן בעייתיות בהיבט של Scalability והניתוח מה עדיף הוא לא טריוויאלי. נושא זה הוא מעבר ל scope של הפוסט הנוכחי.

ככלל אצבע: כשאנו מגיעים ל 10M או יותר רשומות בטבלה אחת בבסיס הנתונים – בסיס הנתונים מתחיל להגיב פחות טוב, במיוחד עם זו טבלה בה הנתונים הם לא רציפים (פרטים על כך בהמשך) ו/או יש ריבוי של אינדקסים.

חזרה לתיאוריה

בואו נתתבונן על שכבת הנתונים (כגון Hibernate) ובסיס הנתונים ביחד – כמקשה אחת, שהיא כקופסה שחורה שאנו מתעלמים ממה שקורה בתוכנה.
סוג השיפור שאני מציע בפוסט זה כנראה לא יעלה מה DBA הטיפוסי. רוב ה DBAs (מהניסיון שלי), מנסים להשיג שיפורים בעזרת שינויים בבסיס הנתונים בלבד, ומבלי לשקול שינויים של הקוד הקיים. סוג של תקרת זכוכית שבאה כנראה עם התפקיד.

הנה טבלה חשובה למדי:

מקור: גוגל

שתי נקודות שהייתי רוצה להדגיש:

  • קריאה מדיסק היא הפעולה היקרה ביותר ברשימה (נו, טוב – מלבד WAN בין-יבשתי), והיא יקרה משמעותית מפעולות מבוססות זיכרון (פי 100 עד פי 100-אלף, תלוי בתסריט).
    אם אנו מתבוננים על שכבת ה Persistence כקופסה שחורה, אזי כדאי לנו מאוד להפחית קריאות לדיסק, גם על חשבון הרבה פעולות בזיכרון. כלומר: להבין מתי בסיס הנתונים או שכבת ה ORM גורמות לקריאות לדיסק להתרחש – ולגרום לקריאות אלו לפחות ככל האפשר.
  • בניגוד לזיכרון, בו יש פער גדול בין גישה כלשהי (100ns) לקריאת 1MB של נתונים (250K ns, פי 2500), בקריאה מדיסק הפער הוא רק פי – 2. כלומר: להביא 1MB של נתונים רציפים לוקח כמו הבאה של 2 חתיכות של 4k.
    הסיבה לפער זה הוא שזמן גישה (seek time) בדיסק כוללת תנועה של זרועה מכנית וסיבוב הדיסק לנקודה הנכונה, משם קריאה רציפה היא כבר "לא סיפור".
    הערה: מגבלה זו השתפרה מאוד עם הצגת כונני ה SSD המודרניים. ניתן לקרוא עוד בנושא בפוסט מבט מפוכח על מהפכת ה SSD. עדיין, קריאה מדיסק ובמיוחד קריאה בלתי-רציפה, היא יקרה למדי. בפוסט הנ"ל ניתן לראות כונן SSD שקורא 180MB בשנייה באופן רציף, אך רק 18MB בשנייה כאשר המידע מפוזר. יחס קצב העברת-נתונים של פי 10-15 בין קריאה רציפה לקריאה אקראית הוא מאפיין שכיח בכונני SSD מודרניים. יחס זה הוא בערך פי 100-200 בכוננים קלאסיים – כך שמדובר בשיפור גדול.
כיצד נגרום לבסיס הנתונים לבצע משמעותית פחות קריאות לדיסק, מבלי לגעת בקוד של ה ORM או של בסיס הנתונים? כיצד נוכל לעשות זאת מבלי לשנות דרמטית את כל המערכת שלנו? התשובה בפסקה הבאה.

Aggregate-Based Data Storage

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

כשטוענים פרויקט:

  • נטענת רשומה מטבלת הפרויקטים
  • נטענת רשומה אחת מטבלת ה Topic (נושא אחד נפתח ב default)
  • נטענות כל רשומות הדיון מאותו ה topic (כדי להציג רשימת שמות) וכל הרשומות מהדיון הראשון, כולל כל ה comments וה attributes שלהם.

זה המידע שנדרש על מנת לספק את חווית השימוש הרצויה.

נניח מצב אופטימלי בו טבלאות הן רציפות בדיסק, ע"פ סדר הכנסת השורות.
כמה פעולות seek של הדיסק יש פה?

  • לפחות קריאה אחת עבור כל טבלה.
  • בעצם, יש אינדקסים שבהם ייתכן ויש להיעזר – כך שבפועל ייתכנו מספר קריאות לכל טבלה.
  • המידע מגיע מהדיסק בבלוקים של 4k או 16k. אם הרשומות בטבלה אינן "קרובות דיין" על מנת להיכנס לבלוק של 16k נתונים – ניאלץ "לדלג" (seek) שוב בתוך הטבלה.
    רשומות מסוג ה Comment יכולות להיכתב בהפרש זמנים ניכר אחת מהשנייה, שכן תגובות יכולות להגיע לאחר שבוע או חודש.
    רשומות מסוג ה Comment Attribute יכולות להיכתב בהפרש (כלומר, פיזור) נוסף, מאחר והן נוספות "רק ע"פ הצורך". לדוגמה: תכונת ה likesCount תיווצר רק בעת שנעשה ה Like הראשון ולא עם יצירת ה comment.
אין לי חישוב של מספר הפעולות בדיסק, אך יש לי בסיס להאמין שהוא יכול להסתיים בעשרות קריאות מהדיסק לכל discussion. השימוש ב ORM יכול להסתיר את העובדה שיצירת אובייקט "discussion" בזיכרון, רק בכדי לקחת את ה title ו lastUpdateDate – יכולה לגרום ליצירת אובייקטי ה comment והקריאה גם שלהן מהדיסק.
נקודה מעניינת, שמעצימה את הבעיה, היא שבמערכת עם הרבה מאוד פרויקטים ודיונים, הכמות הגבוהה של ה comments שמתווספים למערכת בשעה, יכולה לגרום לכך ש 2 תגובות במרחק של 5 דקות אחת מהשנייה – לא יהיו במרחק 16k בדיסק (מכיוון שנכתבו מאז הרבה comments אחרים ב discussions אחרים).
המצב הזה, בו המידע בדיסק מאורגן בניתוק מאופו השימוש בנתונים (קרי – ארצה לראות את כל ה comments מאותו דיון ביחד – ולא את כל ה comments שנכתבו בדקה מסוימת), הוא שורש הבעיה.

אילוסטרציה: כיצד אובייקט אחד (מורכב) שממודל בטבלאות של בסיס נתונים רלציוני, יכול להיות מאוחסן על גבי מספר pages שונים בדיסק הקשיח.

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

במקרים כאלו יש ייתרון ברור לאיגוד הנתונים ע"פ נקודת הייחוס המתאימה לשימוש הנפוץ במערכת, מה שנקרא document-based database או aggregate-database.

אילוסטרציה: כיצד פרויקטים יכולים להשמר בדיסק באופו רציף, כך שיהיה קל לקרוא פרויקט בודד.
ברור שפרויקטים שונים יהיו בגדלים שונים (לא הכללתי זאת בתרשים).

כיצד מממשים זאת?
פשוט מאוד: בוחרים נקודת ייחוס (למשל Topic, בכדי לצמצם מעט את גודל ה"קובץ" שנקרא בכל פעם), ומייצגים אותה ואת כל ההיררכיה של האובייקטים מתחתיה (למשל אובייקטי ה discussion) כרשומת JSON או XML אחת. את רשומה זו שומרים בבסיס הנתונים כ BLOB, כך שיהיה עדיין להינות משירותים של בסיס הנתונים (גיבוי, טרנזקציות וכו').

כעת, במקום להשתמש באובייקטי ה ORM לאובייקטי ה Topic ומה שמתחת – יש לכתוב מימוש מחדש, שיקרא את המידע המתאים מרשומת ה JSON ויטפל באותם הדברים שה ORM טיפל עבורנו עד כה.
במידה (כמו במקרה למעלה) שיצירה של אובייקט Topic גורמת ליצירת כל השאר – השימוש בבסיס הנתונים יהפוך ליעיל בהרבה: כל היררכית האובייקטים נקראת ממקטע רציף על הדיסק.

משמעויות נוספות
יש כמה משמעויות נוספות בגישה זו שכדאי להיות מודעים אליהן.

  • יש לממש לבד לוגיקה של קריאה / כתיבה של אובייקטים לתוך ה BLOB (מה שציינו למעלה).
  • אנו מאבדים את היכולת לעשות שאילתת SELECT על כל האובייקטים במערכת. למשל, למצוא את כל ה Comments שנכתבו בין 2 ל 4 בבוקר.
    אם אנו רוצים לבצע שאילתה שכזו – יהיה עלינו לקרוא את כל ה Topics מהדיסק, אחד אחרי השני, ולסרוק בעצמנו את המידע בתוך ה BLOB.
    אם אנו רוצים מהירות בסריקה (ויש לנו use-case ספציפי) אנו יכולים להשתמש במנועי indexing כגון Lucne.
  • השימוש בסכמה של "טקסט חופשי", כגון פורמט JSON, מאפשרת לנו לבצע שינויים לסכמת הנתונים בין הגרסאות של המוצר מבלי לבצע שינויים לסכמת בסיס הנתונים.
  • השימוש בסכמה של "טקסט חופשי" מאפשרת לנו לשלם על שדות "נדירים" רק כאשר משתמשים בהם (לדוגמה: isDeletedByAdmin) ועדיין להינות מביצועים נהדרים.

עדכון ספטמבר 2015:
הנה שני סיפורים דומים של חברות שבחרו ב"התאמה אישית" של בסיס נתונים רלציוני על פני מעבר ל NoSQL DB:

סיכום

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

כדי להינות מ BIG DATA, מספיק לעשות שינוי קצת שונה בבסיס הנתונים הרלציוני הקיים שלנו. ברור שבסיס נתונים NoSQL ייעודי יכול לתת יותר – אך לא תמיד הפער הזה מצדיק את המעבר.

שיהיה בהצלחה!

אם אתם מעוניינים ללמוד קצת יותר על BIG DATA, אתם מוזמנים לקרוא את הפוסט מה הביג-דיל ב BIG DATA?

הצצה מהירה על Backbone.js (ספריית MVC)

שפת ג׳אווהסקריפט התפתחה בעשור האחרון מ״שפת צעצוע״, המשמשת ליצירת אינטראקציה מקומית בדף אינטרנט, לשפה משמעותית שמשמשת לבניית מערכות שלמות. ממה נבע השינוי הזה?האם השפה עצמה התפתחה הרבה בעשור האחרון? – לא. אמנם הגרסה הבאה שלה[א] אמורה להיות עליית מדרגה משמעותית – אך היא עדיין לא שם. אם כן ניתן לחפש את השינוי במקום אחר.

האם השפה רלוונטית במיוחד לבעיות חדשות בתעשייה, שלא היו נפוצות בעבר? – גם לא ממש.

השינוי במקומה של ג׳אווהסקריפט נבע משילוב של שלושה גורמים עיקריים: שיפור מנועי הג׳אווהסקריפט (תאימות לתקן וביצועים), התקדמות הווב כפלטפורמה פופולרית להפצת תוכנה וקהילה פעילה שבנתה ספריות משלימות שאפשרו להשתמש בג׳אווהסקריפט בצורה יעילה ואמינה יותר. בפוסט זה נתמקד בגורם השלישי.

שייך לסדרה: MVC בצד הלקוח, ובכלל.

סמל, שאם תכתבו ב Backbone.js – תיתקלו בו עוד הרבה…

רקע

לא ניתן להתעלם מתרומתה הרבה של ספריית jQuery ודומותיה[ב] בעיצוב מקומה החדש של שפת ג׳אווהסקריפט. במעגל השני של ההשפעה (לפחות בצד-הלקוח) נמצאות ספריות שעוזרות בכתיבת אפליקציות צד-לקוח גדולות. ספריות אלו נקראות ״MVC Frameworks" או ״ספריות ארכיטקטוניות״. השם MVC עשוי לבלבל את מי שכבר מכיר ועבד עם ספריות MVC צד-שרת כגון Struts או ASP.NET MVC.

בעוד ספריות "MVC קלאסיות" מטפלות באפליקציות רבות דפים והניווט בניהם, ספריות ה "JavaScript MVC" לרוב מטפלות באפליקציות דף-יחיד (כגון GMail) בהן יש מסך עקרי אחד שחלקים בו מתחלפים / משתנים – אך אין ניווט משמעותי. בכלל, עצם הפעולה בצד-הלקוח, ב Ajax, בו אין refresh לדף בעת פעולה – משנה את כללי המשחק: מציב בעיות חדשות ופתרונות אחרים. עוד הבדל בולט הוא שברוב ספריות ה "JavaScript MVC" אין Controller. השם MVC בא לבטא את הרזולוציה ואת הבעיה שהן באות לפתור: ארגון קוד באפליקציית UI גדולה ומורכבת.

קיימות כיום מספר רב של ספריות "JavaScript MVC" וצפוי בהן עוד תהליך של קונסולידציה. שלושת הגדולות הן:

  • Backbone.js כנראה הנפוצה והבוגרת מכולן. מספקת מסגרת כללית בלבד ומשאירה חופש / חללים למפתח להחליט ולמלא בעצמו.
  • Knockout.js ששמה במרכז את ה Data Binding ומאפשרת לכתוב בקלות אפליקציות "מונחות-נתונים" (מערכות מידע וכיוצ"ב) נפוצה במיוחד בעולם ה NET.
  • Ember.js (לשעבר Amber.js), ספרייה מקיפה שכוללת גם Class System[ג] ושואבת רעיונות רבים מ Rails ״כתוב מעט קוד, אך עשה זאת בדרך שלנו" צעירה יחסית – אך זוכה למומנטום משמעותי. נפוצה במיוחד בקרב מתכנתי רובי.

ספריות משמעותיות אחרות הן Spine.js, JavaScriptMVC, Batman.js ו AngularJS (מבית גוגל). האתר המצוין todoMVC מציע השוואה מעמיקה בין האפשרויות בכך שמימש אפליקציית דוגמה בכל אחת מספריות הנ"ל (ועוד כמה ספריות נוספות).

רגע של התפלספות

כפי שציינתי, ספריות "JavaScript MVC" הן לא בדיוק MVC קלאסי. בעצם, גם ל"MVC קלאסי״ יש כמה פרשנויות שונות (לדוגמה, האם ה Controller מתמקד בעיקר בטיפול ב input או אחריות על ניהול הflow והתלויות?[ד]).

המדקדקים מגדירים את Knockout.js כ MVVM) Model, View, View-Model) – תבנית עיצוב שמייקרוסופט מקדמת בעולם הNET. זה דיי נכון.
את Backbone.js ו Ember.js נוהגים להגדיר כ Model, View, Presenter) MVP). האמת, ספריות כמו ASP.NET או GWT מתאימות יותר להגדרה זו. הגדרה כגון Model, View-Controller, Router (בקיצור MVCR) תהיה יותר מדויקת.

ההגדרה המועדפת עלי כרגע היא !Model, View, Whatever (בקיצור MVW או *MV).
התחלתי את הפוסט הזה בניסיון לעקוב אחר ההיסטוריה של תבנית העיצוב MVC, הוריאציות והשינויים שהיא עברה במשך השנים, בעיקר MVP ו MVVM, והניסיון להסביר כיצד ספריות ה "JavaScript MVC" מתאימות לתבניות אלו.

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

בסופו של דבר – MVC הוא ניסיון לארגן אפליקציית UI מורכבת בצורה שתהיה קלה לתחזוקה ושינויים. כל דור של UI (Web, Fat Client, Fully Client Side, Command Line) – והאתגרים שלו.
כפי שמרטין פאולר ציין פעם: "אנשים נוטים להסתבך עם MVC. החלק הכי חשוב הוא לבצע הפרדה אמיתית בין Business Logic ל UI. כל השאר – משני". אני מסכים ב 100%.

לכל ספרייה יש את הדרך שלה לארגן את הקוד – ואולי יותר מעשי ומעניין פשוט להתבונן בישומים קונקרטיים.
בפוסט זה אתן הצצה בסיסית ביותר ל MVW בג׳אווהסקריפט בדמות Backbone.js – הספרייה הנפוצה והמינימליסטית שיכולה להיות בסיס לימודי מצוין להבנת MVC בג׳אווהסקריפט.

אז מה יש ב Backbone.js?

אקצר מעתה ואקרא ל Backbone.js פשוט BB.
  • BB היא קטנה למדי. 700 שורות קוד בערך. 1400 שורות עם הערות.
  • BB משתמשת ב underscore.js (ספרייה טובה ל javaScript Utils מאותו היוצר) ו jQuery. אם אתם עובדים עם BB – יהיה לכם מאוד נוח להשתמש גם כן בספריות אלו, וקצת פחות נוח (אך אפשרי לחלוטין) להשתמש בספריות אחרות. BB מעודדת שימוש בספריית Templating – ואתם יכולים לבחור את זו של underscore (תחביר JSP-like, שלי אישית, עושה צמרמורת) או כל אחת אחרת.
  • ל BB יש תיעוד טוב (reference) ו Tutorials סבירים. יש קהילה גדולה ופעילה.
  • בגלל ש BB פשוטה – מייחסים אותה כמתאימה למערכות פשוטות יחסית – אך בפועל יש גם מערכות גדולות מאוד ומורכבות שנכתבו בעזרתה.
  • מאוד קל לעבוד עם BB מול שרת שמספק REST APIs – במיוחד אם תבנית-הנתונים היא JSON.
  • ל BB יש ארכיטקטורת Plug-Ins וניתן למצוא Plug-Ins רבים שמרחיבים את יכולות-הבסיס שלה.
  • BB מספקת מסגרת לא-מחייבת. אם אתם רוצים לפעול קצת אחרת, יש לכם את החופש לעשות זאת. כאשר מדובר במבנה של האפליקציה שלי – אני אוהב את זה.
  • כמו ספריות "JS MVC" אחרות, BB מספקת:
    1. מערכת-מחלקות (Class System) עבור Model ו Views (וגם Routers ו Collections – שהם רכיבים מרכזיים אחרים בBB)
    2. ניהול של המודלים (ה instances), כולל הקשרים בניהם.
    3. כלים לאימות ערכים (Validation Logic) במודל.

הנה דוגמה למודל מינימלי שניתן לייצר ב BB:

הערה: משפטי ה expect…toBe הן בדיקות-יחידה המציגות את התנהגות הקוד. כל הבדיקות עוברות בהצלחה.

המודל שלנו, Person בדוגמה זו, הוא פשוט למדי. הייתי יכול ליצור אותו גם ללא defaults – רק עלי ידי הורשה ריקה מהמחלקה Backbone.Model. תכונות המודל (כמו שם פרטי/משפחה) הן דינמיות ומוגדרות בזמן יצירת האובייקט הספציפי. ה default היא היכולת לחייב שתכונות מסוימות יהיו בכל מקרה.

ב1 – אנו יוצרים מודל של אדם בשם אוזי כהן.
ב2 – בכדי להדגיש את השוני בינו ובין אמן אחר בשם ״ליאונרד״ ובעל שם משפחה זהה, אוזי משנה את שם המשפחה שלו.
ב3 – אנו מקבלים את המודל המלא בפורמט JSON.

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

השתמשתי במודל הכי פשוט שאפשר – כי ברצוני להתמקד ב View.

PersonView היא המחלקה שאחראית לצייר מודל של Person. יכולים להיות, כמובן, כמה Views שונים לאותו המודל. את ה markup שה View יוצר, ארצה לשלב היכנשהו ב DOM. בדוגמה זו – זה יהיה container$, שמשתמש ב span בכדי לא לבלבל עם ה div ש Backbone יוצר באופן אוטומטי לכל מופע של המודל.

el הוא בעצם אלמנט ה DOM שיצר לי BB כחלק מה View. הוא עושה זאת על מנת לאפשר ל markup בפנים להשתנות, בלי שאצטרך לחבר את ה markup שוב ל container. הבחירה ב div היא כמובן רק default, ואם אני זקוק ל list element (תג

  • ) – אפשר לקבוע זאת.ב1- אני בודק מה יש בתוך ה container – ניתן לראות את ה markup שנוצר בתוך הפונקציה render. זו דוגמה דיי מכוערת – לרוב נשתמש בספריית templating על מנת שהקוד שיוצר את ה html snippet יהיה אלגנטי. לא רציתי להעמיס על דוגמה זו ולכן הקוד ה"פרימיטיבי".

    ב2- אנחנו בוחנים את ה markup כולל ה container. רק לוודא שהתמונה הכללית ברורה.

    ב3- אנחנו מבצעים שינוי למודל ורואים שה markup ב container השתנה באופן פלאי. "פלאי" – כמובן שלא. אנחנו כותבים את הקוד שעושה זאת – backbone רק מכווין אותנו למבנה מסוים ועוזר לנו.

    בואו נבחן את הפונקציה initialize שהיא לצורך העניין הבנאי (constructor) של מחלקת ה PersonView שלנו, וכוללת 2 משפטים מוזרים:
    ב5- אנחנו מבצעים פעולת JavaScripts's bind לכל המתודות במחלקה שאנו הולכים ל"ייצא" כ callbacks לאובייקטים אחרים. במקרה זה – יש רק את ״render״, אך יכולתי באותה מידה לשרשר גם שמות של מתודות נוספות. למה צריך לעשות JavaScript bind? – כדי ש this ימשיך להצביע לאובייקט שלנו. מבולבלים? קראו את הפוסט להבין את JavaScript this.
    הסימן "_" (קו תחתון) הוא הקיצור לגשת לספרייה underscore.js, ממש כמו שניגשים עם $ ל jQuery. אלו המקומות ש underscore מסייעת מאוד לכתוב קוד ב BB. אתם לא חייבים – אבל כדאי.

    ב6- אנחנו "מחייטים" את ה View שלנו לבצע פעולת render כל פעם שהמודל משתנה (change' event'). מאוד דומה לאירועים ב jQuery.
    הבהרה קלה: אם אתם מכירים את תבנית העיצוב MVC אתם יכולים להבחין שה View ב Backbone מבצע פעולות ("חיוט") של Controller. זה נכון: אפשר לומר שה View ב BB הוא בעצם View-Controller: הוא עושה הרבה פעולות שבמקור יוחסו ל Controller.
    אם אתם מכירים את תבנית העיצוב MVP נראה שה View הוא בעצם יותר Presenter. הוא אחראי ל Presentation Logic. זה נכון: בעצם מה שנקרא "View" ב BB הוא דומה ל MVP's Presenter בעוד ספריית ה Templating (בעצם ה Template עצמו) – דומה מאוד ל MVP's View.

    סיכום

    הצצנו ל Backbone וטעמנו כיצד נראה קוד שמשתמש ב Backbone.js. טעימה קטנה.
    דיברנו גם על החשיבות של ספריות "JavaScript MVC" – כאשר קוד הג'אווסקריפט שלכם מתחיל לגדול ולהסתבך. וגם על החשיבות לא לחפור בתבנית-העיצוב MVC וההיסטוריה שלה יותר מדי 🙂

    ייתכן ואמשיך לכתוב קצת על Backbone על מנת להתעמק בו קצת יותר, בניסיון לבחון JavaScript MVC "מהשטח".

    בהצלחה!

    ——

    [א] נקראת Harmony, ג׳אווהסקריפט 2.0 או ECMAScript 6.0. כוללות, ככל הנראה, מחלקות, מודולים (חבילות של מחלקות עם ניהול תלויות), תיקונים לכמה כשלים של שפת ג׳אווהסקריפט ועוד. ממש מהפיכה!

    [ב] Prototype ו MooTools היו גם הן מועמדות ראויות, אך קהל המפתחים בחר לבסוף בjQuery. ניתן לקרוא על העיקרון מאחורי jQuery בפוסט מבוא מואץ ל jQuery.

    [ג] כלים / Framework להגדרת מחלקות וכל מה שקשור בהן. בג'אווהסקריפט, כפי מתואר בפוסט מחלקות בג'אווהסקריפט. בשפת ג'אווהסקריפט לא קיימות מחלקות – ועל המתכנת לייצר אותן לבד. בעוד ספריות "JavaScript MVC" רבות מספקות סביבה אחידה / מוגנת ליצירת מחלקות עבור האלמנטים הבסיסיים בספריה (כמו Model או View).- ספריית Ember מספקת כלים שתוכלו להשתמש בהם עבור כל המחלקות בפרויקט שלכם.

    [ד] המקור של MVC הוא בשפת Smalltalk בשנות ה-70. כשהגיעו ה Fat Clients (ב ++C), ולאחר מכן הווב – נוצרו פרשנויות ווריאציות שונות ל MVC על מנת להתמודד עם האתגרים החדשים שהציבו טכנולוגיות אלו.

על תפקיד ה Product Owner והשפעתו על הארכיטקטורה

בסקראם, ה Product Owner (בקיצור PO) נדרש למלא 2 תפקידים שונים בתכלית:
  • מצד אחד: לחבוש את "מגבעת המרחבים" ולחלום על חזון רחוק, שכרגע אולי ואינו ברור או אפשרי.
  • מצד שני: לחבוש את "קסדת בוב הבנאי" ולהוביל בנייה הגיונית של מוצר מעשי.

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

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

ממש פיצול אישיות!

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

שייך לסדרה: אג'ייל – מתודולוגיות פיתוח רזות

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

בעוד ה PO חובש את מגבעת המרחבים, התמונה הראשונה המתגלה לעיניו על המוצר היא משהו כזה:

אקסל כ"מוצר עוצמתי ורב יכולות עבור המשתמש המקצועי". מקור: http://nizagreen.blogspot.co.il/2012/01/partes-de-excel.html

או אולי משהו כזה… (עבור ה PO בעל הנטיות השיווקיות):

"אקסל מוכן להשקה". מקור: מייקרוסופט

"Begin with the end in mind" היא אחת העצות שנותנים למנהלי מוצר בכדי שיהיו יעילים.

תחת הכובע של בוב הבנאי, התמונה הראשונה שעל ה PO לראות צריכה להיות משהו כזה:

היכולות הבסיסיות ביותר באקסל, מפורטות ומדויקות. מקור: וויקיפדיה.

האם זה אפשרי? האם אדם אחד יכול לעשות כזה "סוויץ'" בנקודות המבט? בואו נניח שכן.

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

מה יקרה אם יהיה לנו ״איש חזון ושיווק״ טוב, שלא יחבוש את ״קסדת בוב-הבנאי״?

בואו ניקח לדוגמה סיפור בו אנו רוצים להוסיף למוצר שלנו יכולת חדשה: בדיקת איות.
ה PBIs[ב] של היכולת, כפי שהוגדרו על ידי ה׳ PO, נראים כך:

  1. כמשתמש, אני רצה שבדיקת האיות תהיה מהירה, תוך כדי הקלדה (= שיפורי ביצועים)
  2. כמשתמש, אני רוצה שיוצגו לי כתיקון לשגיאת הכתיב, קודם מילים נפוצות יותר בשפה
  3. כמשתמש, אני רוצה שיוצגו לי הצעות לתיקון ע"פ מילים שמופיעות במסמך
  4. כמשתמש שמגדיר את העדפותיו – אני רוצה לקבוע אם בדיקת האיות תהיה תוך כדי הקלדה וכמה משאבים יוקצו לה
  5. כמשתמש שמגדיר את העדפותיו – אני רוצה לקבוע אילו מילונים יהיו בשימוש
  6. כמשתמש שמגדיר את העדפותיו – אני רוצה לקבוע חוקים ספציפיים לבדיקת האיות בשפה נתונה
  7. כמשתמש בגיליון – אני רוצה לראות סימון אדום כאשר יש לי שגיאת כתיב
  8. כמשתמש בגיליון – אני רוצה לקבל המלצות לתיקון
  9. כמשתמש בגיליון – אני רוצה להיות מסוגל להחליט שמילה שנתפסה כשגיאה היא תקינה – ולהוסיף אותה למילון

תפקיד ה PO לקבוע priority חד-משמעי לכל PBI: באיזה סדר על צוות הפיתוח לעבוד. זו הדרך של סקראם לוודא שלא עובדים על פיצ'רים לא-חשובים ושומרים על יעילות גבוהה. כפי שנראה בהמשך, מנגנון זה דורש רגישות רבה לאופן בו מפתחים מוצרים.

 
שאלה: בהנחה שיש לצוות יכולת לבצע את כל 9 ה PBIs בוודאות, האם יש משמעות לסדר של ה PBI שהוא מגיש?
תשובה: בהחלט כן! לסדר בו יגיש ה PO את ה PBIs לצוות יש השפעה ניכרת על התוצאה הסופית מבחינת ארכיטקטורה ואיכות הקוד.

אנו נוגעים כעת במרכז העצבים של מתודולוגיות ה Lean / Agile.

על קביעת העדיפויות

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

מצד שני, אם ה PBI הראשון ב backlog יהיה: "כמשתמש, אני רצה שבדיקת האיות תהיה מהירה, תוך כדי הקלדה" (= שיפורי ביצועים) – איך יוכל צוות הפיתוח לגשת למשימה? יהיה עליו לעשות שיפורי-ביצועים, אבל למה? למשהו שעוד לא קיים? למשהו שעוד לא ברור כיצד הוא יעבוד ומה הוא יעשה?!

זכרו את הקונטקסט: בסקראם, כולם נמצאים בריצה מספרינט לספרינט. ברוב הפעמים, לא יהיו הגדרות מדויקות של PBIs, ולא יהיו UI Mockups מוכנים אלא אם זה PBI שצפוי להתממש בספרינט הקרוב. כולם עובדים "Just-In-Time".

הנה תוצאה אפשרית וסבירה למצב הנ"ל:

  • המפתחים יפתחו מנגנון Cache. כי cache = ביצועים.
  • כיוון שהם לא יודעים מה תהיה התנהגות הריצה של מנוע בדיקת האיות, ואלו תבניות התנהגות הוא יכתיב ל Cache – הם יוכלו לכתוב רק Cache "גנרי". כזה שעושה הכל "בסדר", אך לא מצטיין באף תסריט ספציפי.
  • סביר אפילו, שעבור הרגשת הביטחון שה Cache בסדר ("!Done means Done") יפתחו אותו קצת אקסטרה – רק כדי "להרגיש בטוחים", וללא קשר לידע קונקרטי.
  • אם נקביל תהליך זה להתאמת מבנה-נתונים לבעיה, אזי על צוות הפיתוח לבחור מבנה נתונים לבעיה שלא הוגדרה. כיצד בוחרים מבנה נתונים כזה? מחפשים כנראה הרבה (O(1 – אולי HashTable שמאונדקס כפול ברשימה-משורשרת. ברור שזה מבנה נתונים שאינו "גרוע" למקרים רבים – אך כנראה שגם לא אופטימלי לכמעט אף מקרה.

מה לא היה בסדר? ה PO שם במקום הראשון את הדבר שמרקטיאלית הוא החשוב ביותר – זה נשמע הדבר נכון לעשות.
ה PO גם לא יכול היה לספק Functional Spec מפורט להכל מראש – זה לא Waterfall.

כלומר: ה PO יכול "לעשות הכל ע"פ הספר" – ועדיין להגיע לתוצאה לא-טובה.

"Amplify Learning / Amplify Feedback"

אם נחזור לעקרונות ה Lean, ניזכר שאחד מעקרונות היסוד הוא "הגבר את הלמידה" – זהו עקרון מרכזי בעולם האג'ייל[ג].

בעת בניית וסידור ה Backlog, על ה PO לחבוש את "קסדת בוב-הבנאי" ולקחת בחשבון גם שיקולים של בניית מוצר: מה הגיוני לבנות לפני מה? היכן ישנן שאלות מהותיות (טכנולוגיות) פתוחות – שאם נתקוף אותן קודם לכן נוכל לייצב טוב יותר את ארכיטקטורת המערכת.
תזכורת: ב"מפל המים", החופש המלא לקבוע את סדר הפיתוח היה בידי צוות הפיתוח. מנהל המוצר העביר "מסמך דרישות" והוא נפגש עם הצוות לאחר שנה לראות את התוצאות. צוות הפיתוח היה מודע לאספקטים הטכנולוגיים ויכול היה לקבוע סדר הגיוני של פיתוח.
לרוע המזל, שיטת מפל המיים, דווקא בנקודה זו, לא עבדה כ"כ יפה: כמעט תמיד גוף הפיתוח לא סיים לממש את כל הדרישות ולעתים נוצר מצב בו פ'יצר משני פותח במלואו, בעוד פ'יצר מרכזי וחשוב – נשאר בחוץ.

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

להלן 2 כללים שיכולים לעזור ולהנחות כיצד לבנות Backlog שמאפשר גדילה בריאה (טכנולוגית) של מוצר. כמובן שה PO יכול להעזר למשימה זו בפיגורה טכנולוגית.

חתרו למשהו מינימלי ועובד, ורק לאחר שהוא עובד – חזרו ועבו (מלשון עיבוי) את הפ'יצר.

מי שבא מרקע של מדעי המחשב בוודאי זוכר את האלגוריתמים לסריקת גרף: BFS ו DFS. בכדי "לגדל מערכת" באג'ייל אנו נרצה בבירור לעשות DFS (להגיע ל"משהו עובד" מהר ככל האפשר) ולא BFS (לעבוד שכבה שכבה ולסיים עם כולן בו-זמנית).

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

כיצד נראה Flow מינימלי ועובד? – הכי פשוט שאפשר.
בדוגמת האיות, אפשר להגדיר מילון סתמי שאומר:"Ncok ו Rokk הן שגיאות כתיב, כל השאר – בסדר". במקום להתעסק בהגדרת מילונים ובנייתם – אנו יכולים להתקדם לשלב הבא. בניית מילון היא משימה צפויה / ברורה. עלינו לזהות משימות צפויות או ברורות ולדחות אותן לשלב מאוחר יותר.

בשלב הבא: לדאוג שהמוצר שלנו ("אקסל") מאתר את שגיאות הכתיב תוך כדי הקלדה ומציג אותן למשתמש בצורה שאנו מוצאים כטובה. ה UI יכול להיות מאוד בסיסי.
ברגע שיש לנו את הסיפור השלם והבסיסי ביותר עובד (עם כמה הנחות מקלות בדרך – שנחזור ונתקן) – השגנו משהו חשוב מאוד:
שיתפנו את כולם בצורה מאוד ברורה בחזון שלנו. מצגות ומסמכי-מלל הם דבר נחמד, אבל רק כאשר מתחילים לחבר את הפרטים – התמונה באמת מתבהרת. מתכנתים זקוקים לפרטים על מנת לבנות את המערכת בצורה נכונה. גם מנהל המוצר עצמו יוכל לוודא יותר טוב שהוא בכיוון הנכון על בסיס משהו מוחשי וברור – פנימית וחיצונית לארגון.
בשלב מאוחר קצת יותר, כשהמפתחים יגיעו לממש את הסיפור "בדיקת איות תוך כדי הקלדה" – תהיה להם הבנה מספיק טובה של התסריט – על מנת לבנות את המנגנון הנכון להאיץ אותו.
הנה, כך אנו רוצים שבדיקת האיות תראה. אופס – וצריך גם "ignore". האם אתם רוצים לגלות זאת לפני, או אחרי שכתבתם את כל הקוד?


הגדירו Maximum Learning PBIs – ויישמו אותם ראשונים
ה PBIs מהם נלמד הכי הרבה הם לרוב כאלו שכוללים UI.

מדוע? "תמונה אחת שווה אלף מילים". כבני אדם, אנו יכולים להפיק מהגדרות ה UI הבנה טובה יותר של הדרישה מאשר מהתיאור המילולי שלה. אפילו ה PO בעצמו יתגבש טוב יותר – אם הוא יעבוד עם איש ה UX בשלב מוקדם להגדיר את ה User Interaction. בעיות רבות עלולות לצוף באופן זה בשלב מוקדם.
במערכות ללא UI, כגון שרתים – יהיו אלו הנתונים. "נתונים לפני" ו "נתונים אחרי" או "מה נכנס" מול "מה יצא". התבוננות בנתונים ראליים, בעלי משמעות – שווה יותר מעשרות מצגות ופגישות הדנות ב"כוונות" וב"המוצר – לאן?". פשוט אספו נתונים ראליים שאתם רוצים שהמערכת שלכם תעבד והציגו אותם.ב"מפל המים" לימדו אותנו שאי אפשר לכתוב UI לפני שהשירותים שמאחוריו כבר כתובים ועובדים. בואו נתקן אמירה זו: לא ניתן לסיים לכתוב את ה UI לפני שסיימנו לכתוב את השירותים שמאחוריו, אבל אפשר להתחיל.
UI שבו רוב הכפתורים הם disabled – הוא התחלה סבירה על מנת להגביר את הלמידה. כיתבו קוד שמציג התנהגויות מרכזיות שהוגדרו מראש, אפילו בצורה hardcoded, ועליתם מדרגה ברמת הלמידה שניתן להשיג בשלב מוקדם זה.

  • UI יוצר דיון וגורם לאנשים להביע דעה / להבין / להעלות בעיות.
  • ה UI ישתנה בכל מקרה – עדיף להתחיל את סבב השינויים עוד לפני שסיימנו לכתוב את כל המערכת שתומכת בו.
  • UI הוא "הקשר עם הלקוח/משתמש". קשר שממנו כל ההתנהגויות האחרות נובעות. שינינו את החוזה מול המשתמש? סביר להניח שכל המערכת תושפע מכך.

הערה: כל אלה נכונים, באין UI, גם לנתונים איכותיים.

כמובן שיהיו אנשים שיתנגדו לגישה זו: "למה לעשות UI בכאילו? למה לכתוב סימולציה hard-coded רק כדי למחוק אותה בספרינט הבא?".

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

תרגיל מחשבתי: גישה כנגד גישה
בתרגיל המחשבתי שמוצג בתמונות הבאות, ניתן לראות התפתחות אפשרית של מוצר בשני מקרים:

  1. אנו עובדים תחת הנחות מסורתיות של "מפל המים" / BFS: קודם נסיים את התשתיות – ואז נגיע בסוף ל UI.
  2. חתירה, מרבית, ל End 2 End flow – שגם מתחילה מהUI.
התרגיל הוא דמיוני, אך אני מרגיש שהוא מתאר יפה את הדינמיקה המציאותית.
הוא ממחיש כיצד עבודה ממוקדת תוצאה-מוקדמת יכולה למנוע מאיתנו להשקיע בכתיבת קוד שלא יהיה לבסוף בשימוש.
יש גם יתרון מובנה בלסיים "Flow" מוקדם יותר: יש זמן יותר לבחון את ה Flow, לתקן בו בעיות או להציע בו שיפורים.את ההשפעה החיובית של גישה זו על הארכיטקטורה – התרגיל המחשבתי הזה לא מפגין. תאלצו לנסות בעצמכם את שתי הגישות בכדי להיווכח בהבדל.

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

לחצו על התמונות להגדלה.

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

—-

[ב] Product Backlog Item – יחידת עבודה לספרינט עבור הצוות או חלקים ממנו.

[ג] תוכלו לקרוא על עקרון זה ואחרים בפוסט "קיצור תולדות הסקראם – חלק 2" בסדרה זו.

4 כללים למדידת פשטות של תוכנה

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

אחת ההגדרות שאני אוהב היא של בחור בשם J.B. Rainsberger, הגדרה של ״מבנה פשוט של תוכנה״.

אני משתמש במילה מבנה (structure) לתאר את התוצאה בפועל, בניגוד לתכנון (design) שזו התוכנית, או השאיפה, שלא תמיד מוסכמת על כולם או מתממשת בפועל. עצם קיומו של מסמך design לא מבטיח שלאחר שנה של קידוד – כך באמת תראה התוכנה [א].

ע״פ ריינסברגר (עוד), ישנם ארבעה אלמנטים (עם סדר עדיפויות ברור) המובילים לתוכנה פשוטה:

  1. כל הבדיקות עוברות. 
  2. צמצום כפילויות קוד. 
  3. הקוד מתאר כוונה (clarity). 
  4. צמצום  מספר האלמנטים בקוד למינימום האפשרי  אלמנטים שאינם משרתים את מטרות 1-3.

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

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

הנה דוגמה של קוד \"ללא כפילויות\" שהפריע לי והפכתי לכפול:

אני מסכים, זה לא קוד \"מושלם\", וספריית templating הייתה יכולה להפוך אותו ליפה יותר, אך זו דוגמה אמיתית מהחיים.

הנה הקוד לאחר השינוי:

זה בהחלט קוד פשוט יותר, למרות כמה שורות קוד כפולות.
כיצד אני מסביר / מרשה כפילות קוד שכזו?
א. הכפילות קרובה פיסית – קל להבין אותה ולהיות מודעים אליה (=> הסיכוי לתקן במקום אחד ולפספס את השני – קטן).
ב. לא מדובר ביותר מ3 שורות קוד רצופות כפולות. זו דוגמה פשוטה – ביצעתי שינויים דומים לקוד ארוך ומסובך יותר (ולא רק שרשור html, אלא גם לוגיקה), אך לעולם לא השארתי יותר מ3 שורות רצופות של קוד כפול.

קוד המתאר כוונה
אם מזקקים את הכלל של \"כתיבת קוד המתאר כוונה\", לרוב עיקר העבודה היא מסביב לשמות: שמות של פונקציות או משתנים, או שמות חדשים הנוספים ע\"י פעולות \"extract method\" או \"introduce variable\".

ישנן 4 \"דרגות\" של שם:

  1. שם סתמי
  2. שם נכון
  3. שם מדויק
  4. שם בעל משמעות (\"meaningful\").
עצלנות ולחץ גורמים לנו להיצמד לתחתית הסקאלה (1,2), בעוד הקפדה ומקצועיות דוחפים אותנו לראש הסקאלה (3,4).
מאוד נהניתי, כשקראתי את ריינסברגר, כשהוא מתאר בדיוק רב הרגל שגם אני רכשתי: כאשר אני רואה קוד כפול אני מבצע extract method לשורות הכפולות, גם מבלי להבין לעומק מה המשמעות שלהן. פעולה טכנית גרידא.
אני נותן לפונקציה שנוצרה את השם \"foo\". מבלי לחשוב. תוך כדי עבודה אני מבין מה הפונקציה עושה ומשנה את שמה. לאחר 10 דקות עבודה ייתכן והשם שונה כבר שלוש או ארבע פעמים, אך אני מרגיש בבירור שאלו עליות דרגה ברמה של השם. לעתים פשוט צריך לנסות איזה שם ו\"לחיות\" איתו כמה דקות על מנת למצוא שם מוצלח יותר.
צמצום אלמנטים שאינם משרתים את מטרות 1-3. 
למי שנתקל ברעיונות אג\'יליים – אני מניח שעקרון זה הוא מובן מאליו: Eliminate Waste. עדכון המטרה בעיקרון זה היא להימנע ממרכיבים בקוד שלא משרתים את הפונקציונליות, לא מונעים קוד-כפול ולא מסבירים את התנהגות המערכת, כל מיני \"הכנות למזגן\"[ג].

מי שעובד ב (Test Driven Development (TDD נהנה באופן מובנה מעקרון 1 (\"כל הבדיקות עוברות\") ועקרון 4 (\"צמצום אלמנטים לא-חיוניים\"). זו הדרך המהירה ליצירת קוד פשוט, שגם יישאר פשוט לאורך זמן.
מכאן נותרו רק 2 פעולות לשים לב אליהן:
צמצום כפילויות קוד [ב] ו כתיבת קוד המתאר כוונה / קוד ברור. המשיכו לעשות את אלו בצורה תמידית – והמבנה של הקוד שלכם, או ה \"design\" של הקוד שלכם – יהיה פשוט. זו הדרך היעילה ביותר שאני מכיר.
זה אולי נשמע קצת פשוט מדי: אולי ציפיתם לשימוש במחשבון של Cyclomatic complexity וטכניקות של ספירת Weighted Micro Function Points (ממש כמו ספירת קלוריות). 
צר לי לאכזב אתכם: כמה אנשים טובים השקיעו שנים ביצירת מודלים מתמטיים לתיאור סיבוכיות של תוכנה – אך בפועל כמה עקרונות פשוטים הם אלו שיגרמו לקוד שלכם להיות פשוט יותר, וקל יותר לתחזוקה.

שיהיה בהצלחה!

—-

[א] יש המשתמשים במילה Design על מנת לתאר מצב של תוכנה חיה, ולא רק את התוכניות. לדוגמה: \"Refactoring: Improving the Design of Existing Code\". אם הרעיון הזה ברור לכם – הרגישו חופשיים להשתמש במילה design ולא ב structure.

[ב] אזהרה!: יש המבלבלים בין צמצום כפילות קוד בתוך המערכת, לבין שימוש-חוזר בקוד (code re-usability) או \"צמצום כפילות קוד בעולם האנושי\". בעוד ביטול כפילות-קוד בתוך הקוד שאתם כתבתם הוא כמעט-תמיד דבר טוב, שימוש חוזר בקוד (\"חיצוני\") הוא נושא מורכב עם הרבה ייתרונות וחסרונות. אני מתכנן לעסוק בנושא זה לעומק בפוסט נפרד.

[ג] המטאפורה הזו מטעה! מזגן הוא דבר מוכר שאנו יודעים לצפות לו ולבנות לו הכנה טובה. רוב ה\"הכנות למזגן\" בתוכנה הם ניחושים בעלטה אודות משהו לא-ידוע. בהתאמה: מנסיוני, רובן מתגלות כלא-יעילות או חסרות שימוש.

ארכיטקטורה: Quality Attributes (חלק ב' – כיצד משתמשים)

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

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

Reliability – היכולת של המערכת לרוץ לאורך זמן ללא תקלות חמורות. הדרך לייצר Reliability הוא, במידה רבה, תכנון נטול סיכונים גבוהים והשקעה במנגנוני התאוששות ותיקון-עצמי.
דוגמה: מערכת שאוספת נתונים של ניסוי. הנתונים לא יחזרו (בקלות) ולכן המערכת מבצעת שמירה תכופה של המידע לדיסק ואולי אפילו מגבה גם אותו פעם נוספת. התנהגות זו פוגעת בביצועים ובזמני התגובה של ה UI.

Usability – ב"עידן התפוח" לא צריך להסביר כבעבר את החשיבות של השימושיות. שימושיות נובעת מה UI, ומהמהירות בו הוא מגיב, אך גם משפיעה על פנים המערכת: דרישה של שאילתות שקשות לביצוע אך מועילות למשתמש, זמינות נתונים וכו'. השאלה המעניינת, בעידן התפוח, היא מה יותר חשוב במערכת שלכם מ Usability – וסביר שבמערכות רבות יהיו שיקולים שכאלו.

Security (נקרא במקור Integrity) – עד כמה מוגבלת / מאובטחת צריכה להיות הגישה לנתונים / שירותים של המערכת.

Efficiency – עד כמה יעילה המערכת בניצול משאבי החומרה העומדים לרשותה, או בניצול שירותים אחרים (כגון SOA) – והחומרה העומדת לרשותם.

Portability – היכולת של המערכת לרוץ בסביבות ריצה שונות, קרי מערכות הפעלה, סביבות ענן, תצורות רשת וכו'.

Reusability – היכולת לבצע שימוש חוזר במודול שנכתב. מאפיין איכות זה לא אמור לעסוק ברמת המחלקות הבודדות, כי אם ברמת השירות ה high-level. עוד בנושא זה בהמשך.

Interoperability – היכולת להחליף נתונים בקלות עם מערכת אחרת. באופן אישי לא מצאתי מאפיין זה שימושי. Interoperability נראה לי יותר כמו פיצ'ר נקודתי מאשר יכולת רוחבית. אני משאיר אזכור לצורך שלמות הרשימה המקורית.

Maintainability – עלות תפעול (Administration / Operations) נמוכה של המערכת. מה שנקרא TCO (Total Cost of Ownership). מאפיין זה יכול להצביע על הוספת "פ'יצרים של ניהול המערכת". עוד נושא שנכנס לקטגוריה זו היא קלות ההתקנה של המערכת, לעתים מתייחסים אליה כמאפיין איכות עצמאי: Installability.
דוגמה: כלי monitoring שיסייעו לנטר בעיות במערכת. השקעה ב Upgrade קל וכו'.

Developability (נקרא במקור Flexibility) – היכולת לפתח את המערכת בקלות, לבצע בה שינויים או לתקן באגים. מה שנקרא TCD ((Total Cost of Development מאפיין איכות זה עוזר לאפיין את הדילמה שבין איכות שמוסיפה סיבוכיות למערכת (Portability או Efficiency, למשל) למול היכולת לפתח מהר וביתר קלות.

Extensibility – היכולת להרחיב ולהוסיף יכולות חדשות למערכת, בעזרת Plug-in, API וכו'. כמובן שיכולת ההרחבה היא ליכולות / אזורים ספציפיים במערכת.

Supportability – היכולת לתמוך במוצר בקלות, אם מדובר בגוף המייצר את התוכנה או גוף שלישי. כלי Monitoring, לוגים קריאים, יכולת לבצע Dumps של זיכרון או נתונים, דו"ח על תצורת המערכת וכו'.

Scalability – היכולת של המערכת של המערכת לטפל במספר הולך-וגדל של בקשות ו/או היכולת של המערכת לגדול על מנת להמשיך ולהתמודד עם גדילה זו. לרוב עושים הבחנה בין Vertical Scalability – יכולת לצרוך ביעילות חומרה יותר חזקה ו Horizontal Scalability – היכולת של המערכת להתפרס על מספר שרתים פיסיים (cluster).

Testability – היכולת לבצע בדיקות למערכת. באופן אישי, מאפיין זה נראה לי כמו אמצעי להשגת Reliability או Developablity – ולא מטרה בפני עצמה.

Safety – למאפיין זה יש במשותף עם Reliability ועם Security, אך הוא עדיין שונה. בעצם הוא אומר Reliability גבוה בנקודות בהן עלול להגרם נזק ו"נפילה למצב בטוח" במידה ויש ספק. כלומר, העדפה של אי-פעולה במקרים מסוימים. מאפיין זה תקף בעיקר למערכות בהן התוכנה שולטת על חומרה חיצונים (רובוטים, מכשור רפואי וכו').

Availability – מאפיין זה תקף בעיקר לשרתים (או שירותים) והוא מורכב מזמינות (אחוז הזמן שהשרת / שירות זמין ללקוחותיו) או תדירות הכישלונות (מה שנקרא MTBF). שרת יכול להיות זמין 99% מהזמן אך לכשול לשנייה אחת – כל יום, מה שיגרום לתקלות כואבות. (MTBF (Mean Time Between Failures עוזר להשלים את התמונה שנתון הזמינות לבדו לא מספק היטב.

רשימת "מאפייני איכות" ממקורות שונים.  מקור: http://www.clarrus.com/documents/Software%20Quality%20Attributes.pdf

כמה הערות על הרשימה:
שימו לב שבהכללה גסה Reusability, Developability ו Testability הם איכויות פנימיות (משפיעות על הפיתוח) בעוד היתר הן איכויות חיצוניות (משפיעות על הלקוח).

Extensibility, Maintainability ו Supportability הם סוג של "פיצ'רים" שאולי מנהל המוצר לא ייזום ולכן על הארכיטקט להציע ולקדם. לכאורה נראה שהם שונים משאר מאפייני האיכות מכיוון שהם "פיצ'רים נוספים למערכת". בפועל – השקעה בכל מאפייני איכות (למשל Availability או Safety) דורשת עבודה וההבדל בין שלושת מאפייני איכות אלו לשאר היא קטנה משנדמה במבט ראשון.

בחירת מאפייני איכות

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

מכיוון שכל מאפייני האיכות הם "דברים טובים" האתגר הוא להבחין מה יותר חשוב ממה.

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

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

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

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

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

במשך שנים, נושא חם בשוק ה IT היה בעיית ה Lock-In: בחרת ספק מסוים, לו יש API מסוימים. השתמשת בהם והיית מרוצה, אך ביום בו הספק מעלה מחירים / מחפף בשירות / קמים מתחרים עדיפים – "אין לכם שום דרך לבצע שינוי".
האמירה של "אין שום דרך" היא אמירה לא מדויקת – אך מכיוון שהסיפור (אלמנטים טרגיים של בגידה?) הוא סיפור מוצלח – הוא הושרש והשאיר חותמו על אנשים רבים.

אחד ה "Best Practices" שנבעו מכך הוא לכתוב קוד "בלתי תלוי בספק (vendor)" בכלל, ו"בלתי תלוי בספק בסיס-הנתונים" בפרט. עבדתם עם מערכת אורקל ומחר אתם רוצים לעבור ל MS-SQL? אין בעיה – מחליפים רק את ה "connection string" – והכל עובד.

ראיתי כמה מערכות שנעשה בהן שיקול שכזה[ב]. האנשים המעורבים הרגישו שהם עושים "הנדסת תוכנה טובה יותר" והחליטו "להקפיד ולכתוב קוד שלא תלוי בבסיס הנתונים". בפועל:

  • באף אחת מהמערכות הללו לא החליפו בסיס נתונים, עד היום (עד כמה שידוע לי).
  • הצוות הקפיד להשתמש ב ANSI SQL – וכך התעלם מרוב היכולות של בסיס הנתונים. אי-השימוש ביכולות הקשה בצורה משמעותית על הפיתוח ועל שיפורי ביצועים.
  • בכמה פעמים, המוצר נזנח / נכתב מחדש – אך בסיס הנתונים נותר. כלומר: בסיס הנתונים היה חלק יציב יותר מהקוד שהשתמש בו.

בפועל, ההחלטה המעשית הייתה לבחור במאפיין האיכות "no DB vendor lock-in", על פני "Developability" ועל פני "Scalability".

רק להבהיר: אני מדבר על מקרה בו אתם מוכרים Appliance או Hosting. כאשר הלקוח צריך לטפל בבסיס נתונים בעצמו, ברור למדי שלחברות רבות יש העדפה ברורה איזה בסיס נתונים הם רוצים להתקין: אולי יש להן DBA מוכשר שמכיר אותו, אולי הן כבר ביצעו השקעה מסוימת בטכנולוגיה (כלי ניהול, אבטחה וכו') ספציפית לספק בסיס-נתונים זה.
מתן האופציה להתקין כל DB, עבור לקוחות רבים – יכולה להחשב כאיכות של Maintainability – כי אז הם יוכלו להתקין את בסיס הנתונים שהם מעדיפים.
הבחירה של פלטפורמות ג'אווה וNET. לבצע הפשטה שכזו (בעזרת ODBC ו JDBC) באה לתמוך במי שרוצה להשיג Maintainability.
אם כל מה שאני שומר בבסיס הנתונים היא טבלה או 2 של נתונים פשוטים – אין לי רווח רב משימוש בבסיס נתונים ספציפי.


בכל מקרה – אין פה תשובה נכונה או שגויה. עצם העניין הוא הבחינה המודעת של האלטרנטיבות. ההבנה שכתיבת קוד "Database Agnostic" הוא מאפיין איכות אפשרי שמגיע עם תג מחיר, ולא בהכרח "הנדסת איכות טובה יותר".
כניסה לפרטים
גפרור הוא מוצר דיי קטן. מערכת ERP היא גדולה יותר וכנראה שלא סביר להגדיר, באופן גורף, ש"דיוק עדיף על נוחות" או "יכולת אחזור עדיפה על מהירות" לגבי מערכת שכזו. הרי החלטה זו, יכולה להיות נכונה עבור חלק אחד במערכת – ושגויה עבור חלק אחר.


דרך מומלצת 
על מנת להשתמש במאפייני איכות בצורה יעילה כדאי לבחור כמה תסריטים עיקריים במוצר ולהדגיש אותם:
"בתחום ה X אנו מעדיפים d על c ומעדיפים b על a".אפילו כדאי לצרף דוגמאות ספציפיות, מה שנקרא "תסריט":

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

Reusability

עוד תחום בו סקירת מאפייני האיכות יכול לסייע רבות הוא בהחלטה האם לעשות שימוש חוזר בקוד או "איחוד" של 2 מודולים בעלי פונקציונליות דומה.
לעתים רבות יש לנו 2 ספריות / מודולים שרשימות היכולות שלהן, כ checklist היא דומה או אולי זהה. לדוגמה: ספרייה לשליחת הודעות דוא"ל. שאלה מתבקשת היא "מדוע אנו מתחזקים 2 ספריות שעושות אותו הדבר? האם זה לא בזבוז?"
לפני שאתם רצים לאחד את הקוד לספרייה יחידה, כדאי לבחון מהם מאפייני האיכות של כל ספרייה.
אם ספרייה אחת מקפידה על reliability (מכיוון שהיא משמשת לשליחת התרעות מערכת חמורות), בעוד השנייה מתמקדת ה customizability (היכולת להגדיר הודעות דואר יפות ומותאמות-אישית) – ייתכן ואין סתירה וניתן לשלב אותן בהצלחה. כלומר, ספרייה אחת לא תוכל להחליף מייד את השנייה, אך הגיוני לקחת חלקים מכל ספרייה ולאחד אותן. חשוב להבין אלו הנחות נעשו על מנת לאפשר את מאפייני האיכות העיקריים של כ לספרייה ולראות שהם לא מתנגשים.
לעומת זאת, אם אחת מתמקדת ב customizability והשנייה ב performance/scalability (שליחת אלפי הודעות בדקה) – קרוב לוודאי שטוב תעשו אם תשמרו אותן כ2 ספריות נפרדות.
לעתים דיי קרובות, ספריות שאיחודן נראה כ no brainer במבט ראשון, מתגלות במהרה כלא סבירות לאיחוד לאחר שבוחנים את מאפייני האיכות שלהן.

זכרו ש"מאפייני איכות" היא בעיקר דרך לחשוב על מערכת / על חלקיה. יש דרכים רבות לספק את הדרישות ה"פונקציונליות" של מוצר ("כלי רכב בעל 4 מושבים, שמסיע אנשים ממקום למקום ויש לו מזגן ורדיו") – אך יש הרבה פחות דרכים כאשר הגדרתם גם את מאפייני האיכות של המוצר.
מאפייני איכות מסייעים להגדיר ארכיטקטורה עבור "משהו" – ולא ארכיטקטורה "סתם".
זכרו שבארכיטקטורה, כמו בארכיטקטורה, "קוד שעובד" היא נקודת האפס. "קוד שעובד יעיל[ג]" הוא הדבר בעל המשמעות.

נ.ב. – חלק מהספרות הפורמלית בנושא מתאר את "מאפייני האיכות" קצת אחרת – אני התייחסתי אליהן בצורה שהייתה שימושית עבורי והוכיחה את עצמה. חפשו בגוגל "Quality Attributes" ותוכלו למצוא חומר רב.



[א] הגפרור שאנו מכירים.
[ב] אני מכחיש בתוקף!, אך בפועל ייתכן שפעם אני הוא זה שיזם את המהלך.
[ג] Effective, לא efficient.