چگونه از ابتدا یک سیستم توسعه پذیر با بدهی فنی کمتر بسازیم

آیا تا به حال محصولی را از ابتدا ساخته اید؟ اگر چنین است، شرط می بندم که شما قطعاً تطابق بین کیفیت طراحی و زمان عرضه به بازار را تجربه کرده اید. در واقع، ممکن است مجبور شوید بیش از آنچه انتظار داشتید با آن مبارزه کنید. در تمرین Shopify Deconstructing the Monolith: طراحی نرم افزاری که بهره وری توسعه دهندگان را به حداکثر می رساند، آنها نتیجه زیر را دریافت می کنند:
در نتیجه، هیچ معماری اغلب بهترین معماری در روزهای اولیه یک سیستم نیست. این بدان معنا نیست که شیوههای نرمافزاری خوب را پیادهسازی نکنید، اما هفتهها و ماهها را صرف تلاش برای معماری سیستم پیچیدهای نکنید که هنوز نمیشناسید. فرضیه استقامت طراحی مارتین فاولر کار بسیار خوبی برای نشان دادن این ایده انجام می دهد، با توضیح این که در مراحل اولیه اکثر برنامه ها، می توانید با طراحی کم خیلی سریع حرکت کنید. این عملی است که کیفیت طراحی را با زمان عرضه به بازار عوض کنید. هنگامی که سرعت اضافه کردن ویژگی ها و عملکردها شروع به کاهش می کند، زمان آن است که روی طراحی خوب سرمایه گذاری کنید.
فرضیه استقامت طراحی مارتین فاولر، تصویر خوبی از این موضوع است. اما این سوال را مطرح می کند که کجا خط بازده طراحی است. همانطور که مارتین قضاوت می کند، معمولاً بسیار کمتر از آن چیزی است که اکثر مردم فکر می کنند. پس پیامد آن معروف است بدهی فنی باید پرداخت کنید
بدهی فنی چگونه اتفاق می افتد
با فرض اینکه در حال ساخت یک ابزار مدیریت توسعه SaaS هستیم، بیایید بگوییم که با مدیریت اشکال شروع کردیم و مشتریان را با موفقیت روبرو کرد. سپس یک شرکت بزرگ به ما مراجعه کرد و گفت اگر بتوانیم مدیریت وظیفه را در یک بازه زمانی مشخص ارائه دهیم، محصول ما را قبول می کنند. زمان محدود است، بنابراین ما باید آن را در اسرع وقت توسعه دهیم. در اینجا بذر بدهی فنی می آید:
1. تکرار کد
تعجب آور نیست که توسعه دهندگان انتخاب می کنند کد را از مدیریت اشکال کپی و جایگذاری کنند و آن را تغییر دهند زیرا این سریع ترین و ساده ترین راه برای انجام این کار است. این منجر به عواقب تکرار کد می شود، اگرچه هنوز نیازی به پرداخت بدهی ندارید.
2. تغییر دادن مشکل است
محصول ما موفقیت زیادی در بازار به دست می آورد و ویژگی های بیشتری مانند OKR، Scrum، Dashboard، Workflow و غیره اضافه کرده ایم. سپس از مشتریان درخواست دریافت کردیم که آنها باید یک حساب مدیریت داشته باشند که مجوز خواندن/نوشتن هر کدام را داشته باشد. منابع موجود در شرکت آنها بدهیای که باید بپردازید به اینجا میرسد زیرا شامل تغییر کدی است که در هر ویژگی موجود در محصول شما اتفاق میافتد. این نه تنها وقت گیر است، بلکه مستعد خطا است.
3. منحنی یادگیری شیب دار
بدهی دیگری که اکنون باید بپردازید این است که حضور توسعه دهندگان جدید سخت تر می شود. حتی تلاش برای ایجاد تغییرات به ظاهر ساده نیاز به زمینه و دانش زیادی مانند موارد فوق دارد. بدون این دانش، توسعه دهندگان جدید ممکن است ناخواسته خطاهایی را معرفی کنند یا عملکردهای موجود را خراب کنند.
نحوه کاهش بدهی فنی
مشکل بدهی فنی این است که به نوعی اجتناب ناپذیر است. چرا؟ زیرا مهم نیست که در روزهای اولیه چقدر برای طراحی تلاش کرده اید، نمی توانید تضمین کنید که بهترین راه برای رفتن است زیرا نمی توانید به تمام مسائلی که باید با آنها رسیدگی کنید فکر کنید و نمی توانید همه موارد را پیش بینی کنید. تغییرات مورد نیاز در آینده پس چیکار کنیم که کمتر بشه؟
به یاد دارید که بدهی های فنی از ابتدا چگونه رشد می کند؟ بنابراین برداشت من این است که حداقل میتوانیم روی حذف تکراری، یا برنامهنویس عملی DRY (تکرار نکن) تمرکز کنیم.
جعبه ابزار ZenStack که ما در حال ساخت آن هستیم به آن پایبند است. از مدل داده های اعلامی در بالای Prisma استفاده می کند که خط مشی دسترسی و قوانین اعتبار سنجی را اضافه می کند، که از آن به طور خودکار API هایی از جمله OpenAPI، مسیر tPRC و قلاب ها را برای شما تولید می کند. در آخرین نسخه، ویژگی وراثت انتزاعی را برای رسیدگی بیشتر به آن اضافه کردیم.
بیایید مراحل مثال SaaS که قبلا ذکر شد را مرور کنیم تا ببینیم که چگونه به هر یک پرداخته می شود.
شما می توانید این را به صورت مفهومی با استفاده از مدل زیر در ابتدا نشان دهید:
/*
* Model for a team space
*/
model Space {
id String @id @default(uuid())
members SpaceUser[]
bug Bug[]
// require login
@@deny('all', auth() == null)
// everyone can create a space
@@allow('create', true)
// any user in the space can read the space
@@allow('read', members?[owner == auth()])
}
/*
* Model for a user
*/
model User {
id String @id @default(uuid())
password String? @password @omit
name String?
spaces SpaceUser[]
bug Bug[]
// can be created by anyone, even not logged in
@@allow('create', true)
// can be read by users sharing any space
@@allow('read', spaces?[space.members?[owner == auth()]])
// full access by oneself
@@allow('all', auth() == this)
}
/*
* Base model for all models in a space
*/
abstract model SpaceBase {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
ownerId String
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
spaceId String
@@allow('read', owner == auth() || space.members?[owner == auth()] )
@@allow('create', owner == auth() && space.members?[owner == auth()])
@@allow('update', owner == auth() && space.members?[owner == auth()] && future().owner == owner)
@@allow('delete', owner == auth())
}
/*
* Model representing membership of a user in a space
*/
model SpaceUser extends SpaceBase {
nickName String
}
/*
* Model for a bug
*/
model Bug extends SpaceBase {
title String
priority Int
}
1. تکرار کد
توجه کنید به SpaceBase
مدل، تمام زمینه ها و سیاست های دسترسی لازم برای پشتیبانی را دارد انزوا مستاجر. زمانی که باید ویژگی Task را اضافه کنیم، به جای کپی پیست کد ویژگی Bug، فقط کافی است SpaceBase
مدلی مانند زیر:
model Task extends SpaceBase {
title String
size Int
}
سپس API مربوط به آن به طور خودکار برای شما ایجاد می شود. دیگر کد تکراری وجود ندارد.
2. تغییر دادن مشکل است
برای پشتیبانی از نقش مدیر، تغییری که باید در طرحواره ایجاد کنید به شرح زیر است:
-
یک عدد اضافه کنید
SpaceUserRole
/* * Enum for user's role in a space */ enum SpaceUserRole { USER ADMIN }
-
اضافه کردن فیلد نقش در
SpaceUser
model SpaceUser extends SpaceBase { role SpaceUserRole ... }
-
خط مشی را اضافه کنید
SpaceBase
abstract model SpaceBase { ... //allow admin user to do anything @@allow('all', space.members?[role == ADMIN]) }
شما نیازی به تغییر هیچ خطی از کد TS/JS ندارید زیرا خط مشی دسترسی ZenStack از همه اینها مراقبت می کند.
3. منحنی یادگیری شیب دار
اکنون زمانی که توسعهدهنده جدید وارد کشتی میشود، میتواند بهراحتی با بررسی فایلهای طرحواره بهجای فرو رفتن عمیق در پایگاه کد، کل تصویر را دریافت کند. علاوه بر این، او می تواند شروع به اضافه کردن مدل جدید با گسترش آن کند SpaceBase
با کمی نگرانی برای شکستن چیزی
راستش را بخواهید، همچنان لازم است که فیلدهای رابطه مخالف را در آن اضافه کنید User
و Space
مدل ها هنگام اضافه کردن مدل جدید خبر خوب این است که هوشمندی افزونه ZenStack VSCode می تواند آن را برای شما مدیریت کند:
آیا می خواهید آن را امتحان کنید؟ از وب سایت ما دیدن کنید تا ببینید چگونه با آن شروع کنید.