uk
Feedback
ToCode

ToCode

Відкрити в Telegram

טיפים קצרים למתכנתים מאת ינון פרק

Показати більше
1 419
Підписники
Немає даних24 години
Немає даних7 днів
Немає даних30 день
Архів дописів
ToCode
1 419
async function main() {
  const c = new ReactiveClock();
  console.log(c.minutes);
  console.log(c.minutes);
  console.log(c.minutes);
}

main();
ידפיס שלוש פעמים את ההודעה:
Calculate minutes 
אבל אם נוסיף מאזין למשתנה אז מובאקס עובר למצב ריאקטיבי ושומר את הערך בין קריאות, לכן הקוד הבא ידפיס את ההודעה רק פעם אחת, ואחרי זה כל שניה שוב בגלל ה autorun:
async function main() {
  const c = new ReactiveClock();
  autorun(() => {
    console.log(c.minutes);
  });

  console.log(c.minutes);
  console.log(c.minutes);
  console.log(c.minutes);
}

main();
מעניין לשים לב שההדפסה בתוך ה getter תופעל כל שניה, אבל ההדפסה בתוך ה autorun של מספר הדקות שעברו תופעל רק פעם בדקה. ## מידע ריאקטיבי וקומפוננטות ריאקט הבחירה לשמור מידע גלובאלי של היישום בתוך מבנה ריאקטיבי של מובאקס מאפשרת לנו לחבר בקלות את המידע לקומפוננטות ריאקט ב UI במקום סתם להדפיס אותו ב console.log. יש אפילו מודול מובנה במובאקס בשם mobx-react-lite שדואג לכל החיבורים בשבילנו. בואו נראה את זה בפעולה עם שתי דוגמאות - השעון שכתבנו ודוגמת מונה לחיצות ריאקטיבי. נתחיל עם השעון - אני לוקח את אותו קוד שכבר כתבתי ומוסיף לו שורת default export לשעון חדש שאני יוצר, ושומר את הכל בקובץ בשם ReactiveClock.js. זה הקוד:
import { makeObservable, observable, computed, action } from "mobx";

class ReactiveClock {
  constructor() {
    this.ticks = 0;
    setInterval(() => {
      this.tick();
    }, 1000);
    makeObservable(this, {
      ticks: observable,
      minutes: computed,
      tick: action
    });
  }

  tick() {
    this.ticks++;
  }

  get minutes() {
    console.log(`Calculate minutes`);
    return Math.floor(this.ticks / 60);
  }
}

const clock = new ReactiveClock();
export default clock;
עכשיו אני פותח קובץ ריאקט חדש וכותב בו את הקוד הבא:
import "./styles.css";
import { observer } from "mobx-react-lite";
import clock from "./mobx/ReactiveClock";

export default observer(function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <p>Ticks: {clock.ticks}</p>
      <p>Minutes: {clock.minutes}</p>
    </div>
  );
});
וזה הכל. בצורה אוטומטית בזכות הקריאה לפונקציה observer בקוד ריאקט, הקומפוננטה שיצרתי מאזינה לשינויים בכל המשתנים הריאקטיביים שהיא ניגשת אליהם. כל פעם שיש שינוי בצורה אוטומטית הקומפוננטה תחושב מחדש. אתם יכולים לראות את הקוד בפעולה בקודסנדבוקס הבא: <iframe src="https://codesandbox.io/embed/currying-sun-h8wguw?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="currying-sun-h8wguw" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> או בקישור: https://codesandbox.io/s/currying-sun-h8wguw?file=/src/App.js מנגנון כזה של קומפוננטה שמשתמשת במידע גלובאלי הוא דרך קלה לסנכרן מידע בין מספר קומפוננטות. ננסה דוגמה נוספת הפעם עם מחלקה של מונה לחיצות. נתחיל עם המובאקס ונכתוב את המחלקה הבאה:
import { makeObservable, observable, computed, action } from "mobx";

class Counter {
  constructor() {
    this.value = 0;
    makeObservable(this, {
      value: observable,
      click: action.bound,
    });
  }

  click() {
    this.value++;
  }
}

const counter = new Counter();
export default counter;
ה Counter הוא מאוד דומה לשעון אבל במקום לעדכן את הערך אוטומטית הוא מאפשר לקומפוננטות חיצוניות להפעיל את count. בשביל לעשות את זה שיניתי את ההגדרה של הפונקציה שמשנה את הערך באוביקט התיאור ל:
click: action.bound,

ToCode
1 419
הפונקציה autorun של מובאקס היא אחת הדרכים לפתור את האתגר הראשון. היא מקבלת פשוט פונקציה שמשתמשת במשתנים ריאקטיבים, מפעילה אותה, ואז כל פעם שאחד המשתנים הריאקטיביים ישתנה הפונקציה תופעל מחדש. במקרה של השעון שלנו נוכל לכתוב:
const c = new ReactiveClock();

autorun(() => {
    console.log(`Tick: ${c.ticks}`);
});
את הקוד הזה אפשר לכתוב בכל מקום בתוכנית ואין בעיה להפריד בין החלק שיוצר את השעון לחלק של ה autorun. הוא יגרום לזה שכל שניה תודפס לקונסול השורה:
Tick: 0
והמספר ירוץ לפי תקתוקי השעון. כאן צריך לשים לב שהמעקב אחר המידע הריאקטיבי קורה רק בגלל שאנחנו ניגשים למשתנים. הקוד הבא, גם אם קצת מוזר בהקשר הזה, ממחיש את אחת הטעויות הנפוצות שלנו בעבודה מהסוג הזה:
const c = new ReactiveClock();
let isFirst = true;

autorun(() => {
  if (isFirst) {
    console.log(`Setting isFirst to false`);
    isFirst = false;
  } else {
    console.log(`Not a first time - print the value`);
    console.log(`Tick: ${c.ticks}`);
  }
});
הפעם ההדפסה היחידה שאני מקבל היא:
Setting isFirst to false
ואחרי זה כלום - למרות שהשעון ממשיך לתקתק. הבעיה כאן היא שבהפעלה הראשונה של הפונקציה לא נגעתי במשתנה ticks, ולכן ה autorun לא ידעה לקשור את הפונקציה לשינויים במשתנה זה. הקשר בין שני הדברים לא נוצר. מובאקס מספק פונקציית עזר נוספת למצבים כאלה בשם reaction. פונקציה זו יודעת לקבל בתור פרמטר ראשון "פונקציית מידע", שהיא בעצם הפונקציה שיוצרת את הקשרים, והפרמטר השני, "פונקציית פעולה" היא זאת שנקראת כל פעם שיש שינוי במידע. הקוד המתוקן נראה כך:
const c = new ReactiveClock();
let isFirst = true;

reaction(
  () => [c.ticks],
  (value, previousValue, reaction) => {
  if (isFirst) {
    console.log(`Setting isFirst to false`);
    isFirst = false;
  } else {
    console.log(`Not a first time - print the value`);
    console.log(`Tick: ${c.ticks}`);
  }
});
והוא כבר עובד כמו שצריך: פונקציית המידע מחזירה מערך עם איבר אחד, הערך של c.ticks, ופונקציית הפעולה תיקרא כל פעם שמשתנים שניגשו אליהם בפונקציית המידע משנים את ערכם. האתגר השני שלנו היה להגדיר משתנה שערכו מגיב אוטומטית לשינויים ב ticks, וגם את זה אפשר ליצור בצורה ריאקטיבית. נניח שאנחנו רוצים לספור דקות שעברו, ואנחנו יודעים ש ticks מתעדכן כל שניה - אז כל 60 שינויים שלו זה דקה. בגישה נאיבית (שלא תעבוד) אני יכול לנסות לכתוב קוד כזה:
// NOT WORKING CODE

async function main() {
  const c = new ReactiveClock();
  const minutes = c.ticks / 60;
  await new Promise((r) => setTimeout(r, 60000));
  console.log(minutes);
}

main();
אחרי דקה מודפס עדיין הערך 0, בגלל שהשינוי ב ticks לא נכתב החוצה ל minutes. הערך של minutes חושב פעם אחת בשורה השניה של הפונקציה וזהו. מובאקס מאפשר לי להפוך את הקשר הזה לריאקטיבי, ולגרום לכך שכל שינוי ב ticks אוטומטית יעדכן את minutes בעזרת הפונקציה computed. הקוד המתוקן נראה כך:
async function main() {
  const c = new ReactiveClock();
  const minutes = computed(() => c.ticks / 60);
  await new Promise((r) => setTimeout(r, 60000));
  console.log(minutes.get());
}

main();
התיקון מורכב משני שינויים: 1. המשתנה minutes מקבל את ערך ההחזר של פונקציית computed, וזה מה שגורם לערכו להתעדכן כל פעם שאחד המשתנים הריאקטיביים בתוך הפונקציה שהעברנו ל computed משתנה. 2. ערך ההחזר של computed הוא אוביקט ובשביל לקבל את ערכו אני מפעיל את המתודה get שלו. מובאקס מספק קיצור דרך נוסף למי שרוצה להכניס את הערך המחושב לתוך המחלקה, ומאפשר לנו לכתוב קוד כזה בתור מחלקת השעון:
class ReactiveClock {
  constructor() {
    this.ticks = 0;
    setInterval(() => {
      this.tick();
    }, 1000);
    makeObservable(this, {
      ticks: observable,
      minutes: computed,
      tick: action
    });
  }

  tick() {
    this.ticks++;
  }

  get minutes() {
    console.log(`Calculate minutes`);
    return this.ticks / 60;
  }
}
הקוד הפעם מגדיר את minutes בתור משתנה ריאקטיבי נוסף. כל עוד אף אחד לא מסתכל עליו הוא מחושב מחדש כל פעם שפונים לפונקציה minutes, ולכן הקוד הבא:

ToCode
1 419
# מובאקס - מעבר לבייסיקס בעבר כתבתי כאן מספר פוסטים על מובאקס וגם העברתי עליו וובינר של שעה, כולם מתאימים בתור היכרות ראשונה עם מובאקס: 1. מובאקס בעשר דקות 2. איך לנהל State גלובאלי של יישום ריאקט עם MobX 3. וובינר מבוא למובאקס המטרה של מדריך זה היא ללכת קצת יותר רחוק ודרך דוגמאות לחקור צדדים פחות מוכרים של מובאקס, מה הבעיות שצפויים להיתקל בהן במהלך הפיתוח ומה הפיתרונות המקובלים. ## ריאקטיביות מובאקס הוא פריימוורק ריאקטיבי לניהול מידע גלובאלי. מידע גלובאלי זה קל, זה המידע שנשמר מחוץ לקומפוננטות ומשותף לקומפוננטה אחת או יותר. על השאלה איזה מידע כדאי לשמור גלובאלי ואיזה מידע כדאי לשמור בסטייט של קומפוננטות אין תשובה פשוטה. כל מערכת וכל צוות פיתוח מחליטים איפה עובר הגבול שלהם. מובאקס נכנס לתמונה אחרי שהחלטתם שאתם רוצים לשמור מידע מסוים בצורה גלובאלית, ובדומה לרידאקס, מספק תשתית לשמירה של המידע הזה ולכתיבת קוד שמושפע ממנו. המילה השניה הבעייתית - "ריאקטיבי" - מציינת איך מובאקס שומר את המידע הגלובאלי שלו. תכנות ריאקטיבי הוא סוג תכנות בו אנחנו מגדירים קשרים בין משתנים, וכל פעם שמשתנה מסוים מקבל ערך חדש אז כל המשתנים שתלויים בו יתעדכנו אוטומטית. מובאקס מספק שני סוגים מרכזיים של קשרים: 1. ערך מחושב, שהערך שלו נגזר ממידע ריאקטיבי אחר. 2. פעולה אוטומטית שנרצה לבצע כל פעם שמידע ריאקטיבי משתנה. נוח לחשוב על משתנה ריאקטיבי כמו משתנה שכל פעם שהערך שבו מתעדכן הוא גם מדווח לעולם על השינוי. עכשיו אני יודע מה אתם חושבים, בשביל מה צריך שמשתנה אוטומטית ידווח לעולם כל פעם שהערך שלו משתנה? הרי אם אני צריך מנגנון שידווח כשמשתנה מסוים מתעדכן אני יכול להוסיף מנגנון אירועים. התשובה, כמו שנראה בדוגמאות במהלך הפוסט, היא שמנגנון אירועים באמת נותן פיתרון טוב כשיש לי משתנה אחד שצריך להגיב לעדכון שלו, אבל התכנות הריאקטיבי מאפשר לתאר קשרים יותר מורכבים - כמו משתנה ריאקטיבי שממנו נגזר ערך מחושב ומהערך הזה נגזר ערך מחושב נוסף ורק כהערך השלישי משתנה אני רוצה להפעיל קוד מסוים. ככל שמערכת הקשרים בין המשתנים יותר מסובכת, כך אני יותר מרוויח מפריימוורק ריאקטיבי. בשביל לתת לדברים משמעות יותר מוחשית, בואו נכתוב מחלקה ראשונה שכוללת מידע ריאקטיבי - מחלקה של שעון, שמכיל שדה מידע בשם ticks ואחרי שיוצרים אותו הוא אוטומטית מעלה את הערך כל שניה. הנה קוד לא ריאקטיבי לשעון כזה:
class Clock {
  constructor() {
    this.ticks = 0;
    setInterval(() => {
      this.tick();
    }, 1000);
  }

  tick() {
    this.ticks++;
  }
}
אם אני רוצה לקחת את השעון שיצרתי ולהדפיס את הערך של ticks כל פעם שהוא משתנה אני חייב לשנות את קוד המחלקה, כי כרגע השעון לא מספק לי דרך טובה לתקשר איתו. אפשרות אחת לשינוי תהיה להכניס את פקודת ההדפסה לתוך הפונקציה tick; אפשרות קצת יותר טובה היא להוסיף מנגנון אירועים לשעון כך שפונקציית הבנאי תקבל Callback Function ותפעיל אותה כל פעם ש ticks נקראת. מובאקס נותן אפשרות שלישית והיא להפוך את השעון לריאקטיבי. הפקודה makeObservable של מובאקס הופכת כל מחלקה ל Observable, כלומר למשהו שאפשר להסתכל על השדות שלו. הפונקציה מקבלת שני פרמטרים, הראשון הוא אוביקט לעבוד עליו והשני מתאר את שדות המידע והפונקציות במחלקה ומסביר מה התפקיד של כל שדה מידע או פונקציה. זאת הגירסה הריאקטיבית של השעון:
import {
  makeObservable,
  observable,
  action,
} from "mobx";

class ReactiveClock {
  constructor() {
    this.ticks = 0;
    setInterval(() => {
      this.tick();
    }, 1000);

    makeObservable(this, {
      ticks: observable,
      tick: action
    });
  }

  tick() {
    this.ticks++;
  }
}
מתוך הבנאי קראתי ל makeObservable והעברתי לו את this ואוביקט תיאור. אוביקט התיאור אומר ששדה ticks הוא Observable, כלומר קוד חיצוני יכול להסתכל על שינויים בו, ו tick היא פונקציית עדכון של Observable, כלומר היא פונקציה שיכולה לשנות מידע ריאקטיבי והיא תדע להודיע לכל "המאזינים" של אותו מידע שמשהו השתנה. אחרי שבנינו שעון ריאקטיבי האתגר הבא הוא להשתמש בו בצורה ריאקטיבית. אפשרות אחת להשתמש במשתנה ריאקטיבי היא לכתוב קוד שירוץ אוטומטית כל פעם שיש שינוי בערך המשתנה; אפשרות שניה היא לכתוב משתנה שערכו מתעדכן אוטומטית כל פעם שהמשתנה הריאקטיבי המקורי משתנה.

ToCode
1 419
# איך ללמוד כל טכנולוגיה ממש מהר תלמיד שואל: "אני צריך להתחיל לעבוד עם טכנולוגיה X אבל אין לי זמן לקחת עכשיו קורס מלא עליה. מה הדרך הכי מהירה להתחיל לכתוב קוד?" והתשובה הקלה - פשוט להתחיל לכתוב קוד. תמצא את הדרך הכי מהירה להגיע לאפליקציה ראשונה, ומשם תתחיל להוסיף פיצ'רים. בכל טכנולוגיה שראיתי אפשר היה למצוא מדריך באינטרנט שמדגים איך לקחת את הצעד הראשון, ואז עם קצת התאמות ושינויים אפשר היה להתקדם ממנו לצעד השני, השלישי וגם העשירי. לפעמים אתה נתקע וצריך לחפש תשובות ב Stack Overflow או בעוד מדריכים, אבל בגדול אפשר להגיע לתוצאות לא רעות. את הקוד של אתר ToCode כתבתי ב Rails לפני שהכרתי ריילס. צעד אחרי צעד בניתי אתר שעובד עם מערכת ניהול והתחלתי להעלות קורסים ופוסטים לבלוג ומפה לשם אנחנו כבר 7 שנים באוויר. בזמן העבודה על האתר הצלחתי לפתור את רוב הבאגים למרות שלא את כולם הצלחתי להבין. ככל שכתבתי יותר כך חיפשתי להבין יותר, ואז התחלתי לקחת קורסים בריילס ולקרוא לעומק את התיעוד. ככל שהבנתי יותר הצלחתי לכתוב מחדש חלקים מהקוד ולהעלות את הרמה שלהם, וזה תהליך שעדיין נמשך. בתכנות, לימוד טוב הוא כמו משחק פינג-פונג בין פרקטיקה לתיאוריה: מתחילים לבנות, חוזרים ללמוד ואז חוזרים לשפר. נכון, איכות הקוד לא תהיה הכי טובה שאפשר, אבל בקצב ואיכות הלימוד אי אפשר להתחרות.

ToCode
1 419
# בעיות לא גדולות בכל פרויקט יש המון בעיות גדולות אבל יש גם לא מעט בעיות קטנות, שהן קצת מציקות אבל לא מספיק מציקות בשביל שמישהו יתקן אותן, למשל: 1. כל פעם שמעלים גירסה יש כמה שניות של Down Time בהן המערכת לא זמינה. 2. פעולה מסוימת שעושים אותה בתדירות נמוכה דורשת עבודה ידנית לפי איזה מדריך. 3. באג מעצבן בממשק המשתמש שכל פעם שעושים רצף פעולות מסוים צריך ללחוץ על איזה כפתור לא קשור או לרענן את העמוד. 4. יש כמה בדיקות שמדי פעם נכשלות בלי שאף אחד יודע למה. יש צוותים שמחזיקים רשימות של הבעיות הלא גדולות האלה. במקומות אחרים זה משהו שאתה מגלה מהר מאוד כשאתה מתחיל לעבוד על הפרויקט. בכל מקרה עד שאתה נחשף לבעיות כבר יש לך יותר מדי עבודה והבעיה לא מספיק מציקה. אני אוהב להסתכל על בעיות לא גדולות ובמיוחד על כאלה שכולם מכירים בתור הזדמנות. הרבה מהבעיות האלה יחסית קל לפתור ולכן יש להן ROI גבוה. אם הצלחתם "לגנוב" שעתיים ולפתור בעיה לא גדולה זה משהו שהרבה אנשים בארגון ישימו לב אליו, כי היא הציקה להם הרבה זמן. כשאתם חדשים בארגון, כשאתם באים רק לכמה ימים לייעוץ, כשאתם וותיקים שרוצים לרענן, ובעצם בכל מצב - צרו לעצמכם הרגל לחפש בעיות קטנות ולפתור אותן תוך כדי תנועה. מהר מאוד תתחילו להרגיש בהבדל.

ToCode
1 419
# אף אחד אף פעם לא ירצה את זה הפונקציות atob ו btoa של JavaScript משמשות להמרה של מחרוזות ל Base64 ובחזרה. מכל מיני אילוצים טכניים מאפייני שפת JavaScript החליטו שהן יעבדו טוב רק על מחרוזות ASCII כלומר מחרוזות באנגלית בלבד. אז אני יכול לכתוב:
> btoa("ninja")
'bmluamE='
אבל לא יכול לכתוב:
> btoa('תפוז')
Uncaught:
DOMException [InvalidCharacterError]: Invalid character
    at new DOMException (node:internal/per_context/domexception:70:5)
    at __node_internal_ (node:internal/util:497:10)
    at btoa (node:buffer:1229:13)
לכן ה Best Practice בשביל להשתמש בפונקציות אלה הוא להשתמש בעוד המרה לפני הפעלתן. כך מהתיעוד:
function utf8_to_b64( str ) {
  return window.btoa(unescape(encodeURIComponent( str )));
}

function b64_to_utf8( str ) {
  return decodeURIComponent(escape(window.atob( str )));
}
הקידוד ל Base64 בשיטה שמוצעת בתיעוד הוא בדיוק מה שאני מקבל משפה כמו Ruby כברירת מחדל. הבחירה של מתכנני JavaScript לבנות API שיעבוד רק על טקסטים מסוימים היא מוקש שמחכה להתפוצץ. אלה בדיוק הבאגים שקורים רק ללקוח ספציפי ואחרי זה קשה למצוא אותם. מה עושים? בתור משתמשים של API חשוב להבין איזה מקרי קצה מפילים אותו ואיזה Best Practices מומלצים בעבודה איתו. גם אם נראה לכם שאף אחד אף פעם לא ירצה לבחור סיסמה בעברית, עדיין שווה לעבוד לפי ה Best Practices כדי למנוע בעיות בעתיד. ואם יום אחד תחליטו להיות בעצמכם מתכנני APIs, שווה לדאוג שהדרך הנכונה תהיה גם הדרך הקלה.

ToCode
1 419
# חדש ב Node 18 - תמיכה מובנית ב Fetch API גירסה 18 של נוד מביאה איתה לא מעט חידושים: התמיכה ב ES Modules השתפרה פלאים, הוסיפו מודול מובנה להרצת בדיקות יחידה, ונוספה תמיכה לשני ממשקי רשת מהדפדפן שהם Fetch API ו Web Streams API. בפוסט זה אציג את השימוש ב Fetch API ב Node. ## דוגמת Fetch API ופוקימונים פוקי API הוא אתר דוגמה מעולה לבדיקת בקשות API. אתם פונים לנתיב על שרת זה ויכולים לקבל חזרה מידע על פוקימונים. לדוגמה הקישור: https://pokeapi.co/api/v2/pokemon/ditto ייתן לכם מידע על הפוקימון שקוראים לו ditto. עד עכשיו בשביל לשלוף את המידע הזה מתוכנית Node.JS היינו צריכים להתקין חבילה חיצונית או לעבוד קשה ולטפל בכל מיני מקרי קצה לא מעניינים. החל מ Node 18 אפשר לכתוב תוכנית כזאת כדי להציג את כל המידע:
async function main() {
  const res = await fetch('https://pokeapi.co/api/v2/pokemon/ditto');

  if (res.ok) {
    const data = await res.json();
    console.log(data);
  }
}

main();
כרגע התמיכה עדיין ניסיונית וכשמריצים את התוכנית מקבלים את הודעת האזהרה:
(node:44313) ExperimentalWarning: The Fetch API is an experiment
al feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was c
reated)
אם לא מפריע לכם שאולי תצטרכו לשנות חלק מהקוד בעתיד, או אם אתם כותבים סקריפטים קטנים שעוד מעט ייזרקו, שווה להתחיל לשלב את fetch בתוכניות שלכם. לדברים יותר חשובים אפשר לחכות ל node 20. בכל מקרה כיף לראות את החיבור בין Node לדפדפן ואת האפשרויות שהוא פותח לשיתוף קוד בין שתי הסביבות.

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

ToCode
1 419
# ארבע עצות לעצמאים בתחילת הדרך לפני 11 שנים עזבתי את העבודה כשכיר ויחד עם חבר מתכנת הקמנו חברה לבניית אפליקציות ללקוחות. בדמיון ראינו מאות חברות עומדות בתור כדי שאנחנו נבנה להם אפליקציה. אף אחד לא סיפר לנו איך זה עובד במציאות או מה צריך לעשות כדי לקבל הזדמנות. אלה העצות שאולי היו עוזרות לנו להצליח ואולי יעזרו לכם- ## התחילו בלקוחות הדבר הראשון שאתם רוצים לעשות כשפותחים עסק חדש הוא להבין מי אמורים להיות הלקוחות שלכם ולתת להם דרך להגיע אליכם. פה לא מספיק לכתוב פוסט בפייסבוק שאומר "תראו אני בונה אפליקציות" או להתקשר לכל החברים ולבקש הזדמנות. לא משנה כמה אתם נחמדים (ואנחנו היינו נחמדים), מנגנון שיווק רב שכבתי הוא הכרחי כדי לקבל הזדמנות בעסקים, וזה הדבר הראשון לבנות או לתקן כשבונים עסק. כשאני אומר שיווק רב שכבתי הכוונה שלקוחות פוטנציאליים שלכם צריכים לעבור אתכם תהליך לפני שהם מוכנים לשלם. ככל שהמוצר יותר יקר כך התהליך יהיה יותר ארוך. אז אם אנחנו רוצים לבנות חברה שבונה אפליקציות היינו רוצים לייצר- 1. תוכן שאנשים יכולים להיחשף אליו בקלות - למשל אפליקציות חינמיות פשוטות וכיפיות, עדיף ויראליות, עדיף משחקונים ודברים כאלה. 2. תוכן שאנשים שמחפשים לבנות אפליקציה מחפשים ומוכנים לקבל בקלות - דברים בסגנון של מאמרים ומדריכים. אז כאן יהיה לנו מדריך "10 טעויות נפוצות שאנשים עושים כשבונים אפליקציה", או "רשימת 5 האפליקציות המעוצבות של החודש". לקבלת המדריך צריך להכניס כתובת מייל. לכל מדריך כזה יהיה עמוד נחיתה ואתם תצטרכו לשלם קצת לפייסבוק וגוגל כדי להביא אנשים לדפי הנחיתה שיצרתם. 3. תוכן שאנשים שהכניסו כתובת מייל מוכנים לקבל, ועוזר להם להבין מי אתם ולסמוך עליכם - מה שנקרא "סידרת מסרים". בנושא אפליקציות הייתי בונה סידרה שאומרת "איך בונים אפליקציה לעסק שלך" ומורכבת מ 10-15 מדריכים, כל אחד מכסה אספקט שונה של התהליך. את המדריכים האלה אנחנו שולחים ללקוח כל יום למייל וכל מדריך מסתיים בהצעה לרכישה של השירות שלנו. מנגנון שיווק כזה מביא לעסק שלכם כל הזמן פניות מאנשים רלוונטים ומכניס אתכם למעגל של עשייה. ## הציעו פיתרון מדויק לבעיה ספציפית כשהתחלתי את העסק של פיתוח אפליקציות היה לי מודל ברור בראש - לקוח יגיע, יבקש אפליקציה ואני אכתוב אותה. איזה אפליקציה? מה זה משנה. מי הלקוח? מי שבא ברוך הבא. באיזה טכנולוגיה? עכשיו הגזמתם - אני יודע לכתוב בכל שפה שצריך. זאת לא דרך להקים עסק שעובד. לא נתתי לאף לקוח שום סיבה מיוחדת לרצות לעבוד איתי. הגישה שלי היתה "אתם יכולים לבחור בכל אחד, ואני כל אחד". והיא לא עבדה. אם היום הייתי מתחיל מחדש הייתי מחפש לצמצם כמה שיותר את סוג הבעיות שאני פותר, ולייצר קודם כל פיתרון אחד שעובד ושאפשר להתקיים ממנו. אחר כך, אם עוד יהיה זמן ורצון, אפשר לבנות פיתרונות נוספים. הנה כמה דוגמאות לעסקים הרבה יותר ספציפיים בתוך העולם של פיתוח אפליקציות שהייתי יכול להקים: 1. פיתוח אפליקציה גנרית למורים פרטיים (מאפשרת קשר עם תלמידים, מעקב אחר שיעורי בית, ניהול יומן, תזכורות וכו'), והתאמתה לכל מורה פרטי שאפגוש. הלקוחות היחידים שיהיו לי הם מורים פרטיים, ולהם אני יכול להציע מחיר תחרותי ופיצ'רים שאין לאף אחד אחר. אם זה לא עובד אפשר להמשיך ולנסות לפתח אפליקציה גנרית לקהלי יעד נוספים. 2. פיתוח ספריית קוד פתוח שתעזור למפתחי אפליקציות לשלב פירסומות באפליקציות שלהם, ויצירת מודל של עזרה בתשלום ללקוחות שמשתמשים בספריית הקוד הפתוח. אם הספריה לא הפכה למספיק פופולרית אפשר לנסות שוב עם ספריית קוד פתוח אחרת. 3. פיתוח כלי למפתחים שיעזור להם למצוא בעיות אבטחה בקוד של אפליקציות שהם כותבים, והצעת פיתרון משלים של ייעוץ כדי לעזור לסתום את החורים שהתגלו. 4. פיתוח כלי No Code שמאפשר לאנשים לבנות אפליקציות בקלות על בסיס תבנית קיימת. אנחנו יכולים להתחיל מכלי שבונה אפליקציית "כרטיס ביקור", ומי שמשתמש בכלי יכול לראות איזה לקוחות שלו הפעילו את אפליקציית כרטיס הביקור, לשלוח להם נוטיפיקציות, להציע מבצעים ודברים בסגנון. פיתוח רעיון ספציפי אוטומטית מעלה רמה את השיחה שלכם עם לקוחות פוטנציאליים. במקום "אתם יכולים לבחור כל אחד, ואני כל אחד" אפשר להגיד "בניתי כלי על סמך עבודה עם עשרות מורים פרטיים שכבר נעזרים בו כדי לתקשר עם התלמידים שלהם. אלה היכולות שיעזרו לך להרוויח יותר כסף בתור מורה פרטי ואלה הסיבות בגללן כדאי לך להשתמש בכלי". ## הקיפו את עצמכם באנשים מקצועיים

ToCode
1 419
# שמונה עשרה תרגילי גיט כדי להתחיל את השבוע גיט הוא מסוג הכלים בו תמיד יש דרך לפתור בעיות אבל היא לא תמיד ברורה. אני מקווה ש 18 התרגילים הבאים יעזרו לכם לחקור ולהבין את ההגיון של גיט, וקצת להתאמן על פעולות נפוצות בעבודה עם הכלי. ## עבודה יום יומית צרו פרויקט עם קובץ אחד ו-3 קומיטים, בכל קומיט כיתבו שורה נוספת לתוך הקובץ כך שבקומיט השלישי יהיו בקובץ 3 שורות. 1. הציגו את תוכן הקובץ כפי שהיה בכל אחד מהקומיטים. 2. הציגו את תוכן הקובץ וליד כל שורה הציגו מאיזה קומיט היא הגיעה. 3. הציגו את תוכן הקובץ כפי שהיה בקומיט הראשון. 4. החזירו את הקובץ למצב בו הוא היה בקומיט הראשון. לאחר מכן החזירו אותו שוב למצב בו הוא היה בקומיט השלישי. 5. מחקו את כל הטקסט בקובץ והחליפו אותו ב-3 שורות חדשות לגמרי. צרו גם קובץ חדש עם טקסט חדש. עכשיו בטלו את כל השינויים שלכם באמצעות שליחתם ל Stash. 6. החזירו מה Stash את השינויים והכניסו אותם לקומיט. 7. צרו ענף חדש בשם test בשביל הקומיט הרביעי. הוסיפו שם קובץ נוסף ובצעו קומיט חמישי לאותו ענף. 8. חיזרו לענף main, הוסיפו שורה חדשה לקובץ ובצעו קומיט חדש לענף main. 9. מזגו את השינויים מ test לתוך main. החליטו איזה שורות אתם רוצים בקומיט המיזוג. 10. הציגו את הלוג עם גרף הקומיטים המלא שמראה את ה Merge Commit ואת שני ההורים שלו. ## אופס עשיתי את זה שוב בעבודה עם גיט מאוד קל לתקן טעויות כשאנחנו יודעים מה אנחנו עושים. בחלק זה ננסה לשבור דברים כדי לתקן אותם. צרו פרויקט חדש עם שלושה קבצים בשמות apple.txt, banana.txt ו orange.txt. בכל קובץ כתבו את הצבע של הפרי שמתאים לשם הקובץ ובצעו קומיט ראשון. 1. עדכנו את שלושת הקבצים בפרויקט ובתוך כל אחד מהם הוסיפו שורה נוספת (לא משנה מה כתוב בה). אחרי העדכון צרו ענף חדש והכניסו את השינוי לענף החדש. 2. חזרו לענף הראשי, עדכנו שוב את שלושת הקבצים בפרויקט ובצעו git add לשלושת הקבצים. עכשיו צרו ענף חדש והכניסו את השינוי לתוך הענף החדש. 3. חזרו לענף הראשי, עדכנו שוב את שלושת הקבצים בפרויקט ובצעו git add וגם git commit. עכשיו צרו ענף חדש והזיזו את השינוי (כלומר הקומיט) לענף החדש. 4. חזרו לענף הראשי ושנו את הטקסט של הודעת הקומיט של הקומיט הראשון. 5. עדכנו את שלושת הקבצים ובצעו קומיט שני. 6. עדכנו שוב את שלושת הקבצים ובצעו קומיט שלישי. 7. מזגו את שלושת הקומיטים בענף הראשי לקומיט יחיד שהודעת הקומיט שלו תהיה הטקסט "Initial Commit". 8. עברו לענף שיצרתם בסעיף 3 והפעילו git log. האם אתם רואים את הקומיט היחיד מהענף הראשי? עדכנו את הענף כך שהקומיט היחיד בו יהיה קומיט שיוצא מהענף הראשי. 9. מזגו את הענף החדש לענף הראשי באמצעות Fast Forward Merge ומחקו את כל הענפים מלבד הענף הראשי.

ToCode
1 419
# היום למדתי: ההבדל בין URL ל URI לא להאמין שהעברתי חיים שלמים בלי לדעת את זה, אבל אם גם אתם פתאום לא בטוחים אז הגעתם למקום הנכון. קודם כל משמעות ראשי התיבות דומה - URL הוא Uniform Resource Locator ו URI הוא Uniform Resource Identifier. ההבדל ביניהם הוא עדין אבל חשוב: ה URI הוא מזהה ייחודי של "משאב" בטכנולוגיית ווב. אפשר להשתמש בהם כדי לזהות אתרים באינטרנט אבל גם אנשים, מקומות, מושגים, ולמעשה כל דבר. ה URI של משאב לא חייב להיות כתובת אינטרנט אמיתית, ויכול להיות כל מחרוזת שנראית כמו כתובת אינטרנט. לכן כל אלה הם URI-ים:
https://schema.org/Person
foo://example.com:8042/over/there?name=ferret#nose
urn:oasis:names:specification:docbook:dtd:xml:4.1.2
urn:isbn:0-486-27557-4
https://stackoverflow.com/questions/4913343/what-is-the-difference-between-uri-url-and-urn
data:,Hello%20World
חלק מה URI-ים מתיחסים למסמך ספציפי ברשת ומסבירים איך להגיע אליו. למשל ה URI הזה:
https://stackoverflow.com/questions/4913343/what-is-the-difference-between-uri-url-and-urn
הוא קישור ל Stack Overflow. הוא סוג של "מצביע" לדף בפרוטוקול https. מזהים כאלה נקראים URL-ים, כי הם מספקים דרך לאתר (to locate) את המשאב שהם מזהים. הרבה פעמים, וזה קצת מבלבל, ה URI יהיה בעצם כתובת אינטרנט של דף שלא באמת קשור לדבר שהוא מזהה. לדוגמה ב HTML 4 מסמך היה מתחיל בשורה:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
        "http://www.w3.org/TR/html4/strict.dtd">
אבל לדפדפנים לא היה אכפת אם הייתם טועים בכתובת האינטרנט שמופיעה שם. הסיבה שיש כתובת אחרי ה DOCTYPE היא רק בשביל לזהות את הגירסה של ה HTML, ובעצם השורה מתפקדת בתור URI ולא URL - אף אחד לא משתמש בה כדי לקרוא את המסמך שנמצא בה. אותו סיפור קורס ב XML Namespaces וגם במזהים של schema.org. בקיצור אם יש לנו כתובת שמזהה מסמך ואנחנו משתמשים בה כדי להגיע למסמך הזה נקרא לה URL. אם יש לנו מזהה ייחודי של "משהו" שאנחנו לא צריכים באמת להגיע אליו אנחנו קוראים לזה URI. למידע נוסף שווה לקרוא את הפוסט הזה מהבלוג של auth0 שעוזר לחדד את ההבדל בין המושגים: https://auth0.com/blog/url-uri-urn-differences/

ToCode
1 419
# הדרישה שלא כותבים במודעת הדרושים דה מרקר עשו אתמול כתבת ספיישל על מה אומרת כל דרישה במודעות דרושים, אבל הדרישה הכי חשובה בכלל לא הופיעה ברשימה שלהם, וגם לא מופיעה ברוב מודעות הדרושים. הדרישה היא- "מבין או מבינה את הסיפור" להבין את הסיפור זה אומר שאת יכולה לראות קוד של מערכת ולהבין: 1. מה המטרות של הקוד הזה 2. מה האילוצים שהקוד צריך להתמודד איתם 3. מה היו האילוצים של מי שכתבה את הקוד 4. איזה חלקים שם בכוונה ומה שם בטעות 5. מה הולכות להיות הבעיות בקוד הזה בעתיד הקרוב 6. איזה בעיות סביר להניח שיהיו לקוד הזה בעתיד היותר רחוק 7. לאיזה שינויים בעולם הקוד הזה נכתב, כלומר לאיזה דרישות חדשות יהיה קל להתאים את הקוד 8. איזה שינויים בעולם לא אמורים לקרות, כלומר לאיזה דרישות חדשות יהיה מאוד קשה להתאים את הקוד אנשים שיודעים לקרוא קוד ולהבין את הסיפור שלו יצליחו הרבה יותר בקלות לתחזק ולשפר מערכת בלי לשבור אותה. דרך טובה להתאמן על זה היא לענות על 8 השאלות ברשימה כאן על קוד שאתם כתבתם. ודרך טובה לכתוב קוד יותר מדויק היא לנסות לענות על כל 8 השאלות לפני שמתחילים לכתוב את הקוד.