Software Engineering for Data Scientists: What is Good Code?
فصل یک از کتاب درباره این پرسش و مساله هست که کد خوب چه کدی هست؟ و در ابتدا شروع میکنه یک سری معیار برای کد خوب میگه:
- سریع اجرا بشه
- سادهبشه خوندش و فهمیدش
- نگهداری ازش ساده باشه یعنی اگر پروژه تغییر کرد و نیازمندی جدیدی اضافه شده بود، بشه به راحتی کد رو در اون جهت تغییر داد (حالا چه ساید صنعت باشه چه ساید ریسرچ به هر حال نیازمندیهای شما مدام در حال تغییره در زمان)
- نباید پیچیده باشه
- اگر ورودی غیرمنتظرهای بهش داده شد، سقوط نکنه
- و ...
در همین راستا، کتاب اومده در پنج راستای simplicity و modularity و readability و performance و robsutness جنبههایی از کد خوب رو بررسی کرده.
کد خوب کجا و چرا مهمه؟
گاهی وقتا آدم بایستی یک کدی رو که صرفا یکبار نوشته میشه برای یک دموکردنی و دیگه نیاز نیست بعدا روش کاری انجام بشه رو بنویسه. خب قاعدتا در اینجا کد به هر صورت که نوشته بشه، شده. اما گاهی وقتا کدی که نوشته میشه قراره بعدا بارها و بارها با تغییرات و change رو به رو بشه. در این صورت خشت اول چون نهد معمار کج تا ثریا میرود دیوار کج میشه. یکی از پرسشها و وظایف اصلی مهندسی نرمافزار هم همین طرز رفتار با تغییرات نیازمندیها و افزایش پیچیدگی مساله هستش.
نکته اخلاقی این که در زندگی هم باید جوری چید و زندگی کرد که بشه آماده و پذیرای هر نوع تغییراتی بود.
جنبه Simplicity
کدی که پیاده میشه باید simple باشه و نباید complex باشه. تعریفی که کتاب از simple بودن ارائه میده اینه که وقتی یک چیزی رو جایی تغییر میدید باید بتونید پیشبینی کنید چه چیزهای دیگهای تغییر میکنن و در عین حال هیچ چیز بیربط دیگهای نباید تغییر کنه. اما اگر کد complex باشه شما وقتی با نیازمندی جدیدی رو به رو میشید نمیدونید باید کجای کد رو تغییر بدید تا به اون نیازمندی برسید و از اون ور هم نمیدونید این تغییری که میدید بقیه جاها رو چطور تغییر میده (دقت کنید که این پیشبینی پذیری اثر تغییرات در یک جا بر جاهای دیگه چطور در راستای همون توانایی هندلکردن تغییرات نیازمندیهاست.) (در زندگی هم طرز زندگی کردن آدم باید ساده باشه یعنی اگر بتونه پیشبینی کنه اگر من از فردا چنین تغییری در خودم دادم در بقیه عقاید و منطقهام هم چه تغییری رخ میدن) در ادامه کتاب چند نکته و توصیه برای جلوگیری از complex شدن کد ارائه میده:
خودتون رو تکرار نکنید یا DRY
اطلاعات و یک منطق اجرایی نباید چند جا تکرار بشن وگرنه دوباره به هنگام نیازمندی برای تغییر باید یادتون باشه که کجاها رو باید آپدیت کنید. مثلا فرض کنید شما قراره که سه تا دیتافریم رو از سه فایل csv بخونید و روشون پردازشی انجام بدید و دوباره ذخیرهشون کنید. این که این منطقهای خوندن و پردازش و ذخیره کردن رو جدای از هم انجام بدید، اگر فردا رازی خواستید در پردازش تغییری بدید باید در تک تک اون سه تا جداگانه انجام بدید که همین یعنی تکرار.
اینجا کتاب چند توصیه برای جلوگیری از تکرار ارائه کرده:
- شما ممکنه یک سری کد با منطق مشابه مثلا preprocess رو در پروژهها و فایلهای مختلف استفاده کنید. یک کار خوب میتونه این باشه که یک فانکشنی داشته باشید که بتونه تا حد امکان انواع دیتا با تنوع تایپی و فرمی مختلفی رو بگیره و این منطق یکسان رو روشون انجام بده.
- ممکنه چندین نفر بر روی چندین پروژه، یک منطق مشابهی رو به چند فرم مختلف پیاده کنن. کامیونیکت کردن با دیگران و سادهکردن استفاده از کد (مثل فراهم کردن داکیومنتیشن و اینها) برای دیگران باعث میشه این تکرارهای بین فردی رو هم نداشته باشیم.
- کامنت و داکیومنتیشن هم میتونن نوعی از داپلیکیشن و تکرار باشند. کامنتی که داره توضیح میده کد دقیقا داره چی کار میکنه رو نباید نوشت. وگرنه دو روز دیگه خواستید کد رو عوض کنید باید خط به خط کامنت عوض کنید و اگر نکنید ممکنه سه روز دیگه بین ناهمخوانی کامنت و کد گیر کنید.
از پرگویی و پرخطی در کد بپرهیزید
گاهی وقتا شما میتونید یک منطق رو در ده خط پیاده کنید و یا میتونید در بیست خط پیاده کنید. این یک تریدآف هست که شما چه قدر میخواید کدتون کوتاهتر یا خواناتر باشه. با این وجود توصیه میکنه که کدتون رو بیش از حدی که باید طولانی نکنید. مثلا فانکشنهایی builtin ای که وجود داره استفاده کنید عوض این که خودتون بخواید فانکشن بنویسید. و همچنین از متغیرها و Variable های موقتی، به درد نخور هم باید پرهیز کنید.
جنبه Modularity
ماژولاریتی یعنی هنر شکستن یک سیستم بزرگ به قطعات کوچکتر. مزیتهای ماژولاریتی همچین چیزهاییه:
- راحتتر میشه کد رو خواند و فهمید.
- راحتتر میشه دیباگ کرد که مشکل از کجا میاد
- راحتتر میشه از کد در پروژههای دیگه استفاده کرد.
- راحتتر میشه کد رو تست کرد.
چه جوری اما میشه یک سیستم بزرگ رو به قطعات کوچک شکست؟ توصیهای که کتاب میکنه اینه که شما در جایگاه توسعه دهنده باید بتونید پیشبینی کنید که در آینده چه تغییراتی ممکنه به وجود بیاد و بنابراین کدوم قسمتها از کد میتونن با هم باشن و کدوما بهتره از هم جدا بشن.
ماژولاریتی حتما لازم نیست در ادای خیلی فنی رخ بده و مثلا شما as a scientist همین که نوتبوکهای جمعآوری داده و آموزش مدل و اینها رو از هم جدا میکنید خودش یک سطحی از ماژولاریتی هست.
جنبه Readability
اول این سخن بزرگان زیبا رو داشته باشیم:
... code is read muce more than it is written
شما ممکنه یک کدی رو در یک پروژهای در عرض یک روز بنویسید و بعد از اون پروژه جدا شید یا اصلا شغلتون رو عوض کنید اما اون کد اونجا میمونه و هر آیندگان بدبختی قراره اون کد رو بخونه و بفهمه و احیانا اگر تغییری روش نیاز بود اعمال کنه . حالا واسه این که این کد چه برای خودتون در آینده و چه برای دیگرانی که اصلا شما رو ندیدند در آینده قابل خوندن باشه یک سری توصیهای وجود داره که در ادامه میگیم:
استانداردهای استایلی رو رعایت کنید
استانداردهایی مثل PEP8 و اینها باعث میشن تا کد آشنا به نظر برسه و میزان زمانی که صرف خوندن و فهمیدنش میشه کاهش پیدا کنه.
اسمهای درستی برای متغیرها و فانکشنها و اجزای کدتون بذارید
کدتون رو تمیز کنید
بعد این که کارتون روی توسعه فانکشنها تموم شد و تستش کردید، کثیفکاری که راه انداختید رو تمیز کنید. سادهترین حالت اینه که print های بیخود رو بردارید و پیشرفتهترین حالت هم اینه که refactor درست حسابی کنید.
داکیومنتیشن بنویسید
داکیومنتیشن لولهای مختلفی داره. از کامنتهای برخط گرفته تا docstringsها و صفحات Readme و حتی ارائه tutorial کمک میکنن کد قابل فهمتر باشه.
جنبه Performace
یک کد خوب باید از جنبههای عملکردی نظیر مدت زمان و مموری که صرف میکنه، کارا باشه. در قسمت بعدی کتاب در این خصوص بیشتر توضیح داده شده.
جنبه Robustness
کد باید robust باشه به دو معنا. اول این که reproducible باشه یعنی یک فرد دیگه بتونه از صفر تا صدش رو بدون هیچ شکستی اجرا کنه و نتایج شما رو تکرار کنه. و دوما هم این که اگر یک وقت ورودی غیرقابل انتظاری در هر جای برنامه رخ داد، کد باید بتونه جوری نوشته شده باشه که به این تغییرات پاسخ بده و تا حد امکان مانع از fail شدن کل سیستم بشه.
خود این قضیه robust بودن دو جنبه راهکار داره. اول این که انواع مختلف ارورها و اتفاقاتی که در کد میافته باید به خوبی Log بشن.
جنبه دوم هم اینه که برای کد باید Test نوشته بشه. تست باعث میشه تا اگر کسی خواست کد شما رو روی ماشین خودش اجرا کنه، با تست گرفتن هم اون مطمئن بشه که کد درست کار میکنه هم شما نشون بدید که کد درست کار میکنه.