Levelwise
فارسی
معماری و طراحی سیستم

ثبت تصمیم‌های معماری (ADR)

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

بازبینی نشدهبا کمک AI نوشته شدهزمان خواندن: ۱۱ دقیقهمثال فروشگاه اینترنتینمونه سند و ساختار پوشه

نویسنده: bezzad

مشکل: «چرا این را انتخاب کردیم؟»

دو سال پیش، تیم فروشگاه اینترنتی ما برای رویدادهای سفارش، RabbitMQ را انتخاب کرد. امروز یک برنامه‌نویس جدید می‌پرسد: «چرا Kafka نه؟ همه از Kafka استفاده می‌کنند.»

هیچ کس جواب دقیق را نمی‌داند. دو نفری که آن تصمیم را گرفتند، از شرکت رفته‌اند.

زمانجلسه تصمیمهمه دلیل را می‌دانندماه اولدو نفر می‌رونددلیل فقط در ذهن آن‌ها بودسال اولبرنامه‌نویس جدید«چرا این را انتخاب کردیم؟»سال دومدو راه بدتقلید کورکورانهیا تغییر بی‌دلیلکد نشان می‌دهد «چه» ساختیم. نشان نمی‌دهد «چرا»
دلیل تصمیم در ذهن آدم‌ها است. آدم‌ها می‌روند و دلیل هم با آن‌ها می‌رود.

حالا تیم دو راه بد دارد:

  1. تقلید کورکورانه. «لابد دلیلی داشته، دستش نزنیم.» حتی اگر شرایط عوض شده باشد و تصمیم دیگر درست نباشد.
  2. تغییر بی‌دلیل. «این قدیمی است، عوضش کنیم.» شاید دلیلی مهم بوده که هنوز هم درست است. تیم چند ماه کار می‌کند و به همان مشکل قدیمی می‌رسد.

هر دو راه از یک کمبود می‌آیند: دلیل تصمیم جایی نوشته نشده است.

ایده: یک سند کوتاه برای هر تصمیم

سند ثبت تصمیم معماری (Architecture Decision Record یا ADR) یک فایل متنی کوتاه است. برای هر تصمیم مهم یک فایل می‌نویسیم. این ایده را مایکل نایگارد (Michael Nygard) در سال ۲۰۱۱ مطرح کرد و حالا در خیلی از تیم‌ها رایج است.

ویژگی‌های یک ADR خوب:

  1. کوتاه است. یکی دو صفحه. اگر نوشتنش یک روز طول بکشد، هیچ کس آن را نمی‌نویسد.
  2. کنار کد است. در همان مخزن گیت، نه در یک ویکی که کسی پیدایش نمی‌کند.
  3. یک تصمیم دارد. هر فایل فقط یک تصمیم.
  4. شماره دارد. شماره‌ها پشت سر هم هستند و هیچ وقت دوباره استفاده نمی‌شوند.
  5. تغییر نمی‌کند. اگر تصمیم عوض شد، یک ADR جدید می‌نویسیم.

یک ADR چه بخش‌هایی دارد؟

قالب اصلی نایگارد پنج بخش دارد. قالب‌های دیگری هم هست، ولی تقریباً همه همین ایده را دارند.

0004 - Use RabbitMQ for order eventsStatusAcceptedContextDecisionConsequencesچه تصمیمی؟ یک جملهپیشنهاد، پذیرفته، جایگزین‌شدهچرا باید تصمیم بگیریم؟محدودیت‌ها و گزینه‌هاچه انتخاب کردیم؟چه چیزی خوب و بد می‌شود؟هزینه را هم بنویس
  1. عنوان. خود تصمیم، در یک جمله کوتاه. «استفاده از RabbitMQ برای رویدادهای سفارش».
  2. وضعیت. پیشنهاد، پذیرفته، ردشده یا جایگزین‌شده.
  3. زمینه (Context). چه مشکلی داریم؟ چه محدودیت‌هایی؟ چه گزینه‌هایی را بررسی کردیم؟ این مهم‌ترین بخش است. بدون آن، خواننده نمی‌فهمد آیا تصمیم هنوز درست است.
  4. تصمیم (Decision). چه انتخاب کردیم. با جمله‌های روشن و فعال: «ما … استفاده می‌کنیم».
  5. پیامدها (Consequences). چه چیزی آسان‌تر و چه چیزی سخت‌تر می‌شود. هر تصمیم هزینه‌ای دارد. اگر هیچ پیامد بدی ننوشتی، احتمالاً خوب فکر نکرده‌ای.

نمونه کامل در فروشگاه

این همان تصمیم دو سال پیش است، اگر آن روز نوشته شده بود. متن ADR را معمولاً به انگلیسی و با Markdown می‌نویسند:

# 4. Use RabbitMQ for order events

Date: 2024-03-12
Status: Accepted
Deciders: Sara (lead), Reza, Mina

## Context

- Order, Warehouse and Shipping services must react to order events.
- Peak load is about 200 orders per minute.
- The team has 4 developers. Two already run RabbitMQ in production.
- We do not need to replay old events. Each event is handled once.
- Options we looked at: RabbitMQ, Kafka, Azure Service Bus.

## Decision

We will use RabbitMQ (self-hosted) for all order events.

## Consequences

- Good: the team already knows how to run and monitor it.
- Good: simple queues and retries are enough for our load.
- Bad: we cannot replay old events. If we need event replay
  or analytics on the event stream, we must revisit this decision.
- Bad: one more server for the ops team to patch and back up.

دو سال بعد، برنامه‌نویس جدید این فایل را می‌خواند و جوابش را می‌گیرد:

  1. دلیل را می‌فهمد. تیم کوچک بود، بار کم بود و بازپخش رویداد لازم نبود.
  2. می‌فهمد کی تصمیم باید عوض شود. خود سند گفته است: «اگر بازپخش رویداد لازم شد، دوباره بررسی کن.»
  3. دیگر حدس نمی‌زند. اگر امروز تیم بزرگ‌تر شده و تحلیل رویدادها لازم است، شرایط عوض شده است. پس تغییر، دلیل روشن دارد.

چرخه عمر: ADR را پاک نکن

یک سال بعد، تیم تحلیل داده به رویدادهای قدیمی نیاز پیدا می‌کند. تیم تصمیم می‌گیرد به Kafka برود. ADR شماره ۴ را تغییر نمی‌دهیم. یک ADR جدید با شماره ۹ می‌نویسیم.

پیشنهادProposedپذیرفتهAcceptedجایگزین‌شدهSuperseded by 0009ردشده0009 - Move to Kafkaتصمیم جدیدمتن قدیمی را تغییر نمی‌دهیم.فقط وضعیت و یک لینک به تصمیم جدید اضافه می‌کنیم.
سند قدیمی تاریخچه است. نشان می‌دهد در آن زمان، با آن شرایط، چرا آن تصمیم درست بود.

چرا متن قدیمی را عوض نمی‌کنیم؟

  1. تاریخچه می‌ماند. کسی که کد قدیمی را می‌خواند، می‌فهمد چرا آن روز این شکل بود.
  2. اشتباه تکرار نمی‌شود. اگر تصمیم جدید هم شکست خورد، می‌شود دید که چه گزینه‌هایی قبلاً امتحان شده است.
  3. اعتماد بیشتر است. وقتی می‌دانی سندها بعداً بی‌صدا تغییر نمی‌کنند، به آن‌ها تکیه می‌کنی.

در ADR شماره ۴ فقط وضعیت را عوض می‌کنیم و یک لینک به ADR شماره ۹ اضافه می‌کنیم. ADR شماره ۹ هم در زمینه‌اش به شماره ۴ لینک می‌دهد.

کجا و چطور؟

یک پوشه در مخزن کافی است:

docs/
  adr/
    0001-record-architecture-decisions.md
    0002-use-postgresql-for-orders.md
    0003-modular-monolith-first.md
    0004-use-rabbitmq-for-order-events.md

یک روند ساده که با کار روزانه تیم جور است:

  1. پیشنهاد در Pull Request. کسی که تصمیم را پیشنهاد می‌کند، فایل را با وضعیت «پیشنهاد» می‌سازد.
  2. بحث در همان Pull Request. تیم نظر می‌دهد، مثل Code Review. گزینه‌های دیگر به بخش زمینه اضافه می‌شوند.
  3. ادغام یعنی پذیرش. وقتی Pull Request ادغام شد، وضعیت «پذیرفته» است.
  4. کد و تصمیم با هم. اگر می‌شود، ADR را در همان Pull Request که کد را تغییر می‌دهد بیاور.
اولین ADR: خیلی از تیم‌ها اولین ADR را درباره خود ADR می‌نویسند: «ما تصمیم‌های معماری را با ADR ثبت می‌کنیم.» این کار به آدم‌های جدید می‌گوید این پوشه چیست.

چه تصمیمی ADR لازم دارد؟

همه تصمیم‌ها ADR لازم ندارند. اسم یک متغیر یا انتخاب بین دو کتابخانه کوچک، ADR نمی‌خواهد. یک سؤال ساده بپرس: اگر این تصمیم غلط باشد، عوض کردنش گران است؟

بنویس

  • انتخاب دیتابیس، صف پیام یا فریم‌ورک اصلی.
  • سبک معماری، مثل Monolith ماژولار یا Microservices.
  • روش احراز هویت بین سرویس‌ها.
  • قانونی که همه تیم‌ها باید رعایت کنند، مثل «هیچ سرویسی دیتابیس سرویس دیگر را نمی‌خواند».
  • تصمیمی که سر آن بحث زیادی شد.

لازم نیست

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

اشتباه‌های رایج

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

خلاصه در پنج خط

  1. کد نشان می‌دهد «چه» ساختیم. ADR نشان می‌دهد «چرا».
  2. هر ADR کوتاه است و پنج بخش دارد: عنوان، وضعیت، زمینه، تصمیم و پیامدها.
  3. زمینه و پیامدهای بد مهم‌ترین بخش‌ها هستند.
  4. سند را کنار کد نگه دار و با Pull Request مرورش کن.
  5. سند قدیمی را پاک یا ویرایش نکن. یک ADR جدید بنویس که جایگزینش شود.