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