کد نینجا

از جمله وبسایت‌هایی که به عنوان یه جور مرجع برای JavaScript بوکمارک کردم، وبسایت javascript.info هست. به نظر میرسه اول کار، این وبسایت به زبان روسی نوشته شده و بعد از این که دیدن خوب جلو رفتن زبان انگلیسی رو هم ارائه میدن که میشه زبان پیش‌فرضش.

چند روز پیش متوجه شدم که این وبسایت، زبان‌های دیگه‌ای رو هم باز کرده و ملت می‌تونن توی ترجمه‌ی مقالات و صفحاتش مشارکت کنن. خوشبختانه مقاله‌ای که من واقعا دوستش داشتم هنوز ترجمه نشده بود؛ Ninja Code. اسممو ثبت کردم. این چیزی که می‌خونید نتیجه‌ی ترجمه‌ست. مقاله سعی کرده بود با زبان طنز، مشکلات این سبک از کدنویسی رو به مخاطب گوشزد کنه. منم سعی کردم حالت رسمی و متناسب با مقاله‌ی اصلی رو حفظ کنم.

توی مقاله، دو مفهوم نینجا که متعلق به فرهنگ ژاپنه و تائویسم که از مسلک‌های قدیمی چین محسوب میشه با هم ترکیب شدن. با این حال نتیجه، از دید من خیلی یکنواخته.

مقدمه

یادگیری بدون تفکر، بی‌فایده است؛ تفکر بدون یادگیری، خطرناک. -- کنفسیوس

نینجاهای برنامه‌نویس در گذشته از این ترفندها برای باز کردن ذهن حافظان کد استفاده می‌کرده‌اند.

خبره‌های بازبینی کد، در تست‌های خود به دنبال این گونه ترفندها می‌گردند.

توسعه‌دهندگان تازه‌کار، بعضی اوقات در استفاده از این ترفندها بهتر از نینجاهای برنامه‌نویس عمل می‌کنند.

موارد زیر را با دقت بخوانید و مشخص کنید کدام هستید: یک نینجا، یک تازه‌کار، یا شاید یک بازبین کد؟

مزاح: افراد زیادی سعی می‌کنند مسیرهای نینجا شدن را طی کنند. تعداد اندکی موفق می‌شوند.

اختصار، روح شوخ‌طبعی است

کد را تا حد امکان کوتاه کنید. به همه نشان بدهید که چقدر باهوش هستید. بگذارید ویژگی‌های ریز زبان، شما را راهنمایی کنند.

برای نمونه، به این عملگر سه‌گانه (?) نگاه کنید:

// برگرفته از یک کتابخانه‌ی مشهور جاوااسکریپت
i = i ? (i < 0 ? Math.max(0, len + i) : i) : 0;

زیباست، نه؟ اگر شما هم کد خود را به این شکل بنویسید، توسعه‌دهنده‌ای که گذرش به این خط بیفتد و سعی کند مقدار i را دریابد، پس از سرمستی فراوان و در پی جواب، به سراغ شما خواهد آمد.

به او بگویید که «کوتاه‌تر، همیشه بهتر است». وی را به راه نینجا شدن هدایت کنید.

متغیرهای تک‌حرفی

دائو، در سکوت، پنهان است. تنها دائو به خوبی شروع شده و به خوبی کامل می‌شود. -- لائوتسه (دائو دِ جینگ)

یک راه دیگر برای کدنویسی سریع‌تر، استفاده از متغیرهای تک‌حرفی در همه‌جاست. برای مثال: a، b یا c.

همانند یک نینجای واقعی در جنگل، یک متغیر کوتاه در کد ناپدید می‌شود. هیچ‌کس قادر به پیدا کردن آن با استفاده از «جستجو»ی ویرایشگر نخواهد بود. حتی اگر کسی آن را پیدا کند، نخواهد توانست معنی a یا b را رمزگشایی کند.

اما یک استثنا وجود دارد. یک نینجای واقعی هرگز از i برای شمارنده‌ی یک حلقه‌ی for استفاده نخواهد کرد. در هر جایی از این نام استفاده می‌کند، به جز این مورد خاص. به اطراف خود بنگرید، تعداد زیادی از حروف عجیب و غریب وجود دارند. برای مثال، x یا y. از آن‌ها استفاده کنید.

به‌خصوص استفاده از یک متغیر نامتعارف به عنوان شمارنده‌ی یک حلقه، وقتی حالتان را دگرگون می‌کند که بدنه‌ی حلقه، ۱-۲ صفحه باشد (اگر امکان طولانی‌تر کردن آن وجود دارد، دریغ نکنید). حال اگر شخصی با دقت به درون حلقه نگاه کند، به سرعت نخواهد توانست تشخیص دهد که متغیری با نام x، در واقع همان شمارنده‌ی حلقه است.

از مخفف‌ها استفاده کنید

اگر قوانین تیم، استفاده از اسامی تک‌حرفی و مبهم را ممنوع کرده است، آن‌ها را کوتاه کنید، مخفف بسازید.

همانند زیر:

فقط افراد بابصیرت قادر خواهند بود این نوع اسامی را دریابند. سعی کنید همه‌چیز را کوتاه کنید. تنها یک فرد شایسته باید بتواند توسعه‌ی کد شما را تایید کند.

اوج بگیر. سبک باش.

مربع بزرگ، بدون زاویه است
ظرف بزرگ، دیر کامل می‌شود
موسیقی بزرگ، بی‌آواست
تصویر بزرگ، بی‌صورت است. -- لائوتسه (دائو دِ جینگ)

هنگامی که می‌خواهید نامی را انتخاب کنید، سعی کنید از خلاصه‌ترین کلمات استفاده کنید. برای مثال obj، data، value، item، elem و غیره.

تست توجه

تنها یک برنامه‌نویس ملتفت باید قادر به درک کدتان باشد. اما چگونه از کیفیت کد خود مطمئن شویم؟

یکی از راه‌ها، استفاده از اسامی مشابه برای متغیرهاست، برای مثال date و data. هر جایی توانستید، آن‌ها را در هم آمیزید.

خواندن سریع اینچنین کدی غیرممکن خواهد بود. و هنگامی که یک غلط تایپی در کدتان وجود داشته باشد، برای یک مدت طولانی درگیر خواهیم بود. در چنین شرایطی، چای بنوشید.

مترادف‌های خلاقانه

سخت‌ترین کار، یافتن یک گربه‌ی سیاه در یک اتاق تاریک است، به‌خصوص اگر هیچ گربه‌ای وجود نداشته باشد. -- کنفسیوس

استفاده از نام‌های مشابه برای مفاهیم یکسان زندگی را بسیار جذاب‌تر نموده و خلاقیت شما را در معرض دید عموم قرار می‌دهد.

برای نمونه، پیشوند توابع را در نظر بگیرید. اگر تابعی یک پیام را بر روی صفحه نمایش می‌دهد، نام آن را با display... آغاز کنید، مثلا displayMessage. حال اگر یک تابع دیگر، چیز دیگری (برای مثال نام یک کاربر) را بر روی صفحه نمایش می‌دهد، نام تابع را با show... شروع کنید، مثلا showName.

با این کار، این تلقین را ایجاد کنید که یک تفاوت ناچیز بین این توابع وجود دارد، در حالی که این طور نیست.

با یاران نینجای خود عهدی ببندید: اگر آرش توابع را در کد خود با display... نمایش می‌دهد، محمد می‌تواند از render... استفاده کند، آیدا از paint... و غیره. ملاحظه می‌کنید که با این کار، کدتان بسیار جالب و متنوع خواهد شد.

... و اینک برگ برنده!

برای توابعی که تفاوت‌های فاحش و مهمی دارند، از پیشوندهای یکسانی استفاده کنید!

برای نمونه، تابع printPage(page) از یک چاپگر استفاده خواهد کرد. و تابع printText(text) متن را بر روی صفحه نمایش خواهد داد. اجازه بدهید یک فرد اجنبی، با دیدن تابع printMessage به فکر فرو برود: «این تابع، پیام را به کجا می‌فرستد؟ به یک چاپگر یا به صفحه‌ی نمایش؟». برای این که کارتان واقعا بدرخشد، بهتر است printMessage(message) پیام را در پنجره‌ی جدید به کاربر نمایش دهد!

از نام‌ها دوباره استفاده کنید

وقتی کل، تقسیم شود، اجزا به نام احتیاج دارند.
نام‌های موجود کفایت می‌کنند.
باید بدانی چه زمانی توقف کنی. -- لائوتسه (دائو دِ جینگ)

تنها زمانی که واقعا ضروری است از یک متغیر جدید استفاده کنید.

در عوض، از نام‌های موجود دوباره استفاده کنید. کافی است مقادیر جدید را به آن‌ها نسبت دهید.

در یک تابع، سعی کنید تنها از متغیرهایی که به عنوان پارامتر ارسال شده‌اند استفاده کنید.

این کار، تشخیص این که در هر لحظه دقیقا چه چیزی در متغیر ذخیره شده، و همچنین مقدار آن از کجا آمده است، را بسیار سخت خواهد نمود. هدف این است که بینش و حافظه‌ی شخصی که در حال خواندن کد است را تقویت کنیم. فردی با بینش اندک باید کد را خط‌به‌خط تحلیل نموده و تغییرات را در هر شاخه از کد دنبال کند.

نسخه‌ی پیشرفته‌ی این روش این است که به طور محرمانه، در میان یک حلقه یا یک تابع، مقدار متغیر را با یک مقدار مشابه جایگزین کنیم.

برای نمونه:

function ninjaFunction(elem) {
  // بیست خط از کد که با
  // elem
  // کار می‌کنند

  elem = clone(elem);

  // بیست خط دیگر، که با یک کپی خاص از
  // elem
  // کار می‌کنند
}

یک یار برنامه‌نویس که می‌خواهد با elem در نیمه‌ی دوم تابع کار کند، شگفت‌زده خواهد شد. تنها در طول اشکال‌زدایی، و پس از بررسی کد متوجه خواهد شد که در حال کار با یک کپی خاص از متغیر مورد نظر است.

این امر به دفعات مشاهده شده، و به شدت موثر است، حتی در مقابل یک نینجای باتجربه.

زیرخط‌ها برای سرگرمی

از زیرخط‌های _ و __ قبل از نام متغیرها استفاده کنید، برای مثال _name یا __value. عالی است اگر فقط شما معنی آن‌ها را بدانید. حتی بهتر، آن‌ها را صرفا برای سرگرمی اضافه کنید، بدون این که اصلا معنی خاصی داشته باشند. یا می‌توانند معانی مختلفی در مکان‌های متفاوت داشته باشند.

دو نشان را با یک تیر خواهید زد. اول این که کدتان طولانی‌تر شده و خوانایی آن کمتر خواهد شد، و دوم این که یک یار توسعه‌دهنده ممکن است مدت زمان زیادی را صرف فهمیدن معنی زیرخط‌ها بکند.

یک نینجای زیرک، زیرخط‌ها را در یک مکان از کد قرار داده، و در مکان‌های دیگر از آن‌ها پرهیز می‌کند. این کار، کد را شکننده‌تر می‌کند و احتمال خطاهای آتی را افزایش خواهد داد.

عشق خود را نشان دهید

بگذارید همه ببینند موجودیت‌های شما چقدر باشکوه هستند! نام‌هایی مانند superElement، megaFrame و niceItem قطعا مخاطب را روشن خواهند کرد.

از یک طرف، متغیرهایی به این شکل نام‌گذاری شده‌اند: super..، mega..، nice... ولی از طرف دیگر، هیچ جزئیاتی را نمی‌توان از نامشان استخراج نمود. ممکن است مخاطب به مدت یک یا دو ساعت از زمان کاری خود در جستجوی یک معنی مخفی به مراقبه بپردازد.

متغیرهای بیرونی را مخفی کنید

در روشنایی بایست، چیزی در تاریکی دیده نخواهد شد.
در تاریکی بایست، همه چیز در روشنایی دیده خواهد شد. -- گوان یین زی

از اسامی یکسانی برای متغیرهای درون و بیرون یک تابع استفاده کنید. به همین سادگی. نیازی به تلاش برای اختراع نام‌های جدید نیست.

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ... many lines ...
  ...
  ... // <-- یک برنامه‌نویس می‌خواهد در این‌جا با
  ... //     user
  ... //     کار کند
  ...
}

برنامه‌نویسی که تابع render را می‌خواند احتمالا متوجه نخواهد شد که یک user محلی وجود دارد که بر روی متغیر بیرونی سایه انداخته است.

با فرض این که user یک متغیر خارجی است کار را ادامه می‌دهند. خواهند پنداشت این متغیر، نتیجه‌ی تابع authenticateUser() است. تله پهن شده است. درود بر اشکال‌زدایی.

اثرات جانبی در همه‌جا!

توابعی وجود دارند که در ظاهر، چیزی را تغییر نمی‌دهند. مانند isReady()، checkPermission()، findTags() و ... فرض بر این است که این توابع، محاسباتی را انجام داده، داده‌ای را پیدا کرده و برمی‌گردانند، بدون این که چیزی را در خارج از حوزه‌ی خود تغییر دهند. به عبارت دیگر، این توابع بدون اثرات جانبی هستند.

یک ترفند واقعا زیبا این است که در کنار وظیفه‌ی اصلی توابع، یک عمل مفید دیگر را نیز به آن‌ها اضافه کنیم.

حالت تعجب روی چهره‌ی یارانتان وقتی تابعی را می‌بینند که با is..، check.. یا find... نام‌گذاری شده است و چیزی را هم تغییر می‌دهد، قطعا مرزهای منطق و استدلال شما را پهناورتر خواهد کرد.

یک راه دیگر برای شگفت‌زده کردن، برگرداندن یک نتیجه‌ی غیراستاندارد است.

تفکر اصل خود را نشان دهید! بگذارید فراخوانی checkPermission مقدار true/false را برنگرداند، در عوض یک شی پیچیده با نتایچ بررسی را برگرداند.

توسعه‌دهندگانی که سعی کنند از if (checkPermission(..)) استفاده کنند، حیرت‌زده خواهند شد که چرا این تابع به درستی عمل نمی‌کند. به آن‌ها بگویید: «مستندات را بخوانید!». و این مقاله را به آن‌ها معرفی کنید.

توابع قدرتمند

دائوی بزرگ در همه‌جا جاری است،
در چپ، و در راست. -- لائوتسه (دائو دِ جینگ)

توابع را محدود به چیزی که در نامشان نوشته شده است نکنید. بازتر فکر کنید.

برای نمونه، تابع validateEmail(email) می‌تواند (در کنار بررسی صحت ایمیل) پیام خطایی را نمایش داده و از کاربر بخواهد تا ایمیل را دوباره وارد کند.

اعمال اضافی نباید از روی نام تابع به راحتی برداشت شوند. یک کدنویس نینجای واقعی به گونه‌ای کد خود را می‌نویسد که از روی خود کد هم نتوان اعمال اضافی را تشخیص داد.

ادغام چندین عمل در یکی، از کد شما در مقابل استفاده‌ی مجدد محافظت می‌کند.

تصور کنید، توسعه‌دهنده‌ی دیگری می‌خواهد تنها ایمیل را بررسی کنید، نه این که پیامی را برگرداند. تابع شما، validateEmail(email)، که هر دو کار را با هم انجام می‌دهد به کارش نخواهد آمد. در نتیجه وی، مراقبه‌ی شما را با پرسیدن سوالاتی در مورد آن تابع بر هم نخواهد زد.

خلاصه

تمام نصایح بالا برگرفته از کدهای واقعی هستند. گاهی اوقات، این کدها توسط توسعه‌دهندگان باتجربه نوشته شده‌اند، حتی شاید باتجربه‌تر از شما ;)

نظرات

در حال حاضر، از سرویس Disqus برای نظرات استفاده می‌کنم. متاسفانه توی ایران در دسترس نیست (یا ایران فیلترش کرده یا اونا ما رو فیلتر کردن).

اگه نمی‌دونید چه کاری باید بکنید، یا می‌دونید ولی حوصله ندارید، یا یه پیام خصوصی می‌خواید بفرستید، می‌تونید از ایمیل یا سایر راه‌ها ارتباطی استفاده کنید.

نظرات باید زیر همین متن، بارگذاری بشن. اگه نشدن، ۲ پاراگراف بالا رو بخونید.