המתכנת האידיאלי

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

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

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

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

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

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

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

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

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

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

חוב טכני

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

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

חוכמת ההמונים

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

שאלון - המתכנת האידיאלי

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

לסיכום

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

מס' מקורות רלוונטים נוספים שנתקלתי בהם במהלך הדרך:

אשמח לשמוע את דעתכם.

כתיבת אפליקצית AngularJS 1.3 ב- ES6

כבר כמה חודשים שאני נלחם בחשק מוזר להתחיל לכתוב קוד ב- ECMAScript 6. היות ובשנתיים האחרונות אני עובד בעיקר עם AngularJS ולאור ההכרזות על AngularJS 2.0, זה בדיוק הזמן להתחיל ואפילו לקחת את האתגר צעד אחד קדימה – לכתוב אפליקצית AngularJS ב- ES6.
אז קדימה, מתחילים.

על מה נדבר?

אפשר לדבר על המון דברים בנוגע ל- AngularJS, באופן אישי אני מעדיף מאמרים פרקטיים ועניניים, אז מאמר בנוסח "למה אנגולר 2.0 זה אסון" לא תמצאו כאן, זה גם לא "מבוא ל- ES6". בחרתי לדבר על מיגרציה מ- AngularJS 1.x ל- AngularJS 2.0, או בעיקר על הכנה מקדימה למיגרציה שתבוא לידי ביטוי בשכתוב האפליקציה שלנו ב- ES6. כן, לכתוב ב- ES6 כבר היום, עם AngularJS 1.3 ובדפדפנים שאפילו לא לגמרי תומכים בתקן. אני רוצה לבדוק האם זה אפשרי, ואם כן – האם זה גם כדאי.

למה עכשיו?

מי שמפתח ב- AngularJS וסביר להניח שגם מי שלא, בוודאי שמע את ההכרזות האחרונות על AngularJS 2.0. מעבר לעובדה שרוב הדברים שאנחנו מכירים כיום כבר לא קיימים בגרסה החדשה, מסתבר שהפיתוח של הספריה כבר לא יהיה ב- JavaScript כפי שאנו מכירים אותה כיום – אלא ב- AtScript, שזו מעין גרסה משופרת של השפה, המתוארת כ-

AtScript = ES6 + Types + Annotations + Introspections

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

מה המוטיבציה?

המטרה העיקרית היא לאפשר מעבר חלק ופשוט יותר בעתיד הן ל- AngularJS 2.0 והן ל- AtScript (במידה ונרצה) או לפחות ל- ES6. מתישהו יהיה כדאי לעבור ל- ES6 בכל מקרה, אז בואו נבדוק האם זה אפשרי כבר היום.

היכון, הכן, צא!

אתחיל עם העובדה שהדפדפנים היום לא לגמרי תומכים ב- ES6 בצורה רחבה, כך שאנחנו לא יכולים פשוט לכתוב ב- ES6 ולהריץ כמו שאנחנו רגילים, אנחנו זקוקים לאיזשהו כלי שיגשר על הפער הזה. ישנם מס' כלים המאפשרים לנו לכתוב ב- ES6 והם למעשה דואגים להמיר את הקוד שלנו (כל אחד בצורה שלו) לקוד שיכול לרוץ כבר היום, חלקם עושים את זה ב- runtime ואת חלקם צריך לשלב בתהליך ה- build, לא אכנס לזה יותר מדי כרגע – רק אגיד שאני בחרתי ב- Traceur ובמקרה הזה אני מריץ אותו ב- runtime.

כ- PoC בחרתי באפליקציה די פשוטה המכילה View, Controller ו- Service בסיסיים.
אני יוצא מנקודת הנחה שאתם כבר מכירים את התחביר של ES6, אם לא – למה אתם מחכים? בכל אופן, במקרה הזה שילוב של היכרות עם שפות נוספות והיגיון בריא אמור להספיק גם למי שלא מכיר את השפה.

נתחיל עם ה- Service (קובץ: alerts.service.js):

export class Alerts {
  
  constructor($window) {
    this.$window = $window;
  }
  
  fire(alert) {
    this.$window.alert(alert);
  }
}

ה- Controller (קובץ: main.controller.js):

export class MainController {
  
  constructor(Alerts) {
    this.Alerts = Alerts;
  }
  
  get headerTitle() {
    return 'Hello ECMAScript 6!';
  }
  
  buttonClick() {
    this.Alerts.fire('Awesome!');
  }
}

איתחול האפליקציה (קובץ: app.js):

import {MainController} from 'main.controller';
import {Alerts} from 'alerts.service';

angular.module('app', [])
  .controller('MainController', MainController)
  .service('Alerts', Alerts);

ולבסוף ה- View (קובץ: index.html):

<!DOCTYPE html>
<html ng-cloak>

  <head>
    <title>AngularJS 1.3 application written in ECMAScript 6</title>
  </head>
  
  <body ng-controller="MainController as Main">
  
    <h1 ng-bind="Main.headerTitle"></h1>
    <button type="button" ng-click="Main.buttonClick()">Click me!</button>
    
    <!-- traceur -->
    <script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
    <script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
    <script>traceur.options.experimental = true;</script>
    
    <!-- angularjs -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
    
    <!-- bootstrap our application -->
    <script>
      System.import('app').then(function(){
        angular.element(document).ready(function() {
          angular.bootstrap(document, ['app']);
        });
      });
    </script>
    
  </body>
  
</html>

זה עובד, אבל אתם לא חייבים להאמין לי – תראו בעצמכם ב- Plunker הבא.

מסקנות

כפי שניתן לראות, עשינו את זה די בקלות. חדי העין מביניכם אפילו ישימו לב שמלבד שמות הארגומנטים שאנחנו מעבירים ל- constructor, וגם את זה ניתן לשנות בקלות אם רק רוצים – ברמת ההגדרה למחלקות שלנו אין שום מושג שהן רצות בתוך AngularJS, השיוך היחיד שלהן לספריה נעשה ב- app.js ששם אנחנו מעבירים אותן ל- AngularJS בתור Controllers/Services בהתאם לצורך. אפשר לעשות את זה גם ב- ES5 – אך לדעתי ב- ES6 זה הרבה יותר אינטואיטיבי, פשוט ומתבקש.

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

לסיכום

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

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

JavaScript – The "Mixin" Design Pattern

כולנו יודעים שב- JavaScript הירושה מתבצעת באמצעות ה- Prototype של הפונקציות, אבל כמה מכם שמעו על Mixins או השתמשו בהם?

Mixins

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

Mixins משמשים בעיקר ל- Code Reuse – אם יש לנו מקומות שונים במערכת שמטעימים פונקציונליות זהה, אנחנו יכולים להוציא אותה ל- Mixin ולכלול אותו בשני המקומות הללו. כותבים את הקוד פעם אחת, משתמשים בו בכל מקום.

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

נתחיל עם דוגמאת קוד בסיסית:

/**
 * Classes
 */
var Person = function(name) {
	this.name = name;
};

var Dog = function(name) {
	this.name = name
};

 
יצרנו 2 מחלקות, הראשונה בשם Person והשנייה בשם Dog. שתיהן מקבלות ב- constructor שלהן את הפרמטר 'name' ושומרות אותו עבור אותו instance.

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

עכשיו ניצור Mixins עבור הלוגיקה המשותפת הזאת:

/**
 * Classes
 */
var Person = function(name) {
	this.name = name;
};

var Dog = function(name) {
	this.name = name
};

/**
 * Mixins
 */
var mover = {
	walk: function() {
		console.log(this.name + " is walking.");
	},
	run: function() {
		console.log(this.name + " is running.");
	}
};

var speaker = {
	speak: function() {
		console.log(this.name + " is speaking.");
	}
};

 
עד כאן זה די פשוט, יש לנו 2 מחלקות ו- 2 Mixins.
כל Mixin מייצג איזשהי יכולת פונקציונלית כלשהי – הקוד די ישיר וקל להבנה.

קדימה להטמיע

כפי שאמרנו, אנחנו רוצים להטמיע את היכולות האלו, את ה- Mixins האלו, במחלקות שיצרנו עבור Person ו- Dog. איך אנחנו עושים את זה? בקלות – פשוט מעתיקים את המתודות של ה- Mixins לתוך ה- Prototype של המחלקות שלנו.

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

עכשיו זה נראה ככה:

/**
 * Classes
 */
var Person = function(name) {
	this.name = name;
};

var Dog = function(name) {
	this.name = name
};

/**
 * Mixins
 */
var mover = {
	walk: function() {
		console.log(this.name + " is walking.");
	},
	run: function() {
		console.log(this.name + " is running.");
	}
};

var speaker = {
	speak: function() {
		console.log(this.name + " is speaking.");
	}
};

/**
 * Merge the mixins into the classes
 */
$.extend(Person.prototype, mover, speaker);
$.extend(Dog.prototype, mover, speaker);

 
זה הכל, באמת.

הערות:

1. קוד מלא: http://codepen.io/AdirAmsalem/pen/Fewzk?editors=001
2. מימוש של extend:

/**
 * Merge the contents of two or more objects together into the first object.
 * 
 * @param  {Object} target
 * @param  {Object} source1
 * @param  {Object} sourceN
 *
 * @example
 * 	extend(Person.prototype, speaker); // single source
 * 	extend(Person.prototype, mover, speaker); // multiple sources
 * 
 * @return {Object} the merged object
 */
var extend = function(target) {
	if (!arguments[1]) return target;

	for (var i = 1, l = arguments.length; i < l; i++) {
		var source = arguments[i];

		for (var prop in source) {
			if (!source.hasOwnProperty(prop)) continue;

			target[prop] = source[prop];
		}
	}

	return target;
};

 

מאקרו-סמנטיקה, מיקרו-סמנטיקה ומה שביניהן

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

מאקרו-סמנטיקה

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

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

<section>
<article>
<header>
<footer>
<aside>
<time>
<nav>
<address>

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

נגיד.. למה זה טוב?

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

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

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

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

תרחיש לדוגמה

בואו נגיד שבאמצעות התגיות שניתנו לנו ברמת המאקרו-סמנטיקה יצרנו את המבנה הבא:
Basic Semantic Markup
* אין צורך להתפעל מהיכולות שלי בצייר – כל אחד יכול להגיע לתוצאה כזו לאחר כמה שנות ניסיון.

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

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

מיקרו-סמנטיקה

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

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

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

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

קדימה לאוכל, אה – לעבודה

קודם כל בואו נכיר את הסכמה המייצגת מתכון – http://schema.org/Recipe.
מתכון הוא קודם כל Thing, לאחר מכן הוא CreativeWork ולבסוף הוא כמובן Recipe.
כל סכמה בהיררכיה מרחיבה את הסכמה ממנה היא יורשת ומוסיפה תכונות נוספות המייצגות ספציפית אותה. מאפיינים מסויימים בסכמה יכולים להצביע לסכמות נוספות – לדוג' יוצר המתכון הוא למעשה Person או Organization, כל ביקורת היא Review וכן הלאה. כך ניתן להגדיר קשרים בין פרטי המידע השונים וליצור מבנה שבו ברור לחלוטין מה כל דבר מייצג ולאן הוא שייך.

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

נתון הטקסט הבא:

Moms World Famous Banana Bread
By John Smith, May 8, 2009
<img src="bananabread.jpg" />

This classic banana bread recipe comes from my mom -- the walnuts add a nice
 texture and flavor to the banana bread.

Prep Time: 15 minutes
Cook time: 1 hour
Yield: 1 loaf

Nutrition facts:
240 calories, 9 grams fat

Ingredients:
- 3 or 4 ripe bananas, smashed
- 1 egg
- 3/4 cup of sugar
...

Instructions:
 Preheat the oven to 350 degrees. Mix in the ingredients in a bowl. Add the
 flour last. Pour the mixture into a loaf pan and bake for one hour.

140 comments:
From Janel, May 5 -- thank you, great recipe!
...

על מנת לייצג אותו כ- Recipe, נעשה כך:

<div itemscope itemtype="http://schema.org/Recipe">
  <span itemprop="name">Mom's World Famous Banana Bread</span>
  By <span itemprop="author">John Smith</span>,
  <meta itemprop="datePublished" content="2009-05-08">May 8, 2009
  <img itemprop="image" src="bananabread.jpg" />

  <span itemprop="description">This classic banana bread recipe comes
  from my mom -- the walnuts add a nice texture and flavor to the banana
  bread.</span>

  Prep Time: <meta itemprop="prepTime" content="PT15M">15 minutes
  Cook time: <meta itemprop="cookTime" content="PT1H">1 hour
  Yield: <span itemprop="recipeYield">1 loaf</span>

  <div itemprop="nutrition"
    itemscope itemtype="http://schema.org/NutritionInformation">
    Nutrition facts:
    <span itemprop="calories">240 calories</span>,
    <span itemprop="fatContent">9 grams fat</span>
  </div>

  Ingredients:
  - <span itemprop="ingredients">3 or 4 ripe bananas, smashed</span>
  - <span itemprop="ingredients">1 egg</span>
  - <span itemprop="ingredients">3/4 cup of sugar</span>
  ...

  Instructions:
  <span itemprop="recipeInstructions">
  Preheat the oven to 350 degrees. Mix in the ingredients in a bowl. Add
  the flour last. Pour the mixture into a loaf pan and bake for one hour.
  </span>

  140 comments:
  <meta itemprop="interactionCount" content="UserComments:140" />
  From Janel, May 5 -- thank you, great recipe!
  ...
</div>

הסבר קצרצר:

  1. על מנת לייצג פריט אנחנו יוצרים קונטיינר ונותנים לו itemscope.
  2. בנוסף ל- itemscope אנחנו מגדירים itemtype, כאשר ב- itemtype אנו מציינים את עמוד הסכמה הרלוונטי (לדוג': http://schema.org/Recipe).
  3. על מנת לייצג מאפיין של אותו פריט אנו משתמשים ב- itemprop ומציינים את שם המאפיין (לדוג': name).
  4. במידה והמאפיין מצביע לסכמה אחרת – מוסיפים itemscope ו- itemtype כמו בסעיפים 1 ו- 2 וחוזרים על התהליך.
  5. אם רוצים לייצג נתון מסויים אך לא רוצים שהוא יופיע לגולש – ניתן להשתמש בתגית meta אשר מקבלת itemprop כמו בסעיף 3 ואת הנתון עצמו שמים במאפיין content.

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

מנקודת המבט של מנוע החיפוש

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

אספתי עבורכם כמה דוגמאת לתוצאות המכילות דפים כאלו:
The Godfather
Baked Macaroni and Cheese
Lady Gaga

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

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

לסיכום

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

PHP – על Opcode Caching ו- APC

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

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

מה זה Opcode Caching?

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

מה זה APC?

APC (ר"ת של Alternative PHP Cache) הוא אחד התוספים הנפוצים והמומלצים לביצוע Opcode Caching, ב- PHP 6 הוא גם יהיה מובנה בשפה עצמה ולא יהיה צורך להתקין אותו בנפרד.
APC משתמש ב- shared memory ו- mapping table לאחסון המידע.
חשוב לציין ש- APC יעבוד רק כאשר ה- PHP רץ כ- apache module (אמרנו כבר shared memory), אין אפשרות להשתמש ב- APC כאשר אנחנו עובדים בתצורת CGI.

איך מתקינים APC?

את APC ניתן להתקין בקלות באמצעות PECL, לצורך המאמר אסביר איך להתקין אותו במערכות מבוססות Unix.
יש להריץ את הפקודה הבאה כ- root (או באמצעות sudo):

pecl install apc

לאחר מכן יש להוסיף ל- php.ini (קובץ הקונפיגורציה של ה- PHP) את השורה הבאה:

extension=apc.so

כעת יש לבצע הפעלה מחדש לשרת ה- http וזה הכל!
על מנת לבדוק אם התוסף הותקן כמו שצריך, צרו קובץ חדש עם התוכן:

<?php
phpinfo();
?>

הריצו אותו וחפשו אחר "apc", מצאתם? מצויין.

הגדרות חשובות

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

  • apc.enabled – האם התוסף יפעל או לא.
    כברירת מחדל מוגדר כ- "1" (פעיל), הגדרה כ- "0" תבטל את התוסף.
  • apc.shm_segments – כמות יחידות ה- shared memory שיוקצו עבור התוסף.
    כברירת מחדל מוגדר כ- "1".
  • apc.shm_size – נפח הזכרון (ב- mb) שיוקצה עבור כל יחידת shared memory.
    כברירת מחדל מוגדר כ- "32".
  • apc.num_files_hint – הכמות המירבית של הקבצים שישמרו במטמון.
    כברירת מחדל מוגדר כ- "1000".
  • apc.ttl – זמן התפוגה (בשניות) של המידע השמור.
    כברירת מחדל מוגדר כ- "0" (לא פג לעולם).
  • apc.cache_by_default – האם לבצע opcode caching אוטומטית.
    כברירת מחדל מוגדר כ- "1" (כן), הגדרה כ- "0" תבטל את זה.
  • apc.max_file_size – גודל הקובץ המירבי (ב- mb) שניתן לשמור.
    כברירת מחדל מוגדר כ- "1".
  • apc.stat – בודק בכל בקשה האם המידע השמור במטמון עדכני.
    כברירת מחדל מוגדר כ- "1" (כן), הגדרה כ- "0" תבטל את זה (להשתמש בזהירות).

מידע אודות הגדרות נוספות ניתן למצוא בעמוד APC Configuration.

שיהיה לכם Opcode Caching מהנה ומועיל.

מה חדש ב- PHP 5.4

מי שהספיק להתעדכן ומי שלא – כשמונה חודשים לאחר ששוחררה גרסת האלפא הראשונה,
אתמול (01/03/12) שוחררה הגרסה הרשמית והמלאה של PHP 5.4.0.
בפוסט זה אספק סקירה קצרה של השינויים, החידושים, מה כבר לא קיים, ממה יש להיזהר ולמה לשים לב במיוחד בגרסה החדשה. הפוסט מיועד בעיקר למפתחי PHP ולבעלי שרתים שמריצים PHP.

מה חדש?

  • traits – עבור מי מכם שמממש OOP, בוודאי תשמחו לדעת שכעת גם PHP תומכת ב- traits.
    מי שלא בטוח מה זה בדיוק המאכל הזה שרשמתי מקודם, זה די דומה להורשה, מושג שעשוי להיות קצת יותר מוכר לכם.
  • קידוד – קידוד ברירת המחדל השתנה מ- "ISO-8859-1" ל- "UTF-8". בנוסף לכך התווספה תמיכה כברירת מחדל ב- Multibyte.
  • הגדרת מערכים – זוכרים את המילה Array שרשמתם לפני כל הגדרה של מערך? לא עוד.
    מהיום תוכלו להגדיר מערכים בדרך קצרה יותר.

    $a = [1, 2, 3, 4];
    $a = ['one' =&gt; 1, 'two' =&gt; 2, 'three' =&gt; 3, 'four' =&gt; 4];

    אהבתם? גם אני.

  • פונקציות אנונימיות – אולי אתם מכירים את זה כ- Closures, בכל אופן עכשיו ניתן להשתמש ב- this$ גם בהן, אם במקרה זה היה חסר לכם.
  • short_open_tag – זוכרים את ההגדרה הזאת ב- php.ini?
    עכשיו אתם יכולים להשתמש בתגי פתיחה וסגירה מקוצרים גם בלעדיה, זה יפעל תמיד.
  • מד התקדמות – כעת גם אתם יכולים ליצור מד התקדמות במערכת העלאת הקבצים שלכם בלי להסתבך יותר מדי. בטח תשמחו גם לשמוע שזה פעיל כברירת מחדל. מי אמר אני ולא קיבל?
  • שרת אינטרנט מובנה – זה לא חלום, אפילו אחזור על זה למי שפספס – שרת אינטרנט מובנה!
    לא מדובר פה על משהו שמתיימר להחליף את Apache או Nginx בזמן הקרוב, האופציה הזאת קיימת להקלה ולנוחות בשלב הפיתוח בלבד. אבל בואו נודה באמת – בהחלט שימושי ומבורך.
  • מחלקות, פונקציות ושאר ירקות – יש גם לא מעט קבועים חדשים, מחלקות חדשות, שיטות חדשות ופונקציות חדשות, במקום לסקור את הכל פשוט אקשר אתכם לרשימות המלאות.
    קבועים חדשיםמחלקות חדשותפונקציות חדשותשיטות חדשות

מה ירד?

  • register_globals – הגדרה שנוייה במחלוקת שעלולה הייתה ליצור בעיות אבטחה רבות. כבר בעבר היא כובתה כברירת מחדל, כעת היא הוסרה לחלוטין.
  • magic_quotes – הגדרה נוספת שלא הייתה אהודה כל כך בקרב המפתחים, מטרתה הייתה לבצע הברחה אוטומטית (באמצעות התו "") על כל קלט שהתקבל. פעולה זו יצרה בעיות רבות שהעיקרית שבהן הייתה הברחה כפולה. בגרסה החדשה ההגדרה הזאת לא קיימת יותר.
  • safe_mode – הגדרה נוספת שאינה קיימת יותר, החברים ב- PHP החליטו שזה לא מתפקידה של השפה לפתור את בעיות האבטחה הללו. אתייחס לזה שוב בהמשך.
  • מצביעים – עד עכשיו ניתן היה להעביר לפונקציה "התייחסות" (מצביע) למשתנה כלשהו הן בעת הגדרתה והן בזמן הקריאה, כעת ניתן להגדיר פרמטר כמצביע אך ורק בעת הגדרת הפונקציה.
  • סיישנים – הפונקציות session_register, session_unregister ו- session_is_registered אינן קיימות יותר. ניתן להשתמש בהצבה באמצעות האופטור "=" ובפונקציות unset ו- isset למטרה זו.
  • mysqli – מגוון שמות נוספים (Alias) ששימשו לקיצור השמות הארוכים של חלק מהפונקציות במחלקה הזו הוסרו, להלן הרשימה המלאה לנוחיותכם (חלקן עדיין קיימות).

מה נשתנה הלילה הזה?

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

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

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

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