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

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

לפעמים כפל קוד דווקא יכול להיות יותר קריא

04/11/2024

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

const seconds = ref(0);
const minutes = computed(() => {
  get() { return seconds / 60},
  set(minutes) { seconds.value = minutes * 60}
});
const hours = computed(() => {
  get() { return hours / 3600},
  set(hours) {seconds.value = hours * 3600}
})

ואז אני שם לב ש minutes ו hours בעצם חושבו באותה צורה אז אני מארגן מחדש את הקוד:

function deriveTime(start: Ref<number>, factor: number) {
  return computed({
    get() { return Number((start.value / factor).toFixed(2)) },
    set(newValue) { start.value = newValue * factor }
  })
}

const seconds = ref(0);
const minutes = deriveTime(seconds, 60);
const hours = deriveTime(seconds, 3600);

יותר מזה, מאחר ו deriveTime לא באמת קשורה לקומפוננטה אני יכול להעביר אותה לקובץ אחר ואז יש לי רק:

const seconds = ref(0);
const minutes = deriveTime(seconds, 60);
const hours = deriveTime(seconds, 3600);

והשאלה - איזה API טוב יותר? כמה מחשבות:

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

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

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

  4. פיתרון טוב יהיה להוסיף תיעוד באתר הקריאה שמסביר מה קורה פה, משהו כזה:

const seconds = ref(0);
// create reactive modifiable computed refs for other time units:
const minutes = deriveTime(seconds, 60);
const hours = deriveTime(seconds, 3600);

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

פיתרון ביניים נוסף שיכול לעזור כאן הוא להעביר רק את יצירת האוביקט לפונקציה אבל להשאיר את הקריאה ל computed באתר הקריאה כלומר:

const seconds = ref(0);
const minutes = computed(deriveTime(seconds, 60));
const hours = computed(deriveTime(seconds, 3600));

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

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

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

משחקים עם Embeddings

03/11/2024

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

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

המשך קריאה

ניסוי React - ואם היו Directives?

02/11/2024

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

המשך קריאה

ואז זה נגמר

01/11/2024

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

ואותה שאלה חוזרת כל הזמן ובאינסוף גירסאות ... מה עושים עם אפליקציית אנגולר1? מה יקרה עם אפליקציית ה next.js שלי במעבר ל App Router? מה עושים עם כל הקלאסים כשיצאו React Hooks? מה עושים עם אתר הפלאש? הסילברלייט? ה Java Applet?

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

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

ריאקט או Vue - הדגמה דרך הוק קצר

31/10/2024

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

המשך קריאה

טיפ CSS: כותרות דביקות לטבלה

30/10/2024

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

thead {
  position: sticky;
  top: 0;
  z-index: 2;
}

th:first-child, td:first-child  {
  position: sticky;
  left: 0; 
}

td:first-child {
  background-color: white;
}

th {
  background-color: #f2f2f2;
}

בואו נעבור על כל המאפיינים:

  1. המאפיין position: sticky גורם לאלמנט "להידבק" למיקום מסוים אם גלילה תגרום לו לצאת מהמסך. זה בדיוק האפקט שאנחנו רוצים לכותרות של הטבלה. ל thead הגדרתי שיידבק לחלק העליון בעזרת top וכל td שהוא ילד ראשון, כלומר כל העמודה הראשונה, נדבקת לצד שמאל.

  2. הגדרת z-index על thead קבעה ששורת הכותרת העליונה תופיע מעל עמודת הכותרת וכך שם העמודה הראשונה לא מוסתר בגלילה למטה על ידי הערכים של העמודה.

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

העליתי לקודפן את הקוד המלא עם כל ה CSS וה HTML של הטבלה מוזמנים לשחק עם זה בקישור: https://codepen.io/ynonp/pen/zYgWKOM

או מוטמע כאן:

סטראבה

29/10/2024

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

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

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

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

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