<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>پست‌های انتشارات کافه بازار</title>
        <link>https://tech.cafebazaar.ir/feed</link>
        <description>این وبلاگ را بچه‌های تیم‌های فنی کافه بازار، دیوار و بلد و ستون پایه‌گذاری کردند. بعد از مستقل شدن این شرکت‌ها از یکدیگر، جایی‌ است برای به اشترا‌ک‌گذاری تجربه‌ها و چالش‌های تیم فنی «بازار».
https://cafebazaar.ir/jobs</description>
        <language>fa</language>
        <pubDate>2026-06-15 19:59:54</pubDate>
        <image>
            <url>https://files.virgool.io/upload/publication/bfsiexfa8bbb/kiqthx.png</url>
            <title>کافه بازار</title>
            <link>https://tech.cafebazaar.ir</link>
        </image>

                    <item>
                <title>استفاده از هوش مصنوعی و مدل‌های زبانی بزرگ (LLM‌ها) در بازار</title>
                <link>https://tech.cafebazaar.ir/استفاده-از-هوش-مصنوعی-و-مدل-های-زبانی-بزرگ-llm-ها-در-بازار-pl98aa0ylmk0</link>
                <description> شاید تا همین چند سال پیش، شنیدن کلمه «هوش مصنوعی» ما را یاد فیلم‌های تخیلی می‌انداخت؛ ولی امروز دیگر همه می‌دانیم که هوش مصنوعی تبدیل به بخشی جدایی‌ناپذیر از زندگی روزمره و کاری ما شده است. در بازار اما این موضوع چیز تازه‌ای نیست. ما از سال‌ها پیش داستان خودمان را در بهره‌گیری از هوش مصنوعی آغاز کرده بودیم.موج تحول‌آفرین هوش مصنوعی در بازار آغاز سفری نوآورانه با مدل‌های زبانیداستان ما تقریباً ۱۰ سال پیش شروع شد؛ زمانی که از مدل‌های ساده و ابتدایی متن‌کاوی استفاده کردیم تا بتوانیم نظرات کاربرانمان را بهتر مدیریت کنیم و تصمیم بگیریم کدام نظر قابل انتشار است و کدام باید بازبینی شود. شاید امروزه این موضوع ابتدایی به نظر برسد، اما در آن سال‌ها کاری کاملاً نوآورانه و نوپا محسوب می‌شد. این تجربه ساده اولین قدم ما را در مسیر طولانی به‌کارگیری هوش مصنوعی شکل داد و کمک کرد تا خیلی زود نسبت به اهمیت آن و بهبود تجربه کاربران خودآگاه شویم. با گذشت زمان و پیشرفت تکنولوژی، دامنه استفاده ما از هوش مصنوعی گسترش پیدا کرد. در میدان رقابتی تبلیغات دیجیتال، ما برای بهینه‌تر کردن قیمت‌گذاری‌ها و تصمیم‌گیری برای نمایش تبلیغات از تکنولوژی‌های پیشرفته‌تر و دقیق‌تری بهره بردیم. همه این تجربیات باعث شد که ما در زمان ظهور مدل‌های زبانی بزرگ (یا همان LLMها) کاملاً آماده پذیرش آنها باشیم.مدل‌های زبانی بزرگ، مثل ChatGPT، تحول بزرگی بودند که در سال‌های اخیر واقعاً دنیا را به هیجان آوردند و بازار نیز از آن‌ها استقبال کرد. استفاده ما از این مدل‌ها با استراتژی مشخصی آغاز شد و هدفمان از روز اول کمک به تیم‌ها و ساده‌تر کردن کارهای روزمره‌شان بوده است.رابط وب داخلی، دسترسی آسان برای همه همکاراناولین گام ما در این مسیر ارائه یک رابط کاربری کاربردی تحت وب (WEB UI) برای همکاران شرکت بود. چرا؟ چون می‌خواستیم همه تیم‌های بازار بتوانند به راحتی با مدل‌های زبانی بزرگ تعامل کنند و بدون نیاز به دانش تخصصی عمیق در این حوزه، از مزایای این مدل‌ها بهره ببرند. استقبال خوب تیم‌ها از این رابط وب به ما ثابت کرد که این ابزار ساده و کاربردی تجربه متفاوتی را برای همکارانمان خلق کرده است.کاهش چشمگیر زمان بررسی نظرات کاربرانیکی از مهم‌ترین دستاوردهای استفاده از هوش مصنوعی در بازار تغییر چشمگیر سیستم بررسی نظرات کاربران بوده است. در گذشته به علت عدم دقت بالای مدل بررسی نظرات وقت و انرژی قابل توجه‌ای را از تیم‌های بازار میگرفت. اما امروز به لطف LLMها و مدل‌های پیشرفته‌تر، ما توانسته‌ایم میانگین زمان بررسی نظرات کاربران را از تقریباً ۲ روز به کمتر از ۱۵ دقیقه برسانیم؛ یعنی یک تغییر واقعی و ملموس برای کاربران بازار، امروز می‌توانیم از ظرفیت فکری همکاران در بخش‌های دیگر شرکت در جهت توسعه هرچه بهتر یک محصول شایسته برای کاربرانمان استفاده کنیم.خودکارسازی کدنویسی با عامل‌های هوش مصنوعیدر راستای هدف خود مبنی بر ساده‌سازی فرایند توسعه نرم‌افزار و افزایش سرعت و کیفیت کدنویسی تیم‌های فنی، طی ماه‌های اخیر به بررسی و آزمایش ابزارهای گوناگون پرداختیم و در نهایت، کلود کد (Claude Code) را برگزیدیم. این ابزار که به‌سادگی بر روی ترمینال و محیط‌های توسعه مختلف نصب می‌شود، به اعضای تیم فنی کمک می‌کند تا کدنویسی سریع‌تری داشته باشند، تست‌های نرم‌افزاری دقیق‌تری بنویسند و کیفیت کدهای خود را بهبود بخشند. به‌کارگیری این ابزار، چابکی و تمرکز تیم‌ها را بر روی حل مسائل افزایش داده و به ارائه راه‌حل‌های دقیق‌تر و کارآمدتر منجر شده است.بازبینی کد با چشمانی تیزبینیکی دیگر از جنبه‌های حیاتی در تولید محصولات فناورانه، فرآیند «بازبینی کد» (Code Review) است. خوشبختانه، هوش مصنوعی در این زمینه نیز به یاری ما آمده است. در حال حاضر، چندین ابزار هوشمند بازبینی کد در حال آزمایش هستند تا نقش یک دستیار بازبین را ایفا کنند. این ابزارها قادرند خطاهای احتمالی را که به‌سادگی با ابزارهای Linter قابل شناسایی نیستند، تشخیص دهند و موضوعات عمیق‌تری مانند سازگاری با نسخه‌های پیشین (Backward Compatibility) را نیز در نظر بگیرند. به این ترتیب، توسعه‌دهندگان می‌توانند ضمن صرفه‌جویی در زمان و هزینه نگهداری، کیفیت نهایی محصولات را به شکل چشمگیری افزایش دهند.نگهداری بهتر مستندات پروژه‌هانگهداری و به‌روزرسانی مستندات، یکی از چالش‌های همیشگی در سازمان‌های پویا است؛ چالشی که با جابه‌جایی اعضا یا ورود نیروهای جدید به تیم، اهمیت آن دوچندان می‌شود. اکنون، مدل‌ها و ابزارهای هوش مصنوعی با ارائه راهکاری نوین به کمک ما آمده‌اند. این ابزارها می‌توانند هم‌زمان با توسعه کد، مستندات فنی را نیز به‌صورت خودکار تولید، نگهداری و به‌روز کنند. این رویکرد، ضمن کاهش چشمگیر چالش‌های فرآیند مستندسازی به افزایش کارایی و یکپارچگی تیم‌ها کمک شایانی خواهد کرد.نکته قابل توجه این است که اگر قصد دارید از عامل‌های هوش مصنوعی در پروژه‌های خود استفاده کنید، داشتن مستندات کامل و دقیق یک مزیت بزرگ محسوب می‌شود؛ زیرا این ابزارها با درک بهتر از زمینه و نیازهای پروژه، کدی استانداردتر و سازگارتر با ساختار موجود پروژه تولید خواهند کرد. شناخت عمیق‌تر نیازهای کاربران با تحلیل هوشمند یکی از اقدامات راهبردی و آینده‌نگرانه ما، بهره‌گیری از فناوری هوش مصنوعی برای تحلیل عمیق رفتار و نیازمندی‌های کاربران است. مدل‌های هوشمند این قابلیت را فراهم می‌آورند تا با دقت بالا، میزان رضایت کاربران از بخش‌های مختلف محصول، نقاط ضعف و چالش‌های موجود در تجربه کاربری، و همچنین انتظارات و نیازهای واقعی آن‌ها را شناسایی کنیم. تجزیه و تحلیل داده‌های کاربری با استفاده از مدل های زبانی بزرگ (LLM‌ها)، امکان تدوین استراتژی‌های هدفمند و مبتنی بر شواهد را فراهم می‌کند تا بتوانیم به شکل مؤثرتری پاسخگوی نیازهای حقیقی کاربران باشیم. در حال حاضر، این ابزار در مرحله توسعه قرار دارد و به‌زودی آماده بهره‌برداری خواهد بود. سفر به‌سوی افق‌های جدید خوشحالیم قدم در این مسیر نوآورانه گذاشته‌ایم و افتخار می‌کنیم که استفاده از هوش مصنوعی بخشی از فرهنگ بازار شده است. نتایج ارزشمندی همچون کاهش زمان بررسی نظرات، افزایش بهره‌وری تیم‌ها و بهبود کیفیت کدنویسی تنها بخشی از دستاوردهای ما در این مسیر است.از شما دعوت می‌کنیم تا در این مسیر همراه ما باشید و تجربیات ارزشمند خود را با ما در میان بگذارید. امید داریم بتوانیم در کنار یکدیگر، در این راه جذاب و نوآورانه رشد کرده و تجربه‌ای بهتر برای همه کاربران خلق کنیم.</description>
                <category>کافه بازار</category>
                <author>MAHDI AKBARI ZARKESH</author>
                <pubDate>Sat, 09 Aug 2025 13:35:22 +0330</pubDate>
            </item>
                    <item>
                <title>کامپوز و کافه بازار</title>
                <link>https://tech.cafebazaar.ir/compose-cafebazaar-uwayjn17wyry</link>
                <description>اعضای چپتر اندروید کافه‌بازار
تقریبا ۲ سال پیش بودش که اولین نسخه استیبل کامپوز ریلیز شد (28 July 2021) و قبل از اون هم نسخه‌های آلفا از کامپوز در دسترس بودن و تو چپتر اندروید کافه بازار هم بحث‌های زیادی راجع به کامپوز بود و هست، تو جلسه‌های لول آپ چپتر راجع بهش صحبت میکردیم و حتی بچه‌های تیم یه سری سمپل‌های کوچک ازش زده بودن، اما تصمیم گیری راجع به مهاجرت به کامپوز تو اپلیکیشن کافه‌بازار به این سادگیا نبود و نیازمند بررسی‌های خیلی بیشتری بود؛ حدود ۴۳ تا فیچر ماژول و کلی کاستوم ویو کافی بود تا این درک رو داشته باشیم که پروژه خیلی بزرگ هستش و مهاجرت به این تکنولوژی جدید نیازمند برنامه ریزی دقیقی هستش و قاعدتا نیازمند زمان و انرژی برای اعضای تیم جهت یادگیری، همچنین تیم‌های محصولی باید قانع میشدن که چرا باید اندروید دولوپرهای تیمشون وقتشون رو برای این تکنولوژی صرف کنن که آورده‌ی محصولی‌ای نداره اما خوشبختانه چنین فرهنگی در شرکت و تیم‌ها بسیار زیاد هستش و علاقه بسیار زیاد به تکنولوژی‌های جدید در تمام افراد دیده میشه و برای کتابخانه‌ها و تکنولوژی‌های جدید دیگه هم که بهشون مهاجرت کردیم هیچوقت از این جنس مشکلات نداشتیم.شروع این اتفاق با یه محصول جدید تو بازار شروع شد به اسم بازارچه. بازارچه یکی از فیچر‌های اپلیکیشن کافه‌بازار هستش که به کمکش میتونید ووچر کد و گیفت کارت تهیه کنین. بهترین نقطه برای شروع کامپوز بود چون تنها فیچر ماژولی بود که اکتیویتی خودشو داشت و هیچ وابستگی‌‌ای به بازار نداشت. بازارچه - بازار‌منبعد از بازارچه کلی تجربیات جدید پیدا کردیم و چندتا محصول دیگه رو هم با کامپوز توسعه دادیم که در آینده احتمالا بیشتر راجع بهشون بشنویم و در حال حاضر در حال مهاجرت به کامپوز در اپلیکیشن بازار هستیم. امروز قصد داریم یه سری تجربیاتی که در این مسیر داشتیم رو باهم مرور کنیم و یه سری قوانین و تجربیات داخلی که تو کافه‌بازار برای کامپوز داریم و سعی میکنیم حواسمون بهش باشه رو باهم بررسی کنیم.ریکامپوزیشنریکامپوزیشن کنترل نشده اتفاقی هستش که میتونه منجر به تجربه کاربری بد بشه برای کاربرانمون, در تعداد کم شاید چیزی حس نشه اما همواره باید بررسیش کنیم و تو app inspection حواسمون باشه که جایی که باید اسکیپ بشه به اشتباه ریکامپوز اتفاق نیفته، به این نکته توجه داشته باشیم که برای اسکیپ شدن باید تمام پارامترهای ورودی stable باشن و اینکه هیچ تغییری هم نکرده باشن (خیلی مهمه که بدونیم برای مقایسه و تشخیص تغییر از equal استفاده میشه و برای مثال دیتاکلاس‌ها equal رو پیاده سازی کردن و برای کلاس‌های دیگه باید خودمون پیاده سازی کنیم)، در غیر این صورت شاهد لگ و کند شدن اپلیکیشن و اتفاق‌های عجیب‌تر هستیم، در ادامه راجع به ریکامپوزیشن بیشتر صحبت میکنیم.سعی کنیم تا جای ممکن Stable باشیم!کامپوزبل فانکشن‌ها باهوش طراحی شدن؛ به طوری که اگر ورودی‌هاشون تغییری نکنند اسکیپ میشن. اینکه به چه شکلی این اتفاق میفته مبحث جالبی هستش که شاید در آینده دقیق‌تر راجع بهش صحبت کردیم, به طور کلی این اتفاق خوبیه و باعث میشه پرفورمنس خیلی بهتر باشه، اما برای اینکه اسکیپ شدن کامپوزبل فانکشن‌هامون به درستی اتفاق بیفتند باید حواسمون به این موارد باشه:تمام پارامتر‌های ورودی Stable باشن (پریمیتیو تایپ‌ها مثل Boolean, Int, Long استیبل هستن)برای دیتاکلاس‌هامون و هرجا که میتونیم از Immutable@ استفاده کنیم و اگه نیاز هست از Stable@ استفاده کنیم تا کامپایلر بتونه متوجه تغییرات بشهاز کالکشن‌های UnStable استفاده نکنیم, List و Map و Set تو کاتلین استیبل نیستن (با اینکه در باطن هستن, اما کامپایلر نمیتونه متوجهش بشه) به جاش از Kotlinx Immutable Collections استفاده کنیم, یعنی برای مثال به‌جای استفاده از List از ImmutableList استفاده کنیم.حواسمون به لمبدا‌های unStable باشه, در ادامه بیشتر راجع بهشون صحبت میکنیم.لمبدا‌‌های UnSatble!کد زیر رو در نظر بگیرین:@Composable
fun RecompositionWithIssueTest(communicator: Communicator) {
    BazaarComposeLambdaTest(
        name = &amp;quottest&amp;quot,
        onNameClick = { communicator.onNameClick() },
    )
}

@Composable
fun BazaarComposeLambdaTest(name: String, onNameClick: () -&gt; Unit) {
    Text(modifier = Modifier.clickable { onNameClick.invoke() }, text = name)
}انتظاری که از BazaarComposeLambdaTest میره اینه  که اگه با پارامتر name یکسان کال بشه اسکیپ میشه, اما این اتفاق نمیفته, چون هربار که این فانکشن کال میشه یه anonymous class برای اون لمبدا ساخته میشه که ممکنه Stable نباشه، کلاسی که برای این لمبدا ساخته میشه تو کانستراکتورش پارامتر‌هایی که اون لمبدا بهشون دسترسی داره رو دریافت میکنه، تو این مثال میشه Communicator و خب این کلاس با این فرض که stable نیست، لمبدای ما هم استیبل نیستش در نتیجه.// communicator is unstable, so OnNameClickLambda is unstable too
class OnNameClickLambda(val communicator: Communicator) {
    operator fun invoke() {
        communicator.onNameClick()
    }
}فقط توجه داشته باشیم که فانکشن‌های استاتیک و لمبدا‌هایی که از فیلدهای استیبل استفاده میکنن مشکلی ندارن، و برای حل اون لمبدا‌هایی که مشکل ریکامپوزیشن دارن چند راه داریم که در ادامه به آنها اشاره خواهیم کرد:۱- لمبداها رو remember کنیمval onNameClick = remember(communicator) { { communicator.onNameClick() } }۲- از Method References استفاده کنیم@Composable
fun RecompositionWithIssueTest(communicator: Communicator) {
    BazaarComposeLambdaTest(
        name = &amp;quottest&amp;quot,
        onNameClick = communicator::onNameClick()
    )
}همواره Unstable هارو بررسی کنیمبرای اینکه متوجه بشیم کامپوزبل فانکشن‌هامون چقدر قابل اسکیپ شدن هستن و اگه جایی رو اشتباه کردیم مشکل از چی بوده،  Compose Compiler Reports خیلی به کارمون میاد و گزارش‌هایی به شکل html برامون درست میکنه که تمام بررسی‌ها رو انجام داده و کامپوزبل فانکشن‌هایی که قابل اسکیپ شدن نیستند و پارامتر‌های unstable رو مشخص کرده؛ در تصویر پایین یه نمونشو میتونیم مشاهده کنیم.با اینکه list در واقع immutable هستش اما stable نیستاستفاده از key در lazy layout هاوقتی از lazy layout ها (LazyColumn و LazyRow و ...) استفاده میکنیم حتما حواسمون باشه که برای item ها از key استفاده کنیم, key بهمون کمک میکنه که از ریکامپوزیشن بیهوده وقتی یه آیتم اضافه میشه یا حذف میشه یا جابجا میشه تو لیست جلوگیری کنیم و فقط اون آیتمی ریکامپوزیشن براش اتفاق بیفته که تغییر کرده، روش پیشنهادی ما برای استفاده از key داخل آیتم‌هامون، یه اینترفیس سادست :interface ComposeItem {

   @Composable
    fun ComposeView()

    fun getItemId(metadata: String = &amp;quot&amp;quot): String
}و برای استفاده ازش، تو آیتم‌هایی که قراره تو لیست‌ها نمایش داده بشن پیاده سازی میشه:@Immutable
data class HeaderInfoItem(
    private val title: String,
    private val body: String,
) : ComposeItem {
    override fun getItemId(metadata: String): String = &amp;quot$title/$body/$metadata&amp;quot

}اینکه key چی باشه رو آیتم‌های صفحه با توجه به پارامتر‌هایی که دارن راجع بهش تصمیم میگیرن و metaData هم برای حالتی هستش که بخوایم از بیرون وقتی getItemId رو صدا میزنیم بهش پارامتری رو پاس بدیم (برای مثال میتونیم پوزیشن اون آیتم تو لیست رو بدیم) که در حالت دیفالت خالی هستش, این پیاده سازی بهمون کمک میکنه که وقتی یه آیتم واقعا تغییر کرد و نیاز به ریکامپوزیشن داشت براساس لاجیک آیتم‌های لیست و دیتایی که دارن، key تغییر کنه و ریکامپوزیشن اتفاق بیفته.@Composable
private fun Items(items: ImmutableList&lt;ComposeItem&gt;) {
    LazyColumn() {
        items(
            count = items.size,
            key = { index -&gt; items[index].getItemId() },
        ) { index -&gt;
            val item = items[index]
            item.ComposeView()
        }
    }
}استفاده درست از CompositonLocalیکی از قابلیت‌های خیلی خوب CompositonLocal ها هستن که فلسفه کلیشون دسترسی ساده‌تر بدون نیاز به دریافت اونها به عنوان پارامتر هستش، متریال این کارو خیلی زیاد و زیبا انجام داده به عنوان مثال وقتی از Color ها استفاده میکنیم.MaterialTheme.colorScheme.primaryاما باید توجه داشته باشیم که از CompositionLocal ها  درست و درجای مناسب استفاده کنیم، به عنوان مثال باید توجه داشته باشیم که compositionLocal ها برای تمام فرزندها باید قابل استفاده باشد و به شکلی طراحی نشده باشند که کاراییشون فقط برای تعدادی از کامپوزبل فانکشن‌های فرزند باشد، همچنین باید توجه داشته باشیم که استفاده زیاد و اشتباه از CompositonLocal‌ ها باعث میشه خوانایی کد پایین بیاد و دیباگ کردن سخت‌تر بشه، در این قسمت از داکیومنت‌های رسمی به نکات بیشتری راجع به این موضوع اشاره شده است.استیت‌ها و hoistingدر ابتدا یه مرور مختصر راجع به Stateless و Stateful داشته باشیم، به طور کلی اگر کامپوزبل فانکشن‌هامون استیتی رو داخل خودشون نگه دارن و تغییرش بدن یا به طور کلی تر از remember استفاده کرده باشن داخل خودشون، Stateful به حساب میان، در مواری که نیازی به کنترل از لایه‌های بالاتر نیست کاربردی هستن اما نباید فراموش کنیم که نگه داشتن استیت داخل کامپوزبل فانکشن‌ها باعث سخت شدن تستشون میشه و خواناییشونو کاهش میده، در برابر Stateful ها ما Stateless هارو داریم که هیچ استیتی داخل خودشون نگه نمیدارن و خوانایی خوبی دارن و همچنین تستشون کار ساده‌تری هست.سوالی که به وجود میاد این هستش که چطور Stateless باشیم؟ جوابش خیلی سادست، با استفاده از state hoisting و به طور خلاصه این مفهوم که تا حد ممکن استیت‌ها و کال‌بک‌ها رو از لایه‌های بالاتر در ورودی کامپوزبل فانکشن‌هامون بگیریم.یه سری نکات دیگه در رابطه با state ها وجود دارن که در ادامه به آنها میپردازیم:از state فقط تو کامپوزبل فانکشن‌هامون استفاده کنیم و تو ویومدل از فلو‌ها استفاده کینم و از collectAsStateWithLifecycle استفاده کنیم برای تبدیل فلو به استیتبه کامپوزبل فانکشن‌هامون کوچک ترین چیزی که نیاز دارن رو پاس بدیم، به عنوان مثال اگه به یک لیست برای نمایش نیاز دارند، viewModel رو به عنوان پارامتر ورودی دریافت نکنیم، این کار باعث میشه راحت‌تر بتونیم اونها رو تو preview نمایش بدیم و تستشون ساده‌تر باشه و همچنین reusable باشناز state به عنوان ورودی کامپوزبل فانکشن‌ها استفاده نکینم تا حد ممکنوقتی از mutableStateOf داخل کامپوزبل فانکشن‌هامون استفاده میکنیم حواسمون باشه که اونها رو remember کنیم، در غیر این صورت با ریکامپوز شدن، استیتمون رو از دست میدیملگ‌ها و دراپ فریم‌ها رو دنبال کنید!لگ زدن و دراپ فریم‌ها رو ممکنه همچنان شاهد باشیم! نکته‌ای که مهمه اینه که بتونیم پیداشون کنیم و یه آماری ازشون داشته باشیم و در نهایت فیکسشون کنیم، استفاده از پروفایلر میتونه یه گزینه خوب باشه برای تشخیص و رفعشون اما یه گزینه دیگه‌ای که وجود داره برامون که میتونیم باهاش پرفورمنس اپلیکیشن رو بررسی کنیم و اون استفاده از کتابخانه JankStats هستش، به کمک این کتابخانه میتونیم jank هارو پیدا کنیم و جزییات نسبتا دقیقی از اینکه کجا دارن اتفاق میفتن داشته باشیم و حتی میتونیم با یه سری از ابزارهای آنالیتیکس مثل سنتری ترکیبشون کنیم و تمام لگ‌ها و jank هایی که کاربرانمون تجربه میکنن رو در اختیار داشته باشیم و رفعشون کنیم، چیزی که در اختیارمون میزاره در نهایت یه همچین کال بکی هست:fun providesOnFrameListener(): JankStats.OnFrameListener {
    return JankStats.OnFrameListener { frameData -&gt;
        if (frameData.isJank) {
            // A real app could do something more interesting, like writing the info to local storage and later on report it.
            Log.v(&amp;quotJank&amp;quot, frameData.toString())
        }
    }
}لگ زدن و کند بودن اپلیکیشن در نسخه‌های دیباگاگه تمام موارد بالا رو رعایت کردین باید بگم که اصلا جای نگرانی وجود نداره و تو نسخه دیباگ طبیعی هست که اپلیکیشن مقداری کند باشه و به اصطلاح لگ رو تجربه کنیم، نسخه ریلیز رو تست کنید و اینکه حتما از R8 استفاده کنید، همچنین اگه مشکلی وجود داشته باشه JankStats میتونه اطلاعات دقیق‌تری از اون رو بهمون بده.سخن پایانیتو این مقاله سعی کردیم توضیح مختصری از تجربه‌ کامپوز در چپتر اندروید کافه بازار بدیم و مروری کنیم رو تعدادی از قواعد کلی‌ و مهمی که سعی میکنیم رعایتشون کنیم و یک سری از تجربیاتی که داشتیم، این فرآیند همچنان در حال جلو رفتن هستش و حتما سعی میکنیم تجربیات جدیدمون رو باهاتون به اشتراک بزاریم. امیدواریم که این مقاله براتون مفید واقع شده باشه، همچنین اگه نیاز به پروژه‌ای داشتید که بتونید موارد بالا رو تمرین کنید میتونید به MusicAppComposeUI  و Pheedly دوتا از پروژه‌های حمیدرضا صحرایی و PayPal از سعید مرادی یه نگاهی بندازید.</description>
                <category>کافه بازار</category>
                <author>Hamid Mahmoodi</author>
                <pubDate>Wed, 19 Apr 2023 16:14:10 +0330</pubDate>
            </item>
                    <item>
                <title>ژوپیتر چیست؟</title>
                <link>https://tech.cafebazaar.ir/ژوپیتر-qizkb0p4c9xd</link>
                <description>در کافه بازار مهندسان علوم داده و تحلیل‌گران داده در تیم‌های مختلف، تلاش می‌کنند تا با انجام محاسبات تحلیلی بر روی داده‌های محصولی به رشد محصول کمک کنند. آن‌‌ها برای انجام این محاسبات، به زیرساخت‌هایی مانند GPU یا CPU نیاز دارند و در بعضی موارد نیازمند استفاده از ابزارهایی مانند Spark و Flink هستند. ما در تیم زیرساخت کافه بازار برای اینکه به این نیاز پاسخ بدهیم، باید ابزار توسعه کاری ارائه می‌دادیم که قادر به رفع تمامی این نیازها به صورت همزمان باشد. تیم ما از ۵ نفر تشکیل شده است که ماموریت ما، ایجاد ابزارها و روش‌ها، استقرار و نظارت بر آن‌ها در کافه بازار به منظور تسهیل توسعه و نگه‌داری و کاهش هزینه تولید محصول و تضمین پایداری آن می‌باشد. اعضای تیم زیرساختدر این پست می‌خواهیم راجع به یکی از ابزارهای توسعه کار، ژوپیتر(Jupyter) ، صحبت کنیم که مسئولیت توسعه و نگهداری این ابزار با تیم زیرساخت کافه بازار است.مهندسان علوم داده و تحلیل‌گران می‌توانند با استفاده از این ابزار، محاسبات داده‌ای خود را با زبان‌ها و زیرساخت‌های مختلف انجام دهند، بدون آن‌که نیازی به راه‌اندازی و نگهداری ابزاری در سطح تیم خود داشته باشند.در ابتدا چرایی انتخاب این ابزار را بیان می‌کنیم و در ادامه معماری و ساختار آن را بررسی خواهیم کرد.چرا ژوپیتر؟مهمترین سوالی که قرار است در این بخش به آن پاسخ داده شود، این است که چرا تصمیم گرفتیم از ژوپیتر استفاده کنیم و نحوه راه‌اندازی آن به چه صورت است؟ژوپیتر اولین ابزار توسعه کار در کافه بازار نیست و قبل از ژوپیتر، از زپلین (Zeppelin) به عنوان ابزار توسعه کار استفاده می‌شد، که شامل مفسر(interpreter)های اسپارکی به زبان‌های پایتون(Python) و اسکالا (Scala) بود. معماری زپلین به شکلی بود که فقط می‌توانستیم آن را بر روی یک ماشین راه‌اندازی کنیم و مقیاس‌پذیر نبود. در نتیجه با افزایش درخواست‌های کاربران به شدت ناپایدار و به‌طور کامل از دسترس خارج می‌شد. برای حل این مشکل تغییرات زیادی در معماری آن اعمال کردیم تا قابل اجرا بر روی کوبرنتیز باشد و بتوانیم مقیاس آن را به راحتی، بدون نیاز به ماشین دیگری افزایش دهیم. در ابتدا که تعداد کاربران محدود بود، زپلین با ساختار جدید، به خوبی ارائه خدمت می‌کرد، اما پس از افزایش تعداد کاربران، با مشکلات زیادی از جمله دسترس ناپذیری مواجه شد و حتی با افزایش مقیاس آن بر روی کوبرنتیز نیز، این مشکلات برطرف نشد و عملا کاربران نمی‌توانستند با زپلین کار کنند. در نتیجه می‌بایست راه حلی سریع در جهت حل این مشکل ارائه می‌دادیم و نیاز به یک ابزاری پایدار و مقیاس‌پذیر داشتیم. با بررسی‌های انجام شده، زپلین نمی‌توانست نیازهای ما را در زمان رشد و گسترش کاربر و محصول پاسخ دهد، چون حتی با تغییر در ساختار آن و اجرای آن بر روی کوبرنتیز همچنان مشکلات قبلی وجود داشت و با افزایش تعداد درخواست‌ها از دسترس خارج می‌شد. به عبارت دیگر تنها در تعداد درخواست‌ها و کاربران محدود قادر به ارائه سرویس بود. در نهایت با بررسی‌ راه‌حل‌های موجود، تصمیم گرفتیم به ژوپیتر مهاجرت کنیم. ژوپیتر ابزاری است که در صورت پیاده‌سازی درست، بسیار پایدار و به راحتی مقیاس پذیر است.برای راه‌اندازی ژوپیتر دو گزینه وجود داشت. در گزینه اول هر تیم به صورت مجزا باید ژوپیتر خود را راه‌اندازی می‌کرد و توسعه می‌داد. در گزینه دوم باید یک ژوپیتر مرکزی ایجاد می‌شد که می‌توانست به تمامی نیازهای مختلف تیم‌ها هم‌زمان پاسخ دهد. دو نکته وجود داشت:۱- هزینه راه‌اندازی ژوپیتر توسط هر تیم چه از لحاظ مالی و چه از لحاظ انسانی بیشتر از داشتن ابزاری متمرکز بود که همه تیم‌ها بتوانند از آن استفاده کنند.۲- در صورت راه‌اندازی ژوپیتر به صورت مرکزی تیم‌ها می‌توانستند از کل توان پردازشی موجود استفاده کنند و اگر این ابزار به صورت تیمی توسعه می‌یافت هر تیم فقط محدود به استفاده از منابع تیم خود بود.با توجه به این دو نکته، تصمیم گرفتیم ابزاری ارائه دهیم که بتواند به تمام نیازهای متخصصان داده در تیم‌های مختلف، هم‌زمان پاسخگو باشد. با ساخت چنین ابزاری، تیم‌ها دیگر درگیر نگهداری و توسعه آن نبودند و تمرکزشان بر روی ماموریت‌های خودشان بود. در نتیجه با ارائه این راه حل در تمامی هزینه‌ها اعم از هزینه‌های مالی و انسانی صرفه‌جویی کردیم.معماری ژوپیتر چگونه است؟همانطور که اشاره کردیم برای راه‌اندازی ژوپیتر باید طوری عمل می‌کردیم که از طریق آن بتوانیم به زیرساخت‌های موجودمان با زبان‌ها و ابزارهای مختلف دسترسی پیدا کنیم. برای هر کاربر در زمان شروع کار با ژوپیتر یک سرور اختصاص می‌یابد که با کاربر در تعامل است و درخواست‌های کاربر را مدیریت می‌کند. کاربران پس از انتخاب کرنل مورد نظر، می‌توانند محاسبات خود را با زبان انتخابی و بر روی زیرساخت انتخابی اجرا کنند.برای مدیریت دسترسی‌ها و احراز هویت کاربر چالش‌هایی وجود داشت، از جمله این‌که بعد از احراز هویت، کاربر بتواند با استفاده از یک ارتباط امن با سرور خود در تعامل باشد از سوی دیگر می‌بایستی این ارتباطات با سرور پایدار بوده و کاربر به راحتی و بدون قطعی ارتباطاتش کدهای خود را اجرا کند. در ابتدا از ingress خود بازار برای این قسمت استفاده کردیم پس از مدتی کاربران از ریستارت شدن ارتباطات خود شکایت می‌کردند پس از بررسی‌های انجام شده به این نکته پی بردیم که در صورت هر گونه عملیات بر روی ingress بازار ارتباطات کاربران با ژوپیتر نیز دچار اختلال می‌شد، در نتیجه برآن شدیم تا برای حل این چالش‌ها از یک nginx استفاده کنیم که مستقل از ingress خود بازار می‌باشد و درخواست‌های کاربران را به پروکسی ژوپیتر ارسال می‌کند و این ارتباطات را مدیریت می‌کند.چالش دیگری که با آن مواجه بودیم طریقه نگهداری کدهای کاربران تحت ژوپیتر نوتبوک بود, برای این قسمت از PVC کوبرنتیز استفاده کرده‌ایم که هنگام ایجاد سرور برای کاربر این PVC ها به آن متصل می‌شوند. همچنین در هر لحظه که کاربر در حال کدزنی است از نوتبوک‌هایش به فاصله زمانی مشخص بک‌آپ گرفته می‌شود تا در صورتی که ارتباطاتش دچار مشکل شد، نوت‌بوکهایش از بین نروند.شکل زیر معماری ژوپیتر کافه بازار را نمایش می‌دهد، که شامل دو قسمت اصلی کوبرنتیز و DockerSwarm می‌باشد. در ادامه هر قسمت را با جزییات بیشتری بررسی خواهیم کرد.معماری ژوپیتر
بخش Kubernetes:این قسمت واحدهایی را که کاربر با آن درگیر است مدیریت می‌کند که مهم‌ترین آن مدیریت هاب (Hub) است. از هاب برای پشتیبانی چند کاربره بودن ژوپیتر و ساخت سرور استفاده می‌شود. هر کاربر برای تعامل و اجرای کدهای خود نیازمند این است تا یک سرور ژوپیتر در اختیار داشته باشد. مراحل زیر برای ساخت سرور کاربر، طی می‌شود:کاربر درخواست خود را به ژوپیتر برای ایجاد سرور ارسال می‌کند.ین درخواست به وسیله یک Nginx ای که بر روی یک ماشین مجازی قرار دارد مدیریت می‌شود و آن به یک پروکسی(Proxy) که تمامی درخواست‌ها را به سمت هاب ارسال می‌کند متصل است. پس از آن‌که درخواست‌ها به قسمت هاب ارسال شد، هاب هویت کاربر را بررسی می‌کند و پس از احراز آن یک سرور برای کاربر بر روی کوبرنتیز ایجاد می‌کند و Notebook های قبلی کاربر که بر روی PVC کوبرنتیز قرار داشتند، به این سرور متصل می‌شوند. در آخر پروکسی به سرور کاربر متصل شده و تمامی درخواست‌های کاربر را به سرور او ارسال می‌کند و منتظر پاسخ می‌ماند.تا این قسمت کاربر به ژوپیتر متصل شده است، اما امکان اجرای هیچ کدی برای او وجود ندارد. در ادامه نحوه نصب و اجرای Gateway که مسئول ایجاد کرنل‌ها و برنامه‌های کاربر می‌باشد را شرح می‌دهیم.بخش DockerSwarm:این بخش متشکل از چند ماشین مجازی و فیزیکی است که برخی از آن‌ها دارای GPU می‌باشند. این کلاستر وظیفه محاسبه و اجرای کدهای کاربران را بر عهده دارد که با استفاده از یک GatewayProxy با سرور کاربران در تعامل است.برای اجرای کدهای کاربر، پس از ساخت سرور کاربر، کاربر می‌تواند از بین کرنل‌های زیر یک کرنل را برای اجرای کد خود انتخاب کند:Scala with Spark on Yarn cluster: با استفاده از این کرنل کاربران می‌توانند به زبان اسکالا کدهای اسپارکی خود را بر روی کلاستر یارن اجرا کنند.Scala: با استفاده از این کرنل کاربران می‌توانند کدهای اسکالا خود را بر روی ماشین‌های مجازی اجرا کنند.Scala with Flink: ما در بازار برای کارهای پردازشی جریانی از Flink استفاده می‌کنیم. با استفاده از این کرنل کاربران می‌توانند برنامه‌های اجرایی خود را توسط Flink بررسی کنند.Python with Spark on Yarn cluster: این کرنل این امکان را به کاربر می‌دهد تا برنامه‌های اسپارکی خود را با استفاده از زبان پایتون اجرا کنند.Python: با استفاده از این کرنل کاربران می‌توانند برنامه‌های پایتونی خود را اجرا کنند. بر روی این کرنل ابزار conda برای نصب هرگونه پکیج مورد نیاز کاربر وجود دارد.Python with GPU: این کرنل برنامه‌ها را بر روی ماشین‌های مجازی که دارای gpu هستند اجرا می‌کند. مهندسان علوم داده از این کرنل‌ها برای آموزش مدل‌های خود استفاده می‌کنند. همانند کرنل Python در این کرنل‌ها نیز ابزار conda برای نصب پکیج‌های موردنیاز کاربر وجود دارد.پس از انتخاب کرنل توسط کاربر این درخواست از طریق GatewayProxy به Gateway ارسال و با توجه به نوع کرنل انتخابی کدهای کاربر بر روی ماشین‌های مجازی و یا کلاستر Yarn اجرا می‌شود.از این بهتر هم می‌شود؟پس از مهاجرت از زپلین به ژوپیتر، با توجه به پایداری بالای ژوپیتر احساس رضایت کاربران افزایش پیدا کرده بود، اما با توجه به این‌که برخی از ویژگی‌های مثبت زپلین مانند اشتراک گذاری نوتبوک‌ها و نمایش نوار پیشرفت جابهای اسپارکی در ژوپیتر وجود نداشت، در مواردی دچار مشکل شده بودیم، درنتیجه بر آن شدیم تا این قابلیت‌ها را به عنوان یک افزونه به ژوپیتر اضافه کنیم.اولین افزونه‌ای که به ژوپیتر اضافه شد قابلیت اشتراک گذاری نوتبوک‌ها بود. کاربران تنها با یک کلیک بر روی نوتبوک قادر بودند تا نوتبوک‌های خود را با دیگران به اشتراک بگذارند.افزونه بعدی نمایش نوار پیشرفت جابهای اسپارک بود. به دلیل عدم نمایش آن کاربران از میزان پیشرفت جاب خود اطلاعی نداشتند و سردرگم می‌شدند. در نتیجه این افزونه به شکل زیر هنگام اجرای کدهای اسپارک در Notebook، به کاربر را از میزان پیشرفت تسک‌های خود اطلاع می‌دهد.نوار پیشرفت جابهای اسپارکیبا افزودن این افزونه‌ها به ژوپیتر هم از ویژگی‌های مثبت این ابزار استفاده کردیم و هم ویژگی‌های مثبت زپلین را که باعث ایجاد برتری آن نسبت به ژوپیتر می‌شد حذف کردیم.</description>
                <category>کافه بازار</category>
                <author>kosar bakhshi</author>
                <pubDate>Tue, 02 Aug 2022 14:35:51 +0430</pubDate>
            </item>
                    <item>
                <title>بازطراحی مسیر جست‌وجو در بازار</title>
                <link>https://tech.cafebazaar.ir/بازطراحی-مسیر-جست-وجو-در-بازار-mrddnuepknv6</link>
                <description> روزانه از هر ۱۰ کاربری که وارد اپلیکیشن بازار می‌شوند، تقریبا ۶ کاربر از طریق جست‌وجو، محتوای مورد نظر خود را پیدا می‌کنند. همین امر، بخش جست‌وجو را تبدیل به یکی از مهم‌ترین بخش‌های بازار کرده است.در گذشته طراحی مسیر جست‌وجوی بازار با توجه به نیازها و رفتار کاربران در محتوای بازی‌ها و برنامه‌ها انجام شده بود. یعنی نسخه قبلی را برای برطرف کردن نیاز پیدا کردن بازی یا برنامه طراحی کرده بودیم. بعد از اضافه شدن محتوای ویدیویی باید بررسی می‌کردیم فلوی جست‌وجو نیاز به تغییر دارد یا خیر.طرح مسئله: آیا نیاز به تغییر داشتیم؟در نسخهٔ قبلی، وقتی کاربر جست‌وجو می‌کرد، به تناسب عبارت (کوئری) جست‌وجو شده، نتایجی از بین بازی و برنامه به او نشان می‌دادیم. با اضافه شدن ویدیو، با کمترین تغییر ممکن ردیفی به هر نتیجهٔ جست‌وجو اضافه کردیم که در آن ویدیوهایی را نمایش می‌دادیم که تشخیص داده شده  به عبارت جست‌وجو شده نزدیک هستند.این راه حل دو مسئلهٔ جدی داشت:۱) استفاده از فضای زیاد برای برطرف کردن نیاز ۱٪ کاربرهابا بررسی‌ داده‌های جست‌وجو، متوجه شدیم که ۱٪ از کاربران وارد صفحهٔ  پیشنهادات مربوط به ویدیو می‌شدند. یعنی فقط ۱٪ جست‌وجوها منجر به تپ روی محتوای ویدیویی نمایش داده شده در جست‌وجو می‌شد. این به آن معنا بود که نمایش این ردیف به این شکل کار درستی نبوده و درصد زیادی از کاربرها برای پیدا کردن محتوای مورد نظر باید زمان بیشتری صرف می‌کردند.۲) عبارت‌های مشابه در محتواهای متفاوتنمایش بدون تفکیک دو محتوا از جنس‌های متفاوت به کاربر، مشکلاتی به وجود می‌آورد. فرض کنید کاربر عبارت «بازی» را جست‌وجو می‌کند، چطور تشخیص بدهیم که کاربر به دنبال فیلمی است که در عنوان آن واژهٔ «بازی» وجود دارد، یا عبارت «بازی» را به بهانهٔ رسیدن به لیستی از بازی‌های داخل بازار جست‌وجو کرده؟برای مثالی دیگر می‌توان به عبارت «شهرزاد» اشاره کرد. با این عبارت در بازار، هم محتوای بازی داریم هم محتوای ویدیویی.راه حلدر طراحی قبلی، راه رسیدن به جست‌وجو از bottom navigation و در کنار گزینه‌های برنامه، بازی و ویدیو بود. برای بهبود مشکلات بالا، کانال‌های جست‌وجوی محتوا را از هم تفکیک کردیم تا برای کاربر مشخص باشد که در کدام فضای محتوایی جست‌وجو می‌کند.در مسیر حذف جست‌وجو از bottom navigation و تغییر مکان برای جست‌وجو به ۳ مشکل برخوردیم که برخی از این مشکلات قبل از پیاده‌سازی در محصول و با تست‌های کاربردپذیری و برخی بعد از پیاده‌سازی و مشاهدهٔ رفتار کاربر بدست آمد که برای هر کدام راه حلی ارائه کردیم:۱) دسترسی فقط از صفحهٔ اولبعد از حذف دسترسی به صفحهٔ جست‌و‌جو از bottom navigation، کاربر در همهٔ صفحه‌ها به جست‌وجو دسترسی نداشت و برای هر بار جست‌وجو، می‌بایست به صفحهٔ اول برمی‌گشت؛ در حالی که در طراحی قبلی کاربر از هر صفحه‌ای که اراده می‌کرد می‌توانست وارد فرآیند جست‌وجو شود. به این ترتیب، جست‌وجو به هر صفحه‌ای که این ویژگی را نیاز داشت اضافه شد. برای مثال در صفحه‌های جزئیات برنامه یا ویدیو، در لیست‌ها و در صفحه‌های دسته‌بندی، که ممکن بود کاربر بخواهد جست‌وجویی انجام دهد، دکمهٔ جست‌وجو اضافه شد.۲) جست‌وجوی ویدیو در تب بازی و برنامه و یا برعکسبر اساس داده و مطالعات کاربرپژوهی متوجه شدیم که کاربران به اشتباه، عبارت خود را در بخش محتوایی نامربوط جست‌وجو می‌کنند. در قسمت ویدیو، به دنبال بازی می‌گردند و یا در قسمت ویدیو به دنبال بازی و برنامه. مثلا فرض کنید کاربری بازی Clash of Clans را می‌خواهد اما به اشتباه آن را در تبِ ویدیو جست‌وجو می‌کند. پس نتیجه‌ای از این جست‌وجو حاصل نمی‌شود و کاربر فکر می‌کند بازار این محتوا را ندارد. راه‌حل پیشنهادی، طراحی زیر بود که در نسخه‌های اخیر بازار اضافه شد و در آن کاربر می‌تواند با تغییر تب، در محتوای مدنظرش جست‌وجو کند. این راه حل را با تست کاربردپذیری پیش از پیاده‌سازی بررسی کردیم و مشکل مدنظر بهبود پیدا کرده بود.۳) عملکرد دکمهٔ بازگشت بر اساس مسیر طی شده یا معماری اطلاعات اپلیکیشن؟بیایید با هم به این سؤال پاسخ دهیم:فرض کنید کاربر یک عبارت را جست‌وجو می‌کند اما چون نتایج یافته شده چیزی نیست که می‌خواهد، هیچ‌ کدام از نتایج را لمس نمی‌کند. برای رسیدن به محتوای مورد نظر عبارت دیگری جست‌وجو کرده و باز هیچ‌ کدام از نتایج را لمس نمی‌کند. ممکن است این کار را بارها بدون وارد شدن به صفحهٔ جزئیات نتایج نمایش داده شده ادامه دهد. اکنون بعد از لمس دکمهٔ بازگشت، باید به کدام صفحه بازگردد؟وقتی کاربر چندین جست‌وجوی پشت سر هم انجام داده و به نتیجهٔ مطلوب نرسیده، بهتر است با زدن دکمهٔ بازگشت، به صفحه‌ای برگردد که جست‌وجو را از آن آغاز کرده بود، علاوه‌براین، نتیجهٔ کاربرپژوهی این موضوع را برای ما روشن‌ کرد که کاربران انتظار داشتند هنگام زدن دکمهٔ بازگشت، به صفحهٔ اصلی برنامه، بازی یا ویدیو برسند و زمانی که این اتفاق نمی‌افتاد، یا به طور مکرر دکمهٔ بازگشت را لمس می‌کردند یا برنامه را می‌بستند و دوباره باز می‌کردند. این یعنی کاربر بعد از دیدن نتایج و تصمیم برای بازگشت، نیازی به دیدنِ صفحه‌ای که در آن عبارت را تایپ کرده نداشته.پس تصمیم گرفتیم به منظور دسترسی سریع کاربر به صفحه های اصلی، جست‌وجوهایی که به صفحهٔ جزئیات نمی‌رسند و صفحاتی که کاربر در آن عبارت‌ها را جست‌وجو می‌کند در فرآیند بازگشت نشان داده نشوند. در حالی‌که در طراحی قبلی، دکمهٔ بازگشت، صفحات را تک به تک عقب می‌برد.به این شکل فرایند جست‌و‌جو، طی چند iteration متناسب با محتواهای داخل اپلیکیشن بازطراحی شد.روزانه چندین میلیون کاربر از جست‌وجوی بازار استفاده می‌کنند و هر تغییر کوچک و بزرگ چالش‌های زیادی دارد، ما سعی می‌کنیم با طراحی iterative هم ویژگی‌های جدیدی به بازار اضافه کنیم و هم مشکلات موجود را برطرف کنیم. اگر علاوه بر طراحی بصری محصولات دیجیتال، حل مسئله برایتان جذاب است و دوست دارید با این مسائل درگیر شوید، حتما از اینجا، موقعیت‌های شغلی بازار را مشاهده کنید و اگر علاقه‌مند هستید ، رزومه و پورتفولیوی خودتان را برای طراحی/پژوهش تجربهٔ کاربر ارسال کنید.</description>
                <category>کافه بازار</category>
                <author>حسام کرامتی</author>
                <pubDate>Mon, 19 Jul 2021 14:42:39 +0430</pubDate>
            </item>
                    <item>
                <title>زیرساخت کافه‌بازار: فرهنگ دوآپس را چگونه شکل دادیم؟</title>
                <link>https://tech.cafebazaar.ir/زیرساخت-کافه-بازار-فرهنگ-دوآپس-را-چگونه-شکل-دادیم-atqkkwsiafhs</link>
                <description>دوآپس چیست و چرا؟چیزی که باعث شده ما توی کافه بازار اهمیت زیادی برای اتکاپذیری (reliability) سرویس‌هامون داشته باشیم اینه که ما داریم به ۴۵ میلیون کاربر فعال سرویس می‌دیم و هر لحظه از دسترس خارج بودن بازار ضرر مالی زیادی بهمون وارد می‌کنه . سه تا فاکتور هست که برای ما اهمیت داره: کارایی (performance)، دسترس‌پذیری (availability)، مقیاس‌پذیری (scalability). کارایی یعنی برنامه بتونه توی زمان معقول با استفاده از منابع معقول جواب کاربر رو بده. زیاد شدن زمان پاسخ دهی می‌تونه باعث نارضایتی و از دست رفتن کاربرها بشه. دسترس‌پذیری یعنی سعی کنیم ارتباط کاربر‌ها با سرور همیشه برقرار باشه چون اگه ارتباط کاربر با سرور قطع بشه عملا از اپلیکیشن بازار استفاده‌ای نمیتونه بکنه و مقیاس‌پذیری هم یعنی اگه با رشد ناگهانی کاربرها مواجه شدیم، بتونیم همچنان جوابگوی درخواست‌ها باشیم.ولی یک نکته مهم اینجا هست. تلاش ما برای افزایش اتکاپذیری نباید باعث کند شدن فرایند‌های توسعه محصول ما بشه!به صورت سنتی تیم توسعه (Development) و تیم عملیات (Operations) جدا از همن. هدف تیم توسعه اضافه کردن قابلیت‌های جدید به برنامست و هدف تیم عملیات حفظ کردن شرایط پایداری سرویس. مشکلی که این فرآیند ایجاد می‌کنه تضاد منافع بین تیم‌هاست. تیم توسعه دوست داره فیچرهای جدید رو هر چه سریع‌تر توسعه بده در حالی که تغییر توی سرویس یعنی به خطر افتادن پایداری! پس تیم عملیات همیشه باید مطمئن باشه تغییرات جدید مشکلی توی سرویس ایجاد نمی‌کنن. اگه اختیار عمل رو کامل به تیم توسعه بدیم احتملا پایداری سرویس ازبین می‌ره و اگه اختیار عمل رو به تیم عملیات بدیم توسعه محصول به کندی پیش میره.اینجاست که ما به یک تعامل خوب و سازنده بین این دو تا تیم نیاز داریم. این هدفِ تیم عملیاته که پایداری سرویس رو تضمین کنه. ولی خیلی مواقع پیش میاد که بهبود پایداری نیاز به تسلط روی سورس کد داره برای همین تیم توسعه باید توی این فرآیند کمک بکنه. همینطور توسعه سریع‌تر و اتکاپذیری بیشتر، دو کفه یک ترازو هستند پس برای رسیدن به بهترین تعادل به ارتباط قوی بین تیم توسعه و عملیات و همینطور مارکتینگ و محصول نیاز داریم.طبق تعریف، دوآپس ترکیبی از ارزش‌ها و فرآیندها و ابزارهاست که باعث نزدیک شدن فرآیند توسعه و فرآیند عملیات به هم می‌شه تا به سازمان این قابلیت رو بده که بتونه سرویس‌هاش رو با سرعت و کیفیت به دست کاربر برسونه.کافه بازار چطوری به این ساختار رسید؟اون اول وقتی تیم فنی کافه‌ بازار کوچک بود، مرزی بین توسعه و عملیات دیده نمی‌شد. همون کسی که کدی رو می‌نوشت، اول تستش می‌کرد و بعد روی سرورها دیپلوی می‌کرد تا سرویس به روز بشه. خودش هم کارای مانیتورینگ و بقیه کارای عملیاتی رو انجام می‌داد. با بزرگ شدن تیم فنی چاره ای نبود که تقسیم وظایف صورت بگیره. ما بین یک دوراهی قرار گرفتیم. راه اول این بود که نقش‌های مختلف رو از هم جدا کنیم و هر کدوم توی یک تیم مستقل باشن که به component team معروفه. یعنی یک تیم بک‌اند داشته باشیم و یک تیم زیرساخت (و همینطور تیم اندروید و فرانت و مارکتینگ و غیره) یا اینکه بیایم تیم‌‌ها رو بر اساس هدف نهاییشون، یعنی رسوندن یک سرویس خاص به‌ دست کاربر جدا کنیم و توی هر تیم افراد مختلف با نقش‌های مختلف وجود داشته باشن که به feature team معروفه.ما با الهام گرفتن از اسپاتیفای سمت راه دوم رفتیم. توی ساختاری که ما بهش رسیدیم همچنان مثل قدیم که یک شرکت کوچک بودیم مرزی بین توسعه و عملیات دیده نمی‌شه و رفع کردن مشکلات مقیاس‌پذیری و دسترس‌پذیری، مقابله با مخاطرات احتمالی که به بخش عملیات معروفه توسط توسعه‌دهنده‌های بک‌اند انجام می‌شه.توی فرهنگ کافه‌ بازار فقط زمانی می‌شه از تیمی توقع مسئولیتی رو داشت که اون تیم قدرت و اختیار تمام و کمال روی اون موضوع داشته باشه. ما نمی‌تونیم از تیمی توقع داشته باشیم که دسترس‌پذیری و کارایی یک سرویس رو بهتر کنه در حالی‌که تیم دیگه‌ای کدش رو توسعه داده.این نوع شکستن تیم‌ها نیاز ما رو به معماری میکروسرویس زیاد کرد. اگه قرار بود یک تیم بتونه با استقلال به سمت اهدافش بره منطقا اولین چیزی که باید از بقیه مستقل می‌کرد سورس کد پروژه‌ای بود که روش کار می‌کنه. مهاجرت به سمت معماری میکروسرویس یک‌ شبه انجام نشد و چند سال طول کشید تا خورد خورد هر تیم پروژه خودش رو از سرویس یکپارچه کافه بازار جدا کنه.در حال حاضر، هر تیم کافه بازار چندین میکروسرویس داره و دیگه برای نوشتن یک فیچر جدید، به کد نوشتن روی سورس کدی که توسط تیم‌های دیگه مدریریت می‌شه تقریبا نیازی نیست.مدیریت کردن این همه میکروسرویس خودش کار سختیه. برای همین ما توی کافه‌ بازار نقشی به اسم «صاحب سرویس» تعریف کردیم. «صاحب سرویس» کسیه که بیشترین تسلط رو روی اون میکروسرویس داره و مسئولیت اینو داره که کدش رو همیشه با کیفیت نگهداره، کدای بقیه رو بررسی کنه و اگه مشکلی براش رخ داد در اسرع وقت درستش کنه.مقایسه feature team با component teamخوبی و بدی این ساختار چیه؟خوبی:این مدل شکستن باعث می‌شه تضاد منافع بین dev و ops باعث کند شدن ما در رسیدن به یک تجربه خوب برای کاربرامون نشه.شناسایی اینکه مشکل یک میکروسرویس از سورس کدشه یا از پلتفورمی که روش مستقر شده (شامل تنظیمات سرور، دیتابیس، کش (cache) و غیره) راحت تر پیدا می‌شه. توی مدل سنتی شرایطی پیش میاد که تیم ops برای بهبود اتکاپذیری یک سرویس کلی وقت می‌ذارن درحالی‌که ممکنه اون مشکل با یک تغییر کوچیک توی کد حل شه.فرآیند‌های release و deployment راحت‌تر می‌شن چون دیگه خبری از یک سورس کد خیلی بزرگ نیست که هر کس فقط بخشی ازش رو بلد باشه به جاش کلی میکروسرویس کوچیک داریم که صاحبش روی کدش کامل تسلط داره و میتونه سریع یک کد جدید رو بررسی کنه و روی محصول اصلی deployش کنه.بین وقت گذاشتن روی توسعه فیچر جدید یا وقت گذاشتن روی بهتر کردن reliablity خیلی تعادل برقرار میشه. چرا که ارتباط درون تیمی بین افراد یک تیم خیلی راحت تر از ارتباط بین تیمی صورت می‌گیره.بدی:تجربه‌هایی که داخل تیم‌ها به دست میاد تو همون تیم می‌مونه و به سختی با بقیه به اشتراک گذاشته می‌شه. مثلا فرض کنید یکی از بچه‌ها برای افزایش کارآیی میکروسرویسی که صاحبشه وقت زیادی روی tuning دیتابیس می‌ذاره. اگه یک میکروسرویس از یک تیم دیگه مشکل مشابه داشته باشه، به احتمال زیاد صاحب میکروسرویس دوم باید از اول وقت بذاره. ولی توی ساختار سنتی چون افراد با توجه به مهارتشون توی یه تیم قرار میگیرن انتقال دانش بینشون راحت اتفاق میوفته. جلسات هفتگی chapter برای بهتر کردن این مشکل برگزار می‌شن ولی یه جلسه توی هفته هیچوقت نمی‌تونه جای چندین ساعت هم‌تیمی بودن رو بگیره.وقت زیادی صرف کارای تکراری می‌شه. برای مثال یک تیم زمانی رو اختصاص می‌ده که برای مانیتورینگ سرویس ها از prometheus استفاده کنه و برای میکروسرویس های داخل تیم، کلی alert و dashboard مختلف میسازه. اگه یک تیم دیگه هم بخواد از prometheus استفاده کنه باید همون کارارو تکرار بکنه در حالی که شاید یک سری alert و dashboard تکراری وجود داشتن که می‌شد فقط یک بار به صورت کلی انجامش داد.داشتن یک نیرو که کل تمرکزش فقط روی مباحث زیرساختی باشه (DevOps Engineer) توی هر تیم هزینه زیادی داره و برای ما ممکن نیست. صاحب‌های سرویس‌ها هم بیشتر از نصف وقتشون رو دارن روی توسعه اون سرویس میذارن برای همین دانششون توی مباحث زیرساخت سطحی تر از یک نیروی متخصصه.ماموریت تیم پلتفورم چیه؟بالاتر به معایب ساختاری که ما بهش رسیدیم پرداختیم. تیم پلتفورم بازار با هدف ارائه سرویس‌ و ابزار برای میزبانی از میکروسرویس‌های بازار و بهبود اتکاپذیری آنها تشکیل شد تا بتونه معیابی که بهش اشاره کردیم رو کمرنگ‌تر کنه. ولی توی تشکیل تیم پلتفورم یک چالش بزرگ داشتیم. چالش اصلی این بود که نمی‌خواستیم ساختار قبلی رو بهم بزنیم چون در اون صورت مزیت‌هاش رو از دست می‌دادیم. تیم فنی شرکت به تعدادی تیم محصولی شکسته شده بود که اهداف خودشون رو داشتن ما و ما نمی‌خواستیم با تشکیل تیم پلتفورم استقلال اون هارو از بین ببریم. توی ساختار سنتی تیم پلتفورم میتونه نظارت سختگیرانه روی تیم توسعه داشته باشه و برای افزایش پایداری خودش مستقیما وارد عمل بشه. ولی توی ساختار کافه بازار که استقلال تیم ها برای ما مهمه. تیم پلتفورم نمی‌تونه بقیه تیم‌هارو مجبور به رعایت کردن یک سری الگو بکنه یا خودش مستقیم وارد بشه و اون الگو هارو پیاده سازی کنه.برای حفظ استقلال تیم‌های محصولی ما به خود تیم پلتفورم به چشم یه تیم محصولی نگاه کردیم با این تفاوت که مشتری‌های تیم پلتفورم دیگه کاربرهای کافه‌بازار نیسن و در واقع تیم‌های محصولی دیگه شرکت رو به عنوان مشتری خودمون می‌بینیم و باید براشون خلق ارزش کنیم. تلاش می‌کنیم تا مشکلاتشون رو شناسایی کنیم و ابزارهایی ارائه بدیم که اون مشکل‌ رو حل کنه. اگه ابزاری ارائه بدیم و تیمی ازش استفاده نکنه، یعنی توی شناسایی مشکلات تیم‌های محصولی خوب عمل نکردیم. برای هل دادن تیم های محصولی برای رعایت کردن الگوهای درست سعی میکنیم ابزارهایی ارائه بدیم که انجام دادن یک کار از روش درستش راحت تر از انجامش به صورت اشتباه باشه.کار هایی که تا الان توی تیم پلتفورم انجام دادیم:بالاتر خیلی کلی از اهداف و آرمان های تیم پلتفورم گفتیم توی این بخش اشاره می‌کنیم که واقعا چه کارهایی رو تا الان تونستیم انجام بدیم. بعضی از این کارها خودش یک بلاگ پست جدا نیاز داره که حتما سعی می‌کنیم در آینده بنویسیم ولی توی این پست جهت آشنایی با تیم پلتفورم صرفا در حد چند خط بهشون اشاره می‌کنیم.ایجاد chaos یا هرج و مرج در کوبرنتیزبیشتر سرویس‌های ما توی کافه بازار روی کوبرنتیز مستقر شدن. باید بدونیم که اپلیکیشن‌هایی که روی کوبرنتیز مستقر میشن باید در برابر restart شدن و منتقل شدن به یک node دیگه مقاوم باشن. اگه اینطور نباشه اضافه و کم کردن یک سرور به کلاستر کوبرنتیز می‌تونه باعث داون تایم بشه و همینطور کوبرنتیز وقتی تشخیص بده یک سرویس‌ حالش خوب نیست ری‌استارتش می‌کنه. اگه قرار باشه اون سرویس با ری‌استارت شدن حالش بدتر بشه پس عملا استفاده از کوبرنتیز منفعتی برای ما نداشته. دیده می‌شد که خیلی از تیم ها این مسئله رو رعایت نمی‌کنن و خب ما هم نمی‌تونستیم مستقیم وارد تیم‌ها بشیم و چک کنیم که تک‌تک میکروسرویس‌هایی که تیم‌ها نوشتن نسبت به این اتفاقات مقاوم هستن یا نه. راه حلی که ما تصمیم گرفتیم انجامش بدیم ایجاد اختلال عمدی روزانه بود که با این اختلال‌ها تیم‌ها بتونن سریع مشکلاتشون رو شناسایی و رفع کنن. شبیه‌سازی خاموش شدن یک node کوبرنتیز و کاهش کیفیت شبکه داخلی کوبر دو تا از هرج‌ومرج‌هایی هستن که ما روزانه ایجاد می‌کنیم.ارائه‌دادن زیرساخت مانیتورینگبا بررسی مستندات downtime های ۲ سال گذشته (که به postmortem معروفن) به این نتیجه رسیدیم کم کیفیت بودن مانیتورینگ سرویس‌هامون اصلی‌ترین دلیل کاهش uptime بوده برای همین تیم ما داره تلاش می‌کنه که کیفیت مانیتورینگ رو در کافه بازار بیشتر کنه.برای بهتر کردن کیفیت مانیتورینگ تا الان دو تا رویکرد رو جلو بردیم. اولی ارائه ابزار‌های زیرساختی برای مانیتورینگ بوده مثل ارائه sentry و prometheus و grafana. و دومین رویکردمون درست کردن داشبوردها و آلرت‌های عمومی و کلی برای همه میکروسرویس‌ها. به این صورت که الان تیم‌های محصولی کافه بازار میتونن مشخصات میکروسرویس جدیدشون جای مشخص ثبت کنن تا به صورت خودکار براشون سیستم alerting و monitoring ساخته شه.سامانه نظارتهمونطور که اشاره کردیم یکی از مشکلات ما متمرکز نبودن صاحب‌ سرویس‌ها روی کارهای زیرساختی بود که باعث می‌شد خیلی از مواقع به دلیل کم بودن اطلاعات یا حواس پرتی کار تظیمات اشتباهی روی سرور قرار بگیره. برای کم کردن این مشکل ما پروژه «ناظر» رو شروع کردیم. در حال حاضر پروژه ناظر قابلیت نظارت روی کوبرنتیز و ماشین‌های مجازی رو داره و احتمالا در آینده نظارت روی سورس کد سرویس‌ها هم بهش اضافه بشه.  توی قدم اول ما اول اومدیم اشتباهات متداولی که بچه‌ها موقع deploy کردن یک سرویس روی کوبرنتیز یا setup کردن یک سرور مجازی انجام می‌دن رو شناسایی کردیم. بعد اومدیم پروژه ای نوشتیم که به صورت زمانبندی شده بیاد کل کلاستر کوبرنتیز و سرورهامون رو نظارت کنه و اگه یکی از این اشتباهات دیده شد به تیم مربوطه پیام بفرسته. خاموش بودن firewall سرور یا رزرو اشتباه منابع توی کوبر، دو تا از ۸ نوع مشکلی هستن که پروژه ناظر قابل به شناسایی اون‌ها در لحظه‌ است. همینطور ناظر یک صفحه گزارش داره که به صورت هفتگی توی جلسه توسط نماینده تیم و CTO بررسی می‌شه تا تیم ها از تعداد اشتباهاتی که توی تنظیمات زیرساختیشون دارن آگاه باشن.راه اندازی محیط stagingسورس کد بزرگ کافه‌ بازار و میکروسرویس‌های زیاد توسعه محصول را سخت کرده. توسعه و تست یک کامپوننت شاید چالشی نداشته باشه ولی تست نهایی اون کامپوننت در کنار همه میکروسرویس‌های بازار نیاز به یک محیط بزرگی داره که به staging معروفه. هر تغییر قبل از رسیدن به دست کاربر‌های نهایی می‌تونه توی محیط staging که یک نسخه کامل از کل بک‌اند‌های بازاره تست بشه. یکی از کار‌های تیم پلتفورم که سمتش رفتیم ساده‌تر کردن این فرآیند بود. مثلا تلاش داریم با استفاده از سرویس مش ریکوئست‌های alpha tester ها رو از ریکوئست‌های production جدا کنیم و اگه میکروسرویسی خودش رو staging معرفی کرد ریکوئست پروداکشن دریافت نکنه. ابزار دیگه ای که برای اینکار توسعه دادیم پروژه staging database ه. این پروژه هر روز از دیتابیس اصلی یک بک‌آپ میگیره و برنامه‌نویس‌ها می‌تونن با استفاده از cli یک نسخه از دیتابیس دیروز رو روی سرور اجرا کنن و بهش وصل بشن.جمع بندیتوی این پست توضیح دادیم که چطور فرهنگ دوآپس رو توی کافه‌ بازار پیاده کردیم. اینکه ما نیروی متمرکز برای کارهای Ops نداریم و کارهای عملیاتی توسط خود برنامه‌نویس‌ها انجام میشه. همینطور به چالش‌های این ساختار یعنی سطحی بودن دانش و وجود کار‌های تکراری اشاره کردیم و با مثال سعی کردیم توضیح بدیم چطور تیم پلتفورم بازار سعی داره بدون دخالت مستقیم روی پروژه های بچه‌ها این چالش‌ها رو برطرف کنه تا کیفیت و اتکاپذیری سرویس‌های بازار افزایش پیدا کنه.</description>
                <category>کافه بازار</category>
                <author>بردیا حیدری</author>
                <pubDate>Mon, 26 Apr 2021 10:46:22 +0430</pubDate>
            </item>
                    <item>
                <title>تیم داده کافه‌ بازار، چالش‌‌ها و مسأله‌ها</title>
                <link>https://tech.cafebazaar.ir/تیم-داده-کافه-بازار-چالشها-و-مسألهها-jlbfyxae8uck</link>
                <description>تیم داده کافه بازار، مشابه شرکت اسپاتیفای (Spotify)، ساختاری به اصطلاح ماتریسی دارد. یعنی هر عضو دانشمند داده (Data Scientist)، هم در یک تیم محصولی متشکل از نفرات با مهارت‌‌های دیگر (Software Engineer، Product Manager و … ) فعالیت دارد و هم با دیگر دانشمندان داده در ارتباط است که به اصطلاح Data Science Chapter گفته می‌شود. بنابراین یک بُعد از این ماتریس، تیم و بُعد دیگر، چپتر دیتاست. این ساختار کمک می‌کند که اعضای تیم داده، هم به دغدغه‌های محصولات داده‌ای (Data Product) و تصمیم‌گیری‌های داده محور (Data-Driven Decision Making) تسلط پیدا کنند و هم بتوانند با حضور در چپتر دیتا، تخصص‌های خود را تقویت کرده و از دانش دیگر دانشمندان داده استفاده کنند.گفتنی‌ است تعداد اعضای تیم داده کافه‌ بازار از تابستان ۹۸ تا تابستان ۹۹، نزدیک به ۳ برابر شده است. این تیم در حال رشد کردن است و از جذب نیروهای بااستعداد، خوش‌فکر و تحلیل‌گر استقبال می‌کند.این ما هستیم! تیم داده کافه بازار!در حال حاضر، دانشمندان داده در ۹ تیم از تیم‌های کافه ‌بازار فعالیت می‌کنند و کارهای آن‌ها از یادگیری ماشین کلاسیک (Classic Machine Learning) تا یادگیری عمیق (Deep Learning)، داده‌‌های متنی و پردازش زبان (NLP)، سیستم‌های پیشنهاد‌دهنده (Recommendation Systems) و حتی طراحی حراجی (Auction/Mechanism Design) گسترده است. در این مقاله تلاش کردیم که نمونه‌هایی از کارهای متنوعی را که در تیم‌های مختلف بازار انجام می‌شود، به‌طور خلاصه شرح دهیم و چالش‌ها و ایده‌هایی را که برای رویارویی با مسأله و حل آن پیش گرفته شده معرفی کنیم.آمیتیس شیدانیآمیتیس از تیم تبلیغات بازار درباره دو مسئله (بهینه‌سازی مکانیزم حراجی و تشخیص کوئری‌های یکسان در پنل تبلیغات) توضیح می‌دهد. آمیتیس حدود ۲ سال هست که تجربه کار در تیم داده بازار را دارد و بیشتر از یک‌ سال است که در تیم‌های تبلیغات فعالیت می‌کند. علاقه‌ی اصلی‌ آمیتیس به حوزه‌های مکانیزم دیزاین، یادگیری تقویتی و تشخیص فراد است.مصطفی جان‌نثاریمصطفی از تیم تبلیغات ویدیو مسأله‌ی تخمین سن و جنسیت کاربرها و چالش‌ها و راه‌حل‌های استفاده شده را بیان می‌کند. مصطفی حدود یک سال هست که در بخش دیتا کافه‌بازار فعالیت می‌کند و تجربه‌ی حضور در تیم‌های پرداخت و ویدیو را دارد. مصطفی به حوزه‌ی پردازش زبان طبیعی و الگوریتم‌های درختی علاقه‌مند است.علی رحیمی‌کلهرودیعلی از تیم جستجو بازار، در مورد سیستم رنکینگ محتواهای کافه‌بازار توضیح می‌دهد. از حضور علی در کافه‌بازار حدود ۴ سال می‌گذرد و تقریبا ۳ سال است که به‌صورت متمرکز در بخش دیتا کافه ‌بازار فعالیت می‌کند. علی سابقه حضور در تیم‌های جزئیات برنامه، دیسکاوری، تبلیغات و سرچ را دارد و از سروکله‌زدن با مسائل مختلف لذت می‌برد.حمیدرضا احمدیانحمیدرضا از تیم دیسکاوری از سیستم Recommendation در بازار می‌گوید. حمیدرضا حدود ۳ سال است که در بخش دیتا بازار فعالیت می‌کند. او کارش در بازار را با تیم جزئیات برنامه شروع کرده و بعد از آن مدتی وارد تیم سرچ و سپس تیم دیسکاوری شده و بیشتر سابقه‌ی فعالیتش در زمینه‌ی سیستم‌های پیشنهاددهنده است.تبلیغات در بازارمعرفی تیمتبلیغات در سرچ به دو تیم شکسته می‌شود که یکی از تیم‌ها، بهینه‌سازی حراجی و نیازهای کاربر سرچ را بررسی می‌کند (تیم حراجی سرچ) و تیم دیگر، نیازمندی‌های سمت تبلیغ‌گذارنده را برطرف می‌کند (تیم پنل ادز). در ادامه هر یک را بیشتر معرفی می‌کنیم.حراجی سرچ: هدف این تیم، بهینه‌سازی و طراحی حراجی به منظور افزایش درآمد با هدف حفظ ماندگاری نصب است. تلاش این تیم همواره در جهت نمایش تبلیغات مرتبط به کوئری (عبارت سرچ‌شده توسط کاربر) است تا هم بتواند کیفیت نصب را حفظ کند و هم کمترین آسیب را به تجربه کاربری سرچ وارد کند. در این راستا، شخصی‌سازی تبلیغات، شناسایی هدف و نیاز کاربر از کوئری سرچ شده در کنار دانش Mechanism Design برای بهینه‌سازی حراجی از کارهای اصلی این تیم است. به این منظور، کارهای این تیم را می‌توان به دو بخش اصلی تقسیم کرد: &quot;طراحی حراجی&quot; و &quot;بهینه‌سازی آگهی نمایش داده شده&quot;.پنل ادز: هدف این تیم کمک به تبلیغ‌گذارنده‌ها برای انتخاب مناسب‌تر کوئری (عبارت سرچ شده توسط کاربر) در راستای تبلیغ در سرچ است. این تیم همواره در تلاش بوده تا کوئری مرتبط‌ به هر اپلیکیشن را شناسایی کند و آن را در اختیار تبلیغ‌گذارنده قرار دهد. این تلاش دو هدف مهم را برآورده می‌کند:۱- کمک به تبلیغ‌گذارنده برای جذب کاربر با کیفیت۲- جلوگیری از ورود اپلیکیشن‌های نامرتبط به حراجی هر کوئریاز نمونه مسائلی که در این تیم بررسی می‌شوند، می‌توان به &quot;پیشنهاد کوئری به اپلیکیشن تبلیغ‌گذارنده&quot; (نوعی مسئله Recommendation) و یا کمک به افزایش پوشش هر اپلیکیشن (Coverage) در تبلیغات توسط &quot;پیشنهاد کوئری‌های مشابه&quot; به کوئری انتخاب شده توسط تبلیغ‌گذارنده و … اشاره کرد.تعریف مسئلهدر ادامه در راستای هر کدام از تیم‌های مطرح شده در قسمت قبل، یک مسئله را تعریف کرده و چالش‌ها و راه‌حل‌های آن را بررسی می‌کنیم.مسئله ۱ (مدل تخمین احتمال نصب): در راستای کاهش آسیب به کاربر سرچ و بهینه‌سازی درآمد، علاوه‌بر مبلغی که تبلیغ‌گذارنده به عنوان بید (bid) در حراجی اعلام می‌کند، پارامتر دیگری تحت عنوان احتمال نصب را نیز در انتخاب برنده و مرتب‌سازی (sorting) بیدهای حراجی تاثیر می‌دهیم (یعنی جای اینکه بید با مبلغ بالاتر را به عنوان برنده اعلام کنیم، بیدی را به عنوان برنده معرفی می‌کنیم که &quot;بید&quot; * &quot;احتمال نصب&quot; بالاتری داشته باشد). پس بسیار مهم است که این احتمال را با دقت بالایی تخمین بزنیم زیرا هم روی درآمد ما تاثیر قابل توجهی دارد و هم تخمین بهتر آن به معنی نمایش تبلیغ مرتبط‌تر است و باعث افزایش رضایت کاربر سرچ و حفظ کیفیت نصب برای تبلیغ‌گذارنده می‌شود.مسئله ۲ (تشخیص کوئری‌های یکسان): تبلیغ‌گذارنده‌ها می‌توانند در پنل تبلیغات، کوئری مورد نظر خود را انتخاب کنند و هر زمان سرچی با آن عبارت صورت بگیرد، در حراجی آن شرکت کنند. تیم پنل نیز تلاش می‌کند تا ابزارهایی را در اختیار هر تبلیغ‌گذارنده قرار دهد تا افراد بتوانند کوئری‌های مرتبط‌تری را شناسایی کنند.یکی از دغدغه‌هایی که توسط این تیم بررسی شد، مسئله یکسان بودن کوئری‌ها بود. بگذارید با یک مثال بهتر بگوییم، در نظر بگیرید که تبلیغ‌گذارنده کوئری &quot;نقشه‌&quot; را در پنل انتخاب کرده است. این کوئری دقیقا معادل کوئری انگلیسی &quot;map&quot; است. تعدادی از کاربران نیز در سرچ بازار، این کوئری را به صورت &quot;مپ&quot; سرچ می‌کنند یا در بعضی حالت‌ها غلط‌های املایی مشابه &quot;نغشه&quot; رخ می‌دهد. اگر تبلیغ‌گذارنده بخواهد همه‌ی این معادل‌ها را در پنل انتخاب کند، ممکن است به بررسی حالت‌‌های بسیار زیاد و متنوعی نیاز باشد. این از نظر رابط کاربری، اصلا بهینه نیست. علاوه‌بر این، تبلیغ‌گذارنده برای حفظ کیفیت نصب خود، نیاز دارد کوئری‌هایی را نگه دارد که رفتار نصب مشابهی مانند کوئری اصلی دارند. بنابراین نیاز است تا سازوکاری برای تشخیص کوئری‌های یکسان با حفظ کیفیت نصب در پنل وجود داشته باشد و تبلیغ‌گذارنده‌ها بتوانند با فعال کردن یک قابلیت یا ابزار، این دغدغه را برطرف کنند.چالش‌ها‌وراه‌حل‌ها برای مسئله ۱ (مدل تخمین احتمال نصب)چالش‌های بسیاری برای تخمین احتمال وجود دارد ولی دو مورد آن اصلی‌تر از بقیه هستند:۱. بسیار اتفاق می‌افتد که اپلیکیشنی به تازگی وارد سیستم تبلیغات شود. در این صورت، هیچ اطلاعات یا تاریخچه‌ای از رفتار کاربر روی برنامه مذکور نداریم و به تعبیری با یک مسئله Cold-Start مواجه هستیم.۲. تعداد زیادی کوئری وجود دارند که در روز کمتر از ۱۰ بار سرچ می‌شوند و لذا واریانس احتمال نصب روی این کوئری‌ها بسیار زیاد است.در راستای این چالش‌ها، دو عملکرد را پیش گرفتیم. ابتدا سراغ مدل‌های یادگیری تقویتی (Reinforcement Learning) رفتیم که با گرفتن هر فیدبک از کاربر به صورت آنلاین، تلاش می‌کنند تخمین احتمال نصب را به مقدار واقعی احتمال، نزدیک‌تر کنند. در ابتدا، این احتمال را دست‌بالا برابر یک در نظر می‌گیرند و سپس با هر نمایش و نصب یا عدم نصب آگهی، احتمال را آپدیت می‌کنند. این قضیه مشکل Cold-Start را تا حدی حل می‌کرد ولی درباره چالش دوم خیلی راه‌حلی نداشت. برای بهبود آن، تلاش کردیم این سیستم آنلاین را با یک سیستم آفلاین ترکیب کنیم. به این شکل که به جای شروع از احتمال یک، از احتمال نزدیک‌تری به واقعیت شروع کنیم تا سرعت Converge کردن بیشتر شود و برای کوئری‌هایی که اطلاعات کمی داریم، از اطلاعات کوئری‌های مشابه آن‌ها استفاده کنیم. به این منظور از ویژگی‌هایی مانند کیفیت اپلیکیشن مورد نظر، رفتار کاربران روی کوئری‌های مشابه آن کوئری یا اپلیکیشن‌های مشابه آن اپلیکیشن، شباهت متنی کوئری و اپلیکیشن (Semantic Similarity که با استفاده از مدل‌های پردازش زبان مانند Word2Vec این شباهت را بررسی می‌کنیم) و … استفاده کردیم تا نقطه شروع بهتری را انتخاب کنیم. پس از پیاده‌سازی آنچه توضیح داده شد، با حفظ کیفیت نصب از سرچ، نرخ نصب آگهی در سرچ تقریبا ۲ برابر شده است.چالش‌ها‌وراه‌حل‌ها برای مسئله‌ ۲ (تشخیص کوئری‌های یکسان)همان‌طور که پیش‌تر در قالب مثال توضیح داده شد، هدف مسئله ۲، تشخیص غلط املایی، معادل‌های فارسی، انگلیسی و فینگلیش و نیز کلمات هم‌معنی است. اگرچه که این مسأله، در ابتدا روش‌های NLP را در ذهن تداعی می‌کند ولی هدف در اصل، تشخیص کلمات یکسانی است که رفتار نصب مشابهی نیز دارند و در این حالت، روش‌های NLP لزوما بهترین جواب را نمی‌دهند. به این منظور، برای بررسی یکسان بودن رفتار نصب روی کوئری‌ها و تقسیم آن‌ها به گروه‌های مناسب، این‌طور عمل کردیم که هر کوئری را به عنوان‌ بُرداری متشکل از نرخ یا فرکانس اپلیکیشن‌های نصب‌شده از آن کوئری در نظر گرفتیم و مسأله را تبدیل کردیم به سوالی در فضای گراف‌ها! گرافی را در نظر بگیرید که به این شکل ساخته شود:هر کوئری را به عنوانی رأسی از گراف در نظر می‌گیریم و دو رأس را به هم وصل می‌کنیم اگر و تنها اگر، شباهت برداری کوئری‌های متناظر آن‌ها از Threshold بیشتر باشند.با این ساختار، شناسایی دسته‌ای از کوئری‌هایی با نصب یکسان، با پیدا کردن مؤلفه‌های همبندی گراف مذکور معادل خواهد شد. با پیاده‌سازی این روش، مسأله را تبدیل به مسأله Community Detection کردیم و از الگوریتم‌های مربوطه، دسته‌های کوئری‌ها را جدا کردیم. این روش، یک درجه آزادی در قالب Threshold داشت که تعیین می‌کرد بین کدام رئوس، لینک برقرار شود. با کمی بررسی و تحلیل متوجه شدیم که در راستای پیدا کردن Threshold مناسب، می‌توان دغدغه اصلی یعنی بررسی یکسان بودن کوئری‌ها را به عنوان معیار برای خوب بودن دسته‌ها در نظر بگیریم. نکته جالب در این مسأله، این بود که وقتی کوئری‌ها را از نظر رفتار نصب بررسی می‌کردیم و Threshold را سخت‌گیرانه‌تر می‌گرفتیم، هر دسته تبدیل به مجموعه‌ای از کوئری‌های یکسان می‌شدند!نمونه‌ای از خروجی کارتشخیص سن و جنسیتمعرفی تیمتیم ویدیو کافه‌بازار با هدف پشتیبانی فنی و ارائه‌ی محتوا به کاربران ویدئو ایجاد شده‌ و با گسترش محتوا و تعداد کاربران این سرویس، مسائل و نیازهای دیتایی در این تیم افزایش پیدا کرده است. از طرفی سرویس ویدیو‌ی کافه‌بازار به صورت AVOD کار می‌کند. به طور خلاصه در سرویس AVOD محتوا به صورت رایگان به کاربر عرضه می‌شود و کاربر هزینه‌ای بابت اشتراک یا خرید تکی محتوا پرداخت نمی‌کند ولی در هر ویدیو تعداد محدودی تبلیغ مشاهده می‌کند. با توجه به اهمیت این مدل درآمدی، تیم تبلیغات ویدیو به صورت جداگانه بر روی مسأله‌ی تبلیغات تمرکز دارد. هدف این تیم، افزایش همزمان درآمد و رضایت کاربران است.تعریف مسألهسیستم‌ تبلیغات ویدیو‌ی کافه‌بازار شامل یک سمت عرضه (تبلیغ‌گذارنده) و یک سمت تقاضا (کاربر) است و در این بین سرویس تبلیغات تلاش می‌کند بهترین تطابق بین این دو گروه را فراهم کند. در راستای این تطابق، نیاز است که شناخت مناسبی از کاربران به صورت یک پروفایل ایجاد شود تا با استفاده از آن سیستم هدف‌گذاری تبلیغات اجرا شود. یکی از مهم‌ترین اجزای پروفایل کاربر، اطلاعات دموگرافیک (Demographic) آن و در راس آن سن و جنسیت کاربر است. به عنوان نمونه‌ای از کاربرد این اطلاعات می‌توان به پخش تبلیغات مربوط به بازار کودک (بازی‌ها و برنامه‌های مربوط به کودکان) برای افراد کم‌سن و یا پخش تبلیغات مربوط به برنامه‌های مالی و یا فروشگاهی به بزرگسالان اشاره کرد. بنابراین مساله‌ی تیم تبلیغات ویدیو، تخمین سن و جنسیت برای همه‌ی کاربران با حفظ حریم شخصی کاربران بود.چالش‌ها‌وراه‌حل‌ها:برای حل این مساله بهترین گزینه دریافت سن و جنسیت از برخی کاربران و تخمین آن برای سایر کاربران بود. در همین راستا با اعمال تغییراتی در برنامه و بعد از گذشت چند ماه، تعداد مناسبی از کاربران اطلاعات خود را وارد کردند. مشکل اطلاعات بدست آمده، وجود بایاس در نمونه بود. توزیع داده در نمونه با توزیع آن در واقعیت اختلاف داشت. دلیل این تفاوت به خاطر این بود که کاربرانی که سن و جنسیت خود را وارد می‌کنند کاربران فعال‌تر بازار هستند و به همین دلیل تفاوت‌هایی بین این نمونه از کاربران و کل کاربران وجود داشت که باید در نظر گرفته می‌شد.پس از دریافت سن و جنسیت تعدادی از کاربران ، بایستی یک مساله‌ی رگرسیون برای تخمین سن و یک مساله‌ی دسته‌بندی برای تخمین جنسیت حل می‌شد. نخستین چالش این مساله، جمع‌آوری دیتای مناسب بود. به طور خلاصه داده‌های مناسب برای حل این مساله شامل داده‌های رفتاری کاربر (زمان فعالیت در بازار و نوع فعالیت‌ها) و اطلاعات برنامه‌های دانلود شده و ویدیوهای مشاهده شده در بازار بود. به دلیل حجم بالای داده‌ی برنامه‌های دانلود شده، نیاز بود که embedding مناسبی از برنامه‌ها ایجاد شود تا بعد دیتا کاهش پیدا کند و ویژگی‌های مفیدی برای حل مساله ایجاد شود.با استفاده از روش‌های مختلف مانند تجزیه‌ی ماتریسی SVD و الگوریتم xgboost، به دقت مناسبی در پیش‌بینی جنسیت و خطای قابل قبولی در پیش‌بینی سن رسیدیم که برای کاربردهای هدف‌گذاری مورد نظر مناسب بود.جستجومعرفی تیمتیم جستجو بر دو اصل پیدا کردن و پیدا شدن بنا شده است. این تیم سعی می‌کند با استفاده از ابزارهای مختلف فرآیند پیدا کردن محتوای مورد نظر کاربر را بی‌دردسر و آسان کند، همچنین بستر مناسبی برای محتوای تولید شده توسط ناشران باشد تا با پیدا شدن محتواهای‌شان سبب رشد و شکوفا شدن پتانسیل‌های موجود در محصول‌شان گردد.تعریف مسألهروزانه حداقل هفت میلیون بار از سیستم جستجوی کافه‌بازار استفاده می‌شود. کاربران عبارات مورد نظر خود را در این سیستم می‌نویسند و این سیستم تلاش می‌کند از بین بیش از چهارصد هزار برنامه، بازی و ویدیو، لیستی مرتب شده از این محتواها را به کاربران نمایش دهد. مسأله رنکینگ در واقع مرتب کردن محتواهای موجود در کافه‌بازار با داشتن عبارت جستجو شده کاربر است که بیشترین شباهت به محتوای مورد نظر کاربر را داشته باشند.چالش‌ها‌وراه‌حل‌هابه منظور مرتب‌کردن محتواهای مرتبط با عبارت جستجو شده توسط کاربر، جستجوی کافه ‌بازار بر اساس سیستم تگ‌زدن طراحی شده است. منظور از تگ، کلمات و عبارات مرتبط با محتواهای کافه‌ بازار است. بر این اساس می‌توان گفت که در این سیستم به هر محتوا یک مجموعه‌ای از دوتایی‌های تگ‌ و وزن نسبت داده می‌شود. حال زمانی که کاربر عبارتی را جستجو می‌کند، ترکیب‌های مختلفی از واژگانی که در عبارت جستجو شده به کار رفته‌اند، مجموعه‌ی کلی از تگ‌های مرتبط با عبارت جستجو شده را می‌سازند. در این حالت تنها کافی است برای هر محتوا،‌ وزن تگ‌هایی از آن را که در داخل مجموعه تگ‌های عبارت جستجو شده هستند را جمع کنیم و پس از این کار محتواها را بر اساس بزرگی عدد ذکر شده مرتب کنیم و نتایج را به کاربر نمایش دهیم. به عنوان مثال فرض کنید کاربری عبارت «بازی جدید» را جستجو کند، مجموعه‌ی کلی از تگ‌های سازنده این عبارت می‌تواند به صورت «بازی»، «جدید» و «بازی جدید» باشد. حال فرض کنید یک محتوای خاصی دارای دو تگ «بازی» و «بازی جدید» با وزن‌هایی w و v باشد ولی تگ «جدید» را نداشته باشد. عدد نهایی نسبت داده شده به این محتوا در نمایش مرتب شده‌ی عبارت جستجو شده‌ی «بازی جدید» به صورت w+v خواهد بود.یکی از اصلی‌ترین چالش‌ها در ساختن چنین سیستمی، دستیابی به دوتایی‌های تگ و وزن بهینه برای هر محتوا هست. به منظور پیدا کردن تگ‌ها، آن‌ها را به صورت کلی به دو دسته تقسیم می‌کنیم:۱- تگ‌های ساخته شده از رفتار و بازخورد کاربران۲- تگ‌هایی که با استفاده از روش‌های الگوریتمی از دل خود محتواها بدست می‌آیند.تگ‌های نوع اول به همراه وزن‌شان با استفاده از بازخورد دریافتی از فعالیت کاربران هنگام جستجو یک عبارت، بازدید جزئیات یک برنامه یا ویدیو در صفحه سرچ و نصب کردن برنامه و یا مشاهده کردن ویدیو بدست می‌آیند. به طور کلی می‌توان گفت فعالیت کاربران در سیستم جستجو باعث به وجود آمدن چنین بازخوردهایی می‌شود و سیستم جستجو از این بازخوردها استفاده می‌کند تا تگ‌های مناسبی را به محتواها نسبت دهد. یکی از چالش‌های این قسمت هزینه‌ی محاسباتی بالایی بود که داشت و برای حل این چالش از روش‌های مبتنی بر MCMC استفاده می‌کنیم.تگ‌های نوع دوم نیز از ویژگی‌های ذاتی محتواها مانند نام و توضیحات موجود در صفحه جزئیات‌شان و یا شباهت‌های موجود میان دو محتوا استخراج می‌شوند.در نهایت برای نسبت دادن یک تگ خاص به یک محتوای خاص به همراه وزن آن، کافی است وزن‌های مختلف آن تگ را در روش‌های مختلف استخراج تگ برای آن محتوا داشته باشیم و یک ترکیب خطی از آن‌ها را به عنوان وزن‌ نهایی آن تگ اعلام کنیم. داشتن ترکیب خطی از این نظر دارای اهمیت است که می‌توان به روش‌های مختلف استخراج تگ و وزن، اهمیت‌های گوناگونی داد. به عنوان مثال فرض کنید برای محتوای خاصی تگ «بازی» از طریق سه نوع الگوریتم جدا با وزن‌های x، y و z بدست آمده است، دوتایی نهایی را به صورت «بازی» و «ax + by+ cz» می‌سازیم که a، b و c ضرایبی برای مشخص کردن اهمیت سه الگوریتم گفته شده هستند.سیستم پیشنهاد دهندهمعرفی تیمتیم دیسکاوری با هدف کمک به تجربه‌ی بهترِ کاربر از گشت‌وگذار در بازار تشکیل شده است. دسته‌بندی محتوا و ساخت لیست‌هایی چون در حال پیشرفت، پرفروش‌ترین‌ها و در حال رشد به عهده‌ی این تیم است. یکی از مسائل اصلی ما در بخش دیتای تیم دیسکاوری رساندن محتوای شخصی‌سازی شده به دست کاربر هست تا بتوانیم به هدف خود یعنی &quot;تجربه‌ی خوبِ گشت‌وگذار در صفحات بازار&quot; برسیم.تعریف مسألهبازار بیش از ۴۰ میلیون کاربر فعال دارد که به دنبال محتواهایی چون برنامه،بازی یا ویدیو هستند . پس ارائه‌ی لیست پیشنهادی از محتواها در تجربه‌ی گشت و گذار کاربر (discovery) از اهمیت بالایی برخودار است. پیاده‌سازی الگوریتمی که بتواند مقیاس‌پذیر (scalable) و در عین حال سرعت قابل قبولی داشته باشد می‌تواند خیلی در این تجربه‌ اثرگذار باشد.چالش‌ها‌وراه‌حل‌هاعلاوه بر اطلاعات مربوط به برنامه‌ها و ویدیو‌ها ،داده‌های تعاملی کاربران برای ساخت سیستم پیشنهاد‌دهنده موضوع مهمی است. این داده‌ها معمولا به طور غیرمستقیم میزان علاقه‌ی یک کاربر به یک محتوای خاص را نشان می‌دهند و شاید تصویر سلیقه‌ی افراد را بتوان در نحوه‌ی برخوردشان با این محتوا‌ها دید. ما برای جمع‌آوری و پردازش این‌ داده‌های بزرگ از ابزارهایی چون spark استفاده می‌کنیم تا آن‌ها را با فرمت و ساختار مناسب آماده کنیم.از برنامه‌هایی که از لحاظ معنایی به‌هم نزدیک هستند لیست‌هایی ساخته می‌شوند (برای مثال لیست &quot;بازی‌های ماشین سنگین&quot; یا لیست &quot;برنامه‌های رژیم غذایی&quot;). یکی از چالش‌هایی که با آن روبه‌رو بودیم، پیشنهاد دادن این لیست‌ها به کاربرانی بود که به معنای آن‌ها نزدیک هستند. برای حل این چالش به سراغ مدل‌های embedding رفتیم و علاوه بر کاربر، این لیست‌ها را هم در فضای برداری کاربران گنجاندیم و بدین ترتیب با استفاده از ضرب داخلی که در این فضای بُرداری تفسیر پذیر است، توانستیم این لیست‌ها را هم به کاربران پیشنهاد دهیم.چالش دیگری که در توسعه این سیستم داشتیم مسأله‌ی سرعت بود.محاسبه‌ی &quot;سطرهای پیشنهادی برای شما&quot; برای هر کاربر باید خیلی سریع حساب می‌شد تا بتوانیم تجربه‌ی خوبی را برای کاربر رقم بزنیم. برای همین به سراغ الگوریتم‌هایی مثل SAR یعنی Simple Adaptive Recommendation رفتیم. این رده از الگوریتم‌ها مبتنی بر ساخت یک ماتریس شباهت از آیتم‌ها به همدیگر هستند و علاوه بر سرعت بالا، precision بالایی را هم برای ما داشتند.یکی از روش‌های ما برای ساخت ماتریس شباهت استفاده از داده‌های رفتاری کاربران بود، به این طریق که ما هر کاربر را یک متن از کلماتی تصور می‌کنیم که هر کلمه‌‌ی این متن محتوایی هست که کاربر با آن تعامل مثبت داشته و این محتواها را با معیارهای مناسبی مرتب می‌کنیم (همچون متنی که از کلمات پشت سرهم تشکیل شده باشد) و از روش‌های nlp برای ساخت شباهت ماتریسی بین این کلمات که در اینجا محتواها هستند استفاده می‌کنیم. ما از روش‌های مختلفی چون  word2vec , tfidf , swivel  برای ساخت ماتریس شباهت جلو رفتیم و به عنوان source برای ساخت سطرهای &quot;پیشنهادی برای شما&quot; استفاده کردیم. همچنین از خیلی از این source ها برای سطر‌های &quot;برنامه‌های مرتبط&quot; و &quot;دیگران نصب کرده‌اند&quot; در صفحات جزئیات هر برنامه نیز استفاده کردیم. این سطرهای شخصی سازی شده تا ۵ برابر نصب در بازار را بهبود دادند.ما با ایده‌های متنوع، به دنبال خلق ارزشیم. مسائلی که در تلاش برای حلشان هستیم بسیار گسترده هستند و ما با بهینه‌سازی بخش‌ها و ایجاد بخش‌های جدید، یاد می‌گیریم که چطور با این مسائل دست و پنجه نرم کنیم. ما از آدم‌هایی که مشتاق درگیر شدن با چالش‌های دیتایی هستند و دنبال یادگرفتن و خلق ارزشند، دعوت می‌کنیم از طریق صفحه فرصت‌های شغلی کافه بازار برای پیوستن به ما اپلای کنند.</description>
                <category>کافه بازار</category>
                <author>تیم دیتا کافه‌بازار</author>
                <pubDate>Tue, 03 Nov 2020 15:40:44 +0330</pubDate>
            </item>
                    <item>
                <title>بازنویسی وب‌سایت کافه بازار</title>
                <link>https://tech.cafebazaar.ir/بازنویسی-وبسایت-کافهبازار-mr1gcwn9dzge</link>
                <description>در پاییز ۹۷ تصمیم گرفتیم وب‌سایت کافه‌بازار را باز‌نویسی کنیم تا اضافه کردن امکانات جدید به آن، ساده‌تر شود. انگیزه‌ی اصلی، اضافه کردن امکان تماشای ویدئو از طریق وب بود؛ ولی باید محتاطانه پیش می‌رفتیم تا متریک‌های فعلی (نظیر میزان دانلود اپ بازار) آسیب نبیند. نسخه‌ی قبلی legacy شده بود و توسعه روی آن راحت نبود. به مرور زمان امکانات جزئی زیادی اضافه شده بود که پشت هر کدام داستانی وجود داشت؛ از کد شامِد و دسترسی‌های برنامه گرفته تا لوگوی ای‌نماد و غیره. با بخش‌هایی سر‌و‌کار داشتیم که معلوم نبود چرا به آن شکل نوشته شده‌اند، یا متادیتاهایی که نمی‌دانستیم با چه هدفی اضافه شده‌اند. بخش‌هایی از کدها توسط کسانی نوشته شده بود که دیگر در شرکت حضور نداشتند و با مشکلاتی از این دست رو‌به‌رو بودیم.فارغ از وجود ریزه‌کاری‌ها، تعداد زیاد کاربران فعال، اعمال تغییرات را به شدت ترسناک می‌کرد. فضای کار محصول پروداکشن متفاوت از فضای یک پروژه‌ی استارتاپی است که هنوز کاربران چندانی ندارد و به تبع آن، اشتباهات هم هزینه‌ی چندانی نخواهند داشت. در فضای پروداکشن باید خیلی محتاطانه و محافظه‌کارانه حرکت کرد؛ هر تصمیمی باید کاملاً حساب شده گرفته شود و با برنامه‌ریزی، عملیاتی گردد.در این مسیر به چالش‌های زیادی برخورد کردیم. از مسائل مرتبط با معماری، لایه‌ی ارتباط با بکند، پشتیبانی از مرورگرها، SEO و SSR و PWA گرفته تا استراتژی انتشار و چالش‌های پرفرمنسی بعد از انتشار که در ادامه آن‌ها را بررسی می‌کنیم.تغییرات دیزاین ظاهری وب‌سایت از گذشته تا کنونگذشته‌ی Legacyدر گذشته‌های دور، بکند کافه‌بازار (شامل کدهای وب‌سایت)، یک سرویس یک‌پارچه (monolithic) به نام «کافه» بود که با جنگو نوشته شده بود. داخل همان کدبیس، کدهای وب‌سایت هم قرار داشته است (فولدر «پنجره»). بعدها به‌خاطر بزرگ شدن محصول بازار، تیم‌های متفاوتی تشکیل شدند که وظایف‌شان به فیچر خاصی محدود می‌شد. کد زدن روی یک کد‌بیس واحد دیگر راحت نبود و معماری بکند به سمت مایکروسرویس شدن پیش رفت. به مرور، هر تیمی قسمتی از کد «کافه» را برداشت و مایکرو‌سرویس‌های جدا بالا آورد و مسئول توسعه‌ی آنها شد. اما «پنجره» که همچنان به بخش زیادی از کدهای «کافه» وابستگی داشت، امکان جدا شدن کامل را نداشت.بنابراین از کد «کافه» یک کپی مجزا گرفته شد و «سرویس پنجره» نام گرفت. به مرور که در کد «کافه» تغییراتی داده می‌شده، در پنجره هم ـ تنها در صورت لزوم ـ همزمان اعمال می‌شده است. تغییرات وب‌سایت هم فقط در «سرویس پنجره» اعمال می‌شد. به دلایل امنیتی، «پنجره» امکان دسترسی مستقیم به دیتابیس اطلاعات کاربران را نداشت؛ بنابراین برخی صفحات وب‌سایت که به این اطلاعات نیاز داشتند، داخل «کافه» فولدر «پنجره» توسعه داده شدند. یک nginx با config بزرگ و نسبتاً پیچیده هم وجود داشت که تشخیص می‌داد برای هر urlی از چه سرویسی باید جواب بگیرد («سرویس پنجره» یا کافه؟).از سرویس یک‌پارچه به مایکروسرویس در گذر زمان - بخش اولپروتکل ارتباطی کلاینت با بکند در گذشته custom json-rpc بوده؛ در واقع همان JSON-RPC که رمزنگاری کلید عمومی به آن اضافه شده بود و مربوط به زمانی بود که به‌خاطر مشکلات تحریم امکان خرید ssl certificate وجود نداشت. سرویس‌های بکند هم در گذشته با همین پروتکل با هم ارتباط داشتند، اما بعدها تصمیم گرفته شد که به مرور زمان به سمت استفاده از grpc حرکت کنیم.امکان تغییرات سریع وجود نداشت؛ ممکن بود سرویسی grpc شود ولی یک سرویس قدیمی‌تر که با json-rpc کار می‌کرد به آن وابسته باشد. بنابراین یک gateway به اسم «دیلماج» (کلمه‌ی ترکی به معنی مترجم) توسعه داده شد که سرویس‌ها با پروتکل‌های مختلف (grpc یا json-rpc) در پشت آن قرار گیرند و کلاینت از طریق هر پروتکلی (json-rpc یا grpc یا rest) بتواند به دیلماج وصل شود. در عین حال زمانی که پیاده‌سازی وب‌سایت را شروع کردیم هنوز برخی سرویس‌ها پشت دیلماج نرفته بود و تنها راه ارتباطی با آن‌ها custom json-rpc بود که پیاده‌سازی آن سمت وب‌سایت پیچیده بود.مسیر آینده‌ی محصول این بود که اپ اندروید و وب‌سایت هر دو از طریق rest با بکند ارتباط برقرار کنند و برای کوتاه مدت تصمیم نداشتیم custom json-rpc را سمت وب‌سایت پیاده‌سازی کنیم؛ بنابراین سرویس جدیدی به اسم «کوشک» را کلید زدیم که در پشت آن دیلماج و سرویس‌های خاص قرار می‌گرفت. البته بعد‌ها که همه‌ی سرویس‌ها پشت دیلماج قرار گرفت، سرویس کوشک هم باید حذف می‌شد.از سرویس یک‌پارچه به مایکروسرویس در گذر زمان - بخش دومتصویر معماری خیلی خلاصه شده‌ است و تعداد سرویس‌های بکندی بیشتر از این است. این سرویس‌ها ممکن است مستقیماً یا به‌واسطه‌ی دیلماج به هم ریکوئست بزنند. در عین حال سرویس‌هایی که وب‌سایت با آن‌ها ارتباط مستقیم دارد این‌ها هستند:اکانت: امکانات مرتبط با احراز هویت که jwt برمی‌گرداند، refresh token می‌دهد و غیره.فهرست: لیست‌های اپ/ویدئو که صفحات اصلی و لیستی (تازه‌ها، برترین‌ها، بروزشده‌ها و …) را به کمک یک سری سرویس‌های دیگر تولید می‌کند و تحویل کلاینت می‌دهد.سرچ: جستجو در اپ‌ها و فیلم‌ها که ساختار خروجی شبیه به فهرست برمی‌گرداند؛ و امکان پیشگوی متن جستجو که وقتی کاربر در فیلد جستجو مشغول تایپ است یک سری موارد پیشنهادی ارائه می‌دهد.سجل: یا همان شناسنامه‌ی اپ که اطلاعات یک اپ خاص را به همراه اپ‌های مرتبط، لینک رسانه‌ها، تعداد نصب و هر چیز مرتبط با اپ جمع می‌کند و یک‌جا به کلاینت تحویل می‌دهد.ویدئو: فیچرهای مرتبط با ویدئو، صفحات فیلم یا اپیزود یک سریال، امکان پخش ویدئو و غیره.پرداخت: فیچرهای مرتبط با خرید محصول (از طریق وب‌سایت فقط می‌توان ویدئو خریداری کرد).ظهور یک باریستانام سرویس وب‌سایت جدید کافه‌بازار را «باریستا» گذاشتیم؛ بالاخره کافه نیاز به یک باریستا هم داشت. استک فرانت‌اند را بعد از بحث و بررسی vue/nuxt انتخاب کردیم. دلیل انتخاب vue یکپارچگی با بقیه‌ی پروژه‌های فرانت‌اندی محصول بازار بود تا در آینده که احتمالاً افراد دیگر هم درگیر می‌شوند، سربار یادگیری و جابجایی بین دو framework متفاوت را نداشته باشند. انتخاب nuxt هم با توجه به نیاز SSR بود که در بخش «دغدغه‌های SEO» به آن پرداخته خواهد شد.چالش جدی درون وب‌سایت نمایش لیست‌های فهرست بود. این لیست‌ها ساختار متفاوتی دارند که از سمت بکند مشخص می‌شود؛ می‌تواند سطری یا ستونی باشد، عناصر می‌توانند اپ، فیلم، سریال یا تبلیغات باشند و غیره. کامپوننت‌های فهرست را طوری نوشتیم که برای «نتایج جستجو» یا «لیست اپ‌های مرتبط» هم بتوانیم از آن‌ها استفاده کنیم. سرویس‌های «سرچ» و «فهرست» و «سجل» (بخش اپ‌های مرتبط) ساختار داده‌ی یکسانی نداشتند و باید این دیتاها همسان‌سازی می‌شد که در لایه‌ی api این کار را انجام دادیم (در بخش ارتباط با بکند بیشتر به این موضوع خواهیم پرداخت).همچنین ریکوئست زدن داخل کامپوننت‌های vue را تا جای ممکن به asyncData که nuxt روی کامپوننت‌های page ارائه می‌دهد محدود کردیم و wrapper نوشتیم تا در صورت بروز هر گونه خطا در برقراری ارتباط با سرور، خطای مناسبی به کاربر نمایش دهیم (انواع خطاها نیز در لایه‌ی api همسان‌سازی می‌شوند و همیشه ساختار یکسان و قابل پیش‌بینی‌ای دارند). بدین ترتیب لازم نبود هر بار سناریوی خطاهای مختلف را بصورت جداگانه بررسی کنیم.ارتباط با بکندزمانی که وب‌سایت جدید را کلید زدیم، وضعیت چندان جالب نبود. apiها و پروتکل ارتباطی در حال تغییر بودند، فهرست هنوز پشت دیلماج نبود و باید json-rpc ریکوئست میزدیم، فیچرهایی مثل اکانت هنوز در حال توسعه بود و مایکرو‌سرویس جدا نشده بود. علاوه بر مشکلات پروتکل ارتباطی، خود سرویس‌ها هم خروجی متفاوتی برای موضوعات مشترک می‌دادند. مثلاً «لیست اپ‌های مرتبط» یا «نتایج جستجو» که از نظر ظاهری قرار بود شبیه «لیست‌های فهرست» رندر شود، ولی ساختار داده‌ی متفاوتی را برمى‌گرداند و مواردی از این دست.تصمیم بر این بود که در آینده این مسائل سمت بکند اصلاح شود؛ ولی همیشه مسأله‌ی پشتیبانی از نسخه‌های قدیمی «کلاینت اندروید بازار» این مسیر را دشوار و سخت می‌کند. اگر ساختار داده‌ی خروجی یك api تغییر کند، باید همچنان مثل قبل به کلاینت‌های قدیمی ارسال شود؛ تا زمانی که آن نسخه از کلاینت اندروید منسوخ شود و اصطلاحاً force update دهيم تا کاربرها مجبور به آپدیت به نسخه‌ی جدید شوند. البته این force update دادن معمولاً پر‌هزینه است و آسان نیست (آخرین بار فکر می‌کنم سال‌ها پیش دادیم). علاوه بر این ‌minimum sdkی کلاینت اندروید بازار بالا رفته و بعضی نسخه‌های قدیمی اندروید را باید همچنان با نسخه‌های قدیمی کلاینت پشتیبانی کنیم.با توجه به این مسائل باید از ابتدا به فکر راهکاری می‌بودیم تا در آینده این سبک تغییرات api هزینه‌ی زیادی برای نگهداری وب‌سایت کافه‌بازار نداشته باشد و با هر تغییر api مجبور نباشیم تعداد زیادی کامپوننت را آپدیت کنیم. راهکاری که انتخاب کردیم این بود که داخل باریستا یك لایه‌ی api نوشتیم که بعد از ریکوئست زدن، کل دیتاها را normalize کند و با ساختار همسان به کامپوننت‌ها برگرداند؛ با اين روش کامپوننت‌ها دیگر وابستگی مستقیم به apiهای بکند نداشتند. این لایه‌ی api کاملاً مستقل از framework فرانت‌اندی نوشته شد تا در آینده در صورت نیاز reusable باشد. تمامی کدهایی که وظیفه normalize کردن دارند را هم wrap کردیم تا در صورت بروز خطا، صرفاً به sentry لاگ بفرستند و از قسمت مشکل‌دار داده (به دلیل تغییرات احتمالی بکند) صرف‌نظر کنند.همچنین به این لایه امکان cache اضافه کردیم تا در کامپوننت‌ها (تا جای ممکن) نیاز به store نداشته باشیم (سادگی پیاده‌سازی) و نگران بهینه‌سازی ریکوئست تکراری زدن هم نباشیم.لایه‌ی API: شکستن دیتای سرویس‌ها و همسان‌سازی برای استفاده در کامپوننت‌های فرانت‌اندپشتیبانی از مرورگرهابا توجه به تعداد زیاد کاربران کافه‌بازار، گستره‌ی مرورگرهای مورد استفاده هم خیلی وسیع است. بیشتر کاربرها موبایلی هستند و با هدف دانلود اپ بازار وارد وب‌سایت می‌شوند؛ در نتيجه صفحه‌ی دانلود اپ بازار مهم‌ترین قسمت وب‌سایت از دید محصولی است که تحت هر شرایطی باید درست کار كند.با توجه به این مسائل کاربران وب‌سایت را به دو دسته تقسیم کردیم: ۱. کاربری که از مرورگر مدرن استفاده می‌کند و می‌توانيم وب‌سایت را به او نشان دهيم. ۲. کاربری که از مرورگر قدیمی استفاده می‌کند و باید مستقیماً به صفحه‌ی دانلود هدایت شود.صفحه‌ی دانلود را بدون استفاده از library خاصی و به‌گونه‌ای که روی قدیمی‌ترین مرورگر‌ها هم درست کار کند نوشتیم. برای بقیه‌ی وب‌سایت، browserslist داخل پروژه را با توجه به گستره‌ی مرورگرهایی که تصمیم داشتیم پشتیبانی کنیم کانفیگ کردیم و از پلاگین eslint-plugin-compat استفاده کردیم تا مطمئن باشیم کدهایی که می‌نویسیم این مرورگر‌ها را پشتیبانی می‌کند. در ادامه سمت سرور و با استفاده از browserslist-useragent و شناسایی مرورگر کاربر، در صورتی که مرورگر در لیست پشتیبانی شده نبود، کاربر را به صفحه‌ی دانلود اپ بازار هدایت کردیم (هر دوی این libraryها از browserslist داخل پروژه استفاده می‌کنند).البته تحت شرایط دیگرى هم کاربر به صفحه‌ی دانلود هدایت می‌شود تا تشویق به نصب شود. اگر user-agent اندروید باشد و صفحه‌ی باز شده مرتبط به اپ‌های اندروید باشد، نسخه pwa را باز نکرده باشد، اگر خودش روی لینک «باز کردن وب‌سایت» در صفحه‌ی دانلود کلیک نکرده باشد و غیره. آیتم‌های صفحه‌ی دانلود بازار (لینک دانلود فایل apk) هم با توجه به user-agent و نسخه‌ی اندروید کاربر می‌تواند متفاوت باشد (یک فایل ejs با توجه به یک کانفیگ رندر می‌شود تا انتشار کلاینت نیاز به انتشار وب‌سایت نداشته باشد).اطمینان از پشتیبانی مرورگر به کمک linterدغدغه‌های SEOبعد از صفحه‌ی دانلود اپ بازار، مهم‌ترین کاربرد وب‌سایت، جذب کاربران از طریق موتور جستجو است. وب‌سایت‌هایی که SPA نوشته می‌شوند (سمت مرورگر رندر می‌شوند) احتمال دارد توسط برخی از crawlerها به درستی رندر نشوند (گوگل در سال ۲۰۱۵ اعلام کرده كه جاواسکریپت رندر می‌کند اما بعداً در سال ۲۰۱۹ پیشنهاد داده كه به هر حال بهتر است رندر سمت سرور هم داشته باشیم). علاوه بر موتور جستجو، سرویس‌های دیگرى (همچون شبکه‌های مجازی) هم هستند که از متادیتاها (همچون پروتکل opengraph) برای نمایش بهتر لینک‌ها استفاده می‌کنند.بنابراین لازم است که صفحات سایت سمت سرور هم رندر شوند و html خالی به کلاینت ارسال نشود. این رندر کردن می‌تواند به روش‌های مختلفی اتفاق بیافتد. یک راه حل ساده استفاده از ابزارهایی مثل puppeteer و rendertron و prerender و غیره است که به عنوان سرور جدا بالا باشند و بر اساس user-agent تصمیم بگیریم که صفحه‌ی pre-render شده را برگردانيم یا نسخه SPA را.فلوچارت کلی جهت پیاده‌سازی راهکار CSRمزیت این راهکار سادگی پیاده‌سازی است. اما CPU بیشتری مصرف می‌کند و مهم‌تر اینکه امکان ارائه‌ی این html رندر شده به کاربر مرورگر معمولی نیست؛ چرا که امکان rehydration این html با DOM که سمت کلاینت تولید می‌شود وجود ندارد. در نتیجه سرعت بارگذاری محتوای اولیه (اولین لحظه‌اى که کاربر می‌تواند محتوای معنادار ببیند) در مرورگر معمولی کندتر می‌شود و همچنین امکان رندر برخی متاتگ‌های وابسته به دیتا هم نیست. مورد بعدی اینکه لاجیک رندر کردن با توجه به اینکه رندر کدام سمت اتفاق افتاده نمی‌تواند متفاوت باشد؛ مثلاً تگ img یا برخی کامپوننت‌ها سمت سرور کامل رندر می‌شوند، اما سمت کلاینت به دلایل پرفرمنسی lazy-load هستند (در بخش «چالش‌های پرفرمنسی» توضیح داده شده است).راهکار دوم SSR است. با لحاظ کردن تفاوت‌های مرورگر و nodejs می‌توانيم این امکان را فراهم کنیم تا رندر شدن در یک سرور nodejs و مشابه مرورگر اتفاق بیافتد. با توجه به اینکه پیاده‌سازی SSR چالش‌ها و edge-caseهای زیادی دارد تصمیم گرفتیم از nuxt استفاده کنیم تا سرعت توسعه‌ی بالاتری برايمان به ارمغان بیاورد. به علاوه دیگر لازم نبود درگیر کارهای زیرساختی شويم (می‌خواستیم سریع‌تر به نتیجه برسیم و فرانت‌اند توسط یک نفر نوشته میشد).ساز و کار پیاده‌سازی راهکار SSRچالش دیگر، url structure سایت بود که در نسخه‌ی قدیمی چندان search engine friendly نبود، از طرفی هم وابستگی deep-linkهای کلاینت اندروید قدیم امکان تغییرات این url structure را سخت می‌کرد. با توجه به اینکه نسخه‌ی جدید اپ اندروید هم در حال بازنویسی بود، تصمیم گرفتیم سمت وب چند مدل url structure را هم‌زمان پشتیبانی کنیم و همچنان مدل قدیم را به عنوان canonical ست کنیم تا لینک‌های گوگل همچنان امکان باز شدن توسط کلاینت اندروید را داشته باشند. به این ترتیب نسخه جدید اپ می‌تواند از url structure جدید پشتیبانی کند و در آینده که نسخه‌ی قدیمی اپ منسوخ شد (force-update دادیم) آدرس canonicalها را هم اصلاح کنیم.با توجه به اینکه nuxt ساختار url را از روی فولدرها می‌سازد، یک فایل routes.json جدا شامل انواع routeها ساختیم و از روی آن روتر nuxt را extend کردیم. یک تابع کمکی هم برای تولید لینک‌های داخل کامپوننت‌ها نوشتیم که اسم و پارامترهای یک route را می‌گیرد و با توجه به routes.json و با استفاده از path-to-regexp آدرس لینک را تولید می‌کند.نسخه PWAبا توجه به اضافه‌شدن امکان تماشای ویدئو، تمایل داشتیم کاربران iOS را هم جذب کنیم و تصمیم گرفتیم نسخه‌ی PWA را با کیفیت قابل قبولی ارائه دهیم. کیفیت قابل قبول به این معنا که هم Progressive باشد و هم Responsive، جابجایی بین صفحات سریع باشد (SPA)، سایت بدون دسترسی به اینترنت قابل نمایش باشد (Offline Mode)، لینک دادن به صفحات مختلف سایت ممکن باشد، قابلیت نصب توسط مرورگر به کاربر اعلام شود و ...پیشرو (Progressive)پیشرو بودن به این معنی است که گستره‌ی مرورگرهای قدیمی مورد استفاده توسط کاربران، نباید باعث شود از امکانات جدید مرورگرها استفاده نکنیم و کدبیس legacy تولید کنیم. اصل کد با syntax مدرن نوشته شود اما برای پشتیبانی مرورگرهای قدیمی transpile شود و apiهای جدید تا جای ممکن polyfill شوند. اگر در مواردی امکانش نبود (برخی امکانات css)، سایت همچنان قابل استفاده باشد و اصطلاحاً downgrade شود. البته حداقل را روی مرورگرهایی گذاشتیم که flex-box را پشتیبانی می‌کنند.پاسخگو (Responsive)برای سایز موبایل، تبلت و دسکتاپ تجربه کاربری متناسبی ارائه دهیم. نمونه‌ی آن کامپوننت دراور موبایلی است که در سایز کوچک جایگزین navbar بالای سایت می‌شود. یا دکمه‌ی back گوشی که باعث بسته شدن مودال‌ها می‌شود. یا صفحات اپ/ویدئو و غیره که با توجه به سایز مرورگر، خودشان را تطبیق می‌دهند.قابل کشف (Discoverable)برای اینکه مرورگر امکان نصب نسخه PWA را به کاربر نمایش دهد، یک روش هندل کردن رویداد beforeinstallprompt توسط برنامه‌نویس است که پیاده‌سازی اصولی آن نیازمند تحقیق و بررسی بیشتر بوده و هنوز این راهکار را پیاده‌سازی نکرده‌ایم.راه دیگر، تنها ارائه‌ی فایل manifest.json است که اپلیکیشن بودن سایت را اعلام می‌کند. با توجه به اینکه چندین نسخه از وب‌سایت همواره فعال است (استیجینگ برای تست داخلی، بتا تست بیرونی و نسخه پروداکشن)، این فایل با توجه به نسخه باید متفاوت باشد تا لوگو/عنوان نسخه‌های PWA متفاوت باشد.یک راهکار این بود که این فایل در پایپلاین CI و با توجه به یک متغیر env متفاوت تولید شود، راه دیگر این بود که توسط سرور node و به‌صورت داینامیک (با توجه به hostname) تولید شود. با توجه به استراتژی انتشار، راه دوم را انتخاب کردیم که در ادامه بیشتر آن را توضیح خواهیم داد.قابل پیوند (Linkable)تمامی صفحات سایت لینک داشته باشند و بتوان لینک‌ها را به اشتراک گذاشت. لازمه‌‌ی این کار آن است که تا جای ممکن از store یا state داخل کامپوننت استفاده نشود. حتی زبان سایت از طریق url مشخص می‌شود تا لینک دادن به زبانی خاص از یک صفحه ممکن باشد (در این مورد توانایی پشتیبانی از hreflang هم حائز اهمیت بود).مستقل از اتصال به شبکه (Offline-Mode)در صورت عدم اتصال اینترنت، نسخه‌ی PWA باید به‌صورت مینیمال باز شود و شبیه اپ واقعی رفتار کند (دایناسور معروف دیده نشود). لیست assetها در پایپلاین build تولید شده و داخل فایلی ذخیره شوند تا بعد از اولین بارگذاری pre-cache شود و در صورت نبود اینترنت سایت بالا بیاید.همچنین نمی‌توانستیم تمامی صفحات و routeها را pre-cache کنیم؛ nuxt هم بعد از رندر SSR، سمت کلاینت و بر اساس url رندر مجدد انجام نمی‌دهد. با توجه به این مسأله لازم بود که یک آدرس مصنوعی dummy وجود داشته باشد که نسخه‌ی SPA است و SSR روی آن همواره غیر فعال است (سمت مرورگر و بر اساس آدرس رندر می‌شود). این آدرس به عنوان navigation route برای workbox رجیستر و pre-cache می‌شود. بدین ترتیب برای تمامی navigationهای سمت کلاینت همین آدرس رندر می‌شود و وقتی دسترسی به اینترنت وجود ندارد، تمامی صفحات می‌توانند تشخیص دهند و پیغام خطای مناسب را در قالب اپلیکیشن نمایش دهند.ریلود خودکاریک مسأله‌ی مهم در ارتباط با service worker، مشکلات عملکردی دکمه ریلود مرورگر است. مایل بودیم بعد از هر انتشار، حتی اگر چندین تب از وب‌سایت فعال است همگی ریلود شوند و نسخه‌ی جدید را نمایش دهند. برای حل این موضوع زمانی که مرورگر، service worker جدید را تشخیص می‌دهد، از طریق service worker به همه‌ی تب‌های باز دستور ریلود شدن ارسال می‌کنیم.همچنین برای مواقع ضروری یک api روی سرور node اضافه کردیم که شماره نسخه فعلی باریستا را برمی‌گرداند، و سمت فرانت بصورت دوره‌ای (هر ۱۰ دقیقه یک بار) این api را فراخوانی می‌کنیم تا در صورتی که قسمت minor یا major در نسخه تغییر کرده بود، cache و localStorage پاک و صفحه ریلود کامل شود. این مساله برای مواقعی است که ساختار داده‌ای که در cache یا localStorage ذخیره می‌شود در نسخه‌ی جدید عوض شده (مثلا داده‌ای که به صورت عددی ذخیره شده، باید آرایه باشد) و به خطا می‌خوریم.ارتباطات کلاینت/سرور باریستا برای پیاده‌سازی PWAاستراتژی انتشاراستراتژی انتشار نهایی هم بدون چالش نبود. تصمیم داشتیم همواره یک انتشار جدا برای تست انسانی قبل از انتشارهای نهایی داشته باشیم و بعد از اطمینان از اینکه همه چیز به درستی پیاده‌سازی شده (فیچر، ui و غیره) همین نسخه را به پروداکشن انتقال دهیم. کاربر فعال وب‌سایت در لحظه چیزی بین ۳ تا ۱۲ هزار نفر است و با توجه به اینکه مدام روی وب‌سایت تغییرات داریم، به دنبال راهکاری بودیم تا در لحظه‌ی انتشار down-time نداشته باشیم و هیچ کاربری به مشکل برنخورد.استراتژی متداول انتشار سرویس‌های بکندی روی کوبرنتیز Rolling Deployment است. به این ترتیب که podهای نسخه‌ی قدیم کم‌کم با نسخه‌ی جدید جایگزین می‌شوند و طی مدتی محدود نسخه‌ی قدیم و جدید همزمان فعال است. این استراتژی برای وب‌سایت این مشکل را ایجاد می‌کرد که ممکن بود مثلا ریکوئست یک صفحه‌ html و ریکوئست assetهای آن به podهای نسخه‌های مختلف ارسال شود و در نتیجه برخی assetها خطای ۴۰۴ بگیرد (نام فایل‌ها content-hash است و احتمال دریافت فایل اشتباه وجود ندارد).این مسأله بعداً از طریق آپلود فایل‌های استاتیک روی CDN هم قابل حل بود (هم‌زمان assetهای چندین نسخه می‌تواند در یک bucket موجود باشد)، اما باز هم مشکل برای فایل service worker وجود داشت (باید از مسیری دریافت شود که قرار است ریکوئست‌هایش را کنترل کند و نمی‌تواند روی CDN ریخته شود) و ممکن بود انتشار جدید، از service worker مربوط به انتشار قدیم استفاده کند و لیست فایل‌هایی که باید pre-cache شوند اشتباه باشد.در نتیجه بهترین راهکار، پیاده‌سازی استراتژی انتشار آبی/سبز بود. به این شکل که در لحظه دو نسخه به‌صورت موازی بالا است و بعد از trigger توسط gitlab-ci با هم جابه‌جا می‌شوند. یکی از نسخه‌ها (آبی یا سبز) به عنوان نسخه‌ی پروداکشن (با چند replica) و دیگری به عنوان نسخه‌ی استیجینگ (یک replica) کاربرد خواهد داشت.استراتژی دپلویمنت آبی/سبز برای انتشار آنی و Zero-Downtimeاین استراتژی (جابجایی آنی deployment) روی باقی تصمیمات معماری هم تاثیرگذار است. مهم‌ترین تاثیر آن این است که همواره فقط یک build واحد می‌تواند وجود داشته باشد و نمی‌شود با توجه به متغیرهای env خروجی‌های متفاوتی تولید کرد. اگر جایی لازم باشد، باید خروجی به‌صورت داینامیک و بر اساس hostname که ریکوئست می‌آید تولید شود. مواردی از جمله:مقدار ua-id که برای google analytics استفاده می‌شودعنوان نسخه‌ی PWA که می‌توان روی گوشی نصب کردهدر X-Robots-Tag که ارسال می‌شود و مجوز crawl شدن را برای نسخه‌های بتا و استیجینگ به موتور‌های جستجو نمی‌دهدو غیره.چالش‌های پرفرمنسبعد از انتشار وب‌سایت متوجه یک سری مشکلات پرفرمنسی در مرورگر و همچنین سمت سرور شدیم که برای هر کدام باید چاره‌ای می‌اندیشیدیم. سمت مرورگر اینترنت مصرفی بالا بود و بارگذاری کامل صفحات نسبتاً زمان‌بر بود. سمت سرور هم به‌خاطر رندر SSR مصرف CPU خیلی بالا بود و هر از چندی podها توسط کوبرنتیز کُشته و منجر به downtime می‌شد. برای هر کدام از موارد فوق راهکارهایی در نظر گرفتیم.استراتژی باندلینگحجم باندل js تولید شده بزرگ بود و تقریباً نیمی از آن مربوط به video-js بود که فقط برای صفحات ویدئو استفاده کرده بودیم. برای حل این مسأله، فایل‌های js بزرگ را Dynamic Import کردیم تا تنها در زمان نیاز دانلود شوند، webpack را هم کانفیگ کردیم (از طریق nuxt) تا کدهای vendors و common به‌صورت باندل‌های جداگانه ساخته شوند و سمت مرورگر بهتر cache شوند. با توجه به اینکه صفحات مختلف سایت از کامپوننت‌های عموماً یکسانی استفاده می‌کنند، جدا کردن chunk صفحات آورده‌ی چندانی نداشت و همه را در یک فایل build کردیم.تصاویر Responsive و Lazyاکثریت صفحات وب‌سایت دارای تصاویر زیادی هستند. در بکند چندین سایز از تصاویر اپ‌ها موجود است، سمت فرانت باید از امکانات جدید html5 برای نمایش responsive تصاویر استفاده می‌کردیم تا کاربر با توجه به سایز صفحه، کوچکترین تصویری که با کیفیت خوب نمایش داده می‌شود را دریافت کند. همچنین تصاویر باید lazy-load می‌شد تا فقط در صورتی که در صفحه دیده می‌شوند از سرور دریافت شوند.برای حل این موضوع یک کامپوننت LazyImg نوشتیم که سایز پیش‌فرض را به عنوان پارامتر می‌گیرد و تا وقتی دیده نمی‌شود، به عنوان تصویر پیش‌فرض یک مربع خاکستری رندر می‌کند (یک data-uri که از روی svg string تولید شده است). بعد از اینکه تصویر دیده شد (احتمالاً بعد از اسکرول)، تگ picture با سایزهای responsive رندر می‌شود.وجود تصویر پیش‌فرضی که نیاز به ارتباط با سرور ندارد و به عنوان src ست می‌شود، باعث می‌شود مرورگر تا لحظه‌ای که تصویر اصلی کاملاً لود نشده، همان پیش‌فرض را نشان دهد و در نتیجه در حین لود شدن تصویر، لایوت صفحه جابجا نشود و حس بدی را به کاربر منتقل نکند.در این کامپوننت همچنین امکان auto-reload را هم گذاشتیم که اگر بارگذاری تصویر به دلیل اختلالات شبکه/اینترنت به خطا خورد، پس از وقفه‌ای کوتاه خودش تلاش مجدد را انجام دهد و نیاز به ریلود صفحه توسط کاربر نباشد. برای همه‌ی تصاویر داخل وب‌سایت از این کامپوننت استفاده کردیم که حجم اینترنت مصرفی را به میزان قابل توجهی کاهش داد.تصاویر lazy-load، یک راهکار بهینه‌سازی پرفرمنس در مرورگرانتقال Assetها به CDNغیر از تصاویر اپ‌ها که توسط سرویس‌های دیگری نگهداری و به CDN منتقل می‌شود، تعداد assetهای تولید شده‌ی باریستا کم نبود. از فونت‌ها و تصاویر لوگو و غیره گرفته تا فایل‌های js/css که در فرایند build تولید می‌شوند. قبلاً این فایل‌ها توسط nginx که داخل pod باریستا بود serve می‌شد. اوایل که باریستا را منتشر کردیم، service worker را غیرفعال کرده بودیم و مشکل حادی نداشتیم. اما بعداً که تصمیم گرفتیم service worker را هم فعال کنیم، بخاطر pre-cache شدن assetها تعداد ریکوئست‌هایی که به pod می‌آمد تا حدود ۲ برابر زیاد شد.بنابراین تصمیم گرفتیم در فرایند build تمامی فایل‌های static تولید شده را به CDN کپی کنیم که در نهایت باعث کاهش ۳۰درصدی مصرف CPU روی pod باریستا شد.ماژول Cache برای رندرهای SSRبرخی صفحات خاص (مثل اپ‌هایی که کاربران زیادی از سایت‌شان به بازار می‌آیند، یا صفحه‌ی اصلی بازار، یا صفحاتی که روی آن‌ها کمپین تبلیغاتی می‌رفتیم)، تعداد ریکوئست زیادی داشتند و هر بار رندر SSR هزینه‌ی زیادی داشت. بنابراین برای nuxt یک ماژول cache نوشتیم که صفحات رندر شده را داخل redis به مدت یک ساعت نگه می‌دارد و ریکوئست‌های بعدی از این cache استفاده می‌کنند.رندرهای SSR هوشمندبا وجود همه‌ی راهکارهای فوق، همچنان بار روی سرور باریستا زیاد بود. هر از چندی که ریکوئست‌ها peak می‌زد (به صفحات متنوع، نه یکسان)، مصرف CPU بعضی podها بالا می‌رفت و کوبرنتیز pod مربوطه را می‌کُشت و تا pod دوباره بالا برگردد، بار روی باقی podها تقسیم می‌شد؛ در نتیجه قبل از بالا اومدنِ pod کشته شده، باقی podها مثل دومینو یکی بعد از دیگری کُشته می‌شد و بعد از چند بار کُشته شدن در CrashLoopBackOff می‌افتاد و وب‌سایت کاملاً down می‌شد.برای حل این مسأله یک دقیقه زمان warmup تعیین کردیم که پس از بالا آمدن pod، در این بازه رندر SSR انجام ندهد. به این ترتیب بعد از بالا آمدن مجدد، این فرصت فراهم بود که تا زمانی که باقی podها هم بالا بیاید، مصرف کمتری داشته باشد و کُشته نشود.در ادامه rate-limit روی رندرهای SSR گذاشتیم که تعداد رندرهای SSR برای هر IP در دقیقه را محدود کنیم. همچنین podها مصرف CPU خودشان را مانیتور کنند و در صورتی که مصرف CPU در یک ثانیه گذشته آن‌ها بیشتر از ۸۰ درصد شد، SSR ارائه ندهند.روند کلی و ساده‌شده‌ی رندرهای SSR هوشمندنتیجه نهاییوقتی یک محصول با کیفیت مناسب پیاده‌سازی و اجرا شده باشد، در بهترین شرایط هم نباید توقع تمجید را از سوی کاربر داشته باشیم. درست کار کردنِ یک سیستم انتظار طبیعی هر کاربری است، اما اگر کوچکترین اشتباهی رخ دهد باید منتظر انتقاد و نارضایتی باشیم. خوشبختانه در مورد وب‌سایت جدید بازار (با وجود کاربران زیاد) اعلام نارضایتی ناچیز بود و آمار دانلودی که در نظر داشتیم آسیبی نبیند، حتی برای مدتی رشد ۱۰-۱۵ درصدی داشت. (البته این آمار بعداً به روال معمول قدیم برگشت).پایداری سرویس‌هایی که روی کوبرنتیز هستند، نسبتاً بالاست. هر سرویس چندین replica دارد و درصورتی که هر کدام از آن‌ها به مشکل بربخورند، باقی podها پاسخگو هستند تا اینکه کوبر pod جدیدی بالا بیاورد. بزرگترین مسأله‌ی باریستا محدودیت منابع بود که هر از چندی منجر به down شدن می‌شد و بعد از پیاده‌سازی cache و warmup و rate-limit به‌طور کامل برطرف شد. پایداری بالایی که آخر هفته‌ی آرام و بدون استرسی را برایمان به ارمغان آورد.در عین حال شاید بتوان گفت که مهمترین موفقیت حاصل از بازنویسی وب‌سایت، خلاصی از یک سیستم legacy بود که امکان توسعه‌ی بیشتر را واقعاً سخت کرده بود.در کافه‌بازار هنوز راهی طولانی در پیش داریم و روز به روز با چالش‌های جدیدتری روبه‌رو می‌شویم. سرویس‌های فرانت‌اندی زیادی هستند که یا هنوز نوشته نشده‌اند و یا نیاز به بازنویسی دارند. اگر به زمینه‌ی فرانت‌اند علاقه‌مند هستید، برای همراهی ما در این مسیر به ما بپیوندید.</description>
                <category>کافه بازار</category>
                <author>آرش شاکری</author>
                <pubDate>Sun, 29 Dec 2019 18:23:03 +0330</pubDate>
            </item>
                    <item>
                <title>بازطراحی «صفحه‌ی جزئیات برنامه‌ها» در نسخه‌ی جدید برنامه‌ی «بازار»</title>
                <link>https://tech.cafebazaar.ir/bazaar-app-detail-srbnvybatnju</link>
                <description>بهبود تجربه‌ی کاربری یکی از موارد مهم در بازطراحی هر برنامه است. این امر در نسخه‌ی جدید «بازار» نیز نقش مهمی داشت. انتشار نسخه‌ی ۷ بازار به سه سال پیش بازمی‌گشت. این نسخه از نظر تجربه‌ی کاربری مشکلاتی داشت که یکپارچه‌نبودن اجزای طراحی، دسترس‌پذیرنبودن، رعایت‌نشدن کنتراست رنگ و... از جمله‌ی آن‌هاست. در تیم طراحی بازار ما تصمیم گرفتیم با رفع این مشکلات در نسخه‌ی ۸، تجربه‌ی کاربری بازار را بهبود ببخشیم.یکی از بخش‌های مهم در بازار «صفحه‌ی جزئیات برنامه» است که تمامی اطلاعات مربوط به یک برنامه یا بازی را در‌ بر دارد؛ ازجمله‌ی اطلاعات مربوط به این صفحه می‌توان به نام برنامه یا بازی، نام توسعه‌دهنده، اطلاعاتی که توسعه‌دهنده درباره‌ی برنامه‌ی خود می‌نویسد، امتیازات کاربران و مواردی از این دست اشاره کرد. روزانه کاربران بیش از شانزده میلیون بار از آن بازدید می‌کنند؛ همین میزان از تعامل کاربران با این صفحه نیاز به بازبینی در طراحی آن را ضروری ‌می‌کرد.در ادامه‌ی این مطلب به هفت مورد از مهم‌ترین تغییراتی اشاره می‌کنیم که با هدف بهبود تجربه‌ی کاربری برای صفحه‌ی جزئیات برنامه ایجاد شده است. صفحه‌ی جزئیات برنامه در بازار (نسخه ۷)    ۱. افزودن تصویر زمینهتصویر زمینه قسمت جدیدی است که در نسخه‌ی ۸ بازار به صفحه‌ی جزئیات برنامه اضافه شده است. اضافه‌شدن تصویر زمینه در این نسخه مشخصاً سه بخش مختلف را بهبود بخشیده است:الف. محتوا: هدف اصلی صفحه‌ی جزئیات برنامه این است که محتوای مفید در مکان مناسب به کاربر ارائه شود؛ به‌همین منظور، علاوه بر تصاویر برنامه یا بازی، تصویر زمینه را هم به این صفحه اضافه کردیم که این امر به درک بهتر کاربر از فضای حاکم بر بازی یا برنامه کمک می‌کند.با اضافه شدن این ویژگی جدید، هر برنامه یا بازی می‌تواند تصویر زمینه‌ی مخصوص‌به‌خود را داشته باشد. توسعه‌دهندگان می‌توانند در این قسمت تصویری را نمایش دهند که هویت شرکتشان را معرفی می‌کند یا تصویری از فضای درون‌ برنامه یا بازی خود را در این قسمت قرار دهند.ب. محدوده‌ی حرکت انگشت شست: برای بهبود دسترسی انگشت شست می‌بایست اجزایی را که کاربر با آن‌ها بیشتر ارتباط دارد به پایین صفحه منتقل می‌کردیم؛ با این تغییر، محدوده‌ی حرکت انگشت شست روی این اجزا بهبود می‌یافت. افزودن تصویر زمینه به ما این امکان را می‌داد که بخش‌هایی را که کاربر با آن‌ها تعامل دارد به‌میزان مناسب به قسمت پایین صفحه منتقل کنیم.برای آشنایی بیشتر با این موضوع می‌توانید این مقاله را مطالعه کنید:How to design for thumbsج. چینش صفحه: دلیل دیگر برای افزودن تصویر زمینه وسط‌‌چین‌شدن صفحه‌ی جزئیات برنامه در نسخه‌ی ۸ بازار است. در این میان، ساختار نیم‌دایره‌ای تصویر پس‌زمینه هم به هدایت حرکت چشم به‌سمت پایین و جای‌گذاری آیکون برنامه در وسط صفحه کمک کرد.۲. تغییر نحوه نمایش نام برنامهنام برنامه‌ها و بازی‌ها یکی از عناصر مهم در صفحه‌ی جزئیات برنامه است؛ به‌همین علت، برای ما مهم بود که کاربر بتواند راحت آن را پیدا کند و همچنین نام برنامه‌ها یا بازی‌ها خوانا باشد. تغییراتی که به‌این منظور اعمال شد از این قرار است:الف. بزرگ‌تر شدن فونت: برای خوانایی راحت‌تر نام برنامه‌ها و بازی‌ها و همچنین راحت‌تر پیداکردن نام آن‌ها در صفحه فونت آن را بزرگ‌تر کردیم.ب. فضای بیشتر، خوانایی بهتر: وقتی متنی در دو خط جای می‌گیرد، چشم بعد از پایان خط اول برای رفتن به خط دوم درنگی کوتاه می‌کند. این توقف سرعت خوانش را پایین می‌آورد. برای جلوگیری از دوخطی شدن، به نام برنامه‌ها فضای بیشتری اختصاص دادیم که به‌راحتی خوانده شود.تصویر سمت راست بازار ۷ / سمت چپ بازار ۸۳. تغییرات سربرگالف. کنتراست بیشتر میان آیکون‌ها و پس‌زمینه: رنگ آیکون‌ها در سربرگ یکی از نقاط ضعف این قسمت بود. برای خوانایی و تمایز بهتر میان آیکون‌ها و پس‌زمینه‌ی سربرگ نیاز بود رنگ هر دو تغییر کند و کنتراست بهتری برای چشم به وجود آید؛ برای همین از مدل استاندارد WCAG کمک گرفتیم.سربرگ در بازار ۷میزان کانتراست بین آیکون ها و پس زمینه در بازار ۷سربرگ در بازار ۸میزان کانتراست بین آیکون ها و پس زمینه در بازار ۸ ب. اضافه‌شدن آیکون برنامه به داخل سربرگ: صفحات داخلیِ جزئیات برنامه باید این امکان را به کاربر بدهد که در هنگام جست‌وجو در آن به‌سرعت تشخیص دهد اطلاعات به کدام برنامه مربوط است؛ برای این منظور آیکون برنامه را به سربرگ اضافه کردیم. دلیل این تغییر به این امر بازمی‌گشت که تشخیص تصویر از تشخیص متن راحت‌تر است و در‌نتیجه، تصویر اطلاعات را با سرعت بیش‌تری به کاربر منتقل می‌کند. در نسخه‌ی جدید، متن مربوط به نام برنامه یا بازی هم موجود است و کاربر، در صورت تمایل، می‌تواند آن را بخواند؛ در واقع در این نسخه چیزی از بخش سربرگ حذف نشد، بلکه در مقایسه با گذشته اطلاعات کامل‌تر نمایش داده می‌شود.۴. تغییرات در دکمه‌ی نصبمهم‌ترین قسمت در صفحه‌ی جزئیات برنامه دکمه‌ی نصب برنامه است؛ زیرا روزانه ۵/۱ میلیون بار لمس می‌شود. این دکمه در نسخه‌ی قبلی جای کمی به خود اختصاص داده بود و در محدوده‌ی مناسب حرکت شست قرار نداشت؛ پس لازم بود در این زمینه تغییراتی انجام شود:الف. دسترسی راحت‌تر برای انگشت شست: با جابه‌جاکردن دکمه در محدوده‌ی مدنظر اکنون کاربر راحت‌تر از قبل به آن دسترسی دارد.تصویر سمت راست بازار ۷ / سمت چپ بازار ۸ ب. بزرگ‌ترکردن اندازه‌ی دکمه‌ی نصب: با بزرگ‌ترشدن دکمه‌ی نصب، علاوه بر این‌که کاربر آن را راحت‌تر پیدا می‌کند، زمان کمتری برای لمس آن نیاز دارد.ج. جابه‌جایی دکمه‌های به‌روزرسانی و حذف: کاربر بعد از نصب برنامه دو دکمه‌ی «به‌روزرسانی» و «حذف» را مشاهده می‌کرد. این دو دکمه، علاوه بر این‌که در ناحیه‌ی مناسب حرکت شست قرار نداشتند، در فاصله‌ی بسیار کمی از یکدیگر قرار گرفته بودند؛ همین امر احتمال اشتباه‌ فشردن دکمه‌ی مدنظر را افزایش می‌داد. در نسخه‌ی جدید این دو دکمه را با فاصله‌ی بیشتری در کنار هم قرار دادیم و اندازه‌ی هر دو را بزرگ‌تر کردیم که میزان لمس‌های اشتباه کاهش یابد.تصویر سمت راست بازار ۷ / سمت چپ بازار ۸ د. دکمه‌ی شناور نصب: در صفحه‌ی جزئیات برنامه یا بازی، به‌تناسب محتوا، اطلاعات زیادی نمایش داده می‌شود. در نسخه‌ی قبلی کاربر باید اسکرول زیادی می‌کرد که بتواند همه‌ی اطلاعات را ببیند؛ به‌طوری که اگر اطلاعات، او را به نصب برنامه یا بازی ترغیب می‌کرد، باید به بالای صفحه بازمی‌گشت که بتواند دکمه‌ی نصب را لمس کند. یکی از نکات مهم در طراحی نسخه‌ی جدید در دسترس بودن دکمه‌ی نصب بود؛ به‌این صورت که هر زمان کاربر تصمیم گرفت برنامه یا بازی را دانلود کند، دکمه‌ی نصب دردسترسش باشد؛ برای همین، در طراحی نسخه‌ی جدید بعد از اسکرول به‌سمت پایین دکمه‌ای شناور در صفحه ظاهر می‌شود و کاربر می‌تواند در هر مکانی از صفحه‌ی جزئیات برنامه به آن دسترسی داشته باشد.۵. بهبود ساختار نظراتالف. ایجاد انسجام: در نسخه‌ی قبلی نظرات مربوط به برنامه‌ها یا بازی‌ها به‌صورت از هم‌ گسسته و نامنسجم به نمایش درآمده بود؛ دکمه‌های «پسندیدم» و «نپسندیدم» و «گزارش نامناسب بودن محتوای نظر» خارج از چارچوب هر نظر قرار گرفته بود. این نحوه‌ی قرارگیری، تشخیص این امر را دشوار می‌کرد که این قسمت‌ها به نظر درباره‌ی برنامه یا بازی بالایی مربوط هستند یا به نظر درباره‌ی برنامه یا بازی پایینی بازمی‌گردند.در نسخه‌ی جدید چارچوب نظرات را به‌همان صورت گذشته نگه داشتیم، اما دکمه‌های «پسندیدم» و «نپسندیدم» و «گزارش نامناسب‌ بودن محتوای نظر» را داخل چارچوب هر نظر قرار دادیم که انسجام هر نظر حفظ شود.همین‌طور برای خوانش راحت‌تر «نظرات دیگر کاربران» این بخش را به‌سمت راست منتقل کردیم و رنگش را تغییر دادیم؛ درنتیجه، درحالی‌که در نسخه‌ی قبلی بازار امتیاز نظرات در میان محتوای نظرات بود، در چینش جدید به امتیاز نظرات هم اهمیت ویژه‌ای دادیم.تصویر سمت راست بازار ۷ / سمت چپ بازار ۸ ب. اضافه‌کردن امتیاز کلی برنامه: با توجه به ماهیت صفحه‌ی جزئیات برنامه، انتظار می‌رود کاربر بتواند تمامی اطلاعات درباره‌ی برنامه را در همین صفحه مشاهده کند؛ به‌همین دلیل تصمیم گرفتیم امتیاز کلی برنامه را از بخش نظرات به این صفحه منتقل کنیم.۶. برنامه‌ها و بازی‌های برگزیدهدر نسخه‌ی ۷ بازار صفحه‌ی برنامه و بازی‌های برگزیده وجود نداشت. این ویژگی در نسخه‌ی ۸ به بازار اضافه شد که کاربران به محتوای برگزیده دسترسی داشته باشند. قسمتی جداگانه در صفحه‌ی جزئیات برنامه برای این قسمت در نظر گرفتیم که کاربر با لمس آن به صفحه‌ی برگزیده‌ها برود.۷. تغییر ساختار گزارش از برنامه‌هاثبت گزارش در نسخه‌ی قبلی بازار دومرحله‌ای بود؛ به‌این صورت که پس از لمس «گزارش برنامه» ابتدا دیالوگی با چند گزینه به کاربر نمایش داده می‌شد؛ پس از این در مرحله‌ی بعد کاربر با دیالوگی مواجه می‌شد که از کاربر می‌خواست پیامی بنویسد. در[1]  هر یک از این مراحل درصورتی‌که کاربر محلی خارج از محدوده‌ی دیالوگ را لمس می‌کرد، از مراحل ثبت گزارش خارج می‌شد که تجربه‌ی چندان خوشایندی نبود.گزارش برنامه در بازار ۷در نسخه‌ی جدید فرایند ارسال گزارش به صفحه‌ای مستقل ارجاع داده می‌شود. در این حالت کاربر می‌تواند بعد از انتخاب یکی از گزینه‌های گزارش، در صورت تمایل، درباره‌ی گزارش خود متنی هم بنویسد یا با انتخاب یکی از گزینه‌ها برنامه را بدون متن گزارش کند. از دیگر مزایای ارجاع به صفحه‌ی دیگر حذف دیالوگ است؛ این کار از بسته‌شدن اتفاقی صفحه‌ی گزارش هم جلوگیری می‌کند.گزارش برنامه در بازار ۸
در پایان باید گفت که روند بازبینی و بهبود تجربه‌ی کاربری در تیم طراحی بازار ادامه دارد و ما سعی می‌کنیم برای کاربران تجربه‌ی بهتری رقم بزنیم. در حال حاضر معیارهایی را برای بخش‌های مهم بازار در نظر گرفته‌ایم. براساس آن معیارها نسخه‌ی ۸ بازار را رصد می‌کنیم و آن را در مقایسه با نسخه‌ی ۷ بازار می‌سنجیم؛ این مقایسه امکان ادامه دادن روند بهبود طراحی را به ما می‌دهد.اگر شما هم در زمینه‌ی تجربه‌ی کاربری از بازار انتقادی دارید یا برای بهبود آن پیشنهادی به ذهنتان می‌رسد، خوشحال می‌شویم از طریق این ایمیل آن را با ما در میان بگذارید: design@cafebazaar.ir</description>
                <category>کافه بازار</category>
                <author>حسام کرامتی</author>
                <pubDate>Sun, 03 Nov 2019 19:55:27 +0330</pubDate>
            </item>
                    <item>
                <title>پیشخان توسعه‌دهندگان کافه‌بازار: از Monolithic به Microservices</title>
                <link>https://tech.cafebazaar.ir/پیشخان-توسعهدهندگان-کافهبازار-از-monolithic-به-microservices-aesbnymda2gi</link>
                <description>شروعپنل توسعه‌دهندگان کافه‌بازار وابستگی زیادی به پروژه‌ی اصلی بازار داشت، و این موضوع ادامه‌ی توسعه پنل و اضافه کردن امکانات جدید رو سخت کرده بود. به روز نبودن، کثیفی کد و وابستگی‌های دست‌و‌پا گیر ما رو به سمتِ بازنویسی پنل توسعه‌دهندگان برد.هدف ما از بازنویسی پنل، کاهش هزینه‌ی توسعه و نگهداری سیستم بود. در این مسیر روش‌های مختلفی رو امتحان کردیم که بعضی از اونها نتیجه‌ی عکس داشت. طوری که نه تنها هزینه توسعه رو کاهش نمی‌داد بلکه بیشتر هم می‌کرد.از یک سیستم یکپارچه (Monolithic) شروع کردیم، تا تولید چندین مایکروسرویس (Microservices) رفتیم و در نهایت به یک طراحی ساده رسیدیم تا روند توسعه رو ساده‌تر کنیم.در این مسیر با چالش‌های فنی و ساختاری مربوط به تیم مواجه شدیم و نکاتی رو یاد گرفتیم که در ادامه با هم می‌بینیم.خلاصه‌اش اینکه:فقط زمانی کاری رو انجام بدیم که واقعا بهش نیاز داریم و Over-engineering نکنیم. از قدیم هم گفتن «سری رو که درد نمی‌کنه، دستمال نمی‌بندن» :-)یک تیم متمرکز با هدف مشخص، عملکرد بهتری می‌تونه داشته باشه.تاریخچه پنل بازار: از بندر تا پیشخانپروژه بندرپس از عبور از چالش‌های اولیه و به ثبات رسیدن بازار، از اونجایی که رابط کاربری پنل به‌روز نبود (با هر اکشن دوباره بارگذاری می‌شد، با موبایل سازگار نبود یا به اصطلاح Mobile-Friendly نبود و سایر مسائل اینچنینی) و تجربه کاربری خوبی نداشت، و به دلیل در هم تنیده بودنش با کد‌های بک‌اند (قالب‌های جنگو) خوانایی‌اش رو از دست داده بود و توسعه‌اش دشوار شده بود، تیم توسعه‌دهندگانِ وقت تصمیم گرفت پنل توسعه‌دهندگان رو بازنویسی کنه؛ با این هدف که کلاینت وب از بک‌اند جدا بشه و توسعه‌ی هر کدوم ساده‌تر بشه.از این رو، پروژه‌ی «بندر» در سال ۱۳۹۴ ایجاد شد.برنامه‌ها در «بندر» تخلیه و وارد «بازار» می‌شن :-Dبرای پیاده‌سازی کلاینت وب از AngularJS، و از اونجا که پروژه بازار با جنگو توسعه داده شده بود، برای بک‌اند از Django REST framework به صورت یک اپ روی کد بازار استفاده شد.اما این پروژه در نهایت به دلیل Coupling زیادی که با کدهای دیگر بازار داشت متوقف و پس از مدتی کنار گذاشته شد.مشکل این بود که اپ‌های جنگو پروژه بازار در مدل‌ها Coupling زیادی داشتند و بعضی از مدل‌ها در تعداد زیادی اپ استفاده می‌شدند و اگر می‌خواستیم برای پنل یک مدل رو تغییر بدیم باید در قسمت‌های مختلف تغییر ایجاد می‌کردیم. و اگر بقیه مدلی رو تغییر می‌دادن باید اپ پنل هم تغییر می‌کرد.از طرفی جدا کردن داده‌های پنل هم کار سختی بود، چون همه‌ی داده‌ها روی پایگاه داده‌ی بازار بود. مثلا برای جدا کردن سرور بررسیِ برنامه‌ها و دسترسی به داده‌های مورد نیاز، یا باید مستقیما به دیتابیس بازار وصلش می‌کردیم یا روی بازار یک API می‌گذاشتیم تا داده‌های مربوط به پنل بررسی برنامه‌ها رو فراهم کنه.بعد از این موضوع یک سری جلسه داشتیم که برای حل این مشکل چه کار کنیم؟ مثلا یک پایگاه داده‌ی مشترک ایجاد کنیم و همه بتونن ازش API بگیرن؟ اما این جلسات به تصمیم خاصی نرسید...کمی از پروژه‌ی بندرحدود ۳ ماه از کار روی پروژه بندر گذشته بود و ۲ هفته‌ی آخر سرعت و تلاش‌مون رو بیشتر کردیم که به OKR برسیم اما در انتها کدی که تولید شده بود به دلیل عجله برای اتمامِ پیاده‌سازی، کیفیت خوبی نداشت و شاید بدتر از کد قبلی پنل بود. در انتهای OKR یک سیستم باگی داشتیم که تا حدودی کار می‌کرد.در فصل بعد مدیر محصول تغییر کرد و تیم به ۲ بخش تقسیم شد. قرار شد یک تیم پنل بررسی برنامه رو پیاده‌سازی کنه و تیم دیگه روی پنل توسعه‌دهندگان کار کنه.از اونجایی که برای ادامه‌ی پروژه بندر با تخمین تیم حداقل ۲ ماه باید باگ‌فیکس انجام می‌شد، مدیر محصول همراه تیم تصمیم گرفتن که کار روی پروژه بندر متوقف بشه و تیم روی پنل قبلی فیچر بده.بعد از یکی دو فیچر که به پنل قبلی اضافه کردیم، به دلیل هزینه‌ی فنی زیاد پیاده‌سازی دوباره تصمیم بر این شد که روی بندر کار کنیم. اما زمونه عوض شده بود D-:بندر خراب شده بود و دیگه کار نمی‌کردمشکل این بود که در بازه‌ ۲ ماهه‌ای که پروژه بندر متوقف شد، کدهای بازار تغییرات زیادی کرده بود و چون کدهای بندر روی بازار مرج نشده بودن، دچار کانفلیکت‌های زیاد شده بود و درست کردنش به تغییر در بخش زیادی از بندر نیاز داشت.از طرف دیگه، چون کدهای بازار در اون زمان Test Coverage خوبی نداشت، حتی بعد از رفع کانلفیکت‌ها مرج کردن بندر بدون ریسک نبود. چون ممکن بود بخش‌های دیگر بازار هم خراب بشه.حجم زیاد تغییرات و وابستگی تمام اپ‌های جنگو روی مدل‌ دیتا باعث شده بود که با هر تغییر روی یک مدل، قسمت‌های دیگه هم نیاز به تغییر داشته باشند و بندر با تعطیل شدنِ موقت، عملا از کار افتاده بود.این موضوع باعث شد تصمیم بگیریم پنل توسعه‌دهندگان رو همراه با داده‌های مربوطه از بازار جدا کنیم. اما در ادامه فهمیدیم که این جداسازی به اون سادگی که فکر می‌کردیم نیست :-Dپیشخان کافه‌بازاریک سال از پایان پروژه بندر گذشته بود. علاوه بر مشکل Coupling پنل با کدهای بازار—که به کند شدن تیم، معطل شدن برای مرج و … منجر می‌شد—توسعه‌ی اون به دلیل کثیفی کد در طی زمان سخت شده بود، باگ زیاد رخ می‌داد و عیب‌یابی هم دشوار بود.این‌ها دلایلی بود که ما رو به سمت بازنویسی پنل برد.بنابراین جداسازی و بازنویسی پنل توسعه‌دهندگان رو شروع کردیم. و چون پنل مسیرِ ورودی برنامه‌های بازار بود، این پروژه «پیشخان توسعه‌دهندگان کافه‌بازار» نام گرفت.در اون زمان (سال ۱۳۹۵) تیم‌های فنی کافه‌بازار به سمت استفاده از معماری مایکروسرویس در حرکت بودن و تب مایکروسرویس در شرکت داغ بود.با دنبال کردن این جریانِ فنی، ایده‌ی ما برای جداسازی پنل از کافه‌بازار این بود که برای اهداف مختلف، سرویس‌های مجزا تعریف کنیم و تکه‌های مختلف رو در این سرویس‌ها پیاده‌سازی کنیم. چون:قسمت‌های مختلف کارکرد متفاوت از هم داشتن و در آینده تیم‌های دیگه‌ای می‌تونستن صاحب این سرویس‌ها بشن.سرویس‌ها می‌تونستن از تکنولوژی‌های متفاوتی استفاده کنن. البته در عمل با توجه به دانش و تجربه‌ای که داشتیم اکثر سرویس‌ها با پایتون و Django REST framework توسعه داده شدن.برای این منظور سرویس‌های زیر طراحی شدن:ناشر: مدیریت اطلاعات حساب توسعه‌دهنده‌ها و جدا کردن اونها از کاربران عادی.مصدق: احراز هویت با نام کاربری و رمز عبور و احتمالاً در ادامه با ایمیل و یا با OAuth که تیم‌های دیگه هم بتونن ازش استفاده کنن.اَپدارچی: مدیریت برنامه (اطلاعات و بسته‌ها و…) و فرآیند انتشار.دَخل: مدیریت اطلاعات مالی برنامه مربوط به پنل توسعه‌دهندگان.هَشتی: به عنوان API Gatewayو…(در ادامه برای سادگی از عبارت «کافه» برای اشاره به پروژه بازار استفاده شده)سرویس مصدقسرویس مصدق نقشِ تصدیق اطلاعات کاربر رو داشت :-D و چون خودش اطلاعات کاربر رو نداشت باید از کافه اطلاعات کاربر رو می‌پرسید و توکن JWT رو تولید می‌کرد.سرویس ناشرناشر بیشترین امکان برای جداسازی داده‌ها رو داشت و برای شروع با این سرویس پیش رفتیم. اما کافه به اطلاعات ناشر و کارپردازهای اون همچنان نیاز داشت و تصمیم گرفتیم با تغییر این اطلاعات در ناشر، داده‌های کافه رو هم به روز کنیم اما این وابستگی بصورت بدهی فنی (Technical Debt) موند و بازپرداخت نشد.سرویس اَپدارچی؛ پروکسی یا لگاسی؟اَپدارچی قرار بود تمام اطلاعات برنامه رو تا پیش از انتشار داشته باشه و انتظار داشتیم به یک سرویس نگهداری برنامه‌ها تبدیل بشه که دیگران ازش اطلاعات برنامه رو می‌پرسن.اما مشکل این بود که خیلی از سرویس‌های قدیمی به اطلاعات اولیه برنامه (مثلا package_name) نیاز داشتن و بر این مبنا کار می‌کردن و جدا کردن این داده‌ها از پروژه‌ کافه، نیازمند همکاری تعداد زیادی از تیم‌ها بود. در حالی که مزیت این جداسازی در لحظه نسبت به سختی‌ انجامش کم بود و چندان قابل توجیه نبود.از این بابت اطلاعات و لاجیک (Business Logic) کلی برنامه در کافه موند و ما لاجیک مربوط به پنل توسعه‌دهندگان رو با این سرویس جدا کردیم با این امید که مشکل جدا کردن اپ از کافه در آینده برطرف بشه.از اونجا که اَپدارچی به دلیل نیاز به سینک اطلاعات با کافه نمی‌تونست دیتابیس مستقل خودش رو داشته باشه، باید اطلاعات برنامه رو از کافه می‌پرسید. برای این کار یک اپ به اسم Capi روی کافه‌ توسعه دادیم (مخفف Cafe API) که اطلاعات مد نظر سرویس‌های دیگه (مثل اَپدارچی) رو فراهم کنه.اما پرسیدن اطلاعات برنامه از کافه در قسمت‌های مختلفِ لاجیک، کار بیهوده‌ای بود و کلی رفت و برگشت باید انجام می‌شد. در شرایطی باید اطلاعاتی خونده می‌شد و بررسی‌هایی انجام می‌شد و…به دلیل وابستگی زیادی که لاجیک اَپدارچی به اطلاعات کافه داشت، در نهایت تصمیم بر این شد که داخل اپ Capi این چک‌ کردن ها انجام بشه و میزان قابل توجهی از لاجیک اَپدارچی، داخل Capi قرار بگیره.به مرور زمان، اَپدارچی به پراکسی تبدیل شده بود که درخواست رو از سرویسی می‌گرفت و به Capi می‌گفت و پاسخ رو برمی‌گردوند.از این بابت وجودش دیگه مزیتی نداشت و تنها کارها رو برای توسعه‌ی سیستم سخت کرده بود:برای هر تغییر مرتبط با برنامه، یک مرج ریکوئست روی اَپدارچی اضافه می‌شد (علاوه بر Capi)ارتباطات اضافی در شبکه به سیستم تحمیل می‌کردو این شروع حذف این نوع سرویس‌ها از پیشخان بود.نتیجه‌‌ای که ما از این موضوع گرفتیم، این بود که:«قبل از اضافه کردن مایکروسرویس باید به جدا کردن داده‌ها فکر کرد (ما اول به جدا کردن لاجیک فکر کرده بودیم). جایی که داده‌ها خیلی در هم تنیده‌ست، شاید ایجادِ API روی سیستم لگاسی (legacy system)  ایده‌ی بهتری باشه»در واقع برای هدف اَپدارچی، اپ Capi راه حل ما بود و نه یک سرویس جدا.سرویس دخلایده ما این بود که لاجیک‌های مالی پنل رو در سرویس جدایی توسعه بدیم و از کافه جداشون کنیم. با این هدف که تیم توسعه‌دهندگان راحت‌تر بتونه روی این سرویس‌ کار کنه و تغییر ایجاد کنه.برای این کار ابتدا لاجیک مربوط به تصفیه حساب و فاکتور فروش و… رو جدا کردیم و روی دخل پیاده کردیم.سرویس قلک (که اطلاعات و لاجیک مالی کافه رو داشت) برای تهیه‌ی فاکتور و فایل فروش باید کوئری‌های سنگینی اجرا می‌کرد که خیلی زمان‌بر بود. طوری که حتی برای تعدادی از توسعه‌دهندگان به دلیل حجم بالای تراکنش‌ها ممکن بود درخواست تهیه گزارشات مالی با timeout مواجه بشه. از طرفی، تیمی که توسعه قلک رو به عهده داشت زمانی برای بهبود پرفورمنس تهیه‌ی گزارش مالی برای پنل توسعه‌دهندگان نداشت.از این بابت فکر کردیم می‌تونیم در سرویس دخل این داده‌ها رو به صورت روزانه از قلک بگیریم و برای بازه‌ی زمانی مدنظر جمع کنیم و خیلی سریع‌تر این فاکتور و فایل‌ها رو بسازیم.اما مشابه نیاز اَپدارچی به داده‌های کافه، دخل نیاز زیادی به داده‌های قلک داشت.برای رفع این نیاز یک اپ به نام Gapi (مخفف Ghollak API) روی قلک توسعه دادیم که داده‌های مورد نیاز سرویس‌ها رو با API فراهم کنه.اما لاجیک دخل هم وابستگی زیادی به داده‌های مختلف قلک داشت و کلی رفت و برگشت باید انجام میشه. به همین دلیل در عمل این قسمت از لاجیک‌ها داخل Gapi پیاده سازی و رفته رفته سرویس دخل هم به سرنوشت اَپدارچی دچار شد.کمی از جزییات فنیپروتکل ارتباطیبرای ارتباط بین سرویس‌ها، گزینه‌هایی مثل REST و gRPC مطرح بود و از اونجا که تعداد ریکوئست‌های پنل چندان زیاد نبود و مسئله پرفورمنس مطرح نبود، از REST استفاده کردیم تا سرعت توسعه رو بالا ببریم.یک کار خوب که در ابتدا انجام دادیم این بود که با تحقیق روی قراردادهای های موجود، برای API های پیشخان style-guide آماده کردیم تا همه‌ی سرویس‌ها برای درخواست‌ها و پاسخ‌ها ساختار یکسانی داشته باشن.این راهنما که شامل نحوه‌ی نام‌گذاری resource ها، کوئری‌ها، نحوه‌ی ارسال خطا، status code های مشخص و… می‌شد، کمک زیادی به یکپارچه بودن API ها کرد و باعث ساده‌تر شدنِ طراحی API ها و استفاده از اون‌ها در قسمت‌های مختلف مثل فرانت‌اند وب شد.توسعه و دیپلویبرای ساده‌ کردن اجرای سرویس‌ها، همه‌ی اونها رو داکرایز کردیم که در اون زمان خیلی در شرکت بدیهی نبود اما امروز بخشی عادی از روند کاریمون هست.رفتن سمت معماری مایکروسرویس‌ شرایط توسعه رو متفاوت کرده بود. برای کار روی پیشخان باید تمام سرویس‌ها رو همراه با پروژه کافه‌ و قلک روی لوکال بالا می‌آوردیم، طوری که بتونن همدیگه رو ببینن و با هم صحبت کنن.سرویس‌ها از راست: اَپدارچی، کافه، مصدّق، ناشر، دَخل، قلکاین در حالی بود که بالا آوردن پروژه کافه روی لوکال برای بار اول به تنهایی یکی دو روز طول می‌کشید. علاوه بر این، حالا سرویس‌هایی اضافه شده بودند که با هر تغییر تعدادی از اونها باید متوقف و دوباره اجرا می‌شدند.با تغییر هر سرویس باید:کل یا بخشی از سرویس‌ها رو پایین می‌آوردیمتغییرات اون سرویس(ها) رو با گیت می‌گرفتیمروی لوکال داکر (Docker) رو بیلد می‌کردیمدوباره سرویس‌ها رو بالا می‌آوردیم.این پروسه، توسعه‌ی هر دو سمتِ فرانت‌اند و بک‌اند پروژه رو کند می‌کرد.برای سرعت دادن به این کار، یک ابزار با fabric توسعه دادیم که از فایل docker-compose پروژه‌ها استفاده می‌کرد و سرویس‌های اون‌ها رو بالا می‌آورد و بعد برای اینکه سرویس‌ها بتونن با هم در ارتباط باشن، داخل هر کانتینر (Container) در فایل hosts آی‌پی بقیه‌ی سرویس‌ها رو وارد می‌کرد که البته چالش‌هایی هم داشت. برای مثال اگر یک سرویس هنوز بالا نیومده بود، باید صبر می‌کرد تا اون سرویس بالا بیاد و دوباره تلاش کنه، که این کار رو کند می‌کرد.بعدها ابزار توسعه‌ی دیگه‌ای رو استفاده کردیم که یکی از تیم‌های کافه‌بازار توسعه داده بود. و در اون به جای وارد کردن آی‌پی هر کانتینر در بقیه، از docker compose network استفاده می‌کرد.چالش‌های فنیبا رفتن تیم‌ها به سمتِ مایکروسرویس ما هم در تیم توسعه‌دهندگان هم تصمیم گرفتیم با آینده‌نگری به دلایلی که پیش‌تر گفته شد، زمانی رو صرف این کنیم که سیستم رو به چه سرویس‌هایی بشکنیم.به مرور متوجه شدیم که شکستنِ سیستم به تعداد زیادی سرویس‌، باعث طولانی شدن کار شده. مثلاً هر تسک کوچک بک‌اندی، ۳ مرج ریکوئست می‌خواست که گاهی ۲ نفر در تیم باید اون‌ها رو بازبینی می‌کردن و این کارمون رو کند می‌کرد.علاوه بر این، پیچیدگی معماری و نداشتن راهنمای مشخص، توضیح کل سیستم به نیروهای جدید رو سخت کرده بود.تعداد بالای سرویس‌ها، روند نگهداری سیستم رو هم طولانی می‌کرد. برای مثال بروزرسانی پکیج‌هایی که باگ‌فیکس برای اونها اومده بود—که باید برای تعداد زیادی سرویس انجام می‌شد. یا جمع‌آوری لاگ خطا از سرویس‌های مختلف.بعلاوه نیازی که بعضی از سرویس‌ها برای صحبت با کافه و قلک داشتن، صرفاً پیچیدگی به سیستم اضافه کرده بود و وجود اون سرویس‌ها رو زیر سوال برده بود.همانطور که پیش‌تر اشاره شد، دو سرویس اَپدارچی و دخل از این دسته بودند.در اَپدارچی: قرار بود اطلاعات مربوط به برنامه—که تا قبل از انتشار که روی بازار بهش نیاز هست—رو مدیریت کنه. به این شکل که همه‌ی ریلیزها (Package Releases) در این سرویس جمع بشن و کافه صرفا برنامه منتشر شده رو دریافت کنه.ولی در عمل این جداسازی اتفاق نیفتاد و اَپدارچی به یک سرویس واسط بین سرویس‌های پیشخان و کافه‌ تبدیل شد و در نهایت این سرویس حذف شد. (هرچند جداسازیِ برنامه از کافه با تمام چالش‌هایی که داشت شاید امکان پذیر بود اما به سختی‌ای که داشت نمی‌ارزید)همینطور سرویس دخل بعد از پیاده سازیِ گزارش‌گیری، با تغییر schema دیتابیس قلک در فرآیند کاریِ تیم دیگه‌، از کار افتاد و به روز نگه‌داشتن لاجیک دخل متناسب با تغییرات قلک از توان تیم ما خارج بود. و این باعث شد کم کم به این نتیجه برسیم که گزارشات مالی رو خودِ قلک باید ایجاد کنه. در نهایت لاجیک دخل به قلک و اپ Gapi منتقل و سرویس دخل پاک شد.شاید اگر داخل Gapi این امکان رو توسعه داده بودیم و برای اون تست می‌نوشتیم طوری که با تغییر اسکیما تست‌ها رد بشه، این اتفاق پیش نمی‌اومد و دردسر صحبت با یک سرویس اضافه (دخل) رو هم نمی‌داشتیم.بعد از نمایان شدن این مشکلات، با بررسی مزایا و معایب برگشتن به یک معماری ساده و حذف پیچیدگی‌های وقت‌گیر، تصمیم گرفتیم سرویس‌هایی رو از سیستم حذف کنیم.این سرویس‌ها صرفاً به پراکسی تبدیل شده بودن و در چشم‌انداز محصول هم دیده نمی‌شد تیمی بوجود بیاد که مدیریت این سرویس‌ها رو به عهده بگیره.از این بابت سرویس‌های اَپدارچی، دخل و مصدق از پیشخان حذف شدن و لاجیک اونها در قسمت‌های دیگه قرار گرفت:کارهای اَپدارچی داخل اپ Capi در کافهکارهای دخل در اپ Gapi در قلککارهای مصدق در سرویس ناشرسرویس‌ها دو به دو در هم ادغام شدن :-Dسرویس‌های دیگری هم توسعه داده شده بودن که پیش از ریلیز نهایی از پیشخان حذف شدن.در ابتدای شروعِ پیشخان، تکنولوژی‌های زیرساختی در شرکت در حال تحول بود و با ارائه یک سرویس جدید و برای تست و فیدبک اولیه، تیم‌های دیگه سراغ استفاده از این سرویس‌ها می‌رفتن.زمانی که ما توسعه‌ی پیشخان رو شروع کردیم روش یکپارچه‌ای برای ذخیره‌ی فایل‌ها در سطح شرکت نداشتیم. از این بابت برای آپلود و دانلود فایل‌های استاتیک در پیشخان، یک سرویس مجزا به نام «انبار» توسعه دادیم که نیازهایی مثل آپلود، دانلود و تولید لینک دانلود زمان‌دار و… رو برطرف کنه.این سرویس رو با Lua روی nginx توسعه دادیم تا پرفورمنس بهتری مثلا نسبت به پایتون و… داشته باشه.اما پیش از اینکه از این سیستم در پروداکشن استفاده کنیم سرویس Ceph Object Storage توسط بچه‌های زیرساخت کافه‌بازار راه اندازی شد و تصمیم بر این شد که فایل‌های استاتیک پیشخان روی Ceph نگه داشته بشن و به علاوه، برای تست این سرویس زیر بار پروداکشن، پیش از نهایی شدن پیشخان برای فایل‌های پنل قبلی هم از Ceph استفاده بشه.در نتیجه سرویس انبار پیش از استفاده از پیشخان حذف شد.شاید با ارتباط و برنامه‌ریزی کردن در سطح بالاتر (بینِ محصولی) می‌شد از توسعه و بعد حذف سرویس انبار اجتناب کرد. به این شکل که تیم توسعه‌دهندگان فعلا این بخش از پروژه رو متوقف کنه و روی قسمت‌های دیگه متمرکز بشه. در هر صورت این هماهنگی دیر اتفاق افتاد و باعث کار اضافی برای تیم شد.چالش دیگه‌ای که تیم داشت این بود که سازوکار اجزای سیستم در پنل قبلی برای تیم شفاف نبود. مثلاً وضعیت‌های مختلف برنامه و فرآیند انتشار یا وضعیت‌های مختلف ناشر که بطور مشخص چه چیزهایی هستن و در چه شرایطی این وضعیت‌ها اعمال می‌شن و…به همین دلیل همه به مدیر محصول تیم—که از ابتدا در جریان موضوعات فنی پنل قبلی و پیشخان بود—مراجعه می‌کردن تا درباره پیاده‌سازی سرویس‌ها تصمیم بگیرن. این وابستگی هم تمرکز محصولی رو از مدیر محصول می‌گرفت و هم تبدیل به گلوگاه برای تیم شده بود.در زمینه‌ی بردن پیشخان روی پروداکشن و گرفتن فید‌بک هم می‌تونستیم بهتر عمل کنیم.کد‌هایی که زده بودیم دیر روی پروداکشن رفت و اگر این کار رو بطور تدریجی انجام داده بودیم، باگ‌ها زودتر مشخص و فیکس می‌شدن و با انبوهی از باگ مواجه نمی‌شدیم.سعی کردیم این مسئله رو با جایگزین کردن بک‌اند پنل قبلی با API های سرویس‌های جدید انجام بدیم اما شاید می‌تونستیم روی پنل جدید قسمت‌هایی که انجام شده رو بصورت بتا منتشر کنیم و فید‌بک بگیریم و به مرور پنل رو تکمیل کنیم تا زمانی که امکان جایگزین کردنش با پنل قبلی فراهم باشه.چالش‌های غیرفنیمشکلاتِ غیر فنی عمدتاً حول تغییرات زیاد در تیم توسعه‌دهندگان و تغییر تمرکز تیم بود.در زمینه‌ی عدم تمرکز، چالش‌ تیم توسعه‌دهندگان این بود که باید همزمان با انجام پروژه پیشخان، کارهایی روی پنل قبلی انجام می‌داد. بخشی از این کارها از سمت تیم‌های بررسی، حقوقی و مارکتینگ می‌اومد و بخشی دیگه از سمتِ تیم‌های فنی و این میزان context switch تمرکز تیم رو می‌گرفت و توسعه‌ی پیشخان رو کند می‌کرد.در تغییرات تیم و نیروها، طی ۱ سال و نیم از شروع پیشخان، ۲۰ جابه‌جایی نیروی فنی و چندین نوبت تغییر ساختار در تیم توسعه‌دهندگان داشتیم:در بازه‌ای یک تیم ۱۲-۱۳ نفری داشتیم.دست‌کم دو مرتبه به ۲ تیم با هدف یکسان تقسیم شدیم.دو مرتبه به ۲ تیم با اهداف متفاوت تقسیم شدیم:در بازه‌ای یک تیم روی ارائه آمار جدید برنامه‌ها کار می‌کرد و تیم دیگه روی پیشخان.در بازه‌ای یک تیم نیاز حقوقی بقیه تیم‌ها رو پاسخ می‌داد و یک تیم روی پیشخان کار می‌کرد.این حجم تغییرات باعث می‌شد تیم از تجربه‌های قبلی درس نگیره و با تغییر افراد و ساختار، مدام در حال تجربه روش‌های مختلف باشه.مثلاً بعد از تغییرات تیم، مسائل مشابهی در جلسات رترو مطرح می‌شد. از این قبیل که متدولوژی توسعه چی باشه؟ بورد چطور باشه؟ دیجیتال باشه یا نه؟ کارها رو تخمین بزنیم یا نه و چطور تخمین بزنیم و… . و در طی زمان درگیر مسائل تکراری غیرفنی بودیم.مشکلِ دیگه‌ این بود که در این تیم‌ها در طول زمان، معمولاً زمان کاری مشترکِ خوبی نداشتیم.بخش قابل توجهی از تیم نیروی پاره‌وقت بود و بخش کمی تمام‌وقت. این در حالی بود که یک روز از زمانِ حضورِ نیرو‌های پاره‌وقت صرف جلسات تیمی می‌شد.نیازِ تیم به تعامل و نداشتنِ زمانِ حضور مشترک کافی، باعث کند شدنِ کار می‌شد. برای مثال زمانی که نیاز به تصمیم‌گیری فنی بود و فرد مورد نظر حضور نداشت. یا صاحب یک سیستم و فردی که برای اون سیستم merge request ارسال کرده در زمان‌های متفاوتی در تیم حاضر می‌شدند و رفت و برگشتِ کد زیاد می‌شد.این عدم حضور، حسِ انجامِ کار و رسوندنِ محصول در تیم رو هم کم می‌کرد. چون همیشه تقریباً نیمی از تیم در لحظه حضور نداشت.از نظر بررسی پیشرفت کار در طی زمان هم مشکل داشتیم.در طی توسعه‌ی پیشخان، بیشترِ اوقات فرآیند تیمی رو بصورت کمّی دنبال نمی‌کردیم و بطور حسی تغییر روش می‌دادیم. مثلاً ابتدا با Kanban شروع کردیم. بعد سراغ Scrum رفتیم، بعد دوباره به Kanban برگشتیم و دوباره به Scrum تغییر روش دادیم. در بازه‌‌ای کمی به XP نزدیک شدیم (انجام pair programming). این تغییر ها معمولاً با تغییر تیم/افراد رخ می‌داد و خروجی صحبت افرادِ تیم در اون زمان بود.پس از گذشت حدود ۱ سال از کار تیم روی پیشخان، هر OKR این حس رو داشتیم که این فصل کارهای پیشخان تموم میشه اما کار ها کِش میومدن! و این حس که مشکل داریم خیلی دیرتر دیده شد.شاید اگر دنبال کردنِ پیشرفت پروژه رو جدی‌تر می‌گرفتیم و تیمی با تمرکز بیشتر و تغییر کمتر داشتیم، می‌تونستیم تصمیم‌های مربوط به معماری که گرفته بودیم رو زودتر بازبینی و مشکلاتش رو اصلاح کنیم.حرف پایانیدر این نوشته با مرور مسیر ۳-۴ سال اخیر پنل توسعه‌دهندگان کافه‌بازار، سعی کردیم تجربیات خودمون رو در زمینه‌ی فنی و غیرفنی به اشتراک بذاریم. از مهم‌ترین‌ها می‌تونیم به موارد زیر اشاره کنیم:ما در طراحی مایکروسرویس هرچند مطالعه داشتیم و می‌دونستیم که باید «از سرویس اندک شروع کنیم و زمانی نیاز شد اون رو بشکنیم» اما تجربه عملی و تحلیل درستی از «نیاز» و «زمانی» که باید این کار رو انجام بدیم نداشتیم.وقتی یک سیستم لگاسی داریم که نمیشه داده‌ها رو ازش جدا کرد، بهتره سرویسی که به اون داده‌ها نیاز داره رو کنار اون سیستم لگاسی توسعه بدیم.تغییرات ساختاری تیم، نرخ بالای ورود و خروج نیروها—که تجربه تیم رو از بین می‌برد—و عدم تمرکز تیم در طی زمان، عامل دیگری بود که باعث کندی روند توسعه‌ و شناسایی مشکلات طراحی سیستم شد.در انتها از دوستانم که در گردآوری و تدوین این نوشته کمک کردند تشکر می‌کنم:محمدرضا منتظری، محمدرضا بیکی (بیوک)، محمدحسین نوروزی، شروین حاجی‌اسماعیلی، وحید معصومی و آزاده آقایی.</description>
                <category>کافه بازار</category>
                <author>هاشم غلامی</author>
                <pubDate>Mon, 01 Jul 2019 14:16:29 +0430</pubDate>
            </item>
                    <item>
                <title>سرو شدن پوشهٔ گیت: تجربه‌ای از مواجهه با یک آسیب‌پذیری</title>
                <link>https://tech.cafebazaar.ir/سرو-شدن-پوشهٔ-گیت-تجربهای-از-مواجهه-با-یک-آسیبپذیری-wcvzaw95dqor</link>
                <description>شنبه ۷ اردیبهشت ۹۸ کافه بازار با یک مشکل امنیتی مواجه شد. چنان که در دنیای فناوری‌ مرسوم است، زمانی که شرکت‌ها با مشکلی فنی مواجه می‌شوند، پس از گذر از آن موقعیت، تجربیات خود از آن را با جامعهٔ متخصص به اشتراک می‌گذارند. ما نیز در این مطلب قصد داریم با دید فنی در مورد تجربه‌ای که از این ماجرا داشتیم توضیح دهیم. شروع ماجراصبح روز شنبه خبری در شبکه‌های اجتماعی پخش شد که ادعا می‌کرد «کدها و دیتابیس کافه‌بازار» لو رفته است و در آن تصویری از یک فایل حاوی تنظیمات اتصال به یک پایگاه‌ دادهٔ Postgres و دو پایگاه‌ دادهٔ Redis وجود داشت. از روی این تنظیمات متوجه شدیم که این تصاویر مربوط به یکی از پروژه‌هایمان به نام «پنجره» است.تصویر منتشر شده از تنظیمات پروژهٔ پنجرهاین پروژه یکی از مایکروسرویس‌های کافه بازار است که وظیفه‌ی سرو کردن صفحات وبسایت (cafebazaar.ir) را به عهده دارد. تا مدتی تمام اطلاعاتی که در اختیار داشتیم دو تصویر منتشر شده بود که برای تشخیص آسیب‌پذیری کافی نبود. بنابراین همهٔ فرضیات ممکن برای دسترسی به کدها را با بالاترین درجهٔ آسیب‌پذیری ممکن در نظر گرفتیم و برای مقابله با هریک گروه‌هایی تشکیل دادیم و به صورت همزمان دست به کار شدیم. در ادامه دو تا از مهم ترین فرضیات را شرح می‌دهیم.بررسی امکان نفوذ به سرور گیت (Git)ما در کافه‌بازار برای کنترل مخازن کد از Gitlab استفاده می‌کنیم. یکی از فرضیه‌ها این بود که کد، به دلیل آسیب‌پذیری این ابزار به دست مهاجم افتاده باشد.یکی از اشتباهات ممکن، قرار دادن اطلاعات حساس نظیر رمزها و کلیدها در مخازن کنترل نسخه (Version Control) است. تیم‌های فنی باید تلاش کنند با پیروی از فرآیندها، و تولید ابزارهای مناسب این ریسک را به حداقل برسانند. با این حال ممکن است در روند توسعه، این جنس اطلاعات به کد راه پیدا کرده باشند. در راستای مقابله با این فرض، از همه‌ی تیم‌ها خواستیم کدهای خود را بررسی کنند و در صورت وجود این گونه اطلاعات، آن‌ها را تغییر، و به خارج از مخازن گیت انتقال دهند.از طرف دیگر، این امکان وجود داشت که مهاجم از طریقی موفق به گذشتن از سیستم احراز هویت و ورود به پنل گیت شده باشد. با توجه به فعال بودن احراز هویت دو مرحله‌ای، تمام نشست‌های (sessions) فعال گیت را نامعتبر کردیم تا تمام دسترسی‌های فعلی قطع شود. سپس مشغول بررسی لاگ‌های سرور گیت برای یافتن رفتار مشکوک شدیم که در نهایت رفتار مشکوکی در این لاگ‌ها دیده نشد.همچنین ممکن بود نسخه‌‌ٔ Gitlab مورد استفاده‌ی ما دارای آسیب‌پذیری امنیتی باشد. برای بررسی این فرضیه نیز تیم زیرساخت مشغول بررسی آسیب‌پذیری‌های شناخته شده‌ی Gitlab شد. در بسیاری از موارد، مشکلات امنیتی شرکت‌ها ناشی از به‌روز نبودن ابزارهای مورد استفاده‌شان است. در حالی که اکثر ابزارهای معتبر در صورت کشف آسیب پذیری به سرعت به‌روزرسانی‌های لازم را منتشر می‌کنند. در این مورد به‌روزرسانی‌های لازم بر روی Gitlab اعمال شده بود.بررسی امکان نفوذ به سرور پنجرهما در کافه‌بازار از معماری مایکروسرویس‌ها استفاده می‌کنیم. در این معماری سعی می‌شود هر مایکروسرویس تنها به داده‌های مورد نیاز خود دسترسی داشته باشد. پنجره نیز به عنوان مایکروسرویس صفحات وب کافه‌بازار، جدول نشست کاربرانِ وارد شده به وبسایت را در اختیار داشت.با اینکه هیچ‌گونه شواهدی مبنی بر نفوذ به سرور پنجره وجود نداشت، در راستای مقابله با آن اقداماتی انجام دادیم.پنجره از ابتدا به صورت یک مایکروسرویس منزوی طراحی شده بود و نباید به سرورهای دیگر دسترسی می‌داشت. با این حال تمام ارتباطات بین سرورها را بازنگری کردیم و مطمئن شدیم که سرور پنجره به هیچ سرور دیگری دسترسی مستقیم ندارد. همچنین بررسی کردیم که تمامی سرویس‌ها در شبکه داخلی دارای فرآیند احراز هویت باشند.از طرفی با فرض نفوذ مهاجم، برای اطمینان از ادامهٔ عملکرد صحیح این پروژه، سرور را از دسترس خارج کردیم و پروژه‌ی پنجره را روی یک سرور جدید و با تغییر تمامی رمز‌ها راه‌اندازی کردیم.در کنار این اقدام‌ها، برای پیدا کردن ردپایی از دسترسی غیر معمول به تحلیل لاگ‌ها پرداختیم. علاوه بر این، در گروه دیگری مشغول ارزیابی امکان نفوذ به سرور شدیم. اما در هیچ کدام از این مسیرها مشکلی پیدا نکردیم.اما مشکل چه بود؟بعد از دسترسی به کدهای منتشر شده و بررسی آن‌ها متوجه دو نکتهٔ مهم شدیم. یکی وجود تفاوت در بعضی از فایل‌های منتشر شده با نسخهٔ روی گیت و مطابقت آن‌ها با نسخهٔ روی سرور بود. این نکته فرضیه اول یعنی نفوذ به سرور گیت را رد می‌کرد. نکته‌ی دیگر این که برخی از فایل‌های موجود روی سرور در نسخهٔ منتشرشده وجود نداشتند. هیچ یک از این فایل‌ها، هیچ وقت روی گیت نبوده‌اند. این نکته نیز امکان درستی فرضیه دوم را کمرنگ می‌کرد.در ادامهٔ بررسی‌ها، در میان لاگ‌های وب‌سرور Nginx متوجه وجود تعدادی درخواست موفق به مسیر git./ و فایل‌های پروژه شدیم. این درخواست‌ها دقیقا مطابق با فایل‌های منتشرشده بود. با بررسی تنظیمات Nginx فهمیدیم که وجود یک خطا در این تنظیمات باعث می‌شد پوشهٔ کدهای پروژه توسط وب‌سرور قابل دسترسی باشد. مهاجم با استفاده از این خطا ابتدا موفق به استخراج پوشهٔ git. مربوط به پروژهٔ پنجره شده بود، سپس با استخراج آدرس فایل‌های موجود در پوشهٔ گیت توانسته بود تنها همین فایل‌ها را از روی سرور دانلود کند و مجموع این اتفاقات توسط لاگ‌های سرور تایید می‌شد. با همخوانی همه‌ٔ شواهد و کشف دقیق آسیب‌پذیری، فرضیه‌های بالا رد شد و مشخص شد که مهاجم هیچ گونه دسترسی به سرور و دیتابیس نداشته است.این آسیب پذیری از نوع Source Code Disclosure است که با مراجعه به این لینک می‌توانید اطلاعات بیشتری در مورد آن کسب کنید.جمع‌بندیما در کافه‌بازار عادت داریم پس از اتفاق‌های فنی، مانند از دسترس خارج شدن سرویس‌ها، مشکلات امنیتی و ایرادات جدی محصولی، جلسات Postmortem برگزار کنیم تا زوایای مختلف مسئله را بازنگری کنیم و برای آینده تصمیمات اثرگذار بگیریم. این ماجرا نیز نهایتاً باعث شد که ما بررسی گسترده‌ای روی روال‌های امنیتی گذشته انجام دهیم و برای بهبود آن‌ها تصمیمات مهمی بگیریم.همچنین از همهٔ کسانی که در طول این ماجرا با همراهی خود از ما حمایت کردند صمیمانه تشکر می‌کنیم.</description>
                <category>کافه بازار</category>
                <author>علیرضا میرکاظمی</author>
                <pubDate>Tue, 14 May 2019 22:38:02 +0430</pubDate>
            </item>
                    <item>
                <title>از هزاران درخواست در روز به هزاران درخواست در ثانیه</title>
                <link>https://tech.cafebazaar.ir/از-هزاران-درخواست-در-روز-به-هزاران-درخواست-در-ثانیه-egnaiq0ixbub</link>
                <description>کافه بازار از همان ابتدا ۳۵ میلیون کاربر نداشت و برای رسیدن به این عدد چالش‌های فنی گوناگونی را پشت سر گذاشته است. کافه بازار کارش را با دو نفر توسعه‌دهنده شروع کرد و امروز بیش از صد نفر توسعه‌دهنده در این شرکت کار می‌کنند. اوایل راه کم تجربه بودیم و در طول مسیر روز به روز با مسائل جدید‌تری آشنا می‌شدیم. در این مطلب می‌خواهیم با تمرکز بر چالش‌های بزرگ شدن، مرور کوتاهی بر سرگذشت کافه بازار داشته باشیم. در ادامه از مشکلاتی که با آنها مواجه شده‌ایم و راه‌حل‌هایی که برایشان پیدا کرده‌ایم با شما خواهیم گفت.تولد بازاراسفند ۸۹، بازار برای اولین بار منتشر شد. برنامه بازار برای اندروید را با جاوا، سمت سرور را با پایتون و چارچوب جنگو (Django) نوشتیم. سرور اصلی کافه بازار یک ماشین مجازی بود. از Nginx به عنوان وب سرور، و برای پایگا‌ه‌ داده از پستگرس (Postgres) استفاده کردیم.معماری اولیه‌ی کافه بازاراولین چالش جدی ما زمانی بود که به مرز ۵۰ هزار کاربر رسیدیم و متوجه شدیم که برنامه صفحات را به کندی بارگذاری می‌کند و کاربر‌ها به تناوب با خطا روبه‌رو می‌شوند. پس از بررسی بخش‌های مختلف سمت سرور متوجه شدیم که این مشکل ناشی از اختلالات اتصال (Connection) برنامه به پایگاه داده است و به دلیل تعداد زیاد درخواست‌ها، پایگاه داده توان پاسخگویی به همه آن‌ها را ندارد. راه حلی که برای کم کردن تعداد درخواست‌ها به پایگاه داده پیدا کردیم، اضافه کردن لایه‌ی کَش (Cache) با استفاده از Memcached بود. بدین شکل که پاسخ درخواست‌های پرتکرار را در حافظه ذخیره می‌کردیم و در درخواست‌های بعدی بدون نیاز به دسترسی به پایگاه داده، همان داده‌ی کَش شده را پاسخ می‌دادیم. با این روش سرعت پاسخگویی به مقدار زیادی افزایش پیدا کرد و دیگر خطایی از سمت برنامه نگرفتیم.استفاده از Memcached در معماریکابوس کاربران همزمان با ادامه رشد تعداد کاربران به مشکل جدیدی برخوردیم. برنامه بازار گاهی به سرعت صفحات را بارگذاری می‌کرد و گاهی بسیار کند. پس از بررسی لایه‌های مختلف متوجه شدیم که در هر لحظه فقط یک اتصال بین Nginx و جنگو برقرار می‌شود و تا زمانی که این اتصال بسته نشود درخواست‌های بعدی در صف Nginx‌ منتظر می‌مانند. یکی از راه‌حل‌های متداول برای رفع این مشکل در پروژه‌های پایتونی استفاده از یک سرور WSGI، مانند گونیکورن (Gunicorn) است. گونیکورن چندین نمونه از برنامه‌ی جنگو را اجرا می‌کند و از این پس Nginx به عنوان پراکسی درخواست‌ها را از کاربر گرفته و به گونیکورن ارسال می‌کند. به این ترتیب توانستیم به تعداد ورکرهای (Worker) گونیکورن، درخواست‌های همزمان را پاسخ دهیم.استفاده از گونیکورن در معمارینیاز به یک سرور قدرتمندسال ۹۱، در مرز ۱ میلیون کاربر، دیگر ماشین مجازی پاسخگو نبود و اولین سرور اختصاصی را خریدیم و تمام سرویس‌ها را روی آن راه‌اندازی کردیم.اولین سرور اختصاصی کافه‌ بازاربهبود معماری نرم‌افزاربا افزایش امکانات و گسترش خدمات بازار، کدهای پروژه هم بیشتر می‌شد. در این زمان تعداد توسعه‌دهنده‌ها به ۱۵ نفر رسیده بود و هماهنگی‌های لازم برای حفظ یکپارچگی و تمیزی کد سخت‌تر شده بود. بنابراین تصمیم گرفتیم برای حل این مشکل کدهای مربوط به پرداخت را به عنوان یک پروژه جدید، جدا کنیم که سرویس‌های کوچک‌تری داشته باشیم و هر کس بتواند به صورت مستقل روی سرویس مشخصی کار کند. این پروژه را روی سرور مجزایی راه‌اندازی کردیم و این نقطه‌ی شروعی بود برای سرور‌های بیشتر.پایان دوران ارتقای سرورهاوقتی تعداد درخواست‌های یک سرویس از توان پاسخگویی آن بیشتر شود، دو راه برای تغییر مقیاس وجود داردتغییر مقیاس عمودی (Vertical Scaling)تغییر مقیاس افقی (Horizontal Scaling)در تغییر مقیاس عمودی، افزایش توان پاسخگویی با ارتقای سخت‌افزاری سرورهای موجود انجام می‌شود. در تغییر مقیاس افقی، با افزودن سرور‌های بیشتر به سیستم امکان‌پذیر می‌گردد.ما تا سال ۹۲ برای افزایش توان پاسخگویی،‌ سرور قوی‌تری را جایگزین سرورهای موجود می‌کردیم یا به اصطلاح از تغییر مقایس عمودی استفاده می‌کردیم. اما ارتقا دادن سرور تا حدی امکان‌پذیر است. بالاخره به نقطه‌ای رسیدیم که نیاز به افزایش تعداد سرور‌ها حس می‌شد و در زمستان ۹۲ تصمیم گرفتیم که سرویس کافه‌بازار را روی دو سرور راه‌اندازی کنیم و درخواست‌ها را بین این دو پخش کنیم. برای متعادل کردن بار از DNS استفاده کردیم. اما برای پاسخگویی به تمام این درخواست‌ها هنوز یک نمونه از پایگاه داده وجود داشت که گلوگاه سامانه بود. برای حل این مشکل از قابلیت Replication در پستگرس استفاده کردیم و نسخه‌ای از پایگاه داده را در سرور دوم بالا آوردیم. این قابلیت در پستگرس فقط می‌تواند به شکل Master-Slave باشد. به این معنی که به غیر از سرور اصلی، بقیه سرورها قابلیت نوشتن ندارند. خوشبختانه بیشتر درخواست‌های ما هم نیاز به نوشتن در دیتابیس نداشتند. بنابراین سرور دوم را فقط به منظور خواندن از  پایگاه داده در نظر گرفتیم.استفاده از Replication در پایگاه دادهنمونه‌ای دیگر از تغییر مقیاس افقی که در سال ۹۲ انجام دادیم، پروژه‌‌ی تغییر نحوه‌‌ی محاسبه‌‌ی تعداد نصب‌های فعال برنامه‌ها بود. ما تا قبل از این پروژه، برای محاسبه‌ی این عدد، بخشی از کاربران را به صورت تصادفی انتخاب می‌کردیم و برنامه‌های نصب شده‌ی آن‌ها را ساعت به ساعت برای بک‌اند (backend) می‌فرستادیم و بر این اساس میزان نصب هر برنامه را تخمین می‌زدیم. مشخصاً این روش دارای دقت بسیار پایینی بود، در نتیجه تصمیم گرفتیم این محاسبه را برای تمام کاربران انجام دهیم. چالشی که برای رسیدن به این هدف داشتیم تعداد بسیار زیاد کاربران بود و با توجه به ارسال ساعت به ساعت این اطلاعات به قدرت محاسباتی بالایی نیاز داشتیم. برای حل این مشکل پروژه‌ی جدیدی به نام «چرتکه» را شروع کردیم که روی چند سرور به طور همزمان اجرا می‌شد و هر کدام از سرورها بخشی از این محاسبات را به عهده می‌گرفتند. در پایان هر روز یکی از این سرورها به عنوان سرور هماهنگ‌کننده انتخاب می‌شد و اطلاعات لازم را از سایر سرور‌ها می‌گرفت و نتیجه‌ی تجمیع شده‌ را به کافه بازار می‌فرستاد. ده سرور تا سال ۹۴ به این پروژه خدمت‌رسانی کردند.به سوی مایکروسرویس‌هاما در کافه بازار به استقلال تیم‌ها اهمیت زیادی می‌دهیم، تا هر تیمی بتواند با سرعتی بالا به سوی اهدافش حرکت کند. اما وجود فقط تعداد کمی پروژه‌ی بزرگ باعث وابستگی تیم‌های مختلف به یکدیگر ‌می‌شد و نیاز به هماهنگی‌های غیر ضروری افراد و تیم‌ها را در پی داشت. با افزایش تعداد توسعه‌دهنده‌ها و بزرگ‌ شدن تیم‌ها، این وابستگی به یک مشکل بزرگ تبدیل شد که باعث کاهش سرعت خروجی تیم‌ها می‌شد. همچنین با بزرگ‌تر شدن سرویس‌ها مقیاس‌پذیری سخت‌تر و سخت‌تر می‌شد. برای حل این مشکلات تصمیم گرفتیم معماری کلی کافه بازار را به سمت مایکروسرویس‌ها ببریم و در ادامه هر سرویس بزرگ را به چندین سرویس کوچک شکستیم. این معماری جدید باعث شد که هر تیمی بتواند مسئولیت تعدادی از مایکروسرویس‌ها را به عهده بگیرد و بدون وابستگی به دیگر تیم‌ها روی آن‌ها کار کند. روند شکستن کافه بازار از زمستان ۹۴ شروع شد و هم‌اکنون بیش از ۵۰ مایکروسرویس در حال سرویس‌دهی هستند.عکس از www.programmableweb.comبر فراز ابرهااز دیگر چالش‌های ما، وظیفه‌ی نگهداری از سرورها بود که در همه‌ی سطوح (از سخت‌افزار تا سیستم عامل و تمام سرویس‌های آن) به عهده تیم استفاده‌کننده از آن بود. در حال حاضر بیش از ۵۰ سرور برای محصول کافه بازار استفاده می‌شوند. این مسئله هزینه‌ی زمانی زیادی برای تیم‌ها داشت و همچنین در مواقع اضطراری امکان تغییر مقیاس سریع سرویس وجود نداشت. علاوه بر این، امکان استفاده از منابع سرورها به صورت مشترک برای سرویس‌ها وجود نداشت و هر سرویس فقط از منابع یک سرور می‌توانست استفاده کند. در نتیجه استفاده‌ی بهینه از منابع سرورها امکان‌پذیر نبود.تیمی به نام زیرساخت با وظیفه‌ی یافتن راه حلی مناسب برای این مشکلات شکل گرفت. این تیم پس از بررسی راه حل‌های متفاوت، کوبرنتیز (Kubernetes) را به عنوان راه‌حل نهایی انتخاب کرد. با استفاده از کوبرنتیز می‌توانیم تغییر مقیاس هر مایکروسرویس را به طور جداگانه و خودکار انجام دهیم و از منابع سخت‌افزاری سرور‌ها به صورت بهینه استفاده کنیم. در حال حاضر، قسمت عمده‌ای از سرویس‌های کافه بازار به کوبرنتیز مهاجرت کرده‌اند.ما در کافه بازار هنوز راه طولانی در پیش داریم و روز به روز با افزایش تعداد کاربران و قابلیت‌های بیشتر، با چالش‌های جدیدتری روبه‌رو می‌شویم. برای همراهی ما در این راه به ما بپیوندید</description>
                <category>کافه بازار</category>
                <author>مهران اخوان</author>
                <pubDate>Wed, 10 Jan 2018 14:30:03 +0330</pubDate>
            </item>
            </channel>
</rss>