تكاد كل عملية إعادة كتابة لمنتج SaaS طُلب منا إنقاذها تعود إلى الكلمات الثلاث نفسها التي قالها الفريق الأصلي في شهره الأول: «سنضيف ذلك لاحقًا». تعدّد المستأجرين، والتحكم في الوصول القائم على الأدوار، والفوترة الحقيقية. تبدو أمورًا تُلحَق بالمنتج بعد أن تحصل على عملاء. وهي على العكس تمامًا — فهي شكل المبنى ذاته، ولا يمكنك تغيير الأساس بعد أن تُرفع الجدران دون هدم كل شيء.
لقد بنينا منصات تعمل اليوم في أكثر من 40 دولة على قاعدة كود واحدة. والسبب الذي جعل ذلك ممكنًا مملٌّ ومُجمَع عليه: فقد تقرّر شأن الاستئجار والوصول والفوترة منذ اليوم الأول، قبل أول ميزة. وهكذا تبدو تلك القرارات، وهذا ما يكلّفه تأجيلها.
01لماذا تتفاداه الفرق (ولماذا هو فخّ)
الضغط حقيقي. لديك عميل تجريبي واحد، وعرض توضيحي الأسبوع المقبل، وقائمة مهام عالقة من ميزات تبدو فعلًا كالمنتج. وتعدّد المستأجرين غير مرئي لهذا العميل الأول — فهو لا يراه، ومن ثم يبدو كزينة فائضة. فيبني الفريق تطبيقًا أحادي المستأجر «في الوقت الراهن» ويَعِد بتعميمه لاحقًا.
والفخّ أن «أحادي المستأجر في الوقت الراهن» يسرّب افتراضًا واحدًا إلى كل طبقة: أن في العالم مؤسسة واحدة بالضبط. وينتهي هذا الافتراض في استعلاماتك، ومصادقتك، وذواكر التخزين المؤقت لديك، ومهامك في الخلفية، ومسارات ملفاتك. وانتزاعه لاحقًا ليس إعادة هيكلة — بل إعادة كتابة، لأن الافتراض ليس في موضع واحد، بل في كل موضع.
قد يكلّفك إدراج الاستئجار منذ اليوم الأول أسبوعين من العمارة والانضباط. أما إدراجه في السنة الثانية — بعد أن يصبح لديك عملاء حقيقيون وبيانات حقيقية وتوقّعات حقيقية للجاهزية — فهو إعادة كتابة تمتد أشهرًا تحت الحِمل، مع ترحيل بيانات لا يُسمح لك بإخفاقه. العمل لا يختفي إن أجّلته. بل يتراكم.
02المستأجر هو العمود الأول
قاعدتنا بسيطة: كل صفّ يخصّ عميلًا يحمل tenant_id، ولا يوجد مسار كود يستطيع الاستعلام بدونه. ليست عُرفًا يتذكّره الناس — بل قيدًا تفرضه قاعدة البيانات وإطار العمل، بحيث يصبح نسيانه مستحيلًا لا مجرد غير مستحبّ.
وأنظف صيغة نستخدمها تتّكئ على قاعدة البيانات نفسها. فسياسات الأمان على مستوى الصفوف (row-level security) تعني أن الحدّ بين المستأجرين يُفرض طبقةً أسفل كود التطبيق، حيث لا يستطيع خلل في الـ ORM أو شرط WHERE منسيّ أن يسرّب بيانات عميل إلى آخر. يضبط التطبيق المستأجرَ الحالي على الاتصال؛ وترفض قاعدة البيانات إرجاع أي شيء سواه.
-- the boundary lives below the app, not inside it ALTER TABLE orders ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON orders USING (tenant_id = current_setting('app.tenant')::uuid); -- app sets the tenant per request; a forgotten WHERE -- clause now returns zero rows instead of someone else's data SET app.tenant = 'a91f…'; SELECT * FROM orders; -- only this tenant's orders, always
هذا القرار الواحد يزيل عن قائمة همومك، وبشكل دائم، فئةً كاملة من الأخطاء الكارثية — تسريب البيانات بين المستأجرين. إنهما اليومان الأعلى أثرًا في المشروع كله.
أكثر الأخطاء كلفةً في SaaS هو الذي يُظهر لعميل بيانات عميل آخر. اِبنِ عمارتك بحيث يستحيل وقوعه، لا بحيث يكون بعيد الاحتمال.
— عن العزل بوصفه قيدًا03التحكم في الوصول القائم على الأدوار ليس صفحة إعدادات
الـ «لاحقًا» الثاني الذي يتحوّل إلى إعادة كتابة هو التحكم في الوصول. تنطلق الفرق بعالم ضمنيّ من دورين — مدير ومستخدم — مثبَّت في عبارات if متناثرة عبر قاعدة الكود كلها. ثم يطلب عميل مؤسسي دورًا «للفوترة فقط»، أو «مدقّقًا للقراءة فقط»، فتكتشف منطق صلاحيات في مئتي موضع.
نحن ننمذج الصلاحيات بوصفها بيانات منذ البداية: فالأدوار والصلاحيات والتعيينات بينها تعيش في جداول، وكل إجراء محميّ يطرح سؤالًا على سلطة مركزية واحدة: «هل يستطيع هذا الفاعل أن يفعل هذا الشيء بهذا المورد، في هذا المستأجر؟». فتصبح إضافة دور سطرًا، لا إصدارًا. وهذا ما يتيح لمنصة أن تقول نعم للهيكل التنظيمي للعميل المؤسسي دون المساس بكود التطبيق.
- الصلاحيات أفعال على موارد —
invoice:read،invoice:void— لا مستويات مبهمة مثل «مدير». - الأدوار تجميعات من صلاحيات يستطيع المستأجر تأليفها، بحيث قد يعني عميلان شيئين مختلفين بكلمة «مدير».
- كل تحقّق يمرّ ببوابة واحدة. منطق التفويض في موضع واحد قابل للتدقيق؛ أما عبارات
ifالمتناثرة فهي عبء.
04الفوترة منتج، لا سباكة
الفوترة هي حيث يؤلم «سنضيفه لاحقًا» أشدّ الألم، لأنك حين تضيفه يكون لديك عملاء على اتفاقات بالمصافحة، وبيانات غير متسقة عمّن يدين بماذا، ولا مفهوم نظيف للاشتراك. تدارُك الفوترة معناه تسوية الأموال، وهو الموضع الوحيد الذي لا يكون فيه الخطأ مُحرجًا فحسب — بل مبلغًا مستردًّا، أو نزاعًا، أو مشكلة امتثال.
نصمّم نموذج الفوترة جنبًا إلى جنب مع نموذج المستأجر: الخطط، والاشتراكات، والاستخدام، والفواتير، ودورة الحياة التي تربطها. وحتى لو فُوتِر العملاء الأوائل يدويًا، فإن نموذج البيانات حاضر منذ اليوم الأول، بحيث يكون تفعيل الخطط ذاتية الخدمة لاحقًا ميزةً، لا ترحيلًا لكامل تاريخ إيراداتك.
قاعدة كود واحدة، نموذج نشر واحد، مستأجرون كثيرون.
لا تفرّع خاص بكل عميل يلزم صيانته أو يُترك يتباعد.
عزل مفروض على مستوى قاعدة البيانات، لا بالعُرف.
05أربعون دولة، قاعدة كود واحدة
بلوغ أكثر من 40 دولة يبدو كقصة توسّع. وهو في الواقع قصة تهيئة. فالمنصة لا تملك فروع كود خاصة بكل دولة — بل تملك نموذج مستأجر غنيًّا بما يكفي للتعبير عن ما يختلف بين منطقة وأخرى بوصفه بيانات: العملة، والقواعد الضريبية، واللغة، وصيغ التواريخ، والنص القانوني على الفاتورة.
حين يُرسَم الحدّ بين «الكود» و«التهيئة» على نحو صحيح منذ اليوم الأول، تصبح الدولة الجديدة مهمة دمج، لا مشروعًا هندسيًا. وحين يُرسَم على نحو خاطئ، يصبح كل سوق جديد تفرّعًا، وفي أقل من عام تصير تصون عشرات النسخ المختلفة اختلافًا طفيفًا من التطبيق نفسه، وتشحن كل إصلاح عشرات المرات. ونتيجة «قاعدة كود واحدة» إنما تنبثق من قرارات اتُّخِذت قبل أول عميل.
منفعة فعل ذلك منذ اليوم الأول لا تظهر في الشهر الأول — بل تظهر في السنة الثالثة، حين تشحن ميزة مرة واحدة فتتلقّاها 40 دولة في آنٍ، بينما منافسك الأحادي المستأجر لا يزال يدمجها في تفرّعه السابع الخاص بأحد العملاء.
06قائمة تحقّق اليوم الأول
إن كنت ستطلق منصة SaaS هذا الربع، فهذه هي القرارات التي ينبغي اتخاذها قبل كتابة أي ميزة — لا لأنها عاجلة، بل لأنها غير قابلة للعكس:
- ضع
tenant_idعلى كل شيء وافرضه أسفل التطبيق. أمان على مستوى الصفوف أو ما يعادله. اجعل الاستعلام بين المستأجرين مستحيلًا، لا بعيد الاحتمال. - انمذج الصلاحيات بوصفها بيانات. أدوار وتعيينات في جداول، وبوابة تفويض مركزية واحدة. إضافة دور ينبغي أن تكون سطرًا.
- اِبنِ نموذج بيانات الفوترة مبكرًا. خطط، واشتراكات، وفواتير — حتى لو فوترت يدويًا في البداية. لا تتدارك الأموال لاحقًا.
- افصل الكود عن التهيئة. كل ما يتغيّر بحسب العميل أو المنطقة هو بيانات. ينبغي أن تكون الدولة رقم 41 استمارة دمج.
- اكتب مسار تزويد المستأجر أولًا. إنشاء مستأجر جديد على نحو نظيف، بقيم افتراضية معقولة، هو عمليتك الأكثر تنفيذًا. أتمتها منذ اليوم الأول.
لا شيء من هذا غريب. إنها بضعة أسابيع من الانضباط في البداية مقابل ألّا تضطر أبدًا إلى إعادة الكتابة. الفرق التي تبلغ 40 دولة على قاعدة كود واحدة لم يحالفها الحظ — بل رفضت ببساطة أن تقول «لاحقًا» بشأن الأمور الثلاثة التي لا يمكن إضافتها لاحقًا.

