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

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

טיפ לדיבוג בעיות קונפיגורציה של Apache

28/10/2024

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

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

דרך אחת לקבל יותר אינפורמציה על Apache היא הפקודה: apache2ctl -S. הפלט מהשרת שלי נראה בערך כך:

AH00548: NameVirtualHost has no effect and will be removed in the next release /etc/apache2/sites-enabled/002-tocode.co.il.conf:1
VirtualHost configuration:
*:443                  is a NameVirtualHost
         default server tocode.co.il (/etc/apache2/sites-enabled/002-tocode.co.il.conf:8)
         port 443 namevhost tocode.co.il (/etc/apache2/sites-enabled/002-tocode.co.il.conf:8)
         port 443 namevhost www.tocode.co.il (/etc/apache2/sites-enabled/002-tocode.co.il.conf:27)
*:80                   is a NameVirtualHost
         default server tocode.co.il (/etc/apache2/sites-enabled/002-tocode.co.il.conf:3)
         port 80 namevhost tocode.co.il (/etc/apache2/sites-enabled/002-tocode.co.il.conf:3)

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

דוגמת דינו: שמירת תמונות מויקיפדיה

27/10/2024

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

המשך קריאה

פרודוקטיביות (או: מתי לוותר על טייפסקריפט)

26/10/2024

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

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

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

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

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

שיחות עם ChatGPT

25/10/2024

  • הי אני צריך קוד סקאלה שיוציא את כל התמונות מפוסט בויקיפדיה
  • אין בעיה אתה יכול להשתמש ב REST API של ויקיפדיה. הנה הקוד (זורק מלא קוד על המסך).
  • שמע זה עובד אבל מדפיס רק את התמונה הראשית של העמוד.
  • נכון! באמת לקחתי רק את התמונה הראשונה ברשימה. הנה גירסה נוספת של הקוד שעוברת בלולאה על כל התמונות. (זורק גירסה חדשה של הקוד שלא מתקמפלת).

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

  • שומע אני חושב שיש בעיה ב URL שבחרת, יש שם רק את התמונה הראשית מהעמוד.
  • וואו כל הכבוד ששמת לב! באמת יש URL אחר שנותן את המידע על כל התמונות, אבל בלי ה URL-ים של התמונות עצמן. זה לא נורא כי אני יכול לפנות לעוד Endpoint עם רשימת התמונות ולקבל רשימה של URL-ים. (זורק עוד מלא קוד שלא מתקמפל).

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

  • שומע לויקיפדיה יש URL שנותן את כל המידע של התמונות ואפשר עם String Manipulation פשוט לקבל את ה URL-ים של כל התמונות. (מדביק קוד קצר שעובד).
  • אתה ממש צודק ואני שמח שמצאת את זה. (זורק סיכום והסבר ארוכים של הקוד שלי).

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

https://en.wikipedia.org/w/api.php?action=query&titles=table&generator=images&format=json

המידע שמגיע הוא מערך של אוביקטים ולכל אוביקט יש title שהוא שם התמונה, לדוגמה File:Disambig gray.svg. בשביל להפוך את שם התמונה ל URL בונים URL מיוחד לפי התבנית:

https://en.wikipedia.org/wiki/Special:FilePath/file/{title}

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

https://upload.wikimedia.org/wikipedia/en/5/5f/Disambig_gray.svg

אבל תמיד זה עבד

24/10/2024

מיכה שטרית שר-

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

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

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

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

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

ראספברי פי או VPS או אולי בכלל Serverless - איפה לארח פרויקט צד?

23/10/2024

העלייה של פיתרונות Serverless בשנים האחרונות הביאה לשינוי מעניין בהרגלי פרויקטי הצד שלנו. מצד אחד מאוד קל להעלות פרויקט ל Deno deploy או Next ולקבל איחסון בחינם, מצד שני VPS-ים עולים גרושים ובסכומים של 5-10 דולר לחודש אפשר לקבל שרת שיריץ כמעט כל פרויקט צד שתחשבו עליו, ומצד שלישי מחשבים ביתיים רק הופכים לקטנים וחזקים כשאפילו מכשיר כמו Raspberry Pi מגיע כבר עם 8 ג'יגה זיכרון ומעבד 4 ליבות. הנה כמה שיקולים שאני לוקח בחשבון בפרויקטים קטנים:

המשך קריאה

ניסוי Vue - משיכת מידע מקומפוננטה אסינכרונית

22/10/2024

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

https://asyncvue-nr26xan9r48q.deno.dev/

המשך קריאה

טיפ LLM - איך זה עובד ב X ?

21/10/2024

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

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

[:p :foo/bar]

ה HTML שנוצר משמיט את התחילית foo ומראה לי על המסך רק את הטקסט bar. רק בשביל קונטקסט באופן כללי בקלוז'ר אפשר להשתמש בפונקציה str כדי להפוך דברים למחרוזות וכתיב כזה:

[:p (str :foo/bar)]

באמת גורם ל Reagent להציג את הטקסט המלא :foo/bar.

וכך הגעתי לקוד של Reagent ולשאלה פשוטה - איפה בתוך ערימת הקוד שם נמצאת הפונקציה שמכינה את האלמנטים לריאקט? ועם זה הלכתי ל Chat GPT ושאלתי את השאלה הבאה:

given reagent code
https://github.com/reagent-project/reagent

what part of the code causes reagent to convert the element's content to HTML, assuming the content is a keyword

that is what part converts the expression [:p :foo/bar] to React's createElement ?

התשובה כללה הפנייה מדויקת לפונקציה שאני צריך שנקראת as-element ולקובץ הרלוונטי שנקרא reagent.impl.template.

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

  1. Check the commit history of the project and list all commit messages related to this line

  2. Check the issues of the project and find me all issues that might have led to this line

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

שלושה צעדים לפיתרון בעיה

20/10/2024

  1. להשתכנע שיש בעיה ולמצוא את הקריטריון לפיתרון - כלומר להבין איך צריך להיראות העולם כשהבעיה פתורה.

  2. להבין מה גורם לבעיה.

  3. להציע שינויים שיביאו אותנו לפיתרון ולבחור את הפיתרון המתאים ביותר.

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

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

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

דברים טובים קורים כשמחכים (נוד 23 ו await)

19/10/2024

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

וכך אני גם קורא את השורה:

await "good things come to those who await"

שעשויה להופיע בתור השורה הראשונה בקובץ JavaScript בתקופה הקרובה. מה שקורה זה ש node 23 יודע לטעון קבצי JavaScript שמשתמשים בכתיב ה import/export בעזרת require, כלומר כשאני כותב בקוד:

const utils = require('./utils.js');

זה הולך לטעון מעכשיו את הקובץ utils.js בין אם הוא משתמש בכתיב import/export או אם הוא משתמש בכתיב CommonJS. נו זה מצוין רק הבעיה שקובץ שכתוב בכתיב ESM עלול לכלול קוד אסינכרוני ברמה העליונה ביותר, ו require לא יכול להריץ קוד אסינכרוני. מה עושים? פה הסיפור קצת מסתבך ובעצם הקוד שכתבתי יטען את utils.js רק אם הוא קובץ CommonJS או אם הוא קובץ ESM שלא מכיל קריאה ל await ברמה העליונה ביותר.

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

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

// prevent people from loading this with "require"
// so their code won't break if I add await in some later time
await "good things come to those who await"

export function twice(x) {
    return x * 2;
}

ההערה מעל אגב היא ניסוח שלי.