8 اشتباه رایج در توسعه وب و نحوه جلوگیری از آنها
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 14 دقیقه

8 اشتباه رایج در توسعه وب و نحوه جلوگیری از آنها

من اخیرا به یک شرکت استارتاپی کوچک پیوستم که در پیاده سازی یک برنامه تحت وب برای یک مطالعه مهم بالینی که بر COVID-19 تمرکز دارد، همکاری می‌کنم. بزرگترین مشکلی که وجود داشت مهلت انجام پروژه در مدت دو هفته بود. اینگونه به نظر می‌رسد که اجرای آن به تنهایی ترسناک و استرس‌زا است، اما من تصمیم گرفتم این چالش را بپذیرم.

علاوه بر مهلت تعیین شده، توسعه دهنده ارشد که مسئولیت اصلی پروژه را عهده دار بود، در حجم عظیمی از کارغرق شده بود. در نتیجه خروجی کد به دلیل اینکه با عجله نوشته شده بود، کاملا خراب شد. کلیت این پروژه برای تیمی متشکل از دو توسعه دهنده برای مدیریت آن در یک بازه زمانی کوتاه غیر قابل انجام بود.

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

اگر پروژه به درستی تنظیم شده باشد و از بهترین متدها استفاده شود، می‌توان از همان ابتدا به راحتی از انجام کارهای اضافی اجتناب كرد.

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

به منظور صرفه جویی در وقت ارزشمندتان برای پروژه‌های توسعه وب خود در آینده، اطمینان حاصل کنید که از این هشت اشتباه رایج در توسعه وب جلوگیری کنید:

1 – در دسترس نبودن ابزارهای کیفی کد

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

هنگامی که من به پروژه ذکر شده در بالا پیوستم، هیچ چیزی تنظیم نشده بود و کد با ترکیب نامنظمی از کوتیشن‌ها، بلوک‌های فاقد ()catch. و مشکلات قالب بندی همراه بود.

ESLint شما را از تولید چنین خطاهایی نجات میدهد که در وهله اول میتوان جلوی آن را گرفت. پس از اجرای یک اسکریپت lint روی پروژه برای اولین بار با پیکربندی در نظر گرفته شده، بیش از 200 اخطار و خطا در انتظار رفع شدن بودند.

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

توصیه من این است که بسته به نیاز خود از همه یا برخی از این پکیج‌ها برای پیکربندی استفاده کنید:

  • eslint یا typescript-eslint@ برای تنظیم قوانین پایهای
  • eslint-plugin-import برای ایمپورت تمیز و مرتب
  • eslint-plugin-jest برای انجام تست‌های بهتر و دقیق‌تر
  • eslint-plugin-node برای توسعه بک-اند و بررسی ویژگی‌های نسخه پشتیبانی شده
  • eslint-plugin-premium برای جلوگیری از از دست رفتن بلوک‌های ()catch. و سایر اقدامات بد هنگام کار با کد ناهمزمان
  • eslint-plugin-jsx-a11y برای نوشتن کد قابل دسترسی در صورت استفاده از React
  • eslint-plugin-unicorn برای قوانین مفید متفرقه

قبل از پیکربندی‌های پیشنهادی که به شما قوانین پایه را ارائه می‌دهند، من قوانین اضافی مانندeqeqeq ،prefer-template ، prefer-const و no-var را اضافه می‌کنم که در پیکربندی توصیه شده وجود ندارد.

به غیر از اجتناب از اشکالات ناخوشایند و نوشتن کد نامنظم، شما می‌توانید با پیروی از پیشنهادات lint و بررسی مستندات ESLint در مورد دلیل وجود یک قانون خاص و ضرورت آن، دانش زیادی کسب کنید.

از طرف دیگر، Prettier اطمینان حاصل می‌کند که کل تیم با همان دستورالعمل‌های قالب‌بندی مطابقت دارند و خوانایی به دست آمده نیز در وقت شما صرفه جویی می‌کند. تنظیمات پیش فرض پیکربندی ارائه شده توسط prettier بسیار عالی است، بنابراین من فقط باید تنظیمات جزئی را انجام دهم. این یک فایل پیکربندی prettierrc.json. است که تمایل دارم با آن شروع کنم:

{
  "printWidth": 100, // default is 80
  "singleQuote": true, // default is false
  "trailingComma": "all" // default is "es5"
}

پس از راه‌اندازی ESLint و Prettier، یک پایه اساسی از ابزارهای کیفیت کد در دسترس دارید که تجربه توسعه شما را بسیار بهبود می‌بخشد.

2 - استفاده از وابستگی‌های به روز نشده

پکیج‌های مختلف استفاده شده در پروژه شما نسخه‌های متعددی را پشت سر می‌گذارند. وابستگی‌های pack.json بیش از یک سال است که ارتقا نیافته اند. شما می‌توانید به روزرسانی را به تأخیر بیندازید و امیدوار باشید که هرگز مجبور به انجام آن نخواهید شد. اما باور کنید به محض کاهش پشتیبانی در نسخه قدیمی Node.js، آسیب پذیری جدیدی در وابستگی‌های به روز نشده که از آن استفاده می‌کنید، کشف خواهد شد. علاوه بر این ممکن است دوست داشته باشید از جدیدترین ویژگی‌های کتابخانه استفاده کنید، اما به دلیل وابستگی به یک نسخه قدیمی نمی‌توانید این کار را انجام دهید.

هر زمان که من یک پروژه جدید را استارت می‌زنم، یکی از اولین کارهایی که انجام می‌دهم بررسی pack.json برای وابستگی‌های قدیمی است. پس اطمینان حاصل کنید که این وابستگی‌ها تا حدودی به روز هستند تا اشکالات احتمالی و آسیب پذیری‌های امنیتی در کتابخانه‌های شما برطرف شود.

من شخصا در پروژه‌هایی که روی آنها کار می‌کنم، یک فایل packed.md اختصاصی ایجاد می‌کنم که به شکل زیر است:

# Dependency upgrade issues

## "postcss-cli": "^7.1.2"

Major version 8 requires postcss as peer dependency, leads to breakage when running development

## "sapper": "0.28.0"

Keep locked until missing CSS issues are fixed in v0.28.1

به این ترتیب هر یک از همکاران پروژه در مورد موضوعات شناخته شده در ارتقای وابستگی مطلع می‌شوند.

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

3 - نوشتن کامنت و نام متغیرها به زبانی غیر از انگلیسی

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

در پروژه MVP، موجودیت‌هایی که از طریق MongoDB در برنامه نویسی Node.js به دست می‌آمدند، برخی از فیلدها به آلمانی و برخی دیگر به انگلیسی نام گذاری شده بودند، در حالی که بیشتر از انگلیسی استفاده می‌شد. این امر مستلزم نگاشت غیرضروری فراوان از یک کنوانسیون نام گذاری به دیگری است. همچنین استفاده از مختصرنویسی امکان‌پذیر نبود و به راحتی می‌شد فراموش کرد که کدام فیلد مربوط به کدام مورد است. علاوه بر آن هر توسعه دهنده‌ای که ممکن بود به تیم بپیوندد که زبان مادریش آلمانی نبود، در درک استفاده از هر فیلد به مشکل برمی‌خورد.

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

4 - قراردادهای مختلف نام گذاری در کل پروژه

سعی کنید از مخلوط کردن نام گذاری‌های مختلف در HTML ، CSS و JavaScript خودداری کنید. مثلا از kebab-case و snake_case و camelCase در پایه کد استفاده نکنید وگرنه به سرعت گیج می‌شوید و بهره وری خود را از دست می‌دهید.

درباره کنوانسیون‌های مختلف نام گذاری و دلیل وجود آن‌ها بیاموزید. به شما توصیه می‌کنم که به قوانین برنامه نویسی زبانی که استفاده می‌کنید پایبند باشید. متدهای بومی جاوااسکریپت مانند ()toLowerCase. با camelCase نوشته شده‌اند، پس چرا متغیرهای خود را با روشهای مختلف می‌نویسید؟ در حالی که جاوااسکریپت از camelCase استفاده می‌کند. به یاد داشته باشید که از kebab-case برای نامگذاری در HTML و استایل‌های CSS خود استفاده کنید.

5 - استفاده از نام متغیرهای بی معنی

من مطمئنم که قبلا کدی مشابه زیر مشاهده کرده‌اید:

const x = 'Gabriel';

const stuff = x.map((y) => `Hello, ${y}!`);

چه مقادیری در اینجا ذخیره می‌شود؟ آیا گابریل نام شخصی است؟ x چیست که تعریف شده است؟ ممکن است یک آرایه باشد؟ چه چیزی متغیر را نگه می‌دارد؟

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

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

بیایید نگاهی به یک مثال خوب بیندازیم:

// The variable name `firstName` clearly shows the intent of the stored value
const firstName = 'Gabriel';

/**
 * The variable `students` is in plural, so it is probably an array.
 * The value `student` is most likely an object that we are
 * mapping over.
 * We seem to collect `greetings` of `students` here as a result.
 */
const greetings = students.map((student) => `Hello, ${student.firstName}!`);

در اینجا می‌توانیم خیلی بیشتر به اصول نامگذاری و وضوح متغیرها پی ببریم که به معنای سربار شناختی کمتر برای توسعه دهنده است.

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

6 – رها کردن console.log و پراکندگی کد در کل پروژه

این امر به طور همزمان هم برای توسعه دهنده و هم تجربه کاربر مضر است.

console.log('Hello from indexing function');

console.log('Result', result.data);

// TODO: Why does this even work?

// TODO: Add error handling

ترک پیام‌های ()console.log در ابزارهای توسعه برای هر کاربر خجالت‌آور و غیرحرفه‌ای به نظر می‌رسد.

من توصیه می‌کنم از قانون بدون کنسول ESLint استفاده کرده و در صورت لزوم آن را پیکربندی کنید. به همین منظور تمایل دارم که ()console.log را به عنوان یک خطا علامت گذاری کرده و از آن در اتصال قلاب‌های pre-commit در lint برای جلوگیری از کامیت‌های اشتباه استفاده کنم. هنگامی که می‌خواهید اطلاعات مربوط به ورود را ادامه دهید، می‌توانید از ()console.info برای نشان دادن نیاز به خروجی اطلاعات در آن نقطه استفاده کنید.

اگر قادر نیستید از ورود به سیستم کنسول خود صرف نظر کنید، می‌توانید افزونه‌ای مانند babel-plugin-transform-remove-console یا terser-webpack-plugin را انتخاب کنید تا پیام‌های کنسول را برای شما براساس محیط تنظیم کند.

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

7 - ترکیب async/await،promise  ها و سینتکس فراخوانی مجدد

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

بیایید نگاهی به نمونه‌ای از پروژه واقعی MVP بیندازیم:

export const saveLogAuthToken = async (token) => {
  const jwtToken = jwt.verify(token, JWT_SECRET);

  if (!jwtToken) return false;

  const logoutToken = new logAuthToken({ token, expires: jwtToken.exp });

  await logoutToken.save().catch((err) => {
    console.log(err);
  });

  return true;
};

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

مثال کد بالا نبودن دانش کافی در مورد نحوه اجرای async/await را نشان می‌دهد. کد با استفاده از async/await شروع می‌شود که برای نوشتن کد خوانا و مختصر بسیار مناسب است، اما سپس نامشخص می‌شود:

  • چه زمانی تابع به درستی برمی‌گردد؟
  • وقتی وارد بلوک ()catch. در متد ()logoutToken.save می‌شویم، چه چیزی را برمی‌گرداند؟

با چند تغییر ساده می‌توان جریان کد را به شدت بهبود بخشید:

  • برای جلوگیری از پیام معروف UnhandledPromiseRejectionWarning در Node.js، کد باید در بلوک try/catch قرار گیرد.
  • بلوک ()catch. را در ()logoutToken.save حذف کنید، زیرا خطاها در عبارت catch بلوک try/catch گرفتار می‌شوند.
  • از async/await یا از سینتکس Promise ها استفاده کنید. همچنین این می‌تواند ایده خوبی باشد که در صورت عدم موفقیت ()jwt.verify، نه تنها بازگشت نادرست را در نظر بگیریم، بلکه به جای آن به صراحت خطایی ایجاد کنیم.

این اشتباهات در طراحی کد می‌تواند کشنده باشد، به خصوص زمانی که هیچ آزمایشی برای قطعه کد انجام نشده باشد.

8 – عدم اجرای تست نهایی

این کار در بین جامعه توسعه وب بسیار رایج است. من هنوز به یاد دارم که در اولین کارم، تست واحد صفر برای پروژه نوشته شده بود. وقتی در مورد آن پرس و جو کردم همه گفتند: "داشتن یک تست آزمایشی بسیار خوب است، اما وقت کافی برای انجام آن وجود ندارد!"

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

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

برای یک وظیفه که مجبور به انجام آن شدم، موارد بسیار بالقوه زیادی وجود داشت. بنابراین من شروع به استفاده از توسعه آزمون محور (TDD) کردم و تست‌ها را قبل از کد واقعی می‌نوشتم. اگرچه مجبور شدم علاوه بر منطق تجارت، تست‌ها را نیز بنویسم، اما به دلیل داشتن "کمربند ایمنی" در تست‌های واحدی که تمام خطاهای احتمالی و موارد مشابه را دربرداشت، سرانجام مسئله را حدود 30٪ سریعتر تمام کردم. تست پوششی از بروز اشکالات در بخش‌های مختلف نیز جلوگیری خواهد کرد.

خط پایین: در صورت امکان تست‌های واحدی را بنویسید، مخصوصا برای قطعه‌های پیچیده کد و حداقل برای قسمت‌های مهم یک برنامه از تست‌های end-to-end استفاده کنید.

جمع‌بندی

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

حداقل با این کار در تکثیر قطعه کد و هک شدن آن جلوگیری می‌کنید.

نوشتن تمیزترین کد همیشه امکان‌پذیر نیست، اما اجازه ندهید این باعث دلسردی شما شود. بالاخره ما انسان هستیم و انسان هم جایزالخطا است.

آیا قبلا مرتکب این اشتباهات یا اشتباهات مشابه شده‌اید؟ نظرات خود را در بخش زیر با ما در میان بگذارید.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@heshmati74
عرفان حشمتی
Full-Stack Web Developer

کارشناس معماری سیستم های کامپیوتری، طراح و توسعه دهنده وب سایت

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

عرفان حشمتی

Full-Stack Web Developer