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

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

למה לא התייאשתי אחרי הסיפור של Zecento

27/09/2023

בחור משקיע שנתיים ו 15 אלף דולר בפיתוח תוסף לכרום, ובסוף מרוויח מזה משהו כמו 200$. שוק מסוכן? אל תפתחו סטארט-אפ? עדיף להישאר בעבודה קבועה? לא יודע, בואו נקרא את הפרטים:

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

  2. התוסף פעיל רק באיטליה

  3. המוצר הוא Freemium, כלומר התוסף הוא חינמי אבל יש גירסת פרמיום שעולה כסף.

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

מסקנות:

  1. יש מוצרים שיהיה קשה מאוד למכור אותם. יש גם מוצרים שיהיה קל למכור אותם, ובכל מקרה רוב המוצרים יצטרכו התאמה לקהל יעד כדי שאפשר יהיה למכור אותם (מה שקוראים Product/Market Fit).

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

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

הזדמנות שניה

26/09/2023

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

מה שהיה חסר לי באותו דיון הוא אותם אנשים שבחרו לתת הזדמנות שניה.

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

או

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

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

האתגר: להוציא החוצה את התלויות

25/09/2023

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

שתי דוגמאות- הקוד של swapi שמחזיר מידע על מלחמת הכוכבים כולל את המחלקה:

class PeopleViewSet(viewsets.ReadOnlyModelViewSet):

    queryset = People.objects.all()
    serializer_class = PeopleSerializer
    search_fields = ('name',)

    def retrieve(self, request, *args, **kwargs):
        return super(PeopleViewSet, self).retrieve(request, *args, **kwargs)

    def list(self, request, *args, **kwargs):
        return super(PeopleViewSet, self).list(request, *args, **kwargs)

הקוד של kutt כולל את הבלוק:

export const get = async (match: Partial<Link>, params: GetParams) => {
  const query = knex<LinkJoinedDomain>("links")
    .select(...selectable)
    .where(normalizeMatch(match))
    .offset(params.skip)
    .limit(params.limit)
    .orderBy("created_at", "desc");

  if (params.search) {
    query.andWhereRaw(
      "concat_ws(' ', description, links.address, target, domains.address) ILIKE '%' || ? || '%'",
      [params.search]
    );
  }

  query.leftJoin("domains", "links.domain_id", "domains.id");

  const links: LinkJoinedDomain[] = await query;

  return links;
};

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

  1. הקוד שבונה את השאילתה לפי פרמטרים

  2. הקוד ששולח שאילתה לבסיס נתונים ומחזיר תשובה

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

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

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

תוכנית ללימוד שפת תכנות חדשה (והפעם - סקאלה)

24/09/2023

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

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

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

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

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

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

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

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

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

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

אף פעם לא קרה לי

23/09/2023

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

סקרנות היא גישה טובה יותר.

בוא תראה לי,

באיזה גירסאות אתה משתמש?

יש לך פרויקט דוגמה קטן?

למה בחרת להשתמש בכלי בצורה הזאת?

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

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

קבלת החלטות בתנאי אי וודאות

22/09/2023

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

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

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

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

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

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

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

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

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

פיענוח JSON משורת הפקודה עם jq

21/09/2023

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

פוסט זה זמין גם בתור מדריך וידאו למנויי האתר בקישור: https://www.tocode.co.il/boosters/jq

המשך קריאה

ספריה במקום הבנה?

20/09/2023

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

a = []
for i in [1, 2]:
    a.append(lambda: i)
for f in a:
    print(f())

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

וכן דרך אחת זו הפונקציה מהפרויקט scope_capture.

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

from functools import partial

a = []
for i in [1, 2]:
    a.append(partial(lambda i: i, i))
for f in a:
    print(f())

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

  1. לא צריך להתקין כלום, לא הוספתי תלות חדשה לפרויקט.

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

נשווה את זה לקוד של הספריה scope_capture:

import inspect
from types import FunctionType

def _make_cell(value):
    fn = (lambda x: lambda: x)(value)
    return fn.__closure__[0]

def capture(f):
    try:
        frame = inspect.currentframe()
        fake_globals = {}
        fake_globals.update(f.__globals__)
        fake_globals.update(frame.f_back.f_locals)
        captured_cells = []
        if f.__closure__:
            for cell in f.__closure__:
                captured_cells.append(_make_cell(cell.cell_contents))
        call_fn = FunctionType(
            code=f.__code__,
            globals=fake_globals,
            name=f.__name__,
            argdefs=f.__defaults__,
            closure=tuple(captured_cells),
        )
        return call_fn
    finally:
        del frame

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

הצילו גיט - אי אפשר לגשת לקובץ

19/09/2023

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

המשך קריאה