הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

מיתוסים על קוד ישן

17/03/2023

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

  1. הקוד הישן מכוער ולא גמיש.

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

  3. בקוד הישן אין באגים כי כולם כבר תוקנו.

  4. כל בעיות האבטחה של המערכת הן בגלל הקוד הישן.

  5. הקוד הישן הוא הגורם העיקרי לאיטיות.

  6. בשביל לתקן באג בקוד הישן צריך למחוק את כולו ולכתוב הכל מחדש.

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

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

כשההגיון שלך וההגיון של המחשב לא תואמים

16/03/2023

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

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

תרגום טקסטים בפייתון עם AWS

15/03/2023

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

$ aws configure

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

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

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

$ pip install boto3

ואז להריץ סקריפט כמו זה:

import boto3

client = boto3.client('translate')

response = client.translate_text(
    Text='hola mundo',
    SourceLanguageCode='es',
    TargetLanguageCode='en',
)

print(response["TranslatedText"])

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

בונוס למתקדמים - אם יש לכם הרבה טקסטים לתרגם, אפשר להעלות אותם ל S3 ולשלוח את התרגומון לרוץ על כל הקבצים ולתרגם את כולם. זה יכול לעזור כשצריך לבנות כרטיסיות מתוך רשימה ארוכה של מילים שאספתם. מידע נוסף על הפיצ'ר אפשר למצוא בתיעוד של בוטו בקישור: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/translate/client/starttexttranslation_job.html

החשיבות של כללים

14/03/2023

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

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

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

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

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

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

היום למדתי: לא הופכים OpenStruct ל JSON

13/03/2023

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

הסיפור בקצרה - ברובי בדרך כלל הגישה למקום ב Hash נעשית עם סימן סוגריים מרובעים לדוגמה:

3.1.1 :001 > d = {foo: 10, bar: 20}
 => {:foo=>10, :bar=>20}
3.1.1 :002 > d[:foo]
 => 10
3.1.1 :003 >

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

3.1.1 :005 > require 'ostruct'
 => true
3.1.1 :007 > dd = OpenStruct.new(d)
 => #<OpenStruct foo=10, bar=20>
3.1.1 :008 > dd.foo
 => 10
3.1.1 :009 > dd.bar
 => 20

עד לפה הכל טוב ואפשר גם להפעיל to_json ולקבל את כל השדות בחזרה בתור JSON:

3.1.1 :002 > require 'ostruct'
 => true
3.1.1 :003 > require 'json'
 => true
3.1.1 :004 > OpenStruct.new({ foo: 10, bar: 20 })
 => #<OpenStruct foo=10, bar=20>
3.1.1 :005 > OpenStruct.new({ foo: 10, bar: 20 }).to_json
 => "\"#<OpenStruct foo=10, bar=20>\""

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

3.1.1 :001 > OpenStruct.new({ foo: 10, bar: 20 }).to_json
 => "{\"table\":{\"foo\":10,\"bar\":20}}"

מאיפה הגיע לשם ה table???

חיפוש ב Stack Overflow הביא אותי לתשובה הזאת. מסתבר שבמימוש של OpenStruct כל הפריטים נשמרים בתור ערכים של משתנה מחלקה בשם table, והקידוד ל JSON של ריילס פשוט מקודד את כל משתני המחלקה.

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

3.1.1 :077 > OpenStruct.new({a: 10, b: 20}).to_h.to_json
 => "{\"a\":10,\"b\":20}"

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

בעיות שכבר אין בפיתוח תוכנה

12/03/2023

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

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

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

  1. הפצת תוכנה - לפני 20 שנה היה מאוד קשה לכתוב אפליקציה אחת שתרוץ גם על Windows וגם על Mac. כשהאייפון הראשון יצא אי אפשר היה לדמיין פיתוח מערכת שתרוץ גם על מחשב וגם על טלפון, וכשאנדרואיד התחיל לתפוס תאוצה המצב רק נהיה יותר מבלבל. אבל אז האינטרנט השתלט והגיעו חנויות האפליקציות וכלי ה Cross Platform, ופתאום כל אחד יכול לשבת בבית ולכתוב אפליקציה קטנה שכולם בכל העולם יוכלו להפעיל.

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

  3. אבטחת מידע בסיסית - ב 2005 אפשר היה להיכנס לבסיס הנתונים של 90% מהאתרים ברשת עם פירצה מאוד בסיסית שנקראה SQL Injection. ב 2012 אפשר היה לשבור את רוב הרשת עם פירצה שנקראה XSS. טכנולוגיות חדשות הפכו את שתי הפריצות האלה ופריצות פשוטות רבות נוספות ללא רלוונטיות. נכון עדיין יש האקרים ועדיין צריך לאבטח את המוצרים שלנו, אבל הסיכון בפריצה למוצר שלכם (אם כתבתם בפריימוורק מודרני) הוא הרבה יותר נמוך ממה שהיה פעם.

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

ועכשיו שזה מאחורינו, מה עוד עוצר אותך?

שלושה פיצ'רים של בינג החדש שמשאירים את ChatGPT מאחור

11/03/2023

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

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

המשך קריאה

ארגונומיקה

10/03/2023

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

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

המפתחים שכותבים את Visual Studio נדרשים ליכולות שונות ולכלים שונים מאשר מפתחות הבונות משחק מחשב ואלה יצטרכו יכולות שונות ממפתחים הבונים יישום Saas. עם ההבדל הזה בראש כדאי ללכת לקרוא את הדיון המרתק בין דוד בוב לקייסי מורטורי בקישור: https://github.com/unclebob/cmuratori-discussion/blob/main/cleancodeqa.md

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

להוציא קבצים מגיט עם rm cached

09/03/2023

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

$ git add .
$ git commit -m 'fixed bug'

אופס.

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

ניסיון ראשון למחוק את התיקיה עם git rm ייכשל:

$ git rm .idea

fatal: not removing '.idea' recursively without -r

ואם אנסה להקשיב לו ולהפעיל git rm -r .idea אני אומנם אמחק את התיקיה מגיט אבל אאבד את קבצי הגדרות הפרויקט שלי.

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

  1. יש את העותק שאנחנו רואים בתיקיית העבודה.

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

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

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

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

$ git rm --cached -r .idea

rm '.idea/.gitignore'
rm '.idea/cached.iml'
rm '.idea/inspectionProfiles/profiles_settings.xml'
rm '.idea/misc.xml'
rm '.idea/modules.xml'

$ git commit -m 'removed .idea from project'

$ ls .idea
cached.iml              inspectionProfiles      misc.xml                modules.xml             vcs.xml                 workspace.xml

יצירת סיסמאות משורת הפקודה עם pwgen

08/03/2023

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

בהפעלה רגילה pwgen מדפיסה המון סיסמאות אקראיות למסך:

$ pwgen

cahhoh2G oofah7Wo Kain5pha eeKue9th Shieng9i ail5Mai3 ahgh1Woh uuQuu2oh
IVahg0ro Ahju2tah Zuox3laS ual3Iece Ohx7ua2e ib2riyoG UuYie9Oh eicuc7Ib
loh5ja7T Nen9biTh Zee5vaiw ies6AoBo eGheip3m eiyeeNg9 Aish1ohn eM6isees
oR7Zeam4 ithang7O Aing4hah eeNae6Ro ahRashi2 xo2eiVie Eew4ohp8 uusaeCh0
chaic0Vu eu2phaeJ oFee7ei8 zohBu4xe mu9iTeik oow0Ia7e Noothah5 yuuph2Ie
keiFair2 Chutoh1i ji9Quei5 OoX5Ophe yay3AeNo Ahoong3C keeS5Eih Ohbaivi4
pheiPh1A aaloof4B ahsoMai6 they7Xoo IeN1aiqu eoGhai7e ahLaqu9u EiGh3ook
kooCh3ah niGieb7E Chohpai9 uibi2Lah Aiqueiz9 the7ua1T fooS2Ohl Cha9aene
aeT9ahzi teeG8axa the9Wahy WooT0eis tieW5yoh aiYeip1l eTh0iewu azucew3A
oofoKu6h Wievei4c xoo0Quae Lah7Nue0 ieZ8ahqu Oi1ahjee OoW6eeZ3 yei1fi7A
ohb9phuN Gee5see4 eeD5jaik Figha4oo Jet2uo6e Eir9re3i OhDohk9e Yai8ahsu
Ahjaev2i ea8ohJ0e yeiHai4i Cev4Wuuh sheih9Hi akeiZa8t Aic3ahro zoo2AeCa
Ohgoong0 Bouhoo5a wai7Eih0 ZooP7eif ba5eiy3R eewooCh7 Aga2ueZ1 aiT8Shah
Aethee0h Eir9cieg Wo6zoodi iV6idohv Iezei7am chaeR5uf Ooree0ie aequa5Wi
Eeyu8uiF aibac7Li fee4Ahpe HooGo1ei pahng4Ah ohXuo9tu Eeseew0u aefes4Ph
Ea8ohnie kiyaiTh9 Lai9eifu aib4eeSa ua8aaM2o Tied3tee Lo5sooVo Ieshaiw4
pe3Aib4j HiujeeX2 teiJu0Ni geeSh7hi ieNg4oov Ua9ma1oC ahpieC9k Peit0Eih
ab9Raelo jahC6eix QuahG6to aeL7iera ze5OP9Ie uoBai7ob MohK8Ohm Ahr2eemi
loh8iGhu Iu3ahwie uDa4ia8j Quoh4iek aQu6ith0 Ahs5jaiy uech4aiS eodai7Ah
kei4Noox quooy0Ae ohnais7A jeech9Ae ob8ehaeN die4Ahth Ahg8jah3 Aishe8Yi

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

$ pwgen | cat
zo3gi3Ie

$ pwgen -1
shaiPoo2

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

$ pwgen -s -1 -y 30
bPcAig)mVL|\_hMpib|f.=?7$o^RQ|

חוץ מיצירת סיסמאות אפשר להשתמש ב pwgen כדי ליצור בעצם כל מחרוזת שאתם צריכים לבדיקות. לדוגמה אם אתם צריכים 10 כתובות אימייל שנראות אקראיות תוכלו לכתוב:

$ pwgen 10 10 | sed 's/$/@test.com/'
ige6Eiji3i@test.com
joiC9kaes0@test.com
EiZuwofu3z@test.com
nee6Ea0eid@test.com
iye7ieJ4oh@test.com
abaeReeP4y@test.com
ebei4NiQu6@test.com
OCheil6ahr@test.com
ootheiH3tu@test.com
ei8ne6ohMi@test.com

או הסקריפט הקצר הבא שמדפיס 10 משתמשים אקראיים ל JSON כאשר כל משתמש מקבל מזהה, שם ואימייל אקראיים:

echo "["
for i in {1..10}; do
  id=$(pwgen -1 10)
  name=$(pwgen -s 2 1)
  email="${name:l}.${id}@example.com"
  if [ $i -eq 10 ]; then
    echo "{\"id\":\"$id\",\"name\":\"$name\",\"email\":\"$email\"}"
  else
    echo "{\"id\":\"$id\",\"name\":\"$name\",\"email\":\"$email\"},"
  fi
done
echo "]"