تازگی یک بک‌اند بزرگ را از پایتون به Go منتقل کردم. انتظار داشتم سخت‌ترین بخش، بازنویسیِ منطق باشد. نبود. سخت‌ترین بخش این بود که مطمئن شوم رفتارِ سیستمِ جدید دقیقاً مثل قدیمی است.

قرارداد، نه پیاده‌سازی

کلید، یک مجموعه‌تستِ «سطحِ قرارداد» بود — تست‌هایی که فقط ورودی و خروجیِ API را چک می‌کنند، نه جزئیاتِ داخلی را. هر دو نسخه را در برابرِ همان تست‌ها اجرا کردم:

func TestContractParity(t *testing.T) {
    want := loadGolden("create_user.json")
    got  := callAPI("POST", "/users", payload)
    assertJSONEqual(t, want, got)
}

وقتی ۹۱ از ۹۱ تست سبز شد، با اطمینانِ کامل می‌دانستم مهاجرت درست انجام شده — نه چون کد را خط‌به‌خط خوانده بودم، بلکه چون رفتار اثبات شده بود.

سه قانونِ همیشگی‌ام

  1. مرز را قبل از کد تعریف کن. ورودی/خروجیِ واضح، وابستگی‌های صریح.
  2. تستِ رفتار بنویس، نه پیاده‌سازی. تا بتوانی همه‌چیز را بی‌ترس عوض کنی.
  3. درست‌بودن را اثبات کن، حدس نزن. «به‌نظر کار می‌کند» یعنی هنوز نمی‌دانی.

کدی که دوام می‌آورد، کدِ زیبا نیست؛ کدی است که می‌توانی با خیالِ راحت تغییرش بدهی.