קטגוריה: קוד פתוח

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

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

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

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

אם בקשה רגילה לוקחת פחות משניה, אז בקשה כפי שנעשתה עבור המצב הנוכחי דרשה ממני לספק את אותו פרק הזמן, אך בפועל לקחה כ 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. מבחנים לא נכונים

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

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

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

מבחן שגוי

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

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

אבל במקום, מקבלים שאלות בנושא של TCP/IP, כיצד מסד נתונים עובדים, איך יוצרים מודולים לקרנל וכיוב'.
מה עכשיו? האם אתם מתאימים לתפקיד?

אז הגזמתי קצת, נכון? ובכן לא.

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

שאלות בנושא התפקיד להראות יכולת והבנה חשובים הרבה יותר מאשר היכולת אז אני מדבר על full stack עבור פיתוח web, הנה מבחנים טובים יותר מאשר שאלות מדמ"ח:

  1. יש לי צורך לאסוף את הנתונים הבאים – מה הסכמה למסד הנתונים שתתכנן ולמה?
  2. האם מסד נתונים מבוסס סכמה באמת מתאים לזה, או אולי מסד נתונים מסוג אחר?
  3. תן לי הדגמה מתי תבחר בdjango/rails ומתי ב sinatra/flask?
  4. מתי Go עדיפה על פיתון/רובי?
  5. מה ההבדלים בין Vue/React לבין Angular?

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

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

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

במידה ולא אצליח לענות על השאלה עם המטריצה ואצליח לענות על השאלות עבור תפקיד ה full stack האם אינני מתאים לעבודה שכזו? הרי כיום אלא אם התפקיד דורש ממני יכולת לענות על הנושא, יש לי המון כלים להתמודד עם הנושא. כמובן שעדיף שאדע, אבל המבחן אינו מתאים לתפקיד.

זו כמובן דעתי, בלבד, בהצלחה 🙂

עלילות vim בריבוי שפות

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

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

כמובן שהאדם הזה הוא אני.

כרגע פתרתי את הבעיה, אך בגישה שאינני אוהב אותה.

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

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

אני לא היחיד שעושה את זה. ישנן הפצות שונות של vim, כאשר אחת המפורסמות בהן היא Space-VIM.
היא בנויה להיות גנרית ובעלת יכולת התאמה אישית.

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

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

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

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

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


כניסה לפרויקט קיים

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

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

התחלה

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

טעויות בכניסה לקוד/פרויקט קיים

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

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

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

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

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

הדרך שלי ללמוד

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

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

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

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

רכיבים קטנים

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

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

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

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

עבור מה השיטה לא מתאימה?

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

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

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

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

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

הגדרות שגויות לnvim

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

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

איזה סופ"ש אחד החלטתי לפתוח קוד Go שלי ולהמשיך ולעבוד עליו.
פתאום קיבלתי המון שגיאות לא קשורות לקוד שלי, אלא לvim-go.
אמנם עברו 3 חודשים מאז שהתחלתי לכתוב את הקוד בזמני הפרטי, אבל מה השתנה?!
הלכתי לפי הודעות השגיאה (בvim/nvim זה ‎:messages), וגיליתי שפונקציה לא קיימת עבור nvim. הלכתי לספריה ש vim-plug מתקין לי תוספים עבור nvim, ועם rg‏ (ripgrep) חיפשתי את הפונקציה ומצאתי אותה. מסתבר שקובץ חדש בשם config.vim לא נטען. אמממ, אבל הוא קיים בספריה.

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

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

set runtimepath+=~/.vim,~/.vim/after
set packpath+=~/.vim
source ~/.vimrc

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

החלטתי לעשות מהלך שעד עכשיו נמנעתי ממנו. לשים את ה"הפצת" vim שלי תחת nvim, ולעשות שינוי ל init.vim שלי. הוא כבר לא מתעניין ב vim בכלל. הכל נעשה דרכו, כולל הצבעה נכונה לספריות שונות.

לאחר ביצוע פעולה כזו, הכל עבד חלק 🙂

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

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