יש לי רעיון לסטארטאפ- התאמת מחשב

אז- יש לי רעיון לסטארטאפ!

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

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

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

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

מודל עסקי

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

(ויקיפדיה)

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

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

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

Lean Canvas

Lean Canvas מהאתר http://www.businessmodel.co.il
Lean Canvas מהאתר http://www.businessmodel.co.il

כל התוכנית העסקית נמצאת בלוח שלמעלה.

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

לפני שנתחיל

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

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

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

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

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

הבעיה

בשלב הראשון נצטרך להגדיר את הבעיה.

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

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

הלקוח:

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

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

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

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

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

הצעת הערך

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

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

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

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

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

האם הצעת הערך שלי עונה על הבעיות של הלקוחות?

הפתרון

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

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

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

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

ייתרון תחרותי

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

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

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

הכסף והעלויות

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

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

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

מדדי הצלחה

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

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

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

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

ערוצי הפצה

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

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

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

פרסום ברשתות החברתיות, מודעות בחנויות מחשבים, פרסום ממומן.

זהו, עד כאן.

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

קישורים:

מה כדאי לסטודנט לדעת- CI/CD

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

אחרי הפוסטים של טסטים וגיט, הגיע הזמן לשילוב שלהם- תהליכי CI/CD.

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

  • CI: אינטגרציה רציפה (continues integration), כלומר, רציפות של איחוד הקוד המקומי עם הקוד המשותף.
  • CD: מסירה רציפה (continues deployment), כלומר, העברה רציפה של הקוד מסביבת הבדיקות לסביבת השימוש.

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

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

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

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

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

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

CI

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

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

תהליך הCI מתמקד בבדיקות, ולכן נפגוש אותו כשנעשה Pull Request, והוא יריץ בדיקות קצרות (בדרך כלל build וUnit tests לפחות) כדי לוודא בצורה בסיסית שאין בעיה קריטית בקוד, ושאנחנו יכולים לבצע Merge.

לאחר מכן תהליך CI אחר יריץ בדיקות מלאות יותר, מUnit tests ועד e2e (ר"ת End to end), ואולי גם בדיקות על שרתי טסטים וכד'.

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

CD

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

למה אני מתכוון?

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

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

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

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

לקריאה נוספת:

  • ג'נקינס– ג'נקינס היא תוכנת קוד פתוח לביצוע תהליכי CI/CD. ניתן להתקין אותה על כל מחשב (המחשב האישי, שרת החברה, שרת בענן) ולהגדיר פעולות לביצוע.

מושגים לשפה המקצועית:

  • CI: אינטגרציה רציפה (continues integration), כלומר, רציפות של איחוד הקוד המקומי עם הקוד המשותף.
  • CD: מסירה רציפה (continues deployment), כלומר, העברה רציפה של הקוד מסביבת הבדיקות לסביבת השימוש.

מה כדאי לסטודנט לדעת – Git

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

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

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

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

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

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

דבר נוסף שיכול לקרות, שננסה לשנות איזה משהו מסובך בקוד שלנו, ניגע באיזה 5 מחלקות בכמה קבצים שונים, נשנה כל מיני ברירות מחדל ואופס- לא חשבנו על משהו ובעצם זה לא ילך. איך חוזרים אחורה? לא חושב שמספיק לעשות Ctrl+Z לכל הקבצים..

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

הבעיות

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

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

הפתרון

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

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

איך זה עובד?

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

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

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

אז מה יש לנו:

נעבור על ארבעת הבעיות שתיארנו.

חוסר יכולת לבטל שינויים של הקוד

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

"זוכרים"? בשביל שלא נצטרך לזכור (ובשביל עוד דברים) יש לנו את הענפים (מצביעים, כן?).

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

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

קושי בעבודה מבוזרת

אז איך פותרים את הסיפור הזה?

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

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

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

ביצוע Merge– שני ענפים שונים (אמנם אותו שם, אבל ענף אחד בשרת וענף אחד אצלנו) מתאחדים לCommit אחד אחרי Merge.

גיבויים של הקוד

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

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

עבודה במקביל על כמה שינויים ללא תלות של שינוי אחד בשני

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

מעבר בין ענפים לצורך תיקון באג במוצר

יתרונות נוספים

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

  • Blame– אם אני מגיע לקטע קוד בפרויקט מאוד גדול, ואני לא מבין מה קורה שם, אני יכול לראות מי האחרון שנגע בתיקייה / בקובץ / בשורה, ולפנות אליו כדי לקבל מידע נוסף.
  • Bisect– אם אנחנו מגיעים לגרסה מסוימת ומגלים שיש באג. איך אנחנו מוצאים מה גרם לבאג? אם מקפידים שבכל Commit הקוד יתקמפל, אפשר לעשות חיפוש בינארי בעזרת Bisect. אנחנו אומרים לו מה הCommit הטוב האחרון שאנחנו זוכרים, וגיט מעביר אותנו כל פעם לCommit באמצע הדרך ושואל אותנו אם הוא טוב או רע. בסוף גיט אומר לנו מה הCommit הראשון שבו יש שגיאה, ואנחנו רואים מה השינוי שבוצע ומבינים (או שלא) מה גרם לבאג.

קריאה נוספת

  • רן בר זיק באתר "אינטרנט ישראל"– רן בר זיק הוא מפתח Frontend והוא מסביר באתר שלו על המון דברים מעניינים, מפיתוח בטכנולוגיות Web ועד "סייבר". הקישור מפנה לסדרת ההדרכות שלו על גיט.
  • הספר של גיט– לקוראי האנגלית, כאן ההסבר הרשמי של גיט על הטכניקות של הכלי, תוך כדי הסברים על אופן השימוש בפועל.
  • ממשק משתמש לגיט– תוכנת Git Extension היא תוכנה נחמדה לעבודה מול גיט בממשק משתמש אינטראקטיבי ולא בשורת הפקודה. אני משתמש הרבה בתוכנה הזאת.

מושגים

  • Git– גִיט הוא כלי לניהול גרסאות, שמאפשר לנו לעבוד על אותו קוד עם אנשים אחרים, ולעבור בקלות ובמהירות בין גרסאות שונות של הקוד לצורך משימות שונות.
  • GitHub– גִיטהָאבּ הוא אתר לשיתוף קוד שמתבסס על תוכנת גיט.
  • Commit– קוֹמִיט היא הפעולה הבסיסית בGit, שאומר לכלי לשמור את המצב הנוכחי כגרסה.
  • Feature– תכונה. משמש אותנו לתיאור "יכולת" מסוימת. למשל- "ווטסאפ הוסיפו פיצ'ר חדש, וי כחול".
  • Branch– מצביע על Commit מסוים בגיט. או ביותר אנושי- רצף קומיטים כלשהו.

Hyper-V: מכונה וירטואלית בWindows

החל מWindows 8, חברת Microsoft הוסיפה למערכת ההפעלה את תכונת Hyper-V, אפשרות לדמות כונן, נתב או מערכת הפעלה בתוך Windows. אם עד עכשיו היינו צריכים להוריד תוכנה מסוימת לשם כך, עכשיו זה זמין יותר, ולדעתי גם פשוט ובטוח יותר.
הרעיון של מערכת הפעלה בתוך מערכת הפעלה (virtual machine- מכונה וירטואלית) משמש אותנו במידה ואנחנו זקוקים למערכת הפעלה שונה מזאת שיש לנו עכשיו, או שאנחנו רוצים לעשות ניסויים או פעולות בלי חשש לפגיעה בקבצים, הגדרות, תוכנות וכד'. מעין סביבת עבודה מוגנת.

דרישות:

חשוב לשים לב לדברים הבאים:

  1. קובץ iso- בשביל להפעיל מערכת הפעלה נוספת, נצטרך את קובץ הiso שלה. פשוט מחפשים בגוגל Windows 7 iso ומורידים, ממקור אמין כמובן.
  2. Maximum RAM- לתוכנת Hyper-V יש מגבלה כלשהי של זכרון שהיא מאפשרת. המערכת שאנחנו רוצים להפעיל חייבת לתמוך בכמות הזיכרון הזאת. את המגבלה אפשר לבדוק בהמשך בשלב 6, מה שאומר שצריך להמשיך רגע במהירות במדריך, לבדוק את המגבלה, ואז לחזור ולהוריד קובץ iso מתאים.

התקנה:

כדי להפעיל את Hyper-V נצטרך להוסיף את התכונה לWindows.

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

תפריט

לחיצה על "הפעל או בטל תכונות Windows":

 

בחירה בHyper-V ולחיצה על אישור:

 

לאחר ההתקנה אפשר לחפש את Hyper-V Manager ולפתוח אותו. נקבל חלון כזה:

 

כדי להוסיף מערכת הפעלה וירטואלית חדשה, נלחץ על New>Virtual Machine, והגענו לחלון ההגדרה:

  1. Next.
  2. לתת שם למכונה שלנו.
  3. Next.
  4. Generation 1 (אם המערכת הפעלה שאנחנו מתכוונים להפעיל תומכת ב64 ביט, ניתן לבחור גם בGeneration 2).
  5. Next.
  6. 1024, Next.
  7. Next.
  8. Next.
  9. Install an operating system from bootable CD/DVD-ROM.
  10. Image file.
  11. לבחור את הקובץ iso של המערכת שאנחנו רוצים להפעיל (מה זה הקובץ הזה- אני מסביר בהמשך.)
  12. Next.
  13. Finish.

 

מה כדאי לסטודנט לדעת- טסטים

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

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

אז הנושא הראשון שבחרתי הוא-

טסטים- בדיקות (Tests)

ואחרי כל ההקדמה- על מה מדובר?

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

מקרה לדוגמא:

מבוסס על סיפור אמיתי- הטסטים הראשונים שלי.

ניקח תרגיל ממבנה נתונים:

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

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

אני אציג רק את הקוד הרלוונטי כדי לא להעמיס (המימוש הוא בשפת #C. אני מחפש תוסף שיאפשר להציג בכמה שפות):

<br>
class PersonList<br>
{<br>
    private Person head;<br>
    public void InsertNewPerson(Person person)<br>
    {<br>
        var temp = head;<br>
        while (temp.Next()?.age > person.age)<br>
        {<br>
            temp = temp.Next();<br>
        }<br>
        var next = temp.Next();<br>
        temp.SetNext(person);<br>
        person.SetNext(next);<br>
    }<br>
    public int GetMinAge()<br>
    {<br>
        var temp = head;<br>
        while (temp.HasNext()) temp = temp.Next();<br>
        return temp.age;<br>
    }<br>
    public double GetAverage()<br>
    {<br>
        var temp = head;<br>
        var sum = 0, i = 0;<br>
        for (; temp.HasNext(); i++, temp = temp.Next()) sum += temp.age;<br>
        return sum / i;<br>
    }<br>
}<br>

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

לא!

סוגי הבדיקות

הבדיקות נחלקות ל4 קבוצות, כשכל קבוצה מרחיבה את הבדיקות של הקבוצה הקודמת.

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

בדיקות יחידה (Unit Tests):

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

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

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

[TestClass]
class PersonListTests
{
    [TestMethod]
    public void InsertNewPerson()
    {
        var personToAdd = new Person("Baruch", 25);
        var personList = new PersonList();
        personList.InsertNewPerson(personToAdd);

        Assert.AreEqual(personToAdd, personList.GetHead());
    }

    [TestMethod]
    public void GetMinAge()
    {
        var personList = new PersonList();
        personList.InsertNewPerson(new Person("A", 34));
        personList.InsertNewPerson(new Person("B", 100));
        personList.InsertNewPerson(new Person("C", 0));
        personList.InsertNewPerson(new Person("D", 34));
        personList.InsertNewPerson(new Person("E", 22));
        personList.InsertNewPerson(new Person("F", 57));

        Assert.AreEqual(0, personList.GetMinAge());
    }

    [TestMethod]
    public void GetAverage()
    {
        var personList = new PersonList();
        personList.InsertNewPerson(new Person("A", 34));
        personList.InsertNewPerson(new Person("B", 100));
        personList.InsertNewPerson(new Person("C", 0));
        personList.InsertNewPerson(new Person("D", 34));
        personList.InsertNewPerson(new Person("E", 22));
        personList.InsertNewPerson(new Person("F", 57));

        Assert.AreEqual(41, (int)personList.GetMinAge());
    }
}

אם נריץ את הקוד בעזרת הפקודה `dotnet test` למשל, נקבל דו"ח עבור שלושת הטסטים והאם הם עברו.

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

בדיקות אינטגרציה (Integration Tests):

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

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

בדיקות מערכת (System Tests):

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

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

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

בדיקות קבלה (Acceptance Tests):

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

להרחבה:

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

מושגים

  • agile– עדיין אין פירוט למושג הזה.
  • unit-test– עדיין אין פירוט למושג הזה.
  • integration-test– עדיין אין פירוט למושג הזה.

לינוקס במקביל לווינדוס

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

אז Windows מתחילים להעיף מבט לכיוון הקוד הפתוח, כשבהתחלה הם הוכרזו כתורמים ראשיים ללינוקס, ולאחר מכן הוסיפו גם את Linux Shell לגרסאות החדשות של Windows 10 (נקרא WSL- Windows Subsystem for Linux).

אז קודם כל- אפשר להריץ פקודות לינוקס, כולל bash, משורת הפקודה כחלק מWindows.
כדי לעשות זאת נצטרך לאפשר את מערכת WLS:

עבור מי שלא מקפיד שהווינדוס שלו מעודכן, לעדכן את הWindows או לעבוד על פי הקישור.

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

  1. לפתוח את התקנת התכונות הנוספות של Windows ע"י לחיצה על Win + R וכתיבת "optionalfeatures", פקודה שתפתח את חלונית הוספת התכונות לWindows.
  2. סימון של "מערכת משנה של Windows עבור Linux" (באנגלית זה כנראה יהיה Windows Subsystem for Linux")
  3. המחשב יבקש להתקין ולבצע הפעלה מחדש. בסדר.
  4. לאחר ההפעלה מחדש יש להיכנס לחנות האפליקציות של Windows (היא קיימת למרות שלא השתמשתם בה) ולהתקין את אפליקציית Ubuntu.
  5. לאחר שאפליקציית Ubuntu הותקנה, אפשר להיכנס לcmd ולכתוב ubuntu, וקיבלנו טרמינל של אובונטו!
  6. בפעם הראשונה נצטרך להגדיר שם משתמש וסיסמה. אם שוכחים יש דרך לבטל, לא לדאוג.
  7. אם כתוב לכם שיש שגיאה, חפשו אותה בקישורים הקודמים, ניסיתי לעשות לפי הסדר כדי להימנע ממנה.

זהו, אנחנו בWindows ויש לנו את השל shell אובונטו!

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

הקבצים של Windows יהיו מעוגנים תחת תיקיית mnt בשורש, תיקייה לכל מחיצה בWindows. לדוגמא כונן C:

/mnt/c/

לא מומלץ לגשת לקבצים של מערכת לינוקס דרך Windows, אבל אם רוצים לבצע זאת הם נמצאים בתיקיית:

C:\Users\{UserName}\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_{id}\LocalState\rootfs

שימוש בתוכנות גרפיות של Ubuntu

שלב מתקדם יותר הוא להציג תוכנות גרפיות של Ubuntu. האחראי על הצגת חלונות בלינוקס נקרא Xserver, ונצטרך להתקין גרסה שלו בWindows על מנת לאפשר הצגת חלונות Ubuntu.
אני התקנתי את VcXsrv. אחרי ההתקנה יהיו לנו 2 קבצי הרצה (בWindows)- XLaunch & VcXsrv. כאשר XLaunch נותן לנו להגדיר את השרת ואז להפעיל אותו, ואילו VcXsrv מפעיל את השרת ישירות (ניתן לראות את הסמל שלו בשורת הסמלים הקטנה בלמטה של Windows).
בהפעלת XLaunch חשוב לסמן את Disable Access Control, שהסימון שלו יבטל את הגבלת הגישה ויאפשר לכל פקודה להפעיל את השרת.

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

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

sudo apt-get install xfce4

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

נכנס לעריכת הקובץ .bashrc עם הפקודה nano .bashrc, נרד עד סוף הקובץ ונוסיף את השורה export DISPLAY=:0.0, שמגדירה משתנה מערכת DISPLAY עם הערך 0.0, זהו בעצם "הפורט" של שרת הX שבו אנחנו רוצים להציג את החלונות- התצוגה הנוכחית.

שלב אחרון- כדי להפעיל את שולחן העבודה נצטרך כמובן לוודא שXServer מופעל כרגע במחשב, ונכתוב את הפקודה xfce4-session. בעצם קראנו לתוכנית של xfce4, ונראה קצת פלט, ואז נראה את התפריט של XUbuntu (למעלה) ואת משגר האפליקציות (למטה).
דרך התפריט למעלה נוכל ללחוץ על Applications ולראות אפליקציות מותקנות, ולהפעיל אותם!
בשביל להתקין אפליקציות נוספות…. זה כבר שימוש בלינוקס- חיפוש בגוגל, למידה, הרגל וכו'.

RESTful API with ASP.NET Web API 2

סיכום על כתיבת Controller שמגיב לבקשות HTTP.

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

ניצור פרויקט ASP.NET Web Apllication ריק, על מנת שנבין כל שלב בעצמנו.

הבדלים בין Web API 2 לבין MVC Controller

נוסיף לפרויקט תיקייה בשם Controllers (לא חובה דווקא בשם הזה). נוסיף לתיקייה Controller, וייפתח לנו החלון הבא שידרוש מאיתנו לבחור Controller:

Select Controller

אנחנו רואים 2 סוגים של Controllers. אני לא יודע להרחיב לעומק, אבל MVC Controller מתאים יותר למודל MVC שכולל גם View, ומאיתנו לא נדרש View, כי אנחנו רוצים לשלוח נתונים בלבד. אפשר להשתמש ב2 הControllers ל2 המטרות (שליחת נתונים והצגת View), אבל לטובת לימוד של כתיבת RESTful נשתמש בWeb API 2 כדי להבין טוב יותר את הפעולות שאנחנו עושים.

אחרי שנוסיף את Web API Controller – Empty (נבחר ריק- שוב, כדי ללמוד) נכתוב את השם שלו בתיבה שקפצה. השם שלו יהיה מורכב משם הController- הנתונים שנרצה להביא, וסיומת Controller. בדוגמא שלנו נבחר בשם UsersController.

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

בקשות HTTP:

בקשות http אלו הבקשות הרגילות שאנחנו משתמשים בהם בגלישה באינטרנט, והם מבוססות על שורת הכתובת. ישנם 4 בקשות http עיקריות:

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

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

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

DELETE- דומה לבקשת GET. מורכבת מכתובת בלבד ומבקשת מהשרת למחוק אובייקט כלשהו.

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

הController:

כמו שראינו, שם הController הוא שם האובייקט שאיתו נתעסק, עם סיומת controller (אצלנו: UsersController), והוא יורש מapiController, בשונה מMVC Controller שיורש מController. מלבד זאת, אין לנו כרגע פונקציות בקובץ.

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

הגישה לController נעשית באמצעות הכתובות:

/api/{Controller} - /api/Users
 /api/{Controller}/{id} - /api/Users/5

תגובה לבקשות HTTP:

ונוכל בהתאמה ליצור פונקציות שיגיבו לארבעת בקשות הHTTP. כדי שפונקצייה תגיב לבקשה מסוימת, השם שלה צריך להתחיל בשם הבקשה, לדוגמא: GetAll, PostOne, DeleteByID, Put. הController יודע להתאים את סוג הבקשה לפונקצייה המתאימה.

שליחת נתונים באמצעות תבנית:

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

public string GetAll(){} //path: /api/Users
public string GetByID(int id){} //path: /api/Users/3, and id=3

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

שליחת נתונים בעזרת מחרוזת שאילתא:

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

http://www.google.com?search=HelloWorld&type=photos

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

public GetByName(string name){} // path: /api/Users?name=Baruch
public Get(int id, string name){} // path: /api/Users/5?name=Baruch

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

אני לא מרחיב יותר מידי, אפשר על פי הדברים האלו לעשות ניסויים- מה קורה אם משנים את סדר המשתנים? מה קורה אם שולחים את id כמחרוזת שאילתא? מה קורה אם בכתובת כותבים את id כstring?

ניתוב (Routing)

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

שלבי הניתוב שאנחנו מכירים:

  1. הASP.NET לוקח מהכתובת של הבקשה את חלק ה{Controller}, מצמיד אותו למילה Controller ומחפש Controller בשם הזה.
  2. בתוך הController מחפש פונקצייה שמתחילה בשם הבקשה.
  3. לאחר שנמצאו הפונקציות המתאימות לבקשה, מנסה להתאים את רשימת המשתנים לפונקצייה.

נראה שאת חלק מהשלבים אפשר לשנות.

הניתוב  מתבצע ע"י טבלת ניתוב שמוגדרת בקובץ WebApiConfig.cs שנמצא בתיקיית App_Start, ונוצר אוטומטית בזמן שהוספנו את הController הראשון.

config.Routes.MapHttpRoute(
 name: "DefaultApi",
 routeTemplate: "api/{controller}/{id}",
 defaults: new { id = RouteParameter.Optional }
 );

זה הקוד. נעבור על שלושת הערכים שהוא מקבל:

  1. name: שם הניתוב, לא יודע עד כמה הוא משמעותי.
  2. routeTemplate: מגדיר את מבנה הכתובת. {Controller} הוא שומר מקום לשם הController, ובהמשך נראה שומרי מקום נוספים. שאר שומרי המקום הם לבחירתנו, והם השמות של המשתנים. גם עם זה ניתן לשחק ולראות מה קורה.
  3. default: כאן ניתן להגדיר האם יש פרמטרים שניתן להתעלם מהם. אם לא היינו מגדירים את id כאופציונלי, הכתובת ללא id לא הייתה חוקית, ולא הייתה מתקבלת.

שינויים אפשריים בכתיבת הפונקציות:

  • ניתן לכתוב פונקצייה ללא התחלה של שם הבקשה, ולשייך אותה בעזרת אפיון לפי הדוגמא:
    [HttpGet]
    public string GiveMe(int id){}
  • ניתן להוסיף פרמטר נוסף לכתובת הניתוב- {action}, וניתן להשתמש בו ב2 דרכים:
    • הaction בשורת הכתובת יפנה אותנו לשימוש בפונקצייה בעלת אותו שם:
      [HttpGet]
      public string Members(int id) //path: /api/{Controler}/{action}/{id}
    • ניתן להכריז על פונקצייה כמגיבה לפעולה זאת:
      [HttpGet]
      [ActionName("Members")]
      public string GiveMe()

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

טיפול במחשב עם כונן פגוע

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

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

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

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

איך החלפתי כונן HDD פגוע בכונן SSD?

כלים:

  1. מברג.
  2. כונן SSD.
  3. מתאם SATA to USB.

תהליך:

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

  1. פתיחת המחשב והוצאת הכונן הקשיח. זה לא אמור להיות מסובך מידי (אם המחשב לא מידי ישן. במחשבים ישנים הכונן עלול להיות מתחת להרבה דברים אחרים). כדאי לבדוק ביוטיוב אילו ברגים צריך לשחרר, ואיזה לוחות להוריד כדי להגיע לכונן. חשוב מאוד להוציא את הסוללה ולנתק את המחשב מהחשמל!
  2. חיבור הכונן הישן (HDD) למחשב בעזרת המתאם. אני ממליץ על מתאם עם חיבור לחשמל. כשהשתמשתי במתאם שלוקח את החשמל מחיבור הUSB, במקרה של ניתוק בהפתעה, הכונן עלול להיהרס. כדאי לחבר קודם את החשמל, כדי שהדיסק יתחיל להסתובב, ולאחר מכן לחבר את הנתונים והUSB.
  3. גיבוי הכונן בעזרת תוכנת EaseUS Todo Backup. בקצרה- בעזרת Disk / partition Backup, ניתן לגבות את כל הכונן, או רק חלק מהמחיצות שלו. אני חושב שכדאי לגבות הכל, ואם יש מחיצות לא רלוונטיות, להוריד אותם בדרך הרגילה אחרי שהמחשב כבר פועל. כדאי לשים לב לאפשרות גיבוי Sector By Sector, שמגבה את הכונן "אחד לאחד", גם את המקומות הריקים, מה שאומר שהגיבוי יהיה בגודל של הכונן.. אם לא נבחר באפשרות הזאת, הגיבוי יהיה רק על הקבצים.
  4. ניתוק כונן HDD וחיבור כונן SSD למתאם.
  5. שחזור הכונן בעזרת התוכנה הנ"ל. כשנפתח את התוכנה היא תציג את הגיבוי שנעשה בסעיף 3, ותתן לנו אפשרות Recover, שבה נבחר את כונן הSSD.
  6. לאחר שהעברנו בעצם את התוכן מהHDD לSSD, כל מה שנשאר לעשות הוא לחבר למחשב הפגוע את כונן הSSD, בדיוק במקום ובצורה שבה היה הHDD.

וזהו.

שלבים ליצירת RecycleView

    1. יצירת קובץ Resource שמייצג שורה ברשימה, בשם item_photo.
    2. יצירת מחלקה PhotoViewHolder שיורשת מRecycleView.ViewHolder. מחלקה זו משמשת לבניית שורה ברשימה ולהתנהגות שלה.
      1. בנאי- מקבל View (שהוא מופע של הResource שיצרנו בסעיף 1) ומאתחל את הViews שמוגדרים במחלקה (ImageView).
      2. פונקצייה שמקבלת אובייקט מהרשימה שאנחנו מעוניינים להציג (תמונה) ועל פיו מאתחלת את התצוגה של הViews (נותנת את התמונה כפרמטר לImageView).
    3. יצירת מחלקת Adapter בשם PhotosAdapter שיורשת מ
      <RecycleView.Adapter<PhotoViewHolder
      מחלקה זאת מנהלת את הרשימה של האובייקטים שאנחנו רוצים להציג, ומנהלת את הבנייה של השורות.

      1. בנאי- מקבל את הרשימה (List) של האובייקטים שאנחנו רוצים להציג ברשימה (RecycleView).
      2. פונקצייה- onCreateViewHolder שמקבלת ViewGroup, ממנו, בעזרת Inflater שיקבל את הקובץ שיצרנו בסעיף 1, נוכל ליצור View של שורה, שאותו נשים בבנאי בסעיף 2.1, והנה יש לנו אובייקט PhotoViewHolder שאנחנו יכולים להחזיר (הוא הreturn של הפונקצייה).
      3. פונקצייה- onBindViewHolder שתקבל את הViewHolder מהפוקנצייה הקודמת, וגם מיקום של אובייקט, כך שנוכל לפנות לאובייקט במיקום הזה ברשימה, ולהכניס אותו לאובייקט בעזרת הפונקצייה של סעיף 2.2.
      4. פונקצייה- getItemCount שמחזירה את כמות האובייקטים (גודל הרשימה). בנוסף, אם נדרש נוסיף פונקציות לטיפול ברשימה, ובסופם נצטרך להשתמש בNotify.
    4. הכרזה על הRecycleView שלנו (או יצירת הפנייה אליו במידה והוא כבר קיים בResource של הActivity).
      1. הגדרת setHasFixedSize רק אם אנחנו לא מתכננים שהגודל שלו ישתנה.
      2. יצירת אובייקט LayoutManager בהתאם לצורה שבה אנחנו רוצים שהנתונים יוצגו (LinearLayoutManager- Horisontal\Vartical, GridLayoutManager etc') והוספה שלו ע"י myRecycleView.setLayoutManager
      3. יצירת אובייקט של PhotoAdapter ולתת לו את הנתונים. הוספת האובייקט בעזרת myRecycleView.setAdapter

 

 

אם אני אדע איך שמים פה קוד, אני אשים דוגמאות קוד.