3 טריקים ב-CSS שאני מאוד אוהב

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

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

Negative Margin

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

.myelement {margin-top: -20px;}

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

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

JS Bin on jsbin.com

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

Border Box

נניח שאנחנו רוצים שאובייקט מסויים יהיה ברוחב כולל של 200px, ובנוסף שיהיו לו padding ו-border. לפי מודל הקופסה של CSS, בו רוחב האובייקט לא כולל את ה-padding וה-border, אנחנו צריכים לחסר אותם מהרוחב הסופי כדי להגיע אליו במדוייק:

div {
  width: 156px; /* 200 - 40 - 4 */
  padding: 20px;
  border: 2px solid #000;
}

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

div {
  width: calc(50% - 40px - 4px);
  padding: 20px;
  border: 2px solid #000;
}

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

box-sizing: border-box;

שורה זו תגרום למאפיינים width ו-height להתנהג מעט שונה, ובמקום לקבוע את הגודל של תוכן האובייקט בלבד (ללא ה-padding וה-border), הם יקבעו את התוכן כולל פרמטרים אלו. שימו לב לדוגמה הבאה: JS Bin on jsbin.com

ניתן לראות שלמרות שלאובייקט הראשון קבענו padding ו-border, הרוחב הכולל שלו הוא עדיין 200px, בדומה לאובייקט השני שלא מוגדרים עבורו מאפיינים אלו. נסו לשחק עם הדוגמה, למחוק את המאפיין box-sizing ולראות מה קורה.

Relative Sizing

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

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

em היא יחידה שגודלה הוא ה-font-size של האובייקט הנוכחי. כלומר במקרה הבא:

span {
  font-size: 16px;
  margin-bottom: 1em;
}

לאובייקט שיווצר יהיו שוליים תחתונים ברוחב 16 פיקסלים. ומה יקרה אם נשנה את גודל הפונט ל-18px? ניחשתם נכון – גם רוחב השוליים ישתנה בהתאמה.

כמובן שאפשר שגם ה-font-size עצמו יהיה יחסי:

body {
  font-size: 16px;
}

span {
  font-size: 0.75em;
  margin-bottom: 1em;
}

במקרה זה רוחב השוליים התחתונים של span יהיו 16 כפול 0.75 כפול 1, שהם 12 פיקסלים.

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

body {font-size: 16px;}

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

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

@media screen and (min-width: 1024px) {
  body {font-size: 14px;}
}

@media screen and (min-width: 800px) {
  body {font-size: 12px;}
}

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

אגב, זה לא אומר שמעכשיו חובה להגדיר ידנית את גודל הפונט של כל התגיות בעמוד. זאת מכיוון שבכל הדפדפנים, כל גדלי ברירת המחדל מוגדרים גם הם ב-em, למשל גודל הפונט של h1 הוא לרוב 2em, ושוליים עליונים ותחתונים של p הם 1em.

תהליך בנייה לקוד צד לקוח – למה צריך את זה ואיך מייצרים אחד?

במשך שנים התרגלנו לחלק את שפות התכנות לשתי קבוצות:

  • שפות מתקמפלות, כגון c ונגזרותיה, Java, ועוד. שפות אלו מצריכות תהליך הידור (קומפילציה), הממיר אותן לקוד בשפה נמוכה יותר כגון שפת מכונה או שפת ביניים, אותה ניתן להריץ ישירות על מכשירי היעד.
  • שפות סקריפט, כגון Python, PHP, Javascript וכד'. שפות אלו לא צריכות קומפילציה, והקוד שלהן נשלח כמו שהוא לשטח, שם הוא מורץ ע"י interpreter.

לאחרונה הגבול בין שתי הקבוצות החל להיטשטש. ניקח לדוגמה את פייסבוק, שם כתבו מערכת להמרת קוד ה-PHP שלהם לקוד C++ לטובת שיפור ביצועים. גם אם הקוד עדיין מפותח בשפת סקריפט, בפועל הוא מומר ומורץ בשטח כקוד בשפה אחרת. התהליך האוטומטי המבצע פעולות על הקוד כדי להכינו לפרודקשן נקרא תהליך בנייה (בִּילְד).

במאמר זה נתמקד בתהליך בנייה עבור קוד צד לקוח (Front End) הכתוב בשפות Javascript, CSS ו-HTML, נבין איך תהליך כזה עוזר לנו, ונכיר טכנולוגיות העוזרות לנו לממש אותו.

למה צריך תהליך בנייה לקוד צד לקוח?

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

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

איחוד קבצי קוד

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

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

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

טרנספילציה

כאשר משתמשים בשפות הנגזרות מ-Javascript כגון CoffeeScript, JSX או Dart, רצוי להמיר את הקוד ל-JS לפני ההעלאה לאתר ולא בזמן הריצה (אחרת יש צורך בטעינת ספריות js נוספות והרצתן). תהליך ההמרה, הנקרא טרנספילציה או טרנסקומפילציה, מבוצע בתהליך הבנייה, וניתן לבצע אותו גם עבור שפות המומרות ל-CSS כגון less או Sass.

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

מזעור נפח הקוד

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

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

לדוגמה, נניח שהקוד שלנו נראה כך:

// A simple function.
function hello(longName) {
  alert('Hello, ' + longName);
}
hello('New User');

לאחר הרצה של Closure Compiler כחלק מתהליך הבנייה, הקוד ימוזער לקוד הבא, הקצר ממנו בחצי:

function hello(a){alert("Hello, "+a)}hello("New User");

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

ניתוח סטאטי של הקוד

ישנן טעויות רבות בקוד שניתן לגלות בנקל ע"י מעבר אוטומטי על הקוד:

  • שימוש במשתנים לא מוגדרים
  • קוד שאינו בשימוש
  • קוד מת שלעולם לא יורץ
  • שימוש מסוכן באופרטורים
  • שמות משתנים ופונקציות מבלבלים ושאינם מתאימים למוסכמות

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

שינוי שם קובץ הקוד הסופי

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

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

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

כמובן שתהליך קביעת שם הקובץ מתבצע אוטומטית בתהליך הבנייה, כמו גם עדכון קבצי ה-HTML המתאימים כך שידעו איזה קובץ לטעון. הכלי שאני משתמש בו הוא gulp-rev, הקובע את שם הקובץ לפי חישוב hash על הקוד.

הרצת בדיקות אוטומטיות

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

איך מייצרים תהליך בנייה?

במידה ותהליך הבנייה שלכם פשוט ומכיל שלבים בודדים בלבד, תוכלו לממש אותו בעצמכם בשפת הסקריפט המועדפת עליכם, או אפילו ע"י shell script. אך עבור תהליכים מורכבים יותר, קיימים כלים יעודיים ומעולים שנכתבו למטרה זאת, רובם מבוססי Node.js. (שימו לב – זה לא אומר שאתם צריכים שרת Node.js כדי להריץ את האתר שלכם, אלא רק להתקין Node.js על מחשב הפיתוח). שני הכלים הנפוצים ביותר נכון לכתיבת שורות אלו הם:

Grunt

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

Grunt מבוסס על קונפיגורציה, כלומר לאחר התקנת הפלאגינים הדרושים, תצטרכו להגדיר מה אתם רוצים לעשות באמצעות JSON, בקובץ JS הנקרא Gruntfile. דוגמה לקובץ Gruntfile המריץ את Uglify Js:

grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  uglify: {
    options: {
      banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
    },
    build: {
      src: 'src/<%= pkg.name %>.js',
      dest: 'build/<%= pkg.name %>.min.js'
    }
  }
});

Gulp

בדומה ל-Grunt, גם גאלפ מתבסס על אוסף רחב של פלאגינים. אך בשונה ממנו, גאלפ מצריך כתיבת תהליך הבילד בקוד ומתבסס על Node.js streams. בפועל מדובר בקוד פשוט מאוד, שאת רובו ככולו ניתן למצוא מוכן בתיעוד הפלאגינים. לדוגמה:

gulp.task('css_lint', function() {
 return gulp.src(['src/main.less'])
   .pipe(csslint('./.csslintrc'))
   .pipe(csslint.reporter())   
   .pipe(concat(module_name + '.css'))   
   .pipe(autoprefixer());
});

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

דוגמה לתהליך בנייה מלא

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

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

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

 

6 טכניקות שיזניקו את קוד ה-web שלכם אל העתיד

אחת הבעיות המעיקות ביותר בפיתוח web, בין אם מדובר באתר פשוט או ב-web app מורכב, היא חוסר התמיכה של דפדפנים שונים בסטנדרטים חדשים של HTML, CSS ו-Javascript. בשנים האחרונות שפות אלו מתפתחות בקצב מסחרר, וליצרני הדפדפנים השונים קשה לעמוד בקצב. אנחנו כמפתחי web לרוב נרצה שהאתר שלנו יעבוד כמו שצריך על כל הדפדפנים הפופולאריים, אך לעיתים אפילו הגרסאות החדשות ביותר שלהם (שלא נדבר על גרסאות קודמות…) חסרות תכונות בסיסיות, מה שהופך תכונות אלו בפועל ללא שמישות.

לדוגמה, תחביר חדש ושימושי של javascript הוא arrow functions:

var func = (x, y) => x + y - 1;
func(3, 4); // 6

נכון לכתיבת שורות אלו השימוש ב-arrow functions לא אפשרי ב-Internet Explorer (באף גרסה), ואף יגרום ל-syntax error. הדבר מונע לחלוטין שימוש ביכולת זו באתר שאמור לתמוך ב-IE (אלא אם בוחרים להתעלם מבערך 10% מהגולשים שלנו).

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

#1 – הכרת השטח

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

Can I use

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

למשל בתמונה למעלה ניתן לראות ש-CSS3 3D Transforms נתמכים בכל הדפדפנים למעט IE בגרסה 9 ומטה ו-Opera Mini. כעט ניתן להחליט האם להשתמש בפיצ'ר זה ולפגוע בנראות האתר בדפדפנים הישנים או לא.

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

ECMAScript Compatibility Table

בשונה משפות תכנות קונבנציונליות שיוצאות לשוק בגרסאות, Javascript, או בשמה החדש ECMAScript, היא אוסף גדול של יכולות בלתי תלויות. כל פלטפורמה (דפדפן, mobile os, מנוע JS) בוחר לממש חלק מהפיצ'רים האלו בסדר שרירותי כלשהו, כלומר תמיכה ביכולת אחת לא בהכרח מעידה על תמיכה באחרת.

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

ECMAScript Compatibility Matrix
ECMAScript Compatibility Matrix

בדומה ל-Can I use, ניתן להשתמש במידע זה כדי להחליט באילו יכולות שווה לנו להשתמש, באילו לא, ולאילו אפשר לחפש חלופות.

#2 – Transpiling

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

Javascript Transpilers

קיימים מספר טרנספיילרים עבור javascript, והמועדף עליי הוא Babel (בעבר 6to5). כלי זה ממיר שימוש בפיצ'רים חדשים של JS לקוד המבצע את אותה פעולה בדיוק (או בערך), אך ע"י שימוש בקוד JS הנתמך בכל הדפדפנים והפלטפורמות.

כמו שניתן לראות כאן, נכון לכתיבת שורות אלו, מבין כל פלטפורמות ה-JS השונות Babel תומך כמעט במספר הרב ביותר של פיצ'רים (שני רק ל… Edge?!), מה שמבטיח לנו שכמעט כל יכולת חדשה של JS שנשתמש בה תעבוד על כל הדפדפנים.

CSS Transpilers

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

div {
  appearance:button;
  -moz-appearance:button; /* Firefox */
  -webkit-appearance:button; /* Safari and Chrome */
}

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

תהליך בנייה

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

#3 – Polyfills

יכולות מסויימות של Javascript או HTML הן מורכבות מדי כדי לממש בתוך טרנספיילר, למשל SVG, Canvas, HTML Storage ועוד. כדי שבכל זאת נוכל להשתמש בהן, נכתבו עבורן תחליפים עבור דפדפנים ישנים. תחליפים אלו נקראים polyfills (ולעיתים shims). קיימים Polyfills כמעט עבור כל תכונה חדשה של HTML או JS, ורשימה די מקיפה שלהם ניתן למצוא כאן.

ברוב המקרים polyfill לא ירוץ אם הוא יזהה שהיכולת שהוא אמור להחליף כבר קיימת בדפדפן הנוכחי, ולכן זה בטוח להכיל את ה-polyfills בקוד שלנו תמיד. אך שימו לב ש-polyfills הם בסופו של דבר קוד JS, ושימוש מופרז בהם יכול להגדיל את הקוד שלכם משמעותית. אם לא אכפת לכם לבצע בקשת HTTP נוספת, תוכלו להשתמש בשירות polyfills.io, המוריד את ה-polyfills הנדרשים לפי ה-user agent של הדפדפן, וכך יכול לחסוך רוחב פס יקר.

#4 – זיהוי בזמן אמת ומימוש חלופות

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

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

לאחר ריצה של כל בדיקה תתווסף מחלקה (class) לתגית ה-html של האתר שלכם, שתאפשר לכם לבנות התנהגות שונה, אפילו בקוד ה-CSS עצמו, במידה ויכולת מסויימת ממומשת או לא. כמובן שניתן לגשת לתוצאות הבדיקות גם באמצעות JS.

#5 – בדיקות, בדיקות, בדיקות

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

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

  • אתרים כמו Browsershots (ויש עוד עשרות כאלו) יצלמו עבורכם את האתר שלכם בכל דפדפן שתבחרו. כך תוכלו לזהות במהירות היכן יש בעיות נראות לעין ולהתמקד בדפדפנים הבעייתיים.
  • השירות BrowserStack מאפשר לכם להתחבר לעשרות גרסאות של דפדפנים על כל סוגי מערכות ההפעלה (כן, גם מובייל) ולבדוק בפועל את האתר שלכם עליהם. ניתן אפילו לבדוק את האתר כאשר הוא רץ על המחשב שלכם!!
  • אם אין לכם כח לבדוק ידנית כל פעם, ואין לכם צוות QA פנימי, חברת Rainforest QA מספקת שירותי QA as a Service, ומאפשרת לכם להגדיר בדיקות ידניות, שיבוצעו ע"י בודקים אנושיים באופן מיידי. אגב, הם משתמשים ב-Mechanical Turk.
  • ואם יש לכם קצת זמן פנוי, אולי שווה לכם לכתוב בדיקות אוטומטיות באמצעות Selenium. ישנם שירותים (BrowserStack ביניהם) המאפשרים לכם להריץ את בדיקות ה-Selenium שלכם בענן על עשרות דפדפנים שונים.

#6 ואחרון – ניטור שגיאות

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

אך זה שמשהו קורה אי שם אצל הלקוח לא אומר שאי אפשר לדעת עליו ולטפל בו!

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

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

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

לסיכום

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

12 טריקים שכל מפתח python חייב להכיר

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

Dict Comprehension

אחת מפניני התחביר המוכרים ביותר של python היא list comprehension:

>>> [x ** 2 for x in range(10) if x != 4]
[0, 1, 4, 9, 25, 36, 49, 64, 81]

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

>>> {x: x ** 2 for x in range(5) if x != 4}
{0: 0, 1: 1, 2: 4, 3: 9}

Argument Unpacking

בדומה ל-params בשפת C#, גם פייתון תומכת בהמרת הפרמטרים של פונקצייה למערך, ואת הפרמטרים השמיים (keyword params) למילון. זה נראה כך:

def func(regular_arg, *args, **kwargs):
    print regular_arg, args, kwargs

כשנקרא לפונקציה, args יהיה מערך של כל הפרמטרים הרגילים, פרט לראשון שמועבר כ-regular arg, ובנוסף kwargs יהיה מילון שיכיל את כל הפרמטרים שהועברו לפי שם.

>>> func(1, 2, 3, a=1, b=2)
1 (2, 3) {'a': 1, 'b': 2}

הפונקציה enumerate

לפני שהכרתי את הפונקציה enumerate, לעיתים קרובות הייתי כותב קוד כזה:

i = 0
for x in my_list:
    print i, some_processing(x)
    i += 1

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

for i, x in enumerate(my_list):
    print i, some_processing(x)

IPython

אמנם לא מדובר בחלק מהשפה עצמה, אך חבילת IPython מציעה shell נוח הרבה יותר מה-interactive interpreter של פייתון. בין תכונותיו הרבות:

  • Ctrl-C ו-Ctrl-V עובדים
  • השלמה אוטומטית באמצעות מקש TAB
  • צביעת הפלט
  • זיכרון של פקודות מריצות קודמות באמצעות מקש חץ למעלה
  • אפשרות לביצוע embed בתוכניות שלנו ליצירת shell נוח

ניתן להתקין IPython באמצעות pip:

pip install ipython

If טרינארי

במקום לכתוב:

if something:
    x = "yes"
else:
    x = "no"

ניתן לכתוב פשוט:

x = "yes" if something else "no"

הפונקציה get של מילון

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

x = "default"
if key in my_dict:
    x = my_dict[key]

בדיוק לשם כך קיימת הפונקציה get של מילון:

x = my_dict.get(key, "default")

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

New Style Formatting

כמעט כולם מכירים את האופרטור % המבצע string formatting בדומה לפונקציה printf בשפת c:

"My %s is almost %u years old" % ("grandma", 22)

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

"My {relative} is almost {years} years old".format(
    relative = "son", years = 120)

The Zen of Python

מתוך ה-interpreter הריצו את השורה הבאה:

import this

גנרטורים

התחביר של גנרטורים (generators) דומה לזה של list comprehension, אך עם סוגריים רגילים במקום סוגריים מרובעים:

(x ** 2 for x in range(3))

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

>>> generator = (x ** 2 for x in range(3))
>>> generator
<generator object <genexpr> at 0x0000000002B0CF78>
>>> generator.next()
0
>>> generator.next()
1
>>> for x in generator: print x
...
4
>>> generator.next()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

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

List Comprehension מקונן

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

[(i,j) for i in range(3) for j in range(i)]

המשתנה _ ב-interpreter

אם הרצנו ביטוי ב-interpreter, ושכחנו לשים את התוצאה במשתנה, ניתן לגשת לתוצאה האחרונה במשתנה _ (קו תחתון):

>>> expensive_processing("bla")
'ffa6706ff2127a749973072756f83c532e43ed02'
>>> _
'ffa6706ff2127a749973072756f83c532e43ed02'

אופרטורי השוואה משורשרים

בפייתון התחביר הבא הוא חוקי:

>>> 10 < x < 11
False
>>> 10 < x < y < 11
False
>>> 1 >= x < 2
True

פרמטרי ברירת מחדל מצביעים תמיד לאותו אובייקט

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

>>> def func(x=[]):
...     x.append("wa")
...     print x
... 
>>> foo()
["wa"]
>>> foo()
["wa", "wa"]
>>> foo()
["wa", "wa", "wa"]

שימוש ב-Hypothesis למציאה אוטומטית של מקרי קצה בבדיקות יחידה

כאשר כותבים בדיקות יחידה (unit tests) עבור הקוד שלנו, רוב הבדיקות בנויות כך:

  1. הגדרת המידע עליו ירוץ הקוד הנבדק.
  2. הרצת הקוד הנבדק על המידע.
  3. וידוא נכונות הפלט ע"י assertions כלשהם.

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

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

  1. הגדרת מרחב הקלטים האפשריים עליו ירוץ הקוד הנבדק.
  2. הרצת הקוד על כל אחד מהקלטים האלו בצורה אוטומטית.
  3. וידוא נכונות הפלט ע"י assertions כלשהם.

ספריית Hypothesis מאפשרת לנו לעשות בדיוק את זה!

לדוגמה, נניח שאנחנו רוצים לבדוק פונקציית encode ו-decode כלשהן שכתבנו:

def my_encode(value):
    """ return an encoded value """

def my_decode(decoded_value):
    """ return a decoded value """

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

import unittest
from hypothesis import given
from hypothesis.strategies import text

class TestEncoding(unittest.TestCase):
    @given(text())
    def test_decode_inverts_encode(self, value):
        self.assertEqual(decode(encode(value)), value)

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

כדי להפוך את הבדיקות שלנו לאיתנות יותר, הספרייה מספקת כמה שירותים נוספים:

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

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

איך בוחרים web framework בפייתון

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

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

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

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

הכל שאלה של עלות ותועלת

ניתן לסכם את כל הנושא של בחירת תשתית בהשוואה בין עלות ותועלת.

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

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

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

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

חלק נוסף מהעלות נגזר גם מגודל קהילת המפתחים שמשתמשים בה, שואלים ועונים על שאלות ומפתחים עבורה תוספים והרחבות. למשל נכון לכתיבת שורות אלו לחבילת flask יש כ-10,000 שאלות ב-Stack Overflow, ולעומתה על CherryPy נשאלו 960 בלבד.

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

  • מנוע templates
  • ORM – מידול וגישה לבסיסי נתונים (SQL או NoSQL)
  • ניהול משתמשים והרשאות
  • מנוע cache
  • תמיכה במספר שפות
  • Authentication
  • Multitheading
  • תמיכה בקבצים סטאטיים.
  • DB Migrations
  • ניהול קבצים סטאטיים
  • כלי בדיקה

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

לפרוייקטים קטנים – Bottle

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

חבילת Bottle היא מיקרו תשתית (micro-framework), המכילה למעשה קובץ אחד בלבד – bottle.py, שבו ממומשת תשתית web שלמה ואפילו שרת לבדיקה. כדי לכתוב תוכנית בסיסית בעזרת bottle, לא נדרשות יותר מחמש שורות הקוד הבאות:

from bottle import route, run, template

@route('/hello/<name>')
def index(name):
    return template('<b>Hello {{name}}</b>!', name=name)

run(host='localhost', port=8080)

למרות היותה מיקרו-תשתית, bottle תומכת בכל מה שצריך כדי לבנות פרוייקטים קטנים: מנוע routing, מנגנון templating, גישה לאובייקטי request ו-response ותמיכה בהעלאת קבצים. בנוסף מכילה bottle לא מעט adapters שיעזרו להתממשק עם חבילות מפורסמות כמו Jinja2, ומעל 15 שרתי http. להלן דוגמה איך בתוספת של שורת קוד אחת בלבד ניתן להשתמש בתבניות של Jinja2:

from bottle import jinja2_view, route
 
 @route('/', name='home')
 @jinja2_view('home.html', template_lookup=['templates'])
 def home():
     return {'title': 'Hello world'}

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

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

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

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

לפרוייקטים בינוניים וגדולים – Django – המלכה הבלתי מעורערת

Django היא ללא ספק גולת הכתר של חבילות תשתיות ה-web הפייתוניות. עם היסטוריה של 12 שנים, מעל 100k (!) שאלות ב-SO וקהילת מפתחים ותורמים ענקית ופעילה, תוכלו למצוא מענה מיידי כמעט לכל שאלה. מערכת ה-ORM של Django ידועה כמקיפה ביותר, תומכת במספר נאה של בסיסי נתונים מבוססי SQL, ומשמשת תמיד כדוגמה לשימוש מופתי ב-Metaclasses.

בדיוק כמו ההוא מהסרט, Django משוחררת מכל מעצורים ומאפשרת לפתח כמעט כל סוג של פרוייקט רשת, גם בקנה מידה ענקי. הגישה של Django היא "הסוללות כלולות", כלומר מפתחיה משתדלים להכניס לתוכה את כל מה שצריך בשביל רוב הפרוייקטים: ORM מפואר, cache, שפת templates שלמה, מערכת ניהול DB, ניהול sessions, ניהול משתמשים ואותנטיקציה, יכולות אבטחה, תמיכה במספר אתרים במקביל, תמיכה בקבצים סטאטיים, תמיכה במספר שפות, ועוד. ואם בכל זאת מצאתם משהו שלא כלול, בוודאי תשמחו לדעת שבמשך השנים פותחו ל-Django מאות, אם לא אלפי "אפליקציות" הרחבה, ביניהן NoSQL DB Backends, תשתיות REST API, חבילות אותנטיקציה מסוגים שונים, חבילות אבטחה, ואפילו מערכות CMS ובלוגים שלמות.

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

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