<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های میلاد ابراهیمی</title>
        <link>https://virgool.io/feed/@milad_ibra</link>
        <description>مهندس نرم‌افزار ساده</description>
        <language>fa</language>
        <pubDate>2026-06-17 01:31:55</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/14690/avatar/vek1OI.jpg?height=120&amp;width=120</url>
            <title>میلاد ابراهیمی</title>
            <link>https://virgool.io/@milad_ibra</link>
        </image>

                    <item>
                <title>طراحی SDK نقشه با زبان برنامه‌نویسی گولنگ در اسنپ!</title>
                <link>https://virgool.io/snapp-eng/%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-sdk-%D9%86%D9%82%D8%B4%D9%87-%D8%A8%D8%A7-%D8%B2%D8%A8%D8%A7%D9%86-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%DA%AF%D9%88%D9%84%D9%86%DA%AF-%D8%AF%D8%B1-%D8%A7%D8%B3%D9%86%D9%BE-sq6p6dkz4lmt</link>
                <description>مقدمهتو این مقاله می‌خوام در مورد فرآیند طراحی و پیاده‌سازی SDK سرویس‌های مبتنی بر نقشه در اسنپ توضیح بدم.قبل از هرچیزی تعریف SDK رو بررسی می‌کنم. بعد سراغ چرایی نیاز به SDK برای سرویس‌های نقشه اسنپ می‌رم. نکات استخراج شده از بررسی‌هامون روی SDK های متن‌بازی که به خوبی طراحی شدن رو مطرح می‌کنم. و در آخر به نحوه‌ی پیاده‌سازی نیاز‌های منحصر به فرد خودمون با استفاده از زبان برنامه‌نویسی گولنگ اشاره می‌کنم.طراحی SDK نقشه با زبان برنامه‌نویسی گولنگ در اسنپ!تعریف SDKعبارت Software Development Kit یا به اختصار همون SDK، به مجموعه‌ای از ابزار برای راحت‌تر شدن توسعه نرم‌افزار تو یک زمینه‌ی خاص اطلاق میشه.معمولا یک SDK برای اجرا،‌ وابسته به یک یا چند API است. این API ها می‌تونن تحت وب باشند یا کاملا آفلاین. نکته‌ی مهم دیگه که تو SDK ها وجود دارند اینه که اکثرا فقط تو یک محیط خاص (مثلا زبان برنامه‌نویسی، سیستم‌عامل و یا یک سخت‌افزار خاص) اجرا میشن.چند مثال زیر نمونه‌های از SDK های تحت وب هستند که برای زبان‌برنامه‌نویسی گولنگ طراحی شدند:AWS SDK (Golang)Google Maps SDK (Golang) Dropbox SDK (Golang)چرا برای سرویس‌های نقشه‌ی اسنپ نیاز به SDK داشتیم؟ما توی تیم نقشه‌ی اسنپ (خودمون اسمپ صداش می‌کنیم!) سرویس‌های مختلفی فراهم می‌کنیم. بعضی از این سرویس‌ها عبارتند از: سرویس Reverse Geocode: کارایی این سرویس، تبدیل مختصات جغرافیایی به آدرس خوانا برای انسان است.سرویس Search: برای پیدا کردن مختصات مکانی محل‌های خاص در نقشه صرفا با استفاده از متن، از این سرویس استفاده میشه.سرویس Estimated Time of Arrival یا ETA: از این سرویس برای تخمین زمان یک سفر از نقطه‌ی «آ» به نقطه‌ی «ب» استفاده میشه.و ...این سرویس‌ها توسط خیلی از تیم‌های اسنپ، اسنپ‌باکس، اسنپ‌فود و ... استفاده می‌شن.نحوه‌ی استفاده‌‌ی این سرویس‌ها قبلا به این شکل بود که مستندات API ها (معمولا در قالب OpenAPI) در اختیار تیم‌های استفاده کننده قرار می‌گرفت و استفاده از API ها کاملا به عهده‌ی خود تیم بود.این تجربه‌ی توسعه زیاد جالب نبود. هر تیمی به روش خودش از API ها استفاده می‌کرد و در نتیجه کدبیس‌های متفاوتی برای پیاده‌سازی یک functionality در سازمان به وجود می‌اومد.در نتیجه تصمیم گرفتیم برای سرویس‌های اسمپ یک SDK بسازیم و در اختیار استفاده‌کنندگان قرار بدیم.نکات مهم در طراحی SDKبا بررسی SDK های متن‌باز و معروف، مخصوصا SDK های پیاده‌سازی شده با گولنگ، نکاتی رو استخراج کردیم که سعی کردیم اکثرشون رو با توجه به شرایط تیم و پروژه‌ها رعایت بکنیم. چند تا این نکات رو با هم می‌شکافیم.کاربر‌ها را غافل‌گیر نکنید!یک SDK خوب دقیقا همون کاری رو می‌کنه که کاربرش میگه. نه بیشتر نه کمتر! پیاده‌سازی بر اساس پیش‌فرض‌های ذهنی توسعه دهنده‌ی سرویس، که لزوما با پیش‌فرض‌های کاربر‌ان یکسان نیست، ممکنه باعث بشه در برخی از مواقع کاربر غافل‌گیر بشه.از کمترین Dependency ممکن استفاده کنید.استفاده از کتاب‌خانه‌های مختلف در پیاده‌سازی SDK باید به حداقل برسه. یکی از دلایلش اینه که در این صورت شما کاربر رو مجبور به استفاده از یک کتاب‌‌خانه می‌کنید. کاربر ممکنه به هر دلیلی نخواد یا نتونه از اون کتاب‌خانه استفاده بکنه. مثلا برخی از دلایل می‌تونن شبیه لیست زیر باشند: حجم کتاب‌خانه برای اون پروژه بیش‌از حد زیاده.اون پروژه داره از یک نسخه‌ی دیگه‌ای از کتاب‌خانه استفاده می‌کنه و با توجه به محدودیت بعضی از زبان‌های برنامه‌نویسی نمی‌تونه دو تا نسخه‌ از یک کتاب‌خانه رو همزمان داشته باشه.در برخی شرایط ممکنه اون کتاب‌خانه با سخت‌افزار سازگار نباشه.مشکل امنیتی توی کتاب‌خانه وجود داشته باشه. (شما هم یاد log4j افتادید؟ :)) )و ...رفتار ثابتی در SDK طراحی نکنید!نیاز‌های تیم توسعه‌ی کاربران شما می‌تونه تفاوت‌های زیادی داشته باشه. سعی کنید حتی الامکان همه‌ی رفتار‌های SDK قابل پیکربندی و شخصی‌سازی باشن.چند مثال:کاربر باید بتونه آدرس سرویس‌هایی که درخواست‌ها بهشون ارسال میشه رو شخصی‌سازی بکنه. کاربر می‌تونه از این ویژگی برای Mock کردن تست‌ها و یا بهره‌برداری از سرویس‌های دیگه به عنوان fallback استفاده بکنه.کاربر باید بتونه تنظیمات مربوط به کلاینت شبکه رو با توجه به نیاز‌ها و وضعیت شبکه‌ی خودش شخصی‌سازی بکنه. برای مثال توی گولنگ باید بشه http.Transport کلاینت‌های http رو override بکنه.تمام اطلاعات رو در اختیار کاربر بگذارید.هنگام پیاده‌سازی توجه داشته باشید که تمام اطلاعات رو در اختیار کاربر بگذارید. اگه اروری پیش میاد حتما اطلاعات کامل ارور رو در اختیار کاربر بگذارید که در صورت نیاز بتونه مشکلات رو سریع‌تر بفهمه و مرتفع بکنه. یا قابلیت لاگ‌کردن به صورت Verbose رو به صورت اختیاری برای SDK خودتون پیاده‌سازی کنید. در این صورت کاربر می‌تونه تمام اتفاقاتی که داخل SDK می‌افته رو مشاهده بکنه.سعی کنید SDK شما قابل mock کردن باشه.احتمالا از اهمیت تست‌نوشتن برای برنامه‌هایی که می‌نویسید با خبر هستید. پس از اونجا که تست‌کردن برای ما پسندیده است، برای کاربران SDK ما هم پسندیده‌است :)برای این منظور می‌تونید از interface ها در گولنگ استفاده کنید.ما تو اسمپ یک ورژن ماک‌شده‌ی آماده هم از SDK مون در اختیار کاربر قرار می‌دیم که کمتر زحمت بکشه. این کار رو با استفاده از mockgen انجام دادیم.اگر امکانش رو دارید کد‌های SDK رو generate کنید.معمولا کاربران از تکنولوژی‌های مختلفی برای استفاده‌از سرویس‌های ما به کار می‌گیرند. به خاطر همین موضوع بهتره رفتار‌ها و ویژگی‌های یک SDK رو با یک زبان میانی توصیف کنید و در نهایت از روی این توصیفات، SDK مربوط به زبان‌های برنامه‌نویسی مد نظرتون رو generate بکنید.قاعدتا این کار می‌تونه خیلی پیچیده و زمان‌بر باشه. پس زمانی سراغش برید که شرایط تیمتون مناسب باشه.نحوه‌ی پیاده‌سازی SDK نقشه اسنپ با در زبان گولنگساختار کد SDK پیاده‌سازی‌شده‌ی ما در دیاگرام زیر خلاصه شده:نمودار UML ساختار SDKاطلاعات پراستفاده و مشترک بین کلاینت‌های سرویس‌های مختلف در پکیج config نگه داری شده است.نکته: لازم به ذکر است که برای راحتی کاربران SDK می‌توان قابلیت خواندن کانفیگ از فایل، Environment variable و راه‌های دیگر را پیاده‌سازی کرد.برای هر سرویس یک پکیج جدا در نظر گرفته شده است. برای مثال در نمودار بالا پکیج ETA را مشاهده می‌کنید.در پکیج مربوط به هر سرویس، یک اینترفیس شامل تعریف قابلیت‌های سرویس وجود دارد.دو پیاده‌سازی از این اینترفیس‌ها وجود دارد که هر کدام کاربرد متفاوتی دارند. یکی از پیاده‌سازی‌ها که یک شی از config را نیز در اختیار دارد،‌جهت استفاده عادی و ارسال درخواست به سرور‌ها استفاده می‌شود.پیاده‌سازی دیگر جهت استفاده در Unit test های کاربران SDK و Mock کردن هرچه‌ راحت‌تر تست‌هاست.نکته‌ی بسیار مهم در طراحی interface هر سرویس، استفاده از context.Context گولنگ بوده. این امر باعث شده کنترل بیشتر و گسترش‌پذیری بسیار بالاتری داشته باشیم.پایانممنون که پست رو تا انتها خوندید. امیدوارم براتون مفید بوده باشه.در ضمن، اگه حس می‌کنین از پروژه‌هایی شبیه به این خوشتون میاد و در نتیجه علاقه دارید به تیم ما ملحق بشید، خوشحال می‌شیم که رزومه‌هاتون رو از طریق آدرس engineering@snapp.cab برای ما ارسال کنید. مراقب خودتون باشید!</description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Sat, 25 Jun 2022 16:31:23 +0430</pubDate>
            </item>
                    <item>
                <title>استفاده از Mutex یا پکیج atomic؟ مسئله این است.</title>
                <link>https://virgool.io/@milad_ibra/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-mutex-%DB%8C%D8%A7-%D9%BE%DA%A9%DB%8C%D8%AC-atomic-%D9%85%D8%B3%D8%A6%D9%84%D9%87-%D8%A7%DB%8C%D9%86-%D8%A7%D8%B3%D8%AA-rv5qkgmset6w</link>
                <description>تو این پست می‌خوام sync.Mutex و پکیج atomic تو گولنگ معرفی کنم و از تفاوت هاشون براتون بگم. برای این کار یه مسئله‌ی ساده مطرح می‌کنیم و سعی می‌کنیم حلش کنیم.نکته: اگر برنامه‌نویس حرفه‌ای گولنگ هستید احتمالا اوایل این پست به دردتون نمی‌خوره. با این حال به نظرم تا آخر بخونید. سعی کردم مسائلی رو مطرح کنم که خیلی کم بهش اشاره شده.تعریف مسئلهخب مسئله‌ خیلی ساده است. می‌خوایم یه counter داشته باشیم و یه چیزی (هر چیزی می‌تونه باشه، تعداد request، تعداد خرید و ...) رو بشمریم. نکته مهم این مسئله اینه که این متغیر شمارنده ما ممکنه توسط goroutine های مختلف خونده بشن یا تغییر داده بشن.پس یک مشکل کلاسیک جلوی ما هست. Race Conditionهمونطور که احتمالا می‌دونید، گولنگ راه‌حل‌های مختلفی برای حل این مشکل داره. توی این پست به دو مورد از این راه‌حل ها می‌پردازیم. استفاده از Mutex (که داخل پکیج sync استاندارد لایبرری وجود داره)استفاده توابع از پکیج sync/atomic استاندارد لابرریاستفاده از Mutexخب اینجا نمی‌خوام نحوه‌ی کارکرد و فلسفه‌ی Mutex رو توضیح بدم. صرفا یه تیکه کد کوچیک برای مسئله‌ی counter می‌ذارم.برای ساده‌تر شدن مسئله،‌ چند تا goroutine ایجاد نمی‌کنم. صرفا متغیر رو تعریف می‌کنم و یک بار با Mutex مقدارش رو زیاد می‌کنم.خب با این حال کدش خیلی خیلی ساده میشه:package main

import (
   &amp;quotfmt&amp;quot
   &amp;quotsync&amp;quot
)

func main() {
   var counter int32 = 0
   mu := sync.Mutex{}
   mu.Lock()
   counter++
   mu.Unlock()
   
   fmt.Println(counter)
}خروجی این تیکه کد قاعدتا ۱ خواهد بود.استفاده از توابع پکیج sync/atomicاگر همون کد بالا رو با استفاده از این روش بخوایم پیاده‌سازی بکنیم، این شکلی خواهد شد:package main

import (
   &amp;quotfmt&amp;quot
   &amp;quotsync/atomic&amp;quot
)

func main() {
   var counter int32 = 0
   atomic.AddInt32(&amp;counter, 1)
   fmt.Println(counter)
}خروجی این کد هم قاعدتا ۱ خواهد بود.ولی چه عجیب! کمتر نیازه کد بزنیم اینجوری :) خب همیشه همینطوری کد بزنیم دیگه!نه یکم صبر کنید می‌فهمید داستان چیه :)تفاوت دو روشاول از همه می‌خوام یه بنچمارک بنویسم ببینم کدوم یکی از روش‌ها سریع تره. کد بنچمارک:package main

import (
   &amp;quotsync&amp;quot
   &amp;quotsync/atomic&amp;quot
   &amp;quottesting&amp;quot
)

func BenchmarkAtomic(b *testing.B) {
   var counter uint32 = 0
   for i := 0; i &lt; b.N; i++ {
      atomic.AddUint32(&amp;counter, 1)
   }
}

func BenchmarkMutex(b *testing.B) {
   var counter uint32 = 0
   mu := sync.Mutex{}
   for i := 0; i &lt; b.N; i++ {
      mu.Lock()
      counter++
      mu.Unlock()
   }
}با کامند زیر میشه بنچمارک رو اجرا کرد.go test -bench=.خروجی این کامند رو‌ی لپ‌تاپ من شد این:BenchmarkAtomic-8       268115684                4.372 ns/opBenchmarkMutex-8        100000000               10.22 ns/opهمنطور که می‌بینید مقدار نهایی counter تو حالت atomic شده ۲۶۸۱۱۵۶۸۴ ولی تو حالت Mutex شده یه دونه یک با هشت تا صفر!یعنی هر operation تو حالت atomic حدود ۴ نانو ثانیه طول کشیده ولی تو حالت Mutex حدود ۱۰ نانو ثانیه.پس انگار atomic خیلی سریع تره. حدود ۲.۵ برابر!خب اینطوری که atomic هم سریع‌تره و تعداد خط کد کمتری داره. همیشه از این استفاده می‌کنیم پس! بازم نه. یکم صبر :)به نظرتون این تفاوت سرعت از کجا ناشی میشه؟برای اینکه بهتر متوجه بشیم بیاید که کار غیر معمول انجام بدیم. خروجی اسمبلی کامپایلر گولنگ برای دو تا کد رو مقایسه کنیم!مگه میشه؟ آره میشه. با کامند زیر می‌تونید خروجی اسمبلی رو ببینید.go tool compile -S file.go &gt; file.sاز اونجایی که خروجی اسمبلی همین چند خط کد خیلی بلنده، فقط به کپی‌کردن قسمت‌های اصلی اون بسنده می‌کنم.این قطعه کد اسمبلی معادل حالت Mutex (خط ۱۰ تا ۱۳،‌ساخت Mutex تا Unlock کردن آن) هست:0x0026 00038 (mutex.go:10)  MOVQ   AX, CX
0x0029 00041 ($GOROOT/src/sync/mutex.go:74)    XORL   AX, AX
0x002b 00043 ($GOROOT/src/sync/mutex.go:74)    MOVL   $1, DX
0x0030 00048 ($GOROOT/src/sync/mutex.go:74)    LOCK
0x0031 00049 ($GOROOT/src/sync/mutex.go:74)    CMPXCHGL   DX, (CX)
0x0034 00052 ($GOROOT/src/sync/mutex.go:74)    SETEQ  BL
0x0037 00055 ($GOROOT/src/sync/mutex.go:74)    TESTB  BL, BL
0x0039 00057 ($GOROOT/src/sync/mutex.go:37)    JNE    82
0x003b 00059 (mutex.go:10) MOVQ   CX, &amp;quot&amp;quot..autotmp_25+40(SP)
0x0040 00064 ($GOROOT/src/sync/mutex.go:81)    MOVQ   CX, AX
0x0043 00067 ($GOROOT/src/sync/mutex.go:81)    PCDATA $1, $1
0x0043 00067 ($GOROOT/src/sync/mutex.go:81)    CALL   sync.(*Mutex).lockSlow(SB)
0x0048 00072 ($GOROOT/src/sync/mutex.go:186)   MOVQ   &amp;quot&amp;quot..autotmp_25+40(SP), CX
0x004d 00077 ($GOROOT/src/sync/mutex.go:186)   MOVL   $1, DX
0x0052 00082 (mutex.go:13) PCDATA $1, $-1
0x0052 00082 (mutex.go:13) XCHGL  AX, AX
0x0053 00083 ($GOROOT/src/sync/mutex.go:186)   MOVL   $-1, SI
0x0058 00088 ($GOROOT/src/sync/mutex.go:186)   LOCK
0x0059 00089 ($GOROOT/src/sync/mutex.go:186)   XADDL  SI, (CX)
0x005c 00092 ($GOROOT/src/sync/mutex.go:186)   LEAL   -1(SI), BX
0x005f 00095 ($GOROOT/src/sync/mutex.go:186)   NOP
0x0060 00096 ($GOROOT/src/sync/mutex.go:187)   TESTL  BX, BX
0x0062 00098 ($GOROOT/src/sync/mutex.go:187)   JEQ    113
0x0064 00100 ($GOROOT/src/sync/mutex.go:190)   MOVQ   CX, AX
0x0067 00103 ($GOROOT/src/sync/mutex.go:190)   PCDATA $1, $0
0x0067 00103 ($GOROOT/src/sync/mutex.go:190)   CALL   sync.(*Mutex).unlockSlow(SB)
0x006c 00108 ($GOROOT/src/sync/mutex.go:190)   MOVL   $1, DXفهمیدن اینا واقعا سخته. اصلا زبون آدمیزاد نیست! ولی بیاید همین کار رو هم واسه حالت atomic انجام بدیم ببینیم چی میشه!خروجی این شکلی خواهد بود! (فقط خط ۱۰ که عمل اضافه‌کردن به طور atomic انجام میشه)0x0025 00037 (atomic.go:10) MOVL   $1, CX
0x002a 00042 (atomic.go:10)    LOCK
0x002b 00043 (atomic.go:10)    XADDL  CX, (AX)میزان تفاوت شگفت انگیزه! الان می‌تونیم حدس بزنیم چرا atomic سریع تره! میشه گفت توابع پکیج atomic از قابلیت‌های cpu برای انجام  اعمال استفاده می‌کنه و این باعث میشه تعداد instruction های اسمبلی تولید شده خیلی کمتر باشه.ولی ساختن یک Mutex عام‌منظوره که به درد همه‌ی سناریو‌های ساده و پیچیده بخوره خیلی سخت‌تره و کلی کار از cpu می‌کشه. از ساختش بگیر، تا Lock و Unlock کردنش!پس همیشه از atomic استفاده کنیم؟آره دیگه انگار همه‌ی شواهد میگه این کار رو بکنید!ولی نه! یه لحظه وایسید بریم یه سر به داکیومنت رسمی گولنگ برای این پکیج بزنیم. (لینک)یه جمله‌ی خیلی خیلی عجیب همون اولا نوشته:These functions require great care to be used correctly. Except for special, low-level applications, synchronization is better done with channels or the facilities of the sync package. Share memory by communicating; don&#x27;t communicate by sharing memory.صراحتا داره می‌گه اگه نمی‌دونی داری چیکار می‌کنی ازش استفاده نکن و فقط زمانی ازش استفاده کن که می‌دونی داری چیکار می‌کنی!انگار ورق برگشت. ولی خب یعنی چی که «فقط زمانی ازش استفاده کن که می‌دونی داری چیکار می‌کنی»؟اگر این سوال برای شما هم مطرحه باید بگم که جواب این سوال اینه: اگه این سوال واستون وجود داره، احتمالا نمی‌دونید دقیقا دارید چیکار می‌کنید یا چیکار می‌خواید بکنید :))به نظرم این داکیومنت خیلی خیلی ناقصه و سازندگان گولنگ باید بیشتر توضیح می‌دادن!من خیلی جست‌و‌جو کردم که بفهمم داستان چیه! و به یه چیزایی رسیدم که با شما به اشتراک می‌ذارم. توابع پکیج‌ atomic تا زمانی safe هستن که صرفا برای خوندن و نوشتن داده‌های آماری ازش استفاده بشه و نمیشه روی این توابع برای فهمیدن state اپلیکیشن استفاده کرد. مثلا استفاده کردن از این توابع برای ساخت متریک‌های Prometheus اشکالی نداره (واقعا همینطوری پیاده‌سازی شدن.)دستورات atomic تنها این گارانتی رو میدن که مقدار درستی توی حافظه نوشته بشه و integrity حافظه و کش cpu و ... حفط بشه. ولی هیچ گارانتی ‌ای مبنی بر این که گوروتین‌های دیگه وقتی دارن اون محل حافظه رو می‌خونن یا حتی می‌نویسن ( با روش‌های دیگه) مقدار درستی می‌خونن یا می‌نویسن نداره! به عبارتی وقتی شما از توابع atomic استفاده می‌کنید صرفا یک قسمتی از حافظه رو lock می‌کنید. اون هم در یک بازی زمانی بسیار کوتاه. ولی زمانی که از Mutex استفاده می‌کنید، یک بلوک کد رو lock می‌کنید. این باعث میشه بتونید برای عملیات‌های read و  write خودتون اولویت هم بتونید بذارید. (مثلا استفاده از RWmutex) ولی این کار رو نمی‌تونید با توابع atomic انجام بدید.این نکاتی که نوشتم برداشت خودم بوده و ممکنه نقایصی داشته باشه یا حتی بعضی جاهاش غلط باشه! برای همین یک لینک تو قسمت منابع می‌ذارم که اگر علاقه داشتید برید و بحث‌ها رو عمیق تر دنبال کنید.نتیجهاین همه توضیح،‌ می‌رسیم به این جمله که «تا زمانی که دقیقا نمی‌دونید دارید چیکار می‌کنید از atomic استفاده نکنید» :)) (البته این باز نظر شخصی منه. هر طور خودتون راحتید)منابعhttps://groups.google.com/g/golang-nuts/c/0uPDCRiBWqchttps://pkg.go.dev/sync/atomic</description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Fri, 22 Oct 2021 19:36:03 +0330</pubDate>
            </item>
                    <item>
                <title>آهای گولنگ، مموری اضافه‌ی من کجاست؟</title>
                <link>https://virgool.io/golangpub/hey-golang-where-is-my-memory-oky2gqaxd5ly</link>
                <description>اگر تا حالا با زبان برنامه‌نویسی گولنگ کد زده باشید حتما از struct ها استفاده کردید. زمانی که یک شئ (Object) از یک struct می‌سازید با توجه به شرایط و نحوه‌ی ساخت و استفاده،‌ یک مقدار فضای مشخصی از stack یا heap اشغال می‌کنه.ولی تا حالا بررسی کردید چقدر فضا اشغال می‌کنه؟ میشه یه کاری کرد کمتر جا بگیره؟ بریم بررسی کنیم ببینیم چی میشه! اندازه‌گیری میزان اشغال حافظهخب اولین راهی که به ذهن آدم می‌رسه برای اینکه میزان اشغال حافظه یا همون مموری، توسط یه آبجکت رو اندازه بگیره، اینه که سایز هر کدوم از فیلد هاش رو باهم جمع بکنه. تو لیست زیر اندازه primitive type ها تو گولنگ رو می‌تونید ببینید.uint8, int8: 1 Byteuint16, int16: 2 Bytesuint32, int32, float32:  4 Bytesuint64, int64, float64, complex64: 8 Bytescomplex128: 16 Bytesخب با این حساب می‌تونید اندازه یک آبجکت از struct زیر رو اندازه‌گیری کنید؟type Foo struct {
   A bool
   B int64
   C bool
   D float64
}یه جمع و تفریق ساده است دیگه. طبق لیست بالا، ۱ بایت برای هر کدوم از فیلد‌های A و C و همچنین ۸ بایت برای هر فیلد B و D.جمعا میشه ۱۸ بایت.ولی من یه کوچولو شک دارم! بذارید یه کد بزنیم و ببینیم اندازه واقعی یک آبجکت از این نوع Foo چقدر حافظه اشغال می‌کنه!اندازه‌گیری اندازه‌ی آبجکت‌ها در گولنگتوی گولنگ با استفاد از پکیج unsafe میشه اندازه‌ی واقعی آبجکت‌ها رو با استفاده از تابع Sizeof فهمید.قطعه کد زیر یک آبجکت از نوع Foo میسازه و سایزش رو چاپ می‌کنه!package main

import (
   &amp;quotfmt&amp;quot
   &amp;quotunsafe&amp;quot
)

type Foo struct {
   A bool
   B int64
   C bool
   D float64
}

func main() {
   f :=  Foo{}
   fmt.Println(unsafe.Sizeof(f)) // prints 32!
}در کمال تعجب این کد عدد ۳۲ رو چاپ می‌کنه! ۳۲ کجا ۱۸ کجا! این عدد ۷۷ درصد بزرگ‌تر از چیزیه که فکر می‌کردیم!به نظر میاد گولنگ داره مموری می‌دزده!دلیلش چی می‌تونه باشه؟گولنگ و Padding/Alignmentیکم که درباره نحوه‌ی کار کامپایلر گولنگ بخونیم، می‌فهمیم مفهومی وجود داره به اسم Padding یا Alignmentکامپایلر گولنگ یه سری ویژگی‌هایی در این مورد داره. هدف اصلی این ویژگی ها هم استفاده بهتر CPU و سریع تر بودن اجرای برنامه است که دونه دونه بررسیشون می‌کنیم.۱. اندازه‌ی هر struct حداقل ۱ بایت خواهد بود.خب این که تقریبا واضحه. کامپایلر کمتر از ۱ بایت نمی‌تونه در اختیار بگیره.۲. اندازه‌ی هر struct مضرب بزرگ‌ترین فیلد آن است.توی مثال خودمون، بزرگترین فیلد B یا D هستن که هر کدوم ۸ بایت حافظه اشغال می‌کنن. یعنی اندازه‌ی Foo باید مضرب ۸ باشه. پس نتیجه می‌گیریم اندازه‌ی Foo باید بزرگ‌تر مساوی ۲۴ بایت باشه گرچه ما فقط ۱۸ بایت نیاز داریم!شاید بگید باشه همون ۲۴ بایت رو بده مشتری شیم! ولی دلایل دیگه‌ای هست که باعث میشه Foo اندازه‌اش ۳۲ بایت باشه!۳. آدرس شروع هر فیلد در حافظه، باید مضرب اندازه‌ی خودش باشه!این یعنی آدرس شروع یک فیلد از نوع int32 که ۴ بایت فضا اشغال می‌کنه باید مضرب ۴ باشه یا یک فیلد float64 باید توی آدرسی با مضرب ۸ قرار داده بشه.این‌ها همه دلایلی دارن که تو این پست بررسی نمی‌کنیم.این مورد چطوری باعث افزایش سایز میشه؟ در ادامه متوجه میشیم.۴. ترتیب قرارگیری فیلد‌ها توی حافظه، دقیقا همون ترتیبی هست که موقع تعریف struct نوشتیم.الان احتمالا بتونید حدس بزنید چرا سایز Foo شده بود ۳۲ بایت.در ادامه رفتار کامپایلر رو شرح می‌دیم.نحوه‌ی رفتار کامپایلر گولنگ برای ساخت آبجکت‌اگر فرض کنیم توی آدرس 0 بخوایم این آبجکت رو بسازیم اینطوری شروع می‌کنیم: ۱. طبق ویژگی شماره ۴، اول باید فیلد A که از نوع bool هست و ۱ بایت جا میگیره رو رزرو کنیم. از اونجایی که 0 مضرب ۱ هست (ویژگی شماره‌ی ۳) خونه‌ی 0 رو برای این فیلد در نظر میگیریم.۲. فیلد بعدی B هست که ۸ بایت جا می‌گیره. با توجه به ویژگی شماره‌ی ۳، باید آدرس شروع این فیلد مضرب ۸ باشه. پس محل این فیلد تو حافظه میشه خونه‌ی ۸ تا ۱۵. پس خونه‌ی ۱ تا ۷ چی؟ مجبوریم خالی بذاریمش! کامپایلر این محل‌های را اصطلاحا به عنوان padding خالی می‌ذاره. حالا می‌فهمیم کجا مموری دزدیده میشه!۳. فیلد C هم که ۱ بایت جا میگیره با توجه به ویژگی ها می‌ذاریمش تو خونه‌ی ۱۶.۴. برای D هم دقیقا همون اتفاقی که برای B افتاد میفته و مجبوریم بذاریمش خونه‌ی ۲۴ تا ۳۱توی تصویر زیر شکل آبجکت داخل حافظه رو می‌تونید ببینید:نحوه‌ی قرارگیری آبجکت از نوع Foo در حافظهحالا چیکار کنیم؟شاید این سوال به ذهنتون برسه که «چه کاری میشه کرد که برای مصرف کمتر حافظه؟»از ۴ تا ویژگی‌ کامپایلر گولنگ که بالاتر اشاره کردیم، فقط یک موردش رو می‌تونیم کنترل کنیم. اونم ترتیب فیلد هاست!از لحاظ ریاضیاتی ثابت میشه که اگر فیلد‌ها را به ترتیب از بزرگترین به کوچک ترین موقع تعریف struct بنویسیم، کمترین padding ممکن توسط کامپایلر قرار داده میشه.با این حال با اجرای کد زیر که نکته‌ی بالا رو رعایت کردیم عدد ۲۴ چاپ میشه.package main

import (
   &amp;quotfmt&amp;quot
   &amp;quotunsafe&amp;quot
)

type Foo struct {
   //Changed the order of fields
   B int64
   D float64
   A bool
   C bool
}

func main() {
   f :=  Foo{}
   fmt.Println(unsafe.Sizeof(f)) // prints 24
}تصویر زیر هم نحوه‌ی قرارگیری آبجکت جدید در حافظه رو نشون می‌ده:ابزار‌های کمکیلینتر‌هایی وجود دارن که struct‌ های قابل تغییر رو تشخیص می‌دن و به شما اطلاع می‌دن که بهبودشون بدید. لینک دو تا لینتر رو برای این مورد قرار می‌دم.https://gitlab.com/opennota/checkhttps://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignmentنظر شخصیشخصا معتقدم که همیشه نیاز نیست این کار رو انجام بدیم و حواسمون به ترتیب فیلد‌های struct هامون باشه. از نظر من خوانایی کد در وهله اول خیلی مهم تره. در شرایط مختلف ممکنه ترتیب خاصی از فیلد‌ها خوانایی بیشتری داشته باشه. تنهای زمانی می‌ارزه فیلد‌ها را بر اساس اندازه مرتب کنیم که مموری خیلی خیلی برامون مهم باشه.منابعhttps://golang.org/ref/spec#Size_and_alignment_guaranteeshttps://go101.org/article/memory-layout.htmlhttps://dave.cheney.net/2015/10/09/padding-is-hard</description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Thu, 02 Sep 2021 18:34:17 +0430</pubDate>
            </item>
                    <item>
                <title>مشکلات کار با http.ResponseWriter توی گولنگ!</title>
                <link>https://virgool.io/golangpub/golang-http-response-writer-zgzxoz7nzvu7</link>
                <description>توی پست قبل که از اینجا می‌تونید مشاهده‌اش کنید، راجع به پکیج net/http نوشتم. توی این پست می‌خوام راجع به اینترفیس http.ResponseWriter بنویسم و بعضی از مشکلاتی که موقع کار کردن باهاشون ممکنه بخوریم رو توضیح بدم.مقدمهخب همونطور که تو پست قبل هم نوشتم، http.ResponseWriter صرفا یه Interface توی پکیج net/http با تعریف زیره:net/http/server.go
type ResponseWriter interface {
   Header() Header
   Write([]byte) (int, error)
   WriteHeader(statusCode int)
}از این اینترفیس، که یکی از ورودی‌های تابع‌های Handler محسوب میشه، برای تولید HTTP Response استفاده میشه.پیاده‌سازی این اینترفیس تو تایپ http.response انجام شده. پیاده‌سازی این تایپ رو میتونید از اینجا ببینید. البته این تایپ، تنها پیاده‌سازی از اینترفیس نیست. نمونه‌ی دیگه‌ی پیاده‌سازی این اینترفیس توی پکیج net/http/httptest به اسم ResponseRecorder هست که برای تست نوشتن استفاده میشه.نحوه‌ی کارکرد http.response و مشکلات معمولاین تایپ ظاهرا کار پیچیده‌ای نباید بکنه. سه تا متد برای کار با هدر‌ها، ست کردن status code و نوشتن body داره دیگه. حداقل از دور که اصلا پیچیده به نظر نمی‌رسه :)ولی قضیه خیلی پیچیده تر از این حرفاست! با یه نگاه به پیاده‌سازی این تایپ متوجه این موضوع خواهید شد. (توضیح پیچیدگی این پیاده‌سازی تو چارچوب این نوشته نیست. ایشالا یه نوشته‌ی دیگه!) این پیچدگی باعث میشه که اگه بعضی از جزئیات پیاده‌سازی این تایپ رو ندونیم با ارورهای عجیب و غریبی رو به رو بشیم. چند تا از ارورهای معمول رو در ادامه توضیح میدم.ترتیب فراخوانی متد ها مهمهاول از همه باید گفت که وقتی داریم یه Abstraction ایجاد می‌کنیم، باید حواسمون باشه که حتی الامکان Idempotent و Stateless باشه. این باعث میشه ترتیب فراخوانی متد‌های یک تایپ توی یک اسکوپ آنچنان مهم نباشه و فراخوانی یه متد به فراخوانی یه متد دیگه وابسته نباشه.ولی متاسفانه پیاده‌سازی http.response این شکلی نیست. این یکی از اشکال‌هایی هست که می‌تونیم به این قسمت از پیاده‌سازی استاندارد لایبرری گولنگ وارد کنیم (چه غلطا! حتما یه دلیلی داشته که اینجوری پیاده سازی کردن :) ولی من دلیلش رو نمی‌دونم. اگه شما می‌دونید کامنتش کنید. )ترتیب درست به این شکل هست که شما اول باید Header هایی که می‌خواید رو با استفاده از متد ()Header به Response اضافه کنید و یا حتی در شرایطی بعضی از هدر‌ها رو حذف کنید.بعد با متد WriteHeader استاتوس کد پاسخ درخواست رو مشخص کنید. بعد از همه‌ی این کارا تازه می‌تونید body رو با استفاده از متد Write پر کنید.هر ترتیبی غیر از ترتیب بالا باعث ایجاد مشکلاتی خواهد شد. البته دلیل پیاده‌سازی به این نحو تا حدودی مشخصه. با توجه به اینکه پروتکل HTTP یک پروتکل text-based هست و دقیقا به همین ترتیب یک Response باید ارسال بشه. اول Status Code و Header ها و بعد Body.به خاطر همینه که انتظار میره اول هدر ها رو مشخص کنیم. و بعد با مشخص کردن Status Code قسمت اول پیام مربوط به  Response رو بسازیم و بعد Body رو بنویسیم. تو عکس زیر جزئیات یک Response نمونه رو می‌تونید مشاهده کنید.نمونه‌ی یک HTTP Responseحالا با توجه به توضیحاتی که دادیم، اگه متد‌ها رو جا به جا فراخوانی کنیم به مشکل می‌خوریم. مسئله یه خورده پیچیده تر میشه چون وقتی این اشتباه رو بکنیم، با Compile Error یا حتی Runtime Error مواجه نمی‌شیم و اپلیکیشن به کار کردن ادامه میده. این مسئله پیدا کردن مشکل رو بعضی وقتا سخت‌تر می‌کنه.مثلا تو قطعه کد زیر:package main

import &amp;quotnet/http&amp;quot

func main() {
   fn := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
      writer.Write([]byte(&amp;quotHello World!&amp;quot))
      writer.WriteHeader(http.StatusBadRequest)

   })

   http.ListenAndServe(&amp;quot:9090&amp;quot, fn)
}بعد از هر بار ارسال درخواست به سرور، اپلیکیشنتون یه همچین چیزی لاگ می‌کنه.http: superfluous response.WriteHeader call from main.main.func1 (main.go:8)و Status Code ای که ست میشه روی Response کد ۲۰۰ خواهد بود. چون وقتی متد Write رو بدون این که قبلش WriteHeader رو فراخوانی کنیم، فرض میکنه باید کد ۲۰۰ رو ست کنه. حالا هر چه قدر بعد از  Write بیایم WriteHeader رو فراخوانی کنیم یه چیزیه مثل نوش‌دارو بعد مرگ آقا سهراب.آب رفته به جوی بر نمی‌گردد!بعضی وقتا لازمه یه کاری رو بکنیم ولی بعدا پشیمون بشیم! مثلا یه هدر رو «بنویسیم» ولی بعد منصرف بشیم یا بخوایم مقدارش رو عوض کنیم. یا این حالت که بعد از اینکه Body رو نوشتیم، بخوایم یه خورده تغییرش بدیم. مثلا یه فیلد JSON ناقابل به Body اضافه کنیم.متاسفانه باید بگم که تو گولنگ همیشه نمیشه این کار رو کرد!شما وقتی متد WriteHeader رو فراخوانی می‌کنید دیگه نمیشه تغییری تو بخش هدر داد. به خاطر اینکه بعد از فراخوانی این متد State تایپ http.response رو جوری تغییر میده (در واقع میاد فلگ wroteHeader رو ست می‌کنه) که فراخوانی‌های بعدی اثری نداشته باشه.متد Write هم یه همچین حالتی داره. Body توی این تایپ به حالت یک بافر پیاده‌سازی شده که هر چند بار این متد رو فراخوانی کنیم، ورودیش به ‌بدنه‌ی پیام Append میشه. برای مثال تو قطعه کد زیر Response دریافتی شما Hello WorldHello Donya خواهد بود.package main

import &amp;quotnet/http&amp;quot

func main() {
   fn := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
      writer.Write([]byte(&amp;quotHello World&amp;quot))
      writer.Write([]byte(&amp;quotHello Donya&amp;quot))
   })

   http.ListenAndServe(&amp;quot:9090&amp;quot, fn)
}

// Response Of Requests: Hello WorldHello Donyaخب ممکنه بگید «چه کاریه؟ همیشه به ترتیب و فقط یک بار متد‌ها رو فراخوانی می‌کنیم. اینطوری مشکل حل میشه.» توی اکثر سناریو ها بله. مشکل اینطوری حل میشه. ولی بعضی از سناریو‌ها هستن که نمیشه کاریش کرد. مثلا شما نمی‌تونید یه Middleware (Middleware اصطلاحا به تابعی گفته میشه که یه Handler ورودی می‌گیره و یه Handler دیگه خروجی می‌ده) داشته باشید که بعد از انجام کار Handler اصلی یه فیلد JSON به Response اضافه بکنه!قبلا چی نوشته بودم!؟توی بعضی از حالت‌ها ممکنه شما به یه Middleware نیاز داشته باشید که ببینه قبلا شما چه چیزی رو به عنوان Body نوشتید. مثلا یه Middleware رو در نظر بگیرید که می‌خواد یه فیلد خاص از Body رو ببینه و به عنوان یه Prometheus Metric اون رو Expose بکنه. با این Abstraction موجود چنین کاری نمی‌تونیم بکنیم. چون دسترسی به محتوای نوشته شده توسط متد Write رو نداریم.راه حل چیه؟خب این همه مشکل گفتیم. نمیشه که راه حل نداشته باشه. نمیشه که همیشه غر زد.بیایم به این سوال جواب بدیم که «چیکار کنیم؟»یه راه معمول اینه که یه جوری Signature تابع‌های Handler رو عوض کنیم. و به جای ورودی‌های قبلی یه تایپ ورودی جدید تعیین کنیم و اون رو به Handler پاس بدیم. فریمورک‌هایی مثل Gin و Echo همچین کاری کردن و اسم اون ورودی جدید رو گذاشتن Context. البته این اسم به نظرم زیاد خوب نیست چون با context خود گولنگ قاطی میشه. (شاید اسم بهتر نبوده. چی بگم والا!)با این ترفند هر کاری با Response می‌خوایم بکنیم رو تو یه داده‌ساختار میانی (که اینجا اسمش شده Context) انجام می‌دیم. بعد که کارمون تموم شد متد‌های ResponseWriter گولنگ رو با ترتیب و مقادیر درست فراخوانی می‌کنیم. (البته این کار به این سادگی که تو متن میگم نیست و توضیحش توی این مثال نمی‌گنجه. ولی گرا رو دادم بهتون دیگه :) می‌تونید برید پیاده‌سازی اون دو تا فریمورک رو ببینید و ایده بگیرید.)خب پس مشکل اینطوری حل میشه. پس چرا همیشه اینطوری کد نزنیم؟ واقعیتش اینه که برای انجام همچین کاری، میزان پیاده‌سازی شما بیشتر خواهد شد و همچنین باید به هندل کردن کلی Edge case که مربوط به اپلیکیشن شما نیست و مربوط به پروتکل HTTP هست فکر کنید.اون دو تا فریمورک هم که بالا گفتم با توجه به نیاز خودشون یه Context ساختن که با در نظر گرفتن میزان پیچیدگی نیاز شما ممکنه اونا هم جوابگوی شما نباشن.از طرف دیگه کلی ابزار ساخته شده که با لایبرری استاندارد گولنگ سازگار ان و اگه شما از چنین راه حلی استفاده کنید ممکنه مزیت‌های استفاده از ابزار‌های آماده رو هم از دست بدید!نتیجه چی شد بالاخره؟نتایج متفاوتی از این متن میشه گرفت ولی نظر خودم اینه:تا جایی که میشه و نیاز هامون اجازه می‌ده می‌تونیم از استاندارد لایبرری گولنگ استفاده کنیم.اگه نشد فریمورک‌هایی مثل Gin و Echo می‌تونن جوابگوی نیاز‌های ما باشن.اگه نه هم که خودمون بنویسیم دیگه :)ممنون که وقت گذاشتید و تا اینجا خوندید. حتما نظراتتون رو برام بنویسید تا به بهتر شدن نوشته‌های بعدی کمک بکنه. </description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Sat, 03 Apr 2021 10:43:45 +0430</pubDate>
            </item>
                    <item>
                <title>یکم عمیق‌تر در مورد net/http</title>
                <link>https://virgool.io/golangpub/%DB%8C%DA%A9%D9%85-%D8%B9%D9%85%DB%8C%D9%82%D8%AA%D8%B1-%D8%AF%D8%B1-%D9%85%D9%88%D8%B1%D8%AF-nethttp-uizeurqnklfq</link>
                <description>یکم مقدمهزبون برنامه‌نویسی Go (یا Golang) از وقتی اومده، خیلی جاها استفاده شده و الحق و الانصاف کد زدن باهاش خیلی حال میده (این تجربه‌ی منه،‌ امیدوارم تجربه‌ی شما هم همین باشه :) )با این زبون کارای زیادی میشه کرد که یکی از اونا نوشتن وب‌سرویس‌ عه. وقتی بحث وب‌سرویس هم که میاد وسط پروتکل HTTP یه خورده مهم میشه. یکی از خوبی‌های گولنگ اینه که بدون نیاز به کتابخونه‌های خارجی، از این پروتکل واسه نوشتن وب‌سرویس پشتیبانی می‌کنه.تو این پست قراره یه شیرجه‌ی نسبتا عمیق بزنیم تو پکیج net/http. هیچی با net/httpخب اگه بخوایم یه وب سرویس بنویسیم که هیچ کاری نکنه (آره، دقیقا هیچ کاری نکنه،‌ فقط request های http رو بگیره و بریزه دور) کار خیلی ساده‌ای داریم. با تیکه کد زیر می‌تونیم همچین کدی بزنیم:package main

import &amp;quotnet/http&amp;quot

func main() {
   http.ListenAndServe(&amp;quot:8080&amp;quot, nil)
}این تیکه کد یه سرور بالا میاره و رو پورت ۸۰۸۰ گوش میده که request‌ های http رو بگیره. ولی هنوز مشخص نکردیم که با اونا چیکار کنه. برای اینکه یه کاری بکنیم رو request ها باید یه http.Handler بهش پاس بدیم (به جای همون nil توی کد بالا)این http.Handler چیزی نیست جز یه interface کوچولو. کدش هم ایناها:type Handler interface {
   ServeHTTP(ResponseWriter, *Request)
}سلام دنیا با net/httpبرای یه سلام‌دنیای ساده، کافیه یه type تعریف کنیم (یا از یه جای دیگه گیر بیاریم) که این Interface رو implement بکنه. اون وقت یه Handler داریم. مثلا این شکلی:package main

import &amp;quotnet/http&amp;quot

type myHandler struct {}

func (m *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   w.Write([]byte(&amp;quothello world!\n&amp;quot))
}

func main() {
   http.ListenAndServe(&amp;quot:8080&amp;quot, &amp;myHandler{})
}
الان رو پورت ۸۰۸۰ یه وب‌سرویس داریم که فقط سلام میده.خب تا اینجا که چیز خاصی نگفتیم. بریم که دیگه عمیق تر بشیم.تابع ListenAndServe دقیقا چیکار می‌کنه؟اگه پیاده‌سازی این تابع رو نگاه کنید، می‌بینید که یه Instance از http.Server می‌سازه و ListenAndServe اون رو صدا می‌زنه.func ListenAndServe(addr string, handler Handler) error {
   server := &amp;Server{Addr: addr, Handler: handler}
   return server.ListenAndServe()
}خب که الان چی؟ باید بفهمیم متود ListenAndServe روی http.Server چیکار می‌کنه. خب باز بریم پیاده‌سازیش رو ببینم. ایناها (حجم کد‌هایی که دارم میارم اینجا داره زیاد میشه. یه خورده باید کمترشون کنم!):func (srv *Server) ListenAndServe() error {
   if srv.shuttingDown() {
      return ErrServerClosed
   }
   addr := srv.Addr
   if addr == &amp;quot&amp;quot {
      addr = &amp;quot:http&amp;quot
   }
   ln, err := net.Listen(&amp;quottcp&amp;quot, addr)
   if err != nil {
      return err
   }
   return srv.Serve(ln)
}اگه از چک کردن در حال خاموش شدن سرور و تعریف آدرس که بگذریم، این کد داره یه http.Listener از پکیج net میگیره و اون رو پاس میده به متود Serve. تو این پست قصد ندارم راجع به پکیج net و موجودیت‌های، مثل Listener، توضیح بدم. همین که بدونیم واسه قبول کردن کانکشن tcp به درد میخوره کافیه. بازم بریم توی Serve؟ باشه بریم ببینیم چه خبره. کدشو میارم ولی با یکم خلاصه سازی :). اونجا‌های که سه نقطه گذاشتم کد اصلی رو پاک کردم. اگه فرصت شد بعدا توضیح می‌دم چیکار می‌کنن.func (srv *Server) Serve(l net.Listener) error {
   ...
   // some initialization shit
   ...
   for {
      rw, err := l.Accept()
      if err != nil {
         ...
         // handling possible shitty errors
         ...
      }
      ...
     //blah blah blah
      go c.serve(connCtx)
   }
}همونطور که می‌بینید بعد از یه سری Initialization ها مثل آماده کردن context و ... ، یه حلقه‌ی بی‌نهایت داریم (اگه ارور نخوریم البته) که منتظره یکی ریکوئست بزنه و accept اش بکنه. این کار رو که کرد یه کانکشن میگیره و روی اون کانکشن serve اش رو با یه context صدا می‌زنه. البته که یه دونه go انداخته پشتش که توی یه goroutine جدا اجرا بشه.خب همینجا میخوام یه نتیجه کوچولو بگیرم. بر خلاف زبون هایی مثل پایتون که برای اجرای یه سرور باید با چیزی مثل uwsgi یا gunicorn چند تا process از کد ران بکنیم تا درخواست‌های همزمان رو جواب بده، گولنگ خودش بلده request های همزمان رو هندل کنه (حالا جو میدم. صرفا یه goroutine درست می‌کنه)فقط فعلا نپرسید اون serve که با s کوچیک نوشته شده چیکار می‌کنه. شاید بعدا اومدم تو یه پست دیگه توضیح دادم. (البته احتمال اینکه خودتون زودتر برید و بخونیدش بیشتره!)خب تا اینجا با نحوه‌ی Serve شدن Handler نسبتا آشنا شدیم. یکم هم راجع به ResponseWrite توضیح میدم و بقیه‌اش بمونه برای بعد.خب حالا http.ResponseWriter چیه؟اینم چیزی نیست جز یه Interface ساده. کدش رو هم الان میارم. بفرماید:net/http/header.go
type Header map[string][]string

net/http/server.go
type ResponseWriter interface {
   Header() Header
   Write([]byte) (int, error)
   WriteHeader(statusCode int)
}
سه تا تابع ساده داره. Header و Write و WriteHeaderاولی Header هایی که قراره به کاربر فرستاده بشه رو برمی‌گردونه. یه سری تابع هم داره که بشه باهاش header اضافه یا کم کرد.با Write باید body جواب رو پر بکنیم. میتونه یه html باشه. میتونه یه json باشه یا هر چیز دیگه (البته واسه اینا ابزار‌های بهتر و راحت تری هست)با WriteHeader هم همه‌ی Header ها و به اضافه‌ی status code رو می‌فرسته که بره بشینه تو header جواب کاربر. البته اگه کالش نکنیم هم با کد ۲۰۰ خودش کال می‌کنه!مگه ما نگفتیم این یه Interface عه؟ خب پیاده‌سازیش کجاست؟نسخه‌ی پیاده‌سازی شده‌اش که پکیج net/http استفاده می‌کنه، تایپ http.response عه. یه تایپ متاسفانه اکسپورت نشده (البته احتمالا دلیل داشتن برای این کار ولی بعضی وقتا اذیت می‌کنه) که باعث میشه وقتی داریم Handler می‌نویسیم هیچ دسترسی سطح پایین به HTTP Response ای که می‌خوایم به کاربر برگردونیم نداشته باشیم.ماهیت این پیاده سازی هم با توجه به استفاده از buffer تو پیاده‌سازیش،‌ باعث میشه اگه فقط یه بار WriteHeader کال بشه، دیگه نتونیم تو هیچ چیز از Response دست ببریم. این ماهیت باعث میشه تو نوشتن بعضی از Middleware ها اذیت بشیم.خب به نظرم تا همین‌جا فعلا بسه. تو پست بعدیم می‌خوام راجع به مشکلاتی که ممکنه با ResponseWrite بخوریم و اینکه چطوری حلشون کنیم صحبت بکنم. این پست رو می‌تونید از اینجا مشاهده کنید.پ.ن: لطفا نظراتتون رو بنویسید. خیلی مهمه و کمک زیادی می‌کنه به بالا رفتن کیفیت تو ادامه‌ی کار. </description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Mon, 07 Sep 2020 01:13:31 +0430</pubDate>
            </item>
                    <item>
                <title>چهار سال عذاب و عمری که باز نمی‌گردد</title>
                <link>https://virgool.io/@milad_ibra/%DA%86%D9%87%D8%A7%D8%B1-%D8%B3%D8%A7%D9%84-%D8%B9%D8%B0%D8%A7%D8%A8-%D9%88-%D8%B9%D9%85%D8%B1%DB%8C-%DA%A9%D9%87-%D8%A8%D8%A7%D8%B2-%D9%86%D9%85%DB%8C%DA%AF%D8%B1%D8%AF%D8%AF-qqpljusberqf</link>
                <description>مقدمهدر این پست، قصد دارم داستان ۴ سال اخیر خود، که اکثرا مربوط به دانشگاه است، را روایت کنم. با توجه به این‌ که قسمت زیادی از این متن مربوط به مسائل شخصی من است، شاید برای‌تان کمی خسته کننده باشد که پیشاپیش پوزش می‌طلبم.کنکور۲۴ تیر ۱۳۹۵، تاریخی که حکم مبدا تاریخ را برای من داشت. یه یاد می‌آورم وقتی که از حوزه‌ی امتحانی به خانه بازگشتم،‌ برای اولین بار احساس پوچی کردم. گویا بیش‌ از حد به این غول بی شاخ و دم توجه کرده بودم.  به یکی از دوستانم زنگ زدم و پرسیدم : «الان که کنکور تموم شده چیکار می‌کنی؟»پس از اینکه نتایج نهایی اعلام شد و متوجه شدم که محل تحصیلم دانشگاه علم و صنعت ایران است، بسیار ناراحت بودم. نه برای این‌که علم و صنعت دانشگاه بدی باشد. از خودم انتظار بیشتری داشتم. به هر حال با این مسئله کنار آمدم (چون مجبور بودم) و سعی کردم انرژی‌ام را صرف ساخت آینده‌ام (!) کنم.لجن‌گاه‌ علم و صنعتخصوصیات فردی در ابتدای دوران دانشگاهدر ابتدای ورود به دانشگاه،‌ خود را فردی علم‌دوست می‌دانستم و مانند برخی از افراد برای یافتن شغل و کسب مدرک به دانشگاه نیامده بودم. روحم تشنه‌ی یادگیری بود. فقط و فقط به دنبال این بودم که مطالب جدید یاد بگیرم. چه دروس مربوط به رشته‌ام و چه دروس غیر مرتبط. از طرفی من اصطلاحا دیرجوش بودم (احتمالا هنوز هم هستم) و این باعث شده بود که به فردی اجتماع گریز شناخته شوم. البته این حدس من است و لزوما درست نیست! ولی با این حال خیلی اهل شوخی بودم. کم پیش می‌آمد از شوخی کسی ناراحت شوم.از نظر اعتقادی نیز نیم‌چه اعتقاداتی به مسائل مذهبی داشتم ولی آن‌چنان بر اجرای آنان اصرار نداشتم.اتفاقات ناگوار سال اولخوشبختانه مشکلی با دوری از خانواده نداشتم و مانند برخی از اطرافیان زیاد دلتنگ نمی‌شدم.اولین اتفاق بدی که مکررا برای من رخ می‌داد، تمسخر برخی از اطرافیان به خاطر لهجه‌ی زبان مادری‌ام یعنی ترکی بود. نمی‌دانم از قصد بود یا نه ولی این مسئله عمیقا مرا ناراحت می‌کرد. ولی این ناراحتی باعث نشد از کسی کینه به دل بگیرم. ناراحتی خود را طبق معمول پنهان کردم و با آن سوختم و ساختم.کنایه‌های برخی از اطرافیان به خاطر مطالعه‌ی زیاد و گفتن الفاظی که همه‌مان می‌دانیم نیز کمی مرا سست می‌کرد. البته این کنایه‌ها، به زعم من، از روی دل‌سوزی نبود. که کاش از روی دل‌سوزی بود. به یاد می‌آوردم که تا اواسط ترم دوم تنها ۴ ساعت خواب مفید در روز داشتم. از طرفی عادت به مطالعه کتاب نداشتم و تمام مطالعاتم را بر روی موبایلم انجام می‌دادم. این تصمیم باعث شد که در عرض یک سال، برای زندگی روزمره مجبور به استفاده از عینک شوم.داستان عشق و عاشقی کورکورانه هم که شده بود قوز بالا قوز. اکنون در حال نوشتن این متن هستم، خوشحالم که نافرجام بود.در این مدت که از زنجان به تهران آمده بودم هر از چند گاهی به دیدم اقوام می‌رفتم. بیش‌تر از همه به خانه‌ی  یکی از دایی‌هایم می‌رفتم. اندک تفریحی که داشتم با پسر‌دایی‌ام بود. ولی با توجه به اینکه سرباز بود و یک روز در میان به پادگان می‌رفت،‌ فرصت کمی برای وقت گذراندن با او داشتم. امید به اینکه ۱۵ اسفند سربازی‌اش تمام می‌شود و بیش‌تر می‌توانیم وقت بگذرانیم مرا خوش‌حال می‌کرد. جدای از تفریح با توجه به اینکه ۴ سال از من بزرگ‌تر بود حکم برادر بزرگتر نداشته ام را داشت. هنوز برخی از توصیه‌هایش آویزه گوشم است.روز سه‌شنبه،‌۳ اسفند ۱۳۹۵ یک پیام به او فرستادم و قرار شد پنج‌شنبه طبق دفعات قبل به گذران وقت بپردازیم!اما امان از چهارشنبه، نباید صبح چهارشنبه خبر فوت پسر‌دایی‌ام را می‌شنیدم. هنوز برای از دست دادن برادر بزرگ‌ترم آماده نبودم. ضربه‌ی سنگین این اتفاق مرا هر روز به افسردگی نزدیک و نزدیک تر می‌کرد.دو سال میانیبرای فرار از افسردگی دست به کار‌های زیادی زدم که اغلب کورکورانه و بی منطق بود. سعی کردم آنقدر خودم را مشغول درس و دانشگاه کنم، تا فراموشی از راه برسد و مرا کمک کند. پرداختن به کار مورد علاقه‌ام، کمی انرژی تزریق می‌کرد اما خبر از آسیب‌هایی که به من می‌رسید نداشتم. کمردرد من در این دوره عود کرد و در آخر شد آنچه شد!مشغولیت بالا باعث شده بود که به نیاز‌های دیگرم توجه نکنم. در استراحت کردن، تفریح‌کردن و حتی تفکر در مورد مسائل مهم کم‌کاری کرده بودم.ایجاد دوستی با افراد غلط نیز جز اشتباهاتم در این دوره بود. ایجاد دغدغه‌های فکری واهی و بیهوده نه‌تنها به بهبود من کمک نکرد که به تخریب هر چه بیش‌تر حال روحی من کمک می‌کرد.با این حال وقت‌های اضافی‌ام را صرف گفت‌و‌گو با اطرافیانم می‌کردم. دوست داشتم همیشه در محل‌های شلوغ باشم و با افراد مختلف حرف بزنم. ولی در مواقع تنهایی اتفاقات خوبی برای نمی‌افتاد و فکر و خیال‌های گاه و بی‌گاه باعث رنجش خاطرم می‌شدند.دانشگاه یا دستگاه تخریب‌گر زندگی؟جرقه‌های نفرت از دانشگاه از این دوره شروع شد. تصمیمات سلیقه‌ای مسئولین، محیط سمی،‌ اساتید بی کفایت، ناعدالتی و ... باعث می‌شد از دانشگاه دل‌سرد شوم. به طوری که در پایان این دو سال تنها حسی که به دانشگاه (بخوانید لجن‌گاه) داشتم نفرت بود.من که فقط و فقط به خاطر کسب علم و فعالیت در محیط آکادمیک به دانشگاه آمده بودم و قصد ادامه‌ی آن تا آخر عمر خود را داشتم، الآن از لحظه لحظه‌ی حضورم در دانشگاه عذاب می‌کشم. اتفاقات مختلف باعث شد قید ادامه تحصیل حتی در دانشگاه‌های خارج از کشور را، با وجود اینکه شرایطش را داشتم، بزنم.خلاصه که هر چه از این لجن‌گاه بگویم کم گفته‌ام. فقط دوستانی اندکی که (می‌توانست تعدادش خیلی بیشتر باشد) یافتم و اندک دانشی که کسب کردم برایم باقی ماند. در عوض اقدامات سیستماتیک زیادی در جهت تخریب زندگی من و امثال من توسط دانشگاه انجام شد.پیش‌به سوی محل کار!پس از اینکه دور محیط آکادمیک را به خودکاری به شدت قرمز خط کشیدم، به سعی برای یافتن شغل مرتبط با رشته‌ی تحصیلی‌ام پرداختم. این سعی از دی‌ماه ۹۶ تا تیر ماه ۹۷ ادامه داشت. طولانی شدن این موضوع از نظر روانی بسیار برایم آزار دهنده بود. آن ۶ ماه باعث شده بود که حتی خنده‌ی ظاهری همیشگی‌ام از بین برود. بسیار تندخو شده بودم و اطرافیانم را بار‌ها ناراحت کردم. یکی از خطوط قرمز اخلاقی من ناراحت کردن دیگران بود که شکسته شد و هنوز کاملا ترمیم نشده است! عذاب وجدان هم خوره شده بود به جانم. چرا اطرافیانم باید به خاطر اخلاق بد من ناراحت شوند؟سال آخردر محل کار بسیاری از دغدغه‌های پیشینم را فراموش می‌کردم و این مرا امیدوار می‌کرد ولی پس از مدتی متوجه شدم که به فردی Workaholic تبدیل شده‌ام. ولی همچنان راضی بودم چون با وجود همکارانم و گذران وقت،‌ دغدغه‌ی دیگری نداشتم و زندگی برایم شیرین شده بود. در کنار کار،‌ سعی داشتم که دروس دانشگاه را در حد متعادل ترم‌های گذشته دنبال کنم و تا جای ممکن از آن‌ها بهره برداری کنم.احساس می‌کردم که زندگی‌ام روی غلتک افتاده است ولی اشتباه می‌کردم.اوضاع سیاسی جامعه و اتفاقات خواب‌گاهاواخر مهر و اوایل آبان‌ ۹۸ اتفاقات عجیبی در خوابگاه رخ می‌داد. عجیب‌ترین آن‌ها آتش‌سوزی عمدی سطل آشغال‌ها در آشپزخانه‌ی مجاور اتاق ما بود. اوایل شبیه یک شوخی مسخره بود. ولی بعد از بریدن شلنگ گاز، اوضاع کمی ترسناک تر شد. حدود ۲۰ روز مداوم و پر استرس از اتفاقات مترقبه احتمالی آسایش را از همه سلب کرده بود.اتفاقات خوابگاه همزمان شده بود با اوضاع نامناسب کشور در آبان‌ماه. درست زمانی که کم کم احساس خوبی نسبت به زندگی پیدا می کردم، زمین و زمان دست به دست هم دادند تا غم و اندوه سراسر وجودم را فرا گیرد.سال ۲۰۲۰ هم که معروف به اتفاقات بد است. هواپیما‌ی اوکراینی از همه‌ی آن‌ها بد تر. احساس هم‌ذات‌پنداری با مسافرین هواپیما زندگی را برایم تلخ کرده بود.زندگی ادامه داشت تا اینکه:کویید ۱۹ (!)هنوز به این ویروس مبتلا نشده‌ام ولی، به زعم خودم، صدمات جبران ناپذیری به من زده است. کرونا باعث شد که برای حفظ سلامت دورکاری کنیم. این برای من که محل کارم، به غیر از محل کسب درآمد، تنها محل کسب انرژی محسوب می‌شد اتفاق خوبی نبود. هنوز با اکثر افراد محل کارم حتی آشنا هم نشده بودم که این اتفاق افتاد.در این مدت دورکاری، تمام دغدغه‌ها و فکر و خیال‌ها برگشتند و هم‌سفره من شدند. مجازی شدن کلاس‌های دانشگاه و فشار بیش‌از حد وارده برای تنها ۱۲ واحد که تا روز ۱۱ مرداد ۹۹ ادامه داشت، شرایط را بدتر می‌کرد.از طرفی تلاش برای یافتن محل مناسب برای اجاره، که از اوایل تیر ۹۹ شروع شد و تا پایان ماه ادامه داشت، و همزمانی آن به افزایش شدید قیمت دلار و به تبع آن مسکن شرایط را بسیار دشوار می‌کرد.خصوصیات فردی در انتهای دوران دانشگاهپس از گذران ۴ سال، اصلا به محیط آکادمیک علاقه ندارم که مقصر این موضوع را سیستم فشل و ناکارآمد آموزش عالی در ایران می‌دانم.از لحاظ اعتقادات مذهبی، فردی پر از شک و ابهام بنیادی شده ام و ۱۸۰ درجه تغییر کرده‌ام.هنوز تظاهر می‌کنم که از شوخی دیگران ناراحت نمی‌شوم گرچه زود‌رنج شده ام گاهی اوقات عمیقا ناراحت می‌شوم.هدف‌های خود را از دست داده‌ام و نمی‌توانم برای خود هدف جدید انتخاب کنم چون تقریبا همه‌چیز برایم بی اهمیت شده است.و هم اکنون که اجباری چون دانشگاه برایم وجود ندارد، احساس پوچی مطلق می‌کنم. ساده بگویم، روح من در حال پیر شدن است و با مرگ دست و پنجه نرم می‌کند ...</description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Fri, 07 Aug 2020 00:36:37 +0430</pubDate>
            </item>
                    <item>
                <title>گیرنده‌ی دیجیتال روی لینوکس!</title>
                <link>https://virgool.io/@milad_ibra/%DA%AF%DB%8C%D8%B1%D9%86%D8%AF%D9%87%DB%8C-%D8%AF%DB%8C%D8%AC%DB%8C%D8%AA%D8%A7%D9%84-%D8%B1%D9%88%DB%8C-%D9%84%DB%8C%D9%86%D9%88%DA%A9%D8%B3-fy6pwwsdkhhv</link>
                <description>چند سال پیش وقتی داشتم وسایلم رو جمع می‌کردم که به خوابگاه نقل مکان کنم، مطلع بودم که هر اتاق تو خوابگاه تلوزیون نداره! به خاطر همین یه گیرنده‌ی دیجیتال X-Vision از مدل PCDVB-4100 از پسرخاله‌ام قرض کردم. البته استفاده‌ای نداشت،‌مگرنه شاید نمیتونستم قرض بگیرم :) (مدل رو نوشتم تا اگه این روش کار نکرد، تقصیر رو بندازم گردن مدل گیرنده!)اوایل روی ویندوز از گیرنده استفاده می‌کردم. مشکلات زیادی نداشت (غیر از اینکه روی ویندوز ۱۰ نرم‌افزارش نصب نمی‌شد و ویندوز ۸.۱ می‌خواست!) و راحت ازش استفاده می‌کردم.ولی از وقتی Switch کردم رو لینوکس دیگه فکر استفاده‌اش از سرم پرید. بعد از چند مدت گفتم سرچ کنم شاید راه حلی پیدا شد. یه راه حل تو یه وبلاگ ایرانی پیدا کردم. کار هم کرد ولی از قضا بعد از چند مدت لپ تاپم شکست و تقریبا همه‌ی اطلاعات جزئی من از بین رفت. (این قضیه هم اطلاعات جزئی محسوب می‌شد)رو لپ‌تاپ جدیدم خواستم اون روش رو انجام بدم ولی متاسفانه اون وبلاگ Down شده بود. بعد از کلی جست‌و‌جو تونستم روشش رو دوباره پیدا کنم. تصمیم گرفتم اینجا نحوه‌ی استفاده از گیرنده‌ی دیجیتال روی لینوکس رو توضیح بدم که اگه هر کس نیاز داشت استفاده بکنه و هم من برای همیشه داشته باشمش. گرچه بعد از اینکه این روش جواب داد فقط واسه فوتبال دیدن استفاده کردم ازش و کاربرد چندانی برام نداشت!نصب فریموربرای نصب فریمور مربوط به گرینده باید قدم‌های زیر رو انجام بدید.۱- برای نصب فریمور می‌تونید از این دستور رو در ترمینال اجرا بکنید:wget http://www.ite.com.tw/uploads/firmware/v3.6.0.0/dvb-usb-it9135.zip۲- بعد از دانلود فایل فریمور رو از حالت فشرده خارج کنید.۳- دستورات زیر رو انجام روی فایل فریمور انجام بدید (نمیدونم چیکار می‌کنه ولی اعتماد کنید!)dd if=dvb-usb-it9135.fw ibs=1 skip=64 count=8128 of=dvb-usb-it9135-01.fw
dd if=dvb-usb-it9135.fw ibs=1 skip=12866 count=5817 of=dvb-usb-it9135-02.fw۴- دو تا فایل ایجاد میشه که با دستور زیر به محل مناسب انتقالش بدید:sudo cp dvb-usb-it9135-0* /lib/firmware۵- گیرنده رو از سیستم جدا کنید و دوباره وصل کنید.۶- با دستورات زیر پکیج w_scan رو نصب کنید. (دستور مربوط به توزیع Ubunutu):sudo apt update
sudo apt install w-scan۷- آنتن گیرنده رو وصل کنید.۸- با دستور زیر کانال‌های موجود رو جست‌و‌جو کنید و در یک فایل با فرمت xspf ذخیره کنید. این فرمت با نرم‌افزار ‌های مثل VLC Media Player قابل اجرا هستند.w_scan -c IR -L &gt; channels.xspf۹- حالا می‌تونید به راحتی فایل خروجی رو با نرم‌افزاری مثل VLC Media Player اجرا کنید و برنامه‌های بسیار بسیار مفرح تلویزیون ملی (!) ایران رو نگاه کنید!</description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Sat, 13 Jul 2019 02:29:51 +0430</pubDate>
            </item>
                    <item>
                <title>آشفته بازار کار</title>
                <link>https://virgool.io/@milad_ibra/%D8%A2%D8%B4%D9%81%D8%AA%D9%87-%D8%A8%D8%A7%D8%B2%D8%A7%D8%B1-%DA%A9%D8%A7%D8%B1-abraojsfzqyu</link>
                <description>به عنوان اولین پست‌ام دوست داشتم تجربه‌های خودم در مورد اتفاق‌هایی که از بهمن ۱۳۹۷ تا خرداد ۱۳۹۸ افتاد،‌ منتشر کنم. من تو این مدت دنبال شغل بودم و چندین جا مصاحبه رفتم. تا بالاخره روز ۲۹ خرداد تونستم یه شغل مناسب پیدا کنم.تو این پست قصد دارم تجربه مصاحبه با همه‌ی شرکت‌هایی که رفتم رو منتشر کنم. ولی اسم شرکت‌های که از نظرم خوب نبودن رو نمیارم و فقط دلیل اینکه به نظرم رفتارشون نامناسب بود رو میگم.یکم راجع به خودم!اول یکم راجع به خودم توضیح بدم که ایده‌ای داشته باشید که وضعیت من چطور بود. من تجربه کاری گسترده ندارم ولی چند تا پروژه‌ی فریلنسری انجام داده بودم و یه مدت پارت تایم یه جا کار کرده بودم. دنبال موقعیت Backend Developer ای بودم که با پایتون کد بزنم چون نسبت به بقیه زبان‌های برنامه‌نویسی بهش مسلط ترم (دقت کنید،‌ نگفتم مسلط! گفتم مسلط تر)شروع خونین!اولا که شروع کردم به رزومه فرستادن به شرکت‌های مختلف،‌ فکر میکردم بدون مشکل استخدام میشم و مشکلی پیش نخواهد اومد. ولی با فرستادن اولین رزومه و اولین مصاحبه متوجه مشکل بزرگی شدم. تقریبا همه‌ی شرکت‌ها کسی رو می‌خوان که تمام‌وقت کار بکنه. ولی من به خاطر این‌که هنوز دانش‌جو‌ هستم،‌ مجبور بودم دنبال کار پاره‌وقت بگردم. به خاطر همین از فرستادن رزومه به کلی از شرکت‌ها خودم رو محروم کردم.از این به بعد شروع می‌کنم به نوشتن تجربه مصاحبه با شرکت‌های مختلف. ولی اگر از اون شرکت ناراضی بودم اسمش رو مخفف می‌نویسم. شرکت‌هایی هم که رزومه‌ام رو دیدن ولی خبر ندادن رو هم قاعدتا توضیحی راجع بهشون ندارم.شرکت &lt;گ&gt;توسط یکی از دوستان به شرکتی معرفی شدم برای کار. اولین ارتباطمون از طریق تلگرام بود که قرار مصاحبه گذاشته‌شد. اول از همه این‌که معتقدم تلگرام صرفا یک پیام‌رسان هست و نباید کار‌هایی مثل مصاحبه و اطلاع‌رسانی تمرین‌های دانشگاهی و ... از این طریق انجام بشه. به هر حال ...من تو روز موعود که حدودا آخرای سال ۹۷ بود رفتم برای مصاحبه. در نگاه اول محیط خوبی به نظر میومد. یکم منتظر موندم تا مصاحبه شروع بشه. راجع به خودم و سابقه‌ام پرسیدن و جواب دادم. ولی از وقتی که شروع کردن به پرسیدن سوال‌های عجیب و غریب مثل اینکه &lt;چرا در چاه رو گرد می‌سازن؟&gt; یکم متعجب شدم. آخر جلسه قرار شد که یه تسک به من بدن تا توانایی من رو بسنجن. از این تسک هیچ خبری نشد تا اینکه بعد از حدود یک ماه و نیم به من خبر دادن که کلا از استخدام هر‌گونه Developer منصرف شدن! و من کل این مدت رو منتظر این بودم که تسک رو به من بدن تا انجامش بدم.به نظرم این برخورد اصلا نباید برای شرکت‌های نرم‌افزاری وجود داشته باشه.شرکت &lt;ش&gt;در مرحله‌ی اول مصاحبه که به گفته مصاحبه کننده بخشی از مراحل سه‌گانه‌ی مصاحبه بود از من دقیقا سوالاتی رو پرسیدن که مفصل داخل رزومه‌ام جواب داده بودم. بعدش متوجه شدم که اصلا رزومه‌آم رو نخونده بودن. نکته‌ای که واقعا تو ذوقم زد این بود که سوالاتی که واقعا می‌شد جواب‌هاشو با مطالعه رزومه دید و یا تلفنی اون سوال‌ها رو پرسید، باید یک ساعت زمان رفت و یک ساعت زمان برگشت براش صرف می‌کردم که یک ربع حضوری این‌ سوال‌ها رو ازم بپرسن.کافه‌بازاراوایل اسفند بود که رزومه‌ام رو به اصرار یکی از دوستام فرستادم به کافه‌بازار. فکر می‌کردم که توی همون مرحله اول رد بشم. ولی خب اینطوری نشد. یکم توی فرآیند مصاحبه و زمان‌های مصاحبه بی‌نظمی پیش اومد ولی تیم HR کافه‌بازار از قبول کردن اشتباهش ابایی نداشت و با رفتار محترمانه عذرخواهی می‌کرد که خیلی برام ارزشمند بود. بعد از مصاحبه تلفنی که شامل کد زدن آنلاین هم می‌شد (‌کدی که باید می‌زدم خیلی ساده بود) و گذشتن چند مدت به مصاحبه حضوری اول دعوت شد. سیستم اطلاع رسانی و هماهنگی کافه‌بازار به نظرم فوق‌العاده بود و حتی می‌دونستم چه کسی قراره باهام مصاحبه بکنه. من هم قبل مصاحبه می‌رفتم LinkedIn مصاحبه کننده‌ام رو چک می‌کردم! (هنوز نمی‌دونم هدفم از این کار چی بود :)توی اولین مصاحبه کسی که قرار بود باهام مصاحبه کنه نیومده بود و فرد دیگه‌ای از من مصاحبه گرفت. ولی این مسئله اصلا تاثیری توی روند مصاحبه نداشت. برخورد افراد از نگهبان‌ها تا مصاحبه‌گیرنده و حتی افراد دیگه  همه محترمانه بود و حس خوبی به آدم دست می‌داد.بعد از تموم شدن این مصاحبه و گذشتن حدود ۱۰ روز دعوت شدم به مصاحبه‌ی دوم. این مصاحبه برای من پر از یاد‌گیری بود. و تو این مصاحبه به معنای واقعی متوجه شدم که خیلی راه برای رفتن هست. البته بعد از مصاحبه مطمئن بودم که رد میشم (که همین‌طور هم شد) ولی یه حس ‌دو‌گانه پیدا کرده بودم که بخشی از اون نا امیدی و بخشی امید برای یاد‌گیری بیش‌تر بود.فقط بدی این فرآیند این بود که خیلی زیاد طول کشید و من این رو با توجه به حجم درخواست‌های استخدام کافه‌بازار درک می‌کنم.پس از اینکه در این فرآیند رد شدم تصمیم گرفتم که در صورت امکان به صورت کارآموز  در کافه‌بازار فعالیت کنم که در این امر نیز رد شدم!شرکت &lt;ل&gt;این شرکت رزومه‌ام رو بعد از یک ماه! مشاهده کرد و از این شرکت به من زنگ زدن تا برم برای مصاحبه. توی مصاحبه سوال‌های منطقی ازم پرسیدن و به نظرم مشکل آن‌چنانی نداشتن. مصاحبه دوم مصاحبه راجع به مسائل مالی بود. در واقع من رو از نظر فنی قبول کرده بودن. نکته عجیب برای من تفاوت محل مصاحبه‌ی اول و مصاحبه‌ی دوم بود.بعد از اتمام مصاحبه‌ی دوم قرار بر این شد که تا پایان ترم تحصیلی جاری هفته‌ای ۱۶ ساعت و بعد از اون به صورت تمام وقت در شرکت مشغول به کار بشم. البته این همکاری بیش‌تر از یک هفته طول نکشید. بعد از یک هفته به مدیر شرکت گفتم که دیگه نمی‌خوام ادامه بدم و ایشون هم خیلی محترمانه قبول کردند. دلایل‌ام برای قطع همکاری این بود که در سه روزی که به شرکت رفتم، متوجه شدم اصلا نظم برای هیچ‌کاری در این شرکت (از نظر من) وجود نداشت و حتی معلوم نبود که نقش من در شرکت چه چیزی هست. از طرفی به این نتیجه رسیدم که این همکاری خیلی کمتر از چیزی که باید به ارتقا‌ی دانش من کمک می‌کنه. همین‌که اصطلاحا CTO شرکت فرق سیستم REST و غیر REST رو نمی‌دونست من رو مطمئن تر می‌کرد.در کنار همه‌ی این مسائل بابت رفتار خوب افراد این شرکت ازشون تشکر می‌کنم.شرکت &lt;د&gt;بعد از حدود یک هفته از ارسال رزومه به این شرکت،‌ به من زنگ زدن و زمان مصاحبه با من ست کردند. به موقع در محل حاضر شدم. در این‌که کسی نمی‌دونست قراره من حضور داشته باشم و از من مصاحبه گرفته بشه، هیچ توجیهی وجود نداشت. بالاخره یک نفر قبول کرد که با من مصاحبه بکنه.سوال کردن‌ها که شروع شد احساس عجیبی پیدا کردم. تا جایی که اطلاع داشتم به سوال‌ها جواب دادم ولی آخه من برای Python Developer درخواست فرستاده بودم و مدت زیادی از مصاحبه صرف پرسیدن سوال‌های مربوط به ++C و Embedded شد. خب قاعدتا خیلی از سوال‌های تخصصی رو نتونستم جواب بدم. بعد از چند تا سوال متوالی (که احساس کردم در این حوزه خیلی بدیهی هستن) مصاحبه کننده معترض شد که &lt;اگه اینا رو بلد نیستی پس برای چی رزومه فرستادی؟&gt; من هم در کمال بهت گفتم که من برای این موقعیت درخواست ارسال نکردم. تازه اونجا بود که فهمیدیم چه اتفاقی افتاده!مصاحبه روندش رو عوض کرد و سوال‌هایی که باید از من پرسیده می‌شد، پرسیده شد(که تقریبا همش رو جواب دادم).بعد از این همه کشمکش،‌ بعد از یک هفته یک تسک تست برای من فرستادن. ولی از اون‌جا که اصلا از فضای شرکت خوشم نیومده بود و دوست نداشتم هر جایی که بشه کار بکنم، متاسفانه به دروغ خبر دادم که در جای دیگری مشغول شده‌ام!رویداد کارآموز شواستارت‌آپ کوئرا که به نظرم یکی از استارت‌آپ‌های خوب ایران در حوزه‌ی برنامه‌نویسی هست، رویدادی به اسم کارآموز شو  ترتیب داده بود. روند این رویداد به این شکل بود که پس از برگزاری یک مسابقه جهت ارزیابی شرکت کنندگان،‌ شرکت کنندگان اقدام به ارسال رزومه‌ی خود، به شرکت‌هایی که اعلام آمادگی در این رویداد را کرده بودند، می‌کردند. علی‌رغم این‌که رتبه‌ی نسبتا خوبی در این مسابقه کسب کردم و به تمام شرکت‌های مرتبط به حوزه‌ی کاری‌ام رزومه ام را ارسال کردم،‌ غیر از یک شرکت که از قضا همان Quera بود،‌ هیچ‌گونه تماسی مبنی بر قبول یا رد شدن و تنظیم مصاحبه با من برقرار نشد. نکته‌ی منفی این مسئله این بود که اکثر شرکت‌هایی که در این رویداد حضور داشتند جز شرکت‌های نام‌دار حوزه‌ی استارت‌آپیفرآیند پذیرش کوئرا نیز شامل دو مصاحبه بود. مصاحبه‌ی مرحله‌ی اول که بسیار منطقی بود و رفتار محترمانه سرلوحه‌ی افراد این شرکت بود. ولی کار من به مصاحبه‌ی دوم نرسید و در شرکت دیگری مشغول به کار شدم. برای تیم کوئرا که این بستر را فراهم کرد(گرچه نحوه‌ی رفتار شرکت‌های مختلف قابل بازرسی است) آرزوی موفقیت دارم.شرکت &lt;ش&gt; ۲!پس از حضور در مصاحبه‌ی این شرکت،‌ بعد از حدود ۳ روز رضایت شرکت مبنی بر همکاری با من به من اعلام شد. من نیز ۱ روز وقت خواستم که تصمیم نهایی ام را به این شرکت اعلام کنم.بعد از گذشت ۱ روز،‌ به شماره‌ی اعلام شده زنگ زدم تا نظر مثبت خودم را اعلام کنم ولی متاسفانه به تماس من جواب داده نشد. دو روز متوالی دیگر نیز این کار را کردم ولی نتیجه‌ی متفاوتی حاصل نشد.با توجه به به این اتفاقات تصمیم گرفتم به تماس مجدد ادامه ندهم.شرکت &lt;د.د&gt;موقعیت شغلی مربوط رو تو جابینجا دیدم و رزومه‌ام رو ارسال کردم. بعد از مدتی به من زنگ زدن و از من پرسیدن که برای یک تاریخ مشخص برام مقدور هست که برای مصاحبه در شرکت حاضر بشم یا نه. من در اون زمان کلاس مهمی داشتم و گفتم که متاسفانه در این زمان نمی‌تونم برای مصاحبه حاضر باشم. مسئول HR شرکت گفتن که پس بعدا زنگ میزنن و یک زمان برای مصاحبه تنظیم می‌کنن. بعد از گذشت یک هفته ایمیلی برام اومد که مبنی بر رد شدن رزومه‌ام در این شرکت بود. ایمیل رو باز کردم و دیدم که دلیل رد شدن من &lt;کافی نبودن دانش فنی&gt; اعلام شده بود!ترجیح می‌دهم راجع به این مورد توضیح بیش‌تری ندهم!شرکت‌ مدریکمن تنها یک مرحله با مسئول HR شرکت مدریک، از طریق Skype انجام دادم. با توجه به گرمای شدید هوا این عمل به نظرم خیلی تحسین برانگیز هست.شرکت‌های م و م.ب و س و پ.گ و ...نکته مشترک خیلی از شرکت‌ها این بود که ادعا داشتند پس از مصاحبه حتی در صورت رد شدن به درخواست دهندگان اعلام وضعیت می‌کنند. حتی برخی از شرکت‌ها ادعا داشتند که دلایل پذیرش یا عدم پذیرش را نیز به اطلاع درخواست دهنده می‌رسانند. ولی بر خلاف این ادعا ها تمامی این شرکت‌ها هیچ گونه اطلاعی از وضعیت کنونی،‌ حتی زمانی که پیگیر وضعیت می‌شدم نمی‌دادند. به نظرم این ویژگی،‌بدترین ویژگی برای یک شرکت است.شرکت استاد کاربه نظرم مصاحبه‌ی تلفنی و مینی‌پروژه‌‌ي اختصاص داده شده به من کاملا منطقی بود که این فرآیند‌ها برای من  به نتیجه رسید و حدود ۱ روز است که کار را شروع کرده‌ام :)توضیحصحبت کردن راجع به خیلی از تجربه‌های دیگه‌ام خسته کننده است. به خاطر همین به نظرم همین‌قدر توضیحات کافیه. امیدوارم این پست براتون مفید بوده باشه! (بعیده)نتیجهدر این حدود ۶ ماه،‌ اصلا احساس خوبی به بازار کار در حوزه‌ی مهندسی کامپیوتر در ایران پیدا نکردم. برخی از دلایل‌ام را هم در لا‌به‌لای این پست اشاره‌کردم. ولی امیدوارم یا وضعیت آن‌طوری که من فکر می‌کنم بد نباشد یا هر‌چه زودتر به وضعیت بهتری برسیم.</description>
                <category>میلاد ابراهیمی</category>
                <author>میلاد ابراهیمی</author>
                <pubDate>Sun, 23 Jun 2019 18:13:11 +0430</pubDate>
            </item>
            </channel>
</rss>