بدهی فنی: چطور اندازهاش را بگیریم و کجا بپردازیم

جملهای که در هر تیم فنی شنیده میشود: «این کد بدهی فنی دارد.»
و جملهای که در هر جلسهٔ مدیریتی شنیده میشود: «الان وقتش نیست، بعداً درستش میکنیم.»
فاصلهٔ این دو جمله، جایی است که پروژهها آهسته میشوند. نه یکباره — یکباره هیچوقت اتفاق نمیافتد. تیمی که دو سال پیش ویژگیای را در یک هفته تحویل میداد، حالا برای همان اندازه کار یک ماه میخواهد و هیچکس نمیتواند دقیقاً بگوید چه شد.
بدهی فنی چیست، و چه چیزی نیست
استعارهٔ اصلی از وارد کانینگهام است و دقیقتر از چیزی است که معمولاً نقل میشود: بدهی فنی یعنی آگاهانه راه سریعتری را انتخاب کنید تا زودتر تحویل دهید، با علم به اینکه بعداً باید بازش کنید.
نکتهٔ کلیدی «آگاهانه» است. کد بدی که از سر بیتجربگی نوشته شده، بدهی نیست — خرابی است. تفاوتشان در این است که بدهی یک تصمیم است با سود مشخص (زودتر رسیدن به بازار)، و خرابی سودی ندارد.
این تفکیک عملی است، نه تئوریک. با بدهی میشود مثل بدهی رفتار کرد: ثبتش کرد، بهرهاش را حساب کرد، و برنامهٔ بازپرداخت داشت. با خرابی فقط میشود اصلاحش کرد.
بدهی همیشه بد نیست
نکتهای که در تیمهای فنی کم گفته میشود: گرفتن بدهی گاهی درستترین تصمیم است.
استارتاپی که نمیداند محصولش خریدار دارد یا نه، نباید معماری دهساله بسازد. نسخهٔ اول محصول قرار است فرضیه را آزمایش کند و اگر فرضیه غلط باشد، آن معماری تمیز هم دور ریخته میشود. اینجا بدهی گرفتن، سرمایهگذاری درست است.
آنچه غلط است، سه چیز دیگر است:
- بدهیای که ثبت نمیشود. میانبری که فقط در ذهن نویسندهاش هست، شش ماه بعد فرض پایهای میشود که کسی زیر سؤال نمیبرد.
- بدهیای که هرگز پرداخت نمیشود. «بعداً» بدون تاریخ یعنی هرگز.
- بدهی در پایه. میانبر در یک صفحهٔ گزارش، هزینهاش محدود است. میانبر در مدل داده یا در مدل دسترسی، هزینهاش کل سامانه است.
چطور آن را قابل دیدن کنیم
مشکل اصلی بدهی فنی این نیست که تیم نمیداند کجاست. این است که مدیر غیرفنی نمیبیندش، و چیزی که دیده نشود در اولویتبندی جایی ندارد.
راهحل این است که بدهی را به زبان نتیجه ترجمه کنید، نه به زبان کد. یعنی بهجای «این کلاس دو هزار خط است»، بگویید:
«هر تغییر در ماژول فاکتور، سه برابر ماژولهای دیگر زمان میبرد و در چهل درصد موارد باگ ثانویه میسازد.»
این جمله قابل تصمیمگیری است. اولی نیست.
چند سنجه که بدون ابزار خاصی قابل جمعآوریاند و در عمل کار میکنند:
- زمان تغییر به تفکیک ماژول. کجا برآوردها مدام از واقعیت فاصله میگیرند؟
- نرخ باگ برگشتی. چند درصد کارها بعد از تحویل دوباره باز میشوند؟
- مدت استقرار. انتشار یک نسخه چقدر طول میکشد و چقدر دستی است؟
- زمان راهافتادن عضو جدید. برنامهنویس تازه چند هفته طول میکشد تا اولین تغییر واقعی را بدهد؟ این سنجه، بهطور غیرمستقیم، کیفیت ساختار و مستندات را اندازه میگیرد.
- تعداد نقاطی که برای یک تغییر باید دست بخورد. اگر افزودن یک فیلد به فرم، هفت فایل را تغییر میدهد، شما یک مسئلهٔ ساختاری دارید.
هیچکدام از اینها عدد مطلق نیستند. روندشان مهم است.
کدام بدهی را بپردازیم
فهرست بدهی هر سامانهای بلندتر از ظرفیت تیم است. پس اولویتبندی اجتنابناپذیر است و معیار ما دو محور دارد:
۱. چقدر جلوی کار را میگیرد؟ بدهی در بخشی که ماهی یک بار تغییر میکند، عملاً هزینهای ندارد. بدهی در بخشی که هر هفته دست میخورد، هر هفته بهره میگیرد.
۲. چقدر ریسک دارد؟ بدهیای که ممکن است به قطعی سرویس یا نشت داده منجر شود، جدا از هر محاسبهٔ سرعتی، اولویت دارد.
ضرب این دو، فهرست کوتاهی میدهد. و یک قاعدهٔ منفی که به همان اندازه مهم است:
کدی که کار میکند و تغییر نمیکند را بازنویسی نکنید. ماژولی که پنج سال است بدون تغییر کار میکند، هرقدر هم زشت باشد، بازنویسیاش سود ندارد و ریسک دارد. بازنویسی یعنی وارد کردن باگ به جایی که باگ نداشت.
چطور پرداخت کنیم
سه الگو دیدهایم و فقط یکیشان پایدار است.
پروژهٔ بازنویسی بزرگ. «شش ماه هیچ ویژگی جدیدی نمیدهیم و همهچیز را درست میکنیم.» تقریباً همیشه شکست میخورد: ماه سوم کسبوکار فشار میآورد، کار نیمه رها میشود، و حالا دو معماری دارید بهجای یکی.
سهم ثابت از هر دوره. بین ده تا بیست درصد ظرفیت هر اسپرینت به بدهی. ساده، قابل دفاع در جلسه، و کار میکند. اگر عددی میخواهید که با آن شروع کنید، بیست درصد.
اصلاح در مسیر. هر بار کسی وارد بخشی میشود، کمی بهترش میکند. مؤثرترین روش، چون دقیقاً همان جاهایی را بهبود میدهد که بیشترین رفتوآمد را دارند. اما بهتنهایی برای بدهی ساختاری کافی نیست — بازطراحی مدل داده در حاشیهٔ یک کار دیگر انجام نمیشود.
عملاً، ترکیب دوم و سوم پاسخ درست است.
بدهیای که در سامانههای ایرانی خاص است
دو مورد که مکرر دیدهایم و کمتر جایی نوشته شده:
وابستگی به نسخههای قدیمی به دلیل محدودیت دسترسی. بهروزرسانی کتابخانهها وقتی سختتر میشود که دسترسی به مخازن اصلی پایدار نیست. نتیجهاش سامانههایی است که چند نسخهٔ اصلی عقباند و فاصله هر سال بیشتر میشود. این بدهی، برخلاف بقیه، بهخودیخود رشد میکند حتی اگر یک خط کد هم ننویسید. بهترین کار، بهروزرسانیهای کوچک و مکرر است، نه جهشهای چندساله.
قواعد تغییرپذیر مالیاتی و بانکی که بهصورت شرط در کد نشستهاند. نرخها و قواعدی که سالانه تغییر میکنند و در ده جای کد تکرار شدهاند. این را زود جدا کنید؛ هر سال هزینهاش را دوباره میدهید.
اگر سامانهٔ شما همین حالا در این وضع است
نشانههای روشن: برآوردها مدام غلط از آب درمیآیند، هر اصلاح چیز دیگری را میشکند، تیم از تغییر بخشهایی میترسد، و انتشار نسخه یک رویداد پرتنش است.
اولین قدم، بازنویسی نیست. اندازهگیری است — سه ماه داده جمع کنید تا بدانید هزینه واقعاً کجاست. تقریباً همیشه جایی است که تیم حدس نمیزد.
اگر مسئله از حد بدهی گذشته و به بازسازی ساختاری رسیده، مسیرش را در نوسازی سیستم قدیمی نوشتهایم. و اگر مسئله فشار روزمرهٔ نگهداری است که فرصت پرداخت را نمیگذارد، جدا کردن تیم نگهداری از تیم توسعه — چیزی که در پشتیبانی انجام میدهیم — معمولاً همان کاری است که چرخه را میشکند.