ממשקי משתמש – יצירתיות מול שימושיות

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

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

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

כלומר ממשקי המשתמש עבור סיוע לאנשים הסובלים מכאבי שרירים כרוניים אינם מתאימים אותם האנשים.

נלקחת מ: https://commons.wikimedia.org/wiki/File:Mission_Accomplished_-ALS_Ice_Bucket_Challenge(14848289439).jpg

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

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

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

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

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

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

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

עלייתן ונפילתן של רשתות חברתיות – דעה

ב2007 קיבלתי הזמנה לקחת חלק מאנשים מובחרים באמת ברשת חברתית חדשה ואנונימית בשם פייסבוק.

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

במהלך השנים עזרתי לנהל דף פייסבוק מאוד נפוץ שהגיע ליותר מ50,000 עוקבים, שהיו מפולחים מאוד, וקיבלו פרסומות (מתוך פייסבוק) לעסקים שהם באמת רצו להחשף אליהם, עד שיום אחד פייסבוק משום מקום או התראה החליטה לסגור את הדף, להשעות לכלל המנהלים את החשבון ו… הייתי צריך להוכיח שאני לא בוט.

פייסבוק היו מקבלים הכנסות מהדף, ולמרות זאת החליטו לסגור אותו.

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

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

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

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

בעל האתר כרגע מתמודד לראשות עיריית לונדון, אולי במטרה לשנות את המקצוע בעקבות הנושא.

להמשיך לקרוא

אם או אחרת?

לאחרונה היה לי חצי דיון בטוויטר לגבי מתודולוגיה תכנותית – האם יש מצבים בהם יש מקום להשתמש בelse כאשר יש שאלת תנאי.

דעת האדם שלו הגבתי היא שאין מצב מבחינה לוגית שצריך אי פעם else. הוא הראה דוגמאות מאוד מופשטות (בעיני) איך הוא פותר את הנושא.

למשל ראו את קוד גו הבא:

...
a := 1
if b > 12 {
  a = 2
}
...

למעשה הצבתי ערך ברירת מחדל ל a של 1 ואם משתנה בשם b גדול מהמספר 12, אז אציב את הערך 2.

כאן אני יכול להסכים כי אין צורך ב else.

אבל לא תמיד המצב הוא כזה פשוט:

...
err = nil
if b > 12 {
   callOneFunc()
} else {
  err = callSecondFunc(b)
}
...

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

אבל אחרת, הרץ פונקציה המועבר אליה b והחזר שגיאה (במידה ויש).

כאן הצגתי את דעתי למצב שבו יש מקום ל else. היות ופעולה של קריאה לפונקציה ללא צורך היא פעולה יקרה מידי. בייחוד אם רק מהקריאה כאן אין לכם מושג מה כל פונקציה עושה בתוכה.

אבל, יש מצבים בהם אני יכול לוותר לגמרי לשאלת התנאי:

...
caller := map[bool] func(i int)error {
  true: callOneFunc,
  false: callSecondFunc,
}

err = caller[b > 12](b)
...

כאן יצרתי מערכת של רישום פונקציות שונות על ידי שימוש ב hash map. כאשר לא משנה מה התוצר, אני יכול להריץ פונקציה בעלת אותה חתימה, אבל ללא צורך בתנאי כלשהו, אלא רק בתשובה בוליאנית. כמובן שאפשר גם לייצג ככה switch case ללא default, כל עוד האיבר בהכרח חייב להיות קיים.

אז מה "נכון"?

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

אבל נגיד ו2 פונקציות בעלות אותה חתימה. אחת תבצע פעולת HTTP והשניה תריץ קובץ מקומית. עצם הפעולות עצמן הן יקרות, אבל הן גם שונות לגמרי למרות שהתוצר אולי יהיה זהה.

קריאת default תיצור בעיה, וגם לא יהיה ברור מדוע יש קריאה אליו, בעוד ששימוש ב else יהיה מאוד קריא וזול יותר מבחינת המערכת.

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

...
func ExecIfBigger(b int) {
  if b > 12 {
    callOneFunc()
  }
}

...

ExecIfBigger(b)
if b <= 12 {
  err = callSecondFunc(b)
}
....

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

כך שהרעיון בעיני להימנע מ else כאילו היה קורונה, בעייתי מידי ויכולה לסבך את תחזוקת הקוד ואף להוסיף עוד כמה קריאות מיותרות במערכת. ולא להזכיר כי אולי זה לא יהיה קריא, ויש להשתמש בגישה הזו רק כאשר היא נכונה, ולא כמשהו דתי שיש להימנע.

מה דעתכם בנושא?

צעדים ראשונים ב wireguard

נדרשתי למערכת אשר צריכה לעבוד מאחורי NAT.
עם NAT יש מספר בעיות, אשר אחת מהן היא כיצד אני מקבל דברים פנימה, ולא רק כיצד אני ממפה את הפנים כלפי חוץ.

כלומר נגיד ואני רוצה לגשת ל 10.0.0.12 ב HTTP כאשר אני נמצא באינטרנט, והשרת נמצא מאחורי 10.0.0.12, אין לי סיכוי ללא יכולת לעשות Port forwarding, הנגשת השרת כלפי חוץ או reverse proxy כלשהו.

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

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

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

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

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

ברגע שהבנתי את העיקרון הזה, ולי לקח זמן להבין את הרעיון הזה, למעשה הבנתי לגמרי כיצד Wireguard מוגדר ועובד.

ישנם מדריכים מדהימים ברשת, כולל באתר, הרשמי, כך שלא אכנס להגדרות כאן.

שמתי לב כי הוא מתפקד הרבה יותר טוב במרבית האנדרואידים, ויש לו תמיכה ב QR Code בשביל לטעון קובץ הגדרות מאוד פשוט. דבר שמקל מאוד מאשר להזין את הנתונים ידנית.
כמו כן, מצאתי כי קשה לייבא את הקובץ בצורה פשוטה במכשירי האנדרואיד שהיו ברשותי, ולכן זה היה הפתרון הקל ביותר.

אחד הדברים המדהימים באמת של Wireguard זו הפשטות ליצור תעודות הצפנה.
פקודה קטנה ליצור מפתח פרטי ופקודה נוספת לקחת את המפתח הזה וליצור מפתח ציבורי.
בקובץ של המכשיר שמים את המפתח הפרטי, ובהגדרות של כל מי שצריך לדבר איתו שמים את המפתח הציבורי.
המפתחות שמורים כשורת base64 ואין התעסקות עם המון כלים כדוגמת easy-rsa של openvpn, ואין צורך להתעסק עם openssl.
קובץ ההגדרות יראה בצורה הזו ב"לקוח":

[Interface]
PrivateKey = 
Address = 10.1.0.2/24

[Peer]
PublicKey = 
PersistentKeepalive = 20
Endpoint = address:port
AllowedIPs = 0.0.0.0/0, ::/0

[Peer]
PublicKey = 
AllowedIPs = 10.1.0.3/32

[Peer]
PublicKey = 
AllowedIPs = 10.1.0.2/32

 

היצירה של ה QR התבצעה על ידי באמצעות לינוקס עם הפקודה qrencode בצורה הזו:

qrencode -t PNG -o qr_conf_for_phone.png --verbose < wg_conf_for_phone.conf

וקובץ ההגדרות נוצר כקובץ png, שאפשר להציג על המסך ולגרום לתוכנה לסרוק אותו.

בשורה התחתונה, אני מאוד מרוצה מה vpn שלא רק קל להגדרה אלא נראה יציב יותר.

אופטימיזצית פרוייקט בזמן קורונה

יש לי מספר מערכות שפיתחתי במהלך השנים שנמצאות בשימוש יומיומי על ידי מספר חברות מאוד מוכרות בארץ וכן משרדי ממשלה.

במהלך הקורונה, עוד משרד ממשלתי החל להשתמש בו על מנת לדווח לאנשים כי שהו במחיצת חולי קורונה, או נמצאו חיוביים בעצמם.

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

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

התחלתי למדוד מה המקומות שלוקחים הכי הרבה זמן וגיליתי כי משהו שהוא מחוץ לשליטתי הוא הגורם לכך.

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

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

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

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

יצרתי סוג של טיימר הסופר עד כמות מסויימת של מילי שניות ובמידה והגיע אותו timeout לפני שהתוצר הסתיים, הייתי מדווח שגיאה חזרה.
במידה והסתיים, המידע המתאים נשלח כי הוא התקבל חזרה, ובטיימר לא היה באמת sleep כלשהו אלא תפיסה של "אירועים" על ידי שיחרור מיוטקסים או לחכות לmutex מסוים להשתחרר. הידע מתי לשחרר אותו התבצע על ידי שמירת ה payload שמגיע בstreaming pipe עד אשר תו מסויים מגיע, או אורך מסוים הגיע (הראשון מבניהם). במידה ועד שזה מתבצע הגיע הזמן לשחרר, אז mutex אחר שספר ticks פעל כאשר הבדיקה היתה על אחד משניהם.

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

לאחר השינוי הזה, המערכת התחילה לחזור אלי לרוב אחרי 10-200 מילי שניות, כאשר 500 מילי שניות היו לתוכן גדול בהרבה ממה שאותו משרד ממשלתי שולח אלי.

גרמתי למערכת לרדת לפעמים לפחות מ10 מילי שניות כאשר מקודם לכן הממוצע הנמוך עמד על 500 מילי שניות והגבוה על 700 מילי שניות.

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

עכשיו צוואר הבקבוק האמיתי של המערכת שלי הוא תשתית, כלומר עמידה בכמות תעבורה מסויימת וזה כבר עולם לגמרי אחר.

היה שלום פרל, ותודה על הסימנים

לפני מספר שנים כתבתי את המערכת האחרונה שלי בפרל.
היא תרגמה DSL לפעולות בפועל שמבוצעות.

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

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

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

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

על הדרך גיליתי שורות קוד שהזכירו לי גם מה לא אהבתי בפרל (אם כי במכלול הגדול של הדברים, אני מת על השפה). למשל 4 שורות קוד שלקח לי שעה להבין מה הן עושות (ואני כתבתי אותן) כי הן השתמשו במשתנים כדוגמת ‎$_‎ אשר מייצג למשל תוכן של scope ולא תמיד ברור של מה התוכן. בנוסף גם עוד מספר משתנים בסגנון.

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

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

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

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

לשחק עם כובש ההשלמות

אני עובד עם vim כבר המון שנים, ובשלוש שנים האחרונות עם neovim.
עד לאחרונה עבדתי עם מנהל השלמות בשם deoplete, אשר עשה עבודה מדהימה, אבל גרם לי להתקין המון תלויות.

השלמת תוכן ב nvim

החלטתי בהמלצת מאיר לנסות את coc – שפירושו הוא Conquer of Completion .
התוסף הוא בפני עצמו מאוד מעניין. הוא משתמש מאחורי הקלעים בחלק מכובד של תוספים המגיעים מ Visual Studio Code של מיקרוסופט, בנוסף לשימוש ב LanguageServer.
הוא מחזיק בתמיכה של "חלונות צפים" שנכנס לשימוש ב neovim רק לאחרונה:

חלונות צפים עם cocרואים את התיעוד של הפונקציה Errorf? זה חלון צף ב neovim, לפחות סגנון אחד שלו.עוד השלמה שהוא מכניס:

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

וזהו, זה כל תפקיד הכלי, ובזכותו קיבלתי חצי מיכולות של IDE בסיסי.
בנוסף, אני משתמש ב ALE אשר יודע לתת לי חיווי של שגיאות באמצעות תמיכה בlinters, ואף לנסות לתקן דברים אם יש לו כלים לזה.

עד כאן הכל טוב

אבל, שמתי לב למספר בעיות חדשות שצצו לי במערכת. למשל בגלל שcoc משתמש ב node, הוא שוקל יחסית הרבה בזמן ריצה על ה cpu ולא רק על הזיכרון.

יש לו המון אופציות להגדרות, אבל חסרים לי כמה יכולות בסיסיות שהיו לי ב deoplete.
הפשוטה שבהם היא היכולת להשלים רק מה שחסר בטקסט.
למשל יש פונקציה בשם Write, ואני רוצה לבחור ב Fwrite במקום. עם Deoplete הוא ידע להחליף את מה שצריך.
אף ב coc אקבל את FwriteWrite במקום בזמן השלמה.

שורה תחתונה

אחרי חודש וחצי של משחקים, אני חייב לציין שאני מאוד אוהב את coc, וחושב לייצב אותו יותר ב"הפצה" שלי של vim.

הקוד שאתם רואים בתמונה הוא חלק מפרוייקט שפתחתי בגיטהאב שכולכם יכולים להנות ממנו – תמיכה בספקי SMS שונים על ידי יצירת SDK עם בסיס קוד אחיד (go/golang interface).
נכון לכתיבת שורות אלו, יש תמיכה במספר ספקיות ישראליות וזה יתרחב.

 

לצטט ברשת

כפי שהרבה יודעים, וכתבתי על כך גם בעבר, אני מאוד חובב ציטוטים, ויש לי אוסף מכובד (נכון לכתיבת הפוסט 4,702 ציטוטים) שלהם.

ממשק ציטוטים

מספר אנשים שגם הם חובבי ציטוטים תמיד מקבלים ממני קישור לאוסף עצמו שנמצא בקובץ טקסט במבנה מאוד מוגדר:

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

כל שורה (למעט קישור ווב) לא תעבור את עמודה 80, ותהיה ירידת שורה במידה וכן.

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

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

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

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

כל זה ללא שימוש בשום framework או ספרייה כלשהי, אלא נטו קוד שלי.

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

תהנו 🙂

לאן נעלמתי

קיבלתי מספר שאלות בפרטי לאן נעלמתי ולמה אני לא כותב יותר.

התשובה הקצרה היא – אני כן כותב אבל לא כאן, ולא בתדירות שאני מעוניין לכתוב.

התשובה הארוכה, ובכן הנה היא:

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

הקמתי לפני כשנה בערך בלוג נוסף ב medium. הכתיבה שם באנגלית, אבל לא בתדירות שאני מעוניין בה.

אני מקווה כי וורדפרס (או חברת automatic) ישפרו את החוויה, ואז אחזור לכתוב גם כאן, בנתיים תוכלו להנות מדברים שלי שם 🙂

5 דברים טכניים שלמדתי השנה

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

1. אני עדיין צריך ללמוד יותר VIM

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

אחד הדברים שלמדתי מחדש השנה הוא Text Object. היכולת של vim לזהות דברים למשל פסקאות, שאתה בין סוגריים, מרכאות, מילים וכיוב'.
בעוד שיש בברירת המחדל תמיכה די טובה. יש הרבה תוספים שמוסיפים לזה עוד יכולות, או רוכבים על הקיים ומוסיפים לזה תכונות.
למשל היכולת להבין "מתודה" כפסקה, או class ככזו. ויצא לי בעצם ללמוד מחדש את כל השימוש בה, שדי הדחקתי. אם עד השנה הזו הייתי משתמש המון ב visual mode, השה הזו ירדתי בכמות השימוש במצב זה, בזכות ה Text Objects.

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

2. אני יודע מה מפריע לי כל כך בלכתוב טסטים

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

אתחיל מכך שאני כן חושב שבדיקות הן חשובות, אבל לא בגישה העיקרית ששולטת בשוק.
למשל אם תלכו לחבילת go שיצרתי לאחרונה בשם gostrutils, תגלו כי יש לי שם בד"כ קרוב ל100% cover לפונקציות שם.
זה מאוד חשוב לי שיהיה 100%. עד כמה שזה נשמע מוזר, אבל מה זה אומר בעצם 100%?!

אז דבר ראשון מערך הבדיקות מתחלק למספר חלקים:

  • unit test – סט בדיקות שפונקציות עובדות כראוי – בד"כ זה אומר מעט קוד שבודק חלק קטן מתוך פונקציה.
  • בדיקות פונקציונאליות – מוכרת בשם integration testing – כלומר האם לוגיקה קטנה עושה מה שביקשו.
  • בדיקות שימושיות – בד"כ נקרא end to end או E2E בקיצור, אומר כי בודקים האם טופס מסוים מתפקד נכון
  • בדיקת מערכות – האם המערכת הכוללת עובדת ומתפקדת כמו שצריך, כולל תשתיות, ולא רק הקוד
  • בדיקות ביצועים – נקרא גם benchmarks – בדיקה עד כמה קוד, פונקציה או פוקציונאליות מהירה

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

אבל יש לי בעיה עם בדיקות של פונקציונאליות או בדיקות שימושיות. והבעיה היא שהם לא מדמים את המשתמשים עצמם כמו שבאת מאמינים שזה בודק.

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

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

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

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

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

3. אין לי אהבה לקוד

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

התפקיד של קוד הוא לפתור בעיה. צריך להיות ממוקדים בבעיה יותר מאשר כל דבר אחר.

הניסיון לכתוב את הדבר היפה ביותר בצורה הטובה ביותר עם O(log(n)) במקרה הכי אופטימי, ו O(n) הכי פסימי פשוט לא רלוונטית הרבה פעמים.

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

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

4. לא כל המפתחים חושבים זהה

יצא לי להביט על קוד אשר הבנתי אותו, אבל לא מה ניסו לפתור בו.
השתמשו ב hash map אשר מחזיק 3 דברים בתוכו בסה"כ ומוזן בזמן עליה, והבעיה היא ששינוי התוכן התבצע מתוך מספר טרדים, ואז קיבלנו race condition רנדומאליים וקריסות.

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

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

השימוש ב mutex במקרה הזה היה פותר, אך הבנה של הבחירה אולי היתה גורמת לי לשכתב קצת קוד בצורה אחרת שלא היה בה race condition אולי גם ללא שימוש ב mutex. למשל להקפיד שיש רק טרד אחד שיש לו יכולת כתיבה, או הבנה שאפשר לאתחל את המידע עוד לפני השימוש בטרדים, או אולי אפילו לא היה צורך ב map, אלא במשהו שיודע להתנהל עם טרדים.

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

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

5. מבחנים לא נכונים

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

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

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