۵-۶ سال پیش وقتی برای اولین بار استخدام شدم، روز اول کاریم رو هیچوقت فراموش نمیکنم. یه اتاق ۳ متر در ۴ متر بود که یک گوشه اش یه نفر دیگه نشسته بود. میزم رو آماده کرده بودن و من نشستم و برای اولین بار توی شرکتی بودم که سیستمی که بهم داده بودن یه Mac بود. خیلی خوشحال و خندون نشستم و منتظر موندم تا بهم بگن دقیقا برنامه چیه.
بعد از حدودا نیم ساعت، مدیرم اومد و بعد از خوش و بش کردن، گفت:
- خوب بریم سراغ کارمون! برو توی اپ-استور، این اسمی که میگم رو سرچ کن: "RecycleCoach". دانلودش کن یکم باهاش کار کن و بعد بهم بگو.
منم رفتم و دانلود کردم و سعی کردم بفهمم چیکار میکنه. حدودا یک ساعت بعد دوباره مدیرم اومد و خیلی خلاصه و مفید توضیح داد:
- ببین این اپلیکیشن رو ما سال ۲۰۰۹ نوشتیم، برای شهرداری های کانادا و امریکا هست. اون زمان اینو با Cordova نوشتیم. از امروز کار تو اینه که اینو با React Native باز نویسی کنی. الان خرداد هست دیگه آره؟ ما اینو تا آخر تابستون میخوایم داشته باشیم. موفق باشی!
بعد رفت
من نه تنها نفهمیده بودم که اون اپلیکیشن دقیقا چیکار میکنه!! بلکه حتی React هم بلد نبودم چه برسه به React Native! ددلاین ۳ ماهه؟؟ گفتم این چه جاییه من اومدم کار کنم؟؟؟
البته الان که بهش نگاه میکنم میبینم یکی از بهترین تجربه های کاریم بود و اولین جایی که به من برای یه پروژه خیلی جدی (خیییلی جدی) اعتماد شد و من تمام تلاشم رو کردم.
هر روز که میگذشت من نه تنها باید سعی میکردم نحوه کار کرد خود اپلیکیشن رو یاد بگیرم و سر در بیارم، بلکه باید React Native هم یاد میگرفتم.
هیچ داکیومنتی از نحوه کارش وجود نداشت. همه چیز اونجا بود، توی کد. خب یه کدی هم که سال ۲۰۰۹ نوشته شده بود بعد از ۹ سال هیچی نمیشد از توش فهمید. همه چیش قدیمی بود. البته ساختار خوبی داشت اما وقت نداشتم توی ۳ ماه همه چیز رو بخونم و بفهمم و هم توسعه بدم.
خیلی وقت ها پیش میومد که توی کد میدیدم یه چیزی هست که انگار هیچ کاری نمیکنه ولی هست! خب چرااا؟؟ من از کجا بدونم این چیه باید چه بلایی سرش بیارم. همینطوریش هم ۹ سال گذشته بود. توی VsCode روی هر فایل و خطی می ایستادم میدیدم اینو تو یه سالی یه نفری که اسمش هم غریبه بود نوشته بود و کامیت کرده بود. تقریبا ۴-۵ نفر توی اون مدت رو اون پروژه کار کرده بودن و هرکسی هم یه چیزی بهش اضافه کرده بود.
خیلی وضعیت نافرم داشت پیش میرفت. یه موضوعاتی بود که هیچجوره نمیفهمیدم شون. فقط هم کد های پروژه که نبود... خیلی وقت ها اصلا دلیل اتفاقاتی که میوفتاد رو نمیفهمیدم.
تا اینکه یه لیست از سوالاتی که داشتم آماده کردم و رفت دست به دامن مدیرم شدم و گفتم آقا بیا یه جلسه بذار توضیح بده چی به چیه؟ اونم اومد و یه توضیحاتی داد که نه تنها من بازم چیزی نفهمیدم، بلکه گمراه تر هم شدم!
خلاصه که دهن ما سرویس شد ولی به هر ضرب و زوری که بود اپلیکیشن رو با یک ماه تاخیر انتهای مهر ماه رسوندیم.
سخت بود؟ قطعا!!
ارزشش رو داشت؟ قطعا!
میشد کوتاه تر بشه این زمان؟ قطعا!!
تجربه خوبی بود؟؟ در کمال ناباوری خیلی زیااد!!
این داستانی که براتون تعریف کردم، کاملا واقعی بود! من تنها شانسی که آوردم این بود که برنامه شرکت بازنویسی اپلیکیشن بود و نه اضافه و کم کردن امکانات همون قبلیه. عملا هیچ چیزی به عنوان یک مرجع که بشه از روش خوند و فهمید موضوع چیه وجود نداشت. به جز سوالایی که میتونستم از مدیرم بپرسم و اونم جوری که خودش موضوع رو میدید به من توضیح بده. که البته خیلی اوقات بیشتر من رو گمراه میکرد.
این مشکلی بود که من با همه وجودم باهاش مواجه شدم و تازه اهمیت وجود Document برای من اونجا روشن شد. البته من قبل از اون داستان خیلی این بحث رو نمیشناختم. خیلی تجربه ام کم بود.
شاید اگر از روز اول یک یا چند تا داکیومنت مختلف برای این وجود داشت که من بتونم راحت تر بفهمم که باید دقیقا چیکار کنم خیلی زمان کمتری میبرد و قطعا کمتر اذیت میشدم. ولی خب این تجربه به من خیلی کمک کرد که بفهمم چرا این بحث خیلی اهمیت داره.
از روی همین داستانی که تا اینجا تعریف کردم میتونید مشکلاتی که من باهاشون مواجه بودم رو ببینید:
یه جایی یادمه خوندم که میگفت:
Documentation is not a luxury but a necessity for creating a high-quality, consistent, and accessible front-end.
حالا با توجه به این مسائل و مشکلاتی که گفتم، بریم ببینیم چطوری میتونیم این موارد رو حل کنیم.
موقعی که داریم یه برنامه ای رو توسعه میدیم و میخوایم همزمان باهاش داکیومنت بنویسیم باید اول روش فکر کردنمون راجع به داکیومنت رو درست کنیم. خیلی ساده بگم:
باید بدونیم و یاد بگیریم که چی بنویسیم. چرا بنویسیم. چطور بنویسیم. برای کی بنویسیم.
به نظر من باید به جای اینکه تمرکز مون روی "چیستی" اجزای سیستم باشه، باید روی "چرایی" اونها باشه. یعنی چی؟ مثال ساده ای که میزنم اینه: فرض کنیم توی یه فایلی یه تابعی هست که یه دونه API رو داره صدا میزنه. خب اگه بخوایم برای این تابع توضیحاتی رو بنویسیم میتونیم اینطور بنویسیم:
"این تابع یک API را به صورت Async صدا میزند و نتیجه را برمیگرداند". -- مدل چیستی
یا میتونیم اینطور بنویسیم:
"در زمان ثبت نام کاربر، اطلاعات کاربر باید در سرور به منظور ارسال پیامک در آینده ذخیره شود". -- مدل چرایی
به نظر شما اگر مثلا ۶ ماه از زمانی که شما این کد رو نوشته باشید بگذره، کدوم متن توضیحی رو بهتر به خاطر میارید یا میتونه بیشتر کمکتون کنه؟ برای من مدل دوم بوده خیلی اوقات.
پس به نظرم بهتره در زمان نوشتن داکیومنت ها به این توجه کنیم که توی متن داکیومنت توضیح بدیم که چرا این اتفاقات دارن میوفتن. اینکه اون اجزا (توابع، فایل ها، متد ها و ...) چه کاری دارن انجام میدن رو کسی که اون زبون یا framework رو بلد باشه میتونه متوجه بشه خودش.
فکر میکنم تا اینجا این موضوع رو متوجه شده باشید. ولی برای اینکه باهم کاملا همصفحه بشیم:
داکیومنت ها باید هم جنبه های داخلی و هم جنبه های بیرونی برنامه ای که نوشته شده رو پوشش بدن. حالا این یعنی چی؟
منظورم جنبه های بیرونی اینه که
و ... - کلا هر چیزی که به ما کمک میکنه کسب و کار رو بهتر متوجه بشیم.
منظور از جنبه های داخلی اینه که
و کلا خیلی چیز ها مثل کامنت ها، Annotation ها و نحوه اسم گذاری ها و ... مربوط به این بخش میشه.
باید نحوه معماری و ساختار پروژه به طور مفصل توضیح داده بشه. اینکه چرا اینطور توسعه داده شده و چطور داره کار میکنه. الگو های تکرار شونده چی هستند؟ و کلی چیز دیگه.
تو ادامه مطلب در مورد این صحبت میکنیم که این کار ها رو چطور توی پروژه های Front-end انجام بدیم.
پس تا اینجا متوجه شدیم که مهم ترین چیز برای نوشتن داکیومنت روش تفکر هست. خب ما طبیعتا داریم در مورد Documentation توی پروژه های Front-end صحبت میکنیم گرچه مفاهیم کاملا قابل تعمیم هست و خیلی ابزار ها بین همه مشترکه.
این داکیومنت ها میتونن هرجایی باشن. مثلا ممکنه یه تیم به این نتیجه برسه که اگه این داکیومنت ها رو توی Confluence بنویسه راحت تره یکی دیگه ممکنه از Google Docs استفاده کنه. راستشو بخواید واقعا مهم نیست کجا باشه تا زمانی که داره جواب میده براتون. اما چیزی که من بیشتر دیدم اینه که این داکیومنت ها توی فایل های Markdown داخل خود پروژه ها نوشته میشن.
در واقع Markdown یک زبون نشانه گذاری که خیلی شبیه به همون HTML خودمونه. حتی خیلی خیلی ساده تر. البته اوایل ممکنه یکم غریبی کنید ولی بعد یه مدت عادت میکنید. مزیتی که داره اینه که فایل های داکیومنت داخل خود پروژه قرار میگیرن و دسترسی بهشون خیلی خیلی ساده تر از Google Docs و Confluence هست.
پس اولین چیزی که یاد میگیریم: Markdown
اسم فایل ها هم معمولا README.md هست. یه دونه فایل داکیومنت اصلی که داخل root پروژه تون قرار میگیره میتونه کارو براتون دربیاره. البته هر فولدری از هرجای پروژه تون میتونه برای خودش یه فایل README داشته باشه.
حالا اگر بخوایم راجع به ابزار های دیگه که مخصوص فرانت باشه صحبت کنیم میرسیم به Storybook که قبلا یه مطلب راجع بهش مفصل توضیح دادم که پیشنهاد میکنم اونو بخونید حتما.
یک سری تکنولوژی دیگه هم هست مثل TypeDoc و JSDoc که برای Annotation ها هستند که میتونه خیلی کمکمون کنه که البته جلو تر نمونه هاشون رو میبینیم.
از طرف دیگه یکی از مهم ترین ابزار های امروزه برای فرانت-اند، TypeScript هست! خدا میدونه چقدر میتونه روی کیفیت خروجی نهایی کل پروژه و البته داکیومنت ها بهمون کمک کنه. البته اگر درست استفاده بشه و نیایم همه جا از تایپ any استفاده کنیم.
احتمالا اگر ساختار پروژه مون تر و تمیز و سرراست باشه ما یک سری کامپوننت Reusable داریم که بتونیم بار ها و بار ها ازشون توی جاهای مختلف استفاده کنیم. حالا اگه بخوایم خود این کامپوننت ها رو داکیومنت کنیم میتونیم از Storybook استفاده کنیم که کار باهاش هم واقعا سادست. به ما کمک میکنه حالت های مختلف کامپوننت ها رو پیاده سازی کنیم و ببینیم چطور نمایش داده میشن و دقیقا چه کاری برامون انجام میدن.
ولی طبیعتا توی پروژه های بزرگ ما فقط و فقط کامپوننت نداریم! کلی تابع مختلف هست که هر کدوم دارن کاری رو برای ما انجام میدن و بعضیاشون نیاز به توضیح دارن.
کامنت Annotation
چیزی که پیشنهاد شده اینه که اول حتما توی پروژه از TypeScript استفاده بشه. بعد هم برای توابعی که نیاز به توضیحاتی داره از Annotation ها میتونیم استفاده کنیم. خیلی ابتدایی اگه بخوایم توضیح بدیم که دقیقا اینا چی هستن، میتونیم بگیم که این Annotation ها یک سری کامنت ساده هستند که بالای جایی که تابع تعریف شده نوشته میشن که در مورد تابع اطلاعاتی به ما بدن. مثلا اینکه این تابع چه ورودی هایی داره و چه خروجی ای داره. جنس ورودی ها و خروجی ها و مفهوم شون چی هست.
یه مثال ازش ببینید:
یه تابع که ۲ تا عدد رو میگیره و جمع میزنه و برمیگردونه. توی خط اول توضیح اینکه تابع چه کاری انجام میده قرار داده شده.
خط بعدی یه سینتکس param@ داره که در واقع پارامتر اول رو تشریح میکنه و همین روال برای پارامتر دوم هم تکرار میشه.
در نهایت returns@ مشخص میکنه که این تابع چه خروجی ای داره.
مزیت این Annotation ها علاوه بر اینکه فهمیدن کد رو ساده تر میکنن اینه که توی ادیتورمون یه suggestion خفن و تر و تمیز بهمون میده که نوشتن کد رو خیلی ساده تر میکنه برامون:
همینطور که مشخصه، همون Annotation ها که بالا نوشته بودیم اینجا داره بهمون نمایش داده میشه.
درسته که این قابلیت خفنیه، ولی دقت کنیم که یهو جوگیر نشیم همینطوری همه جا بخوایم از اینا بذاریم. معمولا اینو برای توابعی میذاریم که مهم تر هستند، یا کاری که انجام میدن نسبتا کار پیچیده تری باشه که احتیاج به توضیح داره، یا توابعی که به طور مکرر استفاده میشن.
یادمون نره که کلا اگر اسم توابع اسم های خوبی باشن خیلی جاها اصلا نیاز به توضیح اضافه نداره و اسم تابع خودش توضیح دهنده کاری که انجام میده هست. مهارت اسم خوب انتخاب کردن هم مهارت خیلی مهمیه که باید بهش توجه بشه.
بعد از اینکه تقریبا این Annotation هامون رو نوشتیم (که البته معمولا در حین نوشتن تابع انجام میشه) میتونیم بیایم و به کمک TypeDoc یا JSDoc ازشون یه سری خروجی داکیومنت بگیریم. یه چیزی شبیه API Refrence که بتونیم همه توابع پروژه رو یکجا با داکیومنت خودشون داشته باشیم که من دیگه اینجا راجع به اون صحبت نمیکنم. میتونید توی داکیومنت های خودشون پیداشون کنید.
داکیومنت های MD
قبل تر راجع به Markdown صحبت کردم، اینجا میخوام بگم بهتره چه چیز هایی داخل این فایل های MD بهتره که قرار بگیره.
اگر یه نگاهی به کتابخونه های open-souce بندازید، تقریبا یه الگوی مشخص و تکرار شونده میبینید:
خب همین الگو رو ما میتونیم برای پروژه های تجاری هم استفاده کنیم. فایل Readme.md که توی root پروژه قرار گرفته به نظرم قشنگه که شامل این موارد که اشاره کردم بهشون بشه. لزوما قرار نیست همه چیز هم یکجا باشه. میتونید فایل های مختلف md بسازید و توی فولدر های مختلف که مربوط به بخش خودشون هستن قرار بدید و توی فایل اصلی بهشون لینک بدید. اینطوری ساختارش مرتب تر هم میشه.
اگر همه اعضای تیم توی نوشتن این داکیومنت ها مشارکت دارن، دقت کنید که قواعد نوشتاری تون و ادبیات و حسی که انتقال میدید یکدست باشه. اینطوری نباشه که یک جا نوشتار خیلی رسمی باشه و یکجای دیگه خیلی خودمونی.
خیلی لازمه که فیدبک بگیرید و با چند نفر چک کنید و بهترش کنید. همیشه جا برای بهتر شدن هست.
اینکه کی داکیومنت ها رو بنویسیم هم به نظرم خیلی موضوعی هست که میشه روش به توافق رسید ولی چیزی که من متوجه شدم اینه که اگر داکیومنت ها همون زمانی که دارید توابع و کامپوننت ها رو مینویسید انجام بشه نتیجه بهتره.
معمولا اول کار قبل اینکه چیزی پیاده بشه که نمیشه داکیومنتی نوشت، از طرفی آخر کار هم اگر بخواید اینکارو بکنید ممکنه یه چیزایی یادتون بره و از قلم بیوفته.
نکته خیلی مهم اینه که یه طوری بنویسید که اگر ۶ ماه دیگه برگشتید و بهش نگاه کردید بتونید بفهمید چی بوده. همیشه هم آپدیت نگه دارید این فایل های داک رو.
وقتی یه پروژه از یه اندازه ای بزرگ تر میشه، برای اینکه هم داکیومنت ها و هم کد ها یکدست باشن یه داکیومنت بعنوان Code of Conduct یا ساده تر بگیم: How to contribute قرار میگیره که توش به اصول و قواعدی که توافق کردیم تو پروژه، اشاره میکنیم. مثلا:
و مسائلی از این قبیل. این به ما کمک میکنه که افراد جدیدی که وارد تیم میشن، از اینکه چطور میتونن مشارکت کنن آگاه بشن. فرایند Code review ها یکدست اتفاق بیوفته و شاهد چند دستگی بین افرادی که کد رو review میکنن نباشیم. کلی مزیت دیگه هم داره.
طبیعی هست که این اصول و قواعد در طول زمان تغییر کنن و کم و زیاد بشن. یادمون باشه که این داک رو همیشه آپدیت نگه داریم.
یه نمونه از این مورد میتونید اینجا ببینید.
پروژه هایی که بزرگ و بزرگ تر میشن، طبیعتا شاید تو یک سال چند نسخه ازشون منتشر بشه که توی هر نسخه مواردی تغییر کردن، فیکس شدن، حذف شدن و ...
بهتره که لاگ این تغییرات و اینکه چه زمانی و در چه نسخه ای اتفاق افتادن رو داشته باشیم یکجا. توی هر نسخه که منتشر میشه اشاره کنیم که دقیقا این تغییرات چیا بودن و چه تاثیری روی پروژه گذاشتن. آیا تغییری که داده شده میتونه باعث بشه که پروژه از کار بیوفته؟ آیا برای این نسخه جدید همه استفاده کننده هاش باید کاری انجام بدن؟ مثلا یک بار دیگه npm install رو اجرا کنن و مسائل این چنینی.
این فایل رو اسمش رو میذارن changelog.md و توش از نسخه اول همه این موارد رو لاگ میذارن تا بعدا بتونن بهش رجوع کنن. ابزار هایی هم هستند که میتونن برای ما اینکار رو به صورت خودکار انجام بدن. مثلا هر بار که یک ریلیز جدید به کمک Gitlab Pipeline انجام میدیم، خودش به صورت خودکار تغییرات (لیست کامیت ها) به همراه شماره و اسم نسخه جدید رو توی فایل changelog بذاره. دستی هم که میدونیم میشه این فایل رو نوشت و ویرایش کرد و ...
به نظرم یه باور غلطی که وجود داره اینه که خیلی از ما فکر میکنیم اگر کدی رو انقدر خوب بنویسیم که مشخص باشه چیکار میکنه دیگه نیازی به داکیومنت نداریم و اصطلاحا کد self-documented هست.
این حرف به نظر من هم درسته هم غلط. اول باید بفهمیم کد self-doc اصلا یعنی چی و منظورش چیه؟
منظور از این مفهوم، اینه که اسم توابع، فایل ها، کامپوننت ها، متغیر ها، ثابت ها و ... درست انتخاب بشه. یه نمونه خیلی ساده ازش بزنم: فرض کنید یه متغیر دارید که مقدارش Boolean هست، برای اسم این متغیر بهتره اول اسمش یه is بیاد. مثلا isActive یا isOpen یا چیزای شبیه به این. این حس رو در زمانی که داریم کد رو میخونیم القا میکنه که این یه متغیری هست که ۲ حالت داره: یا هست یا نیست!
یا همون مثلا addNumbers که زدیم. از اسمش معلومه که این تابع ۲ تا عدد رو باهم جمع میکنه. این میشه کد self-doc. ولی دلیل اینکه چرا این اعداد باید جمع بشن که مشخص نیست! پس بنابراین همچنان بعضی جاها نیاز به توضیح بیشتر داره که بالاتر راجع بهش مفصل صحبت کردیم.
ممکنه باز هم خیلی هامون فکر کنیم اگر برای پروژه و چیز های مختلف تست بنویسیم دیگه نیازی به داکیومنت نداریم. قطعا تست نوشتن خیلی مهم و خوبه و کلی فایده داره. مثلا اینکه هر تابعی چجوری کار میکنه و خروجیش بر اساس ورودی های مختلف چه چوری میشه.
پس بیاید روی این توافق کنیم که وجود داشتن تست ها برامون اگه نگیم ضروریه، مفید میتونه باشه. اما جایگزین داکیومنت نوشتن نمیشه. چون ما لازم داریم درک کنیم که چرا همون تست ها اونطوری که هست نوشته شدن؟ چه هدفی رو دنبال میکنن و چطور باید باهاشون کار بشه.
نکته مهم راجع به تست نوشتن ها اینه که متن و عنوان Assertion ها تون باید توضیح خوبی بده از اینکه دقیقا چه چیزی داره تست میشه. پس این خودش یه تمرین خوبه. اینکه چه مدل از تست نویسی رو دارید انجام میدید هم مهمه.
فکر کنم همین بود! اگر مورد دیگه ای به ذهن شما میرسه حتما بگید که به مطلب اضافه کنم. اگر دوست داشتید میتونید از طریق لینکدین باهام در ارتباط باشید