لینک کوتاه مطلب : https://hsgar.com/?p=5453

تولید کد ML در مقابل کد نویسی با دست – ما فکر می کنیم برنامه نویسی چگونه خواهد بود

ما در حال کار بر روی یک زبان پیکربندی / DSL برای ساخت برنامه های وب هستیم که با React & Node.js ادغام می شود. چندین بار از ما پرسیده شده استچرا زحمت ایجاد یک زبان جدید برای توسعه اپلیکیشن وب را به زحمت می اندازید؟ آیا Github Copilot* به زودی همه کدها را برای توسعه دهندگان تولید نمی کند؟“.

این به برداشت ما از وضعیت و آنچه فکر می کنیم ممکن است در آینده به نظر برسد، است.

#

به منظور توسعه سریعتر، ما با تکمیل خودکار IDE آمدیم – به عنوان مثال اگر از React استفاده می کنید و شروع به تایپ می کنید componentDid، IDE به طور خودکار آن را تکمیل می کند componentDidMount() یا componentDidLoad(). علاوه بر صرفه جویی در فشار دادن کلید، شاید حتی ارزشمندتر این باشد که بتوانیم ببینیم چه روش‌ها/ویژگی‌هایی در محدوده فعلی در دسترس ما هستند. آگاهی IDE از ساختار پروژه و سلسله مراتب کد نیز بازسازی را بسیار ساده تر می کند.

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

اگر فقط راهی برای کامپیوتر وجود داشت که بتواند تمام کدهایی را که تا به حال نوشته ایم تجزیه و تحلیل کند و به تنهایی یاد بگیرد که چگونه کد ما را به صورت خودکار تکمیل کند و به طور کلی در مورد انسانیت چه کاری انجام دهد، به جای اینکه ما تمام کارهای سخت را انجام دهیم …

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

GitHub Copilot یک بدنه عملکرد کامل را بر اساس امضای خود و نظرات بالای آن ایجاد می کند.

این بسیار شگفت انگیز است! مثال بالا توسط Github Copilot – اساساً یک شبکه عصبی است که بر روی مقدار زیادی کد در دسترس عموم آموزش داده شده است. من وارد جزئیات فنی نحوه عملکرد زیر کاپوت نمی شوم، اما وجود دارد مقدار زیادی عالی مقالات پوشش علم پشت آن

با دیدن این، سؤالاتی مطرح می شود – این برای آینده برنامه نویسی چه معنایی دارد? آیا این فقط تکمیل خودکار IDE در استروئیدها است یا چیزی بیشتر؟ آیا باید به درد نوشتن دستی کد بپردازیم، اگر فقط بتوانیم آنچه را که می خواهیم در نظرات تایپ کنیم و تمام؟

#

هنگامی که به این فکر می کنیم که چگونه تولید کد ML بر روند کلی توسعه تأثیر می گذارد، باید یک چیز را در نظر گرفت که اغلب با نگاه کردن به نمونه های چشمگیر Copilot بلافاصله به ذهن خطور نمی کند.

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

سوال این است که پس از تولید کد چه اتفاقی می افتد؟ چه کسی مسئول آن است و چه کسی آن را در آینده حفظ و بازسازی خواهد کرد؟

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

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

تصور کنید تمام چیزی که ما داشتیم یک زبان اسمبلی بود، اما تکمیل IDE واقعاً برای آن کار کرد، و می‌توانید بگویید «یک تابع را که یک آرایه را مرتب می‌کند، صعودی پیاده‌سازی کنید» و کد مورد نیاز را کاملاً تولید می‌کند. آیا زمانی که نیاز به تغییر نوع خود به نزولی داشتید، هنوز هم دوست دارید در آینده به آن بازگردید؟

به عبارت دیگر، به این معنی است که Copilot و راه حل های مشابه پیچیدگی کد و میزان دانش مورد نیاز برای ساخت ویژگی ها را کاهش نمی دهند، آنها فقط به نوشتن سریعتر کد اولیه کمک می کنند و دانش/مثال ها را به کد نزدیک می کنند (که واقعا مفید است. ). اگر یک توسعه‌دهنده کورکورانه کد تولید شده را بپذیرد، فقط بدهی فناوری ایجاد کرده و آن را به جلو می‌برد.

#

اگر Github Copilot و دیگران نتوانند همه مشکلات ما در یادگیری نحوه کدنویسی و درک جزئیات نحوه عملکرد مدیریت جلسه از طریق JWT را حل کنند، چه چیزی می تواند؟

انتزاع – اینگونه است که برنامه نویسان برای چندین دهه با تکرار کد و کاهش پیچیدگی برخورد کرده اند – با ایجاد کتابخانه ها، چارچوب ها و زبان ها. به این ترتیب است که ما از وانیلی JS و دستکاری مستقیم DOM به jQuery و در نهایت به کتابخانه های UI مانند React و Vue پیشرفت کردیم.

معرفی انتزاعات به طور اجتناب ناپذیری به معنای کنار گذاشتن مقدار مشخصی از قدرت و انعطاف پذیری است (مثلاً هنگام جمع اعداد در پایتون شما نمی توانید دقیقاً مشخص کنید که از کدام ثبات های CPU قرار است برای آن استفاده شود)، اما نکته این است که اگر به درستی انجام شود ، در اکثر موارد به چنین قدرتی نیاز ندارید و نمی خواهید.

انتزاع مساوی است با مسئولیت کمتر
منظور عمو بن در واقع: اجتناب از مسئولیت، فایده اصلی انتزاع است! (متاسفانه پیتر کاملاً از این موضوع غافل شد و به جای یادگیری نحوه کدنویسی تبدیل به مرد عنکبوتی شد)

تنها راه عدم مسئولیت در قبال یک قطعه کد این است که در وهله اول وجود نداشته باشد.

زیرا به محض اینکه پیکسل های روی صفحه رنگ خود را تغییر می دهند، چیزی است که باید نگران آن باشید و به همین دلیل است که مزیت اصلی همه فریمورک ها، زبان ها و غیره است. کد کمتر == تصمیمات کمتر == مسئولیت کمتر

تنها راه برای داشتن کد کمتر، تصمیم گیری کمتر و ارائه جزئیات کمتر به کامپیوتر در مورد نحوه انجام یک کار خاص است – در حالت ایده آل، ما فقط آنچه را که می خواهیم بیان می کنیم و حتی به نحوه انجام آن اهمیت نمی دهیم. تا زمانی که در محدوده زمانی/حافظه/هزینه ای است که ما داریم (بنابراین ممکن است لازم باشد آن ها را نیز بیان کنیم).

بیایید نگاهی به ویژگی بسیار رایج (و مورد علاقه همه) در دنیای برنامه های وب – احراز هویت (aay ☠️ 🔫) بیندازیم! کد معمولی برای آن چیزی شبیه به این خواهد بود:

Auth در باطن در Node.js – مثال

import jwt from 'jsonwebtoken'import SecurePassword from 'secure-password'import util from 'util'
import prisma from '../dbClient.js'import { handleRejection } from '../utils.js'import config from '../config.js'
const jwtSign = util.promisify(jwt.sign)const jwtVerify = util.promisify(jwt.verify)
const JWT_SECRET = config.auth.jwtSecret
export const sign = (id, options) => jwtSign({ id }, JWT_SECRET, options)export const verify = (token) => jwtVerify(token, JWT_SECRET)
const auth = handleRejection(async (req, res, next) => {  const authHeader = req.get('Authorization')  if (!authHeader) {    return next()  }
  if (authHeader.startsWith('Bearer ')) {    const token = authHeader.substring(7, authHeader.length)
    let userIdFromToken    try {      userIdFromToken = (await verify(token)).id    } catch (error) {      if (['TokenExpiredError', 'JsonWebTokenError', 'NotBeforeError'].includes(error.name)) {        return res.status(401).send()      } else {        throw error      }    }
    const user = await prisma.user.findUnique({ where: { id: userIdFromToken } })    if (!user) {      return res.status(401).send()    }
    const { password, ...userView } = user
    req.user = userView  } else {    return res.status(401).send()  }
  next()})
const SP = new SecurePassword()
export const hashPassword = async (password) => {  const hashedPwdBuffer = await SP.hash(Buffer.from(password))  return hashedPwdBuffer.toString("base64")}
export const verifyPassword = async (hashedPassword, password) => {  try {    return await SP.verify(Buffer.from(password), Buffer.from(hashedPassword, "base64"))  } catch (error) {    console.error(error)    return false  }}

و این فقط بخشی از کد باطن است (و فقط برای روش ایمیل و رمز عبور)! همانطور که می بینید، ما در اینجا انعطاف پذیری بسیار زیادی داریم و کارهایی مانند:

  • روش پیاده سازی را برای احراز هویت انتخاب کنید (به عنوان مثال جلسه یا مبتنی بر JWT)
  • بسته‌های دقیق npm را انتخاب کنید که می‌خواهیم برای توکن (اگر با JWT) و مدیریت رمز عبور استفاده کنیم
  • هدر auth را تجزیه کنید و برای هر مقدار مشخص کنید (Authorization، Bearer، …) چگونه پاسخ دهیم
  • کد بازگشتی (به عنوان مثال 401، 403) را برای هر نتیجه ممکن انتخاب کنید
  • نحوه رمزگشایی/کدگذاری رمز عبور را انتخاب کنید (base64)

از یک طرف، داشتن چنین سطحی از کنترل و انعطاف پذیری در کد ما واقعاً جالب است، اما از طرف دیگر، تصمیمات (== اشتباهات) بسیار زیادی باید گرفته شود، به خصوص برای چیزی معمولی مانند احراز هویت!

اگر بعداً کسی بپرسدپس چرا دقیقاً بسته npm با رمز عبور امن را انتخاب کردید یا چرا دقیقاً کدگذاری base64 را انتخاب کردید؟“این چیزی است که ما احتمالا باید با چیز دیگری به جای” پاسخ دهیمخوب، آن پست SO از سال 2012 وجود داشت که کاملاً قانونی به نظر می رسید، تقریباً 50 رأی مثبت داشت. هوم، اکنون نمی توانم آن را پیدا کنم. به‌علاوه، نام آن «امن» است، که خوب به نظر می‌رسد، درست است؟

نکته دیگری که باید در نظر داشته باشید این است که ما همچنین باید نحوه تغییر همه چیز را در طول زمان پیگیری کنیم و مطمئن شویم که پس از گذشت چند سال هنوز از بهترین روش‌ها استفاده می‌کنیم و بسته‌ها به طور مرتب به‌روزرسانی می‌شوند.

اگر سعی کنیم اصول را از بالا اعمال کنیم (کد کمتر، دستورالعمل های کمتر با جزئیات، بیان شده است چی ما می خواهیم به جای چگونه باید انجام شود)، کد auth ممکن است چیزی شبیه به این باشد:

auth: {    userEntity: User,    methods: [ EmailAndPassword, LinkedIn, Google ],    onAuthFailedRedirectTo: "/login",    onAuthSucceededRedirectTo: "/dashboard"  }

بر این اساس، رایانه/کامپایلر می‌تواند از تمام موارد ذکر شده در بالا مراقبت کند، و سپس بسته به سطح انتزاع، نوعی رابط (مثلاً اجزای فرم یا توابع) را برای «قلاب کردن» با React خودمان فراهم کند. کد /Node.js (btw این نحوه عمل در Wasp است).

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

#

نگران نباشید، همه چیز هنوز اینجاست و می توانید تمام کدهایی را که می خواهید ایجاد کنید! نکته اصلی که در اینجا باید درک کرد این است که تولید کد ML و توسعه چارچوب/زبان به جای جایگزینی یکدیگر، مکمل یکدیگر هستند و اینجا باقی می مانند، که در نهایت یک پیروزی بزرگ برای جامعه توسعه دهندگان است – آنها زندگی ما را آسان تر می کنند و به ما اجازه می دهند کارهای سرگرم کننده بیشتری انجام دهید (به جای اجرای auth یا CRUD API برای نومین بار)!

من تکامل را در اینجا به عنوان یک چرخه می بینم (یا در واقع یک مارپیچ رو به بالا، اما این فراتر از توانایی های ترسیم من است):

  1. زبان/چارچوب وجود دارد، جریان اصلی است و افراد زیادی از آن استفاده می کنند
  2. الگوها شروع به ظهور می کنند (مثلاً اجرای احراز هویت یا برقراری تماس API) → ML آنها را ضبط می کند، از طریق تکمیل خودکار پیشنهاد می دهد
  3. برخی از آن الگوها بالغ می شوند و پایدار شوند ← نامزدهای انتزاع
  4. زبان/چارچوب جدید، انتزاعی تر پدیدار می شود
  5. بازگشت به مرحله 1.
تولید کد ML در مقابل کد نویسی با دست – ما فکر می کنیم برنامه نویسی چگونه خواهد بود
این دایره زندگی (زبان) است و همه ما را به حرکت در می آورد – Ingonyama nengw’ enamebala،…

#

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

Fizz Buzz with Copilot - توقف
آینده اکنون است، پیرمرد.

*برای تعصب، راه حل های دیگری نیز وجود دارند که عملکردهای مشابهی را ارائه می دهند – به عنوان مثال TabNine، Webstorm خودش را دارد، بادبادک، Clippy کد GPT (تلاش OSS) و همکاران، اما Github Copilot اخیراً بزرگترین سروصدا را ایجاد کرده است.

#

#

جرمی هاوارد، ماکسی کانتیری، ماریو کوستلاک، ولادیمیر بلاگویویچ، Ido نوامبر، کریستیان صفجان، کلوین را مورد لطف قرار دهید، فیلیپ سودیک، شاین سیژوسکی و مارتین سوسیچ – از نظرات، ایده ها و پیشنهادات شما متشکرم! این پست رو بهتر کردی و مطمئن شدی که از میم ها زیاده روی نکنم :).



لینک منبع

ارسال یک پاسخ

آدرس ایمیل شما منتشر نخواهد شد.