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

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

מה ההבדל בין npm create ל npx?

19/01/2025

מה ההבדל בין npm create ל npx ? ולמה בשביל ליצור פרויקט חדש אני מפעילה npm create אבל בשביל להריץ שרת מקומי זה npx static-server? אפשר לוותר על אחד מהם?

אם יש משהו ש npm יודעים לעשות זה להוסיף עוד ועוד פקודות וקיצורים לפקודות שעושים דברים מאוד דומים. בואו נראה מה הסיפור של npx ו npm create.

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

npx cowsay hello

הפקודה השניה, npm create היא למעשה קיצור דרך לפקודת npm init הישנה והטובה. פקודת init נוצרה כדי ליצור מהר קבצי package.json, דרך מענה על מספר שאלות מובנות. היא איפשרה יצירה מהירה של פרויקטים חדשים ב node במקום שנעתיק קובץ package.json כל פעם מפרויקט אחר. החל מגירסה 6.1 פקודת init הסכימה לקבל שם של חבילה שתהיה אחראית על יצירת הפרויקט. באופן אוטומטי init מוסיפה את התחילית create לשם שנתנו לה, מחפשת חבילה ב npm בשם הזה ומפעילה אותה עם npx, כלומר כשאני כותב:

npm create vite hello-world

אני בעצם מריץ את הפקודה:

npx create-vite hello-world

תודה שאמרת לי

18/01/2025

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

<a href="{{ url_for('tasks') }}">All Tasks</a>

אבל AI שכותב קוד, למרות שהוא מכיר את url_for יותר טוב ממך, עדיין יכול לכתוב:

<a href="/tasks">All Tasks</a>

ואז כשאתה שואל אותו משהו כמו "למה לא השתמשת ב url_for" הוא מיד מתנצל עם איזה "תודה שאמרת לי וכל הכבוד ששמת לב, והנה אני מתקן את הקוד".

כשזה קורה התגובה הראשונה שלי היא להתעצבן על המחשב - מה זה אומרת תודה שאמרת לי!? אם ידעת ש url_for עדיף למה לא כתבת את זה מההתחלה???

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

Please perform a full code review

רוב הזמן באיטרציה השניה נקבל קוד ברמה יותר גבוהה.

ההערה הזאת היא בכלל פיצ'ר

17/01/2025

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

Consider adding rate limiting to the search endpoint

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

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

אני חושב שיותר נכון להסתכל על הערה מהסוג הזה בתור Feature Request יותר מאשר הערת מימוש. במקרה של Rate Limit זה משהו שצריך לעלות בעקבות Security Review על גירסה. לפעמים ה Security Review קורה על כל גירסה לפני העלאה ואז נקבל דוח ונתרגם אותו למשימות שיהפכו לקומיטים, לפעמים ה Security Review יבוא אחרי מתקפת ה DoS הראשונה.

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

חידת Vue: ריאקטיביות

16/01/2025

נתון קוד Vue הבא:

<script setup lang="ts">
import {ref, computed} from 'vue';

const data = { count: 0 };
const value = ref(data);
function btn1() {
  data.count = 5;
}

function btn2() {
  value.value.count++;
}
</script>

<template>
  <div>
  <p>
    Value is: <span>{{ value.count }}</span></p>
  <button @click="btn1">Button 1</button>
  <button @click="btn2">Button 2</button>
  <hr />
</div>
</template>

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

מה יקרה אם נשנה את התבנית ל:

<template>
  <div>
  <p>
    Value is: <span>{{ data.count }}</span></p>
  <button @click="btn1">Button 1</button>
  <button @click="btn2">Button 2</button>
  <hr />
</div>
</template>

מה יהיו ערכי המשתנים אחרי לחיצה על הכפתורים? מה יופיע על המסך?

טיפ טייפסקריפט: יבוא קבצי JSON גדולים

15/01/2025

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

{
  "compilerOptions": {
    "resolveJsonModule": true,
    "esModuleInterop": true
  },
  "include": ["src"]
}

ואז כותבים:

import settings from './settings.json';

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

[{
  "repo": "TypeScript",
  "dry": false,
  "debug": false
}]

ואני אנסה לכתוב בקוד:

import settings from './settings.json';
console.log(settings[0].foo);

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

בעבודה עם קבצי JSON גדולים שווה להכיר את האופציה allowArbitraryExtensions של טייפסקריפט, אופציה שבעזרתה נוכל להגדיר את הטיפוס של ה JSON ידנית, נחסוך לטייפסקריפט עבודה וכך נקבל VS Code מהיר יותר והגדרות טיפוסים אמינות. ה tsconfig.json נראה ככה:

{
  "compilerOptions": {
    "allowArbitraryExtensions": true,
    "resolveJsonModule": true,
    "esModuleInterop": true
  },
  "include": ["src"]
}

ובתיקיית src אנחנו יוצרים קובץ בשם של ה json אבל עם d באמצע וסיומת ts, לדוגמה אם ל json שלי קוראים deposits.json אז אני יוצר קובץ בשם deposits.d.json.ts. בקובץ הגדרת הטיפוס אני כותב:

export interface SavingsData {
  INTERESTSDATE: string;
  SAVINGSPROGRAMBYAGE: string;
  AGE: string;
  BANK: string;
  SAVINGSPLAN: string;
  SAVINGSPERIOD: string;
  FIXEDWITHOUTLINKAGEVAL: number;
  FIXEDLINKEDCONSUMERPRICEINDVAL: number;
  VARIABLESPREAD: number;
}

declare const array: Array<SavingsData>;
export default array;

ומפה אנחנו מסודרים. אפשר לייבא את הקובץ deposits.json אפילו שהוא שוקל 10 מגה וטייפסקריפט ישתמש בהגדרת הטיפוס שבנינו במקום לקרוא את הקובץ ולנסות להבין את הטיפוס לבד.

למה קשה לנו עם "בעצם זה לא היה רעיון טוב"

14/01/2025

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

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

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

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

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

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

הזמנה לסדנה - מנטורינג לבניית פרויקט

13/01/2025

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

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

האמת שלא מעט דברים:

  1. אין לי זמן להשקיע בפרויקט עכשיו.

  2. אין לי רעיון טוב.

  3. יש לי יותר מדי רעיונות.

  4. אין לי מושג איך מתחילים.

  5. חבל על הזמן, ממילא לא יצא מזה כלום.

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

בתחילת מרץ אני מתכוון לפתוח קבוצת מנטורינג לעבודה על פרויקט Full Stack במשך ארבעה שבועות ובאמצעות שילוב כלי AI עם כל הסטאק הטכנולוגי שאנחנו כבר מכירים. העבודה בקבוצה תתן לכם את המקום והכלים ליצור פרויקט מאפס ועד פרודקשן, ואתם לא לבד: אנחנו נקיים מפגשים קבוצתיים בזום בהם נלמד אחד מהשני ונציג את העבודה שעשינו, נחזיק קבוצת דיונים בווטסאפ ותקבלו פגישות אחד על אחד איתי לייעוץ טכנולוגי ו Code Review ספציפי על הפרויקט. העבודה במסגרת קבוצתית ועם מטרה ברורה של בניית פרויקט תתן גם לכם את המוטיבציה להתאמץ יותר ולהגיע לקו הסיום עם פרויקט בפרודקשן.

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

https://www.tocode.co.il/contacts/new

ניהול טפסים בריאקט עם Formik ו Yup

12/01/2025

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

המשך קריאה

פיתרון Advent Of Code 2024 יום 2 ב Ruby

11/01/2025

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

המשך קריאה

דוגמת קוד: שילוב Redux עם Next.js

10/01/2025

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

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

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

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

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

קוד ה Server Side Rendering יכול לעבוד עם אוביקט המידע שהשרת הכין וקוד צד לקוח יכול לעשות dispatch ל Actions לתוך אוביקט המידע הזה.

הריפו של הדוגמה זמין כאן:

https://github.com/ynonp/next-pages-redux

בואו נראה איך זה עובד.

המשך קריאה