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

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

איך לזהות תירוצים לאי התקדמות מקצועית

24/02/2024

יש המון סיבות טובות להתרחק מטכנולוגיה חדשה כמו למשל-

  1. אני כבר ממש טוב ב X, בשביל מה לבזבז את הזמן על Y.
  2. אין לי צורך בזה לעבודה שלי.
  3. זה הכל הייפ, תכף ההתלהבות תיגמר וכולם יחזרו לטכנולוגיה שהיתה קודם.
  4. אין מספיק אנשים בתעשייה שמשתמשים ב Y וחבל לי לבזבז את הזמן על משהו שלא יקדם אותי.
  5. הייתי שמח ללמוד את Y אבל כרגע אנחנו בדיוק באמצע פרויקט, אחרי הגירסה אתחיל להסתכל על זה.

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

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

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

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

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

משחקים עם מקביליות בסקאלה (חלק 2)

23/02/2024

בחלק הקודם של הפוסט כתבתי על pmap ואיך אפשר להשתמש בו כדי לחלק פעולה חישובית למספר תהליכונים כדי לשפר ביצועים. היום אני רוצה לדבר על עבודת IO, על המגבלה של Thread Pool במיקבול משימות הקשורות ל IO ועל הפיתרון עם Virtual Threads.

המשך קריאה

ביי ביי map בשביל לשנות ערכים במיקום מסוים ב JavaScript

22/02/2024

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

const newItems = oldItems.map((item, index) => 
    index === 1 ? 99 : item);

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

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

const newItems = oldItems.with(1, 99);

נשים לב שאי אפשר לכתוב אחרי סוף המערך כך שזה נכשל:

[].with(99, 0)

בנוסף with שובר מערכים מרווחים, אבל אני לא בטוח כמה נזק זה עלול לגרום. כלומר הקוד הזה:

Array(10).forEach(() => console.log('1'))

והקוד הזה:

Array(10).with(0, 0).forEach(() => console.log('1'))

יעשו דברים שונים - הראשון לא ידפיס כלום, השני ידפיס 10 פעמים את ההודעה. אין תמיכה במספר אינדקסים לכתיבה לתוך מערכים מקוננים, אבל כן יש תמיכה באינדקסים שליליים לכתיבה מסוף המערך:

[1, 2, 3, 4, 5].with(-1, 10)

היום למדתי - איפוס הגדרות postcss בפרויקט vite

21/02/2024

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

הסיפור שהיום לקח לי יותר מדי שעות מהחיים קשור ל postcss ולהתנהגות המוזרה שלו בתוך פרויקט vite - בהעדר הגדרה אחרת, postcss יחפש קובץ הגדרות במעלה עץ התיקיות עד לתיקיית הבית. בשביל הניסוי שמתי בתיקיית הבית קובץ בשם postcss.config.js עם התוכן הבא:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

ואז יצרתי פרויקט vite חדש לגמרי בתיקייה:

/Users/ynonp/a/b/c/d/helloworld

הפעלתי npm run build בתיקיה וקיבלתי את הודעת השגיאה הבאה:

> helloworld@0.0.0 build
> tsc && vite build

vite v5.1.3 building for production...
transforming (1) index.htmlnode:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

[Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/ynonp/a/b/c/d/helloworld): [Error] Loading PostCSS Plugin failed: Cannot find module 'tailwindcss'
Require stack:
- /Users/ynonp/postcss.config.js

(@/Users/ynonp/postcss.config.js)
Error: Loading PostCSS Plugin failed: Cannot find module 'tailwindcss'
Require stack:
- /Users/ynonp/postcss.config.js

(@/Users/ynonp/postcss.config.js)
    at load (file:///Users/ynonp/a/b/c/d/helloworld/node_modules/vite/dist/node/chunks/dep-stQc5rCc.js:28883:11)
    at file:///Users/ynonp/a/b/c/d/helloworld/node_modules/vite/dist/node/chunks/dep-stQc5rCc.js:28908:16
    at Array.map (<anonymous>)
    at plugins (file:///Users/ynonp/a/b/c/d/helloworld/node_modules/vite/dist/node/chunks/dep-stQc5rCc.js:28907:8)
    at processResult (file:///Users/ynonp/a/b/c/d/helloworld/node_modules/vite/dist/node/chunks/dep-stQc5rCc.js:28977:14)
    at file:///Users/ynonp/a/b/c/d/helloworld/node_modules/vite/dist/node/chunks/dep-stQc5rCc.js:29107:14]

Node.js v21.5.0

וכן זה לקח הרבה יותר זמן לגלות שהסיבה להודעה היא בעצם הקובץ postcss.config.js שנמצא בתיקיית הבית שלי.

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

import { defineConfig } from 'vite'

// https://vitejs.dev/config/
export default defineConfig({
  css: {
    postcss: {},
  },
})

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

עוד מחשבה על תבנית ה index.ts שמייצא הכל

20/02/2024

בואו נדמיין פרויקט ריאקט שיש בו תיקייה בשם src/components/HomePage ובתוכה ערימה של תיקיות וקובץ אחד בשם index.ts:

.
├── EmptyState
│   ├── EmptyState.tsx
│   └── index.ts
├── ErrorState
│   ├── ErrorState.tsx
│   ├── ErrorState.types.ts
│   └── index.ts
├── RecentArticles
│   ├── RecentArticles.tsx
│   ├── RecentArticles.types.ts
│   └── index.ts
├── RecentArticlesCard
│   ├── RecentArticleCard.tsx
│   ├── RecentArticleCard.types.ts
│   └── index.ts
├── RecentArticlesContent
│   ├── RecentArticlesContent.tsx
│   ├── RecentArticlesContent.types.ts
│   └── index.ts
└── index.ts

תוכן הקובץ index.ts יהיה:

export * from './EmptyState';
export * from './ErrorState';
export * from './RecentArticles';
export * from './RecentArticlesCard';
export * from './RecentArticlesContent';

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

המשך קריאה

איך ליצור דף Github Pages לפרויקט שלך

19/02/2024

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

המשך קריאה

זאת כבר לא הבורות שלנו שמעכבת אותנו

18/02/2024

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

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

זה היה אחרי שלוש שנים של עבודה על Atom. שלוש שנים של ניסיונות לשפר את הביצועים. שלוש שנים של התמודדות עם האתגר של כתיבת Desktop Application מהיר בכלים ווביים.

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

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

פיתרון Advent Of Code 2023 יום 12 ב Scala

17/02/2024

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

המשך קריאה

יותר מדי אינפורמציה

16/02/2024

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

"שמע השבוע התחלתי ללמוד Tensor Flow כי בכל מקום צריכים את זה"

"סיימתי קורס פייתון של 40 שעות וידאו ביודמי - היה מעולה ולמדתי המון"

"תגיד מה דעתך על AWS? התחלתי ללמוד להסמכה של CLF-C01 כי הבנתי שחייבים את זה בשביל להתקבל לעבודה כ MLOps"

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

אבל מעגל הקסמים הזה לא מסתיים.

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

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

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

זה לא מספר השורות

15/02/2024

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

def to_list_of_digits(s: str) -> list[int]:
    result = []
    for ch in s:
        if ch.isdigit():
            result.append(int(ch))
    return result

וזה עובד! אבל אז אנחנו מגלים שבעצם בפייתון יש מנגנון שנקרא List Comprehension ושאנחנו יכולים לכתוב את הפונקציה בצורה הרבה יותר קצרה:

def to_list_of_digits(s: str) -> list[int]:
    return [int(ch) for ch in s if ch.isdigit()]

עכשיו השאלה - האם נישאר עם הפונקציה? אולי עדיף לקחת את השורה האחת ופשוט לשים אותה במקום הקריאה? מי החליט שצריכה להיות כזאת פונקציה בכלל? ואולי אם הייתי מראש יודע על List Comprehension לא הייתי כותב את זה כפונקציה?

התשובה מורכבת אבל כדאי להשאיר בראש כמה נקודות-

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

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

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

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