← Blog
·12 blog.minutes

أساسيات تصميم الأنظمة التي يجب أن يعرفها كل مطور

موازنة التحميل، التخزين المؤقت، التقسيم، شبكات توصيل المحتوى، قوائم انتظار الرسائل، نظرية CAP، تحديد المعدل — مبسطة بأمثلة عملية وتكوينات يمكنك استخدامها فعلاً.

كل مطور خلفية يصطدم بجدار في النهاية. التطبيق يعمل بشكل جيد على حاسوبك المحمول. يعمل بشكل جيد في بيئة الاختبار مع ثلاثة مستخدمين متزامنين. ثم تنشر إلى الإنتاج، ويظهر ألف شخص في وقت واحد، وتنهار قاعدة البيانات. تبدأ API في إرجاع أخطاء 5xx، وتتجمد الواجهة الأمامية، وفي مكان ما على Slack ينشر شخص ما لقطة الشاشة التي تجعل هاتفك يهتز في الساعة 2 صباحاً.

تصميم الأنظمة هو ما يعيش على الجانب الآخر من ذلك الجدار. إنها مجموعة الأنماط والمقايضات وقرارات البنية التحتية التي تفصل تطبيقاً ترفيهياً عن نظام إنتاجي يمكنه التعامل مع حركة مرور حقيقية دون الانهيار. الخبر السار هو أنك لست بحاجة إلى أن تكون مهندس بنية تحتية كبيراً لفهم الأساسيات. تحتاج إلى معرفة حوالي ثمانية مفاهيم، وكيف تتفاعل، ومتى تلجأ لكل منها.

هذا المقال يغطي أنماط تصميم الأنظمة الأكثر أهمية للمطورين العاملين في الخلفية. كل قسم يشرح المفهوم، ويظهر مثالاً عملياً، ويصف المقايضات التي تحتاج إلى تقييمها قبل تبنيه. هذه ليست مفاهيم كتب نظرية مجردة — إنها أدوات ستلجأ إليها كل مرة تبني فيها شيئاً يحتاج إلى التوسع.

موازنة التحميل — إبقاء كل خادم مشغولاً، لكن ليس مشغولاً جداً

موازنة التحميل هي أبسط وأكثر أنماط تصميم الأنظمة تأثيراً يمكنك تنفيذه. الفكرة مباشرة: بدلاً من إرسال كل حركة المرور إلى خادم واحد، توزع الطلبات الواردة عبر مجموعة من الخوادم. هذا يمنحك شيئين في وقت واحد: توفراً أعلى (إذا مات خادم، تستمر الخوادم الأخرى في الخدمة) وإنتاجية أعلى (خوادم متعددة تشارك العمل).

خوارزميات موازنة التحميل الأكثر شيوعاً هي round-robin، وأقل الاتصالات، وتجزئة IP. round-robin تتنقل عبر قائمة الخوادم بالترتيب — بسيطة، متوقعة، لكنها غير مدركة لمدى انشغال كل خادم. أقل الاتصالات ترسل كل طلب إلى الخادم مع أقل اتصالات نشطة، وهو ما يتعامل مع أعباء الطلبات غير المتساوية بشكل أفضل. تجزئة IP تستخدم عنوان IP الخاص بالعميل لاختيار خادم بشكل حتمي، وهو أمر مهم عندما تحتاج إلى جلسات لزجة — ضمان أن نفس العميل يصل دائماً إلى نفس الخادم.

عملياً، معظم الإعدادات الإنتاجية تستخدم مزيجاً. موازن تحميل من الطبقة 4 (يعمل على مستوى TCP) يوزع الاتصالات الخام عبر مجموعة من البروكسيات العكسية أو بوابات API، والتي تقوم بعد ذلك بموازنة تحميل الطبقة 7 (تعمل على مستوى HTTP) لتوجيه الطلبات إلى نسخ تطبيق محددة. هذا النهج متعدد الطبقات يبقي مستوى البيانات سريعاً ومنطق التوجيه مرناً.

# nginx.conf — simple round-robin load balancing across three app servers
upstream app_cluster {
    round-robin;
    server app1.internal:3000 max_fails=3 fail_timeout=30s;
    server app2.internal:3000 max_fails=3 fail_timeout=30s;
    server app3.internal:3000 max_fails=3 fail_timeout=30s;
}

server {
    listen 443 ssl;
    location / {
        proxy_pass http://app_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

التفصيل الحاسم الذي يغفله معظم المطورين هو فحص الصحة. موازن التحميل مفيد فقط إذا كان يعرف أي الخوادم سليمة. إذا تعطل app2 لكن موازن التحميل يستمر في إرسال حركة المرور إليه، يرتفع معدل الأخطاء بمقدار الثلث تقريباً. قم دائماً بتكوين فحوصات الصحة النشطة — موازن التحميل يفحص كل خادم بشكل دوري ويزيل الخوادم غير المستجيبة من المجموعة تلقائياً.

التخزين المؤقت — السرعة، لكن بأي ثمن؟

التخزين المؤقت هو تحسين الأداء الأعلى تأثيراً المتاح لأي مطور. ضربة تخزين مؤقت واحدة يمكن أن تحول استعلام قاعدة بيانات بقيمة 200 مللي ثانية إلى بحث في الذاكرة بقيمة 2 مللي ثانية. بترتيبين من حيث الحجم. مطبقة عبر ملايين الطلبات، هذا الفرق هو الفرق بين فاتورة قاعدة بيانات شهرية بقيمة 10,000 دولار وأخرى بقيمة 500 دولار.

للتخزين المؤقت الكنسي ثلاث طبقات، لكل منها خصائص مختلفة. التخزين المؤقت على مستوى التطبيق (مخازن في الذاكرة مثل Redis أو Memcached) يخزن نتائج العمليات الحسابية المكلفة أو استعلامات قاعدة البيانات. التخزين المؤقت لشبكة توصيل المحتوى (CDN) يخزن الأصول الثابتة وشبه الثابتة في مواقع حافة قريبة من المستخدمين. التخزين المؤقت لـ HTTP يستخدم رؤوس Cache-Control وETag للسماح للمتصفحات والبروكسيات بتخزين الاستجابات مؤقتاً دون إشراك خوادمك على الإطلاق.

أصعب جزء في التخزين المؤقت ليس إعداده — بل إبطال التخزين المؤقت عندما تتغير البيانات الأساسية. استقرت الصناعة على بعض الأنماط الموثوقة. التخزين المؤقت المتجنب (يسمى أيضاً التحميل الكسول) يعني أن التطبيق يتحقق من التخزين المؤقت أولاً، ويعود إلى قاعدة البيانات في حالة عدم وجود البيانات، ويملأ التخزين المؤقت بالنتيجة. التخزين المؤقت بالكتابة من خلال يعني أن كل كتابة تذهب إلى كل من التخزين المؤقت وقاعدة البيانات في وقت واحد. التخزين المؤقت بالكتابة من الخلف يعني أن الكتابات تذهب إلى التخزين المؤقت أولاً ويتم تفريغها بشكل غير متزامن إلى قاعدة البيانات.

هناك شيئان صعبان فقط في علم الحاسوب: إبطال التخزين المؤقت وتسمية الأشياء. إبطال التخزين المؤقت أصعب لأنه لا يمكنك فقط إعادة تسميته والأمل في أن يختفي.

معظم التطبيقات، التخزين المؤقت المتجنب مع وقت حياة قصير هو الإعداد الافتراضي الصحيح. عيّن TTL يتوافق مع تحملك للبيانات القديمة — 60 ثانية لملفات تعريف المستخدمين، 5 دقائق لقوائم المنتجات، 24 ساعة للبيانات المرجعية. عندما تحتاج إلى تناسق أقوى، استخدم التخزين المؤقت بالكتابة من خلال ولكن تقبل زمن كتابة أعلى. عندما تحتاج إلى أقصى إنتاجية للقراءة ويمكنك تحمل التناسق النهائي، استخدم التخزين المؤقت المتجنب مع TTL سخي.

import redis.asyncio as aioredis
import json

cache = aioredis.Redis.from_url("redis://cache:6379")

CACHE_TTL = 300  # 5 minutes

async def get_user_profile(user_id: str) -> dict:
    key = f"profile:{user_id}"
    cached = await cache.get(key)
    if cached:
        return json.loads(cached)
    profile = await db.fetch_user(user_id)
    if profile:
        await cache.setex(key, CACHE_TTL, json.dumps(profile))
    return profile

كلمة تحذير: التخزين المؤقت يمكن أن يخفي المشكلات بدلاً من حلها. إذا كانت استعلامات قاعدة البيانات بطيئة بسبب فقدان فهرس، إضافة تخزين مؤقت تخفي العرض لكن الاستعلام الأساسي لا يزال بطيئاً. التخزين المؤقت يُخرج، الاستعلام البطيء يعمل، المستخدم ينتظر. قم دائماً بتحليل وتحسين مساراتك البطيئة أولاً، ثم أضف التخزين المؤقت فوق النسخة المحسنة.

تقسيم قاعدة البيانات — تقسيم العمل حتى لا تغرق أي قاعدة بيانات واحدة

التقسيم (التقسيم الأفقي) هو ما تلجأ إليه عندما لا تستطيع نسخة قاعدة بيانات واحدة التعامل مع إنتاجية الكتابة أو حجم مجموعة البيانات لديك. الفكرة هي تقسيم بياناتك عبر نسخ قاعدة بيانات متعددة، حيث تحمل كل نسخة (قسم) مجموعة فرعية من البيانات. يقرر التطبيق أي قسم يستعلم بناءً على مفتاح قسم — عادةً تجزئة لمعرف المستخدم، أو منطقة جغرافية، أو نطاق زمني.

مفتاح القسم هو القرار الأكثر أهمية في أي نظام مقسم. مفتاح قسم جيد يوزع البيانات بالتساوي عبر الأقسام ويتوافق مع أنماط استعلامك. مفتاح قسم سيء يخلق أقساماً ساخنة — أقسام قليلة تتعامل مع معظم حركة المرور بينما البقية تبقى خاملة. على سبيل المثال، التقسيم حسب الطابع الزمني للإنشاء يبدو معقولاً حتى تدرك أن قسم اليوم يتعامل مع كل الكتابات بينما أقسام العام الماضي لا تتعامل مع أي شيء.

التجزئة المتسقة تحل مشكلة إعادة التوازن التي تعاني منها التقسيمات الساذجة. في نظام تقسيم بسيط قائم على المعامل (قسم = مفتاح_تجزئة % N)، إضافة قسم جديد يتطلب إعادة خلط كل البيانات تقريباً. التجزئة المتسقة ترسم كلاً من المفاتيح والأقسام على حلقة تجزئة؛ عندما تضيف قسماً، فقط المفاتيح في الجوار المباشر للقسم الجديد تحتاج إلى التحرك. هذا يجعل التوسع لأعلى ولأسفل أقل إيلاماً بكثير.

  • التقسيم القائم على التجزئة — التوزيع بتجزئة مفتاح القسم؛ بسيط ومتساوي لكن إعادة التقسيم مكلفة
  • التقسيم القائم على النطاق — التقسيم حسب نطاقات القيم (مثلاً، المستخدمون بالمعرفات 1–10000 في القسم أ، 10001–20000 في القسم ب)؛ فعال لاستعلامات النطاق لكنه عرضة للنقاط الساخنة
  • التقسيم القائم على الدليل — الاحتفاظ بجدول بحث يربط المفاتيح بالأقسام؛ مرن لكنه يضيف قفزة بحث ونقطة فشل واحدة إذا تعطل الدليل
  • التقسيم الجغرافي — التقسيم حسب منطقة المستخدم؛ ممتاز لزمن الوصول لكنه غير ملائم إذا سافر المستخدمون أو إذا كانت البيانات تحتاج إلى أن تكون عالمية

المقايضة التي تقبلها مع التقسيم هي أن استعلامات عبر الأقسام تصبح مكلفة أو مستحيلة. إذا قسمت جدول المستخدمين حسب user_id وجدول الطلبات حسب user_id، استعلام لجميع الطلبات من آخر 30 يوماً يجب أن يضرب كل قسم. التطبيقات التي تحتاج تحليلات عالمية أو وصلات عبر الأقسام غالباً ما تستخدم نسخة متماثلة للقراءة ثانوية (أو قاعدة بيانات تحليلات مخصصة) تجمع البيانات من جميع الأقسام بشكل غير متزامن.

نظرية CAP — يمكنك اختيار اثنين فقط

تنص نظرية CAP على أن مخزن البيانات الموزع لا يمكنه توفير أكثر من ضمانين من ثلاثة في وقت واحد: الاتساق (كل قراءة تتلقى أحدث كتابة)، والتوفر (كل طلب يتلقى استجابة، حتى لو لم تكن أحدث البيانات)، وتحمل التقسيم (يستمر النظام في العمل على الرغم من فشل الشبكة بين العقد).

عملياً، تحمل التقسيم ليس اختيارياً. الشبكات تفشل. الحزم تُفقد، الاتصالات تنتهي صلاحيتها، مراكز البيانات تفقد الطاقة. لذا فالاختيار الحقيقي هو بين CP (الاتساق + تحمل التقسيم) وAP (التوفر + تحمل التقسيم). نظام CP مثل etcd أو Zookeeper سيرفض خدمة القراءات إذا لم يستطع ضمان الاتساق عبر العقد. نظام AP مثل Cassandra أو DynamoDB سيخدم القراءات من أي عقدة يمكن الوصول إليها، حتى لو كانت تلك العقدة لديها بيانات قديمة.

هذا ليس تمييزاً أكاديمياً. عندما تصمم نظاماً يمتد عبر مراكز بيانات متعددة، يجب أن تقرر ماذا يحدث عندما ينقطع رابط الشبكة بينها. هل تستمر في خدمة الطلبات مع بيانات قديمة محتملة (AP)؟ أم تتوقف عن الخدمة حتى تتعافى الشبكة (CP)؟ الإجابة تعتمد على تطبيقك. شبكة توصيل المحتوى يجب أن تكون AP — محتوى قديم أفضل من لا محتوى. نظام معالجة الدفع يجب أن يكون CP — لا تريد أبداً فرض رسوم مضاعفة على عميل لأن عقدتين قبلتا نفس الدفع أثناء الانقطاع.

قوائم انتظار الرسائل — تحويل الألم المتزامن إلى نعمة غير متزامنة

قوائم انتظار الرسائل هي العمود الفقري للمعالجة غير المتزامنة في الأنظمة الموزعة. تسمح لخدمة واحدة (المنتج) بإرسال رسالة إلى قائمة انتظار دون انتظار المستهلك لمعالجتها. المستهلك يلتقط الرسالة عندما يكون مستعداً، يعالجها، ويؤكد الاكتمال. هذا يفصل المنتج عن المستهلك في الزمان والمكان — لا يحتاجان إلى العمل بنفس السرعة، أو حتى في نفس الوقت.

كل نظام خلفي غير تافه يجب أن يستخدم قائمة انتظار رسائل في مكان ما. المثال الكنسي هو إرسال رسائل البريد الإلكتروني. عندما يسجل مستخدم في منصتك، لا تريد أن تنتظر استجابة HTTP حتى تقوم خدمة توصيل البريد الإلكتروني بعرض قالب، والاتصال بـ SendGrid، وتسليم الرسالة. بدلاً من ذلك، تدفع API الخاصة بك حدث send_email إلى قائمة انتظار وتعيد استجابة 201 Created فوراً. عامل منفصل يلتقط الحدث، ويرسل البريد الإلكتروني، ويضع علامة على المهمة كمكتملة.

نموذجا قوائم انتظار الرسائل السائدان هما النشر والاشتراك (pub/sub) وقوائم انتظار العمل. في pub/sub، كل رسالة تُبث لجميع المشتركين. هذا مفيد للعمارات القائمة على الأحداث حيث تحتاج خدمات متعددة إلى التفاعل مع نفس الحدث — تسجيل مستخدم جديد قد يؤدي إلى بريد إلكتروني ترحيبي، وتحديث CRM، وحدث تحليلات في وقت واحد. في قائمة انتظار العمل، كل رسالة تُسلم لمستهلك واحد بالضبط. هذا مفيد لتوزيع العمل عبر مجموعة من العمال — كل تحميل صورة يذهب إلى مولد صور مصغرة واحد بالضبط.

# docker-compose.yml — minimal RabbitMQ setup for local development
version: "3.8"
services:
  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "5672:5672"   # AMQP port for producers/consumers
      - "15672:15672" # Management UI
    environment:
      RABBITMQ_DEFAULT_USER: app
      RABBITMQ_DEFAULT_PASS: dev-only-password
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq

volumes:
  rabbitmq_data:

الجزء الصعب في قوائم انتظار الرسائل هو التعامل مع الإخفاقات بأمان. ماذا يحدث إذا تعطل المستهلك في منتصف معالجة رسالة؟ RabbitMQ وAmazon SQS تتعاملان مع هذا بإقرارات التسليم — يجب على المستهلك أن يؤكد صراحة أن الرسالة تمت معالجتها بنجاح. إذا قطع المستهلك الاتصال دون تأكيد، تُعاد الرسالة إلى قائمة الانتظار وتُسلم لمستهلك آخر. ضمان التسليم مرة واحدة على الأقل يعني أن المستهلكين يجب أن يكونوا عديمي الحالة: معالجة نفس الرسالة مرتين يجب أن تنتج نفس نتيجة معالجتها مرة واحدة.

قوائم انتظار الرسائل الميتة هي نمط أساسي آخر. عندما لا يمكن معالجة رسالة بعد عدة محاولات (الخدمة النهائية معطلة، البيانات مشوهة، تغيرت قاعدة العمل)، تُنقل الرسالة إلى قائمة انتظار الرسائل الميتة بدلاً من إعادة المحاولة إلى الأبد. مشغل يراقب قائمة انتظار الرسائل الميتة، يحقق في السبب الجذري، وإما يصلح الرسالة ويعيد وضعها في قائمة الانتظار أو يتخلص منها بعد التأكد من أنها آمنة للتجاهل.

شبكات توصيل المحتوى وتحديد المعدل — دفاعات الخط الأمامي

شبكات توصيل المحتوى ومحددات المعدل تخدم أغراضاً مختلفة لكنها تشترك في سمة مشتركة: هي خط الدفاع الأول بين مستخدميك وخوادمك. CDN تبقي الأصول الثابتة والاستجابات المخزنة مؤقتاً قريبة من المستخدم، مما يقلل زمن الوصول ويخفف حركة المرور من خوادم المصدر الخاصة بك. محدد المعدل يمنع أي مستخدم أو عميل واحد من إغراق نظامك بالطلبات.

تعمل CDN عن طريق توزيع المحتوى الخاص بك عبر شبكة عالمية من خوادم الحافة. عندما يطلب مستخدم في طوكيو أصلاً، تخدمه CDN من أقرب موقع حافة بدلاً من توجيه الطلب إلى خادم المصدر الخاص بك في فيرجينيا. هذا يخفض زمن الوصول من 200 مللي ثانية إلى 10 مللي ثانية للأصول الثابتة. CDN الحديثة تذهب أبعد من ذلك — يمكنها تخزين استجابات API مؤقتاً، وإنهاء اتصالات TLS، وحتى تنفيذ دوال بدون خادم عند الحافة.

تحديد المعدل يحمي نظامك على مستويات متعددة. تحديد المعدل العالمي يحد إجمالي الطلبات التي يمكن لنظامك بالكامل معالجتها في الثانية، محمياً ضد ارتفاع حركة المرور وهجمات DDoS. تحديد المعدل لكل مستخدم يضمن أن مستأجراً مسيئاً واحداً لا يمكنه تجويع المستخدمين الآخرين من الموارد. تحديد المعدل على مستوى النقطة النهائية يطبق حدوداً مختلفة على مسارات مختلفة — نقطة نهاية تسجيل الدخول قد تسمح بـ 5 طلبات في الدقيقة بينما نقطة نهاية البحث للقراءة فقط تسمح بـ 100 طلب في الدقيقة.

خوارزمية النافذة المنزلقة هي المعيار الصناعي لتحديد المعدل لأنها دقيقة وفعالة في نفس الوقت. بدلاً من إعادة تعيين العدادات في فترات زمنية ثابتة (مما يسمح بالانفجارات عند الحدود)، النافذة المنزلقة تعتبر الطلبات عبر نافذة زمنية متحركة. Redis هو الخيار الطبيعي لتنفيذ هذا — استخدم مجموعة مرتبة بأختام زمنية كدرجات، وقص الإدخالات خارج النافذة، واحسب الإدخالات المتبقية. تكلفة الذاكرة ضئيلة (بضع بايتات لكل طلب) والتعقيد الزمني لوغاريتمي.

// Redis-backed sliding window rate limiter (TypeScript)
import { createClient } from "redis";

const redis = createClient({ url: "redis://ratelimit:6379" });

async function checkRateLimit(
  key: string,
  limit: number,
  windowMs: number
): Promise<{ allowed: boolean; remaining: number }> {
  const now = Date.now();
  const windowStart = now - windowMs;

  const multi = redis.multi();
  multi.zRemRangeByScore(key, 0, windowStart);
  multi.zAdd(key, { score: now, value: `${now}` });
  multi.zCard(key);
  multi.expire(key, Math.ceil(windowMs / 1000));

  const [, , count] = await multi.exec() as [any, any, number];
  return {
    allowed: count <= limit,
    remaining: Math.max(0, limit - count),
  };
}

كل من CDN ومحددات المعدل تشتركان في مبدأ تشغيلي مهم: فشل مفتوح أم فشل مغلق؟ إذا لم تتمكن حافة CDN من الوصول إلى المصدر، هل تخدم استجابة مخزنة قديمة (فشل مفتوح) أم ترجع خطأ (فشل مغلق)؟ إذا تعطلت مجموعة Redis الخاصة بمحدد المعدل، هل تمر جميع الطلبات (فشل مفتوح — خطر التحميل الزائد) أم تُرفض جميع الطلبات (فشل مغلق — توقف مضمون)؟

لا توجد إجابة عالمية، لكن الإعداد الافتراضي الجيد لمعظم الأنظمة هو: فشل مفتوح للقراءات، فشل مغلق للكتابات. صفحة قائمة منتجات قديمة مقبولة. أمر شراء مفقود ليس مقبولاً. وثق هذا القرار صراحة في دليل التشغيل الخاص بك حتى يعرف مهندس المناوبة أي سلوك يتوقع عندما تنهار البنية التحتية.

وضع كل شيء معاً — التفكير في المقايضات

تصميم الأنظمة ليس عن حفظ الأنماط. إنه عن فهم المقايضات والتعرف على أي نمط يناسب قيودك. كل قرار يتضمن مقايضة بين الاتساق والتوفر، بين إنتاجية القراءة وزمن كتابة، بين التعقيد التشغيلي والأداء الخام. أفضل المهندسين ليسوا أولئك الذين يعرفون أكثر الأنماط — هم أولئك الذين يمكنهم النظر إلى مشكلة وتحديد أي القيود ثابتة وأيها قابلة للتفاوض.

إليك إطار قرار سريع عندما تواجه مشكلة تصميم نظام جديدة. ابدأ بإدراج متطلباتك غير الوظيفية: حجم حركة المرور المتوقع، أهداف زمن الوصول، متطلبات الاتساق، ميزانية تكاليف البنية التحتية، وإلمام الفريق بالتكنولوجيا. ثم اعمل عبر الأنماط في هذا المقال واسأل عما إذا كان كل نمط يقربك أو يبعدك عن متطلباتك.

إذا كان زمن الوصول هو اهتمامك الأساسي، ابدأ بالتخزين المؤقت وCDN — يعطيان أكبر تحسين لأقل تعقيد. إذا كان التوفر حاسماً، استخدم موازنة التحميل مع فحوصات الصحة، وصمم خدماتك لتكون عديمة الحالة، واختر AP على CP حيث يسمح العمل بذلك. إذا كنت تتعامل مع أعباء عمل كثيفة الكتابة، قم بتقييم التقسيم وقوائم انتظار الرسائل مبكراً — فهي أسهل بكثير في التقديم قبل أن يكون لديك ملايين الصفوف من البيانات. إذا كنت تتعامل مع API عامة سيستهلكها مطورو طرف ثالث، قم بتنفيذ تحديد المعدل من اليوم الأول. إضافته لاحقاً يعني إصدار نسخة جديدة من API أو كسر العملاء الحاليين.

أهم مهارة في تصميم الأنظمة هي معرفة ما لا تحتاجه. معظم التطبيقات لا تحتاج تقسيم. معظم التطبيقات لا تحتاج قائمة انتظار رسائل. التوزيع المبكر هو أصل كل شر في تصميم الأنظمة — كل نظام موزع يقدم أنماط فشل لا يمتلكها نظام الخادم الواحد ببساطة. أضف التعقيد فقط عندما تخبرك المقاييس بذلك، وليس لأن النمط يبدو مثيراً للإعجاب في مقابلة عمل.

ابدأ ببساطة. قس كل شيء. أضف نمطاً واحداً في كل مرة. تحقق من التحسن قبل المتابعة. الأنظمة التي تبقى ليست تلك التي لديها أكثر العمارات تطوراً — هي تلك التي يسهل فهمها، وتشغيلها، وتغييرها عندما يظهر عنق الزجاجة التالي.