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

ثبثبثب.gif

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

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

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

هنگامی که کار روی پروژه جدیدی را آغاز می‌کنید، این مورد همیشه باید اولویت اصلی شما باشد. مطمئن شوید ابزارهای کیفیت کد بر اساس نیازهای پروژه در دسترس قرار دارند. هنگامی که به پروژه مذکور پیوستم، هیچ ابزاری در دسترس نبود و کدها با ترکیب نامنظمی از کوتیشن‌ها و بلوک‌های فاقد متد استثناء‌ها (catch) نوشته شده بودند و متاسفانه قالب‌بندی با مشکلات زیادی همراه بود. ESLint یک ابزار جادویی در دسترس توسعه‌دهندگان است. ابزار فوق با ارزیابی کدها مانع از آن می‌شود تا مشکلات این‌چنینی به وجود آیند. پس از اجرای یک اسکریپت lint روی پروژه برای اولین مرتبه بر مبنای پیکربندی‌های اعمال شده، بیش از 200 هشدار دریافت کردم که باید به وضعیت آن‌ها رسیدگی می‌شد. به خوبی متوجه هستم که انجام این تنظیمات مطابق با آن‌چه ابزارهای کیفی پیشنهاد می‌کنند کار دشواری است، اما صاحب یک پروژه به دنبال مشاهده نتایج واقعی است و برایش اهمیتی ندارد چه مدت زمانی را صرف پیکربندی ابزارهای توسعه می‌کنید. با این‌حال، سرمایه‌گذاری روی این موضوع در درازمدت دستاوردهای مثبتی دارد و نباید از آن غافل شوید، زیرا در انتها پروژه‌ای تمیز و عاری از خطا دارید که سایر توسعه‌دهندگان با نگاه کردن به کدها، عملکرد آن‌ها را درک می‌کنند. علاوه بر این، زمانی که از ابزارهای این چنینی استفاده کنید بهره‌وری‌تان بیشتر می‌شود. پیشنهاد من این است که متناسب با نیاز کاری از همه یا برخی از این بسته‌ها برای پیکربندی استفاده کنید:

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

قبل از پیکربندی تنظیماتی که ابزارهای کنترل کیفیت پیشنهاد می‌کنند، من قواعد اضافه دیگری شبیه به eqeqeq، prefer-template، prefer-const و no-var را اضافه می‌کنم که بیشتر ابزارها به این پیکربندی‌ها اشاره‌ای نمی‌کنند. برای آن‌که درگیر مشکلات ناشناخته و کدهای نامنظم نشوید، بهتر است از پیشنهاداتی که lint ارائه می‌کند و مواردی که در مستندات ESLint ارائه شده استفاده کنید. مستندات ارائه شده توضیحاتی در ارتباط با برخی قواعد و ضرورت رعایت آن‌ها ارائه می‌کنند. ابزار قدرتمند دیگری که پیشنهاد می‌کنم از آن غافل نشوید Prettier است. Prettier  یک قالب‌ساز کد قدرتمند است که از بیشتر زبان‌ها و محیط‌های توسعه پشتیبانی می‌کند و گزینه‌های کاربردی خوبی در اختیار توسعه‌دهندگان قرار می‌دهد. Prettier به شما تضمین می‌دهد کل تیم بر مبنای دستورالعمل‌های قالب‌بندی تعیین شده کدنویسی می‌کنند. رویکرد فوق باعث می‌شود تا کدهای یک‌دست و خوانایی داشته باشید و در زمان بروز مشکل به سرعت علت بروز خطا را پیدا کنید. تنظیمات پیش‌فرض پیکربندی ارائه شده توسط prettier عالی هستند، بر همین اساس تنها باید برخی تنظیمات جزیی را تغییر دهید. یک فایل پیکربندی کوچک .prettierrc.json که شاید علاقه‌مند به آزمایش آن باشید ترکیب نحوی این چنینی دارد:

{

  “printWidth”: 100, // default is 80

  “singleQuote”: true, // default is false

  “trailingComma”: “all” // default is “es5”

}

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

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

پکیج‌های مختلفی که در پروژه خود از آن‌ها استفاده می‌کنید باعث می‌شوند نگارش‌های مختلفی از بسته‌ها در پروژه شما قرار بگیرند. وابستگی‌های 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

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

شماره سه، نوشتن توضیحات و نام متغیرها به زبان غیر انگلیسی

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

شماره چهار، قراردادهای مختلف نام‌گذاری کل پروژه

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

شماره پنج، به‌کارگیری نام متغیرهای بدون معنی

بدون تردید کدهایی شبیه به ترکیب نحوی زیر را زیاد دیده‌اید یا از آن‌ها استفاده کرده‌اید:

const x = ‘Gabriel’;

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

چه مقادیری باید در این فیلدها ذخیره شوند، آیا گابریل نام شخصی است، x چیست که این‌گونه ترسیم شده است، ممکن است یک آرایه باشد و متغیر stuff قرار است چه چیزی را نگه‌داری کند؟ ضرورتی ندارد برای شناسایی و رمزگشایی آ‌ن‌چه شما و دیگران نوشته‌اید، وقت زیادی هدر دهید، به جای این‌کار بر رفع مشکلات و پیاده‌سازی ویژگی‌های جدید تمرکز کنید. برخی توسعه‌دهندگان بر این باور هستند که نوشتن نام و عبارات متغیر کوتاه جذاب هستند، اما این‌گونه نیست. برنامه‌نویسی به معنای نوشتن حروف کمتر نیست، بلکه مهم منطق تجاری است که فهم آن ساده‌تر و ارزشمندتر باشد و پس از نگارش نیازی به اصلاح مجدد نداشته باشد. اجازه دهید به مثال خوبی در این زمینه اشاره کنیم:

// 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}!`);

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

شماره شش، رها کردن 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  (https://eslint.org/docs/rules/no-console) استفاده کنید و در صورت لزوم، این پیام‌ها را پیکربندی کنید. در سمت توسعه‌دهندگان، پیدا کردن مواردی که باید تغییر کنند، بدون آن‌که اطلاعات دقیقی وجود باشد یا console.log پیکربندی نشده باشد، تنها باعث سردرگمی آن‌ها می‌شود. من در پروژه‌های شخصی تمایل دارم که console.log() را به عنوان یک خطا علامت‌گذاری کنم و از آن در اتصال قلاب‌های pre-commit در lint-staged برای پیشگیری از بروز کامیت‌های اشتباه استفاده کنم. هنگامی که در نظر دارید اطلاعات مربوط به ورود را نگه‌داری کنید، می‌توانید از soncole.info() برای نشان دادن این‌که نیازمند اطلاعات خروجی هستید استفاده کنید. اگر تمایل دارید از گزارش‌های کنسول استفاده کنید، بهتر است از افزونه babel-plugin-transform-remove-console or terser-webpack-plugin به نشانی https://webpack.js.org/plugins/terser-webpack-plugin/ استفاده کنید که پیام‌های کنسول را بر مبنای محیط شما تنظیم می‌کند. بهتر است، پروژه‌ها در ابزارهای مدیریت مخازن به شکل جداگانه نگه‌داری شوند و اطلاعات کافی در اختیار توسعه‌دهندگان قرار بگیرد تا بدون نیاز به تعامل با شما قادر به کار روی پروژه باشند. علاوه بر این، هنگام بروز مشکلات، برنامه‌نویسان به سرعت از این موضوع مطلع می‌شوند. 

شماره هفت، ترکیب async/await، promises و ترکیب نحوی فراخوانی بازگشتی

اشتباهات کدنویسی سهوی به ویژه در زمان کدنویسی غیرهمزمان باعث بروز مشکلات مختلفی می‌شوند که شناسایی آن‌ها دشوار است. بنابراین مطمئن شوید که هر بار از الگوی مشخص (پارادایم) استفاده کرده‌اید. برای درک بهتر موضوع به ترکیب نحوی زیر دقت کنید:

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;

};

توسعه‌دهندگان مجرب در درک این موضوع که قطعه کد مذکور قرار است چه نتایج مختلفی را ارائه کند با مشکل روبرو می‌شوند. قطعه کد فوق به وضوح نشان می‌دهد که توسعه‌دهنده دانش کافی در مورد نحوه اجرای async.await نداشته است. قطعه کد فوق با async/await آغاز می‌شود که برای نوشتن کد خوانا و مختصر مناسب است، اما در ادامه همه چیز مبهم می‌شود. به‌طور مثال، چه زمانی تابع مقدار true را بر می‌گرداند. وقتی وارد بلوک catch() در متد logout.Token.save() می‌شویم، قرار است چه چیزی برگردانده شود؟

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

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

اشتباهات این چنینی در کدنویسی مهلک هستند، به ویژه زمانی که هیچ آزمایشی روی قطعه کدها انجام نشده باشد. 

شماره هشت، عدم اجرای آزمایش نهایی

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

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

(Test Coverage) مانع بروز مشکلات در بخش‌های مختلف یا نمایش پیغام‌های خطای ناگهانی می‌شود.

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

کلام آخر

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

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

ثبت اشتراک نسخه کاغذی ماهنامه شبکه     
ثبت اشتراک نسخه آنلاین

 

کتاب الکترونیک +Network راهنمای شبکه‌ها

  • برای دانلود تنها کتاب کامل ترجمه فارسی +Network  اینجا  کلیک کنید.

کتاب الکترونیک دوره مقدماتی آموزش پایتون

  • اگر قصد یادگیری برنامه‌نویسی را دارید ولی هیچ پیش‌زمینه‌ای ندارید اینجا کلیک کنید.

ایسوس

نظر شما چیست؟