<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>پست‌های انتشارات پول ریکوئست</title>
        <link>https://virgool.io/pullrequest/feed</link>
        <description>پول ریکوئست، دست نوشته های فنی فارسی زبان</description>
        <language>fa</language>
        <pubDate>2026-06-16 18:58:14</pubDate>
        <image>
            <url>https://files.virgool.io/upload/publication/kywsolcqsvvr/wgwfpz.png</url>
            <title>پول ریکوئست</title>
            <link>https://virgool.io/pullrequest</link>
        </image>

                    <item>
                <title>لود داینامیک فایلها و اپلیکیشن‌ها در پایتون و فلسک</title>
                <link>https://virgool.io/pullrequest/%D9%84%D9%88%D8%AF-%D8%AF%D8%A7%DB%8C%D9%86%D8%A7%D9%85%DB%8C%DA%A9-%D9%81%D8%A7%DB%8C%D9%84%D9%87%D8%A7-%D9%88-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86%D9%87%D8%A7-%D8%AF%D8%B1-%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-%D9%88-%D9%81%D9%84%D8%B3%DA%A9-zvio1hja3k6i</link>
                <description>سلام.در فلسک یه کانسپت بخصوص به نام بلوپرینت(Blueprint) وجود داره که اجازه میده ما بتونیم بخش‌های مختلف اپلیکیشن رو، از همدیگه تفکیک بکنیم. اگر با جنگو آشنا باشید، بلوپرینت‌ها همون ‍&#x60;اپ&#x60; های جنگو هستن.حالا، اخیرا نیاز شده بود که به صورت داینامیک یه اپلیکیشن به پروژه اضافه بشه یا ازش برداشته بشه و میخواستیم این کار به ساده‌ترین صورت ممکن قابل انجام باشه. از بین کل‌ راه‌های موجود برای این‌کار، کپی کردن فایل به محلی مشخص و لود شدن بلو‌پرینت و ریجستر شدنش به صورت اتوماتیک رو انتخاب کردیم.با فلسک، پایتونی کد میزنیم!نکتهٔ خیلی مهم در مورد فلسک، این هستش که بر خلاف جنگو، شما پایتون کد می‌زنید. در واقع، برای اینکه بتونید با جنگو کار کنید، باید یاد بگیرید که هر کاری رو توی جنگو به چه صورت انجام می‌دن. ولی این در مورد فلسک صدق نمی‌کنه. فلسک اونقدر کوچیکه و بدون پیش‌فرض در مورد کد شما، که برای کار کردن باهاش، کافیه بلد باشید اون کار رو توی پایتون چطوری باید انجام داد. باقی قضیه دیزاین و توانایی خودتون هست. این قضیه در عین حال که سادگی بیش‌از اندازه فلسک رو به همراه داره، ساختن اپلیکیشن پروداکشن رو باهاش به شدت سخت می‌کنه. شما مسئول همه چیز هستید و هیچ کاری برای شما انجام نمیشه.انجام نرمال کارهااول، میگم‌که ساختار پروژه‌ام چطوری هستش و فایل‌ها رو چطوری کنار هم می‌چینم و بلو‌پرینت درست می‌کنم، بعدش لود داینامیک اونها رو بررسی می‌کنم.من نیاز دارم که یه پروژه فلسک که بلوپرینت ساپورت می‌کنه بسازم. کل فایل‌هایی که باید بسازم به صورت زیر هستش├── develop.py
├── dloader
│   ├── application.py
│   ├── apps
│   │   ├── __init__.py
│   │   └── test_app
│   │       ├── __init__.py
│   │       └── views.py
│   ├── __init__.py
└── README.mdکدهایی که باید داخل این فایل‌ها بره رو از توی گیت میتونید بردارید. من تمام فایل‌ها رو بررسی نمی‌کنم!کد‌های فایل application.py به صورت زیر هستنfrom flask import Flask

def create_app():
    app = Flask(__name__)
    _load_blueprints(app)
    return app
    
def _load_blueprints(app):
     from .apps.test_app import test
      app.register_blueprint(test)همچنین برای ساختن بلو‌پرینت‌ در فایل init.py به صورت زیر عمل کردم(فایل داخل پوشه apps)from flask import Blueprint
test = Blueprint(&#039;test&#039;, __name__)در صورت اجرا، این‌ کدها به درستی عمل میکنند. حالا میخوایم به صورت داینامیک بلو‌پرینت‌ها رو لود کنیم. تغییرات چندان زیادی لازم نیست. همچنین میشه به صورت های مختلفی این‌کار رو انجام داد. من در نهایت از روشی استفاده کردم که در ادامه توضیحش می‌دم اما کاملا به فکر هستم که سیستم‌اش رو عوض کنم.من فایل application.py رو به صورت زیر ویرایش میکنمimport importlib
import os

 def _load_blueprints(app):
    folder_names =  os.listdir(&quot;dloader/apps/&quot;)
    for name in folder_names:
        if name.endswith(&quot;_app&quot;):
            tmp_blueprint = importlib.import_module(f&quot;.{name}&quot;, &quot;dloader.apps&quot;)
            app.register_blueprint(tmp_blueprint.blueprint)اول از همه کل‌پوشه‌های داخل مسیر رو به دست اوردم، بعد از اون روی لیست پوشه‌ها لوپ زدم. در مرحله بعد بررسی کردم که اگر اسم پوشه به app_ ختم میشه، بنابراین جزو بلوپرینت های من هستش. اون رو ایمپورت کردم و بعد هم ریجستر.این وسط یه اتفاقی افتاده و اون خط اخر هستش. همینجوری که می‌بینید من از ماژول ایمپورت شده، دارم متغییری به نام blueprint رو استفاده می‌کنم. دلیل این قضیه این هستش که من با ایمپورتی که در خط قبلی انجام دادم، نیاز دارم به خود ابجکت بلوپرینت اشاره کنم تا بتونم ریجسترش کنم.حالا برای اینکه اسم بلوپرینت هم داینامیک باشه، من همیشه ابجکت بلوپرینت رو باید داخل یه متغییر به اسم blueprint گذاشته باشم:)فایل init ام به صورت زیر تغییر میکنه بنابراینtest = Blueprint(&#039;test&#039;, __name__)
blueprint = testهمینجور که می‌بینید، من در نهایت ابجکت blueprint رو برای ریجستر کردن استفاده کردم.همچنین در صورتی که بخوام url_prefix استفاده کنم هم می‌تونم به همین صورت یه متغییر بهش بایند کنم و استفاده‌اش کنم.این importlib چیکار میکنهماژول importlib برای ایمپورت کردن پکیج‌ها و ماژول‌های مختلف به صورت داینامیک و از داخل کد هستش. همینجور که می‌بینید ما از یه کد کاملا پایتونی برای انجام این‌کار در فلسک استفاده کردیم.این تابع دوتا ارگومنت می‌گیره که البته دومی الزامی نیست. بر اساس داکیومنت پایتونThe &#x27;package&#x27; argument is required when performing a relative import. It specifies the package to use as the anchor point from which to resolve the relative import to an absolute import.بنابراین فقط در صورتی که داریم رلتیو ایمپورت انجام می‌دیم، نیاز هستش که از package استفاده کنیم. همچنین بهتره که با یه . شروع کنیم اسم ماژولی که باید ایمپورت بشه تا به مشکل نخوریم! :)این ماژول واقعا کار راه انداز هستش و مشخصا بهتون کمک می‌کنه که به صورت داینامیک و راحت، با استفاده از کد ماژول‌های مختلف رو ایمپورت کنین و نه اینکه همیشه همه چیز رو ایمپورت کنین. همچنین میشه به صورت کلی به عنوان یه پلاگین سیستم و لودر اتوماتیک پلاگین‌ها هم بهش نگاه کرد.کد کامل پروژه که کار میکنه رو براتون اینجا اپلود کردم که میتونید استفاده کنین.</description>
                <category>پول ریکوئست</category>
                <author>Maysam senaps</author>
                <pubDate>Thu, 07 Feb 2019 22:22:13 +0330</pubDate>
            </item>
                    <item>
                <title>از Higher-order function تا filter و reduce</title>
                <link>https://virgool.io/pullrequest/%D8%A7%D8%B2-higher-order-function-%D8%AA%D8%A7-filter-%D9%88-reduce-xovgvd0dfrp6</link>
                <description>تو این نوشته می‌خوام در مورد نحوهٔ کارکرد Filter و Reduce بنویسم، اما قبل از پرداختن به شیوهٔ کارکرد Filter و Reduce نیاز هست با یه سری از کلمات کلیدی توی پارادایم برنامه‌نویسی فانکشنال آشنا شیم. نماد لاندا - برنامه‌نویسی فانکشنال ریشه در جبر لاندا دارد.First-class Functionبه طور خلاصه اگه بتونینیک تابع رو به عنوان مقدار به متغییر بدین  تابع رو به عنوان ارگومان به تابع دیگه‌ای بدینخروجی یک تابع، تابع باشهدر صورتی که زبان برنامه‌نویسی که باهاش کار می‌کنید همچین قابلیت‌هایی داشت در واقع میشه گفت first-class citizen هست. اسم دیگه‌ای از این قابلیت که ممکنه بیشتر شنیده باشید anonymous functions یا lambda expression هست، که اکثریت زبان‌های برنامه‌نویسی همچین قابلیتی رو دارن.Higher Order Functionهرگاه تابعی، تابع دیگری از ارگومان ورودیش بگیره یا تابعی رو به عنوان مقدار خروجی برگشت بده، یه تابع Higher order یا به اختصار (HOF) هست. در مقابل HOF مفهوم First-Order Function داریم که نقطه مخالف HOF هست یعنی تابعی که نه تابعی رو به عنوان ارگومان می‌گیره نه تابعی رو به عنوان خروجی برگشت میده. و این امر واضحی‌ست که برای نوشتن Higher Order Function زبان برنامه‌نویستون باید قابلیت نوشتن تابع به صورت First class citizen داشته باشه. نوشتن HOF کمک می‌کنه که منطق عملیاتتون به صورت abstract نوشته بشه. یعنی چی؟ خب باید با مثال جلو بریم.Filterفرض کنید یه ارایه از اعداد مختلف دارین و می‌خواین تمام اعداد فرد رو به عنوان یه ارایه‌ٔ دیگه بدست بیارید.  const getOddNumbers = arr =&gt; {
 let result = [];
 for (let i = 0; i &lt; arr.length; i++) {
 if (arr[i] % 2 != 0) result.push(arr[i]);
  }
 return result;
};
getOddNumbers([1, 2, 3, 4, 5]); // [1, 3, 5]خب خیلیم عالی! حالا فرض کنید نیاز پیدا می‌کنید که لیست اعداد زوج هم بدست بیارین، راه حل‌تون برای این کار نوشتن ۹۹٪ همون کد، با یه تغییره؟ اصلا فرض کنید می‌خواین لیست همهٔ اعداد بخش‌پذیر بر ۵ رو بدست بیارین بازم اکثریت اون کد‌ها تکرار میشن و فقط بخش اصلی یعنی تشخیص اضافه کردن چه چیزی به ارایه‌ٔ جدید تغییر می‌کنه. حالا همین کارو با نوشتن یه HOF انجام میدیم.const filterList = divisibleFunc =&gt; arr =&gt; {
 let result = [];
 for (let i = 0; i &lt; arr.length; i++) {
 if (divisibleFunc(arr[i])) result.push(divisibleFunc(arr[i]));
  }
 return result;
};

const divisibleBy = item =&gt; value =&gt; {
 if (value % item == 0) return value;
};
const divisibleBy2 = divisibleBy(2);
const filterListDivisibleBy2 = filterList(divisibleBy2);
filterListDivisibleBy2([1, 2, 3, 4, 5]); // [ 2, 4 ]تو این کد ما یه تابع کلی داریم که کارش اینکه روی خونه‌های ارایه‌مون حرکت کنه، و تابعی که به عنوان ارگومان بهش پاس داده شده روی تک تک اعضای ارایه امتحان میکنه اگه جواب تابع غیر false بود، اون خونه از ارایه رو توی یه ارایه جدید میزاره در نهایت هم ارایه‌ جدیدو به عنوان خروجی برگشت میده.پایین‌تر هم یه تابع کلی به اسم divisibleBy نوشتیم که ۲ مقدار ورودی میگیره و چک می‌کنه مقدار بر ایتم بخش‌پذیر هست و در صورت درست بودن شرط، مقدار رو برگشت میده.حالا ما یه تابع میسازیم به اسم divisibleBy2 که عدد ۲ رو به عنوان ورودی به divisibleBy میده و مقدار بازگشتی اون یه تابع دیگه هست که یه مقدار (value) به عنوان ارگومان قبول می‌کنه و چک می‌کنه ببینه با ۲ پخش‌پذیر هست یا نه. در نهایت تابع بدست اومده رو به عنوان ورودی به عنوان تابع ارگومان میده به filterList و خب باز یه تابع برمیگرده که یه ورودی می‌گیره که ارایه باشه، این خروجی به متغییر به اسم filterListDivisibleBy2  مقداردهی می‌کنیم و در نهایت با دادن ارایه‌ٔ ورودی بهش خروجی رو می‌بینیم.می‌تونیم ۳ خط آخر به شکل کوتاه شده‌ هم بنویسیم، به این صورت:filterList(divisibleBy(2))([1, 2, 3, 4, 5]);ممکنه با خودتون بگین خب این همه دردسر برای چی؟! روش اول که ساده‌تر بود. تو این روش ما یه filterList داریم که یه فانکشن می‌گیره و روی تک تک خونه‌های ارايه شرطشو انجام میده و در صورت درست بودنش یه مقدار برگشتی داره و در نهایت یه لیست میسازه، این فانکشن تو هر نرم‌افزار دیگه‌ای که نیاز به همچین کاری باشه می‌تونه بدون هیچ تغییری قابل استفاده باشه! تازه! اگه با جاوااسکریپت کار کرده باشید احتمالا با این تابع خیلی سر و کار داشتید در واقع filterList که نوشتیم، متد filter پروتوتایپ Array جاوااسکریپت هست با  یک تفاوت کوچیک، مقداره ورودی‌های ما به divisibleFunc با مقدار ارگومان‌های filter جاوااسکریپت یکم فرق دارن.  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter شکل دیگه‌ای که میشه نوشت به این صورت:filterList(value =&gt; {
    if (value % 2 == 0) return value;
})([1, 2, 3, 4, 5]);
توجه کنید که HOF یک ویژگی خاص تو جاوااسکریپت یا هر زبان دیگه نیست، در واقع HOF یک مفهوم کلی‌ست برای نوشتن منطق کار‌هاتون به صورت انتزاعی‌تر که تو برنامه‌نویسی فانکشنال بیشتر شناخته شدست و ازش استفاده می‌کنن. نکته مهمی که به نظر من در مورد یادگیری برنامه‌نویسی یا مهندسی نرم‌افزار وجود داره، درک همین مفاهیم اصلی هستند که شما رو برنامه‌نویس بهتر می‌کنه و هیچ راه‌حل همه فن حریف برای همه شرایط وجود نداره، برنامه‌نویس کسی‌ست که با شناخت و درک مسئله و شرایط تصمیم و راه‌حل بهتر انتخاب کنه، گاهی ممکنه مسئله با دیدگاه شئ‌گرایی بهتر حل شه، گاهی با فانکشنال و ... یا حتی تلفیقی از راه‌حل‌های موجود، بهترین انتخاب باشه.همونطور که گفتم این مفهوم هرجای می‌تونه مورد استفاده قرار بگیره به شرطی که اون زبان قابلیت first-class function داشته باشه، برای مثال مدتی قبل گذرا یه تیکه کد جاوا مخصوص پلتفرم اندروید رو چک می‌کردم که همش یه کاری تکراری انجام میداد با این تفاوت که فقط یه نقطه از اون کد تغییر پیدا می‌کرد، نقطه شروع این بود که یه فانکشن بنویسم که یه فانکشن و یه ورودی دیگه به عنوان ارگومان میگرفت و داخل بدنه‌ش، ورودی دوم رو به عنوان پارامتر به فانکشنی که از ارگومان اولی گرفته بودم پاس میداد. حالا می‌تونستم هر فانکشنی بنویسم و که همیشه هم یه ورودی بهش داده میشه. تو توییت پایینی می‌تونین بیشتر در موردش بخونین. https://twitter.com/EhsanMaders/status/1064207409588117505 خب اگه تا اینجا رو گرفته باشید در واقع شما مفهوم higher-order-function رو درک کردید و تابع پرکاربرد filter هم پیاده‌سازی کردین.میریم سراغ اصل مطلب یعنی ReduceReduceابزار reduce (fold هم شناخته میشه) به معنی کم‌کنندست ولی در واقع می‌تونیم ترکیب‌کننده هم بهش بگیم، معمولا ۳ ارگومان میگیره یکیش ارایه‌س یکیش تابع ریدوسر و بعدی هم مقدار اولیس (دلخواهی)، reduce روی اعضای ارایهٔ حرکت می‌کنه و تابع ریدوسر روی اعضا انجام میده و خروجی اون رو توی متغییر به اسم accumulator میریزه، همین کارو تا وقتی که اعضای ارایه تموم بشن ادامه میده و در نهایت مقدار accumulator رو برگشت میده.با مثال ببینیمش قبل از استفاده از reduce مثال غیرفانکشنالش رو بنویسیم، می‌خوایم مقدار خونه‌های ارایه رو باهم جمع کنیم.const summation = arr =&gt; {
 let acc = 0;
 for (let i = 0; i &lt; arr.length; i++) acc += arr[i];
 return acc;
};خب حالا بازم فرض کنید نیاز دارین ضرب‌ همه‌ٔ خونه‌های ارایه رو بدست بیارید، بازم اکثریت کد تکرار میشه به جز بخش اصلی یعنی logic که شما می‌خواید رو خونه‌های ارایه انجام بشه و فقط یه خروجی داشته باشه. واسه همچین شرایطی reduce استفاده می‌شه. البته reduce به آچر فرانسه مشهوره :دی کارهای زیادی رو میشه با reduce انجام داد حتی عملیاتی مثل map  و یا filter میشه با reduce انجام داد.خب پیاده سازی reduce به این صورت هست.const reduce = function(arr, reduceFn, accumulator = 0) {
     for (let i = 0; i &lt; arr.length; i++) {
         accumulator = reduceFn(accumulator, arr[i], i, arr);
      }
      return accumulator;
};و برای استفاده از این تابع برای مثال جمع اعداد ارایه اینطوری عمل می‌کنیم.reduce([1, 2, 3, 4, 5], (acc, value) =&gt; acc + value); //15 accumulator وهٔ محاسبه  تفاوت متد reduce پروتوتایپ Array فقط در ارگومان اول هست، اونجا دسترسی به خود ارایه از طریق this هست. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce همونطور که گفته شده reduce مثل چاقوی سویسی هست و کار‌های زیاده رو میشه باهاش انجام داد نمونه‌های دیگه هم پیاده‌سازی توابع compose و pipe با reduce هست.در انتها من ویدیوی در مورد نحوه‌ٔ استفاده از متد reduce برای درس طراحی صفحات وب ضبط کرده بودم که قرار میدم. https://www.aparat.com/v/dfVyv * تو ویدیو اشاره می‌کنم که راه فانکشنال برای پیاده‌سازی reduce بجای استفاده از for باید از map استفاده کرد، که اشتباهه! باید می‌گفتم استفاده از recursion هست.چند تا مقاله برای کاربرد‌های مختلف در استفاده از reduce خونده بودم، لینک‌هاشو میزارم شاید مفید بود. https://medium.com/@spoo.naidu/access-deeply-nested-objects-without-the-long-if-conditions-using-reduce-d440d92aeb1f  https://medium.com/@dominic.tracey_33345/use-array-reduce-and-es6-for-transforming-objects-97cfa9362c52  https://medium.com/@spoo.naidu/js-reduce-function-6841c0f7c0ff منابع: https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d  https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99  https://github.com/getify/Functional-Light-JS </description>
                <category>پول ریکوئست</category>
                <author>احسان</author>
                <pubDate>Sun, 06 Jan 2019 04:59:47 +0330</pubDate>
            </item>
                    <item>
                <title>اولین قدم با اسکریپی</title>
                <link>https://virgool.io/pullrequest/scrapy-getting-started-om0wrjnl8btg</link>
                <description> قصدم نوشتن نکات و حقه های اسکریپی بود که بعد از نظرسنجی قرار بر این شد که با یه آموزش کوتاه و سریع، کارهایی که میشه باهاش انجام داد رو نشونتون بدم و قبل از درگیر شدن با مسائلی که زیر کاپوت میگذره و ممکنه باهاش درگیر نشین، اول با دیدن سریع‌تر خروجی لذت کار کردن باهاش رو بچشین. تو پست های بعدی سعی میکنم کم‌کم نکات فنی‌ترش رو هم بگم براتون.اصلا اسکریپی چیه و به چه دردی میخوره؟اسکریپی یه فریم‌ورک آزاد و اوپن سورسه که به درد جمع آوری اطلاعات از وبسایتها میخوره.همونطور که میدونین کلی اطلاعات به درد بخور داخل اینترنت و وبسایت های مختلف هست که میتونین کلی کار با مزه‌ باهاش انجام بدین.اگر توسعه دهنده‌ی وب هستین و برای سرویسی که راه انداختین نیاز به منبع اطلاعاتی دارین. اگر دیتاساینتیست هستین و نیاز به استخراج دیتا برای آنالیز و ترین کردن مدلتون دارین، اسکریپی میتونه گزینه‌ٔ جذابی براتون باشه.# فرض رو بر این میذارم که پایتون ۳ و مفاهیم اولیه و یکم html , css بلدین و pip مختص ورژن پایتون رو نصب دارین و میتونین با virtualenv کار کنین.# بهتره راجع به سلکتورهای xpath و css بدونین. خودم معمولا با xpath توکار خود اسکریپی کارم رو پیش میبرم. مگر اینکه خیلی کار کثیفی انتظارم رو بکشه و برم سراغ BeautifulSoup.تو این آموزش با هم به راه اندازی پروژه ی اسکریپی و استخراج اسامی و اطلاعات تمامی هتل های ایران از سایت hotelyar.com و ذخیره کردنش داخل فایل به صورت JSON میپردازیم.نصب پیش نیازها و راه‌اندازی اولیه # میتونین به صورت سیستم‌واید و بدون استفاده از virtualenv اسکریپی رو نصبش کنید اما با استفاده از virtualenv دستتون توی انتقال پروژه و مدیریت پکیج‌ها بازتره.اول نیاز داریم تا اسکریپی رو نصب کنیم. env مورد نظرتون رو بسازین و بعد از اکتیو کردنش با این دستور اسکریپی رو نصب کنید:(.env) $ pip3 install scrapyبعدش با این دستور یه پروژه ی اسکریپی تعریف کنید:(.env) $ scrapy startproject tutorialبعد از اجرای این دستور یه دایرکتوری با ساختار زیر درست میشه (که قابل تغییره و این رو تو پست های بعدی میبینیم ).scrapy.cfg: این فایل مربوط به تنظیمات دیپلوی پروژه میشه که فعلا بهش نیازی نداریم.middlewares.py: داخل این فایل میتونین عملیاتی که قبل یا بعد از زدن ریکوئست(تنظیم کردن پروکسی، تغییر هدر ریکوئست و این مدل کارها)، عملیاتی که انجین اسکریپی روی آیتم انجام میده و یه سری کارهای دیگه رو برای پروژه تعریف کنین.pipelines.py: به وسیله ی پایپلاین میتونین آیتم اکسترکت شده‌ٔ نهایی رو تمیز کنین یا داخل فایل یا دیتابیس مقصد ذخیره کنین.settings.py: اینجا هم میتونین تنظیمات گلوبال پروژه رو تعریف کنین. آدرس دیتابیس، فعال و غیر فعال کردن middleware ها و pipeline ها و یه سری تنظیمات دیگه اینجا انجام میشه.قدم دوم: نوشتن اسپایدرقبل از اینکه شروع کنین به نوشتن اسپایدر اول سایت رو دقیق بررسی کنین. ببینین چه اطلاعاتی رو میخواین و چطوری باید بهش رسید.تو این لینک لیست تقریبا کاملی از هتل های ایران داریم.یه فایل پایتون به اسم hotels.py داخل دایرکتوری spiders تو مسیر پروژمون با محتویات زیر میسازیم. https://gist.github.com/mrt-kousha/649610de70512d448cafea759ac0ee5e اول اسکریپی رو ایمپورت میکنیم و یه کلاس میسازیم scrapy.Spider رو داخلش اکستند میکنیم.چند نوع اسپایدر وجود داره برای اکستند کردن داخل کلاسی که میسازین من ترجیح میدم از همین کلاس پیش فرض و ساده و ابتدایی scrapy.Spider استفاده کنم. لیست کامل مدل های مختلف و کارایی اسپایدرها رو میتونین داخل این لینک ببینین:https://doc.scrapy.org/en/latest/topics/spiders.html  name: هر اسپایدری باید یه اسم یکتا به ازای پروژه داشته باشه. تا بشه بعدا فراخوندش. آخر سر میبینیم که چطوری ازین اسم برای صدا زدن و اجرای اسپایدر استفاده میکنیم.custom_settings: تنظیمات سفارشی اونجایی به کمک ما میاد که بیشتر از یک اسپایدر داخل پروژه داریم و هرکدوم تنظیمات مختلف خودشون رو دارن. سرعت درخواست زدن هر اسپایدر تاخیر بین ریکوئست زدن پایپلاین ها و میدلورها ممکن به ازای هر اسپایدر متفاوت باشه و دیگه نمیشه داخل تنظیمات اصلی این تنظیمات رو به ازای هر اسپایدر داشت. برای همین از تنظیمات سفارشی استفاده میکنیم و این مقادیر تنظیم شده اوررایت میشن.start_requests: مرحله ی اول هست. داخلش به اولین صفحه‌ای که حاوی لینک هتل ها بود درخواست میزنیم.# توجه کنین که کل مراحل درخواست زدن async انجام میشه و اگر ببینین داخل متد scrapy.Request، مقدار callback رو روی تابعی که مرحله ی بعد تو بازی میاد تنظیم کردم.extract_hotel_urls: تو این مرحله تمام لینک هایی که ما رو به اطلاعات هتل میرسونه استخراج میکنیم و بهشون درخواست میزنیم تا صفحات هتل ها برامون باز بشن. اما چطوری لینک هتل ها رو در بیاریم؟اگر به لینکی که ما رو به هتل ها میرسونه نگاه کنین همشون یه الگو خاص دارن:https://hotelyar.com/hotel/84/...میتونیم همه‌ی لینک ها رو از صفحه‌ی اول استخراج و یونیک کنیم و داخلش دنبال اون لینکی بگردیم که دوبار کلمه‌ی hotel داخلش تکرار شده.# اگر کلمه‌ی hotel داخل یه لینکی دوبار تکرار بشه حاصل split کردن لینک به کلمه‌ی hotel میشه یه آرایه با ۳ عضو، غیر این باشه هم به کارمون نمیاد. (خط ۲۴)extract_hotel_info: تو مرحله ی آخر هم مقادیری که لازم داریم رو به کمک سلکتور ایکس-پث که امیدوارم دنبالش برین و ازش لذت ببرین، در میاریم. اینجا من فقط اسم هتل و لوکیشن و لینک صفحه ی هتل رو برمیگردونم شما میتونین هر اطلاعات دیگه‌ای که به نظرتون جالب اومد رو برگردونین.در آخر هم بعد از ذخیره سازی اسپایدر رو با اسمی که براش گذاشتیم اجرا میکنیم.(.env) $ scrapy crawl hotel_info -o hotel.jlاطلاعات استخراج و داخل فایل hotel.jl به صورت json ذخیره میشن.در نهایت هر کدوم از json ها رو میتونین داخل این سایت بررسی کنین و با فرمت آدمیزادی ببینیدش D:در آخر نکته هایی که باید بهش توجه بشه callback ها هستن که حتما یکم باهاشون بازی کنین. متد جدید بنویسین و سعی کنین پروسه رو پیچیده کنین. هر متدی که نوشته شد شبیه ساز یکی از مراحل از باز کردن صفحه‌ی حاوی لینک تا پیدا کردن لینک صفحات هتل ها و در نهایت استخراج اطلاعات بود. خیلی این پست طولانی شد و به نظرم خلاصه ترین مدلی بود که میتونستم ازش در بیارم. باز هم اگر نکته‌ای بود خوشحال میشم بهم اطلاع بدین.لینک های مفید:https://www.w3schools.com/xml/xpath_intro.asphttps://doc.scrapy.org/en/latest/index.html</description>
                <category>پول ریکوئست</category>
                <author>mrt.kousha</author>
                <pubDate>Wed, 02 Jan 2019 21:21:40 +0330</pubDate>
            </item>
                    <item>
                <title>آشنائی با RxJava در اندروید</title>
                <link>https://virgool.io/pullrequest/%D8%A2%D8%B4%D9%86%D8%A7%D8%A6%DB%8C-%D8%A8%D8%A7-rxjava-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-yefdumodgefs</link>
                <description> شاید شما هم از قابلیتهای Rxjava شنیده باشین ولی هنوز از اون توی  پروژه‌هاتون استفاده نکرده باشید. خب برای شروع کمی دیر هست ولی ایرادی نداره. دیر رسیدن بهتر از هرگز نرسیدنه. در این آموزشها سعی میکنم توضیحی ساده از اینکه Rxjava و RxAndroid چیه و چه کمکی میکنه ارائه کنم.برنامه نویسی Reactive نوعی از برنامه نویسی واکنش گرا است. هر چیزی که شما مشاهده می کنید جریانی از اطلاعات ناهمگامه که قابلیت مشاهده شدن دارن و بر اساس اون یک اتفاق رخ میده(همون علت و معلول خودمون). شما این جریان داده ها رو می تونید از هر چی بسازید. مثلا اگر متغیری تغییر می کنه، کاربر کلیکی انجام میده، فراخوانی اطلاعاتی از اینترنت انجام میشه، داده ای ذخیره میشه، خطائی رخ میده و ... . منظور از ناهمگام هم اینه که هر کدام از اینها میتونند در چندین اتفاق به صورت موازی و غیرهمزمان رخ بدهد.یکی از مزایای غیرهمزمانی اینه که این قابلیت وجود داره که هر کدام از این علت و معلولها رو مستقلا مدیریت کرد. وقتی که این روش وارد به برنامه های موبایل میشن به این معنی که شما می تونید بعضی از کارها رو در پس زمینه اجرا کنید تا مشکلی در تجربه کاربری پیش نیاد. یک مثال ساده: x = y + z را در نظر بگیرید که مجموع y و z در x قرار میگیره. در برنامه نویسی Reactive وقتی y تغییر میکنه مقدار x بدون اینکه دستور x=y+z اجرا بشه آپدیت میشه. این اتفاق با مشاهده مدام y و z اتفاق می افته.حالا یک مورد دیگه:مثلایک آرایه از داده ها رو در نظر بگیرید که می تونه از هر جائی گرفته شده باشه. ممکنه شما بخواین اعداد زوج رو فیلتر کنین و اعداد فرد رو از اون جدا کنید. این میتونه با یک حلقه و شرط معمولی انجام بشه. اما همین کار رو با یک نگرش جدید در برنامه نویسی reactive انجامش بدین.یک مثال واقعی تر: مثلا شما در حال تماشای فوتبال هستید. اطلاعات زیادی از بازی فوتبال در حال انتشار هستند. مثلا کدام بازیکن در چه دقیقه ای گل زد، چه کسی کارت زرد گرفت و ... . بازی در هر لحظه در حال انتشار اطلاعات جدیده و شما به عنوان مشاهده گر مشغول تماشای بازی هستید.افزونه های Reactiveافزونه های reactive کتابخانه هائی هستند که از اصول reactive پیروی میکنن مثلا می تونند یک برنامه واکنش گرا با استفاده از چندین توالی مشاهدات ایجاد کنند. این کتابخونه ها می تونند با استفاده از واسطها و متدها به برنامه نویسان کمک کنند تا کدهای ساده تر و تمیزتری بنویسند.RXJava چیه؟یکی از این کتابخونه ها RxJava است که توسط شرکت Netflix توسعه داده شده است. این کتابخونه میتونه رویدادهای غیرهمزمان رو مدیریت کنه و در تردهای مختلف اجرا کنه. تعداد زیادی از اپراتورها رو برای تغییر در داده ها اجرا کنه.RxAndroid چیه؟پلتفرمیه که یکسری قابلیتها رو به RxJava اضافه میکنه. به طور خاص Schedulers و AndroidSchedulers.mainThread که فقط در برنامه نویسی اندروید کاربرد داره.اجزای Rxjava:Observable:به معنی مشاهدات و جریانی از داده ها که کاری رو انجام میدند یا اطلاعات رو ارسال میکنند.Observer:به معنا مشاهده گر و داده ها رو از Observable میگیره.Operator / Transformation:اپراتورها دیتاهای رو که توسط Observable ارسال شده را قبل از اینکه توسط Observer دریافت شود را تغییر می دهد.Scheduler:تصمیم میگیرند کدام ترد روی Observable بایستی داده ارسال کند و کدام Observer بایستی داده ها رو دریافت کند. به طور مثال background thread یا main thread.شروع کار در اندروید:حالا بایستی   RxAndrodi و Rxjava را به   build.gradle  اضافه کنید: // RxJavaimplementation &#039;io.reactivex.rxjava2:rxjava:2.1.9&#039;// RxAndroidimplementation &#039;io.reactivex.rxjava2:rxandroid:2.0.1&#039; می دانیم وظیفه Observable  ارسال اطلاعات است. ابتدا بایستی یک Observable  بسازیم. برای قرار دادن اطلاعات در یک Observable  روشهای زیادی وجود داره که ساده ترین آن استفاده از just است.Observable&lt;String&gt; animalsObservable = Observable.just(&quot;Ant&quot;, &quot;Bee&quot;, &quot;Cat&quot;, &quot;Dog&quot;, &quot;Fox&quot;);داستان Observable  به شکل زیر است: حالا باید یک مشاهده گر بسازیم که Observable را مشاهده کند. یک مشاهده گر متدهای زیر را برای اطلاع از وضعیت Observable دارد که با آن آشنا می شویم: onSubscribe():میشود Observable زمانی فراخوانی میشود که مشاهده گر مشترک onNext(): اطلاعات را ارسال میکند Observable زمانی فراخوانی میشود که onError(): در زمان بروز خطا فراخوانی میشودonComplete():تکمیل میشود فراخوانی میگردد Observable  زمانی که ارسال اطلاعات توسط تعریف Observer  Observer&lt;String&gt; animalsObserver = getAnimalsObserver();private Observer&lt;String&gt; getAnimalsObserver() {       return new Observer&lt;String&gt;() {           @Override           public void onSubscribe(Disposable d) {               Log.d(TAG, &quot;onSubscribe&quot;);           }           @Override           public void onNext(String s) {               Log.d(TAG, &quot;Name: &quot; + s);           }           @Override           public void onError(Throwable e) {               Log.e(TAG, &quot;onError: &quot; + e.getMessage());           }           @Override           public void onComplete() {               Log.d(TAG, &quot;All items are emitted!&quot;);           }       };   } حالا observer بایستی به Observable متصل شود که اصطلاحا به آن  subscribe  میگوییم. به این معنی که observer مشترک Observable شده است. مثل اشتراک روزنامه که در اون پس از انتشار روزنامه یک نسخه از اون به خواننده ارسال میشه.  animalsObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(animalsObserver);  subscribeOn(Schedulers.io())   میگه که در ترد بکگراند اجرا شود Observable به observeOn(AndroidSchedulers.mainThread())  به Observer میگه اطلاعات رو در  android UI thread دریافت کند.نتیجه برنامه: onSubscribeName: AntName: BeeName: CatName: DogName: FoxAll items are emitted!</description>
                <category>پول ریکوئست</category>
                <author>علیرضا مدنی</author>
                <pubDate>Wed, 02 Jan 2019 14:10:28 +0330</pubDate>
            </item>
                    <item>
                <title>استفاده از celery و sqalchemy با فکتوری‌های فلسک</title>
                <link>https://virgool.io/pullrequest/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-celery-%D9%88-sqalchemy-%D8%A8%D8%A7-%D9%81%DA%A9%D8%AA%D9%88%D8%B1%DB%8C%D9%87%D8%A7%DB%8C-%D9%81%D9%84%D8%B3%DA%A9-podigdbcr90u</link>
                <description>فلسک یه میکروفریم‌ورک بسیار جذاب و باحاله. مهم‌ترین چیز در موردش این هست که می‌تونی به سادگی چیزی که میخوای رو به سرعت باهاش تولید کنی، و هیچ کد اضافی‌ای داخلش نیست. همه چیز به همون شکل و فرم‌ای هست که خودت میخوای.الزامی نیست که حتما پروژه رو به فلان شکل بخصوص بسازی. متودولوژی‌ها و غیره و ذلک همه به خود شخص بستگی داره.این قضیه، هم خوبه و هم بد. خیلی بد و ترسناکه چون اگر ندونی که داری چیکار میکنی، ممکنه به صورت جد به مشکل بخوری و نتونی محصولی تولید کنی.در ادامه، شما حتما با celery  هم آشنا هستید. اگر نه، خیلی خلاصه براتون به این صورت بگم که یه پراسس هست که شما کارهایی که طول می‌کشه رو بهش میدید تا براتون انجام بده، بدون اینکه برنامتون درگیر بشه. یه مثال بعدا براتون می‌زنم که دقیقا بدونید جریان چیه و منظورمون چیه.در نهایت، sqlalchemy هم یه orm هستش که ازش استفاده می‌کنیم تا کار کردن با دیتابیس برامون راحت‌تر و به صورت ابجکت‌اورینتد باشه، امن‌تر باشه و نیاز نباشه کوری بنویسیم. خلاصه کلی کاربرد که بهتره همینجا ببندیمش و درگیرش نشیم بیشتر!نکته: این مطلب برای کسانی‌هست که با همهٔ موارد بلدن کار بکنن و فقط داره این شیوهٔ بخصوص استفاده از اونها در کنار هم رو بررسی می‌کنه و به هیچ عنوان به عنوان آموزش اولیه کاربرد نداره.اکسپورت ریپورت!برای پروژهٔ شرکت، نیاز هستش که یه سیستم ریپورتر داشته باشیم. کاربر بتونه گزارش‌های مختلف رو ببینه و دسته‌بندی کنه و چیزهای مثل اون. خوب، طبیعی هستش که یکی از نیازهایی که همراه همچین چیزی پیش میاد، امکان اکسپورت کردن(Export) یا دانلود کل گزارش در یک فایل هستش.پروژهٔ ما گاها حتی تا چند میلیون خط هم گزارش باید جنریت میکرد و کوری‌های سنگینی باید اجرا می‌شد. بنابراین ممکن بود شما بعد از کلیک روی لینک اکسپورت، مجبور باشید ۱۰-۱۲ ثانیه منتظر باشید یا حتی ممکن بود با ارور ۵۰۲ بخورید. مسئله این هست که این کار طول می‌کشه و احمقانه‌ست که کاربر رو منتظر بذاریم تا بخواد بیاد نتیجه رو ببینه.کاری که می‌کنیم این هست که دستور اکسپورت رو می‌گیریم، سریعا به کاربر اطلاع می‌دیم که افتادیم دنبال کارهاش و بعدا میتونه سر بزنه ببینه وضعیت گزارش مد نظرش چی هست یا اینکه خودمون بهش ایمیلی چیزی بزنیم و خبرش کنیم که کارش انجام شده.در عین حال، درخواست کاربر رو به سلری می‌دیم تا کار رو برامون انجام بده.امروز، بعد از حدود دو ماه از تعریف این پروژه و برنامه ریزی، تسک انجام این کار به من داده شد. نیاز بود که در بستر فلسک و sqlalchemy، من سلری رو اجرا کنم و تسک‌ها رو به سیستم بدم تا برام انجام بده.انجام تسکنترسید!نمی‌خوام خط به خط و ریز به ریز توضیح بدم که چیکار کردم! به نوشتن کلیت کاری که صورت گرفته تا پروژه به درستی کار کنه بسنده می‌کنم و امیدوارم که بتونه مشکلی از کسی حل کنه و کمی بندازتش جلو.ساختار فعلی پروژه├── deploy.py
├── __init__.py
└── project
├── application.py
├── apps
│   ├── auth
│   │   └── __init__.py
│   ├── __init__.py
│   └── report
│       ├── __init__.py
│       ├── models.py
│       └── views.py
├── config.py
├── extensions.py
    └── __init__.pyتوی چند خط خیلی سریع توضیح می‌دم که هر کدوم از این فایل‌ها چیکار میکننفایل deploy فایلی هستش که برای اجرای اپ فلسک ازش استفاده می‌کنیم. application فایلی هست که تابع‌های مورد نیاز برای ساختن یه اپ flask توش قرار داره. به قول معروف تابع &#x60;create_app&#x60; اینجاست. فایل extensions که شامل پلاگین‌هایی هستش که برای فلسک نصب کردیم. پوشهٔ apps هم شامل app هایی هستش که توی اپلیکیشن وجود دارن. بعدا در موردشون بیشتر حرف می‌زنیم. نهایتا فایلی config جایی هست که تنظیمات اپلیکیشن درش قرار دارند.این پروژه به خودی خود کار می‌کنه و مشکلی نداره.ساخت ورکر و تسک‌هااولین ادیت به فایل extensions اضافه می‌شهfrom celery import Celery  
celery = Celery() ابجکت رو خام اینجا می‌سازیم تا درگیر circular import نشیم. بعدا می‌بینید کاربردش رو.حالا باید بریم داخل فایل application و کدهای زیر رو اضافه می‌کنیمdef create_celery(app):
    celery.config_from_object(app.config)
     return celeryابجکت اینجوری کار می‌کنه که ما هر وقت می‌خوایم یه ورکر سلری رو استفاده کنیم، این تابع رو صدا می‌زنیم و ازش استفاده می‌کنیم. بنابراین میشه با یه کد، چندین اینستنس مختلف با تنظیمات مختلف رو اجرا کرد، بدون اینکه نیازی باشه کدهای ساخت سلری رو عوض کنیم.قبل از اینکه بریم فایل ورکر رو بسازیم، نیاز هست که یه سری تنظیمات برای سلری به فایل config اضافه کنیم. سلری برای کار کردنش، نیاز به یه روشی داره که بتونه پیام‌ها رو از ما بگیره و نتیجه‌اش رو بهمون برگردونه. برای اینکار باید از یه مسیج‌کیو (message queue) استفاده بشه. بعضی‌های ردیس استفاده میکنن ولی من ربیت(rabbitmq) استفاده کردم. دلیل هم این هست که توی این پروژهٔ بخصوص ما، جاهای دیگه‌ای داریم از ربیت استفاده میکنیم و از قبل نصب هست. پس میشه ازش استفاده کرد بدون مشکل.توی فایل config باید چیزی اضافه بشه. ساختار این برای من به صورت کلاس‌های مختلف برای محیط های مختلف هستش. ProductionConfig برای محیط پروداکشن، DevelopmentConfig برای وقتی که دارم دولوپ می‌کنم و... تمام این‌کلاس‌ها یه سری دیتای مشترک با هم دارن که توی یه کلاس به اسم DefaultConfigs قرار دارن و کلاس‌های کانفیگ پروداکشن، دولوپ، تست و ... از اون ارث‌بردن. بنابراین با خیال راحت تنظیمات رو توی همین کلاس دیفالت اضافه میکنم که همه داشته باشنشون. class DefaultConfigs: 
    ...
    CELERY_BROKER_URL = &quot;amqp://localhost/&quot;
    CELERY_RESULT_BACKEND = &quot;rpc://&quot;تو خط اول، داریم ادرس بروکر رو بهش میدیم که اینجا ربیت‌هستش و دومین، ادرس جایی هست که نتیجه عملیات‌ها توش ذخیره خواهند شد! میتونه یه دیتابیس باشه یا هرچیزی. اینجا من فعلا درگیر این قضیه نمی‌شم.حالا باید بریم یه ورکر بسازیم که بتونه اجرا بشه.توی دایرکتوری روت پروژه و کنار deploy ام یه فایل درست می‌کنم به اسم worker.py و کدهای زیر رو داخلش می‌نویسم:from project.application import create_app, create_celery
from project.config import ProductionConfig

app = create_app(ProductionConfig)
celery = create_celery(app)

with app.app_context():
    celery.start()توی خط اول، داریم توابع فکتوری رو ایمپورت می‌کنیم. این توابع رو نیاز داریم تا بتونیم ابجکت‌های سلری و فلسک رو باهاشون بسازیم.خط دوم داریم کانفیگ پروداکشن رو ایمپورت می‌کنیم. دو خط بعدی دارن با استفاده از توابع فکتوری، دوتا ابجکت فلسک و سلری می‌سازن. می‌بینید که کاملا توابع می‌تونن به سادگی و فقط با پاس دادن یه کانفیگ دیگه، یا اپلیکیشن دیگه، کاملا رفتارشون عوض بشه و این زیبایی استفاده از فکتوری‌هاست.در نهایت، چون میخوایم از دیتابیس‌ها استفاده کنیم، و دیتابیس ها توی کانتکست فلسک فقط درست کار میکنن، تابع celery.start رو داخل کانتکست فلسک‌مون اجرا می‌کنیم.خوب، تبریک می‌گم! مراحل راه‌اندازی سلری و فلسک به پایان رسید. شما می‌تونید برید و تسک‌هاتون رو اضافه کنین.من توی پوشه project ام، یه دایرکتوری دارم به اسم utils که داخلش کدهایی که استفاده می‌کنم هستن. یه فایل tasks.py توی این پوشه درست میکنم و کدهای زیر رو توش می‌نویسمfrom project.extensions import db, celeryهمینجور که می‌بینید، رفتم از فایل extensions ابجکت سلری و دیتابیس رو ایمپورت کردم. (ابجکت db هم دقیقا به همون شکلی اونجا ساخته شده که ابجکت سلری ساخته شده)حالا، باید مدل‌هام رو از داخل اپ‌هام ایمپورت کنم به داخل این فایل تا بتونم استفاده‌شون بکنمfrom project.extensions import db, celery
from project.apps.report.models import Namesحالا باید کدها و توابع‌ام رو بنویسم.@celery.task(name=&quot;cl_get_names&quot;)
def get_names():
    names = Names.query.all()
    return [x.name for x in names]در کد بالا، با یه دکوریتور یه تسک درست می‌کنیم. ارگویمنت name برای این هست که سلری با این اسم بشناستش و اگر احتمالا توی کد‌هام جای دیگه‌ای همین تابع رو استفاده کرده بودم، قاطی پاتی نشه چیزا. همچنین من سعی می‌کنم اسمی که برای تسک‌هام میذارم از بیرون قابل فهم باشه. مثلا دو حرف اول اسم تسک رو از اسم اپ می‌گیرم(اینجا من cl برای سلری رو گذاشتم) و بعد اسم خود تسک.در نهایت ادامه کد‌ها کاملا عادی هستند.خوب، حالا باید از یه طریقی به سلری(ورکر) بفهمونیم که باید این تسک‌ها رو ارائه بکنه برای کاربرانمون.(بنابراین شما می‌تونین برای کاربردهای مختلف، ورکر های مختلفی رو با همون یک کد اجرا کنین و در نهایت نیازی هم به تغییر کدهاتون ندارید.)من دوباره به فایل ورکر(worker) بر می‌گردیم تا بهش بگم چه تسک‌هایی رو باید برام اجرا کنه.from project.application import create_app, create_celery
from project.config import ProductionConfig
from project.utils.tasks import (get_names,  )  # we added this!

app = create_app(ProductionConfig)
celery = create_celery(app)

with app.app_context():
   celery.start()بدون تغییر در چیزی، فقط اسم تابع‌ای که تسک‌هام داخلش هست رو ایمپورت می‌کنم. اونها رو توی پرانتز می‌ذارم چون ممکنه کلی تسک داشته باشم و خوب، قائدتا میخوایم بریم توی چند خط احتمالا بنابراین از همین اول پرانتز رو می‌ذارم.حالا یه تب جدید توی ترمینال باز می‌کنم(پیشنهاد میکنم از چیزی مثل tmux استفاده کنین این‌تیپ مواقع که چند تا سرور رو میخواید اجرا کنین) و با کد پایین ورکر‌ام رو اجرا می‌کنم.celery -A worker:celery worker --loglevel=infoتوی فایل worker کلی ابجکت و اینا ساختم، یکیش app که مربوط به فلسک هست. بنابراین با استایل بخصوص worker:celery به سلری می‌فهمونم که از فایل ورکر، ابجکت سلری رو بخونه و استفاده کنه. در نهایت لاگ‌لول رو هم اینفو میذارم تا بتونم ببینم چی داره میشه.استفاده از تسک‌هابرای استفاده از این تسک‌ها، جایی که میخوام ازشون استفاده کنم مثل یه تابع معمولی اونها رو باید ایمپورت کنم. برای مثال، من میخوام لیست کل نام‌ها رو بگیرم توی فایل  views.py ام، بنابراین به این صورت تسک‌رو ایمپورت می‌کنم:from project.utils.tasks import (get_names)

@app.route(&quot;/get_names/&quot;)
def get_names_view():
    get_names.delay()
    return &quot;request sent to celery&quot; همینجور که می‌بینید استفاده کاملا شبیه به یه تابع معمولی هست، با این تفاوت که تابع نیست و یه ابجکت هست. ما میتونیم مثل تابع معمولی صداش کنیم(()get_names) و نتیجه بگیریم، ولی نکته این هست که اینجا دیگه سلری کار رو انجام نداده. برای اینکه تاکید کنیم که سلری تابع رو اجرا کنه، باید ابجکت مد نظر رو با تابع delay اش صدا بزنیم. که توی کد می‌بینید.همچنین در صورتی که ارگومانی باشه که بخوایم پاس‌بدیم خیلی عادی برای همین تابع delay می‌فرستیمش.اجرای اپ فلسک کاملا طبیعی هستش و مثل همیشه که اجرا می‌کنید!این شیوه از اجرا و استفاده از استک سلری، فلسک و  سیکول‌الچمی(sqlalchemy) معمولا زیاد استفاده نمی‌شه ولی با توجه به ساختار بخصوص پروژه ما، شیوه اجرا به این صورت بودش.متن بسیار طولانی هست، ولی انجام دادنش کمتر از ۱۰ دقیقه طول می‌کشه و واقعا راه‌اندازی تسک‌ها با سلری راحت و سریع هستش.بعد از این چی؟بعد از این، باید نتیجه رو یه جوری به کاربر نشون بدین! این بخش از داستان کار راحتیه و بعید می‌دونم مشکلی باشه براش، آموزش‌های زیادی هم براش وجود داره پس من متن رو همینجا کوتاه می‌کنم دیگه!موفق باشید.</description>
                <category>پول ریکوئست</category>
                <author>Maysam senaps</author>
                <pubDate>Wed, 02 Jan 2019 10:54:11 +0330</pubDate>
            </item>
                    <item>
                <title>مقدمه‌ای بر ایونت‌ها در لاراول</title>
                <link>https://virgool.io/pullrequest/%D9%85%D9%82%D8%AF%D9%85%D9%87%D8%A7%DB%8C-%D8%A8%D8%B1-%D8%A7%DB%8C%D9%88%D9%86%D8%AA%D9%87%D8%A7-%D8%AF%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-tyytrwhrnggj</link>
                <description>یکی از امکانات خوبی که لاراول بعنوان یک فریمورک خفن در اختیار شما برنامه نویسان دون‌پایه قرار میده، امکان تعریف ایونت هست. ایونتها کلاسهای ساده ای هستند که با یک تعداد Listener (شنونده؟ گوش به زنگ؟) کار میکنن. ایونتها به شما کمک میکنن تا اکشنهای مختلف یک ماموریت خاص در یک پروژه رو از هم جدا کنید. یعنی چی؟ یعنی یک کاربر میاد و در اپلیکیشن شما ثبت نام میکنه، شما میخواهید یک ایمیل یا یک پیامک حاوی کد تایید بفرستید. تعریف ایونت به شما کمک میکنه که بتونید این عمل ارسال اس ام اس یا ایمیل رو از ماموریت اصلی اون تکه کد که همانا ثبت نام کاربره جدا کنید. در واقع شما یک ایونت به نام &quot;ثبت نام کاربر&quot; تعریف میکنید که کارش این است که اطلاعات اولیه کاربر رو در دیتابیس ذخیره کنه و منتظر تایید هویت اون آدم از طریق مثلا ارسال اس ام اس باشه. یک Listener هم برای این ایونت تعریف میکنید و بهش میگید آقا جان شما گوش به زنگ باش که هر موقع رویداد ثبت نام کاربر در سیستم اتفاق افتاد، براش یه کد رندوم جنریت کنی و بفرستی.حالا شاید بپرسید خب کد ارسال sms رو همونجا بیخ کد ثبت نام مینویسیم دیگه چه کاریه. اولا که بیاید سعی کنیم یک متد بیش از یک کار رو در اون لحظه انجام نده، چون کثیف کاری میشه، همه‌ی ما هم تجربه ی کد کثیف نوشتن رو داریم. دوما فرض کنید شما یک رویدادی در سیستم دارید که وقتی اتفاق میفته باید چندین پیامد دیگه داشته باشه. مثلا شما در یک فروشگاه اینترنتی یک سفارشی رو تایید میکنید و بنابراین باید برای کاربر ایمیل، اس ام اس و نوتیفکیشن ارسال بشه. طبیعتا هر کدام از اینها پیاده سازی منحصر به فرد خودشون رو دارن و منطقی نیست همه چیز رو بیخ هم قرار بدیم. پس بهتره یک ایونت تایید سفارش ایجاد کنیم و چند listener مختلف براش تعریف کنیم تا هر کدوم گوش به زنگ باشن که اگر رویداد تایید سفارش اتفاق افتاد، هر کدوم ماموریت خودشون رو انجام بدن.پس اگر تا اینجا موافقید که استفاده از ایونت ها کار خوبیه، در ادامه نگاهی بندازیم به اینکه چطور باید ایونت ها رو ثبت کرد.برای ثبت ایونتها و Listener ها، شما باید فایل EventServiceProvider.php رو از مسیر app/Providers باز کنید و اونجا در داخل آرایه‌ی listen$ مسیر تمام ایونت ها و listener های خودتون رو بنویسید:protected $listen = [
        &#039;App\Events\OrderApproved&#039; =&gt; [
                &#039;App\Listeners\SendOrderApprovementNotifications&#039;,
         ],
];شما میتونید هر چند تا ایونت و به ازای هر کدوم هر چند تا listener که خواستید رو بالا در قالب آرایه تعریف کنید. حالا باید دستور پایین رو در ترمینال و در مسیر اصلی پروژه بزنید تا دایرکتوری های ایونت ها و listener ها ساخته بشن و همینطور فایلها ایجاد و به این دایرکتوریها اضافه بشن. توضیح اینکه لاراول به صورت پیشفرض دایرکتوری های Events و Listeners رو نداره و بعد از اجرای دستور زیر اونها ایجاد میشن:➜  ~ php artisan event:generateموقعی که دستور بالا رو اجرا کنید، فایل OrderApproved.php در مسیر app/Events و فایل SendOrderApprovementNotifications.php در مسیر app/Listeners ایجاد میشن. اگر فایلها رو باز کنید و نگاهی به داخلشون بندازید میبینید که لاراول زحمت شما رو کم کرده و namespace ها و کلاسهای مرتبط رو خودش برای شما تولید کرده. بدون درد و خونریزی.هر بار که دستور فوق رو در ترمینال اجرا کنید، لاراول یه نگاه به فایل EventServiceProvider.php میندازه. اگر ایونت یا listener جدیدی به آرایه اضافه شده باشه اونها رو برای شما ایجاد میکنه.فایل ایونت: &lt;?php 

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class OrderApproved
{
        use Dispatchable, InteractsWithSockets, SerializesModels;
        /**
        * Create a new event instance.
        *
        * @return void
        */
        public function __construct()
        {
                //
        }
        /**
        * Get the channels the event should broadcast on.
        *
        * @return \Illuminate\Broadcasting\Channel|array
        */
        public function broadcastOn()
        {
                return new PrivateChannel(&#039;channel-name&#039;);
        }
}فایل Listener:&lt;?php

namespace App\Listeners;

use App\Events\OrderApproved;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendOrderApprovementNotifications
{
        /**
        * Create the event listener.
        *       
        * @return void
        */
        public function __construct()
        {
                //
        }
        /**
        * Handle the event.
        *
        * @param  OrderApproved  $event
        * @return void
        */
        public function handle(OrderApproved $event)
        {
               //
        }
}برای اینکه یک ایونت رو هم به همراه listener هاش اجرا کنید کافیه یک خط کد بنویسید در هر جایی که نیازی به اجرای ایونت داشتید:event(new OrderApproved());در مقاله‌ی بعدی در مورد صف ها و اینکه چطور میتونید ایونتهاتون رو برای اجرا در زمانهای مشخص صف بندی کنید خواهم نوشت.</description>
                <category>پول ریکوئست</category>
                <author>مهدی علیپور</author>
                <pubDate>Sat, 29 Dec 2018 23:44:10 +0330</pubDate>
            </item>
                    <item>
                <title>ورود با حساب‌کاربری گوگل در اندروید</title>
                <link>https://virgool.io/pullrequest/google-sign-in-android-llrhgbahtas5</link>
                <description>قرار بود که بعد از منتشر شدن سرویس مهادل، در مورد ویژگی‌های اپ اندروید اون باز هم در ویرگول بنویسم. هدف اصلی و اولیه این سرویس هم، آموزش اندروید بود. ابتدا می‌تونید داستان ایجاد سرویس مهادل رو مطالعه کنید: https://virgool.io/@bkhezry/skill-swap-mahadel-qs9xzbyg275y خوبی ورود با حساب‌کاربری گوگل اینه که دیگه روند دریافت ایمیل، رمزعبور و تایید ایمیل رو حذف می‌کنه. از لحاظ امنیت هم چون رمزی از کاربر دریافت نمیشه، امنیت حساب‌کاربری شخص در اپلیکیشن، در گرو امنیت جیمیل اون است.عکس از گوگلگوگل برای ورود با حساب‌کاربری خودش دو راه حل در اندروید ارائه داده. یکی از اون‌ها قدیمی است و از ابتدای ایجاد این امکان، وجود داشته. این ورود با استفاده از Google play service و کتابخانه  play-services-auth انجام میشه. لینک راهنماروش دیگه استفاده از Firebase و کتابخانه firebase-auth است که به نسبت روش اول جدیدتره. امکانات این کتابخانه بسیار از روش اول بیشتره. با استفاده از اون می‌تونید نه تنها با حساب‌کاربری گوگل ثبت‌نام کنید بلکه امکان استفاده از شبکه‌های اجتماعی دیگه همچون فیس‌بوک، توییتر و گیت‌هاب رو هم فراهم کرده. لینک راهنمامتاسفانه از ابتدای به وجود اومدن این امکان در Firebase ایران در لیست تحریم اون قرار داره و عملا نمیشه برای اپلیکیشن‌هایی که در ایران استفاده میشن از اون استفاده کرد #فناتحریمبرای استفاده از روش اول ابتدا به سایت توسعه گوگل مراجعه کنید و یک پروژه جدید برای اپلیکیشنتون ایجاد کنیدسایت توسعه گوگلبرای مثال من پروژه Skill Swap رو ایجاد کردم.برای استفاده از امکان ورود با حساب‌کاربری گوگل نیاز به Client ID دارید که گوگل به شما میده. برای این‌کار گزینه Credentials رو بزنید. در صفحه جدید بر روی Create credentials کلیک کنید و گزینه OAuth client ID رو انتخاب کنید. در صفحه جدید باز شده، شما باید دو مقدار Client ID ایجاد کنید. یکی Web application نام داره که مقدار اون در تولید توکن در اپ اندروید و اعتبارسنجی مقدار اون در بک‌اند استفاده میشه. مقدار دوم، اپلیکیشن شما رو با استفاده از SHA1 مربوط به کلید ساین و package name اون گوگل معرفی می‌کنه. چنانچه چندین کلید برای ساین اپلیکیشن دارید به ازای هر کدام باید یکClient ID ایجاد کنید. با این کار فقط اپلیکیشنی که در این بخش مقدار مربوط به SHA1 و package name اون ثبت شده باشه توکن اون معتبر خواهد بود.Client ID ایجاد شدهالان مقدار Client ID مربوط به نوع Web application رو کپی کنید تا در پروژه اندروید استفاده بشه. کد کامل بخش ورود با حساب‌کاربری گوگل در این لینک قابل دسترس است.  https://gist.github.com/bkhezry/497c926fd517ad625d8a5103437d4042 مقدار Client ID گوگل رو با استفاده از تابع requestIdToken به GoogleSignInOptions ارسال کنید. با اضافه کردن این بخش، یک توکن با توجه به مقدار اون، توسط سرور گوگل برای شما ارسال می‌شه.  https://gist.github.com/bkhezry/8d461c54de067327ef8376bd1261b76c مقدار توکن با استفاده از تابع getIdToken از شی GoogleSignInAccount دریافت میشه. اطلاعات این توکن برای حساب‌کاربری من در عکس پایین نمایش داده شده‌توکن ارسالی از سمت گوگلاین توکن برای یک ساعت معتبر بوده و شامل نام، نام‌خانوادگی، ایمیل و لینک عکس پروفایل شخص است. این مقدار باید توسط یک ارتباط امن https به سرور برای اعتبارسنجی و ذخیره ارسال بشه.تا اینجا برنامه اندروید به گوگل وصل شده و توکن حساب‌کاربری انتخاب شده رو دریافت و به سرور ارسال کرده. نکات این بخش:به ازای هر کلید ساین اپلیکیشن، باید یک Client ID تولید و مقادیر SHA1 و package name رو براش ثبت کنیمبرای درخواست و اعتبارسنجی توکن باید از مقدار Client ID مربوط به نوع Web application استفاده کنیمحتما از ارتباط امن با سرور برای ارسال توکن استفاده کنیمبرای اعتبارسنجی توکن ارسال شده به سرور می‌تونید از کتابخانه‌های آماده مربوط به زبان بک‌اند خودتون استفاده کنید. تعدادی از اون‌ها شامل پی‌اچ‌پی، پایتون و ... در این لینک در دسترس است. دقت کنید که مقدار Client ID درخواستی سرور هم باید با مقدار ثبت شده در اپلیکیشن اندروید یکی باشد یعنی همان مقدار نوع Web application.</description>
                <category>پول ریکوئست</category>
                <author>بهروز خضری</author>
                <pubDate>Sat, 29 Dec 2018 21:11:49 +0330</pubDate>
            </item>
                    <item>
                <title>ساخت Application های Cross platform با React native</title>
                <link>https://virgool.io/pullrequest/%D8%B3%D8%A7%D8%AE%D8%AA-application-%D9%87%D8%A7%DB%8C-cross-platform-%D8%A8%D8%A7-react-native-sw4g14j2tcix</link>
                <description>سرعت زندگی تو دنیای امروز خیلی سریع شده جوری که اگر تو کاری که داری انجام می دی اگر کمی تنبلی کنید دیگری جای شما رو پر میکنه.تو دنیای تولید نرم افزار هم همین طوره یا سریع develop کن یا جایگزین شو!تو این نوشته سعی دارم بهتون نحوه ساخت برنامه های چند پلتفرمی یا (Cross platform) رو با استفاده از React Native توضیح بدم، در ادامه با ما باشید.قبل از شروع چند تا از ابزارهایی که برای ادامه کار نیاز داریم رو معرفی کنم:NodeJSیک برنامه run-time چند پلتفرمی برای توسعه نرم افزار های جاوااسکریپت که معمولا به عنوان یک سرور جاوااسکریپتی مورد استفاده قرار میگیرد. که برای دانلود و نصب می توانید وارد وب سایت شده و با توجه به سیستم عامل خود پکیج را دانلود کنید و روی سیستم نصب کنید.NPMیک package manager برای جاوااسکریپت هست که به صورت پیش فرض توسط NodeJS برروی سیستم شما نصب می شود.JDKبه این نکته توجه کنید که بعد از نصب JDK حتما JAVA_HOME را در سیستم خود تنظیم کنید.SDKبعد از دانلود SDK شما باید متغیرهای را برروی سیستم خود تنظیم کنید، برای این کار می توانید از این لینک کمک بگیرید.بعد از نصب ابزار های بالا شما باید React Native را یک بار بر روی سیستم نصب کنید برای این کار از دستور زیر در ترمینال استفاده کنید:خوب تا این جا ابزار اصلی برای شروع کارمون نصب کردیم،‌حالا توی مسیری که قرار هست برنامه ساخته بشه دستور زیر رو وارد کنید:که PROJECT_NAME اسم پروژه ای هست که قرار هست ساخته بشه!برای اجرای برنامه یک AVD ساخته و انرا اجرا کنید. سپس در مسیری که پروژه ساخته شده دستور زیر را وارد نمایید:این دستور برنامه‌ی sample روی AVD اجرا می کند.خوب برنامه اندروید اجرا شد، برای اجرای نسخه وب با ما همراه باشید.وارد ترمینال شوید و دستور زیر را وارد کنید:وما از babel برای ترجمه کد های react-native به زبان اجرایی برای مرورگر استفاده می کنیم، و از webpack نیز برای bundle کردن و اجرای پروژه استفاده میکنیم.بعد از اجرای دستورات بالا فایل package.json شما باید به شکل زیر باشد:در قسمت script یک دستور با نام serve نوشته ام که در هنگام اجرای پروژه به جای این که به مسیر web pack برای اجرای پروژه رجوع کنم از این دستور برای اجرای پروژه استفاده می کنم.حال فایل های زیر را در root پروژه خود بسازید:index.htmlو index.web.jsو webpack.config.jsبرای اجرای در ترمینال دستور زیر را وارد کنید:بعد از اجرای دستور بالا و اجرا شدن برنامه بدون هیچ مشکلی وارد مرورگر شوید و ادرس localhost:3000  را وارد کنید تا web app شما اجرا شود. https://www.aparat.com/v/zELhH </description>
                <category>پول ریکوئست</category>
                <author>امیرحسین بیات</author>
                <pubDate>Fri, 28 Dec 2018 00:33:35 +0330</pubDate>
            </item>
                    <item>
                <title>تایم‌زون</title>
                <link>https://virgool.io/pullrequest/%D8%AA%D8%A7%DB%8C%D9%85%D8%B2%D9%88%D9%86-vbbmxrky9dhp</link>
                <description>یک روز سرد پاییزی در ساعت پنج عصر، یک سری دانشمند که مشغول یادداشت کردن تغییرات روز و شب بودند به یک نکته عجیب برخوردند. این سوال برایشان پیش آمده بود که چرا تا همین شش ماه پیش ساعت پنج عصر هوا آفتابی بود و خورشید در آسمان می‌تابید، اما الان هوا تاریک است؟بعد از اینکه این مورد را با دانشمند بزرگ در میان گذاشتند، دانشمند بزرگ تمام شاگردها را از اتاقش به بیرون پرتاب کرد و به دیوار خیره شد. داشت به این فکر می‌کرد که کشف قبلیش که سعی داشت این مشکل را حل کند، چرا کار نمی‌کند؟ رفت دفترچه کشفیات خودش را باز کرد و شروع کرد به خواندن داستان کشف قبلیش که  Timezone نام داشت:طی تماس اسکایپی که با خاله‌ام در برلین داشته‌م متوجه شدم اینجا هوا تاریک است و آنجا هنوز هوا تاریک نشده و خورشید در آسمانشان است. منطقی هم هست، کره زمین گرد است و می‌چرخد و خورشید آن طرف‌تر ثابت ایستاده. خب اگر این طرف زمین آفتاب وسط آسمان باشد، آن طرف دیگر باید نصفه شب باشد دیگر. ولی آیا منطقی است که تعریف ما از ساعت متفاوت باشد؟ من یک عمر در اینجا به شاگردانم دستور داده‌ام که ساعت ۹ صبح بیدار شوند و برای من نان سنگک تازه بخرند، اما اگر با یکی از این کودن‌ها به آنجا برویم ساعت نه هوا مثل سگ تاریک است و هنوز نانوایی‌ها باز نکرده‌اند. نمی‌شود که هر بار به جایی می‌رویم ساعت کاری‌مان به هم بخورد.وزن خود را از باسن چپش به باسن راست انداخت و ادامه داد:برای حل این موضوع به راه‌حلی رسیدم که می‌تواند این مشکل را حل کند. این که آن وسط کره زمین که اسمش گرینویچ است و همیشه ساعت دوازده ظهر هوا روشن و دوازده شب هوا تاریک است را ساعت مبدا کره زمین در نظر بگیریم و ما که اینور با فاصله از مبدا قرار داریم، ساعت روزمره‌مان را با یک فاصله ثابت از آن ساعت مبدا در نظر بگیریم. خاله‌ام در آلمان هم همین وضعیت، منتها یک فاصله زمانی متفاوت. اینطوری این کودن‌ها هر قبرستانی که بروند باز هم باید ساعت نه صبح بیدار شوند و بروند نان سنگک تازه بخرند. با کمک خط‌کش و یک کره زمین پلاستیکی و یک ساعت مچی، اکنون به دستاورد بزرگی رسیده‌ام. لیستی که فاصله‌زمانی یا همان تایم‌زون کشورهای مختلف کره زمین در آن محاسبه شده و باعث می‌شود همه جای دنیا ساعت شش صبح نانوایی‌ها باز باشند:https://www.timeanddate.com/time/mapکمی خود را خواراند و دوباره به فکر فرو رفت. درست است که زمین گرد است و هر ۲۴ ساعت ما باید انتظار موقعیت ثابتی از خورشید در معرض دیدمان داشته باشیم، اما مسخره بازی کره زمین با چرخش به دور خودش باعث می‌شود این معادله دقیق نباشد. یعنی اگر امروز ساعت ۱۲ ظهر خورشید را از در فلان زاویه ببینیم، فردا همین ساعت خورشید کمی آن‌ورتر رفته است. اما اگر قرار بود هر روز این زاویه بیشتر شود، پس چرا آن کشف قبلی هنوز داشت کار می‌کرد؟ چون تایمر خداوند سر سال ریست می‌شود. یعنی درست است که هر روز زاویه تغییر می‌کند، اما میزان زاویه خورشید با یک سال بعد، اما همان روز یکی است.کمی بیشتر خود را خواراند و با خود گفت چه می‌شود اگر ما هر شش ماه یک‌بار بیاییم ساعت روزمره را بکشیم جلو، بعد دوباره سر سال بکشیمش سر جایش تا اون زاویه‌هه دقیق‌تر شود نسبت به انتظار ما. نه که همه چیز را حل کند، اما حداقل می‌دانیم که این باعث می‌شود ساعت نانوایی‌ها کمتر به هم بخورد.سپس بدون فکر به اینکه اینکار باعث می‌شود همه مردم دنیا سالی دوبار علاف این مسخره بازی شوند و بدون در نظر گرفتن تاثیر ناچیز آن روی یک مشکل حل نشدنی، یکی از شاگردها را صدا کرد و به او گفت این تغییر را هم بفرستد برود روی پروداکشن.پنجاه سال بعد، درست سال قبل از فوت این دانشمند بزرگ مراسم تجلیلی از او برگزار شد. در میانه مراسم یک جلسه پرسش و پاسخ برگزار شد که او بخاطر کهولت سن، کنار رفته و شاگردانش این قضیه را هندل می‌کردند. سوال‌ها به ترتیب پاسخ داده می‌شدند که نوبت به یکی از حضار رسید. او پرسید:ما به تازگی یک اپلیکیشن خدمات پرداخت موبایلی نوشته‌ایم و در آن اطلاعات پرداخت‌های مشتریانمان را سیو می‌کنیم. این اطلاعات شامل  یک سری چیزها به همراه تاریخ و زمان تراکنش آنها است. ما خیلی نکته‌سنج بودیم و از ساعت گرینویچ بجای ساعت محلی استفاده کردیم تا مشکل فاصله زمانی نداشته باشیم، اما اگر به پاییز برسیم و ساعت‌ها تکان بخورند، باید ساعت‌های قبلی را آپدیت کنیم؟ ما می‌خواهیم بتوانیم به اطلاعات قبلی اعتماد کنیم...دانشمند بزرگ که به این سوال گوش می‌کرد، عرق سرد از پیشانی‌اش جاری شد و نیمی از موهایش ریخت. رو به آن شاگردش که نامه عقب جلو کردن ساعت را نوشته بود کرد و با تمام توان یک پس‌گردنی محکم به او زد. شاگرد که علت را جویا شد، توضیح داد:پدرسگ من این همه محاسبات کرده‌ام تا به این دستاورد برسم، آن‌وقت تو به این شکل اشتباه به مردم توضیحش داده‌ای؟ من کی گفته‌ام ساعت گرینویچ عقب جلو می‌شود؟ من گفتم ساعت محلی. یعنی همین ماسماسکی که به دیوار آویزان می‌کنیم. یعنی اینکه مثلا آن مثبت سه و نیم که در ایران به ساعت گرینویچ اضافه می‌شود تا ساعتش بدست بیاید، در بهار بشود مثبت چهار و نیم تا آن قضیه نانوایی حل شود. آن‌وقت توی گوساله برداشته‌ای به مردم گفته‌ای که گرینویچ را جابه‌جا کنند؟شاگرد که سرش را پائین گرفته بود، از اعماق دلش ابراز پشیمانی کرد و قول داد دیگر این اشتباه را نکند. سپس دانشمند خطاب به پرسنده سوال قضیه را توضیح داد و از آن شاگرد خواست تا به کل دنیا و مخصوصا برنامه‌نویس‌های برنامه‌های خدمات پرداختی نامه جدیدی ایمیل کند تا آن‌ها نیز متوجه بشوند که ساعت عقب جلو نمی‌شود، بلکه فاصله فقط زمانی تکان می‌خورد.</description>
                <category>پول ریکوئست</category>
                <author>امیر مومنیان</author>
                <pubDate>Sun, 23 Dec 2018 15:56:13 +0330</pubDate>
            </item>
                    <item>
                <title>گفته‌های آرش خانگلدی از اسپاتیفای</title>
                <link>https://virgool.io/pullrequest/%DA%AF%D9%81%D8%AA%D9%87%D9%87%D8%A7%DB%8C-%D8%A2%D8%B1%D8%B4-%D8%AE%D8%A7%D9%86%DA%AF%D9%84%D8%AF%DB%8C-%D8%A7%D8%B2-%D8%A7%D8%B3%D9%BE%D8%A7%D8%AA%DB%8C%D9%81%D8%A7%DB%8C-sravc7z452ti</link>
                <description>خوندن صحبت کسایی که توی شرکت‌های بزرگ دنیا کار کردند به ما کمک میکنه که بدونیم ساز و کار این شرکت‌ها چطوریه، بر اساس چه اصولی مدیریت میشن و پروژه‌هاشون رو چطوری جلو میبرند.از اینجور مصاحبه‌ها تو اینترنت پیدا میشه ولی این یکی فرق داره؛ چون صحبت‌های یه ایرانی هست که مدت‌ها تو ایران بوده و علاوه بر موارد بالا، در مورد نحوه‌ی مهاجرت و تفاوت‌های اونجا با ایران هم صحبت کرده.پیشنهاد میکنم صحبت‌های آرش رو که خیلی با دقت گفته شده و جزئیات زیادی از شرکت اسپاتیفای داره رو بخونید.پیشگفتاراین صحبت‌ها در ۲ جلسه به صورت video conference و یک جلسه حضوری از پاییز ۹۶ تا بهار ۹۷ انجام شده و بعدها قسمت‌هایی برای انسجام مطلب جابه‌جا شده یا بهش اضافه شده. امیدواریم براتون مفید باشه.همینطور تا امروز آرش برامون ۲ جلسه در مورد SOLID و Test ارائه داشته که میتونید ببینید و پیشنهاد میدم چنل YouTube یا آپارات یا تلگرام ما رو دنبال کنید تا از ارائه‌های بعدیش هم با خبر شید.معرفیسلامبرای اونایی که منو نمیشناسن یه معرفی کوچیک کنم.من آرشم، از سال ۹۱-۹۲ اندروید کار میکنم. قبل از این تاریخ توی شرکتی به عنوان وب دولوپر کار میکردم. تازه اندروید اومد بود، یه خورده جذبش شدم و sdk رو دانلود کردم، با Eclipse شروع به کار کردم. یه اپ برای شرکت نوشتم و بعد دوره‌ی اول استارتاپ ویکند رو شرکت کردم. بعد اون یه جورایی به سمت اندروید کشیده شدم. اندروید دولوپر خیلی کم بود، یه سری پروژه سمتم اومد و انجام دادم. کارایی که انجام دادم و معروف بودن بیسفون و نواک بودند. این دو تا پروژه‌‌هایی بود که حداقل یکی دو سال داخلشون بودم. چندتا پروژه کوچیک و بزرگ دیگه هم در مجموع انجام دادم.از ۵ ماه و خورده‌ای پیش با نقش Android engineer اومدم اسپاتیفای. اینجا کلا senior یا junior خیلی معنی نداره، متناسب با contribution که به سورس کد میکنی و متناسب با مسئولیت‌هایی که به عهده میگیری حقوقت بالاتر و پایینتر میاد. کسی senior نیست و مفهوم جالبی هم هست. خیلی از این موضوع خوشم اومد. چون با این بحث در ایران که کی senior یا junior هست موافق نبودم. از نظر خودم ما ها هممون تا آخر عمرمون چیز برای یادگیری داریم و خیلی دوست نداشتم بگم من این عنوان رو دارم یا ندارم.بیشتر توضیح میدی چطور حقوق مشخص میشه؟جلوتر بیشتر توضیح میدم ولی مثلا بستگی به این داره که روی یه سورس کد contribute کنی یا چند تا، بیشتر بخوام توضیح بدم ما تیمی داریم به اسم core، تیم core با ++c یه سری فیچرهای core اسپاتیفای رو میزنن که توی همه اپ‌های وب، ios، اندروید و ماشین مشترک هست. اینا هر روز صبح باید شبیه تیم‌های دیگه feature release بدن و بگن این کد امروز ما هست. هر موقع core آپدیت میشد، ما هم باید gradle رو آپدیت میکردیم. آپدیت گریدل ۲۰ دقیقه طول میکشید و همه شرکت باید منتظر میموند. اومدن اپ رو modularize کردن و الان اپ اندروید ماژول‌های مختلفی داره. اینکه شما توی چند ماژول contribute کنی به عنوان یه اندروید engineer یا حتی بک‌اند یاد بگیری و به بک‌اند دولوپرها کمک کنی، پروژه‌ی خودتو بک‌اندشو بزنی، Machine Learning یاد بگیری یا Data Science و … متناسب با این مشخص میشه تو چه stepای هستی. step یه مقیاسی توی شرکت اسپاتیفای هست که مشخص میکنه آیا فقط تنها به خودت و پروژه‌ی خودت کمک میکنی؟ آیا داری به کل تیمت کمک میکنی (Squad)؟ آیا داری به اعضای چندتا تیمی که با هم به دنبال یک هدف مشترکید کمک میکنی (Tribe)؟ آیا داری به کل شرکت کمک میکنی؟ متناسب با این مشخص میشه stepت چیه، حقوقت متناسب با اون شکل میگیره و به قولی اون درجه‌ی سنیوریتی اونجا مشخص میشه. منتها هرچقدر step بره بالاتر مسئولیت‌هات بیشتر میشه و طبیعتا باید تجربه‌ی این کار رو هم داشته باشی. اگر میخوای یه step بری بالاتر باید خودتو واقعا ثابت کرده باشی.رفتن به اسپاتیفایسعی میکنم خلاصه بگم که چی شد اومدم اسپاتیفای.نواک یه اپ music streaming هست و کاری که میکنه خیلی خیلی شبیه اسپاتیفای هست. در واقع خیلی جاها ما از اسپاتیفای الهام میگرفتیم، چون اسپاتیفای واقعا جزء شرکت‌های پیشرو توی این زمینه هست. منم اونجا اندروید انجینیر بودم.یکی از Recruiterهای اسپاتیفای با من تماس گرفت و گفت اگر دوست داشته باشی میتونیم با هم کار کنیم. منم با خانومم صحبت کردم و گفتم که همچین موقعیتی هست. ما تو فکرمون بود که از ایران بریم ولی جای خاصی توی ذهنمون نبود، بیشتر بصورت ایده و کلی بود.۴ مرحله مختلف ازم تست گرفتند. ۳ تاش تو ایران و ریموت بود. و آخریش که خیلی طولانی بود دعوتم کردن دفتر مرکزی در استکهلم، on-site interview که باید بری توی دفتر و اونجا باهات صحبت میکنن. از تست‌ها اونایی که توی ایران بود کاملا فنی و کد نویسی بود.اولیش یه تست چهار جوابی خیلی سریع بود. اگر اشتباه نکنم چیزی نزدیک به بیست سوال رو باید خیلی سریع جواب میدادی، اینقدر سریع که فرصت نمیکردی آنلاین چیزی رو چک کنی، یا باید ولش میکردی و جواب نمیدادی یا غلط جواب میدادی که بدتر بود. هر مرحله بهت ایمیل میزنن که توی این مرحله قبول شدی یا نه.مرحله بعدی یکی از انجینیرهای اسپاتیفای یه سری سوال فنی ازم پرسید. مثلا در مورد بیس اندروید، چقدر با اکتیویتی‌ها آشنایی؟ چقدر با کامپوننت‌ها آشنایی؟ از چه ابزارهایی استفاده میکنی؟ تو RXJava این رو استفاده کردی؟ تو Dagger اونو استفاده کردی؟ مثلا این رو می‌دونی چطور باید تنظیم کنی؟ از اینجور بحث‌ها، از چیزای خیلی ابتدایی اندروید شروع کرد رفت بالا تا ببینه چقدر میتونم جواب بدم.نیم ساعتی به سوال جواب گذشت، بعد گفت یه کد بنویس. آنلاین باید همونجا اسکرینم رو share میکردم و کد مینوشتم. اگه اشتباه نکنم تو یه ساعت میخواست یه اپ خیلی ساده مثل Run Keeper بنویسم که قدم‌ها رو میشماره و حساب میکنه چند دقیقه دوییدید. فقط تفاوتش این بود که ui مهم نبود، مهم این بود که pure چه جور logic مینویسم، و اینکه باید از Thread استفاده میکردم، میخواستند ببینند ipc بلدم؟ بین ترد‌ها میتونم دیتا جابه‌جا کنم؟ که یا باید از RXJava استفاده میکردم یا EventBus و این جور چیزها.بعد از این مرحله هم یه google doc با من share کرد و گفت ما تو این داک یه کدی نوشتیم هرچقدر میتونی ازش اشتباه بگیر. یعنی اشتباه داره اشتباهاش رو مشخص کن. که از اشتباه‌های syntax و تایپی، کامنت داشتن-نداشتن، اشتباه Architecture، Unit Test داشتن-نداشتن، اینکه چیزی مثلا NullPointerException تو runtime بگیرم-نگیرم، از هرچیزی که میتونستم باید ایراد میگرفتم.مرحله بعدی باز با یه اندروید انجینیر دیگه جلسه داشتم و یه نفر دیگه که نمیدونم برنامه نویس اندروید بود یا نه، اونم تست برنامه نویسی بود. بیشتر میخواستند ببینند تو بازه کوتاه چقدر سریع و با دقت میتونم کد بزنم. یا اگر بهم وقت بدن چقدر میتونم تمیز کد بزنم.وقتی هم از این مرحله قبول شدم گفتند باید بیای اینجا.وقتی اومدم سوئد یک روز کلا از صبح تا شب فقط مصاحبه بود، تو ۴ تا مصاحبه تقسیم شد. ۲ ساعت اول با ۲ تا از مدیرهای ۲ تا تیم جلسه گذاشتیم برای این که کاندید بودم به یکی از اون ۲ تا تیم اضافه بشم، در واقع مصاحبه فرهنگی بود. اسمش Cultrual Interview بود، خیلی سوال‌های کلی میپرسیدند. ربطی به برنامه نویسی نداشت مثلا چه جوری خودت رو از لحاظ تکنولوژی آپدیت نگه میداری؟ چه بلاگ‌هایی میخونی؟ اگر تو محیط کار این مشکل پیش بیاد چطور حلش میکنی؟ اگه این کار اورژانسی پیش بیاد چکار میکنی؟ اگه وقت نباشه ریفکتور کنی ولی فیچرو لازم داشته باشیم چه جور سعی میکنی هم کد رو تمیز نگه داری هم فیچر رو به موقع برسونی؟ از لحاظ شخصیتی چطور آدمی هستی؟بعد یه مصاحبه داشتم با ۲ تا انجینیر اندروید. بهم گفته بودند یه کدی بنویس بیار و هرچی دوست داری استفاده کن؛ هم architectureای که دوست داری هم ابزارهایی که دوست داری. من یه اپ ساده نوشته بودم و باید از کدم دفاع میکردم. تا جایی که میتونستم باید تمیز مینوشتم، دست خودم بود که چقدر وقت بزارم و تمیز بنویسم. باید دفاع میکردم چرا وقتی که با Lint چک بیلد میگیریم این warningها رو درست نکردم؟ یا مثلا چرا این قسمت تست code coverage خوب نبوده؟ یا چرا این logic رو تست نداره؟ باید دفاع میکردم. بعضی چیزهاشم نمیدونستم که باید این کار رو میکردم. همونجا هم یه چیزایی رو گفتند که باید اضافه میکردم و دوباره شروع کردم کد نوشتن.یه قسمتی از تست دوباره فرهنگی بود با دو سه نفر از اعضای تیم‌ها نهار خوردیم که فقط ببینند من چه جور آدمی هستم، میتونیم با هم تو یه تیم باشیم یا نه.آخرین تست بهش میگفتند Problem Solving (حل مساله) که کاملا الگوریتمی بود. یه تخت سیاه دادن دستمون، یه مسئله‌ای گفتند و باید با pseudocode (شبه‌کد) حلش میکردم و مرتبه زمانی الگوریتمش رو حساب میکردم. مسئله رو یادم نمیاد چی بود. اون قسمتش برای من چالش برانگیزترینش بود چون تو ایران خیلی شبه‌کد نمینویسیم و مرتبه زمانی الگوریتم حساب نمیکنیم. البته بهم گفته بودند که همچین تستی ازت میگیریم و من مجبور شده بودم یه دوره‌ی یادآوری از طراحی الگوریتم و دیتا استراکچر بگذرونم که یادم بیاد چی‌به‌چی بود. عملکرد خیلی خوبی نداشتم تو اون تست چون بر خلاف اینجا تو ایران الگوریتم به عنوان چیزی که باید روش مسلط باشی خیلی مطرح نبود.آخر هم بهم ایمیل زدن که قبول شدی و کی میتونی بیای و مراحل اومدن رو شروع کردم. تو نواک هم کار دستم خیلی زیاد بود همه رو تحویل دادم. کم‌کم آماده شدم اومدم اینجا.ورود به اسپاتیفایوقتی رسیدم تو ۳ مرحله من رو OnBoard کردند. Onboarding فرایندی هست که وقتی شما وارد شرکت بشید شروع میشه برای اینکه یاد بگیری چه جوری با شرکت کار بکنی و فرآیندهای خاص شرکت رو یاد بگیری. برای همه ۳ مرحله Onboarding انجام میشه. مجموعا چیزی حدود ۲ هفته من تحت آموزش بودم؛ همش یه جا نبود تو زمان‌های مختلف بود. تو اون ۲ هفته من یک خط کد هم نمینوشتم چون داشتم یاد میگرفتم.تو مرحله اول یاد گرفتم اسپاتیفای به صورت کلی ساختارش چه جوریه؟ رییس کیه؟ به کی گزارش میدی؟ تیم‌ها چه جوری شکل میگیرن؟ چه جوری دلیویر میکنی محصولی که باید دلیویر کنی؟ استک کلی پروژه چه جوریه؟ آیا ما رو کلاودیم؟ آیا ما خودمون دیتا سنتر داریم؟ اندروید پروژه کی شروع شد؟ از اینجور چیزا شروع میکنند و بعد بیشتر توضیح میدند. مثلا ما از چه متودولوژی‌هایی استفاده میکنیم؟ چه جوری اجایل رو مدیریت میکنیم که ازش نتیجه بگیریم؟ مثلا در قالب یه سری بازی فکری این که چطور اجایل کار میکنند رو به ما یاد میدند، که خیلی جالب بود ایده‌هاش و اصلا هزینه هم نداشت، صفر بود عملا، فقط لازم بود یه نفر اجایل رو خوب بشناسه.بعد Mobile Onboarding بود که مخصوص موبایل انجینیرها هست. طبیعتا بک‌اند آنبوردینگ، دیتا آنبوردینگ، و از اینجور چیزها هم دارند. تو موبایل آنبوردینگ مثلا اینکه چه جور از اندروید استادیو استفاده میکنیم یا از چیز دیگه، gradle رو چه جور بیلد میکنیم، مثلا اگه بخوای یه A/B test بنویسی پلتفورمش چیه؟ اگه بخوای کدت رو Modulraize بکنی پلتفورمش چیه؟ اگه بخوای یه کامپوننت جدید تو اسپاتیفای رجیستر کنی چطوره؟ یه فیچر کلا جدید بخوای بنویسی چه جوری باید این کار رو بکنی؟ کجا باید رجیسترش کنی؟ چه جوری می‌تونی تیمت رو رجیستر کنی به عنوان owner این کد؟ و BestPracticeهای شرکت.آخرین مرحله آنبوردینگ IntroDays که کاملا اطلاعات سطح مدیریتی اسپاتیفای بود. مثلا چه جوری پول در میاره؟ چه جوری تبلیغات میکنه؟ چه جوری با هنرمندا قرارداد میبنده؟ یا چه اطلاعاتی میتونید پابلیک بگید چیارو نمیتونید بگید؟ یا اگر نمیدونید چی رو نمیتونید بگید از فلان کس بپرسید. IT چیکار میکنه تو شرکت؟ HR چیکار میکنه؟ اطلاعات کلی شرکت بود.خیلی خیلی آدم زیاد هست تو شرکت و اوایلش گیج میشدم، وقتی یه مشکلی پیش میاد باید چیکار کنم؟ از کی بپرسم؟ یه چیز خیلی خوبی که داشت یک آدمی Assign میشه به تازه واردها به اسم buddy (همون رفیق)؛ کسی هست که role شما رو داره، اونم اندروید انجینیر هست ولی سابقه کار طولانی داره؛ مثلا ۳ ساله اندروید انجینیر هست تو شرکت. تو ماه اول قدم به قدم باهاتون میاد هر سوالی داشته باشید میتونید ازش بپرسید. مثلا از کجا آب بخورم؟ تا اینکه شرکت چه جوری پول در میاره؟ من اگه بخوام به این اطلاعات مالی شرکت دسترسی داشته باشم کجا باید برم؟ اگر بخوام کد رو clone کنم کجا باید برم؟ چون روزهای اول حجم اطلاعاتی که میگرید انقدر زیاد هست که آدم گم میشه تو اون اطلاعات. یه خورده طول میکشه بتونه مدیریتش کنه. حداقل برا من اینجوری بود.چیزی که برام جالب بود شرکتایی که خارج از ایران کار میکنند، برای همه کارمند‌ها یه دوره آزمایشی دارند. با هر رولی که وارد شرکت بشی حتی رول CEO. این دوره از طرف کشور کلا اعمال میشه. تو سوئد قانون هست؛ همه افرادی که استخدام میشند ۶ ماه دوره آزمایشی دارند. تو کشورهای دیگه هم هست یه خورده کمتر بیشتر. مثلا من هنوز تو دوره‌ آزمایشیمم. تو دوره آزمایشی ممکنه شما انقدر خراب کنی که بگند نمیخوایم باهات کار کنیم. یا خودت به این نتیجه برسی که من نمیخوام اینجا بمونم. ولی بعد از دوره آزمایشی قراردادت اتومات بدون اینکه چیزی امضا کنی تبدیل میشه به کارمند فول تایم شرکت.این نحوه ورود من به شرکت بود.اطلاعات مالی شرکت کامل شفافه؟بلهحقوق بقیه رو میشه دید؟تا حالا دنبالش نرفتم ولی خیلی سخت نباید باشه دیدنش. کلا اینجا اطلاعات مالی خیلی شفافه، نه فقط اسپاتیفای، کلا تو سوئد حتی حساب کتاب‌های دولت هم میتونید چک کنید. چه برسه به یه شرکت چند ۱۰ هزار نفره.ساختار تیم‌هابحث بعدی که درباره‌ی شرکت خیلی جذاب هست، مدل شکل گیری تیم‌ها و structureشون هست. از پایین به بالا توضیح میدم. از مهندس نرم‌افزار تا CEO.در لول پایین squad هست، یه تیم متشکل از یه سری آدم که برای هدف مشترک کار میکنند. مثلا من وارد تیمی شدم که کار سرچ انجام میداد. فیچر و پروداکت سرچ دست این تیم بود. (الان تیمم عوض شده) تیم سرچ از من و یه اندروید engineer دیگه، دو تا ios engineer، یه backend developer، یه product owner، یه designer و آدمی به اسم agile coach و کسی که کار chapter lead رو انجام میده تشکیل شده. یعنی این همه آدم فقط داشتن front end سرچ رو مینوشتند.ما دو تا تیم سرچ داریم. یه تیم به اسم search experience و یه تیم دیگه به اسم search platform. من تو search experience بودم، ما فقط front end بخش سرچ رو انجام میدادیم. مثلا بخش اندروید دست من بود، باید maintenance میکردم. اگر پروداکت منیجر چیزی میخواست باید انجام میدادم. اگر A/B Test یا … بود باید پیاده میکردم. ورژن‌های قبلی رو باید maintenance میکردم. چون چندین ورژن از قبل هست که کد بیس و مدل برنامه نویسیش فرق داره و قدیمی بوده. ابزارهایی که توش استفاده شده متفاوت بوده و هنوز لایو هستند، در نتیجه باید maintenanceشون بکنیم. و یه سری کارهای دیگه روزمره.backend engineer تیم ما از تیم search platform اطلاعات رو میگرفت aggregate میکرد و به front end میداد. برای مثال وقتی یه سرچ توی اسپاتیفای انجام میدید، ۱۰ مرحله اطلاعات توی سرویس‌های مختلف جابه‌جا میشه تا برسه دست کاربر، این ده مرحله تو search platform انجام میشه مثلا خوندن اطلاعات از دیتابیس‌های distributed و رتبه‌بندی کردنشون، ساختن اطلاعات از microserviceها و FlatBuffer و باینری کردن این‌ها، optimization برای حداقل کردن زمان. ۱۰ سرویس مختلف هست که این اطلاعات سرچ رو میسازه. backend engineer ما اطلاعات این ده سرویس مختلف رو aggregate میکرد و تحویل front end میداد. کار ما از اینجا شروع میشد، اطلاعات رو از backend خودمون میگرفتیم و نمایشش میدادیم و ….تیم ما یه رولی داره (توی اسکوادمون) به اسم به product owner یا product manager، آدم کاملا غیر فنی هست که نحوه‌ی رشد و اداره کردن این product رو بر عهده داره. این آدم تصمیم میگیره که توی product چه اتفاقاتی بیوفته. مثلا این A/B Test اجرا شه که ما ببینیم میتونیم concussion یوزرمون رو ببریم بالا یا نه، ببینیم میتونیم session یوزر رو بیشتر بکنیم یا نه. یا مثلا براساس یه سری اطلاعات که شرکت بدست آورده اگر اطلاعات رو به جای افقی، عمودی نشون بدیم بهتره؛ حالا اینو زمان‌بندی میکنیم که این feature یه ماه وقت میگیره و اون میشکونتش به taskهای کوچکتر و ما شروع میکنیم روش کار کردن. یا احتمال داره تیم‌های دیگه به ما feature request بدن و بگن این feature رو احتیاج داریم، پروداکت منیجر هست تصمیم میگیره ما توان انجامشو داریم یا نه و اینکه چقدر طول میکشه بخوایم انجامش بدیم، آیا لازم هست کسی از تیم دیگه بیاد کمکمون برای پیاده سازی این feature یا نه.مثلا شرکت تصمیم میگیره تو شش ماه آینده ۱۰۰ میلیون کاربر جدید بگیریم. اینکه چطور این کاربرها گرفته بشه در قالب یه سری اهداف به تیم‌های مختلف اعلام میشه. مثلا برای گرفتن ۱۰۰ میلیون کاربر لازم هست که توی بازار فلان کشور وارد بشیم و یوزرهاشو نگه داریم. برای انجام این کار باید فیچر x این کارو بکنه و فیچر y اینکار. اینو وقتی product owner فهمید میاد با تیم صحبت میکنه که این فیچر رو میخوایم بزنیم. حالا شما باید نیازمندی‌های شرکت رو رفع کنید، بتونی اون چیزی که ازت میخوان رو تحویل بدی، باید زمان‌بندی کنی، بگی من اینکارو میتونم بکنم ولی دو ماه ازم وقت میگیره. اندروید یه تخمین میده ios یه تخمین، back end یه تخمین. زمان تست و … بهش اضافه میکنن و اعلام میکنن این فیچر اینقدر طول میکشه. ممکن هست یه تیم دیگه به یه فیچر شما نیاز داشته باشه. مثلا یه A/B test بکنه یه چیز جدید اضافه کنه. نمونش ویو سرچ دست ما هست ولی اسپاتیفای یه فیچری اضافه کرد به اسم audio assitance، یه دکمه هست تو تولبار بالای سرچ اضافه شده (نمیدونم تو همه کشورها ریلیز شده یا نه) وقتی میزنید میتونید از اسپاتیفای مثل گوگل home سوال کنید، به فرض فلان آهنگ رو پخش کن. توی این مودم برام یه آهنگی رو پخش کن. این assitance باید توی سرچ اضافه میشد. منتها اضافه کردن فقط یه دکمه نبود، چون باید اینو توی پلتفرم‌های مختلف جا بدیم یا اصلا بتونیم توی نسخه‌های مختلف سرچ جا بدیم، یعنی ممکن هست شما بری یه code base رو ادیت کنی که آخرین بار سه سال پیش ادیت شده، چالشش فقط اضافه کردن یه دکمه نبود؛ درگیری برای ما زیاد داشت. باید unit test, end to end test, integration test بنویسی و مطمئن باشی مشکل نداره. طبیعتا code baseای که سه سال پیش نوشته شده نمیتونی توش از rxjava2 استفاده کنی؛ باید اونجا با یه روش دیگه اضافش کنی. خلاصش اینکه تیم چطوری و چه کارهایی رو با چه dead line میتونه انجام بده رو product manager یا product owner مشخص میکنه.نقش جالب دیگه که تو همه‌ی تیم‌ها داریم technical owner هست. معمولا قدیمیترین engineer اون تیم و برخلاف product owner کاملا فنی هست، کد مینویسه همه کار میکنه منتها این نقش رو هم برعهده داره. این دو نفر با هم نقشه راه تیم رو در طول زمان‌های مختلف مشخص میکنند. مثلا ممکن هست یه فیچر باحال به ذهنمون بیاد یا یکی بهمون پیشنهاد بده یا اصلا CEO بگه اگر این فیچر اضافه شه خیلی خوب میشه. ولی technical owner بگه ما توانایی انجامشو نداریم یا کار ضروری تری داریم. اینجوری نیست که فقط product owner از دید غیر فنی بگه اینکارو بکنیم یا این کارو نکنیم.تیم‌های ما که بهش میگیم Squad معمولا ۱۰ - ۱۲ نفر هستند، یه فیچر دستشونه و باید اونو توسعه بدن یا نگهداری کنند. شرکت چند طبقه هست و به هر طبقه Tribe میگن. ترایب مجموعه‌ای از چندتا squad هست معمولا ۵ - ۶ تا و حدود ۸۰ - ۱۰۰ نفر که برای یه هدف مشخص کار میکنند. برای مثال من توی ترایبی بودم به اسم core experience. این ترایب از ۵ اسکواد مختلف تشکیل شده و کل ux و ui اپ موبایل اسپاتیفای دست ترایب ما هست؛ تقریبا اکثر فیچرهایی که توی اپ میبینید. صفحه home, your library, search, radio, album, artist همه اینارو ترایب ما زده. ما با هم تعامل نسبتا نزدیکی داریم، مثلا تیمی که صفحه artist رو مینویسه ممکنه بیاد از code base شما استفاده کنه یا یه A/B test بخواد ران کنه که سرچ یه تغییراتی بکنه. هر هفته یه جلسه‌ی مشترک داریم. چیزایی که توسعه میدیم رو با هم share میکنیم. اگر چیزی یاد بگیریم داکیومنت میکنیم و به بقیه میدیم.بعد از level ترایب یه تیم‌بندی بزرگتری به اسم Alliance داریم، مجموعه‌ای از tribeها هستند که باز یه کار مشترک ولی در سطح بالاتر انجام میدن. مثلا تمام تیم‌های فنی اسپاتیفای توی یه alliance قرار می‌گیرند. مثل تیم موبایل، دسکتاپ، ماشین یا data science. تعداد هم خیلی زیاد هست مثلا alliance ما ۱۵۰۰ نفر هستند. توی alliance معمولا خیلی ارتباط نزدیک معنا نداره. چون بعضی از تیم‌ها اصلا توی یه کشور نیستند. مثلا تیم رادیو توی آمریکا هست و سوئد نیست. ولی خب بفرض من مجبور بودم برای یه A/B test که خودم مینوشتم با یه تیم توی بوستون هماهنگ میشدم که بخاطر محدودیت زمانی که داشتیم کلا دو سه ساعت مشترک میتونیستم کار کنیم. یا اونا بیرون شرکت بودن یا ما. سعی میکنند خیلی مجبور نشید با تیم‌های یه کشور دیگه کار کنید چون زمان از دست میدید.سطح بالای alliance میشه خود شرکت، که مثلا ceo, cto و نقش‌های دیگه داخلش هست.این نحوه‌ی تیم‌بندی اسپاتیفای هست. سطح اول اسکواد با تیم‌های ۱۰-۱۲ نفره، بالای اون ترایب معمولا شامل ۵-۶ اسوکاد، بالای اون alliance با ۷-۸ ترایب مختلف که معمولا تعداد افرادشون خیلی زیاد میشه.مدیریت پروژهتوی اسپاتیفای یه داکیومنتی share شده بین همه اعضا و با اون هدف گذاری میکنند. این داکیومنت‌ها رو معمولا مدیرهای رده بالا C level engineer یا خود CEO میسازند. برای مثال یه مفهومی توی اسپاتیفای وجود داره به اسم north star goal (هدف ستاره قطبی). ستاره قطبی یه مفهوم هست، چیزی که باهاش مسیرمون رو پیدا کنیم. اهداف ستاره قطبی رو CEO میسازه. اینا اهدافی هستند که آینده موسیقی یا صنعت رو قراره شکل بدن. خیلی سطح بالا هستند و ربطی به کارهایی که الان میکنیم نداره و شاید اصلا هیچوقت هم بهش نرسیم. فقط با اون اهداف جهت گیری تیم‌ها مشخص میشه. برای مثال یکی از اهداف اسپاتیفای رسیدن به یک بیلیون کاربر فعال هست. این یک بیلیون کاربر فعال عدد بزرگی هست و طبیعتا طول میکشه بخوایم بهش برسیم. ولی کاربرد‌هایی داره، شما میتونی متناسب با این هدف کار خودت رو بسنجی و بفهمی در راستای این هدف میری جلو یا نه. برای مثال ما میخوایم به یک بیلیون کاربر برسیم و هدف x, y, z. متناسب با این اهداف سالیانه رو میسازیم. مثلا برای رسیدن به یک بیلیون امسال باید ۱۰۰ میلیون کاربر بگیریم. حالا هدف امسال میشه ۱۰۰ میلیون کاربر. بعد اهداف امسال رو میشکونیم به فصل‌های مختلف که بهش میگن quarter مثلا q1, q2, q3, q4. مشخص میکنیم تو هر کوارتر برای رسیدن به هدف چیکار میتونیم بکنیم.با توجه به اهداف مشخص شده با product owner برای یه فصل برنامه‌ریزی میکنیم، بفرض ۴ ۵ ۶ تا اسپرینت میبندیم. توی اسپرینت ۱ به اینا میرسیم توی ۲ اونا رو انجام میدیم و توی ۳ اینا. به همین ترتیب اسپرینت‌ها رو میچینیم. البته دونه دونه، یعنی برای ۴ تا اسپرینت بعدی برنامه‌ ریزی نمیکینم، برای یه اسپرینت بعدی برنامه‌ میریزیم. منتها تو backlog (از jira استفاده میکنیم) تمام تسک‌هایی که باید توی یه کوارتر بهش برسیم هست. اگر توی این کوارتر بهش رسیدیم که عالی دممون گرم، میریم بیرون شام میخوریم. اگر نرسیدیم یه جلسه میذاریم که چرا نرسیدیم؟ چه مشکلی هست؟ آیا باید آدم بیشتری بگیریم؟ آیا درست برنامه‌ ریزی نکردیم؟تسک‌های جیرا رو به این شکل صفر میکنیم و به اونجا میرسونیم که بگیم به اهداف این کوارتر رسیدیم، ایول حالا بریم کوارتر بعدی. این مدلی ما تسک بندی میکنیم.یک بیلیون کاربر فعال رو درست گفتی؟ فکر کنم چند برابر جمعیت زمین هست!نه درست گفتم! Billion تو فارسی میشه یک میلیارد! :)شکل رسیدن اهداف به تیم‌ها، در قالب یه structure هست که بهش میگن OKR. مفهوم OKR احتمالا تو شرکت‌های دیگه به شکل دیگه وجود داره ولی بنیان گزارش گوگل هست. OKR یه مفهوم دو لبه هست که از لحاظ مدیریتی باید هم از بالا به پایین به این مفهوم برسیم و هم از پایین به بالا. OKR مخفف objectives and key results هست. یعنی شرکت میاد اهدافش رو مشخص میکنه که میشه objectiveهای شرکت، مثلا شرکت میگه ما بفرض میخوایم به ۱۰۰ میلیون یوزر جدید برسیم، این میشه objective یا در کنار یه سری اهداف دیگه. برای رسیدن به این اهداف چیزی میاد که تضمین کننده رسیدن یا عقلانی بودن اهداف میشه، به اسم key result. یعنی اگر به این نتایج برسیم (هر objective معمولا چندتا key result داره) این objectiveمون انجام شده. مثلا شرکت اعلام میکنه session کاربرا رو از ۲ ساعت بکنیم ۲.۵، ما برگردیم بگیم شدنی نیست غیر منطقی هست یا ایول شدنی هست بریم انجامش بدیم. این بحث دو طرفه هست تا objective مشخص بشه. بعد از اینکه هدف قابل دسترس شد، ما به عنوان تیم‌ها میایم key resultهامون رو مشخص میکنیم. یعنی میگیم ما برای اینکه به این session برسیم، تیم سرچ consumption آهنگ‌هارو از ۵ دقیقه به ۷ دقیقه، از ۱۰ دقیقه به ۱۵ میرسونه. پله پله میبره بالا. چجوری؟ مثلا با اجرا کردن این A/B تست و مارکت کردن توی فلان کشور و کار x و y و z. یعنی میگیم ما به عنوان تیم سرچ برای رسیدن به این هدف چیکار میکنیم. تیم‌های دیگه هم key resultهاشون رو اعلام میکنند، key resultها از تیم‌های مختلف جمع میشه تا مشخص شه ما توی کوارتر بعدی یا سال بعد قراره چیکار کنیم تا به این هدف برسیم.به key result نمره میدید؟بلهبه اون تیکه که گفتی قراره ۵ بشه ۷ نمره میدید؟آره درجه اولویت داره و یه سری از بقیه مهمتر هستند. مثلا ما تصمیم میگیریم بریم وارد بازار چین بشیم و شرکت میگه این هدف شماره یک هست. متناسب با اون چند هدف دیگه هم اعلام میشه ولی هدف شماره یک مهمترین هست و برا همه درجه اولویتش بالاست. key result که متناسب با اون هدف یا آبجکتیو اعلام میکنی هم رده‌بندی داره و میگی مثلا من برا objective شماره ۱ این key result رو مشخص میکنم برا objective شماره ۲ اینارو و ممکنه به objective شماره ۳ نرسم چون هدف شماره ۳ هست و انقدر هم مهم نیست ولی هدف ۱ و ۲ اولویت بالا داره و باید بهش برسیم.با توجه به اولویتی که داره ضریب میدید؟تقریبا درصدی میریم جلو و زمان درصدی تقسیم میشه، مثلا تا آخر ۳ هفته بعد باید به objective شماره یک برسیم، بک‌لاگمون رو نگاه میکنیم، میبینیم این آبجکتیو ۱۰ تا تسک داره که هنوز انجام نشده در نتیجه میایم با برنامه نویس‌ها یا آدم‌هایی که باید پیاده‌سازی کنند صحبت میکنیم و میگیم که چقدر زمان می‌خوای که این رو انجام بدی؟ میگه من این ۱۰ تا تسک رو تو ۲ هفته انجام میدم. برا اون یه هفته باقی مانده برا objective شماره ۲ تصمیم گیری میکنیم، خوب تو این هفته چندتا تسک میتونی انجام بدی؟ میگه مثلا از objective شماره ۲ این ۲ تا رو میتونم انجام بدم. اول باید به objective شماره یک برسی در کنارش باید برای بقیه هم حرکت کنی.چطور OKR با اسکرام هماهنگ میکنید؟اول OKR از C level manager اعلام میشه و به alliance منیجرها ابلاق میشه (تیم بزرگ که همه تیم‌های فنی توش بودن، همه HR، همه ریکروتر و … همه این‌ها برا خودشون alliance دارند. برای ما تیم دسکتاپ، وب، موبایل و … توشند) اینا پروداکت‌هایی که دستشون هست رو خوب میشناسند، alliance میگه مثلا برای اینکه به ۱۰۰ میلیون کاربر برسیم پروداکت موبایل ۶۰، دسکتاپ ۲۰، اتومبیل ۵ و … میلیون کاربر جدید جذب کنه.آبجکتیو که تو alliance مشخص شد، alliance منیجر میاد به ترایب منیجر (ترایب همون چندتا اسکواد کنار هم بود و ترایب منیجر مدیر بالا سر همه اسکوادهایی هست که تو این ترایب کار میکنند) میگه شما به عنوان مدیر ارشد بخش موبایل باید تضمین کنی سال بعد ۶۰ میلیون کاربر بگیریم. حالا ترایب منیجر تقسیم‌بندی میکنه، برای این کار باید این مارکت رو بگیریم، اونکار رو کنیم و …. یا اگر بگن session کاربر افزایش بدیم از ۱ ساعت بکنیم ۱.۵ ساعت، یه تقسیم‌بندی میکنه میگه تیم home شما باید انقدر افزایش بدید، تیم سرچ انقدر، تیم آرتیست انقدر، میشکونتش به اسکوادهای مختلف و اون موقع هست که به تیم ما به عنوان پایینترین سطح هرم مدیریتی محول میشه. مشخص میشه OKR ما برای ۳ ماه بعد یا سال بعد ۱۵ دقیقه افزایش session کاربر توی جستجو هست. حالا ما به عنوان تیم سرچ این توانایی داریم و تصمیم میگیریم این کار رو بکنیم، اون فیچر اضافه کنیم و به این شکل افزایش میدیم. وقتی همه تیم‌ها sessionشون رو افزایش بدن در نهایت کل اسپاتیفای سشنش از ۱ ساعت میشه یه ساعت و نیم چون هر کدوم از تیم‌ها جدا این کار رو میکنه.تو تیم‌هایی که XP یا اسکرام کار میکنند، بعد از تصویب OKR کجا نوبت اسکرام و XP میشه؟این رو product owner تصمیم میگیره، با توجه به چیزایی که بهش اعلام شده و آبجکتیوها و توانایی تیم شروع میکنه و فیچرهای ۳ ماه بعد رو مشخص میکنه. مثلا این A/B Test رو ران کنیم یا … توی backlog جیرا میذاره، متناسب با اون اسپرینت میبنده، زمان بندی میکنه، تسک‌ها محول میکنه و …تو xp که اکثرا تسک‌ها روزانست و تایم کمتر و اسپرینت نداریم مشکلی پیش میاد، از کجا بفهمیم یا چه جوری زمان بندی کنیم که این تسک‌ها تو اون ۳ ماه تموم شه؟این یه تعامل هست بین product owner, technical owner و ما برنامه نویس‌ها. product owner میگه من این فیچرو میخوام پیاده‌سازی کنم نیازمندیش (sub task) میشه این کارا. technical owner آدمیه که چند سال تو اسپاتیفای بوده، هم خودش خیلی سابقه کار داره هم تیم رو خوب میشناسه هم برنامه نویس‌هارو، میگه با توجه به تجربم به این تسک میتونیم برسیم یا نه.وقتی sub taskها مشخص شد مثلا با من صحبت میکنند، میگن ما این کارارو میخوایم بکنیم، تو اسپرینت اول باید به اینا برسیم تو اسپرینت دوم به اینا و … آیا میتونیم انجام بدیم؟من متناسب با برنامه و کارای خودم میگم آره یا نه. ولی ممکنه یه فیچر انقدر مهم باشه که حتی اگر من نتونم انجامش بدم، اسکواد ما مجبور بشه از یه تیم دیگه یک یا دوتا اندروید انجینیر بگیره؛ موقت بگه شما ۳ یا ۴ ماه بیایید توی تیم ما تا این فیچر رو بتونیم پیاده کنیم. این فیچر انقدر برا شرکت مهم باشه که اسکوادهای دیگه حاضر باشند این فداکاری رو بکنند. بگند اندروید انجینیر ما ۲ ماه بره و ما اندروید دولوپ نکنیم ios یا بک‌اند دولوپ کنیم، چون باید به اون فیچر برسیم. هدف‌هایی که خیلی مهم هست هرجور شده باید بهش برسیم حتی اگر شده نیرو از جای دیگه بگیریم.هر سطح OKR خودش رو داره؟دقیقا هر اسکواد و هر ترایب که میشه مجموعه‌ای از OKR اون اسکوادها و هر alliance میشه مجموعه‌ای از OKR اون ترایب‌هااگر به این اهداف نرسیم چی؟یه سری اهداف هرجور شده باید بهش برسیم. کسایی که این اهداف رو مشخص میکنند آدم‌های معقول و عاقلی هستند، میدونند آیا ما اصلا بهش میتونیم برسیم یا نه. اگر هدف انقدر بلند پروازانه باشه که نتونی بهش برسی یعنی اون مدیر واقعا کارش رو بلد نبوده که همچین هدفی تعیین کرده. هرکس باید بتونه تو جایگاه خودش درست تصمیم بگیره؛ اون مدیر کارش اینه که بتونه برنامه‌ریزی درست کنه، مدیری که نتونه تخمین درستی از تیمش داشته باشه که بدرد نمیخوره.ولی نهایتا پیش میاد که به هدفی نرسی، وسط کار زلزله اومد برنامه نویس مرد. در نهایت یه جلساتی برگزار میشه وقتی به یه هدفی نرسی یا برسی، وقتی sprint رو میبندیم یا quarter یا سال تموم شه، جلساتی داریم به اسم retrospective، تمام اعضایی که تو اون هدف شرکت داشتند مثلا تمام اعضای سه اسکواد میشینند با هم retrospective اسپرینت قبلی رو برگزار میکنند. آیا ما به همه اهداف رسیدیم؟ اگه رسیدیم دممون گرم. اگه نرسیدیم چرا نرسیدیم؟ چه نیازمندی‌ای داشتیم؟ اشتباه برنامه ریزی کرده بودیم؟ نیرو کم داشتیم؟ بلد نبودیم این code base رو؟ یا مثلا code base جدید به ما داده شد نمیدونستیم چیه؟ هرچی هست دلیلش باید مشخص شه اصلا مهم نیست کی اشتباه کرده، دنبال قاتل بروسلی نمیگردند و فقط میخوان علتش مشخص شه که دیگه تکرار نشه. اگه اشتباه برنامه‌ریزی کردیم، اگه زیاد رو خودمون حساب کردیم و رو توانایی‌هامون بیش از حد معقول حساب باز کردیم باید یاد بگیریم دفعه دیگه این کار رو نکنیم. یا مثلا اگر ما تو این اسپیرینت جای ۲۰ تا تسک ۶۰ تا برداشتیم دفعه دیگه نباید این اشتباه رو بکنیم. یا مثلا اگه engineer با این framework خوب آشنا نیست و نمیدونه باید چیکار کنه براش کلاس بزاریم و بقیه انجینیرها بهش یاد بدن چطور کار کنه، یا نیروی جدید بگیریم. یا ۴ تا pr جدید بود و باید review میکردم کارم سنگین بود نرسیدم در نتیجه از اسپرینت بعد با تیم‌های دیگه صحبت میکنیم اگر قرار pr بدند از قبل باهامون هماهنگ کنند که تو زمان‌بندی حسابش کنیم، یا برای اندروید انجینیر ۱ ساعت اضافه در هفته در نظر بگیریم چون باید pr ریویو کنه. هرچی هست علتش مشخص شه و حلش کنیم. تا جایی که بشه سعی میکنیم یا این اتفاق نیوفته یا اگر افتاد بلافاصله یه retrospective برگزار بشه و یاد بگیریم که چی از این قضیه یاد گرفتیم، چی شد که ما این اشتباه رو کردیم و دیگه این اشتباه رو تکرار نکنیم.گفتی با تیم مارکتینگ هم در ارتباط هستید؟ تصور من این بود که باید تو یه الاینس دیگه باشه.همینطوره که میگی ولی الاینس‌ها باید با هم کار کنن که به اهداف شرکت برسیم! تیم مارکتینگ با ترایب خودش هماهنگ میشه، ترایب ما هم به اون‌ها میگه این کارها لازمه انجام بشه و اون‌ها سعی میکنن انجامش بدن! در واقع Tribe Lead ها باهم توافق میکنن! اینجور تصمیم‌ها تو لایه مدیریتی و بین Product Owner/Tribe Lead اتفاق میوفته.ما یه سری جلسات بدرد بخور داریم و فکر میکنم برگزار کردنش خیلی راحت هست. برا ماها خیلی میتونه مفید باشه فارغ از اینکه تو چه شرکت و با چه ابعادی کار میکنیم.ما ۲ تا جلسه داریم که همه افراد تو یه اسپرینت باید توش حضور داشته باشند.یکیش جلسه برنامه‌ریزی هست که معمولا روز اول اسپرینت هست، چیزی نزدیک ۳ الی ۴ ساعت از زمان اون اسپرینت به برنامه ریزی برای ۲ هفته آینده میگذرونیم. شما لازم نیست تو کل اون جلسه حضور داشته باشید چون ممکنه برای ios، اندروید و بک‌اند تصمیم بگیریم. تنها افرادی که حتما باید حضور داشته باشند product owner و technical owner هستند، شما میتونی تا آخر بشینی یا فقط بخش مخصوص به خودت رو بری.جلسه دیگه همون retrospective هست، مخففش میکنیم retro. یه جلسه یک یا ۲ ساعتس که روز آخر اسپرینت برگزار میشه و حتما همه باید حضور داشته باشند، اول تا آخرش. این جلسه‌ای هست که توش اسپرینت قبلی رو بررسی میکنیم؛ به همه تسک‌ها رسیدیم یا نرسیدیم؟ اگه نرسیدیم چرا نرسیدیم؟ معمولا به همه تسک‌ها میرسیم چون سعی میکنیم موقع‌ای که برنامه‌ریزی میکنیم دیدگاهی عاقلانه و منطقی داشته باشیم. رو توانایی‌های نداشتمون، یا میرم یاد میگیرم و اینا حساب نمیکنیم، رو چیزی که الان داریم حساب میکنیم. من تا حالا فقط یه اسپرینت بود که تسک انجام نشده داشتم. تازه اومده بودم و از همون اول میدونستیم که احتمالا به یه سری از تسکام نمیرسم. چون تازه داشتم یاد میگرفتم چی به چی بود. بعد از اون تقریبا به همه اسپرینت‌ها میرسیدم.بلافاصله بعد یه جلسه داریم که بهش میگن demo. باید چیزی که تو این ۲ هفته روش کار کردی رو demo کنی. ممکنه پیور programming باشه و ui هیچ تغییری نداشته باشه، ولی میای demo میکنی مثلا من این رو refactor کردم، جای اینکه از این استفاده کنم از این یکی استفاده میکنم خیلی بهتر شد. یا مثلا unit test coverage رسوندمش به این. تو ui شما هیچی نمیبینی ولی محصولی که دستت هست رو بهبود دادی میای این رو demo میکنی.جلسات retro planning و demo در سطح‌های بالاتر برای کل ترایب و الاینس هم هست ولی لازم نیست شما تو اونا بری، معمولا product owner میره اونجا. مثلا رترو‌هایی که تو ترایب برگزار میشه میگه تیم سرچ تو ۲ هفته گذشته کار ۱ ۲ ۳ ۴ رو انجام داد. تیم فلان این کار رو کرد و …. این نشون میده که آیا پروژه داره پیشرفت میکنه یا نه؟ آیا جایی گیر کرده یا نه؟ آیا جایی نیاز به کمک داره یا نه؟یک نقش خیلی جالبی هم داریم اینجا به اسم delivery lead، یکی از مدیرهای نسبتا بالا با سابقه طولانی تو اسپاتیفای که تو هر ترایب هست. این آدم مطمئن میشه آیا تیم‌های مختلف با اون OKR که تنظیم شده میرن جلو یا نه؟ ما هر ۲ هفته یک جلسه با این آدم داریم. مثلا از من میپرسه اون تسکی که داشتی روش کار میکردی انجام دادی یا نه؟ (به برد جیرا ما دسترسی داره) خوب انجام دادی عالیه تسک بعدی به موقع بهش مرسی؟ ممکنه من بگم آره میرسم یا بگم تسک بعدی یه هفته بیشتر زمان لازم دارم چون میخوام برم مسافرت این جزو مواقع‌ای هست که ممکنه یه اسپرینت بهم بریزه ولی OK هست. برای اینکه من هم آدم هستم و میخوام برم از زندگیم لذت ببرم. یا ممکنه بهش بگم یه هفته بیشتر زمان لازم دارم چون باید فلان تکنولوژی رو یاد بگیرم باز هم OK هست، چون من دارم برای رسیدن به اون هدف تلاش میکنم. delivery lead کارش این هست که اسکوادهای مختلف به اون آبجکتیوهایی که بهشون اعلام شده و key resultهایی که تعریف کردن برسند. مطمئن شه این اسکوادها سلامت و توانایی لازم برای رسیدن به اون‌ها رو دارن. مثلا آیا لازم هست آدم جدید به این اسکوادها اضافه شه یا نه؟ delivery lead محصول‌‌های که این اسکوادها باید تحویل بدن رو جمع‌ آوری میکنه. دونه به دونه میپرسه این رو انجام دادی؟ هر ۲ هفته تکرار میشه و باهاشون جلسه داره.یه جلسه‌ای ۱۵ دقیقه توی تیم ما هست که صبح ساعت ۹:۴۵ برگزار میشه همون Stand-up meeting، یه لیستی از موارد رو معمولا بررسی میکنن. یکی اینکه امروز باگی به ما ریپورت شده یا نه و اگر ریپورت شده درجه‌اش چیه. ما ۵ درجه‌ی اهمیت توی شرکت داریم که بهشون میگن p1 تا p5. اگر باگ p1 به تیم ریپورت بشه، یعنی بخاطر پروژه‌ی شما کدبیس داره کرش میکنه، هرکاری الان دستت داری رو بذار زمین و شروع کن این باگ رو فیکس کن. اگر لازمه یه هفته بمون شرکت این باگ رو درست کن. البته من از وقتی شرکت بودم به پروژه‌ی ما p1 ریپورت نشده ولی با توجه به باگی که بهتون ریپورت شده ممکنه لازم باشه با کسی که ریپورتش کرده صحبت کنی که چیکار کردی کرش کرد، یا یه ورژن جدید براش بسازی که تست بکنه و از اینجور چیزا. البته ممکن هست چند روزی باگ ریپورت نشه.بعد از اون کارهای روزمره‌مون چک میشه مثل TeamCity و اینکه که کی باید چیکار کنه. معمولا کارهارو ریپورت میکنیم که مثلا من فلان کد رو میزنم، بک‌اند فلان کد رو میزنه، شاید من بگم امروز حتما این اندپوینت بک‌اند رو لازم دارم اگر میشه اینو زودتر انجام بدید. یه سری هماهنگی‌های داخل تیمی هست. و در آخر هم یه سری بحث‌های مختلف که یکی مثلا بگه امروز نمیتونم بیام یا فردا کار دارم.ابزارهاما تو اسپاتیفای از git برای سورس کنترل استفاده میکنیم، بطور دقیق‌تر از github. شرکت یه برنچ enterprise از گیت‌هاب خریده و وقتی میخواید کدی کامیت کنید یا pull requestها رو چک کنید تو گیت‌هاب شرکت میبینید.برای issue tracking و چک کردن تسک‌ها از jira استفاده میکنیم.از crashlytics برای crash reporting استفاده میکنیم. یکی از منابع دقیق چک کردن تعداد یوزرهای اکتیو هست. برام جالبه که crashlytics استفاده میکنیم نه google analytics.برای دولوپمنت از Android studio 3 beta 2 استفاده میکنیم، چون چند وقت پیش پروژه بخاطر حجم زیادش شروع کرد به کند و کند شدن. برای هر سری بیلد پروژه اندروید استودیو باید یه apk از پروژه بسازه و ببره روی emulator یا دستگاه deploy کنه تا بتونید تستش کنید؛ وقتی کد پروژه خیلی زیاد بشه، کامپایل کردن و ساختن اون apk طولانی میشه. برای همین کل کامیونیتی اندروید (جاوا بیشتر) اومدن یه راه حلی ارائه دادن که کدبیس رو modularize کنید، یعنی کدتون رو ماژول‌های مختلف کامپایل شده کنید و وقتی کد میزنید نیاز نباشه همه‌ی برنامه کامپایل شه تا یه apk ساخته شه. اگر دارید روی کلاس یا ماژول جدا کد میزنید فقط همون ماژول کامپایل میشه و بقیه ماژول‌ها byte codeهای آماده جاوا هست و شما فقط قسمت خودتون رو میذارید کنار اون byte codeها و apk میگیرید. این کار رو بهش میگن modularization که الان یه سری شرکت‌های نسبتا بزرگ تا حدی درگیرشن، یا انجامش دادن یا میخوان انجامش بدن. خلاصشو بخوام بگم یه ماژولی که شما میخواید روش کار کنید manifest خودشو داره، کدهای جاوای خودشو داره، اطلاعات ماژول در فایل‌های yaml جدا میذارید، همه چی تو ماژول خودتون تعریف میشه، انگار کلا با یه پروژه دیگه کار میکنید. زمان بیلد از وقتی که build clean میکردیم و گریدل همه چیو بیلد میکرد قبلا حدود ۱۰ دقیقه طول میکشید، ولی با این روش حدود ۱۰ ثانیه شده، به شکل خیلی خیلی محسوسی زمان بیلدمون پایین اومده. شرکت چند وقت پیش نفر ساعت حساب کرده بود یه رقم خیلی زیادی رو داشت عملا مینداخت دور، فقط چون منتظر گریدل میشدیم. به همین دلیل ما پروژه modularize رو شروع کردیم. چون اندروید استودیو ۲ روی کدهایی که ماژولارایز هستند خیلی بد و کند هست به مشکل خوردیم. بچه‌های شرکت یه کانال مستقیم با گوگل داشتند و قبل اینکه اندروید استودیو ۳ ریلیز شه یه سری تست‌ها کردند و دیدن جواب میده و خیلی بهتر میشه. در واقع اندروید استودیو ۳ خیلی بهینه شده برای کدهای ماژولارایز. رفتیم روش و دیدیم خیلی خوب جواب میده و ازش استفاده کردیم. حتی از زمانی که canary (قناری) بود استفاده میکردیم. اما بعضی وقت‌ها پایدار نیست، حتی الان که بتا هست به مشکلایی میخوریم. ممکنه کرش کنه یا اتفاقات عجیب بیوفته. تا الان فقط کد بیس اندروید ۱۵۰ ماژول داره و اگر نمیرفتیم روی ماژولارایز کردن دیگه داستان بود و از همه کارهامون عقب میوفتادیم.ابزار دیگه TeamCity هست، پروژه‌ای که شرکت JetBrains نوشته و برای مدیریت تست‌ها هست. باهاش میتونید چک کنید امروز چندتا تستمون fail شده، Integration testing فیل شده دارید یا نه، End-to-End Test فیل شده دارید یا نه. مثلا ایمولاتور اومده لاگین کرده رفته توی فلان صفحه و روی یه آهنگ کیلک کرده ولی اون آهنگ پخش نشده. این یه تست خیلی خیلی مهمه که fail شده و شما باید همون روز چکش کنید. ممکنه یکی یه جای دیگه‌ی کد، یه تغییری داده باشه که الان پلیر کار نکنه. تیم پلیر باید بره End-to-End تست رو چک کنه ببینه اشکال از کد ما بوده یا از اونا، براشون یه باگ ریپورت کنه که اینو درستش کنید چون پلیر کار نمیکنه. برای همین هر روز باید داشبورد TeamCity چک بشه.معماریتو شرکت دو معماری نرم‌افزار داریم mvp و mvvm. بسته به اینکه چیکار می‌کنید معمولا یکی از این دو تا استفاده میشه. Flat کلا وجود نداره و اگر شما فلت کار کنید نتیجه میگیرند که کارت رو بلد نیستی و تقریبا باید از شرکت بری. Flat architecture بی‌معنی هست و باید پروژه architect بشه و همه سر اون معماری توافق کنند. من کدی که فلت باشه اصلا بهش دست نمیزنم. mvp مزایای خودش رو داره mvvm هم همینطور. برای کدهایی که معمولا ماها میزنیم، مثلا یه بک‌اند و فرانت‌اند هست که باید به هم وصل شه و یه اطلاعاتی بخونید و چیزایی تو دیتابیس بریزید از mvp استفاده میکنیم، دلیلشم تست پذیری mvp هست. وقتی برای قسمت رندرینگ بخوایم کدی بنویسیم از معماری mvvm استفاده میکنیم. برای اینکه رندر کردن خیلی راحت‌تره، درواقع کارهای ui. جدا کردن بخش logic و سپردن interaction به دست observableها و rxjava توی mvvm خیلی راحت‌تر میشه، برای هابز که کار رندرینگ رو انجام میداد از mvvm استفاده کردیم.یه بحثی تو معماری mvp هست به اسم multiple presenter. برای مثال بخوام بگم وقتی وارد شرکت شدم، به پروژه سرچ کمک میکردم. وقتی از بیرون به کد بیس بخش سرچ نگاه کنید، یه لایه ویو داره و یه پرزنتر که لاجیک سرچ اونجا انجام میشه و یه مدل و از اینجور داستان‌ها؛ منتها خودش از بخش‌های مختلفی تشکیل شده. ما کلا دو تا سرچ اصلی توی اسپاتیفای داریم. یکی دکمه‌ی ذره‌بین که میزنید و میری اونجا کاراتون رو میکنید، یکی سرچ قسمت رادیو. بخش رادیو اسپاتیفای هم یه سرچ داره که اون رو هم ما باید مینوشتیم و لاجیکش تا حدی فرق میکرد. وقتی چیزی رو سرچ میکنید و ریسپانس شامل آهنگ‌ها آلبوم‌ها و … میاد، یه قسمتی نوشته see all، اگر روش بزنید صفحه‌ی همه آهنگ‌ها و آلبوم‌ها رو میاره، اونجا هم لاجیک خودشو داره. همین برای رادیو هم وجود داره. این فقط بخش معمولی سرچ بود. شرکت بعد از مدتی تصمیم گرفته بود که محصول جدیدی داخل اسپاتیفای ارائه بده، یه لایه جدید از یوزرهای free که داستانی پشتش داره، کاری با بخش بیزینسش نداریم. فکر کنید شرکت یه پروداکت جدید اضافه کرده بود، خود این پروداکت جدید متناسب با نیازش سرچ رو تغییر داده بود، درواقع ما باید برای هر پروداکت که داخل شرکت تعریف میشد یه سرچ متناسب با اون مینوشتیم. مثلا ممکنه سرچ رادیو ورژن جدید با قبلی هم ui، هم endpointهای بک‌اندش، logic، logging، همه چیش فرق بکنه. یه مفهومی وجود داره توی mvp به اسم multiple presenter که میگه برای هرکدوم از این کارها presenter خودشو بساز و تست‌های خودشو بنویس و لاجیک هر کار رو جدا هندل کن. منتها چون این‌ها یه سری functionality داره که بین هم مشترک هست، طبیعتا از یه سری سوپر کلاس استفاده میشه تا کد duplicate نشه.مشکل دیگه که تو کد بیس‌های بزرگ وجود داره پشتیبانی از ورژن‌های مختلفی هست که تا حالا ریلیز کردی. شما حساب کنید این ۶ تا مدل مختلف سرچ تو طول زمان عوض شدن، framework هاشون فرق میکنه، مدل کد بیسش فرق میکنه، اصلا سرچ قبلی تو یه کلاس دیگه وجود داره. مثلا سرچی که اسپاتیفای ۳ سال پیش نوشته هم معماریش فرق داره هم ابزاراش، همه چیش فرق داره ولی چون هنوز تعداد زیادی یوزر دارند و ازش استفاده میکنند و رو ورژن‌های قدیمی اسپاتیفای هستند باید اون رو هم ساپورت کنید. در نتیجه وقتی یه فیچر رکوئستی به ما داده میشه، مثلا اضافه کردن فلان دکمه توی سرچ، در ظاهر یه دکمه کوچیکه اما شما باید برا ۶ مدل مختلف سرچ این رو اضافه کنی و برای ورژن‌های قبلی backward compatibility داشته باشه. یعنی باید بری تو یه کلاس دیگه که ممکنه dependency injection متفاوتی داشته باشه، mvp نباشه و رو چیز دیگه باشه، به اونم اضافه کنی. همینطور برا تمام این‌ها باید تست نوشته شه. یعنی شما نمیتونی اینجا یه کد بزنی بگی من خودم دستی تستش کردم کار میکنه. تو شرکت یه استانداردی وجود داره به اسم end to end testing منتها نه به معنی خود end to end یعنی شما باید تمام لایه‌های تست رو رعایت کنی. وقتی یه کد مینویسی معماریش باید درست باشه، میره تو ریویو و چند نفر میان ریویو میکنند. ممکنه یه سری نظر بدن، معماریش این قسمت اشتباه داره، ممکنه اون قسمتش leak بکنه، اینجا از این ابزار استفاده کن و …. تمام این ریویوها که تموم شد، شما باید تستت قانون ۸۰٪ کد کاوریج رعایت کنه. یعنی کدت باید ۸۰٪ توسط تست کاور شده باشه. لایه پرزنتر توسط unit test باید ۱۰۰٪ کاور شده باشه. بعد از یونیت تست اگر componentهات تغییر کرده باید integration test براشون بنویسی، یعنی دونه دونه کامپوننت‌ها باید جدا تست بشند قبل از اینکه برند با مستر مرج بشند. مثلا اگر یه باتن رو کلیک میکنی قراره یه انیمیشنی اجرا شه و یه کاری بکنه این رو به عنوان یه کامپوننت integration تست براش بنویسی. تازه تو مرحله بعد اگر back end یا end point جدیدی رو دارید تست میکنید، یا اگر کاری که میکنید خیلی برای یوزر critical هست که معمولا پروداکت منیجر و تیم تعیینش میکنه، باید براش end to end test بنویسی، یعنی یه تستی مینویسی که apk میره رو emulator نصب میشه، دیپلوی میشه، بعد لاگین میکنه، میره اون کاری که شما کردید رو انجام میده، بک‌اند واقع‌ای رو تست میکنه جواب رو میگیره چک میکنه و اگر همه چی کار کرد اون موقع تست pass میشه و میگه شما کارت موفقیت آمیز بود، در غیر این صورت کدت تو مستر مرج نمیشه، چندین بار review میشه هم بخش تستش هم بخش کد نویسیش.این جا کد نوشتن یه خرده دردسرهایی داره، معمولا چندتا ورژن رو باید ساپورت بکنی، چندتا ورژن تست بنویسی، بعضی ورژن‌ها اصلا باهم همخونی نداره چون معماریش فرق داره یا ممکنه همه‌چیش فرق داشته باشه.چرا هنوز کد بیس ۳ سال پیش باید تغییر کنه؟برای اینکه تعداد زیادی کاربر ازش استفاده میکنن، در واقع همه بخش‌های پروژه از یک تکنولوژی استفاده نمیکنن، ممکنه چند سال پیش نوشته شده باشه و از اون موقع ریفکتور و بروز رسانی نشده باشه!مگه آپدیت کنه کلا آپدیت نمیشه؟نه الزاما! مثلا صفحه Home و Search دست تیم ما هست! و هرکدوم یه مدل و تو یه زمان نوشته شده! مثلا تو Home از یه کتابخونه برای فلان کار استفاده شده و تو Search از یه کتابخونه دیگه! اینه که ممکنه مجبور بشی برای یه کار دوتا کد با Stack کاملا متفاوت رو تغییر بدی ولی جفتشون یه کار رو باید بکنن در نهایتتایتل این قسمت رو نمیدونم چی انتخاب کنماز ۶ ماه پیش حتما باید TDD هم بنویسیم. البته نهایتا زمانی که کد رو پوش میکنیم کسی نمیفهمه شما TDD نوشتید یا نه، منتها وقتی با یکی پر پروگمینگ میکنید و همیشه بغل دستتون هست یا یه چیزی رو توضیح میدید مخصوصا اگر ببینند یک لاجیکی تست نشده باشه این نشون میده شما TDD ننوشتید.مثلا فکر کنید یه پرزنتری داریم که قرار هست کار شماره ۱-۲-۳ رو بکنه که اینا لیست میشه تو داکیومنتی که داریم. برای اینکه TDD بنویسیم، اولین شروع میکنیم براش تست شماره ۱ رو مینویسیم؛ تست رو اجرا میکنیم و faile میشه، بعد شروع میکنیم حل کردن مشکل. چیکار کنیم که این تست پاس شه؟ کدش رو مینویسیم تا تست pass. و میریم سراغ سناریو ۲ تستش رو مینویسیم و تست faile میشه و شروع میکنیم تست رو حل میکنیم تا آخر. اگر لازم باشه کد رو ریفکتور میکنیم و در نهایت زمانی که کد رو submit میکنیم پرزنترها باید annotation خاصی داشته باشند، برای اینکه بات کد review میاد کلاسها رو چک میکنه و اگر پرزنتیشن لاجیک داشته باشه یعنی این کلاس قرار هست pure جاوا باشه و هیچ قابلیتی از اندروید نباید توش استفاده بشه. این کد باید ۱۰۰٪ تست کاوریج داشته باشه، یعنی به اعضای هر خط کدی که اونجا نوشته شده باید یک تست باشه. هر لاجیکی که هست باید تست شده باشه واگرنه به صورت اتوماتیک پول ریکوست شما ریجکت میشه، اصلا نمیتونید مرج کنید. ۴۳ تست مختلف رو prتون ران میشه، رو هر کامیتی که میکنید اگر رو یه پول ریکوست ۵ تا کامیت هم باشه به اعضای هر ۵ تا تمام تست‌ها روش اجرا میشه. این تست‌ها شامل Static analysis میشه یه سری استانداردها که تعریف شده و با فایل‌های xml مشخص میشه. مثلا بعد از اینکه اسم کلاس رو نوشتید باید یه اسپیس بزارید، زمان تعریف تابع‌ حتما باید پابلیک پرایویت یا پروتکتد بودنشون مشخص باشه. یه سری قانون داره اینارو چک میکنه رو کامیت. بعد مثلا به این میرسیم که این کد آیا تست به اندازه کافی داره؟ رو ورژن ریلیز کانفلیکت نمیخوره؟ وقتی که تست تموم شد شما یه نفر رو پینگ میکنید میگید بیا کد من رو review کن. کد review میشه ممکنه یه سری نظر بده اعمال میکنید و کد مرج میشه میره تو master. هرکسی هم رو fork خودش کار میکنه پول ریکوست میده رو مستر مثل کاری که تو گیت‌هاب رو لایبراری‌های معروف انجام میشه. به اعضای هر ریلیزی هم که قرار هست اتفاق بی‌افته یه برنچ ساخته میشه.rxJavaتوی شرکت از rxjava خیلی خیلی زیاد استفاده میکنیم، ۲ تا چیزی که تو مصاحبه من خیلی روش تاکید شد، یکی چقدر رو rxjava مسلطم و چقدر best practiceهای خود جاوا رو بلدم. تقریبا برای همه چیز از rxjava استفاده میشه. البته تو پروژه اصلی به خاطر infrastructure هنوز رو rxjava1 هستیم. چون هنوز یه سری چیزا تو rxjava2 ساپورت نمیشه نمیتونیم بریم روش. ولی از بک‌اند بخوای با نتورکینگ چیزی بگیرید، یا io انجام بدید، رو دیسک چیزی بزارید، بین پرزنتر‌ها مسیج رد و بدل کنید، هرکاری که بخواید بکنید با rxjava انجام میشه و خیلی به دردمون میخوره.مدل استفاده اینجا از rxjava برای من اوایلش یه مقدار عجیب غریب بود. تجربه قبلی خودم این بود، کدی رو که مینویسید یه observableای هست بعد باهاش یه سری اطلاعات رو subscribe میکنید مثلا map میکنید به یه چیز دیگه، لازم باشه switch map میکنید، لازم باشه compose میکنید، هر کاری که لازم باشه روی زنجیره کارای rxjava انجام میدید. منتها اینجا مدلی که پیاده سازی کردن و باید تبعیت بکنید اینه که هر کدوم از این فانکشن‌ها برای خودش یه کلاس هست. یعنی اگر بخواید ورودی رو بگیرید مپ کنید به یه چیز دیگه، نمیتونید یه rxchain دو - سه تایی پشت هم بنویسید بگید این رو مپ کن به این، بعد سویج مپ کن به اون و …. کاری که باید بکنید اینه که یه کلاس بنویسید بگید ورودیش اینه خروجیش اینه و تابع map رو implement میکنه. مثلا function1 رو ایمپلیمنت میکنه. توی کلاس کد رو مینویسید و کلاس و آبجکت کلاس رو توی rxchain تون پاس میدید بین هم دیگه. نه اینکه یه زنجیره بزرگ از اوپراتورهای rxjava داشته باشید. اولش مفهوم این یه خورده برای من گیج کننده بود، چون پیش میومد مثلا یه زنجیری از rxJava به وجود بیاد که ۱۰ تا اوپراتور داشته باشه و دیباگ کردنش اوایل برای من عجیب بود، مثلا یه باگی ریپورت میشد و لازم بود یه زنجیر rxjava رو چک بکنم و گیج میشدم یهو آبجکت از این کلاس رفت تو اون کلاس، از اون کلاس برگشت پاس داده شد به یه کلاس دیگه، اوایلش مدل متفاوتی برای من بود.اخیرا از فریم‌ورک شرکت‌ که متن باز شده استفاده میکنیم و اون واقعا رو نحوه‌ی استفاده از rxjava و شکل استفادمون فرق ایجاد کرده، اسمش Mobius هست، کدش رو اگر Clone کنی توش مثال هم هست، میبنی یکم متفاوت معماری rx chain توسعه داده شده.DIبرای dependency injection تقریبا از یه سال پیش از dagger 2 استفاده میشه. تقریبا الان هر چیز جدیدی که مینویسید باید با دگر ۲ داخلش دیپندسی‌ها اینجکت بشه و قبل از اون هم constructor injection بود. به جز constructor تقریبا جای دیگه‌ای حق نداشتید آبجکتی رو new کنید. نمیتونستید توی فانکشن بگید من یه object احتیاج دارم الان اینجا new کنم. اگر همچین کدی مینوشتید پول ریکوستتون با static check که داشتیم failed میشد. چون new کردن آبجکت داخل scope فانکشن با اصول دیپندنسی اینجکشن solid مغایرت داره، اگر میخواید همچین کاری بکنید باید اجازه بگیرید. یعنی یه سری permission خاص بگیرید که به این دلیل لازمه اینجا باشه، اون موقع فقط برای اون pr خاص اجازه صادر میشه. معمولا نمیبینید هرجای کد یه سری آبجکت new شه، اینجا خیلی روی اصول solid تاکید میشه.Testهرم تست اینجا به شکل کامل رعایت میشه، unit test خیلی زیاد داریم، تا جایی که میتونید باید یونیت تست بنویسید. Presenter تو معماری mvp باید ۱۰۰٪ code coverage داشته باشه واگرنه پول ریکوست failed میشه. integration test تا جایی که نیاز باشه. end to end test بنا به نیاز نوشته میشه، چون خیلی زمان گیر هست اجرا کردنشون. critical user journey (مهمترین کاربری‌های یک اپ، مثلا در مورد اپ اسپاتیفای بخش گوش دادن موسیقی، جستجو، ثبت نام، خرید اشتراک، موارد اینطوری که خیلی حیاتی هست رو میگن بهشون) رو end to end test براش مینویسیم. آخر هم manual test داریم، هر هفته جمعه تمام تیم‌های اسپاتیفای از ساعت ۱۰ تا ۱۱:۳۰ manual test دارند چون هر جمعه ریلیز داریم.یه داکیومنتی هر جمعه فرستاده میشه برای تیم‌ها و شما باید این داکیومنت رو امضا بکنی که من پروداکتم رو تست کردم داره کار میکنه یا تست کردم باگ داره ریلیز نکنید. تمام تیم تو اون ۱.۵ ساعت جمع میشند و پروداکتی که دستشون هست رو تست میکنند. چیزای مختلف که اون هفته روش کد نوشته شده یا چیزای قبلی رو تست میکنند. استرس تست یا …. خلاصه ۱.۵ ساعت manual testing هست، تا جایی که میتونید باید چیزای مختلف رو تست کنید که یه وقت باگ نداشته باشه.GLUEتو این ۶ ماهی که تو شرکت بودم، تا حالا یکبار هم نشده یه فایل xml رو تغییر بدم. یعنی اصلا فایل xml ندیدم، البته توی پروژه‌ها هست ولی نیاز نشده برم استفاده بکنم. از چند سال پیش اسپاتیفای یه مفهومی رو به شکل داخلی معرفی کرد به اسم glue که دقیق یادم نیست مخفف چیه، اگر اشتباه نکنم یه چیزی مثل Global Language for a Unified Experience هست. اومدند تمام کامپوننت‌هایی که تو اسپاتیفای استفاده میشه رو تو یه زبان مشترکی به اسم گلو تعریف کردند. مثلا button, checkbox, recyclerview یا هر چیزی که به عنوان ریسورس تو ویو استفاده میشه مثل background, image, color، هر المنت ui که داریم و میخوایم به کاربر نشون بدیم، یه بار تعریف کردن و شما از اون به بعد از این المان‌های تعریف شده استفاده میکنید و دیگه تو xml نمیگید خوب یه باتن بساز عرضش انقدر باشه طولش انقدر، یا از تم فرگمنت ارث ببر، از تم اکتیوتی ارث ببر، هیچ کدوم از این کارا رو نمیکنید. فقط میگید یه باتن گلو به من بده، خودش میسازه متناظر با استاندارد اسپاتیفای تنظیم و برای شما تو صفحه دیپلوی میکنه. این باعث میشه که شما هر دفعه xml مختلف نسازید و ادیتش نکنید. باعث ui consistency تو تمام پلتفرم‌ها میشه. چون یه تیم پشت گلو هست و کارش اینه که این المان‌ها رو بسازه و بین پلتفرم‌های مختلف سینکش کنه. برای همینه که امکان نداره موقعیتی پیش بیاد که یه باتن تو اندروید یه پیکسل بزرگتر از ios باشه. چون تیمی این کارارو انجام میده براش تست مینویسه نگهداری میکنه maintain میکنه. اگر قرار باشه باتن جدید اضافه شه، شکل جدید اضافه بشه شما نمیتونید این کار رو خودتون بکنید، باید feature request بدید به تیم گلو و بگید من این رو احتیاج دارم. دیزاینر طرح رو بزنه آماده بکنه با کل اسپاتیفای استاندارد بشه و بعد برای پیاده‌سازی تحویل تیم glue بشه. این تیم دیزاینر زیاد لازم داره چون ذاتش دیزاین هست انجینیرهای اندرویدش هم از اینایی هست که فقط دارند با ویو کار میکنند، با xml یا کلاس‌هایی که از ویو اسکتند شده. اصلا لاجیک نمیزنند.تیم ما دیزاینر خیلی کم احتیاج داره گاهی یه دیزاینر برای مدت کمی با تیم ما امبد میشه. مثلا برای ریفکتور کردن browse جلسه گذاشت که میخوام این شکلی بکنم این درفت اولیه کار هست چالش داری یا نه؟ نهایی که شد پیاده میکردیم. دیزاین و داکیومنت میداد این باید این شکلی باشه پدینگ و … این باشه. بچه‌های بک‌اند هم با glue انجام میدادن ولی یه سری کامپوننت‌های کاستومایز شده برا یه پلتفورم هست یا یه فیچر خاص هست معمولا اینارو کلاینت انجام میده.HUBیه فریمورکی تو اسپاتیفای وجود داره به اسم HUB که بخش iosش بصورت open source رو گیت‌هاب شرکت هست، اندرویدش هنوز open source نشده و شاید به خاطر هزینه نگهداری نکنند.اوپن سورس شه هزینه نگه داری میره بالا؟خیلی، چون کدش باید تمیز باشه و مو لای درزش نره. اگر تحت فشار یه کدی رو بزنی میتونی بگی این کامیت رو ۲ هفته دیگه تو اسپرینت بعدی درستش میکنم. ولی اگر open source باشه نمیتونی کد غلط مرج کنی، برای اینکه خیلی راحت ریورسش میکنند و یه سری از نسخه‌های اپلیکیشن ممکنه هک شه. به خاطر همین دردسر داره نگهداری لایبراری‌های open source.شرکت یه چالشی داشت برای آپدیت نگهداشتن کاربرهای قدیمی که هزینش خیلی زیاد بود. یعنی یه شکل یا ظاهر جدید که قرار بود به اپلیکیشن اضافه شه باید کلی برای کاربرهای قدیمی سرمایه‌گذاری میکردن و یه سری کد برای گوشی‌های قدیمی میزدند «if ورژن کمتر از این بود این کار رو بکن». حالا اگر بخواد یه شکلی توی اپلیکیشن عوض شه شما باید اون if رو بریزید بهم و برای همه ورژن‌ها یه چیزی رو عوض کنید. یا اگه recycleView بخواید داشته باشید و یه سری المان تو لیست نشون بدید باید یه xml میساختید ریسایکلر ویو رو داخلش میذاشتید، بعد adapter میساختید و event handlerهای آداپتر رو هندل میکردید، اگر روی آیتم کلیک شد چیکار کن؟ اگر swipe کرد چیکار کن؟ الی آخر. دردسر نگهداری خیلی زیاد بود، برای همین و همینطور جلوگیری از duplicate شدن کد یه مفهومی تعریف کردن که نمیدونم اولین بار اسپاتیفای داده یا جای دیگه به اسم backend driven ui development-BDD یعنی بک‌اند مشخص بکنه ui تو اپلیکیشن چه شکلی باشه، یه چیزی مابین Hybrid نوشتن و Native هست. اگر یه dump از خروجی json صفحات اسپاتیفای بگیرید، میبینید بک‌اند داره استراکچر رندر شدن توی کلاینت رو ارسال میکنه. یه سری چیزها توافق شده بین ۲ تا تیم، مثلا spotify_button, spotify_card, spotify_view, spotify_listview چیزایی که ممکنه یک دیزاینر بخواد توی برنامه رندر شه. یک تیمی وظیفش اینه که این دیزاین‌هارو تبدیل کنه به کامپوننت‌هایی که توی اندروید، ios، وب و دسکتاپ قراره رندر شه و نگه‌داریشون کنه و کافیه ما از کامپوننت‌های هابز استفاده میکنیم. هابز به ما این قابلیت رو میده که بک‌اند بگه این باتن رو با این رنگ رندر کن. توی تیمی که من کار میکنم تا حالا لازم نشده یه دفعه هم یه باتن توی کدم بنویسم، تنها کاری که میکنم جیسانی که بک‌اند میفرسته رو پاس میدم به هابز و میگم این جیسان رو رندر کن. اون خودش با enum‌هایی که داره iterate میکنه، اگر recyclerview بهت دادم برو این رو برام بذار، اگر recyclerview item از نوع فلان بود لیست رو این جوری بساز، و در نهایت میگید که به من یه ویو لیست HUB بده، و این تو فریم‌ورک بین اندروید، ios، بک‌اند و تمام پلتفرم‌ها expose شده. هرچیزی پایین تولبار (به جز تولبار) رو هابز میتونه رندر کنه و بک‌اند به صورت داینامیک میتونه استراکچر صفحه رو عوض بکنه. خیلی راحت بدرد A/B Test میخوره، مثلا میخوایید بدونید توی یک صفحه اگر کاربر لیست‌های ۲ تایی کنار هم ببینه ux بهتری داره یا یه هدر بزرگ با یه سری لیست؟ خیلی راحت با یه کد واحد سمت کلاینت از سمت بک‌اند میتونیم برای کاربرای مختلف تو یه صفحه چیزای متفاوت رندر کنیم. backward compatibility خوبی هم بهتون میده مثلا اگر یه کمپین تبلیغاتی قرار شد لانچ شه برای یه کاربری که از ۲ سال پیش اسپاتیفایش رو آپدیت نکرده خیلی از کامپوننت‌هایی که اون موقع تعریف کردیم رو میتونیم استفاده کنیم بدون این که کاربر مجبور شه آپدیت کنه. به این مفهوم میگند backend driven ui development - دولوپ ui توسط بک‌اند. یا اگر یه تیمی بیاد یه فیچر جدید رو بخواد امتحان کنه، لازم نیست یه ریلیز بدید که اون فیچر جدید امتحان شه. Ui جدید توسط بک‌اند فرستاده میشه، ایونت هندلرهاش سمت کلاینت از قبل توی هابز نوشته شده که مثلا اگر این دکمه رو زدم اون کار رو بکن، اگر لانگ کلیک کردم اون کار رو بکن و …. خوبیش اینه که اگر به هر دلیلی ریلیزهاتون بلاک شد مثلا گوگل شما رو بلاک کرد گفت ورژن جدید نمیتونید بدید چون یه مشکلی وجود داره، ui تون دیگه قدیمی نمیمونه و میتونه عوض شه.طبیعتا اگر بخواید چیزای خیلی خیلی کاستومایزی بنویسید حتما باید ریلز بدید ولی تا حد خوبی جلو این قضیه گرفته میشه.ما برای همچین چیزی داشتیم فکر میکردیم که چطور میتونیم یه معماری استفاده بکنیم که کارمون رو راحت بکنه، mvvm برای این انتخاب شد چون یکی از قواعدی که باید توش رعایت شه اینه که دولوپر دسترسی مستقیم به ویو نباید داشته باشه. یعنی شما به عنوان کسی که داری مدل یه چیزی رو مینویسی یا ViewModel یه چیزی رو مینویسی به خود ویو نباید دسترسی داشته باشی، این برای خودش یه سری چالش میاره. مثلا فکر کن اگر بخوای یه چیزی رو تو این فریمورک تعریف کنی که خارج از توانایی‌های عادی این فریم‌ورک هست عملا هیچ کاری نمیتونی بکنی، چون به ویو دسترسی نداری که مثلا یه انیمشنی روی باتن بزاری. اگه فریم‌ورک ساپورت نکنه نمیتونی، چون اولین کاری که باید انجام بدی view.setAnimation یا … هست. در نتیجه با وجود اون دردسرهایی که ایجاد میکرد چون در اسکیل بالا کار خیلی از تیم‌ها و نگهداری خیلی از چیزها رو راحت میکرد تصمیم بر این شد که رو mvvm پیادش کنیم.Hermesاگر بخوام یه ذره عمیق‌تر شم، از چند سال پیش یعنی زمانی که HTTP1 بود و هنوز HTTP2 استاندارد نشده بود، شرکت فریمورکی به اسم Hermes نوشت (فکر کنم اوپن سورس هست). هرمس یه فریمورکی رو HTTP1 بود که قابلیت HTTP2 رو بهمون میداد. خیلی شرکت‌ها این رو نوشته بودن چون HTTP1 از یه زمانی به بعد دیگه جوابگو نیاز بعضی از شرکت‌ها نبود و اسپاتیفای هم برای خودش نوشت. اسپاتیفای به نظر من استاد ساختن چرخ از اول هست، چون خیلی چیزارو میبینید از اول برای خودشون نوشتن، برای من عجیبه شاید نیاز بود بنویسند، شاید پروژه‌های دیگه جوابگو کارشون نبوده، ولی خیلی چیزارو از اول نوشتن. حتی الانم که HTTP2 اومده به شکل زیر ساختی وارد پروژه شد ولی ما همچنان رو هرمس هستیم. برای ما فرقی نداره که HTTP1 استفاده میکنیم یا HTTP2 چون پروتکل هرمس برای ما همه‌رو هندل میکنه و یه تیم داره که نگهداریش میکنه. خیلی خوب هست این قضیه چون abstract میشه و دیگه لازم نیست ۱۰۰ تیم مختلف بیان آپدیت بکنند، یه تیم آپدیت میکنه برای تمام پروژه apply میشه.Coreتوی اسپاتیفای به مفهومی وجود داره به اسم هسته یا core. کور اسپاتیفای یه سری فانکشنالیتی‌هایی هست که بین پلتفرم‌های مختلف share شده. مثلا player یا نتورکینگ که با hermes انجام میدیم. core با ++c نوشته شده، پروژه نسبتا بزرگی هست و فکر کنم یه تیم ۲۰ نفره فقط مسئول نگهداری core هستند که شامل پلیر، هرمس و یه سری چیزای دیگه هس، من دقیقا نمیدونم چیکار میکنند چون امکان نداره آدم همیشه بدونه همه‌ی تیم‌ها چیکار میکنند. خیلی شرکت بزرگه، فقط کار خودت رو بتونی خوب انجام بدی هنر کردی. من به شخصه به تیم core خیلی علاقه دارم چون ++c رو خیلی دوست دارم. اگه یه موقع احساس کردم میخوام یه چیز جدید رو امتحان بکنم یکی از گزینه‌هایی که دوست دارم contribute به core هست. همونطور که گفتم پلیر با core هندل میشه و یه پروتکل کاستوم براش نوشته شده. پلیر هم یه کد نسبتا قدیمی هست چون از خیلی وقت پیش ساپورت میشد، ولی آخریا ساپورت رو تا اندروید ۴.۱ آوردیم. به خاطر پروتکل‌های مختلفی که بین ورژن‌های اندروید ساپورت میشه نمیتونستیم به مدیا پلیر خود اندروید اکتفا کنیم. مجبور بودیم مدیا پلیر خودمون رو بنویسیم که بتونه کدک‌ها و پروتکل‌های خودمون رو ساپورت بکنه. و این بود که مجبور شدند یه مقدار low level و با ++C بنویسند تا بتونند توی پلتفرم‌های مختلف بصورت مشترک استفاده بکننش. چون نمیشد فقط با جاوا بنویسی، اگر اونجوری میخواستیم بنویسیم باید یکی برا جاوا مینوشتیم، یکی برا ios، یکی برا وب، یدونه برا ps ،xbox و …CI &amp; CDما الان رو continuous integration هستیم و قرار هست تا آخر ۲۰۱۷ continuous delivery بشیم در حال حاضر مشکلی که وجود داره و دردسر ساز میشه اینه که ما تست‌های premerge و postmerge داریم. یعنی مثلا شرکت ۱۰۰۰۰ تست داره، از این ۱۰۰۰۰ همش قبل از اینکه کد مرج بشه، اجرا نمیشه یه سری بعد از مرج شدن روی کد اجرا میشه. این باعث باگ‌های بدی میشه، برای مثال شما ممکنه کدی بنویسید و تست‌های premerge بگند کدت OK هست و مرج بشه تو مستر. تست‌های postmerge هر چند ساعت یک‌بار اجرا میشند، به خاطر اینکه خیلی تست‌های پر هزینه‌ای هست و طول میکشه بخواد اجرا شه. معمولا هم end to end تست‌ها هستند. بعد از مرج شدن کد و قبل از اینکه تست postmerge روی کد شما اجرا بشه یه نفر دیگه هم یه کدی نوشته برای اون هم تست‌های premerge با موفقیت گذرونده میشه و کدش توی مستر مرج میشه. حالا بعد از ۶-۷ ساعت تست‌های postmerge اجرا میشند و میبینند این دوتا pr با هم inconsistent هستند و نمیتونند با هم اجرا بشند. یا اصلا باعث crash میشه. اون موقع هست که میگند مستر شکسته -master is broken. کد اصلی مستر ممکنه خراب شه و اون موقع معمولا یه هشداری توی شرکت داده میشه و یکی باید بیاد (نویسنده اون ۲ تا کامیتی که باعث شدن مستر بشکنه، اگر خودش نیست جایگزینش اگر جایگزینش نیست مسئول پروژش) و خیلی سریع با هم مستر رو درست کنند. و اگر نه کل پروژه گیر میکنه. این یکی از مشکل‌های عمده ci هست، که همه کامیت‌ها قابل deliver کردن و قابل deploy نیست. به خاطر همین از یه سال پیش شرکت یه تیمی رو تشکیل داد که وظیفش کمک به همه تیم‌هاست که از ci برسند به cd و هر کامیت قابل deploy باشه. یه خورده هم پروژه زمان بری هست، چون کلی از تست‌های شرکت باید تغییر بکنه، خیلی‌هاش اصلا باید زیر ساختش تغییر کنه ولی هم کار جذابیه و هم نتیجه‌اش ارزشمنده.کد گردیاسپاتیفای با اینکه قراره بخش‌هایی از پروژه رو نشونمون بدی مشکلی نداره؟بهشون گفتم که میخوام یه چیزایی رو برای آموزش نشون بدم.برای فیچرهایی که از ۱-۲ سال پیش شروع شده همه حتما باید از دگر استفاده کنند. این الان کل پروژه هست و این پکیجی هست که کد سرچ توش وجود داره قبلا این پکیج ۲ تا بود، چون ۲ تا ورژن از سرچ وجود داشت. ما با یه مکافاتی قبلی رو ریفکتور کردیم و آوردیم رو فریمورک جدید و قبلی رو پاک کردیم.استراکچر اینجوری هست که ما با خودمون توافق کردیم پرزنتر یه جا باشه، کدی که لاگ میکنه یه جا باشه، بخشی که هیستوری سرچ هست یه جا، کال‌بک‌ها تو یه پکیج هست، کاموننت‌ها (ویوهایی که کاستوم کردیم خارج از هابز، توی فریمورک هابز نبودن و بهش اضافه کردیم) و کامندهایی که ساپورت میکنیم تو پروژه رو هم توی یه پکیج گذاشتیم.فریمورک هابز برای ما یه سری کارهای جادویی انجام میده، مثلا رندر میکنه یا یه سری کامند که از پیش تعریف شده از سمت بک‌اند ارسال شه رو خودش هندل میکنه. مثلا شما میتونید با کمتر از یه ساعت کد زدن ورژن ساده صفحه سرچ رو یه بار دیگه از اول درست کنید، یعنی یه پرزنتر ساده مینویسید، بعد یه endpointی رو صدا میزنید و جواب رو به هابز تحویل میدید، خود هابز بقیه چیزارو هندل میکنه و میاره بالا، همه کارهایی که بصورت پیشفرض نیازه مثل click, long click, play و یه سری کامند دیگه رو خودش ساپورت میکنه. منتها یه سری کارهارو لازم هست که به صورت کاستومایز شده انجام بدید مثلا اگر توی سرچ رو چیزی کلیک کردید، بسته به این که چی هست کارهای مختلفی باید انجام شه. مثلا اگر آهنگ باشه باید پلی بکنه، یا اگر آلبوم باشه باید بره تو صفحش و تو هیستوری کاربر هم نگهش دارید، این چیزی بود که بصورت پیش فرض ساپورت نمیشد. مثلا برای این مجبور شدیم یه command handler کاستومایز شده بنویسیم و توی هابز خودمون اضافش کنیم، که فلان کامند رو خودم میخوام هندل کنم؛ یا وقتی رو این کلیک شد دیگه هابز هندلش نکنه من خودم هندلش میکنم.فیسبوک هم یه کتاب خونه داره که دیستریتیو ui مینویسی دیدینش؟ (Litho)ما کتاب‌خونه‌های فیسبوک رو اکثرا نمیتونیم استفاده کنیم به خاطر لایسنسش که یه مقدار سختگیرانست، چند وقت پیش هم react رو عوض کردند و ما تازه تو دسکتاپ شروع کردیم از react استفاده کردن وگرنه قبلا فکر کنم angular مینوشتند.این کدی هست که باهاش لاگ‌های مختلفی که تو پروژه انجام میشه رو مینویسیم، ۲ تا پرزنتر مختلف داره یه پرزنتر برای صفحه سرچ و یکی برای صفحه‌ای که با زدن دکمه see all میرید اون تو.توی کد مدل‌های مختلف ریکوست رو ما به بک‌اند میزنیم، مثلا همین صفحه سرچ ریکوست‌های مختلفی به بک‌اند میزنه، یکیش صفحه سرچ تو حالت عادی هست که میاید یه کوئری میزنید یه جواب میگیرید، ممکنه از صفحه سرچ تو حالت آفلاین استفاده بکنید که ما به هسته ++C ریکوست میزنیم و اون هم تو مجموعه ریکوست‌ها قرار میگیره، یا صفحه see all هم یه مدل ریکوست میزنیم.شرکت یه محصول جدیدی تعریف کرد که خوبه در موردش صحبت کنیم. اسپاتیفای بعد از یه مدتی بررسی و نتیجه‌گیری، بصورت عمومی اعلام کرد که ما یادگرفتیم چطور کاربر پرمیوم بسازیم، یعنی کاربر معمولی رو تبدیل کنیم به کاربر پریمیوم و حتی یاد گرفتیم چطور کاربر پرمیوم رو پرمیوم نگه داریم. در نتیجه خوب داریم پول در میاریم. فقط مشکلی که داریم اینه که نمیتونیم به اندازه کافی کاربر جدید بگیریم و مجبور شدند سراغ کشورهایی که بهشون بازارهای نوظهور (emerging market) میگند مثل چین، هند، اندونزی یا کشورهایی مثل مکزیک، برزیل برند. با توجه به محدودیت‌هایی که این کشورها دارند، اپ فعلی اسپاتیفای اصلا براشون مناسب نیست. این بود که یه ماموریت جدیدی تو شرکت تعریف شد که یه اپی بنویسیم (داخل خود اپ اسپاتیفای) که برای این مدل کشورها طراحی بشه، یکسال کار کردند و این محصول الان ریلز شده. کلا یه محصول دیگه داخل اپ هست. یعنی وقتی کاربر وارد اپ میشه کلا میره تو یه سری پکیج دیگه. مثلا نویگیشن باتن ۳ تا بود، صفحه اصلی استراکچرش عوض شد، بک‌اندهایی که باهاش صحبت میکردیم عوض شد، قابلیت‌های سرچ تغییر کرد، صفحه browse رو با سرچ یکی کردیم. خلاصه بسته به اینکه چه مدل کاربری هستید، تعداد زیادی ریکوست‌های مختلف به بک‌اند زده میشه.برای همین وقتی شما یه پروداکتی توی شرکت دستتون باشه، روی همون تعداد زیادی A/B Test داره انجام میشه یا در واقع پروداکت‌های مختلفی رو در قالب یه پروداکت مشخص دارید به مردم ارائه میدید (مثل همین اپی که برای بازارهای نوظهور درست شده و توی همون اپ اسپاتیفای هست)، یعنی معمولا سرچ رو یه سری کاربر به یه شکل میبینند یه سری دیگه به یه شکل دیگه و همه اینا جزو زیر مجموعه تیم شما هست. اگه قرار باشه یه چیزی عوض شه یا یه پارامتری به صورت کلی به بک‌اند ارسال بشه یا یه uiیی اضافه شه، معمولا شما مجبورید کل این پروداکت‌ها ریفکتور کنید. در نتیجه معمولا حجم کاری تو یه فیچر، از چیزی که به نظر میاد بیشتره.این پکیح rx ما هست، این پکیج مجموعه کارهایی هست که ما با rx java انجام میدیدم.اگر برای صفحاتی که A/B test داریند تسک بیاد رو کدومش انجام میدید؟باید بری همه رو ریفکتور کنییعنی جفتش تو apk هست و سرور میگه کدوم رو لود کنه؟تو سرچ حتی چالش برانگیزتر بود. صفحه اصلی یه معماری و یه سری فریمورک استفاده میکرد دکمه see all رو که بزنی میره تو یه فرگمنت دیگه تو اون کلا یه چیز دیگه بود. مثلا اگر رو یه ترک کلیک کنی تو صفحه اصلی سرچ و اون صفحه باید یه کار رو بکنه، نمیشه تو یکیش پلی کنه تو اون یکی یه کار دیگه. ۲ تا چیز رو باید همزمان maintain میکردی. یا بدتر تو هر کدوم از این فرگمنت‌ها ۴ تا A/B test وجود داشته باشه باید اونا رو هم حتی ساپورت بکنی بعضی وقت‌ها وقعا چالشی میشد ساپورت کردن چندتا ورژن از یه چیزی. به این مفهوم که چندتا ورژن مختلف و چندتا معماری و سبک کد زدن مختلف توی کدبیس باشه میگن شما Technical debt دارید (بدهکاری فنی)، یعنی شما هزینه نگهداری یه کد قدیمی رو میدید و این رو اگر توی طول زمان درست نکنید به صورت تصاعدی بالا میره و تکنیکال دبت رو توی سطح معینی نگه دارید.کی باید درستش کنیم؟معمولا اینجوری میشه، برای سه ماه بعد میخوای برنامه‌ریزی کنی و یه ab تست گنده‌ای روی پروژه ران بشه، میپرسن چقدر پیاده سازیش طول میکشه؟ که شما میگی دو ماه. میگن چرا دو ماه؟‌ میگید چون این تکنیکال دبت داره. میگن خیلی خب ab تست رو نگهدارید و اول تکنیکال دبت درست کنید. سه ماه زمان میذارید، کلی کد ریفکتور میکنید، تست‌های قدیمی رو حذف میکنید، معماریشو عوض میکنید و باید از اول بنویسید.این رو پروداکت اونر میگه یا تکنیکال لید؟بستگی داره، ممکنه حتی از بالاتر بیاد.همه اینا که میگی توی پکیج سرچ و برای سرچ بود؟آره خارج از سرچ نیست، هر فیچر همه اینارو برای خودش جداگونه دارههمه تیم‌ها همین شکل پکیج‌بندی میکنند یا فرق داره؟کم و بیش همینجوری هست، مخصوصا اسکوادهای که تو یک ترایب باشند، چون با هم جلسه زیاد داریم و با هم زیاد کار میکنیم. برای همین احتمالا کدهای یه اسکواد تو نیویورک رو ببینید خیلی متفاوت هستند. البته معمولا اگر تیم‌ها با هم قرار باشه کار کنند، یه داکیونتی رو با هم رد و بدل میکنند به اسم «چطور میخوایم با هم کار کنیم؟» اگر قرار باشه از تیم شما برای ما پول ریکوست بیاد و ما ریویو بکنیم این باید حداقل از ۲ هفته قبل اطلاع رسانی بشه که ما بتونیم براش برنامه ریزی کنیم یا اگر باگی پیش بیاد که شما کدش رو ادیت کردید خودتون باید رفعش بکنید. یه سری قرارداد میبندیم که ما قرار هست اینجوری با هم کار بکنیم.خلاصه rx java تو تیم ما خیلی زیاد استفاده میشه مثلا توی فرگمنت سرچ ما بلافاصله یه subscription میسازیم و observableش رو وصل میکنیم به اون EditTextی که اون بالا هست، به محض این که شما شروع میکنید به تایپ کردن، اون EditText یه سری متن emit میکنه. ما میایم یه دونه observable از این استرینگ ها میگیریم و این رو با یه observable دیگه به اسم session state که session رو مانیتور میکنه (الان آفلاین شدم یا الان آنلاین شدم) combine میکنیم و search params ازش در میاریم. داخلش پارامترهای جستجو هست مثل آفلاینم یا الان آنلاینم تو چه وضعیتی هستم، کوئری چیه. بعد برای اینکه بتونیم یه ریکوئستی بسازیم، معمولا میاییم این ۲ تا پارامتر رو میگیریم و تحویل یه کلاس دیگه میدیم به اسم SearchRequestPerformer قبلا تو کلاس SearchRequestCreator بودیم. SearchRequestPerformer پارامترها رو میگیره و یه سری چک انجام بده، آیا کوئری که داریم بهش میدیم، خالی هست یا نه؟ اگه خالی نیست این رو flatmap کن به یه کلاس دیگه و … پارامترها رو میسازه و در نهایت تحویل SearchRequestRunner میده. اتفاقی که اینجا میافته، عملا این هست که ما اومدیم به جای اینکه یه زنجیره‌ی بزرگ از اوپراتورهای rx java داشته باشیم، برای اینکه خیلی راحت بتونیم براشون تست بنویسیم (چون این کلاس‌ها باید ۱۰۰٪ تست کاوریج میداشتند) اومدیم هرکدوم از کارایی که قرار بوده سرچ بکنه رو شکوندیم به کلاس‌‌های کوچیک، یه کلاس میاد یه observable میگیره باهاش یه پارام هولدر میسازه، یدونه فقط پارامترهایی که باید برای بک‌اند بسازه رو نگه میداره، وقتی ساخت تحویل یه کلاس دیگه میده فقط چک میکنه که پارامترهاش ولید هست یا نه، وقتی که چک کرد ولید هست تحویل یه کلاس دیگه میده این چک میکنه که سرچ الان آفلاینه یا آنلاین و مثلا تو کدوم استیت هست، مثلا برای صفحه see all دارم سرچ میکنم یا مثلا کسی با پارامتر خاصی من رو لانچ کرده یا نه، این رو که آماده کرد تحویل یه کلاس دیگه میده که فقط تایپ ریزالور هست چک میکنه که این دیوایس چه فیچری ساپورت میکنه آیا من میتونم مثلا از Protobuf استفاده کنم یا مجبورم از Gson استفاده کنم، و برای ارسال این دیتا به بک‌اند یه سری چیزارو چک میکنه. این rx chain رو شکوندیم به کارای خیلی کوچیک، یعنی وقتی نگاه میکنید، ما تعداد خیلی زیادی کلاس داریم که هرکدومش یه کار خیلی کوچیک رو میکنه.یعنی الان لاجیکی که هر اوپراتور rx java میخواد اجرا کنه رو تبدیل به یه کلاس کردید؟یه جورایی آره، در واقع rx chain ما به این شکل هست هرکسی کارش رو میگیره تحویل اپوراتور بعدی میدهجنس این کلاس چیه ؟ یه کلاس معمولی جاواست همشون ؟آره همشون POJO هستند و به همین خاطر هست که میشه ۱۰۰٪ براشون تست نوشت.اومدیم در واقع این کارارو شکوندیم به یه سری کار خیلی کوچیک که هرکدوم یه کار خیلی کوچیک رو انجام میده و براش تست متناسب با خودش رو نوشتیم. یعنی شما امکان نداره حالتی پیدا کنید که تستی براش نوشه نشده باشه، چون کار خیلی کمی رو داره انجام میده.مدل کد نوشتنمون تو شرکت معمولا به این شکل هست، اول میریم فرگمنت و پرزنتر رو میسازیم، بعد سنارویوهایی که قرار هست پرزنتر هندل کنه رو با TDD تستش رو مینویسیم و پیاده سازی میکنیم، دونه به دونه میریم جلو. این یه شمای کلی بود از این فیچری که ما تو سرچ پیاده سازیش کرده بودیم. حالا همونطور که گفتم برای هرکدوم از اینا باید دونه به دونه تست بنویسیم، یعنی بنویسیم که این کلاس کاری که باید انجام بده رو انجام میده یا نه، مثلا این تستی هست که برای پرزنترمون نوشتیم، تستها معمولا اینجوری شروع میشه که یه annotation دارن که این فیچر برای کدوم تیم هست. با Robolectric اجرا میشن. یه سری پارامتر رو به صورت پیشفرض با static final و … آماده کردیم، به جز اون چیزی که اتفاق می‌افته شما توی کلاس‌ها نباید هیچ موقع آبجکتی رو new کنید، هرچیزی که یه کلاس لازم داره رو باید همیشه با dagger و بصورت constructor injection بهش پاس بدید، چون dependency injection باعث میشه تست نوشتن خیلی راحت بشه.تعداد زیادی آبجکت داریم که کنار همشون خورده mock، علتش اینه که ما میاییم یه کلاسی رو میسازیم توی حالت تست، تمام آبجکت‌هایی که لازم داره رو mock میکنیم و دونه دونه توش اینجکت میکنیم، یعنی یه کلاس رو با تمام چیزایی که لازم داره به صورت ماک شده میاریم بالا. مثلا کلاسی که میخوایم تست کنیم یه کلاس دیگه رو برای هندل کردن یه باتن نیاز داره، این کلاس رو mock میکنیم و از طریق استراکچر given when then بهش دیتای ماک میدم. این استراکچر اینجوریه:Given یعنی در شرایط فلانWhen اگر این اتفاق افتادThen این کار رو انجام بدهمثلا وقتی رو این دکمه کلیک کردم و منتظر برگشت یک استرینگ از یه فانکشن بودم، این استرینگ رو برگردون به فانکشن بده. این آبجکت‌های ماکی که داشتیم رو تنظیم میکنم میگم تو این شرایط اگر این رو کلیک کردم این کار رو بکن، اگر این کار کردم این کار بکن و سناریوهایی که واقعا توی اپ داره اتفاق میافته رو تنظیم میکنم و دونه دونه شروع میکنم چک کردن فانکشن‌ها که تو پرزنتر هست، مینویسم اگر این اتفاق افتاد با توجه به این ماک‌ها من منتظرم این جواب رو بگیریم، آیا میگیرم یا نه، اگر گرفتم فانکشن داره درست کار میکنه اگر نگرفتم یه چیزی این وسط خراب شده.برای سناریوهای مختلف باید یونیت تست بنویسیم. مثلا تعداد خط کد پرزنتر ما ۴۳۴ خط هست ولی تعداد خط کلاس تست پرزنتر ۴۴۵ خط هست. بیشتر از ۱۰۰٪ کد کاوریج داره. یعنی احتمالا یه سری سناریو، دپریکیت شده.مثلا برای یه خط ۲ تا تست نوشتید؟نه نمیشه برای این که یک نفر کدت رو ریویو میکنهمنظورم اینه که یه متد که نوشتید فقط یه حالت تست مینویسید یا چندتا؟یک متد باید یک کار رو بکنه، در نتیجه وقتی که برای یه متد ۲ تا تست مینویسی، یعنی متدت یه ایرادی داره. طبق اصول solid باید single responsibility داشته باشه. ولی بعضی جاها ممکنه پیش بیاد، اصلا نمیشه کاریش کرد. ممکنه این فانکشن ذاتا side effect داشته باشه و هیچ کاریش نمیشه کرد ولی به صورت کلی باید اون رو رعایت کنیم.حالا فکر کنید یک المان ui رو ادیت کردید، برای این که تستتون یا پول ریکوستتون قابل مرج شدن باشه، باید برای اون کامپوننت ui که تغییر دادید integration test بنویسید. یعنی یدونه کامپوننت رو از شرایط واقعی مجزا میکنید، تست‌هایی که لازم هست رو روش اجرا میکنید، بدون این که مهم باشه در سایر المان‌های اطراف اتفاقی می‌افته یا نه، یعنی در دنیا واقعی نمیخوایم تستش کنیم، فقط خود اون کامپوننت رو تنها میخوایم تست کنیم. مثلا رو یه باتن که کلیک میکنید، میخواید یه انیمیشنی روش اجرا بشه، این رو براش یه کامپوننت تست مینویسید، چطور؟ ما یه ابزار داخلی داریم که باهاش اینتگریشن تست‌ها رو مینویسیم، (اسم فریمورک Caset هست) فکر نمیکنم اوپن سورس شده باشه و فکر کنم که هیچ وقت هم نمیشه، چون خیلی هکی هست و همه چیزش با هک کردن یه سری چیز روی robolectric نوشته شده. مثلا این کلاس اینتگریشن تستی هست که برای صفحه اصلی سرچ وجود داره، یدونه فرگمنت میسازه، فرگمنتی که میخوای توش یه تستی رو انجام بدی، مثلا من فرگمنتم رو کانفیگ میکنم با این شرایط میارم بالا، بهش میگم من یه کاربر free میخوام، یعنی زمانی که این تست رو ران میکنی، یه کاربر free بساز و اینجکت کن تو محیط برنامه و برنامه رو توی حالت پروداکشن بیارش بالا، یه سری فلگ override میکنم برای این کاربر، مثلا تستی که اینجا نوشتم، اسمش اینه testWithSearchResult، دارم تست میکنم که سرچ وقتی که موفقیت آمیز هست، result که من منتظرش هستم باید داشته باشه. چه جوری این کار رو انجام میدم؟ میام یه محیط کانفیگ میکنم، این تست یه جورایی BDD هست یعنی یه جورایی مبتنی بر یه رفتار مشخصی هست. حالا یه محیطی رو کانفیگ میکنم میگم اگر کاربر تو این فرگمنت اومد بالا:when زمانی که من روی اون TextFiled فکوس کردم، کوئری که اون بالا تعریف کردم رو بذار توش، یه مدت زمانی منتظر باشthen اول چک کن که ریزالت‌ها لود شدن یا نه، که این یه فانکشنی هست که به شما برمیگردونه که ریزالت‌ها لود شدند یا نه، آخر سر چک میکنم که get number of show all تعداد آیتم‌های شو آل من بیشتر از ۰ باشه.این یه تست خیلی عمومی بود، بسته به سناریویی که میخوایم تست بکنیم، دقیقتر از این تست میکنیم. مثلا برای وقتی که چک میکنیم ریزالت وجود داره، تعداد تاپ ریزالت‌ها، ریکوردها یا آرتیست‌ها رو میگیریم. ما برای اون کوئری مشخص، اینجا مشخص کردیم که قرار هست بک‌اند ۰ تا تاپ ریزالت برگردونه ۴ تا آرتیست ۴ تا آلبوم الی آخر. کامپوننت این فرگمنت باید عین سناریویی که من منتظرش بودم رندر بشه، اگر رندر شد یعنی تست موفقیت آمیز بوده و اگر نشد یا یه مشکلی تو بک‌اند وجود داره یا تو فرانت‌اند، این تست همیشه هر ۶ ساعت یک بار روی کد بیس اجرا میشه، فارغ از اینکه کامیت جدیدی وجود داشته باشه یا نه، به ما کمک میکنه که مثلا اگر بک‌اند اشتباه کرد و به جای ۴ تا آرتیست ۳ تا برگردوند، تست fail میشه. ما سریع متوجه میشیم و براش تیکت مینویسیم و حلش میکنیم. یا مثلا اگر یکی اومد کد مارو ویرایش کرد، این تست ران میشه و متوجه میشیم مثلا کامپوننت ما بعد ویرایش درست رندر میشه یا نه.بک‌اند رو تست میکنید یا بک‌اند ماک هست؟هم میتونه بک‌اند ماک شده رو تست کنه، هم بک‌اند واقعی بستگی به کانفیگ داره.بک‌اند همیشه دیتا ثابت میده برای این تست‌ها؟ از کجا میفهمیم که بک‌اند خرابه یا مثلا یه آرتیست به دیتابیس اضافه شده؟این رو دیگه باید چکش کنیم و ببینیم اشتباه از کجا هست ولی این فلگ‌ها که اورراید میشه معمولا برای این هست که همیشه تا حد امکان یه ریسپانس مشخص رو بگیره.اگه کامپوننت ui رو ادیت بکنید، باید یه دونه اینترگریشن تست مناسب باهاش رو هم بنویسید. بعد از این مرحله، یه مرحله بالاتر هم تو هرم تست وجود داره به اسم end to end testing، تست‌هایی هستند که با espresso مینویسیم و رو دیوایس واقعی اجرا میشند و همیشه با بک‌اند واقعی کار میکنند.این هرمی که گفتی یونیت تست مشخص هست، اون بالا بالایی هم که end to end test هست مشخصه که با اسپرسو کل یه عملکرد برنامه تست میشه مثلا یوزر چه کارایی میتونه انجام بده. حالا این وسط اینترگریشن تست میشه اکتیویتی با فرگمنت؟ چه جوری محدودش رو مشخص میکنید؟هر چقدر به سمت بالا هرم تست حرکت بکنی هزینه نگهداری و اجرا کردنش خیلی میره بالا. یعنی ما هر دفعه که end to end تست ران میکنیم، end to end تست میاد یه apk رو برمیداره میبره رو یه دونه دیوایس نصب میکنه، login میکنه و …، هزینه اجرا این تست خیلی زیاده و وقتی ما بخوایم اندتواند تست بخش سرچ رو ران کنیم، نیم ساعت طول میکشه. اما اینترگریشن تست‌هامون کمتر از ۱ دقیقه و یونیت تست کمتر از ۱۰ ثانیه طول میکشه. در نتیجه اگر قرار باشه یه اندتواند تست بنویسید باید حواستون باشه که آیا واقعا لازمه؟ چون تا جایی که میتونید، همه چیو باید با یونیت تست و اینتگریشن تست کاور بکنید. معمولا end to end تست رو برای جایی مینویسیم که سناریو فوق‌العاده حیاتی و critical هست، مثلا لاگین هیچ موقع نباید fail بشه، اگر بشه خیلی افتضاح هست، کاربر جدید نمیتونه استفاده بکنه. یا مثلا توی سرچ این که ریزالت‌ها یکبار لود شه و باتن‌ها کار بکنه رو حتما باید تست کنیم. برای این که هر جمعه ساعت ۱۰ تا ۱۱ صبح regression test داریم. تمام اعضای تیم جمع میشن و نفری یه گوشی رندوم با یه کاربر رندوم برمیدارن و شروع به تست کردن یکی از فیچرهای شرکت میکنند، توی رگریشن تست همیشه همه اعضا از پروداکت اونر، مدیر، دیزاینر، برنامه نویس همه جمع میشند و تست میکنند، خوبیش هم اینه که اپ از زوایای مختلف تست میشه و اینجوری نیست که فقط از زاویه دید برنامه نویس تست شه. ریگریشن تست رو هفته‌ای یکبار اجرا میکنیم ولی end to end تست رو هر ۶ ساعت یکبار، اینتگریشن تست روی تک‌تک کامیت‌ها تست میکنیم یعنی مطمئن میشیم چیزی که میخوایم واقعا رندر میشه یا نه.چندتا از هر کدوم دارید؟برای هرکدوم ۲۰-۳۰ تا تست رو در نظر بگیر، تو یه کلاس منظورم هست، البته باید زمان بیلد شدن گریدل رو هم بهش اضافه کرد ولی اون خارج از دست ماست تا حد زیادی و ثابت هست. تعداد کلاس هم متناظر با تعداد کلاس‌های که بیزنس لاجیک هست، تو یه پکیج شاید ۴۰-۵۰ تا کلاس هست، برای اینتگریشن تست حدود ۵-۱۰ تا کلاس و اندتواند ۱ دونه کلاس.گوگل جدیدا توی داکیومنت اندروید، یه بخشی نوشته، که مثلا end to end تست ۲۰ درصد باشه، یونیت تست ۶۰ درصد و … ولی کلا یه سری مثال‌ها گفتن اینترگریشن تست رو با اسپرسو بنویسید، خیلی‌ها اصلا اسپرسو تو اینتگریشن استفاده نمیکنند یا همش روبوالکتریک استفاده میکنند و … من نمیفهمم مثلا اشکال داره اسپرسو تو اینتگریشن استفاده شه؟ یا چون نیاز نمیشه کسی استفاده نمیکنه؟اسپرسو یه قابلیت خوبی که به شما میده اینه که میتونید رو دیوایس واقعی اجراش کنید که میشه همون end to end تست، میتونی تو لول انتگریشن هم ازش استفاده کنی. ولی روبو الکتریک روی دیوایس واقعی کار نمیکنه و اندروید رو امولیت میکنه.اندتواند تست هم معمولا با توافق تیم مشخص میشه «این سناریو خیلی مهم هست پس end to end تست بنویسیم». همه هم میگند تا جایی که میتونید هرم تست رو پایین نگه دارید ولی اینکه درصدش چقدر باشه سناریو چطور باشه تیم به تیم فرق داره. یه سختی دیگه هم که end to end تست داره اینه که باید براش ci یا cd تعریف کنید. مثلا jenkins راه بندازید، اتومیت کنید که اینا کار اندروید دولوپر نیست، اینا یه دوآپس خوب میخواد که این محیط‌ها رو خوب بشناسه، از یه برنامه نویس عادی اندروید نمیشه انتظار داشت که هم کد خوب بنویسه، هم یونیت تست بنویسه، هم end to end تست بنویسه. همه اینا رو باهم از یه تیم کوچیک نمیشه انتظار داشت. اگر بخوای انتظار داشته باشی، سرعت کارت خیلی میاد پایین. ما خیلی از کارامون تو شرکت دلیگیت شده به یه تیم دیگه. مثلا خود تیم ما باتن نمینویسه و دلیگیت شده به تیم دیگه. هر تیمی داره یه کار کوچیکی انجام میده.تیمی که تست مینویسه یه تیم دیگست یا خودتی؟نه خودمم. تست رو خود دولوپر باید بنویسهتیمی هم دارید که تست دستی انجام بده؟نه همون ریگریشن هست که هر جمعه همه باهم تست میکنیم. یه نفر با نقش qa داریم ولی اون یه نفر نمیرسه همه چیز رو تست کنه، معمولا qa داشبورد تست رو maintain میکنه، مثلا صبح به صبح که میگیم این تست fail شد، معمولا اون آدم میره چک میکنه چرا fail شده، اگر سوادش رو داشته باشه ممکنه حلش بکنه اگر نه اساین میکنه به یه دولوپر.ما کلا ۲ تا ویو تو سرچ نوشتیم، جمعا یدونه اون تولبار اون بالا که کاستومایز شدست و تولبار خود اندروید نیست، یکی هم وقتی که سرچ میکنید و نتیجه خالی باشه اون empty view میاد رو هم خودمون نوشتیم. بقیه چیزایی که تو سرچ نوشتیم لاجیک هست یا تست. ویو نمینویسیم. معمولا یه چیزی هم داریم به اسم ویو بایندر چون لایبراری که استفاده کردیم mvvm بوده، ویو بایندر واسطی هست بین ما و اون لایبراری که با توافق ما و اون تیم نوشته شده. مثلا ما بخوایم یه انیمیشنی اجرا بکنیم به ویو بایندر میگیم با اون لایبراری صحبت میکنه، خیلی چیز خاصی نیست.بقیه کلاس‌ها هم یا پرزنتر هست یا ماژول‌های دگر یا کامپوننت یا فرگمنت‌ها و اینطور چیزها. این کد بیس یکی از تیم‌هایی هست که داره اونجا کار میکنه.کد review مکانیزم خاصی؟ هرکسی میتونه کد کس دیگه‌ای رو ریویو کنه؟تمام کلاس‌هایی که نوشته میشه یه annotation داره که برا چه فیچری هست شما اگر کدی رو کامیت بکنی که مثلا یکی از کلاس‌های فیچر ما ادیت شده باشه ما به صورت اتوماتیک پینگ میشیم تو اون کد ریویو. ولی الزامی نداره ما حتما تایید کنیم چون طرف ممکنه یه خط کامنت نوشته باشه. اگر بخوان یه آدم خاص ریویو بکنه پینگش میکنند ولی به صورت کلی هر تیم یه استاندارد داره که خود تیم مشخص میکنه. مثلا استاندارد تیم ما اینه که حتما اینتگریشن تست ۱۰۰٪ هست یا naming convention مشخص داریم comment style مشخص داریم اینارو کسی که با کد ما کار میکنه باید رعایت کنه. اگر یه فیچر رو برای یه تیم دیگه ادیت بکنی یه ایمیلی برات میاد و میگه این داکیومنت این تیم هست و باید بخونی و میگه آیا اینا رو رعایت کردی یا نه؟ چون نمیشه همه چیز رو تو استاتیک آنالایز چک کرد.ساختار پروژه رو کی تعیین میکنه؟تو طول زمان عوض شده ولی به صورت کلی هر تیم مسئول حفظ و نگهداری سلامت فیچر خودشه. کس دیگه‌ای نمیاد بهت بگه اینجوری کار کن مثلا از این استفاده کن خوبه یا بده. تماما به اختیار خود تیم هست و تو تیم تصمیم گیری میشه که چه شکلی باشه. این که به صورت کلی پروژه چه شکلی باشه معمولا انجینیرهای قدیمیتر تو لول‌های بالاتر، مثلا یه نفر لید ترایب هست که اون جهت گیری فنی ترایب رو مشخص میکنه. معمولا هرچی لول بره بالاتر ضریب نفوذ روی پروژه به شکل high level افزایش پیدا میکنه. مثلا این که ما نمیتونیم از یه سری لایبراری‌ها استفاده بکنیم دلیل اینه ترایب لیدمون نمیتونه اجازه بده این لایسنس رو تو پروژمون داشته باشیم.سوال‌های بچه‌هایه جا گفتی اگر به چندتا بخش پروژه کمک کنی مثل بک اند، دیتا ساینس و … استیتت میره بالاتر و وظایف بیشتری انجام میدی، آیا توی فیلد خاص خودت پیشرفت کنی بهتر نیست و برای شرکت ارزشمندتر نیست تا اینکه سعی کنی توی چندتا فیلد تلاش کنی؟این سوال خوبیه که یه کار رو خیلی خوب انجام بدی بهتره یا چندتا کارو انجام بدی؟ دو مدل کلی برنامه‌نویس داریم. برنامه‌نویس T شکل و اگر اشتباه نکنم I شکل.برنامه‌نویس T شکل برنامه‌نویسایی هستند که مثل حرف انگلیسی T بالاش یه خط داره که به یه خط دیگه عمود هست، یعنی شما توی دو جهت دارید حرکت میکنید. مثلا ممکنه یه سری زبان‌های مختلف رو یاد بگیرید. این برنامه‌نویسا معمولا کسایی هستند که برای کارایی مثل مدیریت پروژه خیلی خوبن، کسایی هستند که معمولا میتونن بین تیم‌ها مشترک باشند و آدم‌های ارزشمندی هستند.در برابر اینا برنامه‌نویسای I شکل یه خط دارند، معمولا اینا آدم‌هایی هستند که توی یه زبون خیلی خیلی عمیق میشن. اینا آدم‌های کار راه اندازی هستند، یعنی کسایی هستند که وقتی هیشکی نمیتونه یه چیزی رو درست بکنه، اینا میان درست میکنن. سطح کاریشون هم خیلی بالاست و گرون قیمت هستند.اینکه شما کدوم یکی از اینا بشی دست خودت هست، انتخاب خودت هست چه مدلی پیشرفت بکنی. توی همه‌ی شرکت‌ها به جفتشون نیاز هست. بعضی وقتا واقعا پیش میاد آدم از کاری که میکنه حوصلش سر میره، نیاز داره یه چیز جدیدتری رو امتحان کنه، مثلا شما وقتی دو - سه سال روی یه codebase کار کنی به همه چیزش تسلط داری. خب اون کدبیس چالش جدیدی برات ایجاد نمیکنه. خب اون موقع هست که شما یا باید بگی من از این شرکت برم به شرکت دیگه یا مثلا اگر شرکت امکانش رو داشته باشه بگه بیا برو توی یه تیم دیگه، برو یه زبان دیگه بنویس برو یه چیز دیگه یاد بگیر. چون این آدم آدم ارزشمندی برای شرکت هست، سه چهار سال دانش این آدم توی این شرکت بوده و خیلی حیف هست بذاریم بره. مثلا یا حقوقش رو بیشتر بکنن یا یه چیزی بهش میدن که توی شرکت بمونه. معمولا توی شرکت‌های بزرگ سعی میکنن همچین امکانی رو به دولوپر‌ها بدن که اگر بخوان بتونن یه چالش جدیدی داشته باشن برای انجام دادن. من بشخصه ترجیح میدم T شکل باشم.چطور میشه یه تست خوب نوشت؟ اگر کتابی هست معرفی کن؟ اصن یه تست خوب به چی میگید؟یه تست زمانی میتونه یه تست خوب باشه که فانکشنالیتی خوبی رو تست کنه، یعنی شما در درجه اول باید کدی بنویسید که تمیز باشه تا بتونید تست خوبی بنویسید. مثلا یکی از مباحثی که توی شرکت سعی میکنند بهش زیاد رفرنس بدن یا زیاد استفاده کنند میگن فانکشن شما تا حد ممکن (البته بعضی وقتا نمیشه) نباید side effect داشته باشه. فانکشن زمانی ساید افکت داره که به جز ورودی و خروجی که میگیره و داره، یه سری کارهای دیگه هم بکنه. کدی که ساید افکت داشته باشه تست نوشتن براش سخت هست. چون تست نوشتن برای ساید افکت سخته. چند وقت پیش Dan Lew یه مقاله خیلی خوبی نوشته بود در مورد اینکه testability چه جوریه و چه فانکشنالیتی ساید افکت داره و نداره و تست نوشتن برای اینا چه فرقی داره. ولی خلاصشو بخوام بگم اگر شما یه فانکشن خیلی ساده بنویسید که دو تا عدد رو بگیره با هم جمع کنه و خروجی رو برگردونه، تست نوشتن براش خیلی ساده هست ولی حالا اگر به همین فانکشن یه خط اضافه بشه که این دو تا عدد رو بگیره جمع کنه و برگردونه ولی یه پرینت هم بکنه یه چیزی هم توی لاگ کت بیرون بده این میشه ساید افکت این میشه یه فانکشنالیتی دیگه توی اون فانکشن که شما برای اینم باید تست بنویسید، معمولا وقتی pure logic فانکشنالیتون ساید افکت نداشته باشه هم کدتون تمیز تره و هم تست نوشتن براش خیلی کم دردسر تره.اینجا ممکنه یه کدی بنویسی، یه پول ریکوئستی بزنی، ولی پول ریکوئستت ممکنه مثلا سه چهار روز طول بکشه با مستر مرج بشه. چون آدم‌های مختلف میان ریویو میکنن و در اکثر مواقع نظرات خیلی خوبی میدن. چون شما وقتی یه کدی رو مینویسی فقط از دیدگاه خودت نوشتی ولی وقتی چندنفر دیگه میان منتقد کد شما میشن ممکن هست یه سری دیدگاه‌هایی رو به شما بدن که شما اون لحظه نداشتی و داشتی به چیزهای دیگه فکر میکردی باعث میشه کدی که مرج میشه توی مستر خیلی تر و تمیز باشه. اگر لول ساید افکت رو خیلی پایین نگه دارید یونیت تست‌های خوبی میتونید بنویسید، البته Integration test و End-to-end test خیلی ربطی به تمیز بودن کد نداره. ولی این خلاصه‌ی چیزی بود که من میتونستم بگم چطور تست بنویسیم. کتاب خاصی یادم نمیاد بذارید eBookهامو نگاه کنم بهتون میگم چیز خاصی خوندم یا نه. من بیشتر یادم میاد در مورد تست مقاله خوندم مثلا power mockito یا mockito رو مجبور شدم داکیومنتشو زیر و رو کنم. ولی اگر کتاب مناسب هم بهم رفرنس دادن یا خودم داشته باشم نگاه میکنم بهتون میگم.منظور از ساید افکت اینه که روی ورودی‌ها تغییر ایجاد نشه؟کنار بحث ساید افکت و اینکه یه فانکشن باید فقط همون کاری که اسمش میگه رو انجام میده، این حرفی که میگی خیلی درسته. و معمولا آبجکت‌هایی که توی کلاس ساخته میشه (اینجکت میشه یا توسط کلاس دیگه به عنوان ورودی داده میشه) معمولا final هستند همشون، یعنی اکثر ورودی‌هایی که یه فانکشن داره یه انوتیشن final کنارش نوشته شده که اینا تغییر پذیر نباشن و وسط کار نتونید ورودی رو تغییر بدید که ساید افکت ایجاد بشه. معمولا اکثر مواقع بالای توابع notNull@ وجود داره که بگه خروجی که بهت میدم نال نیست. یعنی توی ران تایم میتونه خیالت راحت باشه و اگر مشکلی باشه من توی کامپایل تایم حل میکنم.امکانش هست حقوقتو بگی یا اینکه بگی هزینه زندگیت اونجا چقدر هست؟خب این سوال واقعا خیلی مرتبط با اندروید و توسعه‌ی نرم‌افزار نیس و بحث حقوق یه مقدار شخصی هست. اگر یکی دوست داشته باشه منابع آنلاین زیادی هستند که داخلش بتونید متوسط حقوق برنامه‌نویسا یا هزینه‌ی زندگی رو پیدا کنید. خیلی پیدا کردنش کار سختی نیست. فقط من یه نکته بگم اگر کسی مثلا علاقه داره که بره خارج از ایران کار یا زندگی بکنه. این تصمیم تمام ابعاد زندگیش رو تحت تاثیر قرار میده و تصمیمی نیست که فقط بحث کاری باشه. اصلا بهش به شکل بحث کاری نگاه نکنید.اگر ممکنه در مورد بخش‌های دیگه هم از لحاظ فنی بگو؟خب اینو کوتاه میگم، چون اگر قرار باشه همه بحث‌های فنی اسپاتیفای رو بگم باید چند روز توضیح بدم. چیزی که من میدونم اینه که درصد خیلی زیادی از بک‌اند ما با جاوا نوشته شده و از این به بعد هر چیزی هم قرار باشه نوشته بشه با جاوا نوشته میشه. این تصمیم رو سطوح بالا شرکت گرفتند و علتش هزینه نگهداری کد و اینجور چیزاس. به غیر از بخش کوچیکی که با پایتون، سی پلاس پلاس و گو نوشته شده و متناسب با نیازش تصمیم گرفته شده که زبون دیگه‌ای استفاده بشه. به همین دلیل معمولا اندروید انجینیرها رو خیلی تشویق میکنند اگر دوست دارند بک‌اند یاد بگیرند و بک‌اند هم بزنن. برای اینکه خیلی کار سختی نیست ماها که جاوا بلدیم، بیایم best practiceهای زبان رو بخونیم، یکی دو تا کورس ببینیم و یکی دو تا کتاب بخونیم تا کد بزنیم. و حتی بعد یه مدت به اصطلاح تاثیر گذار کد بزنیم.ترجیح میدم بیشتر از این در مورد چیزهای فنی بخش‌های دیگه نگم،چون خودمم اطلاع ندارم.اونجا چه کتاب‌هایی برای خوندن توصیه میشه؟از کتاب‌هایی که اینجا روش تاکید میشه کتاب‌های Martin Fowler هست که اگر اشتباه نکنم یکیش مثلا Enterprise architecture هست، کتاب Effective Java و کتاب Clean Code هم جزء کتاب‌هایی هستند که توی شرکت بهشون زیاد رفرنس داده میشه. چندتا کتاب دیگه هم هست که اسماشون الان در خاطرم نیست.شرایط و محیط کار چطوره؟ چه امکاناتی دارید؟ساعت ورود و خروج به شرکت به اون صورت نداریم. تنها مساله‌ای که وجود داره بین تیم‌های مختلف قرارداد میشه که صبح چه ساعتی stand up meeting داشته باشن.یکی از خوبیا شرکت اینه خیلی انعطاف‌پذیر هست که از خونه کار کنی یا داخل شرکت کار کنی، البته تا زمانی که ثابت کرده باشی داخل خونه کار کردنتم به شرکت ضرر نمیزنه. در واقع این بصورت کلی مدل کاریمون و رفت و آمدمون هست: ساعت نداریم هر موقع میخوایم میتونیم بریم و بیایم ولی آدم‌ها هم سعی میکنند واقعا متعهد به کار باشند.بحث امکانات هم مثل شرکت‌های خوب هست، مثلا در رابطه با غذا به حساب شرکت میتونی غذا بخوری یا اتاق بازی یا از اینجور چیزا داریم. البته میگم اینا بحث‌های فرعی هست و خیلی ربطی به توسعه‌ی نرم‌افزار نداره.شرکت و محیط کار چطور هست؟دفترمون اخیرا عوض شد اومدیم یه دفتر جدید. شرکت تو استکهلم شعبه‌های مختلف داشت همشون هم برنامه‌نویسی نبودند، یه دفتر جدید ساختند ۱۲ طبقه وسط شهر و خیلی خیلی ساختمون تحسین برانگیزی هست. اون دفتر قبلی هم که من از اینجا رفتم توش جا خوردم، این دفتر جدید هم که رفتیم توش قشنگ یه دور دیگه جا خوردیم. خیلی خیلی خوب درآوردند علتش هم اینه که پروداکتیویتی آدم‌ها خیلی براشون مهمه، بینهایت برای این که شما احساس راحتی بکنید هزینه میکنند. هزینه‌هایی که شاید به چشم هم نیاد ولی وقتی میشنوی تعجب میکنی. طبقه ۱۲ کلا کافه تریا هست و خیلی بزرگ. چندتا گیمینگ روم داره، پینگ پونگ، بیلیارد، یخچال پر غذا، اتاق ماساژ، اتاق VR، اتاق کاردستی که لوازم تحریر و خیاطی توش هست برای ساختن مثلا یه چیز فیزیکی، به درد اجایل کوچ‌ها میخوره. هر طبقه برای خودش آشپزخونه داره، محیط کار جدا هست، یه چیزی که خیلی به درد میخوره میزایی هست که حالت ایستاده میشند، بغلش ۲ تا دکمه داره میاد بالا و میره پایین. معمولا بعد از اینکه نهار میخورند همه سنگین میشند ۱-۲ ساعت ایستاده کار میکنند و اصلا حالت خواب آلودگی برات پیش نمیاد، به نظرم اینجا هم با هزینه معقول میشه انجام داد و برداشت خیلی خوبی کرد از نیروها. چون نهار که میخوری چایی یا قهوه اونقدر تاثیر نداره ولی وقتی ایستاده کار میکنی قشنگ خون تو بدنت میچرخه و حالت خواب آلودگی تقریبا صفر میشه.یه جا کار میکردیم بعد از نهار نیم ساعت خواب اجباری بود تا سر حال شی.ایده خوبی زده بوده.پروداکتیویتی براشون خیلی مهم هست معیار کار آدما اون ارزش آفرینی هست که میکنند نه اون ساعت کاری. ساعت کار کلا وجود نداره، ولی محیط کاری که ایجاد میکنند خیلی حس تعلق ایجاد میکنه و آدم خیلی با جون دل تو شرکت کار میکنه، حتی خیلی وقت‌ها اضافه بر زمانی که باید بمونه میمونه چون جای با حالی هست برای کار کردن. مثلا تا ساعت ۷ شب کار میکنی میری یه دست PS میزنی، یه شام بیرون میخوری دوباره یه چیزی به ذهنت میاد میای تو شرکت. این محیط فرهنگی که تو شرکت‌ها ایجاد کردند خیلی قابل تحسین هست و گرنه همه جا آدم با استعداد و پشتکار هست ولی اگر بتونی از این آدما بهره وری بکنی هنر مدیریتی هست که اونور وجود داره.همه شرکت‌ها اینجوریند؟اسپاتیفای بخاطر کارهایی که میکنه خیلی تو چشم هست ولی شرکت‌های دیگه هم کم و بیش اینجوری هستند. شاید زورشون نرسه دفتر به اون تجهیزات بخرند ولی مثلا هفته‌ای یه بار بریم بیرون شام مهمون شرکت. یا مثلا ماهی یه بار کل تیم بره اسکی رو یخ. یه شب هتل میگیرن کل تیم رو میبرند مهمونی و بزن برقص. سعی میکنند از آدم‌ها استفاده حداکثری بکنند این چیزیه که اینجا هم انجام دادنش یه آدم خلاق میخواد.چه اپ‌هایی دارید؟بطور کلی اسپاتیفای ۶ تا اپ مختلف داره که از این ۶ تا ۴ تاش اپ اصلی اسپاتیفای هست. یه مفهوم جالب‌تری که توی اسپاتیفای وجود داره وقتی شما کاربر رایگان اسپاتیفای هستید از یه اپ استفاده میکنید و وقتی پریمیموم میشید از یه اپ دیگه استفاده میکنید. البته به چشم یوزر این چیز متفاوتی نیست همه چیز داخل یه اپ هست ولی داخل اپ دو تا پروژه‌ی مختلف وجود داره که شما وقتی پرمیموم میشید بین این دو تا سوییچ میشید. و اتفاق جالبیه، مراحل اینکه از یه پروژه میرید به یه پروژه، داخل کد بیس بامزه هست. ممکنه یه سری از این اپ‌هارو دیده باشید. مثلا یه اپ دیگه داریم به اسم اسپاتیفای زیرو که برای کشور‌هایی هست که یه مقدار سرعت اینترنتشون پایینه یا گوشی‌های ضعیف‌تری دارند مثل هند پاکستان و یه سری کشور‌های آفریقایی، یه اپ دولوپ شده فقط برای اون کشورها که خیلی resource efficient هست، مدل برنامه‌نویسیش فرق داره و ابزارهایی که استفاده میکنند فرق داره. درکل یه سری اپ‌های دیگه هم هست.چه job title داری؟Software engineerچه جاب تایتل‌های مختلفی دارید؟برمیگرده به فرهنگ شرکت‌ها، تو شرکت ما اگر engineer باشی همه Software engineer هستند هیچ فرقی بین lead, senior و … نیست. یه فریمورکی هست نه به معنای برنامه‌نویسی یه فریمورک هست که رو کاغذه، متناسب با تاثیرگذاریت تو شرکت یه step فارغ از این که چند سال تجربه داشته باشی، قبلا چه کارایی کردی بهت اساین میشه. وارد شرکت که میشی استپت همیشه یک هست، با استپ یک وارد میشی بعد از ۶ ماه دوباره چک میشه. استپ یک یعنی کسی که میتونه کارهای خودش رو هندل کنه و گیر نمی‌افته. استپ ۲ یعنی کسی که به اسکواد خودش داره کمک میکنه و ارزش آفرینی میکنه. استپ ۳ یعنی کسی که در لول ترایب داره کار میکنه. استپ ۴ یعنی برای کمپانی داره ارزش آفرینی میکنه، که استپ ۴ ما در کل شرکت ۲ یا ۳ تا داریم. یه انجینیر داریم خیلی قدیمیه حدود ۶۰ سالشه و فکر کنم یکی از کانتریبیوترهای اصلی rx java هم هست اون استپ ۳ هست. مفهوم سنیور و جونیور نداریم خیلی راحت میتونی با همه صحبت کنی و نظر همه رو به چالش بکشی.ایرانی دیگه هم هست؟آره تو استکهلم ۳ نفر تو آمریکا هم هستند ولی همشون کسایی بودند که برای درس رفتند و بعد ویزای کار گرفتند.مدرک تاثیر داره؟مدرک تحصیلی تاثیر نداره البته نه که نداشته باشه مثلا اگر علوم انسانی خونده باشی ممکنه یه مقدار به چالش بکشند ببینند چی بلدی، ولی مثلا برای گوگل خیلی مهم هست.چند سال سابقه برنامه نویسی داری؟از ۸۸ برنامه نویسی حرفه‌ای کار میکردم از ۹۱ اندروید. اندروید دولوپر شدنم سر استارت اپ ویکند بود. قبل از اندروید هم اپ Symbian میزدم.زندگی چطور هست؟استکهلم یه مقدار زندگی کردن توش سخته یا باید با سرما و تاریکی خیلی مشکلی نداشته باشی یا اینکه اذیت میشی. جای خیلی قشنگی هست ولی چالش‌های خودش رو داره مثلا خورشید ساعت ۸:۳۰ صبح طلوع میکنه ساعت ۳ هم غروب میکنه. ما مشکلی نداشتیم پوست کلفت بودیم ولی بعضی‌ها نمیتونند تحمل کنند. هفته اول پاییز قشنگ احساس میکردیم. یهو هم روز کوتاه میشه هم ساعت هارو یه ساعت میکشند جلو، آخر هفته هم بود مونده بودیم خونه ۴ عصر هم خورشید غروب کرد تا شب بال بال میزدیم. معمولا آخر هفته حتما باید یه برنامه بچینی، تو خونه بمونی دپرس میشی.از اونور هم تو تابستون روز خیلی طولانیه، تا ساعت ۱۱:۳۰ شب آسمون روشن هست و ساعت ۳ هم دوباره خورشید طلوع میکنه، ۵ ساعت کلا شبه.اون بازی‌ای که گفتی رو میتونی توضیح بدی؟بازی هدفش این بود که زیاد کار کردن رو در برابر درست کار کردن نشون بده. آدم‌ها تقسیم شدند به تیم‌ها مختلف ۳ یا ۴ نفره. به هر تیم ۱۰۰ تا توپ پینگ پنگ دادن تیمی که میتونست بیشترین تعداد توپ رو بزاره تو ظرف برنده میشد. منتهی یه سری قانون داشت مثلا یه توپ باید بین تمام اعضای تیم به گردش در بیاد بعد بره تو سبد، یک نفر در لحظه بیشتر از یه توپ نمیتونه برداره، توپ‌ها با قاشق باید برداشته میشد و …. شما میتونستید خیلی سریع کار کنید یا باهوش کار کنید و یه فرمولی پیدا کنید که از بقیه تیم‌ها سریعتر توپ‌ها رو بزارید تو ظرف. در واقع خلاقیت آدم‌ها رو به چالش میکشیدند. تیمی هم که برنده میشد راه حلش رو به اشتراک میذاشت. مهم این بود که به آدم‌ها بفهمونند با سریعتر کار کردن یا استرس ممکنه به نتیجه نرسی، اگر قبلش فکر کنید ممکنه بهتر به نتیجه برسید. یه سری بازی اینطوری بود که هزینش فقط خوندن یه کتاب اجایل کوچ و ۴۰۰ تا توپ پینگ پنگ بود.اونجا این شکلیه که یه بخشی از فرایند آن‌بردینگ با اجایل کوچ جلسه داری و از دیدگاه‌های مختلف آموزش میدن، از دیدگاه فنی، از دیدگاه اجایل کوچ، از دیدگاه تیم‌لید، از جنبه‌های مختف سعی میکنند آدم رو جا بندازن و بگند چه انتظاری از جنبه‌های مختلف از فرد میره. یکی از کارهای مهمی که اجایل کوچ‌ها انجام میدند بررسی سلامت تیم از نگاه تیم بودن هست مثلا اگر بعد از مدتی بچه‌ها صبح‌ها جلسه رو نمیان یا جلسه به جای ۱۰ دقیقه ۴۵ دقیقه طول میکشه اینا coordination انجام میدن، آدم‌ها رو سر جای خودشون نگه میدارن جوری که استفاده حداکثری ازشون بشه. مثلا تو رترواسپکتیو چک میکنند به کارهای اسپرینت رسیدیم یا نه؟ یا در مجموع تو ۲ هفته گذشته خوشحال بودید یا نه؟ راضی بودید از کار کردن یا نه؟ یا میگند دو هفته گذشته خودتون رو در قالب یه حیون بکشید، مثلا لاک پشت ۲ هفته گذشته خیلی کند بودیم؛ خرچنگ انگار چندتا دست داشتم و کلی کار مختلف میکردم. یه جورایی با خلاقیت کمک میکنند محیط کار رو بهتر بکنند چطوری میتونیم بهتر کار بکنیم چه عاملایی حذف و اضافه شه اجایل کوچ‌ها بخش عمده‌ای از کارشون کوردینیشن آدم‌هاست.چه ابزارهایی استفاده میکنید؟تمام شرکت رو slack هست تیمای مختلف کانال مختلف دارن خیلی کم از stack overflow و … استفاده میکنیم چون سعی میکنیم با هم صحبت کنیم، تیم‌ها کانالای مختلف دارند مثلا rxJava unit test Android و … کانال مخصوص خودش رو داره و اکثر سوال‌ها اون تو پرسیده میشه. ارتباط اصلی شرکت معمولا رو ایمیل هست. شرکت یه اپ پرایویت فیسبوک رو خریده https://spotify.facebook.com سوشال نتورک شرکت هست مثلا بخوایم یه gif بزاریم یا این جور چیزا. تیم ما از جیرا استفاده میکنند ولی تیم‌هایی هست که از ترلو و … استفاده میکنند. جیرا هم کلی کاستومایز شده معمولا اجایل کوچ‌ها این کار رو میکنند. یه باتی داریم که معمولا pr سامبیت میشه میاد تست‌های مختلف روش اجرا میکنه. jenkins نیست، فکر کنم خودشون نوشتند. داکیومنت‌ها رو گوگل درایو هست، Confluence داریم ولی من از گوگل درایو استفاده میکنم. یه معماری مبتنی بر Redux داریم که یه خورده دستکاریش کردن برای جاهایی که Event base هست. کلا قرار نیست بریم رو Kotlin چون تمام ریسورس‌هایی که میخوایم بیشتر و پخته‌تر رو جاوا هست. برنامه نویس‌های اندروید و بک‌اند رو هم شیفت میدند که فول استک بشند تا جایی که میتونند، چون مایکرو سرویس‌ها هم رو جاوا هست راحت‌تره. (من از quarter قبل شروع کردم بک‌اند بزنم و احتمالا کوارتر بعد بیشتر بک‌اند میزنم.) در نتیجه کاتلین با قاطعیت رد شد هیچ کس نمیخواست بره سراغش ولی شرکت‌هایی هستند که رفتند خوشحال هم هستند. Hangouts خیلی زیاد استفاده میشه همه جلسات و ارتباطات با هنگوات هست. گوگل کلندر خیلی زیاد استفاده میشه و خیلی جدی گرفته میشه.یه جا گفتی رو کلاودیم دیتا سنتر داریم یا … بیشتر بگواسپاتیفای الان یه دونه دیتا سنتر تو لندن داره قبلا بیشتر بود تو اروپا آمریکا و … بعد دیدن هزینه نگهداریش خیلی زیاده و ما زاتا یه شرکت موسیقی هستیم و نمیخوایم به اون سمت بریم و درگیر اون لول از نگهداری بشیم. مثلا تجهیزات سخت افزاری داشته باشیم و رفتیم رو گوگل کلاود و خیلی هم به نفعمون شد. یه دونه تو لندن هست به خاطر یه سری مشکلات با گوگل کلاود که قرار هست اونم تو ۳ ماه آینده کلا خاموشش کنند.اونجا که گفتی بعد از اینکه وارد اپ میشی کلا ۲ قسمت میشه رو میشه توضیح بدیوقتی وارد اپ میشی ۲ تا چیز قبل از لود شدن هر کدی فچ میشه یکی user policy که تا وقتی نیاد شما اون پراگرس بار رو میبینی حتی اگر اطلاعات صفحه هوم اومده باشه یه چیز دیگه داریم به اسم aba که فریم ورک a/b test هست و یه سری configuration میفرسته. مثلا ممکنه قرار باشه ۴ تا تب تو نویگیشن داشته باشی جای ۵ تا، ممکنه اون توی aba باشه ولی به aba معمولا ۰.۷ ثانیه فرصت میدیم اگر از سمت بکند اومد که اومد اگر نیومد با یوزر پالیسی میریم جلو. بسته به این که کدوم یکی برای شما بیاد یه کلاس‌هایی لود میشه به صورت داینامیک که اون تصمیم میگیره بری تو اپ فعلی یا بری توی اپ قدیمی. برای من خیلی جالب بود که پکیج‌هایی که داری انتخاب میکنی متناسب با چیزی که ران تایم هست انتخاب بشه، یعنی الان من این پکیج رو لود کنم یا این یکی رو. مثلا وقتی بخوای core برنامه رو راه بندازی که با ndk کامپایل شده و شما فقط باید run کنیش بسته به این که چه پکیجی رو داری چیز متفاوتی برات لود میشهحجم اپ چقدر هست؟فکر کنم ۴۰ مگ یا همچین چیزی ولی برای کشورهایی که نت داغونی دارن یه اپ دیگه نوشته شد به اسم اسپاتیفای تاینی و همه چیزش فرق داره هیچ چیش با اپ ما یکی نیست. کلا ۲ مگ حجم هست. فقط رندر میکنه چون هیچ لاجیکی توش وجود نداره. از لحاظ ظاهری تقریباً مو نمیزنه با اپ فعلی خیلی شبیه ولی یه تیم دیگه نوشته و یه عده آدمند که تخصصشون فقط تو بهینه سازی، کم کردن حجم کد و … هست در این حد که تو apk حتی یه دونه کلاس اضافه نره. من کدشون رو کم دیدم ولی اصلا استانداردهای کارشون فرق داره و یه جور دیگه کد میزنند و من از کاری که میکنند سر در نمیارم.اپ خودتون انگار ۱۸ مگهاپ دیباگمون ۴۰ مگ هست.این اسپاتیفای تاینی هست. ایمیج میدونم اصلا استفاده نشده از فونت استفاده کردند، انیمیشن نداره حجمش کم شده زمان لود شدن ویوها خیلی اومده پایین و…یه اپ اندروید اسپاتیفای هست که آیکونش سبز کمرنگ هست و بلافاصله میگه این یکی رو نصب کن، داستانش چیه؟یه اتفاق چند سال پیش رخ داده بود که بهش میگند insedence (اتفاق) یکی یه ایمیلی فرستاده بود و ایمیلش یه چیزی شبیه daniel@spotify.com بوده که ceo شرکت هست و گفته بچه‌ها این رو براتون میفرستم تستش کنید. یه لینکی بوده با این لینک عملا کلی از اطلاعات شرکت رو دراورده، من دقیقا نمیدونم چه اتفاقی افتاده ولی مجبور شدند در عرض یه هفته کل پکیج برنامه رو عوض کردند برای اینکه طرف دسترسی نداشته باشه به اونجا. ممکنه key store و پسورد رو در آورده باشه.دیتابیستون چیه؟نمیدونم چون ما از یه apiای استفاده میکنیم که یه تیم دیگه داره maintain میکنه. ولی میدونم هم رو فایل سیستم مینویسیم هم یه دیتابیسی هست که نمیدونم چیه و هم بسته به کاری که بخوای ممکنه رو کلاود بنویسند. تنظیمات ستینگ میدونم مثلا رو کلاود هست. ما با یه سریع انوتیشن میگیم چی میخوایم مثلا این پرامری کی هست و … ولی نمیدونم اون پشت چیهبعضی تیم‌ها هم هستند که اصلا فیچر ندارند مثلا android infrastructure اون تیم فیچر نداره ریپازیتوری رو داره مین‌تین میکنه. مثلا یه تیم داریم client build ریلیز کردن با اون تیمه زمان بیلد اگر بره بالا اون تیم گزارش میده که چرا؟ اون تیم بود که گفت باید ماژولارایز کنیم کد بیس رو و اگر نه یه ۲ سال دیگه با همین سرعت بخوایم کد جنریت بکنیم ۲۰ دقیقه باید منتظر باشیم که گریدل دونه دونه سینک کنه.چندتا تیم دارید؟اون ترایبی که ما کار میکنیم ۸ اسکواد مختلفه ولی کلا نمیدونم چندتا تیم داریم از اینا ۴ تاش تو استکهلمند ۴ تا تو گوتنبرگچه جوری آپدیت میدید؟هر هفته جمعه پوش میکنیم تو استور و هر هفته باید ریلیز کنیم. هر تیمی باید جمعه فیچرهای خودش رو ساین آف کنه، یعنی کدم رو تست کردم OK هست. یه داکیومنتی شیر میشه بین همه، تست میکنن اگر OK بود داکیومنت رو ساین میکنن بعد تیم ریلیز میفهمه همه چی OK هست.آخر هفته ریلیز میکنید به باگ بخورید چی؟یه تیمی داریم که on call هست و همیشه کار میکنه ولی اگر نتونی اپ خودت رو هم تست کنی خیلی اوضاع خرابه دیگهمثلا تست میکنی ردیفه ولی ریلیز که میکنی میبینی ۲ درصد کرش داره مثلا Firebase رو آپدیت کردم این به کرش میخورهاین اتفاق نباید رخ بدهبرا شما نباید برای ما میشه : )وقتی آپدیت میدید relese note چیه؟ باگ فیکسه یا فیچر هدف داریهتیمی که داره ریلیز میکنه اصلا براش مهم نیست که شما چی داری ریلیز میکنی متنی هم که زیر آپدیت‌ها هست همش ثابته امیدواریم از این تجربه اسپاتیفای لذت ببرید و … در نتیجه شما باید همواره هر باگی هست فیکس بکنید چون برای هفته بعد میره بالا. ۲ تا ویدیو هم داده بود قبلا که چطور کار میکنند اسپاتیفای کالچر یا همچین چیزی تو vimeo دیده بودم.(https://vimeo.com/85490944) تو اون میگه از نظر اسپاتیفای ریلیز کردن مثل یه قطار هست اگر به این رسیدی، رسیدی اگر نرسیدی باید صبر کنی با قطار بعدی، تو این کوارتر هم قرار هست ریلیز هر ۳ روز یکبار بشه و قرار هست تو ۲۰۱۸ به هر روز ریلیز و هر کامیت قابل ریلیز کردن بشه حتی اگر ریلیز ندیم.این سیاست منطقش چیه؟اول که اونجا مشکل محدودیت حجم اینترنت و … نداریم که به چشم بیاد. ولی از لحاظ کیفی میخوان به اونجا برسن که هرچی میذاری رو ریپازیتوریت انقدر خوب باشه که بشه ریلیز کرد. الان استاندارد شرکت بالاست یعنی هر چیزی بزاری بالا تو یه هفته میتونی مطمئن باشی که میشه ریلیزش کرد ولی میخوان به جایی برسند که هرچی میذاری همون لحظه اگر بره دست ۳۰۰ میلیون کاربر بدون مشکل کار بکنه cd هست دیگه.الان چندتا لایه کلی دارید توی کدتون؟ که فولی ماژولند؟اگه بخوام کلی بگم مثلا صفحه براوز رو کلا یه ماژولش کردیم تو کد بیس اصلی بود کنار بقیه فرگمنت‌ها ولی چون داشتیم از اول مینوشتیمش یه ماژولش کردیم همه فرگمنت لاجیک و تست رفت توش، فیچرها مثلا ماژول شدن، Search, Home, Your Library, Coreبعد توی اون دوباره خودش ماژولارایز نیست؟نه پکیج بندی کردی ولی یه ماژول هست دیگه اگر circular dependency به وجود بیاد باید بریم اون یکی رو هم ماژولارایز کنیم. کسی که ماژولارایز میکنه باید دیپندنسی رو هم خودش solve بکنه مگر اینکه بخوای پلیر کل برنامه رو ماژولارایز بکنی که باید یه تیم براش بزاری.شده کسی اخراج بشه؟ دلیلش چی بوده؟تا حالا ندیدیم ولی شاید به خاطر مسائل اخلاقی و این‌ها کسی اخراج بشه، پروسه استخدام از لحاظ فنی انقدری خوب هست که پیش نیاد.پاداش یا جریمه دارید؟کسی رو تا الان ندیدم جریمه بکنن، محیط و آدم‌های شرکت‌خیلی حرفه‌ای‌تر از این حرف‌ها هست که جریمه و کسر از حقوق و این‌ها داشته باشیم، نهایتا کسی ناراضی باشه با شرکت خداحافظی میکنه، پروموشن هم برای Exceeding Expectation هست. یعنی از خواسته‌ها و مسئولیت‌هات فراتر عمل کنی. یه مقدار روی حقوق هست.چه کسی step‌ها رو تعیین میکنه؟یه فریمورک هست که مطابق با اون بهت یه درجه‌ی ارشدیت داده میشه و برای اینکه درجه بالاتری بگیری باید یه سری تاثیرات رفتاری و تکنیکی از خودت نشون بدیچه third partyهایی استفاده میکنید؟مطمئن نیستم لیست کاملش چیه، ولی مثلا رتروفیت، okhttp Stethoمگه نتورک تو core نبود؟یه سری چیزهارو داریم دیپریکیت میکنیم، میریم روی okhttp و gRPCجالب بود که کد رو تونستی بهمون نشون بدی، تو شرکت مکانیزمی هست از دزدیدن کد جلوگیری کنه؟ مثلا دسترسی بسته شه یا …اعتماد، اگر بهم اعتماد نداریم دلیلی برای کار باهم وجود نداره! و صد البته یه سری بند و قانون هم تو قرارداد هست.</description>
                <category>پول ریکوئست</category>
                <author>behdad.222</author>
                <pubDate>Fri, 21 Dec 2018 15:13:28 +0330</pubDate>
            </item>
                    <item>
                <title>پکیج‌منجر NPM چطوری می‌تونه ما رو توی مشکل بندازه؟</title>
                <link>https://virgool.io/pullrequest/npm-wipkqlgezyk2</link>
                <description>اول بگم که فانکشن require در حالتی که ما بخوایم یک ماژول رو ایمپورت کنیم باهاش، اول میاد توی فولدر node_modules که کنار فایل index.js هست میگرده، اگه نبود توی ماژول‌های کناری می‌گرده. وقتی توی پروژه جاوا‌اسکریپت و نود‌جی‌اس کامند npm install رو اجرا می‌کنیم، NPM شروع می‌کنه به خوندن فایل package.json‍ و تمام دیپندنسی‌هایی که اونجا تعریف شده رو از توی اینترنت دانلود و توی فولدر node_modules قرار می‌ده. وقتی داخل برنامه زیر کامند npm install رو اجرا می‌کنیم:// package.json
{
	&quot;name&quot;: &quot;my app&quot;,
	&quot;dependencies&quot;: {
		&quot;a&quot;: &quot;1.0.0&quot;,
		&quot;b&quot;: &quot;1.0.0&quot;
	}
}چنین ساختاری برای ما تولید می‌کنه:---- my app-------- node_modules------------ a------------ bخیلی هم عالی. اما گاهی اوقات پیش می‌آد که یکی از دیپندنسی‌ها خودش به یه پکیج دیگه وابسته‌ست و NPM مجبوره برای راضی نگه داشتن اون، دیپندنسی‌های اون رو هم دانلود کنه. توی این مثال، فرض کنید پکیج a خودش وابسته به پکیج c و d باشه و پکیج b دوباره وابسته به پکیج d باشه. ساختار فولدر ما میشه:---- my app-------- node_modules------------ a---------------- node_modules-------------------- c-------------------- d------------------------ node_modules---------------------------- b------------ bخوبی این ساختار اینه که اگه پکیج d داخل خودش بخواد پکیج b رو ایمپورت کنه، اون b داخلیه بهش می‌رسه و اگه ما توی روت پروژه همون کار کنیم، اون b که توی node_modules هست بهمون تحویل داده می‌شه. اما بدی‌هایی داشت این روش که باعث شد NPM روش تولید این ساختار رو عوض کنه. یکیش اینکه ما چرا اصلا باید دو تا b داشته باشیم (با فرض اینکه نسخه‌ها یکی باشن)؟ یا اینکه اگه مثلا c هم یک سری دیپندنسی داشت که اونا هم باز دیپندنسی داشتن که اونا هم... تا ابد باید همینطوری فولدر node_modules بسازه و بره جلو؟ درسته فانکشن‌های ریکرسیو خفنن، ولی نه دیگه تا این حد! مخصوصا اینکه اگه یکی از کلاینت‌های ما بخواد ویندوز باشه و حتی نتونه اون فولدرهای داخلی رو به علت طولانی بودن آدرس نشون بده!بعد از بحث‌های زیاد درباره این قضیه، NPM تصمیم گرفت روشش رو عوض کنه. توی نسخه‌های بعد 3 دیگه NPM سعی می‌کنه تا جایی که راه داره همه چیز رو فلت کنه. الان اگه بخوایم همون وضعیت آخری رو در نظر بگیریم، وقتی با NPM جدید کامند npm i رو اجرا کنیم، ساختار ما این‌شکلی میشه:---- my app-------- node_modules------------ a------------ b------------ c------------ dبه‌به. الان دیگه به جای داشتن یه عالمه node_modules‌ تو در تو فقط یه دونه ازش داریم و همه چیز همینجوری وسطش پهن شدن. اما صبر کنید بذارید وضعیتی رو بگم که ممکنه این ساختار اونقدری هم که بنظر می‌رسه، خوب نباشه.فرض کنید شما دارید یک پلاگین برای babel می‌نویسید. این ماژول مثلا میاد به یه سری فرایندهای کار babel کمک می‌کنه. حالا می‌خواید این رو منتشرش کنید،آیا babel باید توی devDependencyها باشه؟ معلومه که نه. چون همیشه به babel نیاز داره تا کار کنه!فهمیدم، باید توی dependencyها باشه؟ nice try، اما بازم جواب اشتباه بود. این پلاگین به babel نیاز نداره، این babelـه که به این نیاز داره.پس چی‌کار کنیم؟ به babel بگیم این پلاگین مارو به dependencyهای خودش اضافه کنه؟ نه نه. لایبرری babel نمی‌تونه هر روز بیاد یه عالمه plugin که هر روز دولوپرها براش منتشر می‌کنن رو به dependencyهای خودش اضافه کنه. اصلا منطقی نیست. هرکی babel رو می‌خواد دلیل نمیشه همه pluginهاش رو هم بخواد.شاید توی بعضی پروژه‌ها و فایل package.jsonـشون قسمت peerDependencies رو دیده باشید. این لایببری‌هایی که توی این بخش نوشته می‌شن، نه موقع develop و نه موقع انتشار نصب نمی‌شن! فقط کارشون اینه که اگه کسی این پلاگین رو توی پروژه mammad نصب کرد، npm در صورتی که تو همون پروژه mammad پکیج babel نصب نبود، به کاربر یه پیغام زرد رنگ که ممکنه خیلی راحت ازش بگذرید بده که «یکی از پکیج‌های شما یه پکیج دیگه رو می‌خواد که شما نداریدش یا یه نسخه دیگه ازش دارید که اون نمی‌تونه نصبش کنه. نصبش کنید!»این وضعیت ممکنه گند بزنه به پروژه شما. حالت زیر رو در نظر بگیرید: (می‌دونم ممکنه سخت باشه)// package.json
{
	&quot;name&quot;: &quot;plugin&quot;,
	&quot;peerDependecies&quot;: {
		&quot;babel&quot;: &quot;1.0.0&quot;
	}
}

{
	&quot;name&quot;: &quot;another-plugin&quot;,
	&quot;peerDependecies&quot;: {
		&quot;babel&quot;: &quot;2.0.0&quot;
	}
}

{
	&quot;name&quot;: &quot;custom-builder&quot;,
	&quot;dependencies&quot;: {
		&quot;babel&quot;: &quot;1.0.0&quot;,
		&quot;plugin&quot;: &quot;latest&quot;
	}
}

{
	&quot;name&quot;: &quot;my-app&quot;,
	&quot;dependencies&quot;: {
		&quot;babel&quot;: &quot;2.0.0&quot;,
		&quot;another-plugin&quot;: &quot;latest&quot;,
		&quot;custom-builder&quot;: &quot;latest&quot;
	}
}اوه اوه. پکیج plugin میگه نصب‌کننده‌های من باید babel نسخه یک رو داشته باشن. پکیج custom-builder میگه من نسخه یک babel و plugin رو توی دیپندنسی‌هام می‌خوام. و پکیج my-app که پروژه نهایی ماست، پکیج babel نسخه دو و custom-builder رو توی dependencyهاش داره. اینجا به قول معروف NPM گه‌گیجه می‌گیره. مرحله به مرحله با نصب پکیج‌های مورد نیاز my-app پیش می‌ریم:- اقدام به نصب پکیج babel نسخه دو، چون توی روت پروژه نسخه دیگه‌ای ازش نیست، همونجا نصب می‌شه.- اقدام به نصب پکیج another-plugin، چون توی روت پروژه نسخه دیگه‌ای ازش نیست، همونجا نصب می‌شه.- اقدام به نصب پکیج custom-builder، چون توی روت پروژه نسخه دیگه‌ای ازش نیست، همونجا نصب می‌شه.- اقدام به نصب dependencyهای custom-builder...- اقدام به نصب پکیج babel نسخه یک توی روت پروژه. توی روت پروژه نمیشه چون اونجا یه نسخه دیگه ازش هست، پس باید بره توی خود custom-builder.- اقدام به نصب plugin نسخه یک. چون توی روت پروژه نسخه دیگه‌ای ازش نیست، همونجا نصب می‌شه.پس نتیجه می‌شه:---- my app-------- node_modules------------ babel@2.0.0------------ another-plugin------------ custom-builder---------------- node_modules-------------------- babel@1.0.0------------ pluginاینجا یه مشکل بزرگ هست. یه بار دیگه برید بالا به peerDependecyـهای پکیج plugin نگاه کنید. اون babel نسخه یک رو میخواد، اما الان تو این پروژه اگه بخواد babel رو ایمپورت کنه، نسخه دو بهش میرسه، جون نزدیک‌ترین babel بهش نسخه دو هست، نه نسخه یک! و طبیعتا ممکنه توی کارش مشکل بوجود بیاد. تنها کاری که NPM تو این شرایط می‌کنه اینه که موقع نصب پکیج‌های my-app به شما بگه «پکیج plugin نسخه یک babel رو می‌خواد ولی شما نسخه دو رو دارید، برید پکیج babel نسخه یک رو دستی نصب کنید»مشخصه که ما به این راحتی نمی‌تونیم این کار رو کنیم. چون پروژه my-app ما و همینطور another-plugin طبیعتا babel نسخه دو رو لازم دارن. اینجاست که ممکنه editor رو ببندیم و بریم توی توئیتر به NPM و NodeJs و Javascript فحش بدیم.مشخصه که اگه NPM یه جوری می‌فهمید پکیج plugin رو توی custom-builder نصب می‌کرد، مشکل حل می‌شد. اما به عنوان یک npm install کننده، نمی‌تونیم چنین کاری کنیم. پس دو تا راه داریم، یکی با دید اینکه ما صاحب پکیج my-app هستیم و اون یکی دیگه با دید اینکه ما صاحب پکیج custom-builder هستیم و می‌خوایم مشکل رو به صورت اصولی حل کنیم.ما صاحب پکیج my-app هستیم...ما بعد از نصب پکیج‌های مورد نیاز، می‌تونیم بیل رو برداریم و خودمون این مشکلات peerDependenciesهای پکیج‌هامون رو حل کنیم و پکیج‌های مورد نیازشون رو نصب کنیم تا در نهایت اپ ما کار بکنه. حالا مطمئن بشیم که فایل package-lock.json هم تولید شده و اون رو هم با تغییرات دیگه کامیت می‌کنیم. این فایل برای نفر بعدی که کامند npm i رو اجرا می‌کنه ساخته شده و در واقع توش ساختار فعلی فولدر node_modules ماست. پس از این به بعد هر بار npm install کنیم، مطمئنیم دوباره اون مشکل پیش نمیاد و ساختار دقیقا همین شکلی که الان هست می‌شه. اما یه مشکل بزرگ اینه که  این روش فقط وقتی جواب می‌ده که my-app ما یه پروژه نهایی باشه و مطمئن باشیم هیچ‌جوره امکانش نیست که کسی بخواد اون رو به عنوان dependency نصبش کنه. چون این فایل package-lock.json که ما تولید کردیم، فقط زمانی معتبره که کسی مستقیما بخواد داخل این فولدر npm install رو اجرا کنه. حالا فرض دوم رو بخونید.ما صاحب پکیج custom-builder هستیم...اینجا ما باید یه فایل lock پرقدرت‌تر بسازیم. یه فایلی که علاوه‌بر اینکه یوزرهای مستقیم ما جدی می‌گیرینش، یوزرهایی که ما رو توی dependency‌هاشون نصب می‌کنن هم جدی بگیرنش. این فایل دوم توی فایلی به نام npm-shrinkwrap.json ذخیره میشه. این فایل به شما تضمین می‌کنه که در هر صورت ساختار node_modules شما تغییر نمی‌کنه، حتی اگر شما یه dependency باشید. مراحل کار تا حد زیادی شبیه مرحله قبله، اما بعد از اتمام کار و قبل از کامیت کردن، یه کار دیگه لازمه. اینکه بیایم این فایل lock دومی رو با کامند npm shrinkwrap تولید کنیم. این کامند باعث میشه محتویات فایل package-lock.json به فایل npm-shrinkwrap.json منتقل بشه.همین. امیدوارم تونسته باشم به اطلاعاتتون اضافه کنم. درصورتی که بنظرتون مطلب خوبی بود، با اشتراک گذاشتنش من رو خوشحال می‌کنید.</description>
                <category>پول ریکوئست</category>
                <author>امیر مومنیان</author>
                <pubDate>Wed, 14 Nov 2018 12:45:51 +0330</pubDate>
            </item>
                    <item>
                <title>با alias سریع‌تر کد بزنید!</title>
                <link>https://virgool.io/pullrequest/%D8%A8%D8%A7-alias-%D8%B3%D8%B1%DB%8C%D8%B9%D8%AA%D8%B1-%DA%A9%D8%AF-%D8%A8%D8%B2%D9%86%DB%8C%D8%AF-whyuymkvvoxc</link>
                <description>دستورات سریع لینوکسی برای برنامه‌نویس‌هامقدمهالبته که دوست دارم یه متن بلند بنویسم! اما همزمان که دارم این مطلب رو مینویسم، دارم روی یه مطلب (چند نسخه‌ای) کار میکنم در باره‌ی برنامه‌نویسی وب. مثل همیشه، از صفر تا صدش، برای اونایی که تا حالا حتی برنامه‌نویسی هم نکردن! کار عجیبیه، کلی چیز باید بخونم و انجام بدم و یاد بگیرم. بگذریم…این مطلب من، قرار نیست خیلی طولانی باشه و پیشبینی میکنم که به مرور بروز رسانی بشه. داستانش هم از این قراره که، من علاقه ندارم کدها رو چندبار بنویسم. مد نظرم اینه که یه سری «کلید» برای خودم داشته باشم و جاهای مختلف ازشون استفاده کنم.لینوکسلینوکس یه هسته‌ی سیستم‌عامله. یعنی دستورات و فرایندهایی که یه سیستم‌عامل نیاز داره رو براش فراهم میکنه و یه راه ارتباطی بین سخت‌افزار و سیستم‌عامله. ضمنا، لینوکس متن بازه، یعنی میشه هرچیزی رو توش تغییر داد و شخصی‌سازی کرد.سیستم‌عامل‌های زیادی هم وجود دارن که مبتنی بر لینوکس هستن، یعنی، از هسته‌ی لینوکس استفاده میکنن و یه جورایی «توزیعی» یا Distributionای از لینوکس هستن (بین کاربرهای لینوکسی، به Distro هم معروفن). من اینجا سعی کردم در مورد سیستم‌عامل‌های لینوکس بیشتر توضیح بدم.شِل یا Shellخیلی ساده، شِل یا «تِرمینال» یه برنامه هست که دستورات رو از کاربر میگیره و اونا رو به سیستم‌عامل و در نهایت خروجی رو نمایش میده. شِلِ لینوکس یکی از مهمترین اجزای لینوکس به حساب میاد و یکی از محیط‌های مورد علاقه‌ی برنامه‌نویس‌هاست.اینجا من تصمیم دارم که روی یه سری از اصول این نرم‌افزار کار کنم و در نهایت یه سری از دستورات به درد بخور که خودمم استفاده میکنم رو بگم. ضمنا، اگر شما هم دستوراتی رو دارید، بهم بگید که اینجا آپدیت کنم.برای باز کردن نرم‌افزار تِرمینال، میتونید توی سیستم‌عامل لینوکسی دکمه‌ی Ctrl + Alt + T رو بزنید، یا دکمه‌ی CMD + Space رو توی سیستم‌عامل مک بزنید و دنبال Terminal بگردید.دستورات پایهاین دستورات رو بدون علامت $ اجرا کنید. $ یک نشانست و به این معنیه که دستور داره با مجوز یک «کاربر» و نه یک «مدیر» اجرا میشه. نشانه‌ی یک مدیر، علامت # هست.۱. pwdاین دستور به شما آدرس جایی که توش هستید رو میگه. وقتی ترمینال باز میشه، معمولا از پوشه‌ی Home شروع به کار میکنه. ضمنا، این آدرس‌ها Absolute هستن، یعنی از پوشه‌ی ریشه یا Root شروع میشن.$ pwd
/home/aien۲. lsاین دستور به شما محتویات پوشه‌ای که توش هستید رو میگه. اگر به آخرش -a هم اضافه کنید، میتونید فایل‌های مخفی رو هم ببینید.$ ls -a
.                Downloads      .gtk-bookmarks  .node-gyp            .rdm                                                       .vscode
..               .dropbox       .gtkrc-2.0      .npm                 .rediscli\_history                                          Web
...۳. cdاین دستور به شما این امکان رو میده که جای خودتون رو عوض کنید. مثلا از پوشه‌ای که توش هستید، برید به یه پوشه‌ی دیگه.$ pwd
/home/aien/
$ cd /home/aien/Bilder
$ pwd
/home/aien/Bilder۴. mkdir و rmdirاین دستور به شما آدرس جایی که توش هستید رو میگه. وقتی ترمینال باز میشه، معمولا از پوشه‌ی Home شروع به کار میکنه. ضمنا، این آدرس‌ها Absolute هستن، یعنی از پوشه‌ی ریشه یا Root شروع میشن.$ mkdir sample
$ cd sample
$ pwd
/home/aien/Bolder/sample
$ cd ..
$ pwd
/home/aien/Bilder
$ rmdir sample
$ ls
...۵. touchاین دستور به شما امکان ساخت فایل‌ها رو میده.$ touch file.txt
$ ls
...
file.txt
...۶. rmاین دستور به شما امکان حذف فایل‌ها و تو بعضی شرایط، فولدرها رو میده.$ rm file.txt۷. cpاین دستور به شما امکان کپی کردن فایل‌ها رو میده. این دستور، دوتا ورودی دریافت میکنه، اولی آدرس جایی هست که فایل قرار داره و دومی آدرس جایی هست که فایل باید اونجا بره.$ touch file.txt
$ pwd
/home/aien/Bilder
$ cp ./file.txt /home/aien/Musik
$ cd /home/aien/Musik
$ pwd
/home/aien/Musik
$ ls
...
file.txt
...۸. mvاین دستور به شما امکان جابه‌جا کردن فایل‌ها رو میده و مثل دستور کپی عمل میکنه.$ pwd
/home/aien/Musik
$ mv file.txt /home/aien/Videos
$ ls
...
...
$ cd ../Videos
$ pwd
/home/aien/Videos
$ ls
...
file.txt
...۹. rmاین دستور به شما امکان حذف فایل‌ها و تو بعضی شرایط، فولدرها رو میده.$ rm file.txtدستورات حرفه‌ای‌تر۱. nanoاین دستور به شما امکان نوشتن داخل یک فایل رو میده. اگر اون فایل وجود نداشته باشه، اون رو ایجاد میکنه. بعد از اینکه داخل فایل نوشتید، کافیه Ctrl + X رو بزنید، Y رو بزنید و از برنامه خارج بشید. نوشته‌ها ذخیره شدن!$ nano file.txt۲. catاین دستور به شما امکان نمایش محتوای یک فایل رو میده.$ cat file.txt
In yek file nemune ast!دستور aliasدستور alias کمک میکنه تا دستور یا دستوراتی رو با اسامی دیگه صدا بزنیم و اجرا کنیم. شاید با خودتون بگید آخه چه کاربردی داره وقتی میتونم خودم دستور رو تایپ کنم؟موضوع اینه که گاهی نیاز هست دستوراتی رو اجرا کنیم که نسبتا پیچیده هستن و فقط تو بعضی چیزهای کوچیک فرق میکنن.برای شروع، دستور زیر رو تایپ کنید:$ aliasیه لیست طولانی از دستورات رو خواهید گرفت:-=&#039;cd -&#039;
...
l=&#039;ls -lah&#039;
...
serve=&#039;http-server -o -a localhost .&#039;
which-command=whenceآخر این لیست، دستور هست که نوشته l=&#x27;ls -lah&#x27;. اگر شما این دستور رو تو ترمینال بنویسید، یعنی بنویسید l، مثل این میمونه که به ترمینال گفتید دستورls -lah رو اجرا کنه.حالا برای نمونه، دستور زیر رو بزنید:$ alias p=&quot;pwd&quot;از این به بعد اگر من دستور p رو بزنم، انگار دستور pwd رو زدم، پس آدرس جایی که هستم رو میگیرم:$ alias p=&quot;pwd&quot;
$ p
/home/aienحالا فرض کنید که توی سیستمتون، نرم‌افزار Apache رو داشتید. برای دستکاری فایل اصلیش، لازم بود که مثلا nano رو با کل آدرس اون فایل وارد کنید. چی میشد اگر مینوشتید:$ alias a=&quot;nano /etc/httpd/conf/httpd.conf&quot;اونوقت به جای هربار تیاپ کردن دستور، فقط a رو اجرا میکردید و فایل رو دستکاری…چطور دستورات alias رو دائمی کنیماگر تو یه ترمینال، یه دستور alias رو وارد کنید، بعد از بستن اون ترمینال، دستورتون هم از بین میره و دفعه‌ی بعدی دوباره باید دوباره تعریفش کنید. راهکارش اینه که از فایل .bashrc استفاده کنید. خیلی ساده، دستور زیر رو بزنید:$ nano ~/.bashrcاین دستور، فایل .bashrc که توی پوشه‌ی Home شما هست رو باز میکنه. حالا دستور زیر رو به بالای این فایل اضافه کنید:alias p=&quot;pwd&quot;
alias bashrc=&quot;nano ~/.bashrc&quot;
...با این کار، هربار که دستور p رو بزنید، به جاش دستور pwd اجرا میشه. برای حذفش هم، کافیه دوباره اون رو از فایلتون حذف کنید (دستور زیر رو تو ترمینال بزنید تا فایلش باز بشه).$ bashrcدستورات پر کاربردیه لیست از دستورات پر کاربرد هست که توی سایت خودم گذاشتم. میتونید به اونجا سر بزنید و دستورات بروز شده رو ببینید.موفق باشید</description>
                <category>پول ریکوئست</category>
                <author>آیین</author>
                <pubDate>Wed, 31 Oct 2018 15:07:27 +0330</pubDate>
            </item>
                    <item>
                <title>وب‌اپلیکیشن</title>
                <link>https://virgool.io/pullrequest/service-worker-b95eybil2lfa</link>
                <description>سرویس‌ورکردستم رو زیر چونم گذاشته بودم و با چشم‌هایی که به زور باز نگهشون داشته بودم، به کنسول برازر خیره شده بودم. ایونت‌ها همه به درستی از سرور به من می‌رسید، اما این سرویس‌ورکر برازر انگار درست کار نمی‌کرد. بعضی ایونت‌ها رو لایی می‌کشید و هیچ اتفاقی توی کلاینت رخ نمی‌داد. ایونت‌ها مهم بودن. حتی یک ایونت هم سمت کلاینت نباید از دست می‌رفت. زمان به ما اجازه نمی‌داد که منتظر آپدیت بعدی برازر باشیم که این مشکل مسخره رو حل کنه. پروژه رو باید فردا تحویل می‌دادیم. داشتم به روز اولی که این پروژه رو قبول کرده بودیم فکر می‌کردم. من و سالار و سیامک، توی جلسه‌ای که با شهرداری تهران داشتیم قبول کرده بودیم که این پروژه رو با تکنولوژی node برای سمت سرور و وب‌اپلیکیشن برای سمت کلاینت انجام بدیم. وب‌اپلیکیشن پیشنهاد من بود. سالار و سیامک هر دو تردید داشتند، اما من اصرار کردم و هر جور شده راضی‌شون کردم. ته دلم به قدرت روزافزون تکنولوژی‌های جدید سمت وب باور داشتم. سالار قبل از اینکه رضایت خودش رو اعلام کنه، بهم گفت:برازر هنوز امتحان خودش رو برای چنین اپلیکیشنی پس نداده. مطمئنم به مشکل می‌خوریم. سرویس‌ورکر هنوز ساپورت خوبی نداره توی همه برازرها و ما نمی‌تونیم مجبورشون کنیم آخرین نسخه رو نصب کنن...قبل از اینکه سالار حرفش رو تموم کنه، مدیر شهرداری و احتمالا کارفرمای جدید ما وسط حرفش پرید و گفت:خیالتون از این نظر راحت باشه دوستان. این برنامه قرار نیست دست همه باشه، فقط روی سیستم‌های کارکنان شهرداری نصب می‌شه و تمام سیستم‌ها به‌روز هستند و آخرین نسخه تمام برنامه‌ها را دارند. حتی ما می‌تونیم یه جلسه توجیهی/آموزشی براشون برگزار کنیم و کار با این برنامه رو بهشون آموزش بدیم.این حرف خیال من رو راحت کرده بود. اما حالا که متوجه شده بودم با وجود نصب آخرین نسخه کروم روی لپتاپ من باز هم بعضی ایونت‌ها به درستی کار نمی‌کنن، کمی اوضاع به هم ریخته بود. فردا ساعت 11 صبح جلسه تحویل پروژه بود و الان با سیامک به خیال خودمون داشتیم کدها رو ریویو می‌کردیم. سیامک فقط نشسته بود و هی منو‌ها رو بالا پایین می‌کرد و از شاه‌کاری که توی طراحی این اپلیکیشن کرده بود، احساس پیروزی می‌کرد. اما الان مشکل مهم‌تر از این‌ها بود. با اینکه می‌دونستم سیامک هم می‌تونه کمک کنه، بهش چیزی نمی‌گفتم. نمی‌خواستم بعدا بزنه توی سرم که &quot;تو هیچ کاری نکردی&quot;. از همون اول هم قرار بود توی کار همدیگه دخالت نکنیم. توی این موقعیت، داشت استایل‌ها رو بالاپایین می‌کرد. سالار هم که کارش رو هفته پیش تموم کرده بود، الان احتمالا خونه خوابیده بود. مشکل فقط سمت من بود و کدهای کلاینت. از خوش‌خیالی سیامک اذیت می‌شدم. غرق در فکر شده بودم و داشتم صدای احتمالی خروپف سالار را تصور می‌کردم.بخش دومروز دوم، ساعت 10 صبح، پارک روبه‌روی شهرداریحدود دو ساعت دیگه تا تحویل پروژه مونده بود و با سیامک توی پارک منتظر سالار نشسته بودیم. من داشتم سیگار می‌کشیدم و سیامک از ترس اینکه بوی سیگار من با بوی عطرش قاطی نشه، با فاصله از من وایستاده بود و داشت نگاه &quot;تو یه یابوی سیگاری بدبختی که هیچی حالیت نیست&quot; تحویل من می‌داد. من توی ذهنم داشتم مکالمه‌ای رو مرور می‌کردم که قرار بود با حضور سالار صورت بگیره. باید این مشکل رو با اون‌ها مطرح می‌کردم. هنوز کام آخر رو نگرفته بودم که سالار از روبه‌رو به سمت ما اومد.سالار: بههه سلاممم. چطورید رفقا؟من: سلام سالار. قربونت، تو خوبی؟سالار: خوبم. سیامک جون تو چطوری؟ خوبی؟سیامک: نوکرم داداش، چه خبر؟ ردیفی؟سالار: قربونت منم خوبم. همه‌چی ردیفه؟سیامک: آره همه‌چی عالی شده و استایل‌ها...من: ولی یه مشکلی داریم.نگاه بهت زده سالار و سیامک به سمت من برگشت. قصد نداشتم اینطوری بدون پروا بگم. می‌خواستم کلی مقدمه بچینم، ولی نتونستم جلوی خودم رو بگیرم. سالار که داشت آدامس می‌جویید، انگار فکش خشک شده بود. حتی نفس هم نمی‌کشیدند. مثل آن سکانس معروف سریال‌های تلویزیون که نمایانگر شدت تعجب کاراکترها است، هردو با کمی تاخیر و یک‌صدا گفتند: &quot;مشکل؟&quot;من: به یه مشکلی خوردم توی کدها.سیامک: تو که گفتی همه‌چیز کار می‌کنه! چه مشکلی؟من: انتظار این مشکل رو نداشتم.سالار: کدوم مشکل؟مشکل رو توضیح دادم. با اینکه این نقص برازر بود، بازهم چیزی از تقصیر من کم نمی‌کرد. دوست نداشتم اشتباه خودم رو گردن کس دیگه‌ای بندازم. سالار خیلی داشت جلوی خودش رو می‌گرفت که حرفای روز اولش که بهم گفته بود &quot;با وب‌اپلیکیشن به مشکل می‌خوریم&quot; رو بهم یادآوری نکنه، اما سیامک اینطوری نبود. با نگاه توأم با تنفرش با عصبانیت رو به من گفت: &quot;حالا چه گهی بخوریم؟&quot;. موقعیت سختی بود. داشتم سعی می‌کردم موقعیت رو کنترل کنم. بعد از اینکه سیگار دوم رو روشن کردم، شروع به صحبت کردم:می‌دونم. حتی احتمالا تمام فحش‌هایی که دارید بهم می‌دید رو هم می‌دونم. اما اتفاقیه که شده. واقعا کاری ازم ساخته نیست. باید توی جلسه یه جوری این تیکه رو بپیچونیم. کار سختی هم نیست. بیشتر تمرکزمون رو می‌ذاریم روی نشون دادن استایل کار و توضیح راجع به اتفاقات سمت سرور. بعد از جلسه احتمالا تا اون موقعی که اینا بفهمن برنامه دقیقا چجوری کار می‌کنه، برازرا خودشون را آپدیت می‌کنن و این مشکل مسخره حل می‌شه. بذارید این جلسه خوب پیش بره و بعدش راجع به مشکل حرف می‌زنیم. موافقید؟بعد از چیزی حدود ده ثانیه سکوت، جواب &quot;باشه&quot; رو در حالی شنیدم که مطمئن بودم پشتش هزار تا حرف دیگه بود. حداقل یکی از آن هزار حرف و مودبانه‌ترین آنها این بود که «می‌مردی زودتر این قضیه رو بهمون می‌گفتی گوسفند؟»خاکستر خاموش شده سیگار توی دستم، تقریبا هم قد خود سیگار شده بود که با تکان دست من به روی زمین ریخت. خواستم قبل از رفتن به جلسه، سیگار آخر رو هم بکشم که سیامک هم یک نخ سیگار از من گرفت. سالار همچنان سعی داشت این تغییر ناگهانی رو توی صحبت‌هایی که قرار بود انجام بده رو برای خودش حل کنه.بخش سومروز دوم، ساعت 11 صبح، اتاق جلسهسالار روی صندلی چرم اتاق شهرداری نشسته بود داشت تصویر لپتاپش رو روی پروژکتور می‌انداخت. بخار داشت از لیوان‌های چای روی میزها بلند می‌شد. من و سیامک هم دو طرف اون نشسته بودیم و داشتیم به مدیر شهرداری و سه مسئول دیگر لبخند تحویل می‌دادیم. سعی می‌کردیم چهره خود را شبیه به کسانی که موفق به انجام کار شده‌اند نشان دهیم. هرچه بیشتر طول می‌کشید، تواناییمان در حفظ این حالت کمتر می‌شد. کشتی‌گرفتن سالار با کابل پروژکتور همچنان ادامه داشت. مدیر شهرداری داشت با لپ‌تاپ قدیمی‌ای که روبه‌رویش بود ور می‌رفت. کم‌کم لپم داشت از این لبخند مصنوعی طولانی درد می‌گرفت که ناگهان تصویر صفحه لپ‌تاپ سالار روی پروژکتور افتاد و با لبخند رو به بقیه گفت &quot;بالاخره درست شد. اگه موافق باشید شروع کنیم؟&quot;. بقیه هم به نشانه تأیید سر تکان دادند. مدیر شهرداری سرش را از روی لپ‌تاپ بلند کرد و مشتاقانه گفت «شروع کنیم». سالار بعد از سر کشیدن لیوان چای‌ش که یخ کرده بود، توضیحات خود را شروع کرد.حدود یک ساعت گذشته بود. سالار تمام بخش‌های برنامه را برای آنها توضیح داد، به تمام سوالات آنها جواب داد و باعث شد کوچکترین استرسی به آنها انتقال پیدا نکند. بدون اغراق، توضیحات سالار و معرفی نرم‌افزار عالی پیش رفت. با خوش‌شانسی فوق‌العاده ما هم هیچ‌کسی متوجه هیچ کمبودی نشد و همه راضی به نظر می‌رسیدند. دو مسئول دیگر داشتند با خوشحالی راجع به استایل باز شدن منوها با هم حرف می‌زدند. همه چیز خوب بود. مدیر شهرداری شروع به صحبت کرد:خب عالی بود دوستان. نتیجه کار واقعا خوب شده و بهتون تبریک می‌گم. گل کاشتین. فقط لطفا این پروژه رو روی لپ‌تاپ خودم هم نشون بدید. این یکی از لپ‌تاپ‌های کارکنان اینجاست و باید مطمئن بشیم روی این هم بدرستی کار می‌کنه.لپ‌تاپی که روبه‌روی مدیر شهرداری بود، یک لپ‌تاپ قدیمی توشیبا و رنگ‌ورو رفته بود. یاد حرف روز اول مدیر شهرداری افتادم که گفته بود تمام سیستم‌های شهرداری به‌روز هستند و آخرین نسخه همه نرم‌افزارها روی آن‌ها نصب است، اما قیافه آن لپ‌تاپ چیز متفاوتی را بیان می‌کرد. این وسط چیزی درست نبود. مطمئن بودم که هر سه ما دقیقا به همین موضوع فکر می‌کنیم. تقریبا تمام نگاه‌های اتاق رو به سالار بود و منتظر بودند برود و به مدیر شهرداری کارکرد برنامه را نشان بدهد. سالار گلوی خود را صاف کرد و خود را با صندلی به سمت لپ‌تاپ مدیر شهرداری هول داد. من و سیامک منتظر بودیم تا لبخند رضایت را روی صورت سالار ببینیم. استرسمان فوق‌العاده زیاد بود. هرچه بیشتر منتظر می‌شدیم، سکوت حاکم بر اتاق بیشتر ما را اذیت می‌کرد. سالار حرف نمی‌زد و حتی دستش را به طرف لپ‌تاپ نبرده بود. فقط داشت نگاه می‌کرد. اصلا شبیه آن سالاری که پیش از این می‌شناختیم نبود. من و سیامک که خیلی نگران بودیم، به سمت او رفتیم و با دیدن مانیتور لپ‌تاپ، سالار را درک کردیم. سیامک تقریبا سکته کرد. تصویر لپتاپ، منظره‌ای سبز با آسمان آبی را نشان می‌داد و لوگوی ویندوز اکس‌پی و آیکون اینترنت‌اکسپلورر 6 روی آن، تقریبا هر جنبنده‌ای به غیر از مسئولان حاضر در اتاق را از پای درمی‌آورد.</description>
                <category>پول ریکوئست</category>
                <author>امیر مومنیان</author>
                <pubDate>Sat, 18 Aug 2018 14:50:53 +0430</pubDate>
            </item>
                    <item>
                <title>داکر، داکر کامپز، کانتینرها و اتوماسیون</title>
                <link>https://virgool.io/pullrequest/%D8%AF%D8%A7%DA%A9%D8%B1-%D8%AF%D8%A7%DA%A9%D8%B1-%DA%A9%D8%A7%D9%85%D9%BE%D8%B2-%DA%A9%D8%A7%D9%86%D8%AA%DB%8C%D9%86%D8%B1%D9%87%D8%A7-%D9%88-%D8%A7%D8%AA%D9%88%D9%85%D8%A7%D8%B3%DB%8C%D9%88%D9%86-wp4sf7bcg10s</link>
                <description>داکر چی هست؟ داکر کامپز چیکار میکنه و چجوری میتونیم پروژمون رو اتوماسیون کنیم؟داکر، کانتینر و اتوماسیونمقدمهبالاخره بعد از یه مدت طولانی که از داون شدن سایتم میگذره، تونستم دوباره وصلش کنم و کانفیگای سمت سرور رو دوباره فیکس کنم. شاید جالب باشه ولی سرور من جای خلوتیه و چیز زیادی توش نیست و وب‌سایت خودم تنها برنامه‌ایه که توش اجرا میشه، اما نکته‌ی جالب اینه که موقع دیپلوی باهاش خیلی مشکل دارم. بگذریم، همین مشکلات باعث شد که به نوشتن این مطلب فکر کنم و تا جایی که میتونم در مورد داکر توضیح بدم.خواهش من مثل همیشه، انه که ان مطلب رو بخونید، ازش لذت ببرید و برای دوستاتون هم بفرستید و اگر جایی اشکالی دیدید، بهم خبر بدید. ضمنا، حقوق نویسنده رو هم فراموش نکنید و اگر جایی از این مطلب استفاده میکنید، اسم نویسنده اصلی (من، آیین) رو توش درج کنید.سپاسداکر (Docker) چی هست؟اول از همه، داکر اسم شرکتی هست که واژه کانتِینِر یا Container رو روانه‌ی دنیای آی‌تی کرده. البته که قبلا هم از این واژه استفاده‌های گسترده‌ای میشده، اما داکر باعث شده که واژه کانتِینِر معنی مشخص‌تر و دقیق‌تری پیدا کنه.خود نرم‌افزار داکر، یه سرویس برای مدیریت کانتینرها یا Container Service Manager هست. کلیدواژه‌هایی که میشه برای داکر استفاده کرد، توسعه‌دادن، دیپلوی کردن و اجرا کردن هستن. در واقع داکر هدفش اینه که وقتی برنامه‌نویس‌ها، نرم‌افزاری رو می‌نویسن، اون رو به کانتینرها منتقل و به سادگی اون رو هر جایی اجرا کنن.پلتفرم (سَکو؟!) کانتینر چی هست؟پلتفرم کانتینر یا Container Platform یه سرویس کامل برای سازمان‌ها و شرکت‌هاست که بتونن باهاش مشکلات مختلفی رو حل کنن. مهمترین ویژگی کانتینرها اینه که تمام نرم‌افزارهایی که برنامه لازم داره رو، توی خودشون نگه‌میدارن. اما یکم در مورد کانتینرها عمیق بشم…کانتینر یه پراسِس (Process) داکر هست که فقط روی لینوکس یا ویندوز اجرا میشه (ظاهرا برای مک هم هست ولی من جایی ندیدم) و داخل خودش هرچیزی که برای اجرا شدن نیاز داشته‌باشه رو داره. در واقع یه کانتینر یکمی شبیه به ماشین‌مجازی یا Virtual Machine هست با این تفاوت که هسته‌ی سیستم‌عامل میزبان یا Host رو به اشتراک میذاره.عکس بالا، اساسی‌ترین تفاوت کانتینرها با ماشین‌های مجازی رو نشون میده. چارت سمت راست کانتینر و سمت چپ، ماشین مجازی. زیر ساخت‌ها یا Infrastructures شبیه به هم، داکر از سیستم‌عامل میزبان و ماشین مجازی از Hypervisor که یه لایه هست تا برنامه‌های مورد نیاز سیستم‌عامل داخل ماشین رو فراهم کنه، استفاده میکنه. تو ماشین مجازی شما به یه سیستم‌عامل مهمان احتیاج دارید، مثلا توی سیستم‌عامل لینوکستون، ویندوز نصب میکنید، اما داکر اینطور نیست و از موتور خودش استفاده میکنه تا با سیستم‌عامل میزبان ارتباط برقرار کنه. تو ماشین مجازی شما سیستم‌عامل رو نصب میکنید که برنامه‌های لازم رو بتونید فراهم کنید. این قدم تو کانتینر از بین رفته (و دقیقا چیزیه که سرعت کانتینر رو به نسبت ماشین‌مجازی خیلی بیشتر می‌کنه). در نهایت کتابخونه‌ها و برنامه‌های لازم اجرا میشن و شما میتونید سیستمتون رو بالا بیارید.این رو اضافه میکنم، فرض کنید داکر همون نرم‌افزار Oracle VirtualBox یا VMWare هست و کانتینرها، همون سیستم‌عامل‌هایی که توشون نصب می‌کنیم.Containerization vs Virtualizationقبل از اینکه وارد این مبحث بشم، یه نگاهی به ترمینولوژی داکر داشته باشیم:موتور داکر یا Docker Engine قسمتی از داکر که وظیفه‌ی ایجاد و اجرای کانتینرها رو دارههاب داکر یا Docker Hubسرویسی از داکر برای به اشتراک‌گذاری کانتینرها با دیگرانداکر کامپوز یا Docker Composeابزاری که باهاش برنامه‌های چند کانتینری تعریف میکنیم. این ابزار از فایلهای Yaml استفاده میکنه تا فایل‌ها و تنظیمات رو کانفیگ کنه و کانتینر رو آماده اجرا کنه.در کل استفاده از این ابزار سه مرحله داره:محیط برنامه رو با یه Dockerfile تعریف میکنیم تا همه‌جا بشه ازش استفاده کرد.سرویس‌هایی که لازم هست برای اجرای این کانتینر رو تو فایل docker-compose.yml میذاریم تا با فایل Dockerfile یه جا اجرا بشن.دستور docker-compuse up رو اجرا میکنیم و تموم میشه!داکر ایمِج یا Docker Imageتو داکر، همه‌چیز بر اساس ایمِج‌ها ساخته شده!داکر فایل یا Docker fileدستوراتی که برای ساخت ایمِج لازم هست اینجا تعریف میشه.حالا کانتِینِریزِیشن در مقابل ویرچوالیزِیشن یا مجازی‌سازی چی هست؟هرچه‌قدر که زمان میگذره، تکنولوژی داکر بین حرفه‌ای‌های IT محبوبیت بیشتری پیدا می‌کنه، که باعث میشه برای برنامه‌نویس‌ها دونستن حداقل مبانی کانتینر‌ها به یک «باید» تبدیل بشه.صنعت IT هرروز درحال تغییر و پیشرفته و «سرعت و بهینه‌سازی» تبدیل به اساس این صنعت شدن. تکنولوژی‌ها سعی کردن روش‌های بهتری رو برای اتوماسیون ارائه بدن تا پروسه ساخت و تولید برنامه‌ها روراحتتر و سریع‌تر کنن.نمیدونم که آیا با Industry 4.0 آشنایی دارید یا نه، اما مجازی‌سازی یا ویرچوالیزیشن (Virtualization) سعی کرده تا این نسخه از صنعت IT رو بهینه‌تر و قابل‌حمل‌تر یا Portable کنه. با این‌حال، تکنولوژی مجازی‌سازی نقاط ضعف جدی رو داره، مثل کاهش محسوس پرفورمنس نرم‌افزارها که بخاطر وزن زیاد و ساختار سنگین VMها یا Virtual Machineها اتفاق میوفته. یا مثلا غیرقابل حمل بودن برنامه‌ها و کارایی پایین در مدیریت منابع سیستم و چیزهای دیگه از این دسته.اینجاست که صنعت IT رفته سراغ تکنولوژی داکر و کانتینریزیشن! دقیق‌تر میگم، موتور داکر برای کانتینریزیشن ساخته شده که مراحل بسته‌بندی، حمل و گسترش برنامه‌ها رو بسیار ساده کرده.شکافت هسته‌ی اتم!برای یادگیری و راه‌اندازی داکر، نیازی نیست فیزیک کوانتوم بلد باشید یا بتونید حداقل یکی از سوالای میلنیوم ریاضی رو حل کنید! فقط کافیه یکمی با محیط ترمینال و cli دوست باشید و یک تکست‌ادیتور هم کنار دستتون باشه، و خب قطعا داشتن یکمی دانش برنامه‌نویسی هم میتونه به کارتون بیاد!نصب و راه‌اندازی داکرگرفتن تمام ابزارهای مورد نیازتون برای داکر میتونه کمی خسته‌کننده به نظر برسه. اما توصیه‌ی من اینه که قبل از نصب داکر، حتما نرم‌افزار vscode رو دانلود کنید و در نهایت داکر رو برای سیستم‌عامل خودتون دریافت کنید.بعد از نصب داکر، دستور زیر رو اجرا کنید تا مطمئن بشیم همه‌چیز درست انجام شده:$ docker run hello-worldکه در پاسخش باید نتیجه‌ی زیر رو دریافت کنید:Hello from Docker.
This message shows that your installation appears to be working correctly.بازی با BusyBoxحالا که داکر نصب شده، بریم و یکم دستامون رو کثیف کنیم. برای اینکار یه کانتینر BusyBox (یه نرم‌افزار که یسری از ابزارهای Unix رو توی یه فایل به شما میده) رو نصب میکنیم تا طعم دستور docker run رو بچشیم.برای شروع، دستور زیر رو اجرا کنید:$ docker pull busyboxدقت داشته باشید که ممکن هست به ارور permission denied برخورد کنید. بهترین راه اینه که خودتون رو توی گروه docker اضافه کنید تا ازش پیشگری بشه.دستور pull ایمِج BusyBox رو از رجیستری داکر (Docker Registry) یا همون داکرهاب (Docker Hub)دریافت و اون روی روی سیستممون ذخیره میکنه. ضمنا شما میتونید دستور docker images رو اجرا کنید تا ببینید چه ایمِج‌هایی روی سیستمتون نصب شدن.REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
nexus.ida-analytics.de/ida/moira     latest              e3503776559a        6 days ago          387MB
nexus.ida-analytics.de/ida/grafana   latest              a1192aa71c7f        7 days ago          387MB
nexus.ida-analytics.de/ida/grafana                 6145fb02ee49        7 days ago          387MB
nexus.ida-analytics.de/ida/moira                   1323d422a434        7 days ago          387MB
busybox                              latest              8c811b4aec35        7 weeks ago         1.15MB Docker Runعالی شد! حالا وقتش شده که یه کانتینر داکر، مبتنی بر ایمِجی که گرفتیم رو اجرا کنیم. برای اینکار از دستور زیر استفاده میکنیم:$ docker run busybox
چی شد؟! چرا هیچ اتفاقی رخ نداد؟! خب، واقعیت اینه که کلی اتفاقات اینجا افتاده که با چشم غیر مسلح قابل رویت نیست :دی. وقتی که شما دستور run رو صدا میزنید، داکر ایمِجی که گفتید رو پیدا میکنه (اینجا میشه Busybox)، یه کانتینر براش میسازه و یه دستور رو توی کانتینر اجرا میکنه. اگر دقت کنید، تو دستور docker run busybox ما هیچ دستور اضافی رو فراهم نکردیم. بنابراین کانتینر ساخته شده، بالا اومده، یه دستور خالی رو اجرا کرده و خارج شده.دستور بالا رو میشه اینطور هم نوشت:$ docker run busybox echo &quot;Salam Donya!!&quot;
Salam Donya!!اینجا داکر، دستور echo رو اجرا کرده و در نهایت بسته شده. حالا فکر کنید میخواستید همه‌ی اینا رو با ماشین‌مجازی اجرا کنید! چقدر باید صبر می‌کردید؟حالا وقتش شده که دستور docker ps رو اجرا کنیم.$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMESاز اونجایی که هیچ کانتینری در حال اجرا نیست، ما هم یه لیست خالی رو دریافت کردیم. حالا یکم دستور رو دقیق‌تر میکنیم:$ docker ps -a
CONTAINER ID        IMAGE                                       COMMAND                CREATED             STATUS                      PORTS               NAMES
2059d101f971        busybox                                     &quot;echo Hallo&quot;           15 minutes ago      Exited (0) 15 minutes ago                       hopeful_nightingale
45f36d883bd4        busybox                                     &quot;sh&quot;                   15 minutes ago      Exited (0) 15 minutes ago                       kind_mcnulty
375b34dfa64c        nexus.ida-analytics.de/ida/moira:latest     &quot;/bin/moira&quot;           6 days ago          Exited (137) 6 days ago                         moira
45ba50ce36f9        nexus.ida-analytics.de/ida/grafana:latest   &quot;/bin/start_grafana&quot;   6 days ago          Exited (0) 6 days ago                           grafanaهمون‌طور که مشخصه، پارامتر -a دستور داده که لیست تمام کانتینرها، فاقد از وضعیتشون (که اینجا exitedهست) برگردونده بشه.شاید براتون سوال بشه که چطور میشه دستورات بیشتری رو توی کانتینر اجرا کرد؟ خیلی آسون. اگر با دستور ssh آشنایی داشته باشید، میخوایم اینجا یه کاری مشابه اون رو انجام بدیم:$ docker run -it busybox sh
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # pwd
/
/ # uptime
 22:30:58 up  3:26,  0 users,  load average: 1.54, 1.46, 1.48
/ #اجرا کردن دستور run با پارامتر -it باعث میشه که ما به داخل کانتینر (در واقع با ساخت یه سِشِن tty) بریم و دستوراتی که میخوایم رو اونجا اجرا کنیم.قبل از اینکه به مرحله‌ی بعدی برم، بهتون این نکته رو هم میگم که چطور میشه کانتینرها رو حذف کرد. بالاتر دیدیم که با اجرا کردن دستور docker ps -a میتونیم لیست تمام کانتینرهامون رو بدست بیاریم. برای حذف یه کانتینر کافیه که CONTAINER ID او کانتینر رو برداریم و بعد دستور docker rm CONTAINER_ID رو اجرا کنیم. که در پاسخش باید آی‌دی کانتینر دوباره به شما نمایش داده بشه.دستور زیر هم هست:$ docker rm $(docker ps -a -q -f status=exited)اجرا کردن این دستور، تمام کانتینرهایی که وضعیت exited دارن رو پاک میکنه.وب‌اَپ در داکرتا اینجا فهمیدیم که دستور docker run تا حدودی چطور کار میکنه، همزمان یکمی هم با کانتینرها بازی کردیم و با ترمینولوژی داکر بیشتر آشنا شدیم. حالا وقتش شده که بریم سراغ مسائل اساسی، و ببینیم که چطور میشه یه وب‌اَپ رو تو داکر دیپلوی یا Deploy کرد.سایت‌های استاتیکبذارید قدم‌های کوچک برداریم. اولین چیزی که میریم سراغش، دیپلوی کردن یه سایت استاتیک خیلی ساده هست. یه ایمِج رو از داکر‌هاب دریافت یا pull میکنیم و اجراش میکنیم تا ببینیم چقدر راه‌اندازی سایت‌های استاتیک کار آسونیه.ایمِجی که برای اینکار استفاده میکنیم، یه ایمِج از قبل آماده شدست:$ docker run prakhar1989/static-siteاز اونجایی که این ایمِج به صورت لوکال یا محلی تو سیستم ما نیست، داکر اون رو از رجیستری دریافت میکنه و بعد ایمِج رو اجرا میکنه. اگر همه‌چیز خوب پیش بره، شما پیام Nginx is running... رو خواهید دید. حالا که سرور راه‌افتاده، چطور میشه بهش دسترسی داشت؟ چطوری میشه فهمید تو چه پورتی اجرا شده؟خب تو این حالت، داکر هیچ پورتی رو نمایش یا اصطلاحا اِکسپوز (Expose) نمیکنه، برای همین لازم هست که مجددا دستور docker run رو اجرا کنیم تا پورت‌ها رو نمایش بدیم. همزمان هم باید کاری کنیم که ترمینال روی این حالت قفل نشه و ما بتونیم از ترمینال، بدون بستن سرور خارج بشیم. به این حالت میگن Detached Mode یا حالت جدا شده!$ docker run -d -P --name static-site prakhar1989/static-site
531b38e65151ba7ee133aff085d4c3e31d9ab349d0fed2b48b91cf28d53ca685تو دستور بالا، -d حالت detached mode رو اجرا و -P پورت‌هارو باز میکنه. --name هم اسمی رو برای کانتینر درنظر میگیره. حالا میتونیم پورت‌های باز شده رو پیدا کنیم:$ docker port static-site
443/tcp -&gt; 0.0.0.0:32768
80/tcp -&gt; 0.0.0.0:32769حالا، http://localhost:32769 رو باز کنید و نتیجه رو ببینید.همچنین میتونید یه آدرس پورت دلخواه هم به ایمِجتون بدید:$ docker run -p 8888:80 prakhar1989/static-site
Nginx is running...این دستور میگه که: به پورت ۸۸۸۸ سیستم هاست یا میزبان، هرچیزی که تو پورت ۸۰ ایمِج بود رو اختصاص بده. برای اینکه کانتینر رو متوقف کنید، دستور docker stop رو به همراه آی‌دی کانتینر اجرا کنید.مطمئن هستم که شما هم موافقید این کار واقعا آسون بود! فکر کنید شما یه سرور دارید و میخواید سایتتون رو روش راه‌اندازی کنید، کل کار اینه که داکر رو نصب کنید و دستورات بالا رو وارد کنید!ولی خب، این برای ما کافی نیست، چون ما دوست داریم بیشتر بدونیم :) برای همین باید یاد بگیریم چطور داکر ایمِج خودمون رو بسازیم…داکر ایمِج‌هاما با ایمِج‌ها تا حدودی آشنا شدیم، ولی تو این بخش میخوام عمیق‌تر شیرجه‌ بزنیم تو ایمِج‌ها و ببینیم اصلا ایمِج‌های داکر چی هستن… کاری که انجام دادیم این بود که ایمِج busybox رو از رجیستری دریافت یا pull کردیم، و به داکر گفتیم که یه کانتینر مبتنی بر اون ایمِج برامون بسازه. اول بیاید یه لیستی از ایمِج‌هامون رو بگیریم:$ docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
nexus.ida-analytics.de/ida/moira     latest              e3503776559a        7 days ago          387MB
nexus.ida-analytics.de/ida/grafana   latest              a1192aa71c7f        7 days ago          387MB
nexus.ida-analytics.de/ida/grafana                 6145fb02ee49        7 days ago          387MB
nexus.ida-analytics.de/ida/moira                   1323d422a434        7 days ago          387MB
busybox                              latest              8c811b4aec35        7 weeks ago         1.15MB
prakhar1989/static-site              latest              f01030e1dcf3        2 years ago         134MB این لیست، لیست همه‌ی ایمِج‌هایی هست که من روی این سیستمم نصب دارم و از رجیستری‌های مختلف دریافت کردم. TAG به یک نسخه‌ی مشخص از ایمِج اشاره می‌کنه و IMAGE ID به آی‌دی منحصر به فرد اون ایمِج.برای سادگی فهم، ایمِج‌ها رو به چشم ریپازیتوری‌های گیت ببینید. ایمِج‌ها میتونن کامیت بشن و ورژن‌های مختلفی داشته باشن، در حالت عادی هم، داکر نسخه latest رو دریافت میکنه که مشابه شاخه یا branch اصلی یا main تو گیت هست. مثلا میخوایم یه نسخه از اوبونتو رو دریافت کنیم:$ docker pull ubuntu:12.04برای اینکه از یه ایمِج استفاده کنید، یا میتونید اون رو از رجیستری داکر یا همون داکر هاب دریافت کنید یا یه ایمِج برای خودتون بسازید.مهم‌ترین چیزی که باید موقع انتخاب ایمِج‌ها تو ذهن داشته باشید، تفاوت بین ایمِج‌های پایه یا base و ایمِج‌های مبتنی یا child هستن:ایمِج‌های پایه یا Base images: ایمِج‌هایی هستند که بر هیچ ایمِج دیگه‌ای مبتنی نیستن، که معمولا (و نه همیشه) سیستم‌عامل هستن، وایمِج‌های مبتنی یا Child images: که مشخصا مبتنی بر ایمج‌های پایه هستن.و دو جور ایمِج رسمی و عیر رسمی هم داریم:ایمِج رسمی یا Official image: که توسط خود گروه داکر پشتیبانی و نگهداری میشن. معمولا تک‌کلمه‌ای هستن مثل ubuntu، busybox و hello-world. وایمِج‌های عیر رسمی یا Unofficial image: که توسط کاربرهایی مثل من و شما ساخته میشن و بر اساس ایمِج‌های اصلی هستن که معمولا شکل نمایششون user-name/image-name هست.اولین ایمِج ماحالا که درک بهتری از ایمِح‌های داکر داریم، وقتش شده که یه ایمِج برای خودمون بسازیم. برای اینکار، یه برنامه‌ی ساخته شده با ری‌اکت‌جی‌اس رو اصطلاحا داکرایز یا Dockerize میکنیم:$ npm i -g create-react-appاول که باید نصاب ری‌اکت رو نصب کنیم، و بعد پروژه رو میسازیم:$ create-react-app our-awesome-app
$ cd our-awesome-appاز اینجا به بعد هر کار خواستید با پروژه بکنید… اما در نهایت یه فایل به اسم Dockerfile تو پوشه‌ی اصلی پروژه بسازید و اون رو بازش کنید و محتوای زیر رو بهش اضافه کنید:FROM node:9.6.1

RUN mkdir /usr/src/app
WORKDIR /usr/src/app

ENV PATH /usr/src/app/node_modules/.bin:$PATH

COPY package.json /usr/src/app/package.json
RUN npm i --silent
RUN npm i react-scripts -g --silent

CMD \[&quot;npm&quot;, &quot;start&quot;\]و یه فایل .dockerignore بسازید (به . اول اسمش دقت کنید) و توش محتوای زیر رو بریزید:node_modulesو در نهایت ایمِج رو بسازید:$ docker build -t our-awesome-app .
...
Successfully built 8ab82c09e422
Successfully tagged our-awesome-app:latestو بعد از اتمام ساخت، کانتینر مربوط به ایمِجتون رو ایجاد و اجرا کنید:$ docker run -it \
  -v ${PWD}:/usr/src/app \
  -v /usr/src/app/node_modules \
  -p 3000:3000 \
  --rm \
  our-awesome-appبه همین سادگی! شما پروژتون رو داکرایز کردید :) حالا میتونید http://localhost:3000 رو باز کنید و ببینید پروژتون رو.داکر کامپُز (Docker Compose)تا اینجا با خود داکر سر و کله زدیم و تا حدودی اکوسیستمش رو شناختیم. منتهی همچنان ابزارهایی هستند که یادگیریشون برامون خیلی پر کاربرد خواهد بود. چندتا از این ابزارهای خوب:Docker Machine که کمک میکنه هاست‌های داکر روی کامپیوترتون، کلاد یا فضاهای ابری و حتی دیتا‌سنترتون بسازید،Docker Compose ابزاری برای ساخت اپلیکیشن‌های چند کانتینری داکر (چندتا کانتینر رو در کنار هم قرار میده) وDocker Swarm که ابزاری برای ساخت کلاسترها یا خوشه‌های کانتینریه.تو این بخش میخوایم بریم سراغ داکر کامپُز و ببینیم که چطور میشه اپلیکیشن‌های داکر مبتنی بر چند کانتینر ساخت.اینطور فرض کنید که یه کانتینر مسئول اجرای کدهای PHP، Go، JavaScript و زبان‌های دیگست، یه کانتینر دیتابیس MySql و MongoDb رو داره، یه کانتینر وب‌سرور Apache یا NginX و…گذشته‌ی داکر کامپُز جالبه، تقریبا چهار سال پیش (سال ۲۰۱۴) شرکتی به اسم OrchardUp ابزاری رو به اسم Fig به بازار عرضه کرد. هدف از ساخت Fig این بود که بشه محیط برنامه‌نویسی یا Development Environment مبتنی بر داکر ساخت و اونها رو ایزوله کرد، تا فضای کاری برنامه‌نویس‌ها مشابه به هم بشه.تا اینجا، داکر یه ابزاری بود برای ساخت پروسه‌ها یا Application Processes. بعد از این، داکر APIهای مختلفی رو ارائه داد که بشه پوشه‌ها رو بین کانتینرها به اشتراک گذاشت و پورتی رو از هاست به کانتینر فوروارد کرد، لاگ‌ها رو نمایش داد و غیره. ولی با همه‌ی اینها، داکر فقط یه چیز بود: ابزاری برای ساخت پروسه‌ها!با اینکه داکر این امکان رو میده تا بشه کانتینرهای مختلف رو با هم اورکِسترِیت یا Orchestrate کرد (به زبان ساده یعنی هماهنگی بینشون ایجاد کرد تا منظم و درست کار کنن)، همچنان با این کانتینرها به شکل «یک موجودیت» یا Single Entity برخورد نمیکنه. یعنی مهندس نرم‌افزار باید همه‌چیز رو خودش مدیریت کنه. اینجاست که حضور ابزاری مثل Fig خیلی بدرد خورد! از این به بعد مهندسین باید به این شکل بهش نگاه میکردن: «یک برنامه‌ی داکر رو اجرا کنیم که کلاستری از کانتینرها رو مدیریت میکنه» و نه اینکه صرفا یه کانتینر رو اجرا کنیم.مشخص شد که خیلی از بروبچه‌ها و مهندسینی که از داکر استفاده میکردن با این تعریف موافق بودن. برای همین هم وقتی که Fig در حال محبوب شدن بود، شرکت داکر اون رو خرید و اسمش رو به Docker Compose تغییر داد.خب، حالا اصلا کامپُز برای چی استفاده میشه؟ کامپُز یه ابزاره که کمک میکنه برنامه‌هایی رو با چند کانتینر اورکستریت کنیم. این ابزاری فایلی رو ایجاد میکنه به اسم docker-compose.yml که کل دستوراتی رو که لازم هست رو در خودش داره و اونها رو فقط با یه دستور اجرا میکنه.بذارید با هم برنامه‌ای رو که بالاتر با داکر ساختیم، اینبار با داکر کامپُز اجرا کنیم. برای اینکار فایل docker-compose.yml تو پوشه‌ی اصلی برنامه بسازید و محتوای زیر رو توش قرار بدید:version: &#039;3.5&#039;

services:

  our-awesome-app:
    container_name: our-awesome-app
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      \- &#039;.:/usr/src/app&#039;
      \- &#039;/usr/src/app/node_modules&#039;
    ports:
      \- &#039;3000:3000&#039;
    environment:
      \- NODE_ENV=developmentحالا کامپُز رو نصب کنیم…بعد از نصب کامپُز، دستور زیر رو اجرا کنید:$ docker-compose up -d --buildساختار داکر کامپُزاصلی‌ترین دلیل ساخت داکر کامپُز، ایجاد برنامه‌ها بر اساس معماری مایکروسرویس بود، یا درواقع کانتینرها و روابط بینشون. اما داکر کامپًز ویژگی‌های دیگه‌ای هم داره:ساخت یک ایمِج داکر (درصورتی که یک فایل Dockerfile معتبر تو پوشه‌ی اصلی موجود باشه) با دستور:docker-compose buildمقیاس‌بندی کانتینرها با دستور:docker-compose scale SERVICE=3نجات‌دادن یا درواقع اجرای مجدد کانتینرها در صورت پَنیک با دستور:docker-compose up --no-recreateیکی از مهم‌ترین دستورات داکر کامپُز، docker-compose up هست که اول دستور docker-compose build و بعد docker-compose run رو اجرا می‌کنه.جریان کار یا Workflow در داکر کامپُزجریان کار تو داکر کامپُز سادست:هر سرویس رو تو یه داکر فایل تعریف می‌کنیم (یک روش)،سرویس‌ها و روابطشون رو تو فایل docker-dompose.yml تعریف می‌کنیم ودستور docker-compose up رو اجرا می‌کنیم تا سیستم بالا بیاد.اما برای اینکه بهتر متوجه بشیم، میریم که چندتا اپ مختلف بسازیم تا روش‌ها دیگه برای استفاده از داکر کامپُز رو یاد بگیریمادامه‌ی مطلب رو میتونید از سایت خودم بخونید…</description>
                <category>پول ریکوئست</category>
                <author>آیین</author>
                <pubDate>Mon, 23 Jul 2018 16:01:42 +0430</pubDate>
            </item>
                    <item>
                <title>آشنایی با Symfony با ساختن یه وبلاگ به صورت پروژه ای</title>
                <link>https://virgool.io/pullrequest/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-symfony-%D8%A8%D8%A7-%D8%B3%D8%A7%D8%AE%D8%AA%D9%86-%DB%8C%D9%87-%D9%88%D8%A8%D9%84%D8%A7%DA%AF-%D8%A8%D9%87-%D8%B5%D9%88%D8%B1%D8%AA-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%A7%DB%8C-xkrvck3jrmcr</link>
                <description>eبین تعدا زیادی از تکنولوژی‌های وب که وجود دارن، پی‌اچ‌پی تو طول زمانی تغییرات سریعی رو پشت سر گذاشته و الان به یک زبان بالغ تبدیل شده.پی‌اچ‌پی امروز هم برای وب‌سایت‌ها و وب‌اپلیکیشن‌ها کوچیک و همینطور بزرگ استفاده میشه و شاید یکی از دلایل محبوبیتش، سادگیش باشه.با توجه به اینکه هرروز پیچیدگی وب‌اپلیکیشن‌ها بیشتر میشه، برنامه‌نویس‌ها بیشتر به استفاده از فریم‌ورک‌ها رو آوردن و حتی برای پروژه‌های کوچیکشون هم از اونا استفاده می‌کنن. شاید با خودتون بگید «فریم‌ورک چیه؟». فریم‌ورک‌ها اساسا چیزی بجز یکسری توابع و ساختاربندی‌های کلاسی نیستن که از قبل آماده شدن و قابل استفاده هستن، که مهمترین ویژگیشون اختراع نکردن چرخ از اول هست و این یعنی صرفه‌جویی زمان.تقریبا تمام زبان‌های برنامه‌نویسی برای خودشون فریم‌ورک‌هایی رو دارن که هرکدوم با هدفی ساخته شدن. یسری از فریم‌ورک‌های محبوب پی‌اچ‌پی، Zend Framework, Cake PHP, Smarty, Laravel و Symfony هستند، و من خودم بشخصه طرفدار سیمفونی هستم و دلایلش رو هم میگم. در نهایت سعی میکنم برای کسانی که دوست دارن با سیمفونی آشنا بشن، توضیحی از نحوه نصب و راه‌اندازی و کار مختصر باهاش رو توضیح بدم.انعطاف‌پذیریتو خیلی از پروژه‌هایی که کار کردم و دیدم، سیمفونی بهم ثابت کرد که خیلی زیاد انعطاف‌پذیره و یک انتخاب خوب برای کسب‌وکار. با قابلیت‌هایی مثل Dependency Injectorها یا Event Dispatcherها کاملا قابل تنظیم و شخصی‌سازی شده. سیمفونی یک ساختار OOP و SOA رو به صورت حرفه‌ای پیش برده که باعث شده برای کسایی که بخوان چه از ساختار Monolithic و چه از Micro Service بهره ببرن، گزینه‌ی مناسبی باشه، و این یعنی توسعه سریع، افزایش مقیاس پروژه و نگهداری راحت.برنامه‌نویس‌ها نیازی ندارن تا ویژگی‌های پایه و اساسی پروژه رو از اول بنویسن یا حتی بازنویسی کنن، مثل سرویس مدیرت فرم. سیمفونی برنامه‌نویس رو به چالش واقعی دعوت میکنه، محاسبات و مدیریت. بنظرم یکی از بهترین ویژگی‌های سیمفونی داشتن Web Debug Toolbar هست که حقیقتا یه دوست واقعی برای برنامه‌نویسه.ساختار تیکه تیکه! (باندل)بنظرم یکی از ویژگی‌های بسیار مثبت سیمفونی، ساختار تیکه تیکش هست. یعنی سیمفونی از اجزای کوچکتر ساخته شده که هرکدومشون جداگانه قابل استفاده هستن، یک نمونش HTTP Foundation که خیلی زیاد تو لاراول استفاده شده. در واقع خود سیمفونی، یک تیکه کوچیک از این تیکه‌هاست که در حقیقت وظیفش جمع کردن همه‌ی تیکه‌ها (اسم درستشون باندل هست) و در کنار هم قراردادنشونه. برای همین سیمفونی رو میشه به دو حالت استفاده کرد:یک فریم‌ورک کامل: کل ابزارهای سیمفونی و یک عالمه ابزاری که برنامه‌نویس‌های دیگه نوشتن رو کنار هم و یکجا داشته باشیدمیکرو فریم‌ورک‌ها: که درواقع شما توی پروژه‌ای که کار میکنید، بدون نصب سیمفونی، فقط از باندل‌های بدرد بخورش براش خودتون استفاده کنید، مثل FormBundleدر مورد TWIG!الان تو دنیایی هستیم که کمتر کسی علاقه‌ای به نوشتن ظاهر سایت و HTML بصورت دستی داره، بخصوص زمانی که تعداد فایلها قراره زیاد بشن و شما مجبور بشید به ازای هر صفحه یه فایل جدید بسازید. توییگ، یه موتور ساخت قالب هست که بهتون کمک خیلی زیادی توی طراحی میکنه. مثلا شما میتونید یک فایل html بسازید و داخلش از توییگ استفاده کنید و فقط متغیرها رو بهش پاس بدید. و اون برای شما به سادگی و سریع رندر کنه.سیمفونی به صورت پیشفرض از توییگ پشتیبانی میکنه و خیلی از زمان طراحی شما کم میکنه.داکترین (Doctrine)داکترین در واقع یک سری کتابخونه برای php هست که وظیفش ایجاد یک لایه‌ی اضافی برای ارتباط برنامه‌نویس با دیتابیسه، در حقیقت به جای اینکه برنامه‌نویس مجبور بشه کدهای sql رو دستی وارد کنه، با نصب داکترین، فقط به کدهای php بسنده میکنه و داکترین تمام کارهارو براش انجام میده.داکترین تبدیل به یک پروژه بالغ شده و بنظرم تمام امکانات مورد نیاز برنامه‌نویس رو در اختیارش قرار میده. خود داکترین شاخه‌های زیرمجموعه داره که مهمترین و پرمصرف‌ترینش Doctrin ORM هست که وظیفش بصورت ساده و خلاصه، تبدیل کلاس‌های php به مدل‌های دیتابیسی هست.سیمفونی بطور پیشفرض از Doctrine ORM پشتیبانی میکنه و نیازی نیست شما اون رو کانفیگ کنید.تست راحتبنظرم سیمفونی با ایده‌ی «تستینگ در اولویت» پیش رفته. Unit Testingها با استفاده از PHPUnit خیلی خوب از کدها جدا شدن مدیریتشون خیلی راحت شده. با یه کمک خیلی زیاد از PHPUnit این امکان برای برنامه‌نویس وجود داره تا به سادگی درخواست‌های http رو بازسازی، آزمایش و تست کنه بدون اینکه مجبور باشه کد زیادی رو با ابزار تست بزنه.در واقع این امکان سیمفونی حتی زمان زیادی رو هم توی تست حفظ میکنه و سرعت عمل رو بالا میبره. در مجموع اکوسیستم سیمفونی روی تستینگ و کیفیت بالای اون خیلی تمرکز داشته و موارد خوبی رو فراهم کرده.دیباگ مثل آب خوردنمحبوبترین ویژگی سیمفونی، Web Debug Tool هست که اطلاعات کاملی و دقیقی راجع به وضعیت فعلی درخواست‌ها، ترنسپایلر‌ها و غیره میده و حتی امکان تست سرعت رندر هم داره. Symfony Profiler همون ابزار دیباگینگ هست که در مورد تمام اتفاقات پروژه تو هر مرحله گزارش کامل و دقیق میده.نصب سیمفونیحتما براتون سوال شده که چطور میشه سیمفونی رو نصب و راه‌اندازی کرد. برای اینکار روش‌های مختلی وجود داره، اما بهترین روش که من هم ازش استفاده میکردم و راضی بودم، استفاده از نصاب خود سیمفونی هست که برای راه‌اندازیش میتونید به شکل زیر عمل کنید:دریافت فایل نصاببرای لینوکس و مکsudo curl -LsS https://symfony.com/installer -o /usr/bin/symfony
sudo chmod a+x /usr/bin/symfonyبرای ویندوزC:\&gt; php -r &quot;readfile(&#039;https://symfony.com/installer&#039;);&quot; &gt; symfonyاستفاده از نصابsymfony new helloWorld ltsحتما مطمئن باشد که از PHP Phar Extension استفاده میکنید!نصب Composerکامپوزر، در حقیقت یک Package Manager هست که پکیج‌هایی که تو ریپازیتوری کامپوزر وجود دارن رو براتون پیدا و بهترین رو از لحاظ مطابقت با ورژن‌های شما نصب میکنه.کامپوزر رو میتونید به سادگی از ریپازیتوری‌های توزیع لینوکستون پیدا کنید. مثلاsudo packman -S composerیاsudo apt-get install composerراه اندازی سرور و ادامه کد زدندایرکتوریتون رو به فولدر helloWorld تغییر بدبدcd helloWorldو سرور داخلی سیمفونی رو اجرا کنید:app/console server:runگام به گام با هم پیش بریمبیاید باهم یه وب‌سایت ساده بنویسیم. فکر میکنم یه وبلاگ میتونه گزینه مناسبی برای شروع باشه. پس اگر هنوز نصاب سیمفونی و کامپوزر رو نصب نکردید، برگردید عقب و انجامشون بدید تا در ادامه مشکلی نداشته باشید.قدم اول، راه اندازی پروژهsymfony new MyBlog ltsپس از اجرا، باید همچین پاسخی رو دریافت کنید:Downloading Symfony...

    5.4 MiB/5.4 MiB ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  100%

 Preparing project...

   Symfony 2.8.24 was successfully installed. Now you can:

    * Change your current directory to ~/MyBlog

    * Configure your application in app/config/parameters.yml file.

    * Run your application:
        1. Execute the php app/console server:start command.
        2. Browse to the http://localhost:8000 URL.

    * Read the documentation at http://symfony.com/docو در نهایت دستور composer install رو داخل فولدر MyBlog اجرا کنید.حالا IDE یا ادیتورتون رو باز کنید و پروژه رو داخلش بیارید بالا.تنظیمات دیتابیستمام تنظیمات پروژه، داخل فولدر app/config قرار میگیرن. فولدر app موظف به نگهداری از تمام منابع اپلیکیشن هست، مثل قالب‌های توییگ، لاگ‌ها و تنظیمات و هسته اپلیکیشن.فایلهایی که داخل پوشه app/config هستن، معمولا با پسوند .yml یا .yaml قرار میگیرن که میتونم بگم از لحاظ کاربری مشابه .json هستن.حالا فایل app/config/parameters.yml رو باز کنید. با همچین ساختاری مواجه میشید:# This file is auto-generated during the composer install
parameters:
    database_host: 127.0.0.1
    database_port: null
    database_name: symfony
    database_user: root
    database_password: null
    mailer_transport: smtp
    mailer_host: 127.0.0.1
    mailer_user: null
    mailer_password: null
    secret: 90830183902924603f797e4b794ff7e8353e43e9تیکه اول درواقع تنظیمات عمومی اپ برای ارتباط با دیتابیس هست. این تنظیمات رو به:database_name: MyBlog #یا هر اسم دیگه‌ای که برای دیتابیس دوست دارید
database_user: root #یوزرنیم که باهاش به دیتابیس وصل میشید
database_password: null #پسورد دیتابیستون...تغییر بدید. من در حالت عادی با MySql کار میکنم، اما سیمفونی گزینه‌های دیگه‌ای هم مثل MongoDb و دیتابیس‌های دیگه داره و از اونا پشتیبانی میکنه.ساخت دیتابیسیکی از گزینه‌های خیلی خوب سیمفونی که من دوست دارم، کنسولش هست. که خیلی از کارهارو برای شما انجام میده. میتونید اطلاعات بیشتری راجع بهش رو صرفا با تایپ app/console بدست بیارید.الان فقط ساخت دیتابیس برامون کافیه تا بریم مراحل بعدی:app/console doctrine:database:createو پاسخش:Created database `MyBlog` for connection named defaultحالا چک کنیم که تا اینجا همه‌چیز خوب پیشرفته:app/console server:run

[OK] Server running on http://127.0.0.1:8000

// Quit the server with CONTROL-C.میتونید صفحه رو باز کنید و چک کنید. نوار سیاه پایین صفحه، همون Web Debug Tool هست که گفته بودم. بعد از اینکه مطمئن شدید اوضاع خوبه، پروسه رو قطع میکنیم و میریم گام بعدی.ساخت یک باندل برای مدیریت وبلاگیک نکته مهم، از اونجایی که سیمفونی ساختار تیکه تیکه داره (Bundle) شما میتونید هر قسمتی از سایت رو بر اساس ساختار خودتون به یک باندل تبدیل کنید. مثلا فرض میکنیم که وبلاگ سایت، برای خودش یه تیکه جداست، یعنی میتونیم خیلی راحت از پروژه حذفش کنیم و دیگه کاربرا بلاگی نبینن. برای اینکار، یه باندل جدید میسازیم:app/console generate:bundle

 
  Welcome to the Symfony bundle generator!  
 

Are you planning on sharing this bundle across multiple applications? [no]: 

Your application code must be written in bundles. This command helps
you generate them easily.

Give your bundle a descriptive name, like BlogBundle.
Bundle name: MyBlogBundle

Bundles are usually generated into the src/ directory. Unless you&#039;re
doing something custom, hit enter to keep this default!

Target Directory [src/]: 

What format do you want to use for your generated configuration?

Configuration format (annotation, yml, xml, php) [annotation]: 

 
  Bundle generation  
 

&gt; Generating a sample bundle skeleton into app/../src/MyBlogBundle
  created ./app/../src/MyBlogBundle/
  created ./app/../src/MyBlogBundle/MyBlogBundle.php
  created ./app/../src/MyBlogBundle/Controller/
  created ./app/../src/MyBlogBundle/Controller/DefaultController.php
  created ./app/../tests/MyBlogBundle/Controller/
  created ./app/../tests/MyBlogBundle/Controller/DefaultControllerTest.php
  created ./app/../src/MyBlogBundle/Resources/views/Default/
  created ./app/../src/MyBlogBundle/Resources/views/Default/index.html.twig
  created ./app/../src/MyBlogBundle/Resources/config/
  created ./app/../src/MyBlogBundle/Resources/config/services.yml
&gt; Checking that the bundle is autoloaded
&gt; Enabling the bundle inside app/AppKernel.php
  updated ./app/AppKernel.php
&gt; Importing the bundle&#039;s routes from the app/config/routing.yml file
  updated ./app/config/routing.yml
&gt; Importing the bundle&#039;s services.yml from the app/config/config.yml file
  updated ./app/config/config.yml

 
  Everything is OK! Now get to work :).سوال اول اینه:Are you planning on sharing this bundle across multiple applications? [no]که اگر جوابتون yes باشه، تمام فایلهای باندل، مثل قالب‌ها، تنظیمات و غیره رو توی خود فولدر باندل میریزه که شما بتونید راحت جداش کنید. در غیر این صورت، قالب‌ها داخل فولدر app/Resources/views ریخته میشن. (که عملا جدا کردنشون کار سختی میشه…)برای ادامه آموزشمون، میتونید این گزینه رو همینطور رها کنید (اینتر بزنید) و بریم سراغ سوال بعدی:Bundle name: MyBlogBundleاین اسم، ارتباطی با اسم پروژه نداره، یهو به ذهنم رسید، شما میتونید از BlogBundleیا HaminjuriBundle استفاده کنید… مهم اینه که حتما کلمه Bundle به آخرش، به صورت CamelCase اضافه بشه. سوال بعدی این خواهد بود:Target Directory [src/]:تمام باندل‌های سایت شما (مگر زمانی که خودتون کانفیگ کنید) داخل فولدر src ریخته میشن، درواقع شما ۹۹٪ زمانتون رو تو این فولدر صرف میکنید. ماهم این گزینه رو به حال خودش رها میکنیم.Configuration format (annotation, yml, xml, php) [annotation]نوع تنظیماتی که باندل قرار داشته باشه چطوره، میتونید از هر نوعی که گفته استفاده کنید ولی من annotation رو ترجیح میدم چون خیلی راحت میشه تغییرش داد و مدیریتش خیلی آسونتر هست. پس فقط اینتر رو میزنم و میرم سراغ گام بعدی!ساخت یک مدل ساده از پست وبلاگخب، تا اینجا خوب پیشرفتیم. فرض میکنیم کاربرای ما، نیازی به مدلسازی پیچیده‌ای ندارن و در حد ساده‌ی آموزش ما براشون کافیه. یک پست وبلاگ چه چیزا‌هایی احتیاج داره؟عنوان (title) از نوع رشته‌ای (string) که حتما باید وارد بشه (not nullable) و نباید تکراری باشه (unique)متن نوشته (content) از نوع متنی (text) که حتما باید وارد بشه (not nullable)تاریخ ساخت (createdAt) از نوع تاریخ (data) که حتما باید وارد بشه (not nullable)دوباره میریم سراغ کنسول:app/console doctrine:generate:entity

 
  Welcome to the Doctrine2 entity generator 
  
This command helps you generate Doctrine2 entities.

First, you need to give the entity name you want to generate.
You must use the shortcut notation like AcmeBlogBundle:Post.

The Entity shortcut name: BlogBundle:Postاینجا بهش میگیم که این انتیتی (entity)، یا همون مدلمون، مربوط به کدوم باندل هست. اسم باندل که باهم درست کردیم دو نقطه اسم انتیتی که میشه پست…Configuration format (yml, xml, php, or annotation) [annotation]همچنان با انوتیشن پیش میریم. از اینجا به بعد ازتون میخواد که پراپرتی‌های مدل رو مشخص کنید. زمانی که کارتون تموم شد، میتونید اینتر رو بزنید و ادامه رو به کنسول بسپارید. پس پیش میریم:New field name (press &lt;return&gt; to stop adding fields): title
Field type [string]: 
Field length [255]: 
Is nullable [false]: 
Unique [false]: true

New field name (press &lt;return&gt; to stop adding fields): content
Field type [string]: text
Is nullable [false]: 
Unique [false]: 

New field name (press &lt;return&gt; to stop adding fields): createdAt
Field type [string]: date
Is nullable [false]: 
Unique [false]: 

New field name (press &lt;return&gt; to stop adding fields): 

 
  Entity generation  
 

  created ./src/BlogBundle/Entity/
  created ./src/BlogBundle/Entity/Post.php
&gt; Generating entity class src/BlogBundle/Entity/Post.php: OK!
&gt; Generating repository class src/BlogBundle/Repository/PostRepository.php: OK!

 
  Everything is OK! Now get to work :).حالا اگر برید داخل src/BlogBundle/Entity متوجه میشید که فایل Post.phpبراتون ساخته شده. این فایل در واقع یه کلاس ساده PHP هست که صرفا پراپرتی‌های مدل رو نگه‌داری میکنه. در واقع این همون مدل پست ماست که میتونید بعدا خیلی راحت تغییرش بدید.ضمنا همزمان با ساخت این مدل، یک فایل هم داخل src/BlogBundle/Repositoryبه نام PostRepository.php ساخته شده، که در واقع وظیفه اصلیش اینه:یک جاهایی از پروژه شما نیاز پیدا میکنید تا مثلا یک رکورد خاص رو از دیتابیس، با محاسبات خاصی دریافت کنید. اینجا میتونید تمام دستورات لازم رو بنویسید تا همه‌جا بشه به سادگی ازشون استفاده کرد.حالا که مدل ساخته شد، اون رو به دیتابیس منتقل میکنیم تا ساختار دیتابیس با مدل‌های ما یکی بشه:app/console doctrine:schema:update --force
Updating database schema...
Database schema updated successfully! &quot;1&quot; queries were executedچرا از آرگومان --force استفاده کردیم؟ یک دلیلش اینه که مطمئن باشیم داریم درست پیش میریم. پیش میاد زمانی که احتمالا دستور رو اشتباه وارد کنیم و باعث بشیم تا دیتابیس بهم بریزه. مورد بعدی اینکه شما میتونید از آرگومان --dump-sql هم استفاده کنید تا ببینید دقیقا چه دستوری به SQL اجرا میشه.ساخت فرم برای مدیریت پست‌هاتا اینجا خوب پیشرفتیم. حالا باید این امکان رو برای کاربر فراهم کنیم، تا بتونه پست‌ها رو کراد (CRUD = Create, Read, Update and Delete) کنه. خوبی سیمفونی اینه که تقریبا تمام این کارها رو با کنسول میشه انجام داد. پس:app/console doctrine:generate:crud

 
  Welcome to the Doctrine2 CRUD generator

This command helps you generate CRUD controllers and templates.

First, give the name of the existing entity for which you want to generate a CRUD
(use the shortcut notation like AcmeBlogBundle:Post)

The Entity shortcut name: BlogBundle:Post

By default, the generator creates two actions: list and show.
You can also ask it to generate &quot;write&quot; actions: new, update, and delete.

Do you want to generate the &quot;write&quot; actions [no]? yes

Determine the format to use for the generated CRUD.

Configuration format (yml, xml, php, or annotation) [annotation]: 

Determine the routes prefix (all the routes will be &quot;mounted&quot; under this
prefix: /prefix/, /prefix/new, ...).

Routes prefix [/post]: /blog

 
  Summary before generation  
 

You are going to generate a CRUD controller for &quot;BlogBundle:Post&quot;
using the &quot;annotation&quot; format.

Do you confirm generation [yes]? 

 
  CRUD generation  
 

  created ./src/BlogBundle/Controller//PostController.php
  created ./app/Resources/views/post/
  created ./app/Resources/views/post/index.html.twig
  created ./app/Resources/views/post/show.html.twig
  created ./app/Resources/views/post/new.html.twig
  created ./app/Resources/views/post/edit.html.twig
  created ./src/BlogBundle/Tests/Controller/
  created ./src/BlogBundle/Tests/Controller//PostControllerTest.php
Generating the CRUD code: OK
  created ./src/BlogBundle/Form/
  created ./src/BlogBundle/Form/PostType.php
Generating the Form code: OK
Updating the routing: OK

 
  Everything is OK! Now get to work :).بذارید مرحله به مرحله توضیح بدم. اول دستور app/console doctrine:generate:crud رو زدم. بعد ازم پرسید که برای چه انتیتی میخوام کراد بسازم و من هم BlogBundle:Post رو وارد کردم. بعد ازم پرسید که پیشوند مسیرم برای کاربر چیه، در واقع کاربر از چه مسیری توی سایت میتونه به این عملیات‌ها دسترسی داشته باشه. خودش بصورت پیشفرض، اسم انتیتی (Post) رو در نظر گرفته بود و من به /blogتغییرش دادم. و در نهایت پرسید که آیا مطمئن هستم؟ و منم مطمئنم…خب، حالا یه تست کنیم ببینیم تا اینجا چطور پیشرفتیم:app/console cache:clear
app/console server:runاز این آدرس استفاده کنید تا بتونید بهتر ببینید:http://localhost:8000/app_dev.php/blog/app_dev.php در واقع محیط دیباگ رو فراهم میکنه. اگر واردش نکنید، سایت اصلی بدون گزینه‌های دیباگ و پولیش شده جلوتون قرار میگیره.تا اینجا عالی بود. پیشنهاد میکنم یکم با این صفحه کار کنید و چندتا پست نمونه بسازید، پاکشون کنید و تغییرشون بدید. چقدر راحت شد همه‌چیز.ولی ظاهرش اصلا قشنگ نیست… قدم بعدیمون، میریم سراغ یکم طراحی…یکم کار با قالب‌هاتمام قالب‌های مربوط به کراد، با توجه به اینکه قرار نیست ما این باندل رو جدا کنیم، تو فولدر app/Resources/views/post قرار گرفتن. از اسم فایل‌ها مشخصه که هر کدوم چه وظیفه‌ای به عهده دارن. پسوندشون هم .html.twig هست که درواقع یک فایل توییگه که باهاش به شکل فایل html برخورد میشه.فایل index.html.twigاگر به فایل src/BlogBundle/Controller/PostController.php نگاه کنید، که بعد از اجرای دستور doctrine:generate:crud ایجاد شده، متوجه میشید که تمام عملیات کراد اینجا انجام میشه.کنترلرها در واقع کلاس‌هایی هستن که وظیفشون مدیریت فعالیت‌های قسمت‌های مختلف باندل یا مسیرهای اوناست. اینو رو ببینید:/**
* Lists all post entities.
*
* @Route(&quot;/&quot;, name=&quot;blog_index&quot;)
* @Method(&quot;GET&quot;)
*/
public function indexAction()
{
    $em = $this-&gt;getDoctrine()-&gt;getManager();

    $posts = $em-&gt;getRepository(&#039;BlogBundle:Post&#039;)-&gt;findAll();

    return $this-&gt;render(&#039;post/index.html.twig&#039;, array(
        &#039;posts&#039; =&gt; $posts,
    ));
}چون تعریف کردیم که تظیمات رو با انوتیشن انجام میدیم، تمام تعاریف اکشن‌ها (یا همون تیکه‌های کوچیک که وظیفه مدیریت قسمت‌های مختلف باندل رو دارن وکنترلر اونارو یجا جمع کرده) بالاشون، انوتیت میشن.مثلا اینجا به @Route دقت کنید، آرگومان اولش / هست، یعنی مسیرباندل/مسیر کنترلر/مسیر اکشن که اینجا برای ما میشه /blog/. دقت کنید که خود باندل بلاگ، مسیری رو نداره، و ما زمان ساخت فرم گفتیم که پیشوند مسیر رو از /post به /blog تغییر بده. اگر دوست داشتید که مسیر باندل رو هم عوض کنید، فایل app/config/routing.yml رو باز کنید و prefix باندل رو از / به هرچی که دوست دارید، مثلا hello تغییر بدید. در نتیجه این تغییر، کاربر شما برای دریافت لیست پست‌ها باید به آدرس hello/blog/ مراجعه کنه.خب، برمیگردیم به خود کد و اون رو مرور میکنیم. $em درواقع انتیتی منیجر داکترین تعریف شده. وقتی ما این کلاس رو از Symfony\Bundle\FrameworkBundle\Controller\Controller اکستند کردیم، امکاناتی مثل getDoctrine() به $this اضافه شده.بعد اومده و لیست تمام رکورد‌های موجود توی جدول پست رو گرفته، و در نهایت فایل app/Resources/views/post/index.html.twig رو رندر کرده و پست‌ها رو پاس داده بهش.حالا بریم سراغ فایل app/Resources/views/post/index.html.twig. تو خط اولش متوجه میشیم که {% extends &#x27;base.html.twig&#x27;%} اجرا میشه، یعنی، این فایل ما یک بخشی از فایل اصلی base.html.twig هست که تو پوشه app/Resources/views/base.html.twig قرار گرفته.شما با تغییر دادن این فایل میتونید ظاهر سایت رو عوض کنید. مثلا من بالای base.html.twig میخوام اسم سایت رو اضافه کنم. فایلش رو باز میکنم و:حالا بالای صفحه یه هدر دارم و پایین هم یه فوتر. یه استایل ساده رو داخل فولدر web/style.css میذارم و اینا رو داخلش اضافه میکنم:body {
    font-family: sans;
}

.header {
    margin: 15px;
    background-color: #eee;
}

.footer {
    margin: 15px;
    border: 1px solid #ccc;
}و در نهایت به base.html.twig میگم که این فایل رو بیاره و تو صفحه‌ها نمایش بده:دستورات توییگ با { { } } (بدون فاصله) اجرا میشن. و asset(&#x27;...&#x27;) میره و هرچیزی که داخل فولدر web بود رو برای شما با اون اسم میاره. دیگه نیازی نیست نگران آدرس فایل‌ها باشید!حالا میخوام جدولی که توی فایل app/Resources/views/post/index.html.twig بود رو یکم قشنگتر کنم، یه فایل table.css توی web میسازم و اینارو داخل مینویسم:table, td, th {    
    border: 1px solid #ddd;
    text-align: left;
}

table {
    border-collapse: collapse;
    width: 100%;
}

th, td {
    padding: 15px;
}و بعد، میخوام هرموقع که کاربر صفحه ایندکس پست‌ها (indexAction) رو باز میکنه، این استایل نمایش داده بشه. برای اینکار، فایل post/index.html.twig رو باز میکنم و:کارمون تموم شد. یه تست میکنیم:app/console cache:clear
app/console server:runحالا صفحه پست‌ها رو باز کنید و ببینید چه اتفاقی افتاد…خب ما باهم تونستیم یه وب‌سایت خیلی ساده رو درست کنیم و تقریبا هیچ کدی نزنیم. سیمفونی هنوز مونده و خیلی خیلی جا برای یادگیری داره، اما بنظرم شروع خوبی داشتیم و قطعا خودتون میتونید از اینجا به بعد رو تنهایی پیش برید.پیشنهاد میکنم حتما سری به سایت سیمفونی، سایت آموزش داکترین و توییگ بزنید تا باهاشون بیشتر آشنا بشید.با آرزوی موفقیت روزافزون برای شما</description>
                <category>پول ریکوئست</category>
                <author>آیین</author>
                <pubDate>Fri, 13 Jul 2018 15:10:58 +0430</pubDate>
            </item>
                    <item>
                <title>از سیر تا پیاز ری‌اکت، نکست و ریداکس!</title>
                <link>https://virgool.io/pullrequest/%D8%A7%D8%B2-%D8%B3%DB%8C%D8%B1-%D8%AA%D8%A7-%D9%BE%DB%8C%D8%A7%D8%B2-%D8%B1%DB%8C%D8%A7%DA%A9%D8%AA-%D9%86%DA%A9%D8%B3%D8%AA-%D9%88-%D8%B1%DB%8C%D8%AF%D8%A7%DA%A9%D8%B3-vkxtmkwxk1ts</link>
                <description>عمیق‌تر در مورد جاوااسکریپت، ری‌اکت و نکست‌جی‌اس بدونیمنکست‌جی‌اس و ریداکس‌تانک، از مبتدی تا پیشرفتهمقدمهخیلی از دوستان من با موضوعات مختلفی خصوصا در زمینه جاوا‌اسکریپت دچار مشکل شدن. بعضی از مفاهیم رو نفهمیدن و یا نمیدونستن از کجا باید شروع کنن. برای همین تصمیم گرفتم که این مطلب رو بنویسم. قصد دارم اینجا برای سطوح مبتدی تا پیشرفته در مورد ری‌اکت و نکست‌جی‌اس و همچنین ریداکس توضیح بدم. و سعی میکنم این مطلب رو تا حد امکان گسترده کنم تا تمامی مواردی که برای یک برنامه‌نویس خوب شدن لازم هست رو پوشش بدم.اما اساسا چرا این مطلب رو نوشتم؟ خب، اول از همه اینکه بتونم هر آنچه که بلد هستم رو یک‌جا نگه دارم و بعدا هم ازش استفاده کنم. دوم اینکه همچین آموزشی رو تقریبا هیچ‌کجای وب، و خصوصا تو سایت‌های ایرانی پیدا نکردم.متنی که اینجا نوشته شده، حاصل چند سال تلاش من برای یادگیری، و تقریبا یک ماه برای خلاصه‌کردن تمام اون اطلاعات بود تا بتونم یک مرجع کامل رو بسازم، سعی کردم خط به خط کدها رو توضیح بدم و ریپازیتوری گیت رو باهاش سینک و همگام نگه‌دارم. از اینکه این مطالب به دست دیگران هم برسه و کمک به چند نفر بکنه واقعا خوشحال میشم، اما تنها خواهش من از خوانندگان و شما، اینه که اگر این مطلب رو جایی به اشتراک میذارید، منبع اون رو هم ذکر کنید.همچنین مطلب کامل رو در سایت خودم میتونید پیدا کنید و در مورد ری‌اکت و نکست‌جی‌اس بیشتر مطالعه کنید.سپاس.جاوا‌اسکریپت و اکما‌اسکریپت (ECMAScript)اولین چیزی که لازم هست بدونید، اینه که جاوا‌اسکریپت توسط شرکت نِت‌اِسکیِپ (شرکت قدیمی موزیلا فایرفاکس) ساخته و به سازمان اِکما داده شد تا اون رو استانداردسازی کنن. اِکما سازمانیه که وظیفش استانداردسازی اطلاعاته.اونچه که در نهایت از جاوا‌اسکریپت توسط سازمان اِکما ارائه شد، اِکما‌اِسکریپت بود. ساده‌تر بگم، اِکما‌اِسکریپت یک استاندارد هست، در حالی که جاوا‌اسکریپت محبوب‌ترین پیاده‌سازی از اون استاندارد به حساب میاد. جاوا‌اِسکریپت، اِکما‌اِسکریپت رو پیاده‌سازی میکنه و بر پایه اون ساخته میشه.حالا سوالی که مطرح میشه، اینه که “ES” چیه؟ای‌اِس، مخفف اِکما‌اِسکریپته (ECMAScript)، هرجایی که ای‌اِس رو در کنار یک عدد دیدید، مثل ای‌اس۶، یادتون باشه که داره به یک نسخه از اِکما‌اِسکریپت یا در واقع یک نسخه از استاندارد اشاره میشه.ای‌اِس (ES)===========ای‌اِس تا به اینجا ۸ نسخه مختلف رو ارائه کرده، ای‌اِس۱، ۲، ۳ و ۴ بین سال‌های ۱۹۹۷ تا ۱۹۹۹ ارائه شدن و دیگه ازشون پشتیبانی نمیشه. (ما هم کاری بهشون نداریم).ای‌اِس۵ تقریبا ۱۰ سال بعد از اس‌اس۴ یعنی اواخر سال ۲۰۰۹ (تاریخ دقیق رو نمیدونم) ارائه داده شد.ای‌اِس۶ در سال ۲۰۱۵ منتشر شد که برای راحتی کار، بهش ای‌اس۲۰۱۵ هم میگن. در واقع چون ای‌اس۶ در سال ۲۰۱۵ ارائه شده، بهش ای‌اِس۲۰۱۵ میگن!ای‌اِس۷/ای‌اس۲۰۱۶ که مطمئنا میتونید پیشبینی کنید در سال ۲۰۱۶ منتشر شد.ای‌اس۸/ای‌اس۲۰۱۷ هم، در سال ۲۰۱۷ منتشر شد.چرا ورژن‌های مختلف از ای‌اس منتشر شدن؟دلیلش اینه که هر وِرژِن، ویژگی‌های جدیدی رو ارائه کرده که با زمان خودش هم‌خوانی داشته باشه. فقط باید چند نکته رو به خاطر داشته‌باشید:پیشبینی میشه که هر سال یک نسخه جدید از اِکما‌اِسکریپت ارائه بشه،اولین نسخه‌های اِکما‌اِسکریپت با عدد نسخه‌بندی میشدن، مثل ای‌اِس۱، ای‌اِس۲ و…نسخه‌های جدید که از سال ۲۰۱۵ شروع شدن، به شکل ای‌اس[سال انتشار] نام‌گذاری میشن.اکما یک استاندارده، جاوا‌اسکریپت محبوب‌ترین پیاده‌سازی از اون استاندارد هست.چرا ای‌اس۶؟نسخه ۶‌ام از اِکما‌اِسکریپت، ویژگی‌های خیلی خوبی رو به زبان جاوا‌اسکریپت اضافه کرد، و همین، یکی از دلایلی شد که ری‌اَکت به طور پیش‌فرض ازش پشتیبانی میکنه. مثلا مفاهیمی مثل «کلاس‌ها» و «ماژول‌ها»، که برای زبان‌های شئ‌گرا اجباری هستند، بهش اضافه شدن. از جمله ویژگی‌های دیگش، اضافه شدن دستورات for، جِنِراتور‌های شبیه به زبان پایتون، توابع فِلِشی (Arrow Function)، کالِکشِن‌ها، پرامِس‌ها (Promise) و غیره بوده.متاسفانه هنوز مرورگرها پشتیبانی از ای‌اِس۶ رو کامل نکردن و ای‌اِس۶ به خودی خود تو مرورگرها پشتیبانی نمیشه. اینجاست که مفاهیم تِرَنس‌پایلِرها (transpiler) خودشون رو نشون میدن که بعد از توضیح توابع فِلِشی (برای راحتی کار میگم اَرو فانکشِن) بهشون میپردازم.Arrow Functionsیکی از ویژگی‌های خیلی خوبی که توی ای‌اِس۶ وجود داره، استفاده از اَرو فانکشن‌هاست. به اسمش دقت نکنید، مفهومش خیلی سادست. ارو فانکشن‌ها، در واقع همون توابع قدیمی جاوااسکریپت هستند (با ویژگی‌های جدید‌تر که اینجا بهشون کاری نداریم) که تو ای‌اس۶ به شکل دیگه‌ای تعریف میشن و موقع تِرَنس‌پایل (یکمی پایینتر توضیح میدم در این مورد) به شکل توابع جاوااسکریپت درمیان، مثلا:x =&gt; {
 return x * x;
}

(m, n) =&gt; {
 return m+n
}

data =&gt; {
 data.json()
 .then(result =&gt; {
 return result;
  })
}بعد از تِرَنس‌پایل تبدیل میشن به:(function (x) {
 return x * x;
});

(function (m, n) {
 return m + n;
});

(function (data) {
 data.json().then(function (result) {
 return result;
  });
});ترنس‌پایلر (transpiler)حتما تا به حال واژه‌های «کامپایلِر» و «مفسر» به گوشتون خورده. تِرَنس‌پایلِرها در واقع نوعی کامپایلر هستند با یک تفاوت اصلی:کامپایلرها معمولا زبان رو به یک نسخه قابل اجرا برای ماشین تبدیل می‌کنن، مثلا زبان سی، کدهای باینری یا همون صفر و یک میسازه، یا جاوا بایت‌کد رو تولید میکنه.این درحالیه که ترنس‌پایلرها، یک سورس‌کد رو به یک سورس‌کد دیگه تبدیل می‌کنن (یا مثلا به یک زبان دیگه که مستقیم برای ماشین قابل درک نیست و باید دوباره کامپایل، اینتِرپرِت یا همون تفسیر بشه). مثلا کافی‌اسکریپت (CoffeeScript) که از خودش جاوا‌اسکریپت تولید میکنه، یا بابِل (Babel) که ای‌اس۶ رو به ای‌اس۵ (قابل پشتیبانی برای مرورگرها) تبدیل می‌کنه.بابِل یا بِیبِل (Babel) یک ترنس‌پایلر برای ای‌اس۶ هست که اون رو به ای‌اس۵ تبدیل میکنه. ای‌اس۵ توسط مرورگرها خیلی خوب پشتیبانی میشه و در حقیقت، ای‌اس۵ همون جاوا‌اسکریپتیه که عموما باهاش آشنایی دارن.بابِل، بِیبِل…؟ (Babel)بابل یک تِرَنس‌پایلِر برای جاوا‌اسکریپته. بابل رو اکثرا بخاطر توانایی خوبش تو تبدیل ای‌اس۶ به ای‌اس۵ میشناسن.به عنوان مثال این کد که با ای‌اس۶ نوشته شده:let input = \[1, 2, 3\];
console.log(input.map(item =&gt; item + 1)); // \[2, 3, 4\]توسط بابل تبدیل میشه به کد ای‌اس۵:var input = \[1, 2, 3\];
console.log(input.map(function (item) {
 return item + 1;
})); // \[2, 3, 4\]که تقریبا همه‌جا قابل اجراست. بابل خیلی خوب از پُلی‌فیل‌های جاوا‌اسکریپت پشتیبانی میکنه و باعث میشه که کد جاوا‌اسکریپت ساخته شده برای مرورگرهای قدیمی هم قابل پشتیبانی باشه. به همین خاطر، بابل باعث میشه شما از تمام ویژگی‌های ای‌اس۶ استفاده کنید بدون اینکه پشتیبانی از نسخه‌های قدیمی مرورگرها رو از دست بدید (مفهوم پُلی‌فیل اینجا معلوم میشه).فرانت‌اند و مفاهیم اِس‌پی‌اِی (SPA)اطلاعاتی که ما در اینترنت میبینیم، مجموعه‌ای از کدهای HTML، JavaScript و CSS هستن. قدیم‌ها، زمانی که خیلی از برنامه‌نویس‌ها از PHP (هنوز هم استفاده میکنن) برای نوشتن صفحات وب استفاده می‌کردن و جِی‌اِس مثل امروز محبوب نبود، هر صفحه از سایت باید بصورت جداگانه نوشته می‌شد. و زمانی که کاربر روی یک لینک کلیک میکرد، کل صفحه از اول رِندِر (Render) می‌شد. این فرایند برای کاربر، خسته‌کننده و طاقت‌فرسا بود و حتی گاهی بخاطر زمان زیادی که باید برای بارگذاری صفحه صرف میکرد، از ادامه کارش پشیمون میشد.کم‌کم تکنیک‌هایی مثل اِی‌جَکس (AJAX) استفاده شدن، که مثلا وقتی صفحه در حال بارگذاری بود، شروع میکرد یک نماد کوچک لودینگ و بارگذاری (بهش معمولا میگن اِسپینِر یا spinner) رو نشون دادن و کاربر رو متوجه میکرد که اطلاعات در حال ارسال و دریافتن.این ایده، که اطلاعات تو یک صفحه بارگذاری بشن و کاربر مدام مجبور به عوض کردن صفحه‌ها نشه (مگر در مواقع لازم) باعث ایجاد تعریف جدیدی از وب‌اَپلیکِیشِن‌ها شد، اِس‌پی‌اِی (SPA) یا Single Page Application راهش رو به لغت‌نامه‌ی برنامه‌نویس‌ها باز کرد!SPA هدفش ایجاد یک تجربه کاربری بهتر برای کاربرهاست. در حقیقت، SPA کار رو برای برنامه‌نویس سخت‌تر و برای کاربر راحتتر کرد. تو یک اپلیکیشن SPA ایده‌آل، کاربر شما متوجه عوض شدن صفحه‌ها نمیشه و احساس میکنه که همه‌چیز در حال رخ‌دادن تو یک صفحست.اما داستان سمت برنامه‌نویس کمی فرق می‌کنه. کاربر زمانی میتونه اطلاعات رو تو مرورگرش ببینه، که کد‌های HTML ساخته شده باشن، و این یعنی برنامه‌نویس باید با هر درخواست کاربر، یا کلا صفحه رو از اول بارگذاری کنه (که دیگه SPA نیست) یا اینکه اون قسمتی از سایت رو که مرتبط با کاربر هست یا نیاز به تغییر داره رو مجددا با داده‌های جدید که از سمت سرور اومدن بارگذاری کنه.اینجاست که سایت شما، تا حدود زیادی از سمت سرور خودش جدا میشه و مفهوم جدیدی به عنوان فرانت‌اِند (Front End) رو تعریف می‌کنه. فرانت‌اند و بَک‌اِند، در زمان‌های دور وجود نداشتن یا خیلی به هم نزدیک بودن. شما یک سایت رو کامل می‌نوشتید و مثلا تو یک صفحه PHP، همزمان از HTML و JavaScript استفاده می‌کردید. هر زمان که کاربر درخواست صفحه‌ی جدیدی رو میداد، شما یک اسکریپت جدید رو بارگذاری و صفحه‌های مربوطه رو از نو بازنویسی می‌کردید.زمانی که تعاریف فرانت‌اند و بَک‌اند ایجاد شدند، طراحی سایت شکل جدیدی به خودش گرفت. فرانت‌اند یجورایی بیشتر نماد طراحی سایت شد و بَک‌اند بیشتر نماد منطق کاری (Business Logic). کدها سمت بک‌اند نوشته می‌شن و اطلاعات رو موقع نیاز به فرانت‌اند ارسال می‌کنن. از اینجا به بعد، برنامه‌نویس فرانت نیازی نداره که نگران SQL و دستورات مربوط به ارتباط با پایگاه داده و دریافت محصولات از اون باشه، یا حتی نگران فرایند عضویت و ورود به سایت. فرانت‌اند خودش رو بیشتر با بهبود تجربه کاربری درگیر کرد. لازم هست که بگم، این یک مفهوم کلی و جداسازی کلی بک‌اند و فرانت‌اند هست. اینکه وظیفه‌ی برنامه‌نویس چی باشه، نسبت به هر پروژه قابل تغییر هست و امرو، خیلی از برنامه‌نویس‌ها به هرد شاخه بَک و فرانت تسلط دارن.اینجا بود که مارس ۲۰۱۳، فیسبوک اولین نسخه از کتاب‌خونه ری‌اَکت رو ارائه داد…ری‌اکت (React)ری‌اَکت یا ری‌اَکت‌جِی‌اِس، یک کتابخونست برای ساختن روابط کاربری. ری‌اکت به برنامه‌نویس این اجازه رو میده، که بتونه وب‌اپلیکیشن‌های بزرگی رو بسازه که از اطلاعات مختلفی استفاده می‌کنن و میتونن تو یک صفحه تغییر کنن، بدون اینکه صفحه رو مجدد لود کنن (مفهوم SPA). هدف اصلی سازندگان ری‌اکت، سرعت، سادگی و مقیاس‌پذیری بوده.ری‌اکت صرفا تلاش میکنه تا رابط کاربری رو از راه Virtual-DOM تغییر بده. اینکه این ساز.کار به چه صورتی هست رو فعلا تشریح نمیکنم اما، اگر با معماری MVC آشنا باشید، ری‌اکت اون قسمت “V” رو به خودش اختصاص میده و میتونه با بقیه کتابخونه‌های جی‌اس خودش رو وفق بده.یکی دیگه از اَشکال ری‌اکت، ری‌اَکت‌نِیتیو هست که کمک میکنه، نرم‌افزارهای (اکثرا موبایل) نِیتیو یا سازگار با سیستم‌عامل توسط ری‌اکت ساخته بشن. ضمنا ری‌اکت از جی‌اس‌اکس (JSX) برای نمایش و ساخت المان‌ها استفاده میکنه که توضیح میدم در این مورد.جی‌اس‌اکس (JSX)جِی‌اِس‌اِکس، یک زبان مشابه با HTML یا XML که کمک میکنه، تیکه‌های سایت (Component) جدای از هم ساخته بشن و به شکل یک شئ جاوا‌اسکریپت در بیان.ساده‌تر بگم، با JSX میشه ساختارهای مشابه به HTML رو ساخت. مثال:var nav = (
    &lt;ul id=&quot;nav&quot;&gt;
      &lt;li&gt;&lt;a href=&quot;#&quot;&gt;Home&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#&quot;&gt;About&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#&quot;&gt;Clients&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#&quot;&gt;Contact Us&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
);اینجا، nav به عنوان یک کامپونِنت (Component) شناخته میشه. و شما میتونید جاهای مختلف برنامه، ازش استفاده کنید (یکبار بنویسید و چندبار استفاده کنید).این استفاده از JSX یکی دیگه از ویژگی‌های ری‌اکت به حساب میاد! اینکه شما برنامتون رو به تیکه‌های کوچکتری تقسیم می‌کنید و میتونید در جاهای مختلفی ازش استفاده کنید.کمی بیشتر در مورد ری‌اکتدنیای ری‌اکت جالبه. فرض کنید که یک کامپوننت رو ساختیم، مثلا یه لیست از محصولات. بعد کاربر روی یکی از محصولات کلیک میکنه، صفحه محصول باز میشه و کاربر محصول رو میبینه. کاری که ری‌اکت انجام داده چیه؟ ری‌اکت متوجه میشه که کدوم قسمت‌های سایت باید تغییر کنن و فقط اونها رو تغییر میده! پس اول شناسایی میکنه که چه چیزی تغییر کرده، و بعد تغییرات رو اعمال میکنه. ری‌اکت، به جای اینکه کل صفحه رو از اول بازسازی کنه، فقط قسمت‌هایی که تغییر کردند یا به نوعی وضعیتشون تغییر کرده رو با داده‌های جدید پر می‌کنه و اونها رو از نو میسازه و خیلی هم سریع این کار رو انجام میده!نصب و شروع کار با ری‌اکتتصمیم ندارم اینجا به طور عمیق وارد ری‌اکت بشم و کد منبع ری‌اکت رو تشریح کنم، هدفم اینه که فضای کلی برای ورود به نکست‌جی‌اس رو ایجاد و شما رو با ساختار کلی و نحوه کار با ری‌اکت آشنا کنم. پس پیش میریم برای نصب ری‌اکت‌جی‌اس و یک آشنایی کلی با این کتابخونه.نصاب ری‌اکتنصاب ری‌اکت، که توسط فیسبوک ساخته شده، تو مخازن npm موجوده و میتونید اون رو به صورت عمومی توی سیستم‌عاملتو بریزید:$ npm install -g create-react-app

\# ساخت نرم‌افزار
$ create-react-app my-app-name

\# نصب وابسته‌ها (Dependencies)
$ cd my-app-name
$ npm installمطمئن باشید که حتما npm رو روی کامپیوترتون نصب کردید. یکی از مهم‌ترین ویژگی‌های create-react-app اینه که تمام ابزارهای لازم رو از قبل برای شما فراهم کرده. مثلا از قبل Babel برای شما نصب شده و نیازی نیست که خودتون رو درگیر نصب و راه‌اندازیش بکنید. حالا میریم سراغ یک توضیح در مورد ساختار این کتابخونه.ساختار پوشه‌هااگر داخل پوشه public رو ببینید، متوجه حضور فایل index.html میشید. این فایل در حقیقت نقطه شروع برنامست و حتما باید وجود داشته باشه، این یکی از بایدهای برنامه‌های ساخته شده با create-react-appهست. یه نگاهی به داخل این فایل میندازیم:&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot;&gt;
 &lt;!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    --&gt;
    &lt;link rel=&quot;manifest&quot; href=&quot;%PUBLIC_URL%/manifest.json&quot;&gt;
    &lt;link rel=&quot;shortcut icon&quot; href=&quot;%PUBLIC_URL%/favicon.ico&quot;&gt;
 &lt;!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the \`public\` folder during the build.
      Only files inside the \`public\` folder can be referenced from the HTML.

      Unlike &quot;/favicon.ico&quot; or &quot;favicon.ico&quot;, &quot;%PUBLIC_URL%/favicon.ico&quot; will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running \`npm run build\`.
    --&gt;
    &lt;title&gt;React App&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;noscript&gt;
      You need to enable JavaScript to run this app.
    &lt;/noscript&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
 &lt;!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the &lt;body&gt; tag.

      To begin the development, run \`npm start\` or \`yarn start\`.
      To create a production bundle, use \`npm run build\` or \`yarn build\`.
    --&gt;
  &lt;/body&gt;
&lt;/html&gt;همینطور که میبینید، این فایل چیز خاصی رو داخل خودش نداره. به جز خط ۲۸ام. که اِلِمانی تعریف شده با آی‌دیroot، این رو تا اینجا توی ذهنتون داشته باشید. اتفاقی که از اینجا به بعد رخ میده، توی فایل src/index.js قرار داره. بذارید یه نگاهی هم به این فایل بندازیم:import React from &#039;react&#039;;
import ReactDOM from &#039;react-dom&#039;;
import &#039;./index.css&#039;;
import App from &#039;./App&#039;;
import registerServiceWorker from &#039;./registerServiceWorker&#039;;

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#039;root&#039;));
registerServiceWorker();این فایل هم یکی از فایل‌هایی هست که حتما باید وجود داشته باشه. دلیلش اینه که تو تتظیمات create-react-app این دو فایل به عنوان نقاط شروع برنامه تعریف شدند. خود create-react-app این دو فایل رو به هم متصل میکنه و زمانی رو که شما لازم هست خودتون بذارید تا تنظیمات رو انجام بدید، براتون ذخیره می‌کنه. میریم سراغ بررسی این فایل:اول از همه، این کد با استاندارد ای‌اس۶ نوشته شده. اگر قرار بود از استاندارد ای‌اس۵ (یا به اصطلاح Common JS) پیروی کنیم، باید مینوشتیم: var React = require(&quot;react&quot;); تا کلاس ری‌اکت رو به پروژه اضافه کنیم. بعدا به این خواهیم پرداخت که این خط چه کارهایی انجام میده. فعلا بریم سراغ خط بعد.تو خط دوم، شئیی به نام ReactDOM فراخوانی شده. برای اطلاعاتون، قبل از نسخه ۰.۱۴ ری‌اکت، کتابخونه‌های react و react-dom یکی بودن. تنها وظیفه‌ای که ReactDOM به عهده داره، اینه که با اِی‌پی‌آی‌های render یا ReactDOM.render یک اِلِمان جِی‌اِس‌اِکس رو، داخل یک اِلِمان دیگه (اینجا document.getElementById(&#x27;root&#x27;)) نمایش بده.خط سوم، خیلی ساده اِستایل‌های موجود در فایل index.css رو، برای تمام اِلِمان‌هایی که اینجا قرار هست رِندِر بشن، اعمال میکنه.خط چهارم یک کامپوننت رو، اینجا به اسم app، از یک کلاس ری‌اکت فراخوانی کرد و بعد تو خط هفتم، به ReactDOM گفت که این کامپوننت رو داخل اِلِمان root رِندِر کنه. اما بریم سراغ کلاس ری‌اکت و ببینیم اصلا حرفش چی هست.import React, { Component } from &#039;react&#039;;
import logo from &#039;./logo.svg&#039;;
import &#039;./App.css&#039;;

class App extends Component {
 render() {
 return (
      &lt;div className=&quot;App&quot;&gt;
        &lt;header className=&quot;App-header&quot;&gt;
          &lt;img src={logo} className=&quot;App-logo&quot; alt=&quot;logo&quot; /&gt;
          &lt;h1 className=&quot;App-title&quot;&gt;Welcome to React&lt;/h1&gt;
        &lt;/header&gt;
        &lt;p className=&quot;App-intro&quot;&gt;
          To get started, edit &lt;code&gt;src/App.js&lt;/code&gt; and save to reload.
        &lt;/p&gt;
      &lt;/div&gt;
 );
  }
}

export default App;خب، طبق معمول خط اول، کلاس‌های ری‌اکت (React) و کامپوننت (Component) رو از کتابخونه ری‌اکت فراخوانی کرده. اما تفاوت براکت‌ها {} برای کامپوننت و ری‌اکت که براکت نداره چیه؟ این یکی از ویژگی‌های ای‌اس۶ هست که به این صورته:فرض کنید کلاسی رو به شکل زیر تعریف کردیم:class YeClassJadid1 {/*...*/}

export class YeClassJadid2 {/*...*/}

export default class YeClassJadid3 {/*...*/}

export default class YeClassJadid4 extends YeClassDige {/*...*/}اشیائی که تعریف شدن همشون ویژگی کلاس بودن رو دارن، اما تفاوتشون به این صورته که، کلاس اول (YeClassJadid1)، فقط تو همون فایلی که تعریف شده، یا اسکوپی (Scope) که تعریف شده قابل خونده شدنه و جاهای دیگه قابل استفاده نیست. کلاس دوم (YeClassJadid2) رو شما میتونید جاهای دیگه، با استفاده از روش import { YeClassJadid2 } from &quot;path/to/the/file&quot; فراخوانی کنید. دلیلش هم اینه که کلاس دوم، به طور پیش‌فرض کلاس اصلی این ماژول یا فایل نیست. اما، کلاس سوم YeClassJadid3 به طور پیش‌فرض، کلاس اصلی تعریف شده (default) و زمانی که بخواد فراخوانی بشه، میتونه به صورت import YeClassJadid3 from &quot;path/to/the/file&quot; یا حتی import YeEsmeDige from &quot;path/to/the/file&quot; فراخوانی بشه. و در نهایت هم، کلاس چهارم YeClassJadid4 مثل کلاس‌های قبلی، اما با ارث‌بری از کلاس YeClassDige خارج یا اِکسپورت میشه.نکته‌ای که گفتنش حائز اهمیته، اینه که هر ماژول، یا هر فایل، فقط یک کلاس رو میتونه به صورت default خارج کنه! تو مثال بالا، یکی از کلاس‌های YeClassJadid3 یا YeClassJadid4 میتونن خاصیت default رو داشته باشن!باز هم میگم، هدفم این نیست که عمیق وارد ری‌اکت بشم و فقط میخوام شما رو با این محیط آشنا کنم. برنامه اینه که شما وارد نِکست‌جِی‌اِس بشید و من اونجا بیشتر درمورد خود ری‌اکت می‌نویسم. تا اینجا هم خوب پیش اومدیم، بریم سراغ کامپوننت‌ها و یه کامپوننت بسازیم.ساخت کامپوننتبرای اینکه بهتر متوجه بشید کامپوننت‌ها چی هستند، صفحات وب رو به تیکه‌های کوچیک تقسیم کنید. مثلا نوار بالای صفحه یه کامپوننت جدا، لیست محصولات یه کامپوننت جدا و همینطور ادامه بدید…اول، کل صفحه یک کامپوننت هست، بعد هر تیکه‌ای از صفحه تقسیم به کامپوننت‌های کوچک‌تر میشه و پیش میره. این موضوع رو با ساخت یک کامپوننت راحتت درک میکنید. برای شروع، داخل پوشه src یک پوشه دیگه به اسم components یا هر اسم دیگه‌ای که دوست دارید ایجاد کنید. اینکه پوشه‌ها چطور باشن، دست خودتونه، اما یادتون باشه که یک‌سری استاندارد یا کانوِنشِن (Convention) برای اینکار هست که مدیریت کد رو راحتتر میکنه. در نهایت داخل این پوشه، یک فایل به اسم MyComponent.js بسازید و داخلش این کد رو قرار بدید:import React from &quot;react&quot;;

export default class ThisIsAComponent extends React.Component {
    render() {
         return (
            &lt;ul&gt;
                &lt;li&gt;Item 1&lt;/li&gt;
                &lt;li&gt;Item 2&lt;/li&gt;
                &lt;li&gt;Item 3&lt;/li&gt;
                &lt;li&gt;Item 4&lt;/li&gt;
                &lt;li&gt;Item 5&lt;/li&gt;
            &lt;/ul&gt;
         );
    }
}کامپوننتی که ساختیم خیلی سادست، یه لیست با ۵تا آیتم. که باید، حتما داخل تابع render قرار بگیرن، و این تابع هم، فقط باید یک اِلِمان رو، یا چند اِلِمانی که داخل یک المان والد جمع شدند رو برگردونه. حالا باید این کامپوننت رو داخل App.js فراخوانی کنیم. کار خیلی ساده‌ایه. App.js رو باز کنید و اون رو به شکل زیر تغییر بدید:import React, { Component } from &#039;react&#039;;
import logo from &#039;./logo.svg&#039;;
import &#039;./App.css&#039;;
import ThisIsAComponent from &quot;./components/MyComponent&quot;

class App extends Component {
 render() {
 return (
      &lt;div className=&quot;App&quot;&gt;
        &lt;header className=&quot;App-header&quot;&gt;
          &lt;img src={logo} className=&quot;App-logo&quot; alt=&quot;logo&quot; /&gt;
          &lt;h1 className=&quot;App-title&quot;&gt;Welcome to React&lt;/h1&gt;
        &lt;/header&gt;
        &lt;p className=&quot;App-intro&quot;&gt;
          To get started, edit &lt;code&gt;src/App.js&lt;/code&gt; and save to reload.
        &lt;/p&gt;
 
        &lt;ThisIsAComponent /&gt;
      &lt;/div&gt;
 );
  }
}

export default App;خب، کامپوننت رو ساختیم و اون رو به کامپوننت اصلی اضافه کردیم. حالا دستور npm start رو اجرا کنید و بعد داخل مرورگر به آدرس localhost:3000 برید و نتیجه رو ببینید. بعد از اینکه تموم شد، میتونید سرور رو ببندید و برید سراغ مرحله بعد.گریزی به stateفرض کنید که قرار بود دکمه‌ای وجود داشته باشه، تا با کلیک کردن روش، کامپوننت ما یا حتی یک بخشی از کامپوننت نمایش داده بشه و یا مخفی بشه، یکی از این دو حالت ساده! اینجا لازم هست که کمی در مورد مفهوم استِیت (state) توضیح بدم. کامپوننت‌های شما، همینطور که تا اینجا دیدید، کار خاصی رو به خودی خودشون انجام نمیدن، چون در حقیقت اِلِمانهای HTML هستند، یا حتی شاید لازم باشه در طول زمان تغییراتی رو پیدا کنن. ملموس‌ترین نوع تغییر، زمانی رخ میده که شما اطلاعاتی رو از جایی (از یک سرور) دریافت میکنید و می‌خواید به کاربر نشون بدید، اما تو مدت زمانی که اطلاعات در حال دریافت هستند، علاقه دارید تا یک اسپینر (spinner) رو نمایش بدید، تا به کاربر بگید که اطلاعات در حال بارگزاری هستن. یه سناریوی دیگه اینکه، با کلیک روی یک چِک‌باکس (checkbox) یک فیلد مخفی شده رو نمایش بدید و مثال‌های دیگه. اینجاست که استیت به کمک شما میاد.استیت در حقیقت محل ذخیره‌ي آخرین تغییرات و حالات کامپوننته. مثلا شما بهش میگید که کامپوننت من در حالت عادی، نمایش داده میشه، اما من میخوام، زمانی که کاربر روی یک دکمه کلیک میکنه، کامپوننتم رو مخفی کنم. اینجا، نمایش داده شدن یا مخفی شدن، جز حالات یا استیت کامپوننت به حساب میاد. ری‌اکت، زمانی که استیت تغییر میکنه، کامپوننت رو مجدد بارگزاری یا رِندِر میکنه.برای اینکه با استیت هم کار کنیم و بهتر درکش کنیم، به تریتیب زیر، به کامپوننت ThisIsAComponent استیت میدیم،حالت دیفالت یا اولیه رو برای کامپوننت تعریف میکنیمتوسط یک ایونت (Event) حالت یا همون استیت رو تغییر میدیم.کامپوننت رو به شکل زیر بازنویسی میکنیم:import React from &quot;react&quot;;

export default class ThisIsAComponent extends React.Component {
 constructor() {
 super()

 this.state = {
            isHidden: false
        }
    }

 toggleState() {
 this.setState({
            isHidden: !this.state.isHidden
        })
    }

 render() {
 return (
            &lt;div&gt;
                &lt;button onClick={this.toggleState.bind(this)}&gt;Change&lt;/button&gt;
                &lt;ul hidden={this.state.isHidden}&gt;
                    &lt;li&gt;Item 1&lt;/li&gt;
                    &lt;li&gt;Item 2&lt;/li&gt;
                    &lt;li&gt;Item 3&lt;/li&gt;
                    &lt;li&gt;Item 4&lt;/li&gt;
                    &lt;li&gt;Item 5&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;
 );
    }
}کارهایی که انجام شده، به همراه توضیحاتشون به ترتیب زیر هستن:constructor به کامپوننت اضافه شده. این تابع که در حقیقت از تعاریف کلاس در جاوا‌اسکریپتبرگرفته شده، کارهای متفاوتی رو میتونه انجام بده. اما یادتون باشه، هر موقع که میدونستید کامپوننتتون قرار هست حالات مختلفی رو برای نمایش داشته باشه، حالت اولیش (Initial State) رو اینجا تعریف کنید. مهمترین نکته اینه که به محض تعریف کردن constructor، تابع super() فراخوانی بشه. اگر فراموش کنید که super() رو بلافاصله فراخوانی کنید، this که در حقیقت همون کامپوننت شماست و اطلاعات کامپوننت رو توی خودش داره، خالی خواهد موند و در نتیجه امکان استفاده از اِستِیت و بقیه ویژگی کامپوننت‌ها رو نخواهید داشت.قدم بعدی، داخل construct و زیر super()، استیت اولیه یا همون Default رو تعریف کردم. شکل تعریفش هم یه شئ ساده بوده که داخلش فقط از isHidden: false استفاده کردم تا بگم، در حالت عادی نمایش داده نمیشه (اینکه چطور و چه چیزی از این حالت استفاده میکنه و تغییر میکنه رو پایین‌تر توضیح میدم)یک تابعی رو تعریف کردم با اسم دلخواه toggleState که قرار هست موقع کلیک کردن دکمه، فراخوانی بشه. کاری هم که انجام میده، خیلی ساده، استیت رو تغییر میده. کد نوشته شده شاید شما رو یکم سردرگم کنه. تعریف کد به این شکله: isHidden: !this.state.isHidden و یعنی، مقدار جدید isHidden برابر خواهد بود با هر آنچه که this.state.isHidden بوده، اما چون یک ! هم اولش آوردم، یعنی اون مقدار رو بر عکسش کن. پس اگر this.state.isHidden برابر با false بود، مقدار جدیدش برابر با true میشه و برعکس.چون یک دکمه هم به کامپوننت اضافه کردم، باید کل اِلِمان‌ها رو داخل یک اِلِمان اصلی و والد جا بدم. برای همین هم، تمام المان‌ها رو داخل یک div گذاشتم.زمانی که کاربر روی دکمه کلیک میکنه، رویداد (Event) onClick اتفاق میوفته. اینجا بهش گفتم، زمانی که این رویداد اتفاق افتاد، تابع toggleState رو صدا بزنه. ضمنا از bind هم استفاده کردم، چون دکمه‌ها در حالت عادی، رویدادها رو انجام نمیدن و بایند (Bind) موظف هست تا حالت اصلی شئی که بهش پاس داده میشه رو حفظ و برای تابع مربوطه ارسالش کنه. در این مورد بعدا بیشتر توضیح میدم.و در نهایت، به المان ul گفتم، تا مخفی بودن یا نبودنش رو از this.state.isHidden بگیره. حالا، هر موقع که isHidden تغییر کنه، المان ul مجددا رِندِر میشه.برنامه رو تست کنید و ببینید که کارکردش چطوره. کارتون که تموم شد، میریم تا یکم دیگه با تئوری دست و پنجه نرم کنیم.درک مفاهیم ری‌اکتمهم‌ترین تفاوت کتاب‌خونه ری‌اکت با فرِیم‌وُرک‌هایی مثل انگولار، اینه که ری‌اکت فقط برای فرانت‌اند ساخته شده. در مورد ری‌اکت، چیزهای خیلی زیادی برای گفتن وجود داره. اما دوتا از ویژگی‌های اصلی ری‌اکت، داشتن مفاهیمی مثل Properties یا به طور خلاصه Prop و همچنین State هست که قبل‌تر کمی با مفهوم State آشنا شدیم. اینجا تصمیم دارم در مورد این دو و همچنین چرخه زندگی کامپوننت‌ها بیشتر توضیح بدم.Propsپراپ‌ها در واقع راهی برای ارتباط بین کامپوننت‌ها و جز ویژگی‌های اونها هستند (هرجا که شما کلاس React.Component رو استفاده یا همون extend کنید، میتونید ازشون بهره‌ ببرید). مهم‌ترین ویژگی پراپ‌ها، اینه که از سمت کامپوننت والد به فرزند منتقل میشن و اصطلاحا uni-directional (یک‌طرفه) هستن. زمانی هم وجود داره که شما مثل استِیت، پراپ‌های اولیه رو تعریف میکنید تا کامپوننت شما با اطلاعات اولیه (و نه حالات اولیه)، شروع به کار کنه. پراپ‌ها به شما کمک میکنن تا اطلاعات رو بین کامپوننت‌ها جابجا کنید، و زمانی که کاربر بخواد مستقیما روی خود کامپوننت تغییری اعمال کنه، باید از استیت استفاده بشه. بذارید با یک مثال ساده از پراپ استفاده کنیم:فرض کنید که توی کامپوننت ThisIsAComponent از یک تگ هدر h1 استفاده میکردیم و قرار بود مقدار داخلش رو توسط کامپوننت والد تغییر بدیم. کد جدیدمون به این شکل میشد (فقط تابع رِندِر رو گذاشتم و مابقی کد دست‌نخورده باقی مونده):render() {
 return (
            &lt;div&gt;
                &lt;h1&gt;{this.props.title}&lt;/h1&gt;
                &lt;button onClick={this.toggleState.bind(this)}&gt;Change&lt;/button&gt;
                &lt;ul hidden={this.state.isHidden}&gt;
                    &lt;li&gt;Item 1&lt;/li&gt;
                    &lt;li&gt;Item 2&lt;/li&gt;
                    &lt;li&gt;Item 3&lt;/li&gt;
                    &lt;li&gt;Item 4&lt;/li&gt;
                    &lt;li&gt;Item 5&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;
 );
    }حالا باید مقداری رو برای title از کامپوننت والد، یعنی App در نظر بگیرم. کار آسونیه و به شکل زیر تغییری رو تو App.js ایجاد میکنم:...
&lt;ThisIsAComponent title={&quot;This is a new title&quot;}/&gt;
...Stateپراپ‌ها نباید تغییر کنن (به اصطلاح باید Immutable باشن)، برای همین از استیت استفاده میشه. در حالت عادی، کامپوننت‌ها استیت ندارن و از این جهت اصطلاحا بهشون Stateless گفته میشه. کامپوننت‌هایی که استیت پیدا میکنن، بهشون Stateful میگن.کاربرد استیت برای اینه که کامپوننت بتونه اطلاعاتی که در هر بازسازی دریافت میکنه رو حفظ کنه. زمانی که شما از this.setState() استفاده میکنید، وضعیت کامپوننت بروزرسانی و مجددا بازسازی میشه. تمام این فرایند بازسازی توسط ری‌اکت اتفاق می‌افته و خیلی هم سریعه.پراپ و استیت خیلی شبیه به هم هستند و تقریبا کار مشابهی رو انجام میدن، اما برای کارهای متفاوتی ازشون استفاده میشه. این امکام وجود داره تا خیلی از کامپوننت‌های شما Stateless باشن.چرخه زندگی کامپوننت‌ها (Component Lifecycle)کامپوننت‌ها به شما کمک میکنن تا یو‌آی (UI) رو به تیکه‌های کوچکتر تقسیم کنید. در حالت کلی، شما کامپوننت‌ها رو به شکل کلاس‌های جاوااسکریپت تعریف میکنید:class Greeting extends React.Component {
 render() {
 return &lt;h1&gt;Hello, {this.props.name}&lt;/h1&gt;;
  }
}اما کامپوننت‌ها صرفا جهت نمایش ساخته نمیشن و کارهای بیشتری میشه باهاشون انجام داد. اصطلاحا هر کامپوننتی برای خودش یک چرخه‌زندگی بخصوص یا Lifecycle داره که چندتا از پر کاربردترین‌ها رو توضیح میدم:constructor() که دقیقا قبل از بارگذاری کامپوننت توسط ری‌اکت خونده میشه. بهترین استفادش، تعریف state اولیه کامپوننت هست. اگر کامپوننت stateless باشه، نیازی به تعریف این تابع نیست.componentWillMount() دقیقا قبل از بارگذاری خونده میشه. و قبل از تابع render() اتفاق میوفته. به همین خاطر تعریف استیت تو این تابع پیشنهاد نمیشه. این تابع سمت سرور کارهاش رو انجام میده و اصطلاحا server-side هست. (در این مورد تو بخش Isomorphism توضیح میدم)componentDidMount() بعد از اینکه کامپوننت بارگذاری شد، خونده میشه. این تابع بهترین جا برای ارسال درخواست‌ها به سرور شماست و اگر استیت رو تو این تابع با استفاده از تابع this.setState() تغییر بدید، باعث میشید که باز دوباره تابع render() فراخوانی بشهcomponentWillReceiveProps(nextProps) زمانی فراخوانی میشه، که شما از طریق کامپوننت والد، پراپ‌های کامپوننت فرزند رو تغییر بدید و بخواید استیت جدید رو بر اساس پراپ‌های جدید تنظیم کنید. برای اینکار میتونید آرگومان‌های nextProps و this.props رو با هم مقایسه کرده و تغییرات رو ایجاد کنید. (مقایسه رو حتما انجام بدید!)componentWillUpdate(nextProps, nextState) قبل از بارگذاری مجدد رخ میده، اگر پراپ‌ها و استیت‌های کامپوننت تغییری کرده باشن.componentDidUpdate(prevProps, prevState) بلافاصله بعد از بارگذاری مجدد اتفاق میوفته و به شما این امکان رو میده تا المان‌ها رو دستکاری کنید.componentDidCatch(error, info) برای مدیریت اِرورها و خطاها تو UI استفاده میشه.مطالعه بیشتر…جمع‌بندیپراپها استفاده میشن تا اطلاعات از کامپوننت والد به کامپوننت فرزند یا حتی داخل کامپوننت فرزند منتقل بشن. پراپ‌ها تغییرناپذیر یا به اصطلاح Immutable هستند و نباید در هر رندر تغییر کنند.استیت برای این استفاده میشه تا اطلاعات رو تغییر بدیم، یا اطلاعات تغییر یافته رو نمایش بدیم و اصطلاحا تغییر پذیر یا Mutable هستند. مثلا کاربر چیزی رو در سایت سرچ میکنه و بلافاصله زیرش تعدادی از نزدیک‌ترین نتایج جست‌وجو نمایش داده میشه.مفهوم Isomorphic یا Universalزمانهای قدیم، قبل از موقعی که NodeJS بین برنامه‌نویس‌ها محبوب بشه، جاوااسکریپت زبانی بود که باهاش تغییراتی رو تو صفحه‌های وب ایجاد میکردن تا صفحه‌ها از حالت مرده و استاتیک خارج بشن. تا اون موقع زبان جاوااسکریپت، زبانی بود که تو مرورگر کاربر اجرا میشد و به اصطلاح Client Side یا سمت کاربر بود. بعد از ظهور NodeJS جاوااسکریپت این قابلیت رو پیدا کرد که تو محیط سمت سرور هم اجرا بشه و بعد از اون جاوااسکریپت به یک زبان Server Side هم تبدیل شد.ایزومورفیزم (Isomorphism) از ریاضیات گرفته شده و «هم‌سان» معنی میشه. چون واژه ایزومورفیک برای برنامه‌نویس‌ها کمی مشکل‌ساز میتونه باشه، به جاش از واژه یونیورسال (Universal) هم استفاده میکنن.ایزوموفیک در شاخه وب، به معنی ساخته شدن صفحه سمت سرور یا سمت کاربر هست و در حالت کلی به NodeJS و خود JavaScript اشاره میکنه. برای اینکه تعریف رو ساده‌تر کنم، اینطور در نظر بگیرید که، کدی که سمت سرور ساخته شده، به صورت HTML به کاربر ارسال میشه و کاربر میتونه تو کد صفحهش اون رو ببینه. اما کدی که سمت کاربر ساخته میشه، تو کدهای صفحه قابل مشاهده نیست. اینجا لازم هست که به این نکته اشاره کنم، که منظور از سرور، الزاما بک‌اند نیست، و اونچه که مد نظر هست، ابزاریه که وظیفه تبادل ارتباطات رو به عهده داره.دلایل مختلفی وجود دارن که برنامه‌نویس‌ها به اپ‌های ایزومورف علاقه‌مندند:بهبود سئو،پرفورمنس بهتر،و نگهداری راحت‌تر.یکی از مضوعاتی که خیلی در این مورد مهم هست، اینه که کد‌های ایزومورفیک که سمت کاربر ساخته شدن، توسط موتورهای جست‌وجو به خوبی خونده نمیشن، برای همین هم اپ‌های SPA معمولا با این ساختار دچار مشکل میشن و لازم هست تا کدشون سمت سرور ساخته بشه. (سرور رو الزاما با کامپیوتر سرور اشتباه نگیرید!)Promise و درک ناهم‌گام‌سازی (Asynchronous)بیاید با هم یه دنیای جالبی رو تجسم کنیم، تو این دنیای ما، هنوز گوگل وجود نداره و شما رئیس یک شرکت «پاسخ به سوالات» هستید. نحوه کار به این شکله که کاربر سوال خودش رو بسته‌بندی میکنه (Data Package) و اون رو به یک پست‌چی میده، پست‌چی این بسته رو میاره برای شرکت شما (Request) و شما بسته رو باز میکنید، به سوال جواب میدید و اون رو به پست‌چی میدید و ایشون هم برای کاربر جواب رو میبره (Response). پروسه تا اینجا واضحه فقط چند شرط وجود داره:از اونجایی که شرکت ما خیلی خاصه :دی،کاربر زمانی که بسته رو به پست‌چی میده، باید دم در خونه صبر کنه تا پست‌چی برگرده (و از کار و زندگیش هم می‌افته)پستچی تا زمانی که شما پاسخ رو بسته‌بندی نکردید و بهش تحویل ندادید پیش شما میمونه.این روش ارتباط، روش هم‌گام یا اصطلاحا Synchronous نام داره و همون روش قدیمیه کار با اینترنته، کد PHP مینوشتیم و با هربار کلیک رو دکمه، کاربر باید منتظر صفحه جدید میموند و نمیتونست کارهای دیگه توی سایت رو انجام بده.حالا شرکتمون رو کمی پیشرفته‌تر میکنیم، فرایند همون شکل قبلی رو داره اما:زمانی که کاربر درخواستش رو بسته‌بندی کرد و به پست‌چی داد، برمی‌گرده خونش و کارهاش رو انجام میدهشما فقط یک پست‌چی ندارید و پست‌چی‌ها میتونن از کاربر درخواست‌های مختلفی رو بگیرن و برای شما بیارناین نوع ارتباط، ارتباط نا‌هم‌گام یا Asynchronous نام داره و خیلی تو وب‌اپ‌های SPA پر کاربرده و AJAX یکی از معروف‌ترین تکنیک‌ها برای برقراری این نوع ارتباطهاست. تو نسخه‌های جدید جی‌اس، برای اینکه کاربر رو منتظر نذاریم بعد از اینکه درخواستش رو ارسال کرد، بهش قول میدیم که در آینده جوابی رو برای درخواستش ارسال میکنیم، و کاربر میتونه به کارش ادامه بده و ماهم پردازشمون رو انجام میدیم. اینجاست که پرامِس (پرامیس؟) یا همون Promiseها خودشون رو نشون میدن.قبل از معرفی شدن پرامس‌ها، تو جاوااسکریپت از Callbackها استفاده میشد، کال‌بک‌ها توابعی بودن که مثلا به عنوان آرگومان یک تابع دیگه تعریف میشدن، تا هنگام بُروز یک رویداد خاص (Event) کار خاصی رو هم انجام بدن. مشکل اینجا بود که ایده‌ی کال‌بک‌ها، هرچقدر هم که قشنگ بوده، تو پردازش‌های پیچیده مدیریت کد رو خیلی سخت میکرده و به اصطلاح، برنامه‌نویس رو وارد جهنم کالبَک‌ها یا همون Callback-Hell میکرده. برای همین پرامس‌ها ساخته شدند تا جایگزینی برای کال‌بک‌ها باشن.پرامس‌ها یا جوابی رو برمیگردونن یا اینکه دلیلی رو برای عدم انجام موفقیت‌آمیز اون فرایند ارائه میدن. از اینجا میشه گفت که پرامس‌ها ساختار مشابهی مثل try/catch دارن و از همه مهمتر، سه وضعیت کلی رو شامل میشن:Pending: که یعنی در حال انجام کاری هستند،fulfilled: که یعنی کارشون رو به خوبی انجام دادن وrejected: که یعنی از پس کاری که بهشون داده شده بر نیومدن.یه مثال ساده برای اینکه با نمونه‌ای از یک پرامس آشنا بشید به این شکله:fetch(url)
 .then(process)
 .then(save)
 .catch(handleErrors)اینجا، تابع process صبر میکنه تا کار تابع fetch تموم بشه، بعد تابع save منتظر process میمونه و اگر هرکدوم از این توابع جایی به مشکل خوردند، تابه handleErrors وظیفش رو انجام میده.اگر هرکدوم از این توابع، پرامس باشن، میتونن ساختار مشابهی رو برای خودشون بگیرن، در واقع این قابلیت رو به شما میدن تا بتونید پرامس‌ها رو تودرتو کنید.استفاده از fetchحالا که متوجه مفهوم ایزومورفیک و همچنین درخواست‌های نا‌همگام شدید، بریم تا با یه مثال تو پروژمون به درک بیشتری ازشون برسیم.قدم اول، نصب کتابخونه isomorphic-fetch از مخازن npm هست. fetch یکی از کتابخونه‌های مورد علاقه‌ی من که در کنارش کتابخونه axios وجود داره. تفاوت عمده این دو کتابخونه، تو پردازش اطلاعات هست و اکسیوس برای ای‌اس۶ آمادگی بیشتری داره، منتهی من طبق عادت پیش میرم و از فِچ (fetch) استفاده میکنم.$ npm install --save isomorphic-fetchکتابخونه رو نصب کنید، اینکه واژه ایزومورفیک اولش استفاده شده، نشون میده که این کتابخونه رو، هم میشه سمت سرور و هم سمت کاربر استفاده کرد. حالا، کامپوننت ThisIsAComponent رو یکمی تغییر میدیم. در نهایت کدمون باید به شکل زیر بشه:import React from &quot;react&quot;;
import fetch from &quot;isomorphic-fetch&quot;;

export default class ThisIsAComponent extends React.Component {
 constructor() {
 super()

 this.state = {
            done: true,
            items: \[\]
        }
    }

 fetchData() {
 this.setState({
            done: false
        });

 fetch(&#039;http://jsonplaceholder.typicode.com/posts&#039;)
 .then(data =&gt; {
 data.json()
 .then(res =&gt; {
 this.setState({
                    done: true,
                    items: res
                })
            })
        })
 .catch(error =&gt; {
 console.log(error)
        })
    }

 render() {
 return (
            &lt;div&gt;
                &lt;h1&gt;{this.props.title}&lt;/h1&gt;
                &lt;button onClick={this.fetchData.bind(this)}&gt;Get Data&lt;/button&gt;
                &lt;p hidden={this.state.done}&gt;Loading&lt;/p&gt;
                &lt;div&gt;
 {
 this.state.items.map(item =&gt; {
 return (
                                &lt;p&gt;{item.title}&lt;/p&gt;
 )
                        })
 }
                &lt;/div&gt;
            &lt;/div&gt;
 );
    }
}خط به خط بریم جلو ببینیم چه اتفاقی افتاده:اول، از کتابخونه isomorphic-fetch شئ fetch رو فراخوانی کردم.تو تابع constructor، استیت اولیه رو تغییر دادم و به جای isHidden که اول داشتیم، done رو، که وظیفه نگهداری از وضعیت بارگذاری آیتم‌ها رو به عهده داره و items که نگهدارنده آیتم‌های دریافت شده از سرور هستند رو ساختم.تابع toggleState رو پاک کردم و به جاش از fetchData استفاده کردم. اسمش رو هم خودم انتخاب کردم. داخل این تابع اتفاقات جالبی میوفته.زمانی که این تابع خونده میشه (یا در واقع رو دکمه‌ای کلیک میشه که باید این تابع رو اجرا کنه) وضعیت done به false تغییر پیدا میکنه. چون در حقیقت آیتمی دریافت و کار ما هم تموم نشده.قدم بعدی، از fetch استفاده کردم تا از یک آدرس پیش‌فرض، یک‌سری اطلاعات الکی رو دریافت کنم. نکته مهم اینه که fetch یک پرامس هست و وضعیتش رو میشه کنترل کرد. برای همین، تو خط پایینش گفتم، هر موقع که دریافت اطلاعات تموم شد، از اطلاعات دریافت شده که اسمشون رو data گذاشتم استفاده کن و…یکی از مهم‌ترین تفاوت‌های fetch و axios تو این مرحلست، پاسخی که fetch به شما برمیگردونه، یک پاسخ خام هست و باید تبدیل به دیتای قابل خوندن بشه. fetch این کار رو با برگردوندن یک پرامس انجام میده (برای نوشتن توابع از Arrow Functionها استفاده کردم). پس گفتم، زمانی که جواب رو از سرور گرفتی، اون رو به json تبدیل کن (که خود تابع json یک پرامس برمیگردونه) و بعد از اینکه عمل تبدیل به json درست انجام شد، از حاصلش استفاده کن تا دوباره استیت رو تغییر بدی.done رو هم برابر با true کردم تا نشون بدم عمل دریافت اطلاعات کامل شده. در نهایت نتیجه حاصل از دریافت اطلاعات، یا همون آیتم‌های مورد نظرم رو، که حالا تبدیل به json شدند، داخل items استیت ذخیره کردم.اینکار باعث میشه که ری‌اکت، یکبار دیگه کامپوننت‌هایی که از این استیت استفاده میکنن رو بارگذاری کنه. در نهایت هم بررسی میکنه که آیا اشکالی وجود داشته تو کل این فرایند یا نه.آخر سر، تو تابع render دکمه Get Data رو به تابع fetchData وصل کردم. یک تگ p هم اضافه کردم که بهش گفتم، وضعیت hidden بودنش رو از done موجود تو استیت بگیره. (هر موقع که بارگذاری تموم شده بود، این المان محو میشه و هرموقع بارگذاری در حال انجام بود، این المان نمایش داده میشه)داخل المان div که به جای ul نشسته، با استفاده از تابع map() که شکل جدیدی از forEachهست، آیتم‌ها رو نمایش دادم. (پایین‌تر درمورد map توضیحات بیشتری میدم)ادامه مطلب در سایت خودم…</description>
                <category>پول ریکوئست</category>
                <author>آیین</author>
                <pubDate>Tue, 22 May 2018 15:47:59 +0430</pubDate>
            </item>
                    <item>
                <title>ری‌اکت برای تازه‌کارها - بخش یک</title>
                <link>https://virgool.io/pullrequest/%D8%B1%DB%8C%D8%A7%DA%A9%D8%AA-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AA%D8%A7%D8%B2%D9%87%DA%A9%D8%A7%D8%B1%D9%87%D8%A7-%D8%A8%D8%AE%D8%B4-%DB%8C%DA%A9-lpo51s0vto7s</link>
                <description>اگر در یک سال اخیر در مورد تکنولوژی‌های وب مطالعه کردید، پس احتمالا با فریم‌ورک React در حد خوبی آشنایی دارید. یکی از شکایت‌هایی که تازه واردان در مورد ری‌اکت می‌کنند اینه که «چرا اینقدر شروع و ساخت یک پروژه باهاش دنگ و فنگ داره؟» که خب البته راه‌های زیادی برای فائق اومدن بر این پیچیدگی‌ها ارائه شده.اولین راه استفاده از یک CDN هست. شما می‌تونید فقط لینک یک تگ &lt;script&gt; رو توی فایل HTML تون کپی کنید و بعدش از ری‌اکت استفاده کنید.&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js&quot;&gt;&lt;/script&gt;خب البته انگار یک تگ نیست :)) حقیقتا سه تا تگ هست.اولین تگ، برای وارد کردن خود react.دومی react-dom که رندر کردن کدهامون توی مرورگر بهش نیاز داریم.سومی هم Babel هست. babel یک ترنسپایلر (transpiler) هست که وظیفه داره کد ES6 و ES7 ما رو به ES5 قابل فهم برای مرورگر تبدیل کنه. با استفاده از Babel ما می‌تونی کدمون رو با آخرین استانداردها (از جمله استانداردهای React مثل JSX) بنویسیم و نگران این نباشیم که مرورگرهای قدیمی نمی‌تونن اون رو اجرا کنن.&lt;script type=&quot;text/babel&quot;&gt;
  class App extends React.Component {
    render() {
     return &lt;h1 className=&quot;wrapper&quot;&gt;Hello Pullrequest.ir!&lt;/h1&gt;
    }
  }

  ReactDOM.render(
    &lt;App /&gt;,
    document.getElementById(&#039;root&#039;)
  );
&lt;/script&gt;حالا می‌تونیم کدهای ری‌اکتمون رو مثل مثال بالا توی یک تگ اسکریپت (که البته باید ویژگی typeاش رو برابر با «text/babel» قرار بدیم) بنویسیم و برنامه رو اجرا کنیم: https://codepen.io/mehrad77/pen/zjBWXZ/ اما خب اینجوری کلی از امکاناتی که کار با ری‌اکت رو راحت‌تر و کارامد می‌کنه رو از دست می‌دید و خوب نیست که یک پروژه بزرگ رو به این شکل پیش ببریم.راه مناسب‌تر استفاده از ابزارهایی مثل Webpack و NPM هست تا خیلی شیک و راحت بتونید از همه‌ی امکانات پیشرفته و کارآمدی که ازشون حرف زدیم استفاده کنید، اما باز برگشتیم سر خونه اول :) راه اندازی و استفاده ازوب‌پک برای یک تازه کار واقعا کار آسونی نیست.من می‌خوام در این مجموعه نوشته، راهکاری بهتون معرفی کنم که بتونیم این سختی نصب و تنظیم Webpack / Babel / NPM / React رو آسون کنیم. اسم این ابزار create-react-app است.نوشته‌ای که مشفول خواندن آن هستید برداشتی آزاد از نوشته‌ای از براندون ریچی است. می‌تونید برای خواندن مقاله به زبان اصلی، به پیوند مراجعه کنید.مخاطبین این آموزشاین مقاله آموزشی به درد افرادی می‌خوره که با نصب و استفاده nodejs / npm آشنایی نسبی و پیش‌زمینه‌ای در Javascript و CSS دارند. همچنین اگر شما با ری‌اکت آشنایی دارید، احتمالا چیز زیادی از (حداقل این قسمت) این آموزش یاد نخواهید گرفت.اگر nodejs یا npm را روی دستگاه نصب ندارید می‌تونید روش‌های نصب و استفاده رو از https://nodejs.org دنبال کنید.اگر جاوااسکریپت بلد نیستید هم می‌تونید از طریق دوره آموزشی جاوااسکریپت سایت سکان آکادمی، کلیات این زبان رو فرا بگیرید.برای بهره وری از ری‌اکت در بهتری حالت بهتره که به استاندارد ES6 هم مسلط باشید، هرچند که من هر جا نیاز باشه اشاره‌ای بهش خواهم کرد، اما اگه با ES6 آشنا نباشید ممکنه گیج بشید.وب‌پکوب پک (webpack) یک ماژول باندلر قدرتمند برای برنامه های مدرن جاوا اسکریپته. وقتی وب‌پک برنامه شما را پردازش می کنه، یک گراف وابستگی از هر ماژولی که برنامه شما نیاز دارد، می‌سازه، سپس تمام اون‌ها را توی فایل‌های باندل کوچکی قرار می‌ده که توسط مرورگر لود می‌شن. وب‌پک کارهایی دیگه‌ای مثل ترنسپایل کردن ماژول‌ها، بسته بندی ماژول‌ها، Minify کردنشون و … رو هم انجام می‌ده.برای آشنایی بیشتر با وب‌پک می‌تونید ویدیوی زیر رو مشاهده کنید: https://www.youtube-nocookie.com/embed/nfmvexyoHXE?rel=0 حالا create-react-app چیه؟ابزار create-react-app ( که از حالا به بعد به شکل خلاصه بهش می‌گم CRA) راه‌حلی برای آسون کردنِ پیچیدگی شروع ساخت یک پروژه ری‌اکته. مجموعه‌ای از پیش‌آماده از ابزارهایی مثل Webpack و Babel و React است که کمک می‌کنه با یک دستور، یک محیط فوق‌العاده و آماده داشته باشید که بتونید شروع به استفاده از ری‌اکت کنید و وقتتون رو صرف تنظیم و نصب این ابزارها نکنید.البته این آماده سازی و قبول کردن مسئولیت تمام تنظیمات پروژه باعث نمی‌شه که CRA فقط به درد تازه‌کارها بخوره. در حقیقت CRA یک دستور eject داره که باعث می‌شه هر وقت که دیگه نیازی به تنظیمات اولیه نداشتید پروژه رو از دستش بگیرید و CRA رو از برنامه‌تون خارج کنید تا هر جور که دلتون خواست ادامه بدید.نصب CRAبا در نظر گرفتن اینکه شما از قبل npm را نصب کرده‌اید، می‌توانید با دستور زیر این ابزار رو نصب کنیم:$ npm install -g create-react-appیا اگه به جای npm از yarn استفاده می‌کنید (که پیشنهاد می‌کنم این کار رو کنید)، می‌تونید با دستور زیر به شکل گلوبال این ابزار رو نصب کنید:$ yarn global add create-react-appبرای اینکه امتحان کنیم که به شکل درست نصب شد یا نه، می‌تونیم از دستور زیر استفاده کنیم:$ create-react-app --version
  create-react-app version: 1.4.3ساخت یک پروژه ری‌اکت با آناگر نام پروژه شما hello-world باشد، ساخت یک پروژه به سادگی زیر است:$ create-react-app hello-world

(...)

Success! Created hello-world at .../dev/create-react-app/hello-world
Inside that directory, you can run several commands:
yarn start
    Starts the development server.
yarn build
    Bundles the app into static files for production.
yarn test
    Starts the test runner.
yarn eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd hello-world
  yarn start
Happy hacking!راهنمایی که در انتها چاپ می‌شه خیلی مهمه. چهار دستور مهمی که توی این ابزار استفاده می‌شه عبارت است از start، build، test و eject.با اینکه در خروجی نصب توضیحات مناسبی ارائه شده اما بیایید یه بار دیگه اون‌ها رو مرور کنیم:yarn startاین دستور باعث می‌شه این ابزار یک وب‌سرور کوچولو راه‌اندازی کنه که پروژه‌تون رو توی مرورگر بهتون نشون بده. این دستور یک وب سرور مخصوص توسعه محلی توی آدرس http://localhost:3000 براتون می‌سازه که می‌تونید شمای پروژه رو ببینید و با هر تغیری که توی کدتون بدید، به شکل خودکار سرور رو ریفرش کنه.yarn buildدر توضیحات نوشته «Bundles the app into static files for production». اگر با تا به حال با ابزارهایی مثل گالپ یا وب‌پک کار کرده باشید و با فرایند آشنا باشید، احتمالا می‌دونید که اینها چه معنایی داره . این دستور تمام جاواسکریپت‌هایی (و چیزهایی دیگه‌ای) که توی برنامه‌تون نوشتید رو می‌گیره، تبدیلشون می‌کنه به زبونی که مرورگر بتونه اجراشون کنه و سپس، در یک فایل اون‌ها رو فشرده می‌کنه این کار باعث می‌شه که بر خلاف نسخه‌ای که برای توسعه روی کامپیوتر خودتون اجرا می‌کنید، نسخه build بسیار فشرده و کم‌حجم بشه که بتونید با خیال راحت اون رو توی هاستتون بارگذاری، و برنامه رو آنلاین کنید!yarn testبزارید یه خبر خیلی خوب بهتون بدم! از نسخه 0.3.0 به بعد، این ابزار کتاب‌خونه تست‌نویسی Jest رو هم به ابزارهایی که نصب و تنظیم می‌کنه اضافه کرده تا دیگه حتی برای برای تست نوشتن هم نیاز به نصب و تنظیم چیز اضافه‌ای نداشته باشید. با این دستور می‌تونید تست‌هاتون رو اجرا کنید و نتیجه رو ببینید.همین الان به مجموعه فایل‌هایی که دارید نگاه کنید، احتمالا چیزی شبیه به این خواهید دید:اگر دقت کنید یک فایل جاواسکریپت با نام App.js و یک فایل تست با نام App.test.js توی پروژه وجود دارن. پس با اجرای دستور yarn test، احتمالا خروجی مثل خروجی زیر بگیرید:همونطور که دیدید، دستور test، تست‌های برنامه رو شناسایی و اجرا کرد. شما می‌تونید با استفاده از Jest هر تستی که می‌خواهید رو برای کد‌های برنامه تون بنویسید. یه رازی رو بهتون می‌گم و این رو از من به نصیحت داشته باشید، اگه خودتون می‌خواستید همه این‌ها رو سر هم کنین واقعا عذاب الیمی می‌کشیدید :) پس قدر این راحتی رو بدونید و برای همه‌ی کامپوننت‌هاتون تست بنویسید :)Yarn Eject«این ابزار و وابستگی‌های آن را حدف می‌کند و فایل‌های پیکربندی و اسکریپت را در دایرکتوری برنامه قرار می‌دهد. اگر این کار را بکنی، راه برگشتی وجود ندارد!». خب، توضیح مناسبی بود.اونچه که این دستور انجام می‌ده اینه که برنامه شما رو از چارچوب create-react-app بیرون میاره و به یک چارچوب وب‌پک استاندارد تبدیل می‌کنه. این کار به این به شما این امکان را می‌ده که در صورت نیاز بتونید ابزارهایی رو حذف/اضافه کنید و دست‌‌کاری‌های پیشرفته‌ای رو که مي‌خواهید، انجام بدید. می‌تونید بهش به این شکل نگاه کنید که میاد و داربست‌های CRA رو بر می‌داره و برنامه شما رو در کنترل خودتون می‌زاره.البته با توجه به گستردگی و پوشش فوق‌العاده‌ای که مستندات CRA برای اکثر ابزارها ارائه داده، شما تقریبا (مگر در مواردی واقعا خاص) نیازی به خروج از این چارچوب ندارید.آشنایی با ساختار برنامهخب، بیایید دوباره ساختار پروژه رو نگاه کنیم:ابزار CRA برای شما یک فایل README.md ساخته که توش راهنمایی جامع از کل برنامه رو ارائه داده. هر وقت به مشکلی برخوردید یا جایی از ابزار براتون مبهم بود می‌تونید به این فایل مارک‌داون مراجعه کنید. در پوشه public/ فایل‌های استاتیک برنامه مثل favicon.ico یا index.html (که کد ری‌اکت ما در این فایل رندر می‌شه) رو می‌بینید.بزارید داخل فایل index.html رو یه نگاهی بندازیم:&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot;&gt;

    &lt;link rel=&quot;manifest&quot; href=&quot;%PUBLIC_URL%/manifest.json&quot;&gt;
    &lt;link rel=&quot;shortcut icon&quot; href=&quot;%PUBLIC_URL%/favicon.ico&quot;&gt;
 
    &lt;title&gt;React App&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;noscript&gt;
      You need to enable JavaScript to run this app.
    &lt;/noscript&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
 
  &lt;/body&gt;
&lt;/html&gt;مهم‌ترین جزء این کد HTML، برچسب &lt;div id=”root”&gt; هست. داخل این برچسب هست که قراره کد ری‌اکت ما Render بشه.به جز این‌ها ما فایل package.json رو داریم که وابستگی‌ها و اطلاعات کلی برنامه‌مون اونجا قرار داره. توی پوشه node_modules/ هم تمامی وابستگی‌هایی که برنامه ما بهشون نیاز داره قرار داده شدن که با دستور yarn install نصبشون کردیم. تقریبا هیچوقت نیاز نیست در محتوای این دایرکتوری دست ببرید.اما مهم‌ترین دایرکتوری برای ما، دایرکتوری src/ هست. اکثر کدهای جاوااسکریپت ما قراره که توی این پوشه قرار داده بشن که محتواش رو جلوتر بررسی می‌کنیم.فایل index.js حاوی فرخوان Render برنامه ما از ReactDOM هست که فایل App.js ما رو می‌خونه ( که درش کدهای اصلی برنامه‌مون قرار داره). و همچنین به ReactDOM می‌گه کجا این‌ها رو نشون بده. ( اون &lt;div id=”root”&gt; رو که یادتونه؟) index.css هم استایل‌های کلی رو نگه‌داری می‌کنه.و اما App.js، نخستین کامپوننت ری‌اکت ما هست که جلوتر باهاش کار داریم. App.css استایل‌های مربوط به این کامپوننت به خصوص را شامل می‌شه و تصویر logo.svg هم فقط لوگوی ری‌اکته، نه چیزی بیشتر. App.test.js هم که قبلا در موردش صحبت کردیم یک فایل تست نمونه برای کامپوننت App.js هست.ساخت نخستین کامپوننتحالا شروع کنیم به کد زدن. اول توی دایرکتوری پروژه توی ترمینال دستور yarn start یا npm start رو بزنید تا وب‌سرور توسعه ما شروع به کار کنه و توی مرورگر بتونم نتیجه پروژه‌مون رو ببینیم… حالا بیایید یه کار باحال کنیم. تمام محتوای App.js و App.css رو پاک کنید تا از اول بنویسیمشون! پس این دو فایل رو باز کنید و محتواشون رو خالی کنید.وقتی از استاندارد ES6 استفاده می‌کنید، دو راه وجود داره تا کامپوننت بسازید. از طریق فانکشن‌ها یا از طریقکلاس‌های ES6. در این قسمت ما کامپوننتمون رو با استفاده فانکشنم می‌سازیم و در قسمت بعد اون رو با class بازنویسی می‌کنیم تا تفاوت‌هاشون رو متوجه شیم.هر دو روش نیازمند این هستند که خود کتابخونه ری‌اکت رو وارد کامپوننتمون کنیم. پس داخل فایل کامپوننتمون (در src/App.js) این خط رو اضافه کنید:import React from &#039;react&#039;;و تمام! ما هرچیزی که از ری‌اکت نیاز داریم برای ساخت کامپوننت‌ها رو در دسترس داریم! دستور importکتابخونه ری‌اکت که قبلا با دستور yarn install به شکل خودکار توی پوشه node_modulse نصب کرده بودیم رو در متغیر React در دسترس ما قرار می‌ده.حالا فانکشن کامپوننت رو بنویسیم:const App = () =&gt; {
 return (
    &lt;div className=&quot;wrapper&quot;&gt;Hello World!&lt;/div&gt;
 )
}با همین ۵ خط ما تا خرخره توی ES6 فرو رفتیم :) اگه با سینتکس ES6 آشنا نیستید پیشنهاد می‌کنم بیش از ادامه دادن به آموزش در موردش مطالعه کنید و ویژگی‌هاش رو یاد بگیرید.ما ابتدا با دستور const، کامپوننتمون با نام App رو داخل یک ثابت تعریف کردیم (از اونجایی که قرار نیست در حین اجرای اپلیکیشن اون رو تغیر بدیم، پس تعریف یک ثابت کار خوبیه). تعریف کامپوننت رو با استفاده از یک Arrow Function انجام دادیم که از ویژگی‌های خوب ES6 هست. فانکشن ما هیچ آرگومانی نمی‌گیره. هر کامپوننتی که به شکل فانکشن تعریف می‌کنیم باید حتما یک JSX رو به ما با دستور return برگردونه. و یادتون باشه که همیشه محتوای JSX تون رو داخل پرانتز قرار بدید. پرانتز فقط هنگامی که جی‌اس‌اکس چند خطی داشته باشید الزامی هست، اما این تمرین خوبیه که همیشه ازش استفاده کنید تا از اشتباهاتی که ممکنه پیش بیاد جلوگیری کنید. همچنین با استفاده از className=&quot;App&quot; یک کلاس CSS به کامپوننتمون می‌دیم. نکته مهم اینه که از اونجایی که class توی زبان جاوا‌اسکریپت یک واژه رزرو شده‌ست، همیشه برای تعین کلاس باید از واژه className استفاده کنیم.این JSX که می‌گی، چی هست اصلا؟اگه می‌دونید JSX چیه می‌تونید این بخش رو رد کنید، اگر نه این خلاصه رو بخونید تا درک بهتری داشته باشید.جی‌اس‌اکس (JSX) یک زبان الگویی (templating) است که خیلی به HTML شباهت داره. این زبان به شما این امکان رو می‌ده که برای کامپوننت‌هاتون الگوهایی رو بنویسی که خیلی شبیه HTML هستند اما یک سری تفاوت‌ها داره. اول اینکه شما می‌تونید توی الگوهاتون هر جایی که خواستید از با استفاده از آکولاد (از این‌ها: {}) از جاواسکریپت استفاده کنید. دوم اینکه، برخی از کلمه‌ها در جاوااسکریپت از قبل رزرو شدن و جزو خود زبان هستند، (مثل class) بنابراین از یک سری مشخصه‌های دیگه (مثل className) به جای اون‌ها استفاده می‌کنیم.علاوه بر این، وقتی که شما دارید توی دستور return تون یک JSX بر می‌گردونید، شما فقط باید یکدونه المان رو بگردونید. برای مثال اگر این کار رو بکنید:خطای زیر رو دریافت می‌کنید:که برای رفع این مشکل می‌تونید کل JSXتون رو داخل یک &lt;div&gt; مادر قرار بدید:ادامه کارخب برگردیم که ادامه ساخت کامپوننتمون. الان اگه به خروجی مرورگر نگاه کنید هنوز هم نمی‌تونید کامپوننتمون رو ببینید.در ES6 باید یک کار دیگه هم بکنیم تا بتونیم این امکان رو به باقی فایل‌ها بدیم تا بتونن به داخل فایل ما دسترسی داشته باشن و اون کار اکسپورت (export)‌ هست!در انتهای فایل خط زیر را اضافه کنید:export default App;با این خط کامپوننتمون رو که توی ثابت App ساخته بدویم استخراج می‌کنیم. حالا اگه فایل رو سیو کنید و برگردید به مرورگرتون می‌تونید کامپوننتمون رو ببینیم که صحیح و سالم داره نشون داده می‌شه.افزودن CSS به کامپوننتمونکامپوننتمون خیلی ساده و سفیده. پس بیایید فایل CSSای که توی پروژه‌مون وجود داره (در آدرس: src/App.js) رو بهش وصل کنیم و چند خطی CSS بنویسیم.برای وارد کردن فایل CSS به پروژه‌مون باید با دستور import به شکل زیر این کار رو انجام بدیم.import &#039;./App.css&#039;;حالا وارد فایل src/App.css بشید و کلاس زیر را وارد کنید:.wrapper {
 border: 2px solid black;
 text-align: center;
 background: #f5f5f5;
 color: #333;
 margin: 20px;
 padding: 20px;
}با ذخیره کردن فایل و چک کردن صفحه مرورگر وب‌سرور توسعه‌ام می‌تونیم صفحه خوشگل و با سی‌اس‌اس‌مون رو به شکل زیر ببینیم:خب!‌ پس ما الان مراحل ساخت یک کامپوننت ساده رو فهیدیم و اون رو پیاده‌سازی کردیم. بیایید در انتها ببینیم که این کامپوننتمون داخل فایل index.js چجوری داره نمایش داده می‌شه.فایل src/index.js رو باز کنید و داخلش رو نگاه کنید:import React from &#039;react&#039;;
import ReactDOM from &#039;react-dom&#039;;
import &#039;./index.css&#039;;
import App from &#039;./App&#039;;
import registerServiceWorker from &#039;./registerServiceWorker&#039;;

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#039;root&#039;));
registerServiceWorker();خُب در خط اول، import React رو قبلا توضیح دادیم.در خط دوم ما import ReactDOM رو داریم. به طور خلاصه react-dom بخش از React است مسئول تبدیل و نمایش کامپوننت‌های React از دام‌مجازی به دام‌اصلی هست. توی این مورد خاص ما از تابع render()‍ این کتاب‌خونه استفاده کردیم.در خط سوم هم فایل CSS اصلی ما اومده که سی‌اس‌اس های مادر رو می‌تونم توش قرار بدیم.در این خط (import App) ما کامپوننتی که نوشتیم رو فرا می‌خوانیم. این دستور در واقع فایل App.jsکه ما دقایقی پیش ساختیم رو وارد می‌کنه که به شکل پیش‌فرض اگر پسوند فایل ما .js باشه، نیازی به ذکرش نیست و می‌تونیم فقط اسم بدون پسوند رو بنویسیم و خود ES6 متوجه این می‌شه که فایل با پسوند .js رو وارد کنه. همچنین «./» ابتدای آدرس باعث می‌شه که جاواسکریپت متوجه شه این فایل در همین پوشه که index.js وجود داره هست و یک ماژول npm رو وارد نکنه.خط بعد هم فراخوان فایلی‌ست که به شکل پیشفرض در CRA وجود داره و وظیفه‌اش ساخت یک نسخه PWA یا همان Progressive Web Apps از برنامه شماست که البته فعلا لازم نیست در موردش نگران باشید. می‌تونید بزارید سرجاش باشه تا در انتهای یادگیری ری‌اکت، سری بهش بزنیم و بررسیش کنیم.و در انتها ما تابع ReactDOM.render رو داریم که وظیفه نمایش برنامه ری‌اکت ما رو داره. این فانکنش دو آرگومان می‌گیره که اولیش کامپوننتی هست که می‌خواهیم بارگذاری بشه، و دومین آرگومان، مکانی است که اون آرگومان باید نمایش داده بشه. که ما برای آرگومان اول، کامپوننت App ای که نوشتیم رو قرار دادیم و در آرگومان دوم هم به همان &lt;div id=”root”&gt; ،که قبلا در فایل index.html بهش اشاره کردیم.جمع‌بندیتبریک! حالا شما یک فانکشنال کامپوننت ساختید! در مورد webpack و babel هم همه چیز بدون اینکه بفهمیم تمام شد و بدون هیچ دردسری راه‌انداری شدند! این یک روش بسیار خوب و تمیز برای شروع کار با react است. ما در آموزش بعدی ساخت کامپوننت با استفاده از classهای ES6 و مفهوم state ها رو یاد می‌گیریم.قسمت دوم: به زودی…</description>
                <category>پول ریکوئست</category>
                <author>مهراد روستا</author>
                <pubDate>Wed, 25 Apr 2018 15:34:00 +0430</pubDate>
            </item>
                    <item>
                <title>به پیشرفت جامعه متن باز کمک کنید، تیشرت مجانی بگیرید!</title>
                <link>https://virgool.io/pullrequest/%D8%A8%D9%87-%D9%BE%DB%8C%D8%B4%D8%B1%D9%81%D8%AA-%D8%AC%D8%A7%D9%85%D8%B9%D9%87-%D9%85%D8%AA%D9%86-%D8%A8%D8%A7%D8%B2-%DA%A9%D9%85%DA%A9-%DA%A9%D9%86%DB%8C%D8%AF-%D8%AA%DB%8C%D8%B4%D8%B1%D8%AA-%D9%85%D8%AC%D8%A7%D9%86%DB%8C-%D8%A8%DA%AF%DB%8C%D8%B1%DB%8C%D8%AF-rjvceytwh18w</link>
                <description>سلام خدمت همه دوستان اوپن سورسی! ? چند روزه ک رویدادی (نمیدونم رویداد بهش بگیم درست تره یا جشنواره!) به اسم هکتوبرفست توسط شرکت DigitalOcean شروع شده که تا آخر اکتبر ادامه داره. میخوام توی این پست براتون دربارش توضیح بدم، آخر سر اگه تیشرت گرفتید بگید دمش گرم چه پست خفنی گذاشته بود!! ?Hacktoberfest چیه دیگه؟!همونطور که بالاتر گفتم، هکتوبرفست یه دوره یکماهست که از اول اکتبر شروع میشه و تا آخر اکتبر ادامه داره. توی این جشن (خودشون بهش میگن سلبریشن!) میان میگن هرکی طی این یکماه توی پروژه های متن باز نرم افزاری توی گیتهاب (همون ریپازیتوری ها) مشارکت کنه و 4 تا پول ریکوئست موفق بفرسته که از طرف owner اون ریپازیتوری قبول و مرج بشه، براش یه تیشرت و کلی استیکر باحال از اینا که میچسبونن رو در لپتاپ، میفرستیم!شرایطش چیه؟هیچی! فقط توی چهارتا ریپازیتوری توی گیتهاب مشارکت کنید و پول ریکوئست بفرستید. الان که این مطلبو میخونید یعنی من اولیشو فرستادم و مرج شده و فقط سه تا دیگه تا تیشرت اصل آمریکایی دیجیتال اوشنی فاصله دارم! ??بابا ما که ایرانیم!از اونجا که شرکت دیجیتال اوشن یه شرکت آمریکاییه و به ایرانی ها سرویس نمیده طبق معمول، اولین چیزی که به ذهن هممون میرسه اینه که برای ما چیزی نمیفرستن، اونم چی، از آمریکاااای ترامپی! ??منم اولش همینو گفتم اما یکی از بچه ها توی گروه ویو (مهران رسولیان عزیز) که این جشنواره رو بهمون معرفی کرد، گفت که پارسال سرش شلوغ بود و تونست فقط 3 تا پول ریکوئست موفق بفرسته که مرج بشه اما در یکی از همین روزا در عین ناباوری براش یه بسته که روش نوشته بود Happy Hacking و توش چنتا استیکر گوگولی گیت و گیتهاب و لوگو خودشون بودو فرستادن!پول پُست کردن بسته رو که دیگه باید بدیم!اینم بگم سرتونو از شدت این همه تناقض توی رفتار با ایرانیا میکوبید به دیوار!! جالبی قضیه اینه که حتی یک قرون هم لازم نیست بدید، شما برنامه نویس و مشارکت کننده متن باز هستید، یعنی دست کنی تو جیبت ناراحت میشم! تنها کاری که باید بعد از 4 تا پول ریکوئست موفق بکنید اینه که چاییتون رو بریزید (یا حالا قهوه) و منتظر بشید بستتون برسه!هَپی هَکینگ!فکر کنم همه اطلاعاتی که لازمه رو دادم دیگه، فقط یه لینک وبسایتش مونده که برید ثبت نام کنید و کارتون رو استارت بزنید:https://hacktoberfest.digitalocean.comبه امید روزی که برای هر ریپازیتوری پول ریکوئست فرستادیم یه تیشرتی ماگی پیکسلی چیزی برامون بفرسته. ?راستی یادتون نره به دوستاتونم درباره این جشنواره بگید یا لینک این پست رو براشون بفرستید. جامعه اوپن سورس هرچی فعال تر بهتر! ?</description>
                <category>پول ریکوئست</category>
                <author>عرفان صحاف نژاد</author>
                <pubDate>Fri, 06 Oct 2017 14:20:28 +0330</pubDate>
            </item>
                    <item>
                <title>چرا باید از فریمورک استفاده کنیم؟</title>
                <link>https://virgool.io/pullrequest/%DA%86%D8%B1%D8%A7-%D8%A8%D8%A7%DB%8C%D8%AF-%D8%A7%D8%B2-%D9%81%D8%B1%DB%8C%D9%85%D9%88%D8%B1%DA%A9-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%DA%A9%D9%86%DB%8C%D9%85-vh3xk0y4g4lb</link>
                <description>سلام! ?توی اولین پست پول ریکوئستم میخوام در رابطه با موضوع “استفاده کردن یا نکردن از فریمورک ها” صحبت کنم. توی گروه های تلگرامی سوالات زیادی درباره این موضوع پرسیده میشه برای همین تصمیم گرفتم این مطلب رو بنویسیم تا برای دوستانی که سردرگم هستن کمکی باشه. مقدمه چینی بسه، بریم ببینیم داستان از چه قراره!فریمورک چی هست اصلا؟فریمورک یک چارچوب نرم افزاریه که کمک میکنه اپلیکیشن شما تحت ساختار خاصی ایجاد و توسعه داده بشه.برای استفاده از فریمورک شما باید از مجموعه قوانین خاصی پیروی کنید و با فرمت خاصی پروژتون رو توسعه بدید. مزیت پیروی از این قوانین منسجم بودن کد، صرفه جویی توی زمان، پایداری بیشتر، امنیت قابل اعتماد تر و قابلیت های دیگه ای هست که بسته به فریمورک مورد استفاده متفاوته. جالبه نه؟ ?تنها بخشی که ممکنه بخواد شمارو از استفاده از فریمورک منصرف کنه، مدت زمانی هست که شما باید صرف یادگیری قوانین، کلاس ها و ساختار فریمورک کنید. با توجه به اینکه یه مدت زمان میبره تا با فریمورک جدید آشنا بشید و بتونید باهاش کار کنید، منطقیه که اصلا سمت فریمورک بریم؟ باید بگیم بله، کاملا ارزشش رو داره!فریمورک استفاده کنیم یا نه؟جواب سوال خیلی واضحه و بستگی به پروژه شما داره. در صورتی که پروژه شما ممکنه از حد و سطح پروژه های ساده خارج بشه، قطعا شما احتیاج به یک فریمورک دارید. چون شما به عنوان یک برنامه نویس تنها یا حتی تیم، توانایی پوشش دادن همه جوانب یک اپلیکیشن رو بصورت “کامل” ندارید. دلیل دیگه ای که آدمو مجاب میکنه که برای پروژه های متوسط و بزرگ از فریمورک استفاده کنه، صرفه جویی توی وقت زمان پیاده سازی، توسعه و نگهداریه.در صورتی که از فریمورک استفاده نکنید (یا بخواید فریمورک شخصی بنویسید) چندین و چند مسئله هست که باید خوب راجع بهش فکر کنید:اصلا علم کافی برای پیاده سازی حرفه ای یک اپلیکیشن بدون فریمورک یا ساخت فریمورک اختصاصی رو دارید؟آیا کد هایی که نوشتید بهینه ست و توی بهترین حالت ممکن نوشته شده؟اگه مشکل امنیتی داشتید و هیچوقت متوجهش نشدید چی؟پیاده سازی وبسایت بدون استفاده از فریمورک های از پیش ساخته شده، چقدر میتونه زمان ببره؟اگر احتیاج داشتید پکیج از پیش نوشته شده ای به پروژتون اضافه یا کم کنید، بدون دردسر این کار انجام میشه؟اگر کسی برای توسعه اپلیکیشن به شما و تیمتون اضافه شد، چقدر طول میکشه به ساختار کد یا فریمورک شما عادت کنه و باهاش آشنا بشه؟کد های شما دارای داکیومنت کامل و جامع هست؟چقدر میتونید زمان صرف توسعه هسته فریمورکتون کنید و آپدیت نگهش دارید؟در صورتی که بتونید به سوالات بالا جواب منطقی و قانع کننده بدید، شما احتیاج به فریمورک ندارید، اما اگر جواب منطقی برای سوالات بالا نداشتید حتما یک فریمورک رو برای پروژه پیش روتون انتخاب کنید!چقدر آماده استفاده از فریمورک هستید؟موضوع دیگه ای که خیلی مهمه اینه که از لحاظ فنی آمادگی یادگیری و استفاده از فریمورک رو دارید؟جواب این سوال برای پلتفرم ها و زبان های مختلف متفاوته اما بصورت کلی برای شروع کار و استفاده از فریمورک شما باید قبلا تجربه پیاده سازی چندین اپلیکیشن رو داشته باشید و با الگو های مرسوم و روند تولید نرم افزار کاملا آشنا باشید. بدونید چه چالش هایی توی کد نویسی بدون فریمورک وجود داره و سعی کنید خودتون اون چالش هارو برای یادگیری بیشتر رفع کنید. یک مثال از زبان پی اچ پی میزنم. توی پی اچ پی در صورتی که شما شی گرایی و ساختار ام وی سی رو بلد نباشید به هیچ وجه نمیتونید نحوه کار فریمورک هایی از جمله لاراول، سیمفونی، کدایگنایتر و سایر متوجه بشید. توی اکثر فریمورک های زبان های دیگه هم این قضیه صادقه. مثلا اگه شما شی گرایی توی جاوا رو بلدید نباشید نمیتونید ساختار و روند ساخت اپلیکیشن اندروید رو یاد بگیرید.تجربه شخصیمن چند سال پیش زمانی که میخواستم اولین پروژه تجاری که سفارش گرفته بودم رو بنویسم، اصلا درکی از فریمورک نداشتم پس شروع کردم از صفر نوشتن همه بخش های وبسایت و پنل ادمین. نه تمپلیت انجینی استفاده میکردم نه کوئری بیلدر (چه برسه به اُ آر اِم). درواقع هر کوئری که میخواستم بزنم رو دستی مینوشتم. همینطور برای خودم ساختار درست کرده بودم که مثلا هر ماژول رو جدا داشته باشم که توسعه ام راحت تر باشه. تا حدی کمکم کرد این ساختار اما بازم همه چیز تو هم و بدون انسجام بود. این تجربه شد که دفعه بعد هرچیزی که لازم دارم رو توی کلاس بنویسم.پروژه بعدیم رو شروع کردم به نوشتن، برای امنیت سیستم کلاسی داشتم که متدی هایی برای اسکیپ کردن ورودی و … داشت. کلاسی داشتم که شامل متد هایی برای ایجاد، حذف، نوشتن، خوندن از فایل و آپلود کردن عکس بود. همینطور یک کلاس هم نوشته بودم که کار با تاریخ رو راحت تر کنه برام.توی این پروژه با شی گرایی خیلی خوب آشنا شدم و نتیجش تکرار کمتر کد ها، خوانایی بیشتر کد ها، و توسعه راحت تر نسبت به سیستم قبلی بود. اما باز هم مشکلات زیادی داشتم. مثلا برای اینکه قابلیت جدید اضافه کنم احتیاج داشتم تغییری توی ساختار دیتابیس بدم که این یعنی مکافات! باید میرفتم از دیتابیس بک اپ میگرفتم که نکنه یوقت خراب کاری کنم، بعد فیلد جدید رو اضافه میکردم، کد های جدید رو اضافه میکردم و وقتی همه چی اوکی بود دوباره یه بک آپ دیگه میگرفتم میبردم روی هاست اپلود میکردم. این بک آپ گرفتن و ریستور کردنا وقتی تعدادشون بالا میرفت دیوونم میکرد! خلاصه با مشکلات کلی آشنا شدم و خودم رو آماده کار با فریمورک میدونستم.اول از همه فریمورک کد ایگنایتر استفاده کردم که قابلیت های کمتری نسبت به بقیه فریمورک ها داشت، اما وقتی فهمیدم فریمورک ها چطور کنترلر و مدل و ویو رو از هم جدا میکنن و روند کارشون چطوریه، اومدم سمت لاراول. دیدم هر مشکلی که توی کد نویسی از صفر وجود داشت با استفاده از لاراول رفع میشه! مثلا همون بخش آپدیت کردن ساختار دیتابیس با یک فایل مایگریشن انجام و برای اعمالش روی سرور فقط یه کامند باید زده میشد! به همین راحتی!نتیجه گیری نهاییاگه از اون دست افرادی هستید که اعتقاد دارن باید همه چیز رو از صفر نوشت یا به قول معروف چرخ رو از نو اختراع کرد وگرنه برنامه نویس نیستیم، باید بهتون بگم که سخت در اشتباهید. برنامه نویس کار درست برنامه نویسیه که از تمام امکانات موجود استفاده کنه، اگر نقصی توی ابزار هایی که براش فراهم شده وجود داشت بتونه اون نقص هارو رفع کنه و بزاره تمام دنیا از نسخه اصلاح شدش استفاده کنن. کاری که روی گیت هاب و پروژه های اوپن سورس انجام میشه.درحال حاضر تعداد شرکت هایی که تمایل به استفاده از فریمورک برای پروژه هاشون دارن روز به روز در حال افزایشه و از اون سمت، شرکت هایی که اعتقاد به اختراع دوباره چرخ دارن در حال انقراض هستن. پس بهتون پیشنهاد میکنم توی زمینه کاریتون حتما اول پیش نیاز های کار با فریمورک رو یاد بگیرید و بعد به سمت استفاده و یادگیری یک فریمورک برید تا از بازار کار و رقابت عقب نمونید!</description>
                <category>پول ریکوئست</category>
                <author>عرفان صحاف نژاد</author>
                <pubDate>Fri, 08 Sep 2017 15:18:49 +0430</pubDate>
            </item>
                    <item>
                <title>پنهان کردن هدرهای پیش فرض مرورگر در پرینت صفحه وب</title>
                <link>https://virgool.io/pullrequest/%D9%BE%D9%86%D9%87%D8%A7%D9%86-%DA%A9%D8%B1%D8%AF%D9%86-%D9%87%D8%AF%D8%B1%D9%87%D8%A7%DB%8C-%D9%BE%DB%8C%D8%B4-%D9%81%D8%B1%D8%B6-%D9%85%D8%B1%D9%88%D8%B1%DA%AF%D8%B1-%D8%AF%D8%B1-%D9%BE%D8%B1%DB%8C%D9%86%D8%AA-%D8%B5%D9%81%D8%AD%D9%87-%D9%88%D8%A8-spfatjzfpqz0</link>
                <description>ممکنه نیاز داشته باشید صفحه وب رو با جاوااسرکیپت پرینت کنید. خود این تسک کار ساده ایه و با یه دستوری ساده مثل window.print() انجام پذیره. اما یه مشکلی توی این پرینت وجود داره و اونم اینه که تنظیمات پیش فرض مرورگرها عنوان صفحه و تاریخ روز رو بالای صفحه ای که میخواید پرینت بگیرید وارد میکنه، مثل عکس زیر:اگر بخوایم اونو پنهان کنیم کافیه دستور زیر رو توی فایل سی اس اس پروژه قرار بدید تا از شر اون هدر خلاص بشید:@media print {
  @page {
    size: auto;
    margin: 0mm;
  }
}یادتون نرهاون قسمت @media print رو از قلم نندازید. این کد توی سی اس اس اصلی ممکنه عوارض جانبی توی مرورگرهای قدیمی تر به بار بیاره و بهتره فقط توی حالت پرینت اعمال بشه.برای اطلاعات بیشتر در مورد سلکتور @page میتونین به لینک زیر مراجعه کنید.MDN @page guide</description>
                <category>پول ریکوئست</category>
                <author>فرزاد یوسف زاده</author>
                <pubDate>Sun, 16 Jul 2017 15:17:27 +0430</pubDate>
            </item>
            </channel>
</rss>