בשנים האחרונות אני רואה המון "תלונות" על שפת golang וכמה התחביר שלה נוראי.
אישית אני מאוד אוהב את השפה, ומעדיף לכתוב בה המון דברים. יותר מזה, קשה לי מאוד עם מרבית הפוסטים השונים המביאים "תלונות".
הסיבה ל"קושי" הזה, היא שהתלונות אינן ענייניות, אלא תלונות בסגנון של "אני רגיל לבצע לולאת while ובשפת go יש רק לולאת for המחליפה הכל".
במידה והתלונה היתה "אני זקוק ללולאת while כי הצורה לבצע איטרציה עם לולאת ה for ב go מונעת ממני לבצע …", הייתי מאוד שמח לביקורת שכזו, כי היא יכולה לתרום להתפתחות השפה ומתן מענה למקרים שכנראה כיום צריך לעקם אותם בשביל להשיג את אותה המטרה.
אז החלטתי לאחר כ11 או 12 שנים עם השפה לכתוב מה אני פחות אוהב, ואני מפריד בין שפה, ספריות המגיעות עם השפה (יש המון כאלו) וכן גישה או אפילו בעיות תיעוד.
זו לא רשימה מלאה, אבל כן רשימה שלדעתי מפריעה להרבה אנשים במצבים שונים.
אני נתקל בכל כך הרבה מידע מוזר על הנושא של eSIM, ומאמרים של עיתונות לאנשים טכניים בארץ שמציגים מידע לא נכון על העולם הזה, שלא לדבר על גישה אשר לדעתי היא ילדותית בסגנון של "אני רוצה, אבל לא מביאים לי".
אז החלטתי לנסות ולעשות קצת סדר בנושא.
למען גילוי נאות: בשנתיים האחרונות אני נציג של חברה מסחרית בועדת תקינה בנושא של eSIM (ועוד מספר תחומים) ומנסה לכתוב כהסבר טכני מופשט במטרה להסביר את הבסיס והרעיון בלבד.
בשנים האחרונות אנחנו רואים מספר החלטות תמוהות של חברת רד-האט בעיקר מאז שזו נרכשה על ידי IBM, ולמעשה היא נשארה יותר מותג מאשר חברה.
פוסט זה הוא דעתי על ההחלטה האחרונה (הקשורה להחלטה הקודמת של החברה) כראיה עסקית. ההחלטה היא כמובן על סגירת את הקוד – אשר מאפשר רק לבנות מחדש RHEL, אבל ללא יצירה של יצירות חדשות על בסיסו.
כמובן שאין לי בעיה על הנגשת קוד המקור רק ללקוחות החברה – דבר לגיטימי לגמרי גם עם GPL.
הנה בעיה מעניינת – כיצד אני מביא רשומה המתאימה ביותר להתחלה כלשהי, כאשר ההתחלה משתנה באורך/תווים שלה?
בשביל להיות פחות כללי, אציג את הבעיה כך: יש לי קידומות של מספרי טלפון ואני צריך למצוא מי ספק הטלפון שמחזיק בקידומת הזו.
כאשר אני יודע כי 2 תווים ראשונים מחזיקים תמיד בקידומת הבעיה מאוד פשוטה – אני חותך 2 תווים ראשונים ומחפש עליהם קידומות בצורה חד חד ערכית, ואם מדובר ב unique index חיפוש מהיר ביותר שאולי יעשה sequence scan במקום חיפוש באינדקס ויהיה מהיר מאוד.
אבל כאשר חלק מהקידומות הן בעלות 2 תווים, חלק אחר בעלות 3 תווים ואפילו ישנן קידומות בעלות 4 או 6 תווים כאשר התו האחרון הוא זה שמבדיל בין ספקיות שונות יש בעיה. הרי אין לי ידע מקדים מה הקידומת ומה הוא המספר עצמו.
למזלי יש כלים מאוד חזקים המסייעים בזה עם מסד נתונים PostgreSQL או PG בקיצור.
מזה זמן רב היתה לי בעיה מאוד רצינית עם systemd. יש לי תוכנה שיצרתי אשר לפי ארגומנטים שונים מתנהגת לגמרי שונה בזמן עליה שלה. למשל התמודדות עם docker, הרצת תהליך לפי כמות תורים (כל תור מקבל תהליך בפני עצמו) ועוד מספר התנהגויות.
כאשר אני רץ ב docker אין בעיה – דוקר מריץ אותי ישיר, אני משתמש במשתני סביבה של דוקר לקבל מידע שאני חייב, אך יש לי בעיה אחרת (שווה פוסט בפני עצמו) – הנחתת ביצועים ברמה מפחידה.
כאשר אני מריץ ידנית את התוכנה, הכל עובד ללא בעיות וההתנהגות היא כמצופה. אבל כאשר אני מריץ אותה ב systemd, וההגדרות של המערכת היא יצירה של הרבה פרוססים, המערכת נתקעת או מתה מהר מאוד ללא שגיאה כלשהי (לרוב מתה מהר).
מחקר
לקח לי שבועות לבדוק את הקוד, לשנות גישה, לנסות להריץ את המערכת בגישות אחרות, ותמיד רק כאשר זה רק ב systemd התהליכים בעיקר מתים.
התחלתי לשחק קצת עם הגדרות systemd בכיוונים של type ב service שיצרתי, אבל זו לא היתה הבעיה.
התחלתי לחשוב כי אולי מדובר במשתני סביבה, או אפילו limits שונים, אבל הליכה ל pid תחת proc לא הראתה משהו ממש שונה שהרים לי נורה אדומה או שוני הנדרש ממני מעבר להגדרות הסביבה עצמה שהוגדרו.
כאשר הרצתי ידנית – הדבר העיקרי שהיה שונה היו משתני סביבה של חיבור ssh. חשבתי כי אולי הבעיה היא stdout, אבל גם ככה אני תופס אותו וממיר אותו לקובץ, והוא אפילו כותב בזמן הריצה תחת systemd.
על מנת להבטיח כי זו אינה הבעיה, עשיתי הרצה עם detach ואז התנתקות מה ssh. חזרתי לשרת אחרי עשר דקות בערך והמערכת עדיין רצה ללא בעיה, למרות שכבר לא היה לה טרמינל, הצלחתי לשלול את זה לגמרי (?).
בנקודה הזו אני כבר חודש (לא קלנדרי או רצוף) חוקר את הבעיה, ומתחיל לחפש באגים ידועים של systemd. מסתבר אני לא היחיד שמתאר את התופעה, והיו תיקוני באגים שגרמו במקרים שונים להתרסקות של תוכנות, אבל אף אחת לא קשורה לשלי, והגרסה של systemd כבר מכילה את התיקונים לבאגים שמצאתי.
במקרה באחד הבאגים, היתה שאלה אשר ביקשה לקבל מידע על הגדרות בנושא של systemd.kill ושיש לבדוק אותו.
כאשר התחלתי לחפש על הנושא במנוע חיפוש, גיליתי כי מרבית השאילתות במנוע החיפוש היו איך "להרוג" את התוכנה עם systemd ואיך שולחים אותות (סיגנאלים) עם systemd, ופתאום האתר החביב על כולם stack-overflow גילה לי שיש אופציה ששוה לבדוק בשם KillMode (ואתם חשבתם כי דף 3-4 במנוע חיפוש זה מקום שפתרונות באים למות בהם).
חיפוש של systemd.kill עם KillMode הוביל אותי לתוצאות רלוונטיות יותר.
מציאת פיתרון
כאשר נכנסתי ל system.kill, יש הסבר שלדעתי לא מספיק טוב וממצא לאיך systemd מתמודד עם "הרג" של תהליכים בכל מצב.
ההגדרות עצמן יהיו תחת הגדרות ה service, אבל יש תיעוד מיוחד עבור נושאים שונים. וכאמור, לפחות עבורי, תיעוד הנושא של KillMode אינו מספק על כלל האופציות שיש לו.
מסתבר כי ברירת המחדל היא להרוג "קבוצה" של תהליכים, כלומר התהליך שלי והבנים שלו, גם אם אני הרצתי עם exec ומנותק לגמרי מהבנים, עצם זה שהתהליך הריץ עוד דברים בזמן עליה, תגרום לכך שהיציאה של התהליך (במקרה הזה), תהרוג את הבנים (תודה stack overflow להסבר).
הפיתרון היה לשים
KillMode=process
לא ברור לי כל כך התפקיד של האופציה (מבחינת איך היא באמת עובדת), אבל היא אומרת שבמידה וה"אבא" שוחרר מהעולם, אין לשחרר את הבנים גם כן. וזו "אופציה לא מומלצת" (wwz).
כבר מספר שבועות שהמערכת נשארת באוויר ולא מתה בגלל systemd (אלא באגים שלי שאני מגלה ומתקן תוך כדי, אבל זה התפקיד שלי – ליצור באגים במקומות חדשים).
מבט לאחור
היו לי מספר דברים מאוד מביכים:
לא שמתי לב כי בתחתית התיעוד של כל תיעוד בנושא יש הפניה לעוד נושאים עם קישור.
החיפושים שלי היו שגויים ובדיעבד רק כאשר הבנתי שהמילים שלי היו לא נכונות התחלתי למצוא מידע רלוונטי.
הגעתי למצב "שהסתובבתי סביב הזנב של עצמי" עם הבעיה במקום לשחרר אותה ולחזור בשלבים מאוחרים יותר.
כאשר הצלחתי להבין מה לחפש – המידע נהיה נגיש, אבל אוסף הדברים המביכים למעלה, גרם לכך שזה יקח זמן רב יותר, ולימוד אותי המון.
ב2017 עברתי מ VIM לפרויקט חדש (יחסית) בשם neovim אשר שם לו למטרה לבצע refactoring ב VIM ולהסיר קוד ישן, לתקן קוד קיים בעייתי, בו בעת להמשיך את VIM והתפתחות שלו.
למיטב זכרוני, הפרויקט הוקם לאחר שהמתכנת הראשי של neovim פשוט סורב ל Patch בתוך VIM שתיקן בעיה קריטית. הצורה לסירוב כפי שהבנתי לא היתה ממש "יפה" וזה הוביל לפרויקט חדש הלוקח את קוד המקור של VIM וממשיך משם.
בחודשים האחרונים עברתי לעבוד עם coc.nvim אשר מנסה לספק השלמה אוטומטית, תמיכה ב LSP, תמיכה ב Linting ואפילו Fuzzy Finder, וכל זה בשימוש של תוספים המבוססים על Visual Studio Code וכתוב ב node.js.
הבעיות שלי עם coc.nvim שיש לי איתו צרות של CPU גבוה מאוד ובנוסף זיכרון שמתמלא כאשר אני עובד עם קבצים של אלפי שורות קוד שכולם פתוחים ב buffer כי אני עובר בין אחד לשני הרבה. בנוסף לזה, הייתי מוצא מספר process של LSP שונים לאותה שפה (בפורטים שונים) עם הודעות שגיאה ב messages כשהוא מנסה להעלות אותם ונכשל (עד שמוצא פורט פנוי).
לאחר השחרור של 0.5 הבנתי כי אני צריך לשנות גישה והתחלתי לחשוב מחדש מה התוספות שאני רוצה להשתמש בהם וכיצד לכוון את nvim שיעבוד נכון עבורי והצרכים שלי.
nvim הנוכחי שלי
אז נכון לכתיבת שורות אלו, הצלחתי להתקבע על סט כלים חדש, אחרי ניסיונות של מספר רב של תוספים וכלים, בהם:
השינוי מאפשר לי להתאים כלים עבור neovim עם היכולות שלו ולמעשה אני מסיר תוספים לא נדרשים שהייתי צריך אותם ב vim 7 כי לvim עצמו לא היתה תמיכה בדברים.
עבור צביעת התחביר בחרתי לעבור לשימוש ב tree-sitter, תוסף שאמור לספק תמיכה מדוייקת יותר מאשר הצביעה ה"רגילה" המשתמשת בregex, אשר נמצא בשימוש אף הוא אצלי, במידת הצורך.
השימוש שלי ב nvim-lsp מאפשרת לי לקבל הגדרות ברירת מחדל (שאני יכול לעקוף) למנועים מוסימים ובכך אני לא זקוק ל ALE לדעת על בעיות מצד אחד, ומצד שני אין לי צורך ב coc להשלמה. זה מאפשר לי גם לספק כלי חדש לראות symbols שונים עם LSP בנוסף ל ctags למשל, ולכן אני משתמש ב vista.vim במקום Tagbar.
להצגת קבצים עברתי ל nvim-tree הכתוב ב lua. יש לתוסף כמה יכולות ממש נחמדות בברירת המחדל, וזה במקום NerdTree עם תוספות. כחלק מהיכולות של התוסף, זו יכולת מעקוב אחרי יצירת קבצים בזמן עבודה מתוך nvim ולרפרש אותם אוטומטית. ובנוסף הוא יודע להבין סטטוס של git ללא תוספות, ולדעת מתי לא לסמן יותר אם מתוך nvim עשיתי commit.
עבור מערכת ה Fuzzy Finding החלטתי לעבוד עם Telescope. התוסף מכוון אצלי לעבוד עם ripgrep בברירת מחדל, אבל יש לי גם תמיכה ל fzf במידת הצורך.
יכולות telescope
השלמה אוטומטית שלי היא כאמור באמצעות ddc.vim אשר משתמש בdino, ולמעט באג בהשלמה של zsh (אשר ביטלתי את התוסף), החתימה שלו מאוד קטנה בזיכרון וב cpu שלי, ומצד שני קל מאוד לקינפוג, אבל רק אחרי שמבינים את הגישה שלו (אשר לא אינטואיטיבית).
השלמה אוטומטית, פלוס הדפסת הcommit האחרון (שלא קיים) בשורה שאני עומד עליה
בנוסף, ל snippets שהייתי עובד איתם בצורה רגילה, החלטתי להוסיף לשם שינוי גם snippets לbolier plate שאני עושה המון בשפות שונות ואין לי snippets עבורם. החיבור עם ddc עובד ממש יפה לרוב. מערכת ה Snippets שבחרתי היא UltiSnips.
יש תמיכה ממש טובה ב git גם הצגה של מה נוסף/שונה/נחמק וכו'. ואפילו blame על השורה שאני עומד בה.
הצגת שגיאות מבסיס LSP
סיכום
נכון לכתיבת פוסט זה רוב הדברים שאני זקוק להם קיימים, אך לא מושלם. נשאר לי לשנות readme לגמרי, ולהעביר את זה ליציבות מלאה.
חלק מהעץ הראשיחלק מהעץ הראשיחלק מהעץ הראשיבמידה ואתם מעוניינים להשתמש בהגדרות שלי, תוכלו למצוא אותם כרגע כאן ובסוף זה יהיה כחלק מהעץ הראשי (במידה והקישור הקודם כבר אינו עובד).
מזה זמן (שנתיים או שלוש) שאני רוצה ללמוד את שפת Rust, היות ויש לי שימוש בשבילה – להעיף את ++C מדברים שאני צריך לפתח. הבעיה שגרמה לי לא לעשות זאת עד עכשיו זה זמן.
ההשקעה שלי לשפה התחילה ב"השגת" ספרות לימודית בנושא. כך שבנוסף לספר הרשמי של השפה, רכשתי גם ספר בשם Zero To production, אבל אין לי הרבה זמן לקרוא וכאשר יש לי אני עייף מידי לקלוט את מה שאני קורא, שלא לדבר על להבין את הקוד שאני כותב בעקבות הקריאה, אז ירדתי מהרעיון של לנסות ולקרוא ספר בשביל להתחיל ללמוד.
החלטתי ללמוד את השפה בדרך שבה אני לומד שפות אנושיות – עשייה בלי הבנה ואני משקיע על זה לרוב בין חצי שעה עד שעתיים ביום (תלוי בזמן ובאנרגיה).
הטענה של החברה כי היא מפסידה עסקים בכך שיש הפצה המבוססת על העבודה שלה מתוך RHEL מאוד בעייתית ואינה מציאותית ועל כך אני מעוניין להתייחס.
אתחיל בשאלה: מי הלקוחות של החברה? תשובה – גופים המכונים Enterprise. אם אתם לא מכירים, אז מדובר בגופים העושים עסקאות ומחזורים יחסית גדולים ועד אסטרונומיים (ומחולקים גם כ SME – Small Medium Enterprise ופשוט Enterprise). זו גם הגדרה משפטית פר מדינה, אבל אינני נכנס אליה כאן.
עכשיו מה אם אתה סטארטאפ, חברה קטנה, או יועץ עצמאי למשל? ובכן אתם לא קהל הלקוחות של החברה. היא אפילו לא תזיז פיקסל במסך בשביל לספק מענה.
פתרון CentOS הוא למעשה שימוש בהפצה יציבה ל"עניים" אשר מקבלים מעט מאוד תמיכה לטווח ארוך אבל עדיין יכולים להנות ממשהו ברור ויציב יחסית.
כמובן שגם הלקוחות של החברה רכבו על כך והם רכשו רישיון בודד או מספר קטן של רשיונות וכל השאר היה על CentOS, אבל זה כבר על החברה שלא סיפקה משהו עבור לקוחותיה שיסייע להם לבחור את RHEL במקום.
במידה ורד האט היתה עושה את מעשה "מיקרוסופט" ומכוונת לשווקים שונים ומשונים, המשבר שהיא חווה לא היה מתקיים.
מה הכוונה? אתם enterprise? יש לנו פתרון מדהים לכל דבר שאתם זקוקים לו, וזו העלות שלו.
אתם סטארטאפ? מדהים! יש לנו בדיוק את הדבר שמשתנה אתכם. אתה יועץ שמספק לחברות שונות? קח רישיון מפיץ בגישות שונות שמתאימות לך רק דבר איתנו ותקבל מענה ואף גיבוי במידת הצורך.
כמובן שזה רעיון "מהפכני" שקל לזרוק בבלוג, אבל למעשה ככה חברת מיקרוסופט עובדת. לכל סוג שוק היא מתאימה את המערכות שלה בגישה שמתאימה לשוק הזה. יותר מזה, יש לה תתי מחלקות של מכירות שמתמחות בשווקים שונים לגמרי.
אף אחד לא ירכוש Windows 10 Pro Enterprise אם הוא לא חלק מארגון או רוצה לשפוך המון כסף לחינם. אבל הרישיון מאפשר לEnterprise מענה ממש טוב עבור צרכי הניהול שלו בארגון.
אתם בית ספר? אין לכם צורך ביער מסובך, אבל כן מערכות לימוד קבלו רק היום 2 במחיר של 1 – Windows 10 לבתי ספר.
אתם סטארטאפ שצריך מערכת embedded? איך עוד לא בדקתם את Windows 10 for ARM?!
אתם צריכים למידת מכונה, כלים מתקדמים, או גמישות של ענן? איך עדיין לא ניסתם את Azure?! לא יפה!
יצרן OEM עם סביבה הדורשת מערכת הפעלה? למה לא אמרת?! יש לנו בדיוק את מה שאתה צריך!
מספיק דוגמאות?
כמובן שאני עדיין מפשט את הדברים מעבר למה שהם באמת, אבל אתם שמים לב איך לוקחים מוצר אחד (ויש למיקרוסופט המון מוצרים) ועוטפים אותו לשווקים שונים?
כל המעשים האלו לא נעשו על ידי רד האט. אני זוכר שהפנתי לאיש מכירות בתוך רד האט ישראל מישהו שמאוד רצה להחליף מערכת שעולה לו מאות אלפי דולרים בשנה במשהו טוב אבל גם בגישה יותר ראלית לעסק שלו שרד האט הציעה גם היא.
עברו שלוש שנים והוא עדיין "מחכה". רק עד שידברו איתו, הוא בנתיים הוא עבר לפתרון אחר בקוד פתוח של חברה אחרת שהוא משלם להם תמיכה ואפילו שילם להם פיתוח דריבר שהיה צריך לתת מערכת שלו. עד שיחזרו אליו כמובן (אנא המתן ותענה לפי התור).
הסיבה שאותו ליד חם זכה להתעלמות היא כי מחזור העסקים שלו היה קטן מידי לרד האט (תחקרו אותי מה גודל החברה לפני שהחליטו להתעלם).
זו הסיבה שלא העברתי עוד לידים חמים לחברה. הפנתי אותם לגופים אחרים במקום ששמחו לעשות עסקים.
כך שהבעיה היא לא גרסת קוד פתוח כפי שרד האט מציגה, אלא העדר ידע עם התמודדות של שווקים שונים שהיו יכולים להנות משירותיה, וזה רק על רד האט עצמה ולא על אלו שבוחרים לעבוד עם משהו "חופשי" ופתוח.
ואם כבר אני חייב לשלם על רישיון וזהו, למה לבחור דווקא ברד האט? למה לא בסוזה למשל או כל ספק אחר?!
ואני, אני מה אכפת לי, אני עובר לשרתי אובונטו ודביאן. בשבילי זה זהה לעבודה עם CentOS אין באמת תמיכה, אבל לפחות זה יציב עובד ונתמך למספר שנים ללא צורך בשדרוג "עצבני".
כאשר הייתי מתכנת צעיר, ראיתי קוד אשר לוקח struct (או משהו שמאגד שדות) שנשמר לקובץ, או נטען מקובץ בינארי. לא הבנתי בדיוק איך זה עובד. כלומר הבנתי מה זה struct, הבנתי מה הוא מספק לי כמתכנת, אבל לא הבנתי איך זה עובד מול קובץ. כיום אני מבין את זה ואנסה להסביר כאן על מנת להסביר את הנושא של יצירת קובץ wav.
הסיבה שאני מתעקש להסביר איך struct עובד עבור קובץ בינארי, היא משום שזה הטריק חשוב מאוד שמקל עלינו על החיים, ואעשה זאת על ידי הסבר כיצד יוצרים קובץ wav בלי להשתמש בספרייה כלשהי.
אשתמש בשפת Go לשם כך, אבל במידה וזו אינה השפה שלכם, תוכלו לממש את זה בכל שפת תכנות שתרצו בפועל, כלומר אין מגבלה כאן, אך יכול להיות שהצורה לבנית מבנה הנתונים תהיה שונה בהתאם לצורה בה השפה עובדת.
מה הוא struct?
בצורה המופשטת שלו, struct זה אוסף של שדות המכילות מבנה נתונים מוגדר בעל גודל מוגדר לפי חישוב גודל לפי שדה וגודל כלל השדות מחליט על גודל הstruct.
בעברית אנחנו נקרא ל struct "רשומה". המונח כמעט זהה למבנה טבלאי במסד נתונים רלציוני (במקרה הזה) ברעיון שלו (אך המימוש שונה במסד נתונים).
למשל במידה ויהיה הקוד הבא:
type S struct {
B1 byte
B2 byte
}
יש מבנה נתונים של 2 בתים: B1 ו B2. כלומר יש לי רשומה של 2 בתים וזה גודל הרשומה.
כלומר פעולת sizeof (אופרטור/פונקציה – בהתאם לשפה המחזירה גודל בבתים של מבנה או טיפוס נתונים) יחזיר לנו "2" כלומר מבנה הנתונים מחזיק 2 בתים.
אם הייתי מוסיף גם שדה מסוג uint64 מבנה הנתונים היה עכשיו בגודל 16 בתים.
ברשומה שלמעלה לכל שדה יש טווח מספרים כאשר במקרה הזה בין 0 ל255, כלומר אני יכול לייצג 256 מספרים שהם חיוביים.
הייצוג של הרשומה למעלה זהה לדבר הבא:
var B1, B2 byte
אך רק כיצוג של 2 בתים. אך כאן אין רשומה, ואין שדות, אלא רק משתנים שביחד מספקים 2 בתים.
אז למה לעבוד עם רשומה? ובכן יש מספר סיבות לכך והנה חלקם:
יצירת טיפוס נתונים שמחזיק כבר מבנה ידוע – חיסכון בקוד החוזר על עצמו.
סדר של מידע לפי צורך מסוים כאשר המיקום חשוב. כאשר המיקום אינו חשוב, לא "מרגישים" את הסדר.
יכולת לקרוא ולתחזק קוד בצורה פשוטה יותר מאשר משתנים אשר צריך לחפש אותם.
קל יותר לחשב גודל כולל של מידע שצריך אותו. או לפחות הגודל בתוך מבנה הנתונים עצמו (במקרה כאן של wav).
קל יותר להעביר מידע רב כמשתנה אחד – יש הרבה פעמים מגבלה של כמות ארגומנטים שניתן להעביר לפונקציה שמבוססת מערכת הפעלה, מעבד ו/או שפה (כאשר מדובר בשפה מפורשת).
קצת על קובץ Wav(e)
קובץ wav הוא סוג של מיכל (כלומר container) עבור מידע מסוג קול (אודיו) בלבד מבית מיקרוסופט. הקובץ יודע להחזיק מידע raw כלומר מידע שלא עבר עיבוד (PCM ו ADPCM) וכן של codec. כאשר אנחנו מדברים על codec אנו מדברים על ייצוג של מידע בצורה מעובדת, כדוגמת קיבוץ מידע, סימון מידע כדוגמת מתי מתחיל ונגמר שקט או כל דבר אחר אשר מכיל את המידע הגולמי בצורה מעובדת שיכולה גם להיות קטנה יותר מהמקור או לציין בצורה דיגיטלית מידע שלא ידוע בצורה "טהורה" של קול או ווידאו.
בנוסף לבסיס, הפורמט של קובץ ה wav יודע לעבוד עם "חתיכות" מידע, לרוב באמצעות מבנה בשם RIFF. פורמט קובץ הwav יכול להיות כחלק מcontainer אחר בשם AVI אשר גם הוא הגיע מבית מיקרוסופט, והפורמט יודע לשמור מידע של תמונה נעה (ווידאו) וכן אודיו לרוב בפורמט של WAV עם RIFF. כאמור, קובץ הWAV נוצר על ידי חברת מיקרוסופט, והוא למעשה פורמט מאוד "בסיסי" (אך לא פשוט) במערכת ההפעלה של החברה – Windows.
יצרתי באמצעות שפת C++ (וקצת C) איזשהו DLL המבצע קיצורי קוד עבורי שבכל דרך אחרת הייתי צריך לממש הרבה COM בשפת Go – כאב ראש עם הרבה boilerplate code של Vtbl (Virtual Function Table) ומימוש Interfaceים שלמים, כולל ירושות שלהם מinterface אחרים.
אני מאוד אוהב את שפת Go אבל יש לי גם ביקורת קשה כלפיה – הביקורת העיקרית שלי (לצורך הפוסט כי יש לי יותר מביקורת מאחת) – הוא שמצד אחד ניסו לספק לי קוד "גבוה" שלא מעניין אותו מערכת ההפעלה, כאשר מהצד השני, רואים שהוא נבנה עבור Unix/Linux וכאשר אנו נמצאים במערכת הפעלה שונה כדוגמת Windows מערכת ההפעלה מרגישה שם כ second class citizen.
בעיה ראשונה – יש לי פונקציה בdll המוגדרת בגדול מאוד בחתימה כזו:
void GetBuffer(type_t * in, const char **buffer, const unsigned int * length);
אני מקבל struct שמכיל מידע ומחזיר בתמורה buffer של בתים (במקרה הזה).
בGo יש ספרייה בשם unsafe אשר נועדה למצבים שבהם אני צריך לתרגם מצביע Go למצביע "מערכת" (כדוגמת C) למשל. היא מאוד לא מומלצת לשימוש כלשהו, כי כמו שהשם מרמז, היא לא בטוחה והיא תלוית התנהגות של מערכת ושוברת את כל הבטיחות ששפת Go מספקת לשפה.
הבעיה היא שבזמן טעינת DLL בזמן ריצה בצורה דינאמית עם LazyDLL (למשל) צריך להעביר פרמטרים וזה דורש מאיתנו להשתמש ב unsafe.Pointer בשילוב של טיפוס בשם uintptr.
יש הרבה תיעוד בנושא של unsafe.Pointer וגם המון פוסטים ודוגמאות ברשת, אבל רובם מסובכים מידי ולא תמיד "נכונות" לכל מצב.
בהתחלה כאשר ניסיתי לקבל את buffer חזרה, עבדתי לפי התיעוד. קיבלתי באמת מידע בינארי, אבל כאשר שמרתי אותו לקובץ ופתחתי אותו עם hex editor גיליתי הרבה "זבל" כמו תוכן זיכרון כללי של התוכנית שלי ולא התוכן שציפיתי לקבל.
אחרי יום שלם כמעט של האבקות חופשית החלטתי להתעלם מכל מה שקראתי וראיתי אצל אחרים וחזרתי למקורות הפשוטים ביותר:
בעצם "נזכרתי" כי מערך בC יכול להיות בעצם מצביע אשר אם אני מקבל את האורך שלו, אני יכול לקרוא רק את מה שאני צריך ולא צריך מעבר.
כלומר במקום הצעות להמיר את הslice לעוד מצביע עם uintptr ואז לבצע casting, חזרתי לחשיבה הפשוטה שדווקא היא עבדה וקיבלתי את התוכן הנכון.
הכנסתי לdll דגל אשר במידה ומדובר בגרסת פיתוח הוא גם כותב את buffer בעצמו לקובץ, וכך גם ידעתי להשוות בין הדברים (היות וקומפיילר של Go אינו יודע ליצור debug symbols בWindows) בצורה "טיפשה". וזאת לאחר ש delve לא הסכים לעבוד לי.
בקוד רגיל, ולא בהדגמה שכזו, חשוב גם לבדוק שגודל length אינו גדול יותר 2 << 24 לשם כך אפשר לבדוק את ה cap של ה slice ואין צורך לחשב מחדש.
בפוסט הבא בנושא אסביר כיצד להתמודד עם struct שלם בתוך DLL.
במהלך 2020 יצרתי מערכות הרצות על Windows. זה לא במקום עולם הלינוקס שאני מפתח בו, אבל כן קשור לעולם שאני חי בו – פיתוח מערכות רשת, טלפוניה ומערכות IT.
מי שלא מכיר, הייתי בעבר מתכנת Windows, פיתחתי בעיקר עם VB/VBA ודלפי, כך שהזיכרון שלי על win api הדהים אותי שעדיין קיים ורלוונטי יחסית.
מצד אחד מיקרוסופט מנסה להיפתר לאט לאט מקריאות ישנות, ומצד שני היא מנסה לתת דגש רחב יותר כלפי העבודה מול COM ומשפחתו. ואם זה לא מספיק, אז מיקרוסופט מנסה לספק מענה לעוד שפות תכנות כדוגמת Javascript באמצעות מנגנון הנקרא Universal Windows Platform (או UWP בקיצור).
כל זה הכניס אותי למצב בו חלק מהדברים שאני מכיר נמצאים כ deprecated כאשר אני חייב למצוא להם תחליף. וחלק נמצאים במצב של "קיים, אבל אנחנו הולכים לכיוון אחר אז מתישהו זה יהיה deprecated" או במילים אחרות legacy. ועבור מיקרוסופט זה אותו הדבר פחות או יותר. וזה הכניס אותי למצב שפתאום גיליתי את UWP.
אחד הדברים הראשונים שכל מתכנת בעולם הלינוקס מגלה – Windows אינה מערכת קלה לפיתוח בה. היא גורמת לכל דבר שמרגיש מאוד קל ופשוט בלינוקס להיות מסורבל ומסובך בWindows ברמה שגורמת לפעמים לתסכול רב.
המערכת שאני כותב היא cross platform וכתובה ב Go. בלינוקס היה לי מאוד קל להתחבר לlibc כאשר אין לי binding או חבילה מוכנה בGo (ואין לי את הזמן ליצור את התמיכה בכל הapi set ואני צריך רק פונקציות וstruct מסוימים מאוד), אבל בווינדוז אני מוצא את עצמי עובד מאוד קשה מול COM או פשוט ליצור ב VS איזשהו DLL שאני כותב ב C/++ (משלב בין שניהם כי לא באמת רגיל לתכנת ב ++C).
התסכול הבא, היה לאבחן בעיות ותקלות. הנה בעיה מוזרה – כלל החומורת מכילות דריברים המספקים לי פידבק אודות מעשים מסויימים באמצעות Windows Messages. אבל – חלק מהחומורת ו/או דריברים לא מעלים את ה events וחלקם כן. יותר מזה, אין ב DC שום דבר המגביל זאת, וכן אין משהו שחוסם משהו. אמנם יש דרכים כנראה לפי התיעוד להכריח את ווינדוז לעשות זאת, אבל זה משהו שאסור לי לעשות בשל רגולציות שונות עבור אותה מערכת (לפי מה שהוסבר לי) ולכן אם זה לא עובד אין לך תמיכה במערכת.
במהלך הפיתוח יצא לי פעם ראשונה בחיים שלי לכתוב גם קוד ב #C. יש בו רעיונות ממש נחמדים, אם כי הרעיון המרכזי של תכנות מונחה עצמים עדיין בעייתי בעיני – כלומר שאני חייב תמיד אובייקט לכל דבר אם כי לדעתי הם פתרו כמה דברים שג'אווה (לפחות הגרסאות שאני נגעתי בהן) מאוד מקשות על ההתמודדות איתן, אבל זו לא מטרת הפוסט.
בזמן התמודדות הבעיות הבנתי למה לינוקס נוח עבורי כל כך, בזמן שפיתוח בWindows ממש לא (אפל איפשהו באמצע). בעיה מוזרה שהיתה לי – יוצר קובץ (בינארי ו/או טקסט) כותב אליו, עושה close ואולי אפילו flush לפני – מנסה לגשת אליו ממערכת אחרת והקובץ לא קיים, או בגודל 0. הבעיה היתה שבעוד שה File Descriptor (או Handle בWindows) שוחרר, מערכת ההפעלה עדיין לא כתבה את המידע או כל המידע לקובץ בפועל. עד עכשיו אני לא יודע למה, וזה גם לא מתרחש כל הזמן.
בלינוקס מאוד קל להבין עם strace או ltrace מה קרה בפועל עם תוכנה שרצה אבל בווינדוז זה לא כזה פשוט. למעשה לא מכיר דרך בווינדוז, אשמח להארה בנושא.
עוד בעיה – יצרתי dll ב ++C (או חצי ++C וחצי C מבחינת התחביר), לדעתי ייצאתי (כלומר export) את כל הפונקציות שרציתי, אבל בפועל לא הצלחתי לעשות לכולן link. בלינוקס אני יכול עם objdump לראות מה ייצאתי בפועל, אבל בווינדוז? אני צריך כלי חיצוני שלא מגיע עם VS.
ניסיתי גם להדפיס למסך תוכן שהוא utf-8 לא הכל מוצג. עכשיו שאלות – האם זה פונטים? האם זה באג שלי? הcmd לא כזה ברור אם הוא מסוגל להציג את זה בכלל (הבעיה היתה הגדרות locale של פונטים). למשל בלינוקס הרצה של טקסט מול od -x תעזור לי מאוד לראות ב hex האם מדובר בבעיה שלי או משהו אחר.
כאשר ניסיתי לעבוד ב VS עם breakpoints להבין את הבעיה למעלה הגעתי למצב שהווינדוז נתקע עם 100% cpu במשך 45 דקות. מה שקרה בסוף זה אתחול כפוי של מערכת ההפעלה כי שום דבר לא הגיב לי. זה נמשך 45 דקות כי הייתי צריך להתמודד בין לבין עם משהו אחר אז השארתי את זה לרוץ בזמן שעברתי וכשחזרתי זה עדיין היה תקוע.
כאשר אני כותב מערכות לווינדוז, אני מרגיש כמו משהו זר שאיכשהו בכל זאת זה עובד, אבל בקסם, בעוד שלינוקס זורם לי מאוד למרות שאני עובד ב (neo)vim, וטרמינל בעיקר במקום איזשהו IDE גדול.
והפיתוח בWindows ועבור Windows מתסכל מאוד. לא בגלל חוסר הכרות עם המערכת באמת, אלא כי היא בנויה למשתמשים "פשוטים" (יחסית) ויש בחירה לא לאפשר לעשות איתה משהו באמת מתקדם בצורה פשוטה כתוצאה מכך.
יצא לי להכיר אנשים הסובלים מבעיות שונות, למשל דיסלקציה, בעיות ראיה, בעיות שמיעה, שיתוק כלשהו ואפילו כאבי שרירים כרוניים.
כאבי שרירים כרוניים פחות מוכרים, אבל אלו גומרים לכך שקשה מאוד להשקיע אנרגיה וכוח לבצע פעולות שלפעמים עושים רושם כמשהו מאוד פשוט וקל.
נסו לקחת שופרפרת משחת שיניים וללחוץ עליה בשביל שתצא לכיוון המברשת. זו לא פעולה כזו פשוטה. גם גלגול השופרפרת אינה פעולה פשוטה בכלל. אז יש כלים שונים לסיוע, אבל – לרוב הם מסובכים יותר מבחינת הדרישות שלהם מאשר השימוש בשפורפרת עצמה עבור אלו הסובלים מכאבי שרירים כרוניים.
כלומר ממשקי המשתמש עבור סיוע לאנשים הסובלים מכאבי שרירים כרוניים אינם מתאימים אותם האנשים.
שמתי לב לדבר נוסף – אנשים מבוגרים מאוד מתקשים להשתמש בעכבר ובמקלדת. לגבי העכבר, מדובר בנושא שבו קשה להם לקשר את מיקום היד שלהם לכיוון הסמן. לגבי המקלדת – הם לא מחזיקים בזיכרון תנועתי, אלא מחפשים כל פעם מחדש את האות, כי הפריסה של האותיות אינם בנויות בצורה שברורה לעין איך לחפש.
יצירת ממשקים שקל להתמודד עם מקשים, אבל בלי צורך לשים את הידיים על המון מקשים בו זמנית, וכן יצירת מקלדת עם פריסת מקשים אשר הגיונית עבור אנשים מבוגרים – למשל סידור לפי א' ב' במקום סידור לפי אחוז השימושיות בתווים, היתה מקלה עליהם מאוד (לדעתי לפחות).
לי גם יש בעיות – צבעים שונים שהם בהירים מידי גורמים לי לתחושת סינוור. רקע זז שעליו טקסט גורמים לי לסחרחורות ובחילות במידה ואני צריך להשקיע את הריכוז להבין את הכתוב. יותר מאתר אחד פספס אותי לגמרי בשל כך, כי פשוט היצירתיות של אנשי מקצוע לתכנון ממשקים וכן הגרפיקאיים גרמו לי לברוח מהמקום.
זה התחיל בערך בעשור האחרון – יותר ויותר חברות הולכות לאנשים שהמקצוע שלהם – "חווית משתמש" ו/או "תכנון ממשקים", ונראה כי מאז שזה מתרחש – לי קשה מאוד להתמודד עם הרבה מאוד תכנים. שמתי לב כי ככול שהממשק נקי יותר וממוקד יותר במה שאני צריך ממנו – ככה קל לי יותר לעבוד איתו, בעוד שככול שהוא עשיר יותר בצבעים וכו' קשה לי להתמודד איתו.
ככה גם אני מנסה ליצור ממשקים למערכות שאני יוצר – נקי וממוקד מאוד עם מינימום צבעים ו"גרפיקה", רק מה שנדרש לביצוע הפעולה וכמה שיותר ממוקד.
אני חושב כי אנשי 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. היות ופעולה של קריאה לפונקציה ללא צורך היא פעולה יקרה מידי. בייחוד אם רק מהקריאה כאן אין לכם מושג מה כל פונקציה עושה בתוכה.
אבל, יש מצבים בהם אני יכול לוותר לגמרי לשאלת התנאי:
כאן יצרתי מערכת של רישום פונקציות שונות על ידי שימוש ב 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 כאילו היה קורונה, בעייתי מידי ויכולה לסבך את תחזוקת הקוד ואף להוסיף עוד כמה קריאות מיותרות במערכת. ולא להזכיר כי אולי זה לא יהיה קריא, ויש להשתמש בגישה הזו רק כאשר היא נכונה, ולא כמשהו דתי שיש להימנע.
נדרשתי למערכת אשר צריכה לעבוד מאחורי 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.
קובץ ההגדרות יראה בצורה הזו ב"לקוח":
יש לי מספר מערכות שפיתחתי במהלך השנים שנמצאות בשימוש יומיומי על ידי מספר חברות מאוד מוכרות בארץ וכן משרדי ממשלה.
במהלך הקורונה, עוד משרד ממשלתי החל להשתמש בו על מנת לדווח לאנשים כי שהו במחיצת חולי קורונה, או נמצאו חיוביים בעצמם.
אם עד הקורונה, השימוש במערכת שלי, גם בימי בחירות עמד על כמות מסויימת, לכמות הזו נוספה עוד דרישה – לא עמידה בעוד מליוני בקשות בשניה (למזלנו), אלא להתמודד עם בקשות מאוד גדולות שמכילות יותר מידע ממה שהמערכת שלי תוכננה להתמודד מההתחלה, וזאת מבלי לפגוע משתמשים הרגילים הקיימים במערכת.
אם בקשה רגילה לוקחת פחות משניה, אז בקשה כפי שנעשתה עבור המצב הנוכחי דרשה ממני לספק את אותו פרק הזמן, אך בפועל לקחה כ 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, אשר עשה עבודה מדהימה, אבל גרם לי להתקין המון תלויות.
החלטתי בהמלצת מאיר לנסות את coc – שפירושו הוא Conquer of Completion .
התוסף הוא בפני עצמו מאוד מעניין. הוא משתמש מאחורי הקלעים בחלק מכובד של תוספים המגיעים מ Visual Studio Code של מיקרוסופט, בנוסף לשימוש ב LanguageServer.
הוא מחזיק בתמיכה של "חלונות צפים" שנכנס לשימוש ב neovim רק לאחרונה:
רואים את התיעוד של הפונקציה Errorf? זה חלון צף ב neovim, לפחות סגנון אחד שלו.עוד השלמה שהוא מכניס:
כלומר בכל נושא של השלמה התוסף מספק יכולת לקבל תיעוד, לדעת לפי תוכן מה להשלים ואיך, ולעקוב אחרי.
וזהו, זה כל תפקיד הכלי, ובזכותו קיבלתי חצי מיכולות של IDE בסיסי.
בנוסף, אני משתמש ב ALE אשר יודע לתת לי חיווי של שגיאות באמצעות תמיכה בlinters, ואף לנסות לתקן דברים אם יש לו כלים לזה.
עד כאן הכל טוב
אבל, שמתי לב למספר בעיות חדשות שצצו לי במערכת. למשל בגלל שcoc משתמש ב node, הוא שוקל יחסית הרבה בזמן ריצה על ה cpu ולא רק על הזיכרון.
יש לו המון אופציות להגדרות, אבל חסרים לי כמה יכולות בסיסיות שהיו לי ב deoplete.
הפשוטה שבהם היא היכולת להשלים רק מה שחסר בטקסט.
למשל יש פונקציה בשם Write, ואני רוצה לבחור ב Fwrite במקום. עם Deoplete הוא ידע להחליף את מה שצריך.
אף ב coc אקבל את FwriteWrite במקום בזמן השלמה.
שורה תחתונה
אחרי חודש וחצי של משחקים, אני חייב לציין שאני מאוד אוהב את coc, וחושב לייצב אותו יותר ב"הפצה" שלי של vim.
הקוד שאתם רואים בתמונה הוא חלק מפרוייקט שפתחתי בגיטהאב שכולכם יכולים להנות ממנו – תמיכה בספקי SMS שונים על ידי יצירת SDK עם בסיס קוד אחיד (go/golang interface).
נכון לכתיבת שורות אלו, יש תמיכה במספר ספקיות ישראליות וזה יתרחב.
השבוע אדם ביקש ממני אם אוכל לתת לו להשתמש באוסף ושאלתי את עצמי למה בעצם אדם צריך לעבור על הקובץ במקום פשוט לקבל ציטוט, ו… יצרתי בכמה שעות בלילה ממשק ווב נחמד שמציג כל פעם ציטוט רנדומאלי אחר.
כל הקוד נמצא (נכון לכתיבת הפוסט) בקובץ javascript בודד, שכתוב ב es6 והוא קורא את הקובץ באמצעות fetch ומפרסר את הציטוטים.
לאחר הריצה הראשונית, כל הצגה של ציטוט חדש, מתבצע מתוך הזיכרון ולא מטעינה חדשה של המסך.
כל זה ללא שימוש בשום framework או ספרייה כלשהי, אלא נטו קוד שלי.
נכון לכתיבת פוסט זה, רק השימוש בקוד המעתיק לclipboard שכתבתי לא מתפקד כמו שצריך ולכן אינו בשימוש כרגע.
החלטתי לעשות סוג של פוסט סיכום שנה, ואז קלטתי שלמדתי המון דברים בשנה החולפת, אז חשבתי לתת עליהם ראשי פרקים. חשוב לי להדגיש כי המקצוע שלי הוא בתכנות, והנושא הוא על תכנות. הסדר של הלימוד אינו משנה, ונבחר אקראית על ידי 🙂
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, הנה מבחנים טובים יותר מאשר שאלות מדמ"ח:
יש לי צורך לאסוף את הנתונים הבאים – מה הסכמה למסד הנתונים שתתכנן ולמה?
האם מסד נתונים מבוסס סכמה באמת מתאים לזה, או אולי מסד נתונים מסוג אחר?
תן לי הדגמה מתי תבחר בdjango/rails ומתי ב sinatra/flask?
מתי Go עדיפה על פיתון/רובי?
מה ההבדלים בין Vue/React לבין Angular?
אלו לא רק שאלות, אלא ניתן לפתח אותם כמבחנים ממש, והם ממוקדים מאוד אודות התפקיד, כי הם נדרשים לתפקיד עצמו.
שאלות כמו "יש לי מטריצה לא ממיונת, על כל שורה בה איך תמצא את המספר הגדול ביותר והקטן ביותר, מה התוצאה של המכפלה השלישית של כלל המספרים האלו? ואם יש גם מספרים שליליים?" הן שאלות לא רלוונטיות.
למה הן לא רלוונטיות? נגיד ואני יודע לשלוף את האלגוריתם הכי יעיל, להגיד לכם מה ה O(n) שלהם, וכמה סייקלים של cpu יקח לסרוק את המטריצה, האם אני יודע לענות על חמשת השאלות שהעלתי למעלה? לא בהכרח, אבל הן חשובות לתפקיד.
במידה ולא אצליח לענות על השאלה עם המטריצה ואצליח לענות על השאלות עבור תפקיד ה full stack האם אינני מתאים לעבודה שכזו? הרי כיום אלא אם התפקיד דורש ממני יכולת לענות על הנושא, יש לי המון כלים להתמודד עם הנושא. כמובן שעדיף שאדע, אבל המבחן אינו מתאים לתפקיד.
פתאום קם אדם בבוקר, רוצה לכתוב תוכנה ומתחיל לקבל הודעות שגיאה.
בהתחלה נראה כאילו הבעיה היא בהגדרות שגויות של התמיכה הכפולה שלו ב 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 שאני משתמש בה, אך לא מפתח אותה, אתמקד במקור השימוש הבעייתי בשביל לתקן את הבעיה, או לנסות ולהבין אותה. אך במידה והייתי כחלק ממפתחי הספריה, היה חשוב לי להיכנס לבסיס כמו שצריך.
כמו כל סיפור טוב בעולם הטכני, גם הסיפור הזה מתחיל עם באג.
מזה זמן רב, שאני כמעט ולא נוגע ב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. שניהם מסוגלים לחיות זה לצד זה בהפצה שלי עכשיו.
במידה וגם אתם חווים בעיות בסגנון, אני מקווה כי הפוסט יעזור לכם לפתור אותם.
לפני מספר ימים ראיתי הודעה בקבוצת טלגרהם של פיתון עם שאלה איך למצוא פורטים פתוחים.
כולם הציעו לבחור "לרכב" על nmap, ואילו אני הצעתי משהו מעט שונה, אשר זכה להתייחסות.
אני מדבר כרגע על TCP. ובשביל להבין כמה זה לא מסובך, צריך להבין כיצד בקשת TCP עובדת בעצם.
ישנם מספר פעולות אשר נמצאות ברמה נמוכה יותר (בתוך מימוש TCP), אשר מבצעות עבורינו לחיצת יד. פעולות אלו מסוגלות לעשות זאת רק כאשר יש תשובה בצד השני, ומקבלים ack.
אם לא מקבלים אישור, יכולים להיות שני גורמים:
התעבורה עברה, אבל לא קיבלנו אישור – הפורט פתוח אבל מפולטר (מלווה ב Connection Refused).
אין פורט פתוח – אולי מפולטר, ואם כן מקבל drop כאילו אין פורט פתוח.
פעולה זו של SYNchronize היא הדרך עבורינו לגלות האם TCP פתוח.
ההצעה הפשוטה ביותר להתמודד עם זה, זה לנסות לפתוח חיבור TCP בפורט המבוקש לכתובת, במידה ונפתח, החיבור פתוח. במידה והחיבור נכשל (אם קיבלנו timeout או connection refused), אנחנו יודעים שהוא לא פתוח. כאשר האחרון אומר כי הוא מפולטר.
זו לא הדרך היחידה לעשות את זה, אבל זו הדרך הפשוטה ביותר.
יצרתי מימוש לזה בצורה מאוד נאיבית ברובי וגו, בתקוה שאולי תעזור גם לאחרים להבין שאין הרבה קסמים, ולפעמים השקעה של חצי שעה במקום "לרכב" על תוכנה קיימת יכולה להיות נחמדה 🙂
רובי:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
המון שנים אני מוצא את עצמי יוצא מנקודת הנחה כי מתודולוגיות תוכנה הן כלי אחד מהחשובים ביותר שיש לנו המתכנתים לניהול פרוייקטים ותוכנות בכלל.
בנתיים איבדתי הרבה שיער, וכמות שורות הקוד שאני כותב לצערי רק עולה (בכל זאת, המתודולוגיה אומרת כי למחוק קוד זה הכי טוב), והדעות שלי בנושא פיתוח לאט לאט משתנות.
התחלתי להבין כי אני כבר לא בטוח במתודולוגיות, כאשר שמתי לב שאני תמיד מחפש קודם את הסיפור מאחורי השינוי של קוד, מאשר להגיד "שמע, הקוד הזה משוכפל, זה לא טוב" למשל.
כיצד זה התחיל?
על פניו קוד משוכפל זה גרוע, וזה מקום שאפשר ליפול בו, נכון?
אז זהו, שזה מורכב יותר. היה לי review כזה לקוד, ובמקום להעיר על השכפול, שאלתי למה זה משוכפל. כלומר ביקשתי את הסיפור מאחורי הדברים.
מסתבר שהכותב (מתכנת מוכשר ביותר), הגיע להבנה כי מה שהוא מפתח לא צריך להיות בשימוש לאחר יד, וחשוב להבין את המשמעות של השימוש של מה שעושים.
לכן, לדעתו עדיף לכתוב מעט יותר קוד, כי צריך לעצור ולהבין לעומק את המשמעות של הדברים.
בזכות השאלה שלי, הוא הוסיף גם הערות בקוד שמסבירות את זה. והנה סיבה שכנראה מספיק טובה מדוע שכפול קוד במקום הספציפי הזה דווקא חשוב לעשייה.
העניין הוא שעבדתי גם עם אנשים שהם מאוד מוחלטים בעשיה. הם מעירים בשחור ולבן, ולא שמים לב שבין review ל review הם לא אחידים בדיעות שלהם. כלומר אם פעם אני כותב קוד בגישה של ההערות הקודמות שלהם, פתאום בהערות החדשות, זה כבר לא טוב וצריך לכתוב קוד בגישה אחרת על אותו סגנון שהם במקור רצו שאקח.
אני חושב שזה מה שגרם לי לשנות את הגישה שלי.
כאשר אנחנו עובדים בקבוצה, מי שמבצע משימה כלשהי נמצא עם הרבה יותר מידע מאשר מי שעושה לו review.
הוא מבין יותר את כל מה שקשור למה שהוא עובד עליו, עשה ניסויים, מבין את ההשכלות של דברים (בתקווה לפחות) וכיוב'. כאשר אני עושה את ה Review, אני מקבל תוצר לעיניים שלי, אבל לא את התהליך שהוביל אליו.
הסיפור של התהליך מאוד חשוב לא פחות מהתוצר. כלומר הדרך המתמטית שתמיד ביקשו מאיתנו להציג, ולא רק את התוצאה, פתאום אני מבין כמה היא חשובה.
שאלה אחת שווה אלפי שורות קוד
נגיד ויש לי דיווח על באג, ששדה מסוים לא מוצג על המסך.
זה כל המידע שאני מקבל. חסרים כאן המון פרטים.
גם אם אני יודע בדיוק על מה מדובר (מסך בודד, ושדה בודד), עדיין אין מספיק פרטים!
באיזה שלב הוא לא מוצג?
האם הוא היה מוצג בעבר?
האם הוא צריך להיות מוצג?
מתי הוא אמור להיות מוסתר?
האם יש שימוש בשדה הזה בכלל?
חמשת השאלות למעלה נראות מוזרות, אבל הן סופר חשובות (הן לא לפי הסדר, אבל כרגע לדיון הסדר לא חשוב).
במידה ו"האם יש שימוש בשדה הזה בכלל?" היא שלילית, הרי השאלה אם השדה מוצג או לא, לא רלוונטית כרגע. כי המערכת עושה מה שהיא אמורה, אבל היא מובילה אותנו לשאלה אחרת: "האם השדה צריך להיות בשימוש?"
במידה והתשובה היא כן, יש לנו המשך ברור של קו שאלות.
כלל השאלות ששאלתי, מובילות אותנו למסלולים שונים. חלק מהשאלות נראות זהות, עם ניסוח שונה, אבל למעשה הן שונות לגמרי.
כי המסלול שהתשובה תספק יהיה שונה. למשל "מתי הוא אמור להיות מוסתר" לא זהה ל "האם הוא צריך להיות מוצג".
כי אלו שני תהליכים שונים, הסתרה והצגה. כי האם צריך להסתיר את השדה כאשר הוא כבר מוצג? אם כן, אז מתי?
ככול שיהיו יותר שאלות, ככה נוכל לקבל תמונה ברורה ומוגדרת מאוד. התמונה הזו, למרות שהיא מאוד לא ידידותית למתכנן, מובילה לבניה נכונה יותר של מערכת או משהו מסוים.
ככול שנבין יותר לעומק את הצורך והרצון, ככה התוצר יהיה טוב יותר, ולא נכתוב קוד מיותר או נשקיע אנרגיה מיותרת.
ספר לי סיפור
בארץ רחוקה, לפני הרבה שנים, חיו להם יצורים מוזרים בשם מתכנתים.
אותם יצורים היו לוקחים משהו לוגי והופכים אותו לאומנות, ומשקיעים בהרבה תפל במקום להתמקד בעיקר.
כל מה שנדרש מאיתנו לעשיה דורש מאיתנו להבין תמונה המורכבת מהמון פיקסלים קטנים, ברזולוציה מטורפת.
אם אנחנו לא מבינים הקשרים של עשייה, אנחנו עובדים לחינם ומשקיעים זמן בדבר הלא נכון.
אבל יותר מזה, אם עוד שנה מהיום אני צריך לחזור לקוד ואני לא זוכר מה ביקשו ממני, לא תמיד יהיה אפשרי להתמודד כמו שצריך עם הקוד.
סיפור על מה כתוב כרגע כחלק מתיעוד של PR ו/או אפילו bug כתוב יכול לסייע להתנהל ולהתמודד עם הרבה שאלות. לפעמים תיאור המצב הקיים ומה הניסיון לשנות בו, או להוסיף אליו גם יסביר את הלך החשיבה במימוש אותו קוד. כאשר זה קורה השאלות של "למה לעזאזל זה כתוב בצורה הזו, אפשר לעשות את זה בצורה אחרת", יכולים להפוך ל"אוקי, אז זה כנראה התאים מאוד, כי זה שיפר מצב קיים, המצב הנוכחי דורש שינוי חדש".
גם אדם חדש הנכנס לקוד או אפילו לחברה או פרויקט יכול לעשות זאת בצורה טובה ומהירה יותר, כי יש לו נקודת התייחסות.
מתכנת ❤ קוד
ביקשו ממני פעם לחשב חישוב תאריכים בצורה מאוד לא פשוטה.
לחשב את כל יום שלישי הקרוב ביותר לשבוע עבודה. כאשר אם מדובר ביום ראשון זה יהיה יום שלישי של אותו שבוע, אבל אם מדובר ביום שני, זה יהיה שבוע הבא כבר.
אבל על זה, להכניס בחשבון תאריכים כדוגמת חגים ומועדים שונים מוכרים מראש.
אחרי יומיים של חשיבה מאומצת, ואבטיפוס שלא היה כתוב יפה, מצאתי פתרון. כתבתי קוד מאוד יפה ומתועד שמסביר את הלוגיקה מאחורי החישוב ומה כל בלוק בפנים עושה בשביל שזה יהיה קריא וברור.
זה עבד בדיוק חודש, ו…אנשי הפרודקט האלו לא מבינים כלום. אני אומר לכם,הם אנשים מעצבנים שרק רוצים להרוס למתכנתים. לקחו את הקוד המדהים שלי, אלגוריתם כל כך מדהים, ו… לפח 😠
וכאן מתחילה הבעיה. שלי, לא של אנשי ה product. התאהבתי בקוד שיצרתי. הוא היה כזה מושלם, ממש לקחת להכיר להורים. אבל הדרישה העסקית השתנתה, והתפקיד שלי כמפתח הוא לממש דרישה עסקית ולא את האהבה שלי לקוד, את זה צריך לעשות בזמני הפרטי (אוהב אותך אלגוריתם שלי ❤).
ההבנה כי קוד זה דבר נחמד, יפה, וכיף לכתוב אותו מגיעה מאוד מהר, אבל ההבנה כי זה כלי בלבד להשיג משהו שבסופו של דבר משלם את המשכורת, או את התקציב לפרוייקט שאתה בו, לוקחת הרבה שנים לקבלה. קשים הם חיי האמן.
משחק סכום אפס
יצא לי לעבוד עם בוגר טכניון שלמד שם תואר ראשון ושני. כל משפט חמישי שהיה יוצא לו מהפה על מידע שהוא היה מתעסק איתו "תשמע, זה משחק סכום אפס, לא משנה אם אני אעביר את זה כאן או כאן, עדיין זה אותו מידע ויקח אותו זמן לנתח אותו".
יש לי בעיה עם האמירה הזו. זה לא אומר שהיא לא מתקיימת, אבל לרוב מה שחסר זו הגישה לסדר פעולות נכונה, ולא כמשחק סכום אפס בדיוק.
לאחרונה עברתי דירה. ירשתי שם מקרר עם מקפיא. שניהם קטנים מאוד יחסית לדירה הקודמת. כאשר העברתי את כל מה שהיה לי שתפס מדף במקפיא הישן, כמעט שום דבר לא נכנס לחדש. משחק סכום אפס נכון? לא משנה כמה אנסה לסדר את הדברים, הרי הם תופסים אותו נפח, ולכן אי אפשר למצוא להם מקום.
אז השקעתי חמש דקות בלעשות סוג של sorting לגבי הצורה, גובה, רוחב וכו', לאחר החמש דקות האלו, לא רק שהכל נכנס כמו שצריך, נשאר לי הרבה יותר מקום ממה שחשבתי שיש.
הדרך הראשונה שלי לסדר את המקפיא היתה סוג של brute force. מה שנכנס ראשון הוא נמצא איפה שנמצא. אבל זה חסם דברים אחרים, ובגישה כזו לא גיליתי שיש הרבה רווח כלוא לא בשימוש ולא מנוצל שאין לי גישה אליו.
שינוי הגישה הזו של brute force יצרה פלא הנדסי שבו אני מגלה כי אני לא משתמש נכון במקום שיש לי, וניצול נכון שינה לגמרי את ההתיחסות לגודל והנפח של הדברים. עכשיו יש לי פי 3 יותר דברים ממה שהתחלתי, ועדיין יש לי מקום פנוי במקפיא.
אותו הדבר עם תוכנה! הרבה פעמים נדמה לנו כי לא משנה סדר פעולות תמיד התוצר יהיה איטי מאוד, או יקח הרבה נפח וכו', אבל לא תמיד זה המצב.
למשל קרה במקום שעבדתי בו מצב אשר קראו מתוך מסד נתונים לתוך מערך מאוד גדול את כל הנתונים ואז היו שופכים את המידע לקובץ csv. יום אחד הגיע אותו המידע לגודל של 64 מגה, והbuffer נגמר. ראינו load על השרת והמון שימוש ב swap.
חמש דקות אחרי זה הראתי למתכנת שטיפל וכתב את הקוד כיצד לפתור את הבעיה. בתכנות יש מושג שנקרא callback. הוא אומנם יהיה בהכרח איטי יותר מאשר שימוש בזיכרון רציף, אבל הוא מאפשר לי לקבוע אחרי כל כמה זמן אני יכול לכתוב buffer מסוים לזיכרון, ובכך אני למדתי את אותו מתכנת את האפשרות לכתוב כמעט ישירות לקובץ, והגענו למצב שהמערכת כתבה למעלה מ68 מליון רשומות לקובץ csv, ואז גילינו משהו חדדש. אקסל ושאר גליונות האלקטרונים לא מסוגלים שקובץ אחד יכיל כל כך הרבה מידע.
אין בעיה. אני סופר 65,000 רשומות ויוצר עוד קובץ וכיוב'. הנה מימוש pagination רק עם קבצים. כאשר יש את כל הקבצים, לוקחים את כל הקבצים ומאגדים אותם ב zip.
כמות הזמן של כל העיבוד הזה של מספר קבצים ואז zip לקחה שניה וחצי, כאשר לפני השינוי המקורי המערכת היתה קורסת אחרי למעלה מעשר דקות של עבודה, בגלל נושא הבאפרים וניהול הזיכרון.
סיכום
בפוסט זה החלטתי לעשות סדר לגבי התובנות שלי במהלך השנים בנושא פיתוח.
ככול שאני מפתח יותר, כך אני מגלה כי יש המון איזורים אפורים ומעט מאוד איזורים של שחור ולבן.
סיפקתי מספר נושאים ודוגמאות בנושאים אלו, אך אפילו לא התחלתי לגרד את השטח בנושא.
ב2003 החלטתי ללמוד vim כמו שצריך, אז עשיתי את כל מה שבן אדם שפוי עושה – יוצר מדריך בעברית המלמד עבודה בסיסית עם vim.
כאשר התחלתי לגעת ביוניקס, מצאתי את עצמי כמו כולם, עובד עם vi, או nvi, ו…נמצא בחדר בריחה. כאשר מצאתי את הקוד הנכון לצאת, חשבתי לעצמי "מי לעזאזל מתכנן מערכת כל כך מסובכת?! מה רע בעורך טקסט שפשוט כותבים בו טקסט?!".
כמה טעיתי בחשיבה הזו. מה שאני לא הבנתי לגבי vi או vim, וכיום neovim שאיתו אני עובד בעיקר, זה עד כמה אני לא מבין מה המטרה של עורכי טקסט.
כבר הסברתי את זה הרבה פעמים בעבר, אבל אחפור גם כאן על זה – אנחנו כמתכנתים (למשל), קוראים המון קוד, מחפשים המון מידע על המתרחש, ויחסית לכל הפעולות שאנו עושים, דווקא כתיבת טקסט היא פחות העיקר. אבל עריכת טקסט והתמודדות איתו, ובכן היא משהו שאנו עושים הרבה.
אז יצאו להם שני גישות בעולם, גישה של emacs וגישה של vi.
על רגל אחת:
הגישה של emacs אומרת שהפוקוס הוא קודם כל על כתיבת טקסט, וכל דבר אחר הוא משני.
הגישה של vi אומרת שהפוקוס הוא על הcontext של הטקסט וכתיבת טקסט היא דבר משני.
כאשר אנחנו רוצים לגשת למילה השנייה במשפט השלישי בשורה שאנו נמצאים, הגישה של emacs אומרת שאנחנו חייבים ללכת עם החצים עכשיו עד לשם ואז להחליף. אם יש תמיכה בעכבר נעשה את זה בעזרתו.
אבל הגישה של vi היא שונה כאן. אם כל משפט נגמר בנקודה, אפשר להתחיל בגישה הבאה:
בואו נלחץ על 2f. (כלומר הספרה שתיים האות f ואז נקודה) ונגלה כי אנחנו מגיעים לנקודה השלישית הנמצאת בשורה שלנו. עכשיו ניתן להקיש 2w ו… הגענו למיקום הנכון. מכאן נלחץ על cw ונשנה את המילה הרצויה.
עכשיו מתחיל ה OCD שלי לפעול. אתם מבינים? ב vim תמיד צריך לחפש את הדרך הקצרה ביותר לעשות דברים, ובעיני הדרך הזו מעט ארוכה. אמנם קצרה יותר מemacs זה בטוח, אבל ארוכה.
כאשר אני רואה אנשים עובדים קשה מידי להשיג דברים שאפשר להשיג בפחות מאמץ, אני מרגיש שזה ממש כואב לי לראות את זה
תנו לי לנסות את זה שוב. אולי אעשה את זה כך: 2) ואז על w ואקנח ב cw. טוב זה פחות הקלדות.
ועכשיו נשאלת השאלה – האם אני עדיין יכול לקצר את התהליך. אמנם במקום 7 תווים ירדתי ל5, עדיין זה הרבה בעיני.
אז מה השלב הבא? ובכן חשבתי להשתמש ב search and replace, אבל ב Neovim זה לא עבד כמצופה. אז עכשיו האם אני ארים ידיים?
ובכן לא! אמשיך לחפש דרכים טובות לעשות את זה, אבל בשוטף, ולא בעבודה הנוכחית, כי כרגע זה מספיק טוב עד למציאת הדרך הקצרה יותר הבאה שכמעט תמיד מסתתרת בפינה חשוכה ומחכה לפנס שלי להאיר לה את הדרך.
אני יודע מה הרבה חושבים, שזה מאוד קטנוני למצוא דרכים קלות יותר, אבל יש לזה מספר משמעויות גדולות יותר מזה.
כל פעולה אשר אני מבצע ב vim, יכולה להישמר כמקרו, וגם כפונקציה. במידה ואני יוצר מאקרו או פונקציה vim ידע לחזור על הפעולות מספר פעמים.
יותר מזה, גם לזיכרון התנועתי שלנו כבני אדם תהיה יכולת להתרכז בפחות פעולות לביצוע דברים, כך שאפילו אין לי צורך לזכור בראש את הפעולות, אלא רק האצבעות שלי במצב מסוים יבצעו דברים.
היכולת במינימום פעולות לבצע המון פעולות מאפשרת לי לשרשר המון פעולות במקביל, למשל בגישה שלינוקס/יוניקס עובדים, ובכך ניתן להיות מאוד אפקטיביים בעבודה בלי להשקיע הרבה אנרגיה בעריכה עצמה.
זו הסיבה שה OCD שלי נכנס לפעולה, אני כל הזמן מחפש את הדרך הקלה והפשוטה יותר לעשות דברים, וכמה שפחות פעולות בשביל להשיג את אותה המטרה כאשר מדובר בvim, או לינוקס.
אני מאוד אוהב לנסות טכנולוגיות מידי פעם, הפצות לינוקס, עורכי טקסט, או סתם סביבות עבודה שונות מאלו שאני נוגע בדרך כלל.
לאחר דיון עם חבר על הפצות לינוקס, וזה שהוא אוהב ג'ינטו וסלאק, הוא הציע לי לנסות הפצה בשם void-linux.
החלטתי לא לבטל את הרעיון שלו, והחלטתי לקרוא קצת על ההפצה ואז להתקין את הגרסה הכי בסיסית שלה על libvirt שאני אוהב כל כך לניסוי.
ההפצה עצמה היא הפצה מעניינת אשר אינה מבוססת על לינוקס זה או אחר, אלא נבנתה בסגנון של Linux from scratch. היוצר שלה הוא אחד ממפתי OpenBSD לשעבר, אשר החליט כי הוא יכול לעשות נכון יותר הפצת לינוקס.
לא יודע מה אתכם, אבל אותי זה סיקרן. תחשבו רגע, מרבית ההפצות המקובלות בשוק מגיעות מאותו בסיס, שזה בסיס דביאן (ו אובונטו), או הפצות מבוססות RPM שרובן התחילו בעבר שלהם כחלק מRedHat וקיבלו תפנית, אבל עדיין אכישהו מזכירות אחת את השנייה.
אז אני ניתקתי את עצמי מכך, והלכתי לכיוון של ארץ' וחשבתי ש void-linux דומה בגישה שלה ל ארץ'. טעיתי.
ובכן, אין יחסית הרבה בחירה בהפצה הזו, היא מאוד דעתנית. אפשר לבחור סוג מימוש של libc שהוא glibc או musl.
אין למשל תמיכה ב systemd ובמקומה יש ruinit.
ההפצה בברירת מחדל מגיעה עם live cd, בטעמים של שולחנות עבודה Enlightenment, Cinnamon, LXDE, LXQT, MATE ואפילו Xfce.
מה חסר? ובכן גנום, kde4 ו plasma.
אני החלטתי ללכת לכיוון של musl ו live cd שהוא רק shell. רציתי להרגיש את ההפצה בברזל שלה.
והיא ממש מדהימה, קלה וזריזה מאוד.
אז החלטתי להתקין אותה על libvirt שאני מאוד אוהב, וההתקנה הלכה כמעט לגמרי חלק, למעשה רק דבר אחד לא עבד אצלי וזה הגדרת locale, אבל אני מה אכפת לי, אני יודע להגדיר גם אחרי ההתקנה, אז התעלמתי מזה.
הגדרת המחיצות הלכה די פשוט, אבל בחירת מערכת הקבצים אילצה אותי לשנות את הבחירה שלי.
הסיבה היא שרציתי להתקין lvm, ושם שני מחיצות, אחת של root והשניה של home, אבל בבחירת מערכות קבצים, אי אפשר לבחור גם איך לעשות logical partitions שכל כך חשובים אם רוצים lvm. אז פשוט יצרתי את זה כ XFS וסגרתי עניין.
אוקי, עשיתי בוט להתקנה, ו… עכשיו הגיע הזמן להתקין דברים. אז להפצה יש מנהל חבילות משל עצמה בשם xbps שדווקא עניינה אותי, אבל המסך שלי נתקע פתאום כשהתקנתי neovim, אם כי ההפצה עצמה המשיכה לרוץ בהצלחה. מסתבר שהיה משהו בתוכנה עצמה שנעל עדכון מסך, שעד עכשיו לא הבנתי מה הוא ולמה. ד"א Ctrl+q לא עבד, ניסיתי 🙂
היכולת להשתמש במנהל חבילות בינארי הוא לא חדש, והפורמט הוא בס"כ הסוואה ל tar.xz.
התוכן שם קצת מוזר לי אבל, למשל רשימת החתימה של הקבצים משתמשת ב xml בקובץ בשם files.plist.
גם הקובץ props.plist הוא למעשה קובץ xml.
אחרי שעתיים של משחקים שונים עם ההפצה, החלטתי שהיא לא בשבילי.
יש לכך שני סיבות עיקריות עבורי:
כאשר נכנס systemd לעולם, הוא שיפר המון כאבי ראש שהיו עד אז בלינוקס, הוא פותר כאב אמיתי שהיה קיים בהפצות עד אז, למרות השנאה העזה והאנטגוניזם תחתיו, יש לו מקום בעולם.
ההפצה לא באמת מביאה לי משהו ייחודי. כלומר כמות האנרגיה הזו שהושקעה בה, הייתי מצפה שיהיה בה משהו שונה באמת, ולא מרידה בהפצות. אם אקח את Arch, היא מספקת לי יכולת בחירה מההתחלה ועד הסוף, אבל אין לי צורך לקמפל כלום. אבל void לא עושה את זה, היא נותנת לי רשימה של מה שהיא תומכת וזהו.
האם זה אומר שהיא גרועה? לא! רחוק מזה!
היא פשוט לא בשבילי, ואני אשאר בנתיים עם ארץ' 🙂
אחד הדברים החשובים ביותר לאנשי טכנולוגיה הוא היכולת לבדוק ולהשוות טכנולוגיות שונות.
כאשר מדובר באנשי IT, לפעמים זה אפילו אומר כי יש להשוות חומרה והתמודדות שלה, ולאנשי תוכנה, מדובר בשפות תכנות, סביבות פיתוח, ספריות שונות וכיוב'.
כבר יותר מפעם נשאלתי מתי לבחור בGolang ומתי בשפה דינאמית, או אולי ב Rust.
אז החלטתי לתת בקצרה דגשים כיצד אני ניגש לדברים, בתקווה שזה יעזור לכם, וגם אולי לשמוע ממכם כיצד אתם עושים את הבדיקות האלו.
כיצד מתחילים?
כאשר אני ניגש לטכנולוגיות אני מנסה לקרוא קצת על הטכנולוגיה, לראות דוגמאות לשימוש בפועל, או לפחות דוגמאות של יוצר הטכנולוגיה, ובמה היוצר מתמקד.
הסיבה לכך היא, שההתמקדות הזו מסייעת להבין איפה הדגש העיקרי של הטכנולוגיה.
זה נחמד שזו ספרייה לעיבוד UI בJavascript, אבל מה הדגשים שאוהבים לתת שם?
נגיד הדגש הוא רינדור מידע סטטי שלא משתנה. או אולי זה משהו שיש לו observable, אשר מגלה שינוי במידע.
כל גישה כזו ממקדת אותך בדברים שונים.
למשל React יוצרת מצב סטטי תמידי, וכל שינוי מידע יוצר דינדור מחדש של כל הסביבה אשר מחזיקה במידע.
כמובן שיש מספר דרכים להתמודד שזה לא יקרה (למשל states).
לעומת זאת, יש את Vue.js שזו מחזיקה observable וכאשר מידע משתנה, רק מי שמשתמש במידע מרונדר מחדש על ידי שימוש ב shadow DOM, כאשר שאר המסך לא משתנה או מרונדר מחדש.
זה מלמד אותי שאם יש לי מצב בו הרבה מידע מתעדכן באופן קבוע, כנראה ש React פחות מתאימה עבורי, היות והרינדור יהיה יקר יותר, מאשר בצורה שבה רק מה שצריך להשתנות ישתנה.
עוד לפני שהתמקדתי בבדיקה מעמיקה של ספרייה, כבר אני יודע לקחת החלטה במה לשים יותר דגש בהמשך הבדיקות שלי, היות וביצעתי את ההבנה איפה ההתמקדות של אותה ספרייה, או טכנולוגיה.
ניסויים
השלב השני הוא לקחת דוגמאות קיימות של אותה טכנולוגיה, ולהתחיל לסבך אותם.
זה דורש ממני להתחיל לקרוא תיעוד, וזה עוד משהו חשוב.
עד כמה התיעוד מסייע לי בנושא הטכנולוגיה, מול עד כמה הטכנולוגיה makes sense, וקל "לזרום" איתה.
כיצד השפה של הטכנולוגיה (כלומר מונחים וכיוב') מסייעים לי להבין דברים? האם קל להתמקד שם או אולי דווקא להתפזר?
עד כמה המידע שיש עדכני ומסייע לי להתעדכן?
אם אחזור לדגומא של React מול Vue.js. אז התיעוד של React מאוד מתפזר בתהחלה, ומנסה להסביר concept, בעוד שהתיעוד של Vue מאוד ממוקד מטרה – מה לצפות כאשר מתחילים לעבוד.
שימו לב, הדבר הראשון ש React עושים זה להציג Hello World. אנחנו לא מבינים כלום עוד, וכבר יש סוג של Tutorial כתיעוד, בלי קשר ל Tutorial.
זו הסיבה שקל למשל לזרום עם Vue ולהתחיל לבצע איתו משימות, בעוד שעם React עדיין עסוקים בלהבין את התמונה הכללית.
כלומר התחלת העבודה עם React תקח עקומת לימוד גבוהה יותר בגלל התיעוד, אך זה עדיין לא אומר כלום על הטכנולוגיה, אלא עד כמה קל להיכנס להבין את הטכנולוגיה.
אב טיפוס
אז שיחקתי עם דוגמאות, קראתי תיעוד, עכשיו הגיע הזמן לראות עד כמה קל לי להגיע ליעד המקורי שלי – הסיבה שאני מחפש את הטכנולוגיה.
הבחינה היא לא רק עבור למשל כמה קל ליצור "שלד" להתחלה, כי הוא יכול להיות לא פשוט, אבל לאחר היצרה שלו, הכל זורם וקל לתחזק דברים.
הבחינה היא יותר מה קורה כאשר סיימתי להגדיר את הסביבה שלי, מה אז?
האם אני יכול "להתחיל" או האם אני צריך ללמוד איך "להתחיל" לכתוב קוד?
למדתי עכשיו ללמוד את הקוד, האם עכשיו אחרי ההתחלה זה "זורם" או אני עדיין תקוע בהבנת ה concept?
במידה ואני תקוע כל הזמן ב"להבין", אז מורם לי דגל מאוד גבוה לגבי הטכנולוגיה. משהו שם בעייתי מאוד. היא מסובכת מידי.
הטכנולוגיה חייבת לזרום איתי לצרכים שלי, ואם אני מוצא את עצמי תקוע בטכנולוגיה במקום במה שאני זקוק לעשות, כנראה שהיא לא מתאימה לצורך שלי.
דגשים שגויים
כאשר אני מדבר עם אנשים, הרבה פעמים הדגש שלהם נמצא במקום פחות מעניין, או יותר נכון במקום הלא רלוונטי.
נגיד ויש לי 4 טכנולוגיות לבחור מהן. אם אני מתעקש כמה stars יש להם בgithub למשל, זו בעייה. כמובן שאני לא מדבר על טכנולוגיה שמישהו עשה אותה במרתף חשוך ואף אחד לא נוגע בה.
אבל מה זה משנה אם יש 10,000 כוכבים או 100,000,000 כוכבים בgithub?
האם יש תחזוקה טובה? האם יש תמיכה טובה? קהילה שמסייעת? הם החשובים, ולא כמות הכוכבים.
כבר יצא לי לראות פרוייקט עם המון כוכבים אבל 3 או 4 אנשים שמסוגים לעזור איתו. אז לא עשינו בזה הרבה.
יותר מזה, טרנדים זה דבר משתנה כל הזמן, אבל הטכנולוגיה שבוחרים לא נמצאת כאן לחצי שנה, היא אמורה להיות חלק מהפרוייקט לאורך זמן.
היום אתה משתמש בספרייה או טכנולוגיה פופולרית, עוד שנה מהיום כולם יגידו שהם עברו למשהו סקסי יותר.
האם תעביר את כל הפרוייקט שלך בגלל משהו סקסי יותר?
במידה והטכנולוגיה בפיתוח מתמיד, ומתוחזקת, ויש אנשים מאחוריה, אז החשש פחות חמור.
עוד דגש בעייתי הוא "חברה X עובדת עם זה, אז זה חייב להיות טוב".
ובכן, זה טוב עבורם! לא בהכרח עבורכם.
תראו למשל את Gitlab, הם הסבירו מעבר שהם עשו לVue.js. אבל הם לא נשארו שם.
כלומר הם לא נשארו באמירה "הנה עברנו טבנולוגיה", אלא הם המשיכו לבדוק. ואפילו פרסמו ניתוח שנה אחרי, מה גורל המעבר.
סיכום
בפוסט זה ניסיתי לתת קצת טעימה כיצד אני מביט על טכנולוגיות ומנסה להבין כיצד הן יכולות לסייע לי, ועל ידי כך לבחור בזו שהכי ממקדת אותי במשימה.
לפני כשבוע נפלה פצצה בעולם התוכנה בעולם – מיקרוסופט רוכשת את Github. זה הגיע לאחר גל שמועות, למרות שהרכישה עצמה לא התבצעה עדיין (הרכישה מיועדת לסוף 2018), אך ההכרזה הגיעה בגלל עודף שמועות, וספקולציות אף פעם לא טובות לשוק.
לקחתי לעצמי מספר ימים לחשוב על הנושא, והגעתי להבנה עם עצמי שאני לא אוהב את המצב.
עכשיו נשאלת השאלה למה?
ובכן אני מאוד אוהב סדרות מדע בדיוני, ואחת הסדרות היא מסע בין כוכבים. שם יש את שני החוקים הבאים של עולם ה"פרינגי" שהם יצורים אשר מבוססים על תרבות מסחר ענפה, שהם הגיעו לביקרות על תורת מרקנטיליזם הקיצונית בעיני מספר אנשים:
מלחמה טובה לעסקים.
שלום טוב לעסקים
החוקים האלו הם חלק מחוקי הרכישה, כאמור של העולם הפרינגי. והם די מתארים את הבעיה שיש לי עם מיקרוסופט.
מעולם לא באמת שנאתי את מיקרוסופט, היו לי כמה שיחות על זה בתקופה של באלמר שאמרתי את זה במפורש. הבעיה שלי היא בכך שיש לי חוסר אמון בחברה.
תראו את ביל גייטס. הוא היה צריך ליצור לעצמו שוק אשר לא היה קיים, אז הוא יצר את עולם רישוי התוכנה. אבל הוא עשה עת זה בצורה בעייתית מעט, אשר גרמה לזעזוע עמוק בשוק בצורה שכיום אנחנו חושבים כאילו מאז ומתמיד היה רישוי תוכנה, אבל זה לא המצב.
אז הגיע באלמר, הוא היה דמות דורסנית במיוחד במיקרוסופט, אשר גרמה לגייטס להראות ממש נחמד. הוא הכריז כי קוד פתוח זה קומוניסטי, סרטן ומה לא.
אז הגיע המנכ"ל הנוכחי – סאטייה נאדלה. הוא "אוהב" קוד פתוח, לא מעניין אותו אם העובדים שלו עובדים על מחשבים לא עם ווינדוז ולמעשה עובד המון גם על תמיכה בלינוקס.
אבל נאדלה לא עושה את זה מאהבה אמיתית (לפחות לא מבחינה הצד העסקי, לא מכיר אותו אישית), הוא עושה את זה מסיבות עסקיות. כיום קוד פתוח ניצח את השוק שגייטס יצר. הקוד פתוח, המשכורות לא. החומרה לא, התמיכה לא וכיוב', אבל הקוד פתוח לכולם.
על מנת לקחת את החברה קדימה, נאדלה חייב ללכת עם הזרם, וזה לאהוב קוד פתוח. וזו בדיוק הבעיה שלי.
אתם מבינים? כיום מיקרוסופט אוהבת קוד פתוח, כי היא הבינה איך עושים דרכו כסף, אבל מחר? מחר הוא יכול להיות חזרה אויב אכזר לחברה, ומה אז?
הרכישה של מיקרוסופט מזעזעת את כל השוק, בגלל שמיקרוסופט היא לא "שחקן ביניים", כלומר כזה שמספק תשתית לכולם (למרות הניסיונות שלהם עם Azure), אלא הם ספק יעד סופי עם תשתית שהיא רוצה שישתמשו בה. הם לא פלטפורמה למתחרים, אלא הם פלטפורמה בראש ובראשונה למערכות של עצמם. וככאלו, הם לא יכולים לקחת מערכת כדוגמת תשתית לפיתוח קוד ולהחזיק בה בצורה נאמנה.
השאלה הראשונה שעלתה לי לראש כששמעתי על הרכישה היתה מה יקרה למידע של המתחרים, איך הוא יכול להיות שם? מיקרוסופט לא תסתכל על הקוד? היא לא תתחרה בהם יותר? כלומר מה בדיוק יהיה המשחק כאן?
כמובן שאלו עם קוד פתוח, לא בבעיה, אבל שאר התעשייה, מה איתה?
ולכן אני לא מאמין ברכישה הזו. אני חושב שלאורך זמן רחוק יותר אנחנו נסבול מהרכישה יותר מאשר נהנה ממנה, ולכן אין לי אמון ברכישה הזו, ואני העברתי גרסאות קוד של לקוחות שלי לשעבר למקומות אחרים, בעוד שכל פעילות קוד פתוח שלי תשאר כפי שהיא באתר, היות וזה לא מפחיד אותי בשלב הנוכחי.
אנחנו לא יכולים להרשות לעצמנו להחזיק קוד לא פתוח בGithub כאשר מיקרוסופט היא הבעלים שלו. לפחות כל עוד שמיקרוסופט היא שחקן קצה ולא שחקן תשתית בלבד, ואני לא מצליח לראות את השינוי הזה בחמש שנים הקרובות.
לפני מספר שנים פורסם כי לחברת טויוטה יש בעיה רצינית, בה דוושת הגז של הרכב נתקעת. זה הגיע בעקבות תאונה קטלנית בארה"ב כאשר נוסע לצד הנהג התקשר ל911 (שירותי חירום בארה"ב), והודיע כי הם לא מצליחים לעצור את הרכב, והגז תקוע.
בעוד שאנשים כמוני, אשר אינם מומחים לרכב או נהיגה נבהלים שאולי הדבר יכול להתרחש גם אצלהם, מומחי נהיגה ורכב בדקו את הנושא לעומק.
בכל המקרים בהם יש קופסה שחורה לרכב, והנהג חשב כי דוושת הגז נתקעה, לא נרשמה כלל לחיצה על דוושת הבלם. כך שלמעשה, בעת פאניקה, במקום להסיר את הרגל מדוושת הגז, ולגרום רגע לרגל להפסיק ללחוץ על איזושהי דוושה, הפאניקה גרמה לכך שהלחיצה על דוושת הגז תמשיך, ולספק תחושה כאילו דוושת הגז נתקעה, בעוד שהנהג בטוח כי הוא לוחץ על הבלם.
בניסויים שונים שנערכו בהמון כלי רכב, כולל בעלי מנוע משופר (הנועד למירוצים), שהוא חזק הרבה יותר ממנוע רכב רגיל, כאשר גם דוושת הגז וגם דוושת הבלם נלחצות ביחד, הרכב עדיין עוצר. לוקח לו כברת דרך מאוד ארוכה עד שהוא נעצר, אך תמיד הוא נבלם, היות והבלמים חזקים יותר מפעולת המנוע, ואנו יכולים להבין זאת בזכות חוקי ניוטון.
כך שלמעשה טעויות נהג, בנושא של אי לחיצה על דוושת הבלם, והמשך לחיצה על דוושת הגז הוא הגורם לבעיות, ולא דוושת גז תקועה.
בעולם התוכנה, ישנם המון מתודולוגיות לעבודה "נכונה". המתודולוגיות האלו, הן סוג של דעות אשר אומרות כי בשביל לקבל פיתוח אופטימלי, יש לעקוב אחרי ההוראות האלו עד כמה שניתן.
אחת המתודולוגיות האלו, אומרות כי בכל מצב שבו יש משהו שאולי אינו צריך להתרחש, יש להיכשל על כך. להמשיך לקרוא ←
אנחנו אנשי הלינוקס, חיים כמעט לגמרי במסך "שחור" טקסטואלי מוזר, כזה שאנשים במערכות אחרות אינם מבינים למה צריך אותו, ויש להם תוכנה לכל פעולה קטנה, שאנחנו בעלי המסך השחור, מקבלים ב"חינם".
הפתיע אותי לגלות שבמערכת הפעלה שנועדו להדיוטות, שחייבים רק גרפיקה, יש טרמינל איכותי וטוב יותר מכל מה שהכרתי בלינוקס. שם התוכנה שראיתי היה iTerm2. והיא נבנתה ל Mac. התוכנה מסחרית, ולכן לא תקבלו קישור.
יש לה הרבה תכונות מדהימות, כמו "שרת" שמחזיק את הטרמינל עצמו, מול סביבה גרפית שמציגה ומאפשרת לעבוד. חיפוש בתוך הטרמינל, יצירת split חכם ועוד המון תכונות נוספות מדהימות.
אז התחלתי לחפש ולהתעניין אם יש לנו, אנשי המסך השחור, גם משהו בסגנון.
הדבר הקרוב ביותר שמצאתי הוא תוכנה בשם Tilix, אשר קיבלתי עליה המלצה. התוכנה עצמה כתובה בשפה בשם D, והיא מספקת המון תכונות מדהימות, אשר חלקן לקוחות מכלים כדוגמת screen ו tmux, וחלקן מ iTerm2.
התוכנה קרסה לי פעם אחת, אבל בין לבין, היא עובדת ממש טוב, ומאפשרת לי ממשק מאוד קל ונוח עם מקלדת, כולל הגדרות של מקשים, כך שיצרתי לה התאמה בין ה vimrc שלי, לבינה.
בנוסף לכך שיש לי split שקל לעבור בניהם עם המקלדת, אפשר גם ליצור משהו שנקרא session, ממש כמו virtual desktop. וגם אליהם ניתן לעבור בקלות עם המקלדת.
היא מאפשרת למשל לשתף מקלדת עם כל או חלק מהטרמינלים הפתוחים בסשן, כך שהקלדה במקום אחד תתקבל גם בשאר, חיפוש של תוכן שהודפס למסך (למשל לוג שאתם פתחתם עם tail -f, ואולי פספסתם משהו בו), ואפילו ניהול של clipboard, בנושא שאל העתקה והדבקה, ולא בניהול של היסוטוריה וכיוב' שלו, הוא יודע לספק הודעת מערכת כאשר פעולה הסתיימה, להגדיר מסוף כ readonly (לא מקבל מקלדת, בניגוד ל CTRL+S שפשוט משהה את הפעילות) ובנוסף, יש לו תמיכה בכלים שונים שלא ניסיתי עדיין.
אני יודע מה אתם חושבים, למה להשתמש בה ולא ב tmux?
ובכן, דבר ראשון, אני כמעט אף פעם לא עובד על שרתים מרוחקים בשנה האחרונה. כלומר כן לפעמים מתחבר, אבל עדיין לטרמינל שלי.
היות וב ssh במידה ואני צריך, אני עושה mixing של הסוקט (באמצעות ControlMaster), זה די "זול" לפתוח גם מספר חיבורים ולעבוד איתם ישירות מהטרמינל הקיים, ולכן tmux לא משנה.
אבל זה לא הכל, אני יודע שכאן אני תלוי בטרמינל, ולא בתוכנה שרצה בטרמינל, אבל בסופו של דבר, האם זה משנה אם התוכנה נקראת tmux, screen או אולי Tilix?
ובכן, קצת 🙂
הייתי מאוד שמח לראות את Tilix משפרת מספר דברים אצלה:
יצירת שרת שיחזיק את הפעילות שלי ברקע (גם אם הממשק יתרסק, הפעילות שלי לא)
היכולת לשמור את כל ה"סשנים" של הטרמינלים שפתחתי ולאחזר אותם, ולא רק את הפעילים כרגע.
היכולת לבצע התאמה אישית של טמרינל פתוח on the fly ולא רק כמשהו שהוגדר מראש.
במידה והשניים הראשונים יהיו, לדעתי מדובר במתחרה ראוי ל tmux (למעשה הוא כזה כבר עכשיו, אבל עדיין).
כמובן שגם אפשר להריץ tmux בתוך tilix, ואז בכלל זכיתם המון גמישות 🙂
כמובן שיש עוד תכונות של iTerm2 שהייתי שמח לראות אצלי, אבל הדברים שבאמת חשובים לי נוצרו כבר.
בשורה התחתונה, אני מאוד מרוצה, ואם אתם כמוני, עובדים המון עם מסך שחור בלינוקס, ממליץ לכם בחום לקחת את הטרמינל לסיבוב, אולי גם אתם תאהבו להשתמש בו.
לאחרונה אני מוצא את עצמי מפתח הרבה בפיתון, ואני מוצא את עצמי מאוד מתוסכל מזה.
אני מרגיש כאילו פיתון שמה לי רגליים ומקשה עלי מאוד לפתח. כל פעולה הכי קטנה, דורשת ממני המון אנרגיה, לרוב, כי זה לא מרגיש לי מספיק אינטואיטיבי.
פוסט זה, נועד עבורי, בשביל "להוציא קיטור". הוא לא נועד לפגוע באנשים שאוהבים פיתון, אבל הוא מציג את הקשיים שאני חווה במעבר בין השפות.
אז מה לא אינטואיטיבי עבורי? הכל 🙂
סתם, הנה הדגמה קטנה לרובי:
אני נחשב למתכנת מסוג full stack מסתבר, ככה לפחות הגדירו אותי אחרים.
אני מוצא את עצמי נמצא רוב הזמן כאשר אני מתכנת לפחות, משתמש בvim, כל עוד לפחות, לא מדובר בשפת Javascript, על שלל ספריותיה, כדוגמת React או Vue אשר איתן אני עובד לרוב.
הבעיה שלי עם Javascript וכל הספריות שלו, שהתחביר נתמך, אבל לקבל linter ולקבל יכולות כמו זיהוי טוב של מבנה, ואפילו Refactoring לא תמיד עובד נכון, גם כאשר אני עובד עם כלים כדוגמת eslint.
כאשר אני מתכנת בשפות כדוגמת Golang, רובי או פיתון, יש לי כלים ממש איכותיים לנושא עבור vim, גם כאשר אני נוגע למשל בתסריטי teraform, אני עדיין מקבל תמיכה יחסית טובה, אבל כאשר מדובר ב JS, זו כבר בעיה. אני חושב שהסיבות הן שיש הרבה תקנים שהם עדיין במצב של offer ו draft וככה es6 וכיוב', יוצרים אתגרים מעניינים.
בשנה האחרונה, התחלתי מאפס ליצור vimrc משלי, וביחד עם dotfiles שלי, מנסה לענות טוב יותר על בעיות שונות.
אך עדיין איכשהו JS או Ecmascript אם להיות יותר קורקטיים, מתחמקת ממני.
לאחרונה אפילו עברתי ל neovim, אשר דווקא עושה עבודה מדהימה, עם כמה בעיות ממש קטנות, הוא מספק לי כלי מעט טוב יותר מvim עצמו, הוא אפילו מכיל כמה כלים שלפעמים נדמה ש tmux קצת מיותר (למרות שאני עדיין לא משתמש בו), למשל היכולת לקבל מסוף מובנה עם :terminal אשר עדיין מקבל תוכנות של vim בתוכו.
אני כבר הרבה זמן מנסה לגרום לvim לעבוד עם Javascript, והתחלתי לקרוא מאמרים שונים, כדוגמת המאמר הבא, אשר מסביר קצת יותר לעומק איך להגדיר את JS, והנה אני כבר בשלבים להתחיל לזנוח את Atom, ולגרום ל neovim להיות עורך הטקסט כמעט היחידי שלי.
אני עדיין בשלבים של הגדרות ה vim, אבל כבר פחות ופחות אני מוצא את עצמי עם צורך לשנות אותם. אשמח לקבל פידבק מאחרים לגבי העובדה בשביל שאוכל לשפר ולהשתפר, וכמובן שכולם מוזמנים לעבוד עם ההגדרות שלי.
כאשר התחלתי ללמוד לתכנת לבד, אחד הנושאים שהחלטתי לעבוד עליהם חזק הוא ניתוח מחרוזות.
ככה למדתי המון גישות לניתוח מידע, כדוגמת מציאת שם דומה, באמצעות אלגוריתם הצלילים בשם soundex, ומאוחר יותר גיליתי גם את גם את Metaphone אשר עושה פעולה חישובית טובה יותר.
אבל הדבר שלא הצלחתי להבין הרבה זמן (בכל זאת, למדתי לבד), הוא איך אני סורק תווים ומקבל עליהם "הבנה" באמצעות הקוד שלי. כלומר איך אני יודע את המשמעות של מה שאני סורק?
על פניו, זה נשמע די קל ופשוט, אני מוצא משהו המוגדר כ"גבול", ואז אני מחפש על זה משמעות נכון? ובכן, מסתבר שזה לא כזה פשוט. יותר מזה, גיליתי שישנם 2 סוגים של "שפות" כאשר רוצים לנתח מידע – אחת נקראת "שפה דטרמיניסטית" והשנייה "שפה לא דטרמיניסטית". באנגלית הם קיבלו את השמות Deterministic language ו Nondeterministic . כאשר מדברים עליהם, מדברים על הבנת ההקשר (context). להמשיך לקרוא ←
הרבה שנים שאני מנסה להסביר לאנשים מה המקצוע שלי. אני מנסה להסביר כי המקצוע שלי הוא "מתכנת IT", אבל אנשים חושבים כי אני מדבר על DevOps.
התשובה היא לא, זה לא DevOps.
תנו לי רגע להסביר מה זה בעצם IT, שכולם אוהבים להתייחס אליו.
IT הם ראשי תיבות של Information Technology. כלומר מי שמוגדר כאיש IT, מתעסק בטכנולוגיות העוסקות במידע.
עכשיו אשאל אתכם שאלה: האם איש DBA שייך לקבוצה הזו? התשובה היא כן. האם הוא איש DevOps? התשובה היא לא.
המונח של DevOps התחיל כאיש פיתוח אשר יודע לגעת בהרבה רבדים של תוכנה וחומרה, אבל כיום מתייחסים אליו בעיקר כסוג של מנהל רשת אשר יודע גם לכתוב קצת קוד, ולכן אני לא נכנס להגדרה הזו.
אני יודע לגעת בציוד רשתות, אבל זו לא המומחיות שלי. אני הרבה פעמים צריך מישהו בעל ידע ורקע בנושא שיסייע לי בדברים מעט יותר מתקדמים שקשורים לדרך שמערכת מסוימת עובדת בה.
למשל אני יודע כיצד מסדי נתונים שונים עובדים, אני יודע להשתמש בהם, ולנסות ולגרום להם להיות יעילים, אבל אינני מומחה במסד נתונים מסוים, או מבין עד הסוף את כל הניואנסים שיש לסוג מסד נתונים מסוים להציע. זה התפקיד של איש DBA, הוא "חי" את העולם הזה, ומתמקד רק בזה, קורא את זה מהבוקר ועד הערב.
איש DBA יודע למשל המון על IO ועל דיסקים, וזיכרון, אבל זה לא אומר שהוא יודע מספיק בשביל להחליף ציוד, או לבחור חומרה בצורה נכונה. הוא רק יודע מה מסוגל לתת ביצועים טובים, אבל האם לוח מסוים עדיף? האם שרת מסוג מסוים? האם בקר מסוים? האם מעבד מדגם אחד טוב יותר וכיוב', הם פחות מהמומחיות שלו. זה לא אומר שהוא אינו יודע, זה אומר שהוא פחות חי את זה. זה יותר סוג של by-product של העבודה, מאשר המומחיות.
כאשר רוצים בדרך כלל בשפות תכנות, הורגלנו לחשוב בתבניות, שבהם אנחנו שמים סוג של place holder עבור חלק מסוים של תאריך ו/או שעה.
כאשר מדובר בשפת Go (או Golang למחפשים), הגישה בנויה מעט שונה. במקום place holder רגיל שבו "m" מייצג חודש בעל תו בודד (כלומר אם החודש הוא "5", אז הוא יופיע כ"5", אך אם החודש הוא "10", הוא יופיע כ"10") או "mm" שהוא חודש דו ספרתי (כלומר אם החודש הוא "5", אז הוא יופיע כ "05", וכאשר מדובר בחודש שהמספר שלו הוא "10", הוא עדיין יופיע כ"10") אינה מתקיימת.
התבניות האלו מוחלפות בגישה אחרת, שאותי לפחות מאוד בלבלה במשך הרבה מאוד זמן. הגישה אומרת כי יש לנו offset holders. מה הכוונה? ובכן תאריך ושעה בGo נשמרים בברירת המחדל כמספר שלם בגודל 64 ביט (כלומר int64). בנוסף, ישנו מספר בגודל 32 ביט (int32) שמחזיק בנונו השניות לשנייה מסוימת., ובנוסף לזה, יש גם מערכת לשמור מיקום.
כל המשתנים האלו מאוגדים ברשומה בשם Time.
השדה סופר בשניות, את הזמן שעבר מחצות בלילה של הראשון בינואר שנת 1 (המייצגת את הולדת ישו) ועד הרגע. היות והערך יכול להתחיל בשלילי ולסיים כחיובי, זו גם הדרך שהמידע הזה נשמר בפועל.
כאשר רוצים להמיר את המידע הזה למשהו קריא לבני אדם, יש לבצע מספר פעולות מתמטיות בנושא, כדוגמת חילוץ מידע לפי המיקום שלו. אך יש פורמט מאוד ברור בנושא, אשר בנוי לסייע בעצם להמיר את המידע.
בשביל המבנה, יצרו בגו סוג של Fixed Date שהוא המייצג את הימדע הזה:
Mon Jan 2 15:04:05 MST 2006
המידע הזה הוא נקודה קבועה היודעת להיות מתורגמת ל Unix Epoch 1136239445. להמשיך לקרוא ←
גו היא שפת תכנות מהודרת, אשר יוצרת קבצי ריצה טבעיים של מערכת ההפעלה. מאז גרסה 1.5, נכנסה האפשרות ליצור ספרייה משותפת דינמית (DSO). בגרסה 1.5, התמיכה הייתה ניסיונית, ובגרסה של 1.6, התמיכה נכנסה כתמיכה רשמית של המהדר.
מה זו בעצם ספרייה משותפת?
בקצרה, על רגל אחת, ספרייה משותפת זהו קובץ "ריצה" (למשל בפורמט PE בווינדוז או ELF ביוניקס), אשר אינו מחזיק בחלק ריצה כפרוסס (לרוב נקרא main).
לקובץ הריצה יש רשימה של פונקציות אשר "מיוצאות", כלומר הכתובות שלהם נגישות כלפי חוץ כאשר רוצים להשתמש באותן הפונקציות.
השמות של אותן הפונקציות נקראות symbols והגישה אליהן מתרחשת על ידי כתובות זיכרון.
במידה ופרוסס מסויים מקשר אליהן בצורה סטטית, כלומר בזמן הידור, יודעים את המיקום שלו ביחס לשאר הפונקציות של הפרוסס, כאילו היה חלק מהפרוסס עצמו.
כאשר הטעינה מתבצעת בזמן ריצה, יש אתחול זיכרון דינאמי אשר מחזיק את הכתובת.
על רגל אחת, RDP הם ראשי תיבות של Remote Desktop Protocol. אך השם מעט מטעה. זוהי למעשה אסופה של הרבה מאוד פרוטוקולים, חלקם אפילו לא של מיקרוסופט, אלא של ארגון ITU. למעשה הוא מבוסס על סדרה של פרוטוקולים שהחלו את דרכם בארגון ה ITU אשר נועדו לאפשר לתוכנות לשתף מידע ותכנים בניהם.
הפרוטוקולים עצמם הם פרוטוקולים בינאריים, ולא פרוטוקולים טקסטואליים (כדוגמת HTTP למשל), וככאלו יש עבודה רבה עם bit manipulation והבנה של מיקומים שונים ומה המשמעות שלהם.
הרעיון של RDP הוא להצליח להעביר את כל סביבת המיקרוסופט למחשב מרוחק, כולל העתקת קבצים, שיתוף מדפסות, שיתוף כוננים, שיתוף קול ואפילו לבצע הזרמה של ווידאו.
כל זה נעשה תחת מערכת שמבצעת הזדהות מול מערכת כדוגמת Active Directory בפרוטוקולים כדוגמת NTLM ו spnego, אשר האחרון, הוא מימוש הזדהות התומך גם בקרברוס למשל, כאשר התעבורה אמורה להיות מוצפנת בגישות שונות, בהתאם לתמיכה של שני הצדדים. על כל זה מופעל סוג של compression למידע.
לאחרונה התחלתי בשיתוף עם בר חופש ליצור מימוש טבעי בשפת רובי עבור פרוטוקולי RDP של מיקרוסופט. המימוש שלנו משוחרר בקוד פתוח (רישיון MIT), והוא נועד לספק שרת, וגם לקוח טבעיים במערכות הפעלה כדוגמת לינוקס ובכלל שליטה מלאה בכל מה שעובר בפרוטוקול, כולל האפשרות להגדיר מה אפשרי ומה לא בצורה תכנותית.
כל מי שהנושא מעניין אותו, מוזמן כמובן לגשת לפרויקט, וכמובן שאשמח גם לקבל תרומות קוד מאחרים 🙂
כרגע כפי שניתן לראות, הבלוג שלי כמעט ולא זוכה לעדכון, וזאת בשל כך ששנת 2015 הייתה שנה מאוד עמוסה ומאתגרת עבורי מבחינה מקצועית.
לצערי לא אוכל לדבר בהרחבה על הרבה מהדברים שהתבצעו, לפחות בינתיים, אך אני מקווה כי שנה מהיום כבר אוכל לחשוף את העיסוקים השונים.
ב2014 "זכיתי" באלימות, ניסיון סחיטה לאחר אלימות הזו, ואז גם מימוש הסחיטה בפועל, כאשר מישהו לא רצה לסדר דברים אצלו ברשת, והחליט שאלימות זו הדרך לפתור דברים.
אך הרבה לפני כן, התחלתי לבצע פרויקטים שהגיעו לאט לאט למשהו גדול יותר, שב 2015 מצאתי את עצמי שקוע רק בהם, ולמעשה גם כרגע אני עדיין עסוק באותם הפרויקטים.
הגעתי למשל למצב שבו אני יושב עם אנשי מתג של חברת תקשורת מוכרת בישראל, והם צוחקים עלי שאני ילד, כי אני "רק" תשע שנים בתחום התקשורת. כאשר אני התחלתי (ועדיין אפילו לא מתקרב לחיתולים) ללמוד קצת על SS7 ו SIGTAN עבור אחד הפרויקטים.
פרויקט של גוף ממשלתי שהשורשים שלו נטעו בסוף 2012, הגיע למצב של פיילוט ב2015, שעכשיו ב2016 כנראה שיכנס להיות בכל הארץ אצל אותו גוף ממשלתי.
מספר פרויקטים נוספים גם הם נמצאים בשימוש של מספר גופים בארץ שלהם משתמשים רבים התחיל ב2015 ועכשיו מתחיל להתרחב לשימוש גדול עוד יותר.
התחלתי לבצע הרבה ניתוח של דרישות ומימושים קיימים בשביל לשפר אותם וחלקם אפילו לשכתב, ואפילו ליצור סוג של roadmap לפרויקטים שונים, דבר שהתגלה כאתגר מאוד מעניין, אך עדיין אני מוצא כי כתיבת קוד מהנה הרבה יותר מאשר תחזוקה או תכנון ללא מימוש שלי של אותו התכנון.
דברים נוספים שהתחילו ב2013, התפתחו יותר ב2015, כאשר אני מקווה שעד 2017 אוכל כבר להציג ולדבר עליהם בחופשיות יותר, היות והם הולכים לשנות מספר גישות שאנחנו מכירים כיום, במידה ויישארו בכיוון שנכנס כרגע, והיו כמובן משוחררים בקוד פתוח.
בכלל, חשוב לי להדגיש כי כל הדברים האלו מבוססי קוד פתוח שהיה קל מאוד להכניס אותם לפרויקטים האלו, משהו שלפני רק שלוש שלא לדבר על חמש שנים, היה על סף בלתי אפשרי. עכשיו זה אפילו כמעט מתבקש וכבר לא סוג של מילה גסה.
יש אצלי למה לחכות, ואנסה לעדכן יותר את הבלוג תוך כדי.
לאחרונה התחלתי ללמוד קצת על מערכת אותות מספר 7 (SS7) במסגרת פרוייקט מאוד גדול שאני מתעסק בו כבר מספר חודשים לחברה מאוד גדולה וידועה.
עד עכשיו היה שימוש רק ב E1, אז החיבור הוא קווי באמצעות קו רגיל של ISDN, אבל יש לזה מגבלה של 30 ערוצי תקשורת פר חיבור (אם אין דחיסה).
אנחנו כמובן מעוניינים לספק כמה שיותר תעבורה, אז התחלנו לברר על משהו הנקרא SIGTRAN אשר הוא בעצם SS7 על גבי IP, אך הוא אינו משתמש ב TCP או UDP בשביל זה, אלא על גבי פרוטוקול (יחסית חדש) בשם SCTP.
אני כבר מספר שנים יודע כי הפרוטוקול קיים, ומעולם לא הבנתי מדוע עולם ה VoIP לא לקח אותו לשימוש. ולאחר שלמדתי על HTTP/2 לא הבנתי גם מדוע הוא לא נבחר לשם.
הפוסט הבא מנסה להסביר בקצרה יחסית מהו הפרוטוקול, בתקווה שיעזור לכם להיחשף לעוד מידע שלרוב איננו נחשפים אליו.
מהו SCTP?
הקדמה
ישנם שני פרוטוקולים מעל רמת IP אשר מאוד מוכרים בעולם – TCP ו UDP.
על רגל אחת, כאשר מדברים על TCP, מדברים על פרוטוקול אשר יודע לטפל בנתונים העוברים תחת רשת ה IP, כשהוא מודע האם תוכן הגיע ליעדו או לא, מה הסדר שלו, והאם צריך לשדר אותו מחדש, כולל ניסיון לנהל עומס בבקשות החוזרות.
כאשר מדברים על UDP, אנו מדברים על מערכות הדורשות גישה של real-time. אין בפרוטוקול עצמו איזשהו מנהל אשר יודע מה קורה עם המידע, כלומר הוא לא יודע לזהות ולדווח האם הבקשה הגיעה ליעדה בהצלחה או לא, או האם הסדר הוא בכלל תקין, ובעצם הניהול הוא בפרוטוקול עצמו (שכבה 5 ולפעמים גם 7) המשתמש ב UDP ולא מתוך שיטת התעבורה הנקראת UDP.
אז מה הוא SCTP או Stream Control Transmission Protocol בשמו המלא?
הסבר הרעיון (TL;DR)
תארו לכם מצב בו אתם רוצים לדבר בהודעות, או מידע במקום בפקטות. אבל זה לא הכל, אתם רוצים לשלב כמה שיותר מידע שאפשר לשלוח ולספק עליו חיווי.
לפעמים נרצה לדעת אם זה נכשל, לפעמים לא. לפעמים נרצה להסביר את סדר החתיכות, ולפעמים אנחנו רוצים להפקיד שההודעה כמו שהיא אינה מחולקת בכלל.
כלומר אם אשווה את זה לבקשת HTTP, אני יכול להגיד כי תוכן מסוים של ווב, יהיה חייב להישלח כמו שהוא, בעוד שחלקים קטנים של מידע, למשל css ו JS, כולל דף ה HTML ישלחו בחלקים באותה בקשה.
במידה ואני רוצה שגודל הדף, גודל הפרמטרים, ורשימת הקבצים להוריד יגיעו בפעם אחת, אז הפרוטוקול מאפשר. כאשר התוכן של הקבצים יכולים להגיע בבקשה נפרדת, או מחולקת גם בבקשה הזו, בהתאם לגודל.
מוכר לכם ממשהו? למשל מגישה בשם HTTP/2?
אבל כאן זה פרוטוקול ברמה נמוכה יותר, ולא מימוש בשכבה 7.
אבל העניין הוא שזה ממש לא הכל. יש לפרוטוקול תמיכה בmultihoming – כלומר האפשרות להתחבר למספר שרתים במקביל, וכך להבטיח שרידות בקבלה ושליחה של המידע.
הפרוטוקול אפילו מאפשר תמיכה במציאות הנתיב עם האיכות הכי טובה שהגדרנו, כמו ש MPLS, DiffServ וכיוב' מספקים.
עוד פיטצ'ר נחמד, הוא תמיכה ב jumbo frames, אשר מאפשרים לשלוח עם MTU גדול יותר מ1500 בתים.
לאחרונה אני כותב הרבה ב golang מספר actors או workers (אם תרצו) שעושים מספר דברים בצורה מאוד גמישה מצד אחד, ומצד שני מאוד מהירה.
השימוש שלי הוא בכך שאני יכול להריץ הרבה instance של אותו הדבר (בין אם זו אותה מכונה או מכונה נפרדת), לקבל אירועים מ AMQP ולבצע משימה.
עד כאן הכל יפה ונחמד.
הבעיה היא שבשביל שאהיה גמיש, אני מקבל כחלק מהמידע הוראות ב json. כלומר המידע עצמו מכיל כבר הוראות כיצד צריך להתייחס אליו.
ולמימוש הטבעי של golang עבור json יש בעיה "קטנה", בה החליטו כי slice של בתים ישמר כ base64. זה אפילו מתעד.
הסיבה לכך די ברורה, אבל שגויה ביסוד שלה – הם מניחים כי אם אתה שומר slice של בתים, אתה כנראה מנסה לשמור מידע בינארי או שונה ממה שצריך.
רצית רק לשמור סדרה של ערכים, שאף אחד מהם לא יהיה גדול מ255? לא, זה מידע אחר.
אם ההתנהגות הזו היתה זהה למשל ל uint16, אז הייתי אומר "אוקי, כל slice שהוא מספרי, ייחשב כמידע". אבל כאן רק אם זה byte, או uint8 אשר הם למעשה זהים אני אקבל המרה ל base64.
ההחלטה הזו עבור המשתמש מה נכון עבורו היא בעייתית.
בראש ובראשונה כי אני למשל במקרה הזה עובד עם API חיצוני שאני לא שולט בו, והדרישה היא "מערך של מספרים היכולים להיות בין 0 ל255". מן הסתם יש להם משמעות מסויימת, אבל לא בהכרח מה ש go חושב שהיא.
דבר שני, נגיד והיתה לי על כך שליטה. האם אני כמתכנת, לא צריך לשלוט במידע שהוא שומר?
נכון, אין הגדרה בjson למידע בינארי, אבל לא כל סדרה של מספרים היא בהכרח "מידע בינארי". אך עצם ההחלטה עבורי שזה כך, פוגעת בי כמפתח.
הפתרון שאני מצאתי, היה פשוט שה struct יהיה uint16, כלומר שני בתים, למרות שאני צריך לשמור רק בית בודד בכל איבר. וזה מאוד בזבזני, אבל הדרך היחידה שהצלחתי לגרום לזה לתפקד כמו שאני צריך.
אני יודע שיהיו כאלו אשר יבואו ויסבירו כי המחשבים של היום מכילים המון משאבים, ושslice של 16 ביט (כלומר 2 בתים), זה לא כזה סיפור.
אבל למעשה זה חלק מהבעיה – אני מאתחל מידע שאני בהכרח יודע שאין לי צורך בו. זו בזבזנות של משאבים שיכולה ללכת לדברים אחרים.
זה שיש לי למשל 2 פטה בית של RAM, לא אומר שאני צריך לכתוב מערכת שתתפוס את כל הזיכרון. למעשה אם אכתוב מערכת שתתפוס רק את כמות הזיכרון שהיא חייבת ולא מילמטר מעבר, אני לא אגיע למצב שאני צריך 2 פטה בית של זיכרון.
וזה כרגע הדבר הכי מעצבן שמצאתי ב golang. זה לא מונע ממני להמשיך לעבוד, אבל זה בעיני מאוד מעצבן.
למי שאינו יודע, כמעט כל אירוע אוגוסט פנגווין זוכה אצלי לסוג של סיכום, מאז שהבלוג הזה התחיל את חיו.
לפני שנה לא היה האירוע השנתי, וכנראה שזו הסיבה שהשנה היו יחסית מעט אנשים.
אך מצד שני, הגיעו כמה אנשים שהרבה שנים (חלקם אפילו 9) כבר לא פגשתי, אז היה די נחמד.
הגעתי יותר מקודם ממה שהציגו ברכבת,שהיה דבר נהדר, אז הגעתי מוקדם וסייעתי לארגן דברים כמה שיכולתי עד שהפריעו לי לגמרי.
ניסיתי גם למצוא את השם שלי, אך הוא עוד לא הודבק, אז חיכיתי עוד קצת, וגם זה הגיע 🙂
ואז התכנסנו בחדר המרכזי. הייתה פתיחה של שי, ואז הרצאה מאוד מעניינת של אחד ממפתחי systemd. זו מערכת שאני מאוד אוהב באופן אישי, כאשר משווים אותה למה שהיה קיים עד לפיתוח שלה. ובהרצאה גם הבנתי למה.
ממש נעשה מחקר בנושא לפני שהחליטו על כיוון, אבל כמו כל מערכת, היא לא מושלמת ותמיד יש מה לשפר אצלה כפי שעודד הראה לי בצורה חיה במחשב שלו.
הרצאה על systemd
לאחר מכן, הגיע הפרס, לאדם מעניין מאוד שתרם רבות לדו כיווניות ובעיקר לשפה העברית בהרבה מערכות ותכנות. עם נאום ארוך.
ואז התפזרנו לצילום קבוצתי (שכמו כמעט בכל כנס – לא השתתפתי בו) ומסלולים שונים.
לצערי ההרצאות די לא עמדו בזמנים, ובכלל היו קצת דחוסות בעיני, דבר שרק סיבך את כל האנשים.
הלכתי להרצאה של אבטחת מידע באנדרואיד, וקצת אכזב אותי שהיא הייתה בסיסית מידי, ולא מספיק טכנית לטעמי, אך בכל זאת היה בה הרבה עניין.
לאחר מכן, הלכתי לשמוע על מערכות הפעלה, והמרצה ניסה לסמן מטרה גדולה מידי, ופספס אותה לגמרי תוך כדי הציור שלה.
הוא לקח את לינוקס והגזים עם המון דברים, כמו למשל הרעיון שיש לך הרבה tty פתוחים גם בשרת ווירטואלי. אבל אפשר לכבות את זה. במידה ואתה עם מערכת init של sysv יש לך את הקובץ Inittab ואם אתה משתמש ב systemd אז אפשר גם שם לסגור דברים.
העניין הוא, שהוא פספס לגמרי את השימוש ב tty ומה הם אומרים – שזו תקשורת סיריאלית אשר מסוגלת לעשות המון דברים, בהתאם לסוג המסוים, כמו למשל text terminal, או teleprinter וכיוב'… כן זה עולם ישן, אבל הפשטות שלו גורמת לו להיות מאוד יציב ואיכותי יחסית, שניתן לזרום איתו להרבה כיוונים שונים.
הוא מצד אחד מדבר על שרתים, ותוך כדי מספק דוגמה דמגוגית של שימוש במערכת הגרפית של איקס וcups, ואז מדבר שלא צריך אותם בשרת.
וכמובן עוד הרבה מאוד דברים, אשר די פספסו את המטרה של מה שהוא רצה להגיד – שיש צורך במערכות הפעלה ייעודיות יותר לסוגים שונים של דברים.
לאחר מכן, הלכתי לשמוע על רעיון חדש עבור shell. המרצה העלה הרבה רעיונות מאוד מעניינים. למשל, אם הרצתי פקודה וקיבלתי פלט, מן הסתם הפקודה הבאה כנראה רוצה להשתמש במידע שקיבלתי, אז מדוע אין shell שמספק לי את זה?
הרעיון בגדול מאוד, היה מעניין עד מאוד, אם כי לדעתי יש מספר דברים שעדיין צריכים להתפתח שם ולהבשיל, אך בהחלט יהיה בעיני מאוד מעניין להשתמש ב shell כזה (במידה וגוגל יוכל למצוא את השם).
לאחר מכן, הלכתי להרצאה של חץ על מערכות קבצים, שמן הסתם ניחשתי מבעוד מועד כי הוא בעיקר ידבר על zfs שאותו הוא מאוד אוהב.
לצערי, כמעט בכלל לא קיבלתי שם מידע חדש (דווקא הנושא מאוד מעניין אותי), אך בכל זאת מעניין לשמע דברים, וחץ תמיד נכנס לעובי הקורה כאשר הוא ממליץ על דברים.
אז החלטתי שהגיע הזמן לחזור הביתה (יצאתי מאוד מוקדם בבוקר), ולא נשארתי להרצאה האחרונה ודבר הסיכום של השנה.
לכנס הזה, הצעתי הרצאה (שבעיני לפחות מאוד מעניינת מן הסתם) – עד כמה אתם מודעים לכמות המפתחים שיש לכם לכמה מהפרויקטים הכי נפוצים?
אך שי החליט שזו הרצאה שלילית מידי, אז גנזתי אותה במקום להציג.
סה"כ מאוד שמחתי לפגוש כמה אנשים שלא ראיתי מספר רב מאוד של שנים, ואחרים "רק" מספר חודשים.
כיף לראות שיש אנשים בקהילה שלנו שמגיעים לכל כנס ותורמים רבות להצלחה.
לצערי מרבית האנשים שמתנדבים, הם אנשים קבועים, עם פחות "דם חדש" שנכנס ומארגן דברים, אך מצד שני, גם אני מתקשה להתנדב, אז זהו, עד לשנה הבאה 🙂
נמצאה בעיית אבטחה (למעשה באג) באנדרואיד כאשר מקבלים mms. הבעיה בגדול מאוד, אומרת כי ניתן ליצור תמונה שתוכל להריץ קוד זדוני, וכל מה שצריך לעשות זה לשלוח לטלפון mms, ואפילו לא צריך לפתוח אותו, היות ואנדרואיד יוריד ויפענח את התמונה ואז הוא גם יריץ את הקוד.
הפתרון שרוב האנשים שמציעים הוא שתעדכנו את ה firmware של אנדרואיד. זה פתרון די לא יישים – למעלה מ90 אחוז מאוכלוסיית בעלי מכשירים מבוססי אנדרואיד אינו יודע לעשות את זה, וחלק לא מבוטל, גם אסור לו לעשות את זה.
אז לפני שאסביר מה הפתרון שלדעתי כן ישים, אסביר מאוד בקצרה מה זה בכלל mms.
בעולם הסלולרי, יש לנו יכולת לשלוח הודעות קצרות (אם כי לאחרונה זה הפך להיות יותר כמו מגילה מאשר הודעה קצרה) בשם SMS. הפירוש של SMS הוא Short Message Service.
הוא נשלח באמצעות אותות בשם ss7 ממכשיר לאנטנה, ומאנטנה לספק טלפון כלשהו. עכשיו הספק מעביר את זה הלאה לספק אחר שהוא שולח את ההודעה למכשיר אחר.
עד כאן, הכל דבש (או סילאן, לרגישים). אבל אז הגיעו מהנדסים והתחילו לצור תמיכה ב"אינטרנט" לסלולרי, באמצעות פרוטוקול בשם WAP. הפרוטוקול על רגל אחת, מדבר על כך שאני מקבל רשת מחשבים על גבי מכשיר סלולרי.
אחרי שהגיע אלינו WAP, הגיעו מהנדסים ושאלו מדוע לא ניתן "לנצל" את ה WAP ואת ה SMS ביחד ולשלוח בעצם מולטימידיה באמצעות הודעות קצרות (דמיינו מם לזה). וכך נולד לנו ה MMS.
כיצד עובד ה MMS? אני שולח הודעת SMS במבנה מסוים שאומר כי יש לי משהו כדוגמת תמונה בשרת שניתן להוריד באמצעות WAP.
אז עכשיו אחרי שהבנו, מבלי להיכנס לעובי הקורה כיצד הדברים עובדים, הנה הפתרון הכל כך פשוט:
ספקי הסלולר המחזיקים שרתי WAP, או בכלל ספקי SMSc המחזיקים בשרת WAP, יכניסו משהו פשוט שנקרא "אנטי וירוס", אשר מזהה מערך התקפות על קבצי מולטימידיה, ובכך בעצם הבעיה קיבלה מענה די הולם, אם כי לא מושלם.
זה עדיין לא אומר שלא צריך לשדרג את הגרסאות הבעייתיות, אבל זה לפחות משהו ישים יותר, מאשר לרדוף אחרי הרבה מאוד לקוחות.
התחלתי לפתח בשפת רובי בסוף 2006.
הסיבה שהחלטתי לנסות אותה, הייתה אחרי שראיתי את הקוד של metasploit, שעבר משפת פרל לרובי, ואהבתי את מה שראיתי בעיניים, אז רציתי גם להבין.
הרבה מאוד אנשי לינוקס אינם רואים ברובי כשפה "רצינית" והמון שיחות מתחילות בדחיפה של פיתון לכל דבר, בייחוד כאשר מתקשים לבצע קצת פעולות ש Bash דווקא סבבה איתן, אבל צריך להבין איך Bash עובדת בשביל לממש אותם.
בגדול מאוד, אין לי בעיה עם פיתון, היא שפה נחמדה מאוד, ויחסית קריאה. אבל מצד שני, עבורי (ואני מדבר רק על עבורי), רובי קריאה הרבה יותר.
ואז אני רואה שאלות (הדגמה בלבד: 1, 2, 3, 4) שונות על פיתון (כי הרי דוחפים אותה לכל שיחה בערך), ואני רואה איפה למעשה אנשים מסתבכים איתה, ואני מנסה להשוות את זה לרובי, ואני יודע שבמקומות האלו לא ממש יפלו לי שם עם רובי (וזה לא בגלל שאין מקומות ליפול בהם ברובי), והייתי רוצה להסביר גם למה.
החלטתי לקחת את ארבעת הפוסטים האחרונים ב whatsup בנושא שמצאתי (נכון לכתיבת פוסט זה), והחלטתי להדגים כיצד הייתי מבצע אותם ברובי.
שאלה ראשונה
אז הבעיה הראשונה היא לבצע איטרציה על מערך ולהכניס אותו למערך חדש, אלא אם תנאי מסוים מתקיים.
a = 10.times.select{ |x| x % 2 == 0 }
a
=> [0, 2, 4, 6, 8]
זהו, זה כל מה שצריך, שורה אחת 🙂
אסביר אותה בקצרה. ברובי כל דבר הוא אובייקט, אין כזה דבר משהו שהוא "פרימיטיבי" (בהשוואה לג'אווה למשל).
מספר שלם "רגיל", הוא אובייקט מסוג Fixnum. יש לו מתודה אשר מקבלת ירושה ממחלקת האב – Integer בשם times, אשר מבצעת כמות הפעמים של המספר המבוקש איטרציה.
במידה ולא יוצרים block או proc בשביל לרוץ כמניית האיטרציה, חוזרת לנו מחלקה מסוג Enumerator אשר היא מחלקת בסיס לדברים בעלי יכולת מנייה (כדוגמת מערכים). במחלקת ה Enumerator ישנה מתודה בשם select, המאפשרת להגדיר אילו ערכים באיטרציה כלשהי יחזרו אלי כמערך, כאשר אני יצרתי כאן למעשה proc עם הוראות.
היות וברובי השורה האחרונה בהכרח תמיד חוזרת (אם אין explicit return), אז הוא יודע שכאשר יש מספר זוגי, אז אני רוצה שזה הערך אשר יחזור אלי.
עכשיו הנה משהו מעניין על רובי תוך כדי ההסבר – אין ברובי אופרטורים. כל מה שנראה כאופרטור (כדוגמת חילוק השארית ופעולת השוויון), הם למעשה מתודות, ולכן מתודה כדוגמת even? זהה למה שהייתי צריך, ויתר מזה, אין צורך בסוגריים, ולכן זה יכול גם להראות כך:
a = 10.times.select{ |x| x.even? }
a
=> [0, 2, 4, 6, 8]
היות ונהוג ברובי, שכאשר יש איזו מתודה המחזירה ערך בוליאני, אז המתודה מסתיימת בסימן שאלה, לדעתי לפחות, קל להבין מה קורה כאן יותר טוב מהשם עצמו.
שאלה ראשונה – תת שאלה
בתוך סעיף אחד, מסתתרת עוד שאלה על יכולת לבצע תנאי של שורה אחת ללא צורך בהתניה כאשר משהו נכשל.
אקח את הרעיון של השאלה בצורת המימוש שלה, ואדגים כיצד רובי יראה במידה וארצה לממש את זה בתפיסה של פיתון:
a = []
10.times do |i|
a.push(i) if i.even?
puts a.inspect
end
כפי שניתן לראות, זה אפשרי לגמרי לעשות את זה.
יותר מזה, ניתן גם לבצע פעולת unless, אשר תכניס את הערך רק כאשר התנאי בתוכה אינו מתקיים.
ובנוסף, לרובי גם יש תמיכה בשני תחבירים של משפט טרינארי אשר שם נפל השואל:
i = 1
a = if i.even? then 't' else 'f' end
b = i.even? ? 't' : 'f'
הביטוי הראשון קריא יותר מאשר הביטוי השני בעיני, אך הביטוי השני מוכר יותר, ושניהם עושים את אותה העבודה.
שימו לב, שהביטוי שחוזר מהתנאי, הוא זה שנכנס למשתנה אשר מצפה לערך.
כמובן שגם unless יכול לקבל ביטוי זהה, ובכך למנוע שאלה של not:
c = unless i.even? then 'f' else 't' end
שאלה שניה
השאלה השנייה מדברת על כך שיש מערך מקונן, והרבה אנשים ניסו להציג פתרונות יצירתיים ומעניינים מאוד כתשובה לשאלה.
הנה הפתרון של רובי לנושא:
קצת הארכתי את הקוד, אשר יכול להיות בשורה אחת בלבד, אך רציתי להדגים את הערך שיחזור אלי.
כפי שכתבתי בתשובה לשאלה הראשונה, מערך ברובי, יורש מ Enumerable, אבל יש לו גם הרבה מתודות משל עצמו.
מתודה אחת כזו היא flatten, אשר עושה בדיוק את מה שאותו בחור ביקש.
המתודה יכולה להגיע גם בעוד צורה בשם flatten! אשר התפקיד שלה הוא זהה, עם שינוי קטן אחד – היא לא מחזירה מערך חדש בלי לגעת בקיים (כפי ש flatten עושה), אלא היא משנה את המערך הקיים וחוזרת עם אובייקט המערך עצמו.
מרבית המתודות עם סימן הקריאה ברובי, אומרות כי הן ישנו את הערך של האובייקט עצמו, במקום להחזיר תוכן חדש בלי לגעת בתוכן הקיים.
ובכך גם בשם ניתן להבין טוב יותר מה יהיה.
למעשה סימן הקריאה, וגם סימן השאלה, מסייעים לקבל את עיקרון ההפתעה הנמוכה ביותר (תרגום עצמי לעקרון זה). העיקרון אומר, כי דברים צריכים להיות הכי צפויים בהתנהגות שלהם, וכמה שפחות לצפות להפתעה כאשר מתרחש משהו. ורובי מצטיינת בגישה הזו (לדעתי, גם כאשר משתמשים נכון בכלים כמו method_missing). בכך שהרבה דברים מתנהגים בצורה צפויה לפי תחביר, גם כאשר מדובר בערכים לגמרי שונים ממה שחשבו עליהם בהתחלה. ולכן קל מאוד לכתוב למשל DSL באמצעות השפה.
שאלה שלישית
בשאלה השלישית, רצו לבצע דבר מה, כאשר מחרוזת מתקיימת במשתנה.
חשוב לי להגיד לפני הפתרון, כי פיתון יעילה יותר עם מחרוזות מאשר רובי, היות ומחרוזת בפיתון היא "קבוע" בעוד שברובי מחרוזת היא לא קבוע.
אבל לרובי יש גישה אחרת שמנסה להתמודד עם בעיה זו: symbols . symbol יעיל מאוד כי הוא קבוע, ולכן כאשר אין צורך במשהו שיהיה מחרוזת, אנחנו משתמשים בו במקום.
אם אחזור לשאלה השלישית, אז ברובי יש מספר רב של דרכים לענות על השאלה השלישית, אבל אציג רק שלוש מהן:
ישנם כאמור עוד דרכים לביצוע השוואת מחרוזות, אבל אלו מספיקות, לא?
על רגל אחת (בלי באמת למצות את ההסבר עד הסוף שלדעתי דורש פוסט שלם רק על זה):
המתודה (בדיקה) הראשונה של eql? בודקת האם האורך והתוכן זהים.
המתודה (הבדיקה) השנייה של ==, בודקת האם זה תוכן זה הוא מסוג מחרוזת, ובמידה וכן, בודקת אורך ורק אז את התוכן.
המתודה (הבדיקה) השלישית של ===, בודקת גם היא את סוג המחלקה, ובמידה ושניהם מחרוזות אז מבצעים פעולה הזהה ל==, במידה ולא, תהה בדיקה האם יש מכנה משוטף בניהם הניתן להשוואה באמצעות פעולת case.
שאלה רביעית
בשאלה הרביעית, רצו להוריד קובץ מאתר מאובטח.
הנה משהו פשוט שמוריד תמונה מגוגל בגישה מאובטחת:
קצת פירקתי את הדברים לקריאות.
אני טוען את הספרייה open-uri, אשר תפקידה הוא לדעת לפתוח קישורים של ftp, http ו https כאילו היו file descriptor של רובי.
אני במקרה הזה ניגש לקובץ תמונה של גוגל, ושומר אותו כקובץ בינארי בספריית /tmp/ .
שימו לב כי אינני סוגר את הקובץ תמונה שאני שומר בדיסק, והוא נסגר לבד, בזכות פעולת ה proc, אשר בעצם מתבצעת "מאחורי הקלעים" באמצעות yield. וכאשר פקודה זו מסתיימת, הפונקציה של open סוגרת את עצמה לבד.
סיכום
אני מוצא את רובי מאוד מהירה ויעילה לביצוע המון דברים.
יש לה תמיכה מובנת במשהו המזכיר את monads, כפי שכבר זכיתם לראות בקוד, היא קריאה ומהירה מאוד בהתמקדות בעיקר, שזה להשיג מטרה, ופחות בטפל, לפחות בהרגשה שלי, וכנראה כי גם ינון מרגיש על פיתון שיש התמקדות יותר טפל.
רובי מגיעה עם המון כלים מובנים בשפה (למשל כאן בפוסט הזה, לא השתמשתי במשהו שאינו מגיע עם רובי עצמה).
ובכך אני מוצא את עצמי מרוכז דווקא בעבודה מעשית, משהו שלפחות לתחושתי שלי, פיתון פחות חזקה בו. למרות שאני מקווה כי אני טועה בכך.