تعرف على مطالبات Claude Code لترحيلات PostgreSQL لتغييرات expand-contract الآمنة، ملء البيانات، وخطط الاسترجاع، وما الذي يجب التحقق منه في التجريبية قبل الإصدار.

يبدو تغيير مخطط PostgreSQL بسيطًا حتى يواجه حركة فعلية وبيانات حقيقية. الجزء الخطير عادةً ليس SQL نفسه، بل عندما تتوقف شيفرة التطبيق، حالة قاعدة البيانات، وتوقيت النشر عن التطابق.
أغلب الفشل عملي ومؤلم: نشر يكسر لأن شيفرة قديمة تصل إلى عمود جديد، ترحيل يأخذ قفلًا على جدول ساخن وتزداد مهلات الوقت، أو تغيير "سريع" يحذف أو يعيد كتابة بيانات بصمت. حتى عندما لا يتعطل شيء، قد ترسل أخطاء دقيقة مثل قيم افتراضية خاطئة، قيود مكسورة، أو فهارس لم تنتهِ من البناء.
الترحيلات المولَّدة بالذكاء الاصطناعي تضيف طبقة مخاطرة أخرى. الأدوات قد تنتج SQL صالحًا لكنه غير آمن لحجم عملك، حجم بياناتك، أو عملية الإصدار. قد تخمن أسماء الجداول، تغفل الأقفال طويلة الأمد، أو تتهرب من خطة الاسترجاع لأن التراجعات صعبة. إذا كنت تستخدم Claude Code للترحيلات، فاحتج إلى حواجز واقية وسياق ملموس.
عندما يذكر هذا المنشور أن تغييرًا "آمنًا"، فالمعنى ثلاثة أمور:
الهدف أن تصبح الترحيلات عملًا روتينيًا: متوقعًا، قابلًا للاختبار، ومملاً.
ابدأ ببضعة قواعد غير قابلة للتفاوض. تبقي النموذج مركزًا وتمنعك من شحن تغيير يعمل فقط على جهازك المحلي.
قسّم العمل إلى خطوات صغيرة. تغيير المخطط، ملء البيانات، تعديل التطبيق، وخطوة التنظيف كلها مخاطر مختلفة. ربطها يجعل رؤية الخطأ أصعب ويجعل الاسترجاع أصعب.
فضّل التغييرات الإضافية قبل المدمرة. إضافة عمود أو فهرس أو جدول عادةً خطره منخفض. إعادة تسمية أو حذف الكائنات حيث تحدث الأعطال. نفّذ الجزء الآمن أولًا، حرّك التطبيق، ثم أزل القديم فقط عندما تتأكد أنه غير مستخدم.
اجعل التطبيق يتحمل الشكلين لفترة. يجب أن تقرأ الشيفرة إما العمود القديم أو الجديد أثناء النشر. هذا يتجنّب السباق الشائع حيث تشغّل بعض الخوادم شيفرة جديدة بينما قاعدة البيانات لا تزال قديمة (أو العكس).
عامل الترحيلات ككود إنتاجي، ليس سكربتًا سريعًا. حتى إن كنت تبني بمنصة مثل Koder.ai (خلفية Go مع PostgreSQL، بالإضافة إلى عملاء React أو Flutter)، فقاعدة البيانات مشتركة بين كل شيء. الأخطاء مكلفة.
إذا أردت مجموعة قواعد مضغوطة لوضعها في أعلى كل طلب SQL، استخدم شيء مثل:
مثال عملي: بدلًا من إعادة تسمية عمود يعتمد عليه التطبيق، أضف العمود الجديد، املأه ببطء، انشر شيفرة تقرأ الجديد ثم القديم، وفقط لاحقًا احذف العمود القديم.
يمكن لـ Claude كتابة SQL جيد من طلب غامض، لكن الترحيَلات الآمنة تحتاج سياقًا. عامل طلبك كموجز تصميم صغير: أرِ ما هو موجود، اشرح ما يجب ألا يتعطل، وحدد ماذا يعني "آمن" لعملية النشر.
ابدأ بلصق الوقائع المتعلقة بالقاعدة التي تهم فقط. ضمّن تعريف الجدول بالإضافة إلى الفهارس والقيود ذات الصلة (المفاتيح الأساسية، قيود UNIQUE، المفاتيح الأجنبية، قيود CHECK، المشغلات). إذا كانت جداول مرتبطة متضمنة، ألصق مقتطفاتها أيضًا. مُقتطف دقيق وصغير يمنع النموذج من التخمين بأسماء أو إغفال قيد مهم.
أضف المقياس الواقعي. أعداد الصفوف، حجم الجدول، معدل الكتابة، وذروة الحركة يجب أن تغيّر الخطة. "200M صفوف و1k كتابة/ث" مختلفة عن "20k صف ومعظمها قراءات". أضف أيضًا نسخة Postgres وكيف تُشغَّل الترحيلات في نظامك (معاملة واحدة مقابل خطوات متعددة).
صف كيف يستخدم التطبيق البيانات: القراءات والكتابات والمهام الخلفية المهمة. أمثلة: "الـ API يقرأ بالـ email"، "العمال يحدثون الحالة"، أو "التقارير تفحص حسب created_at". هذا يحدد إذا كنت بحاجة إلى expand/contract، أعلام ميزة، ومدى أمان الملء العكسي.
وأخيرًا، كن صريحًا بشأن القيود والتسليمات. هيكل بسيط يعمل جيدًا:
طلب SQL وخطة تشغيل يجبر النموذج على التفكير في الترتيب، المخاطر، وما يجب فحصه قبل الشحن.
نمط التوسيع/التقلص يغيّر قاعدة بيانات PostgreSQL بدون كسر التطبيق أثناء التغيير. بدلًا من تبديل محفوف بالمخاطر، تجعل القاعدة تدعم الشكلين القديم والجديد لفترة.
فكّر فيه كالتالي: أضف أشياء جديدة بأمان (expand)، حوّل الحركة والبيانات تدريجيًا، ثم إحذف الأجزاء القديمة (contract). هذا مفيد خصوصًا عند العمل بمساعدة AI لأنه يجبرك على التخطيط للمرحلة المتوسطة غير النظيفة.
تتدفّق العملية عمليًا كالتالي:
استخدم هذا النمط كلما قد يبقى المستخدمون على شيفرة التطبيق القديمة أثناء تغيير قاعدة البيانات. ذلك يشمل النشرات متعددة النسخ، تطبيقات الهواتف التي تتحدّث ببطء، أو أي إصدار قد يستغرق الترحيل فيه دقائق أو ساعات.
تكتيك مفيد هو التخطيط لإصدارين. الإصدار 1 يفعل التوسيع والتوافق بحيث لا ينكسر شيء إن كان الملء العكسي غير مكتمل. الإصدار 2 يفعل التقلص فقط بعد التأكد من وجود الشيفرة والبيانات الجديدة.
انسخ هذا القالب واملأ الأقواس. يدفع Claude Code لإنتاج SQL يمكنك تشغيله، فحوص لإثبات النجاح، وخطة استرجاع يمكنك اتباعها فعليًا.
You are helping me plan a PostgreSQL expand-contract migration.
Context
- App: [what the feature does, who uses it]
- Database: PostgreSQL [version if known]
- Table sizes: [rough row counts], write rate: [low/medium/high]
- Zero/near-zero downtime required: [yes/no]
Goal
- Change: [describe the schema change]
- Current schema (relevant parts):
[paste CREATE TABLE or \d output]
- How the app will change (expand phase and contract phase):
- Expand: [new columns/indexes/triggers, dual-write, read preference]
- Contract: [when/how we stop writing old fields and remove them]
Hard safety requirements
- Prefer lock-safe operations. Avoid full table rewrites on large tables when possible.
- If any step can block writes, call it out explicitly and suggest alternatives.
- Use small, reversible steps. No “big bang” changes.
Deliverables
1) UP migration SQL (expand)
- Use clear comments.
- If you propose indexes, tell me if they should be created CONCURRENTLY.
- If you propose constraints, tell me whether to add them NOT VALID then VALIDATE.
2) Verification queries
- Queries to confirm the new schema exists.
- Queries to confirm data is being written to both old and new structures (if dual-write).
- Queries to estimate whether the change caused bloat/slow queries/locks.
3) Rollback plan (realistic)
- DOWN migration SQL (only if it is truly safe).
- If down is not safe, write a rollback runbook:
- how to stop the app change
- how to switch reads back
- what data might be lost or need re-backfill
4) Runbook notes
- Exact order of operations (including app deploy steps).
- What to monitor during the run (errors, latency, deadlocks, lock waits).
- “Stop/continue” checkpoints.
Output format
- Separate sections titled: UP.sql, VERIFY.sql, DOWN.sql (or ROLLBACK.md), RUNBOOK.md
سطران إضافيان يساعدان في الممارسة:
RISK: blocks writes، ومتى تُشغّلها (خارج الذروة أم في أي وقت).التغييرات الصغيرة لا تزال قد تضرك إذا أخذت أقفالًا طويلة، أعادت كتابة جداول كبيرة، أو فشلت منتصف الطريق. عند استخدام Claude Code للترحيلات، اطلب SQL يتجنب إعادة الكتابة ويجعل التطبيق يعمل بينما القاعدة تواكب.
إضافة عمود قابل لأن يكون NULL عادةً آمنة. إضافة عمود مع قيمة افتراضية غير فارغة يمكن أن تكون خطرة في إصدارات Postgres القديمة لأنها قد تعيد كتابة الجدول بأكمله.
نهج أكثر أمانًا هو تغيير من خطوتين: أضف العمود كـ NULL بدون قيمة افتراضية، املأه على دفعات، ثم عيّن القيمة الافتراضية للصفوف الجديدة وأضف NOT NULL بعد أن تتأكد من نظافة البيانات.
إذا لا بُد من فرض قيمة افتراضية فورًا، اطلب تفسيرًا عن سلوك الأقفال لإصدار Postgres لديك وخطة بديلة إذا طالت المدة المتوقعة.
بالنسبة للفهارس على جداول كبيرة، اطلب CREATE INDEX CONCURRENTLY حتى تستمر القراءة والكتابة. اطلب أيضًا ملاحظة أنه لا يمكن تشغيله داخل كتلة معاملة مما يعني أن أداة الترحيل تحتاج خطوة خارج المعاملة.
بالنسبة للمفاتيح الأجنبية، الطريق الآمن عادةً هو إضافة القيد كـ NOT VALID أولًا، ثم التحقق لاحقًا. هذا يجعل التغيير الأولي أسرع بينما ما زال يفرض الـ FK للكتابات الجديدة.
عند تشديد القيود (NOT NULL, UNIQUE, CHECK)، اطلب "نظف أولًا، فرض ثانيًا." يجب على الترحيل اكتشاف الصفوف السيئة، إصلاحها، ثم تفعيل القاعدة الصارمة.
إذا أردت قائمة تحقق قصيرة للصق في الطلبات، اجعلها ضيقة:
الملء العكسي هو المكان الذي يظهر فيه معظم ألم الترحيل، وليس ALTER TABLE. الطلبات الآمنة تتعامل مع الملء كوظائف متحكم بها: قابلة للقياس، قابلة للاستئناف، ولطيفة على الإنتاج.
ابدأ بفحوص قبول سهلة التشغيل وصعبة الجدل: أعداد الصفوف المتوقعة، معدل القيم الفارغة المستهدف، وبعض الفحوص العشوائية (مثلاً قارن القديم والجديد لعشرين ID عشوائي).
ثم اطلب خطة دفعات. الدُفعات تقصر الأقفال وتقلّل المفاجآت. طلب جيد يحدد:
اشترط القابلية لإعادة التشغيل لأن الملء قد يفشل منتصف الطريق. يجب أن يكون SQL آمنًا لإعادة التشغيل دون تكرار أو تلف البيانات. أنماط نموذجية: "حدث فقط حيث new_col IS NULL" أو قاعدة حتمية تجعل نفس المدخل دائمًا ينتج نفس المخرج.
أيضًا اشرح كيف يبقى التطبيق صحيحًا أثناء الملء. إذا استمرت الكتابات الجديدة أثناء الملء، تحتاج جسرًا: كتابة مزدوجة في شيفرة التطبيق، مشغّل مؤقت مؤقت، أو منطق قراءة بديلة (اقرأ الجديد عندما يكون موجودًا، وإلا القديم). قل أي نهج يمكنك نشره بأمان.
أخيرًا، بني إيقاف واستئناف في التصميم. اطلب تتبُّع التقدم ونقاط تحقق، مثل جدول صغير يخزن آخر ID مُعالَج واستعلام يعرض التقدّم (الصفوف المحدثة، آخر ID، وقت البدء).
مثال: تضيف users.full_name مستخرجة من first_name وlast_name. ملء آمن يحدث فقط الصفوف حيث full_name IS NULL، يعمل بنطاقات ID، يسجل آخر ID محدث، ويبقي التسجيلات الجديدة صحيحة عبر كتابة مزدوجة حتى اكتمال التبديل.
خطة الاسترجاع ليست مجرد "اكتب DOWN migration." إنها مشكلتان: التراجع في المخطط والتعامل مع أي بيانات تغيَّرت بينما النسخة الجديدة كانت حية. استرجاع المخطط غالبًا ممكن. استرجاع البيانات غالبًا لا يكون ممكنًا إلا إذا خططت لذلك.
كن صريحًا بشأن ماذا يعني الاسترجاع لتغييرك. إذا كنت ستحذف عمودًا أو تعيد كتابة قيمًا في المكان، اطلب إجابة واقعية مثل: "الاسترجاع يعيد توافق التطبيق، لكن البيانات الأصلية لا تُستعاد دون لقطة." تلك الصراحة تحافظ على أمانك.
اطلب محفزات استرجاع واضحة حتى لا يجادل أحد أثناء الحادث. أمثلة:
اشترط حزمة الاسترجاع كاملة، ليس مجرد SQL: DOWN SQL (فقط إن كان آمنًا)، خطوات شيفرة/تهيئة التطبيق اللازمة للتوافق، وكيفية إيقاف مهام الخلفية.
هذا النمط عادةً كافٍ:
Produce a rollback plan for this migration.
Include: down migration SQL, app config/code switches needed for compatibility, and the exact order of steps.
State what can be rolled back (schema) vs what cannot (data) and what evidence we need before deciding.
Include rollback triggers with thresholds.
قبل الشحن، التقط "لقطة أمان" خفيفة للمقارنة قبل وبعد:
وكن واضحًا أيضًا عن متى لا تسترجع. إذا أضفت عمودًا قابلًا لأن يكون NULL والتطبيق يكتب مزدوجًا، قد يكون التصحيح المتقدّم (hotfix للشيفرة، إيقاف الملء مؤقتًا، ثم استئناف) أكثر أمانًا من التراجع وإحداث انحرافات أكبر.
يمكن للذكاء الاصطناعي كتابة SQL بسرعة، لكنه لا يرى قاعدة بياناتك الإنتاجية. معظم الإخفاقات تحدث عندما يكون الطلب غامضًا والنموذج يملأ الفراغات.
فخ شائع هو تخطي المخطط الحالي. إن لم تلصق تعريف الجدول، الفهارس، والقيود، قد يستهدف SQL أعمدة غير موجودة أو يغفل قاعدة تفوقية تجعل الملء بطيئًا ويأخذ أقفالًا.
خطأ آخر هو شحن expand، backfill، وcontract في نشر واحد. هذا يزيل طريق الهروب. إذا أخذ الملء وقتًا طويلاً أو أخطأ منتصف الطريق، تصبح في موقف حيث تتوقع الشيفرة الحالة النهائية.
القضايا التي تظهر غالبًا:
مثال ملموس: "إعادة تسمية عمود وتحديث التطبيق." إن كانت الخطة المولّدة تعيد تسمية وتملأ في معاملة واحدة، قد يحمل الملء الطويل أقفالًا ويكسر حركة الإنتاج. طلب أكثر أمانًا يجبر دفعات صغيرة، حدود زمنية صريحة، واستعلامات تحقق قبل إزالة المسار القديم.
التجريبية هي المكان الذي تجد فيه مشاكل لا تظهر أبدًا على قاعدة بيانات صغيرة في التطوير: أقفال طويلة، قيم فارغة مفاجئة، فهارس مفقودة، ومسارات الشيفرة المنسية.
أولًا، تحقق أن المخطط يطابق الخطة بعد الترحيل: الأعمدة، الأنواع، القيم الافتراضية، القيود، والفهارس. نظرة سريعة غير كافية. فهرس مفقود واحد يمكن أن يحوّل ملءًا آمنًا إلى فوضى بطيئة.
ثم شغّل الترحيل على مجموعة بيانات واقعية. ويفضل أن تكون نسخة حديثة من الإنتاج مع إخفاء الحقول الحساسة. إن لم تستطع، فطابق حجم الإنتاج ونقاط الضغط (الجداول الكبيرة، الصفوف العريضة، الجداول ذات الفهارس الكثيرة). سجّل أوقات كل خطوة لتعرف ما تتوقعه في الإنتاج.
قائمة تحقق قصيرة للتجريبية:
أخيرًا، اختبر تدفقات المستخدم الحقيقية، وليس SQL فقط. أنشئ، حدّث، واقرأ سجلات المتأثرة بالتغيير. إن كان expand/contract هو الخطة، أكد أن كلا المخططين يعملان حتى التنظيف النهائي.
تخيّل أن لديك عمود users.name يخزن أسماء كاملة مثل "Ada Lovelace." تريد first_name وlast_name، لكن لا يمكنك كسر التسجيلات، الصفحات الشخصية، أو شاشات الإدارة أثناء الدوران.
ابدأ بخطوة توسيع آمنة حتى لو لم تُنشر شيفرة بعد: أضف أعمدة قابلة لأن تكون NULL، احتفظ بالعمود القديم، وتجنّب الأقفال الطويلة.
ALTER TABLE users ADD COLUMN first_name text;
ALTER TABLE users ADD COLUMN last_name text;
ثم حدّث سلوك التطبيق لدعم كلا المخططين. في الإصدار 1، يجب على التطبيق أن يقرأ من الأعمدة الجديدة عند وجودها، يعود إلى name عندما تكون فارغة، ويكتب لكلاهما حتى تبقى البيانات الجديدة متسقة.
بعدها يأتي الملء العكسي. شغّل وظيفة دفعات تحدث قطعة صغيرة من الصفوف في كل مرة، تسجل التقدّم، ويمكن إيقافها بأمان. مثال: حدّث users حيث first_name هو null بترتيب تصاعدي للـ ID، ألف صف في كل مرة، وسجل كم صفًا تغيّر.
قبل تشديد القواعد، تحقّق في التجريبية:
first_name وlast_name وما زالت تضع namename فقط موجودًاusers ليست أبطأ بشكل ملحوظالإصدار 2 يبدّل القراءات إلى الأعمدة الجديدة فقط. وفقط بعد ذلك يجب إضافة القيود (مثل SET NOT NULL) وحذف name، ويفضل في نشر لاحق منفصل.
للاسترجاع، اجعلها عادية. التطبيق يستمر في قراءة name أثناء الانتقال، والملء العكسي قابل للإيقاف. إن احتجت للتراجع عن الإصدار 2، أعد توجيه القراءات إلى name واترك الأعمدة الجديدة حتى تستقر مرة أخرى.
عامل كل تغيير كدليل تشغيل صغير. الهدف ليس طلب مثالي، بل روتين يجبر التفاصيل الصحيحة: المخطط، القيود، خطة التشغيل، والاسترجاع.
وحد ما يجب أن يتضمّنه كل طلب ترحيل:
قرّر من يملك كل خطوة قبل أن يُشغّل أي أحد SQL. تقسيم بسيط يمنع "الكل ظن شخصًا آخر قام به": المطورون يملكون الطلب والشيفرة، العمليات تملك توقيت الإنتاج والمراقبة، الـ QA يتحقّق من السلوك التجريبي والحالات الحديّة، وشخص واحد هو قرار الاستمرار/التراجع النهائي.
إذا كنت تبني تطبيقات عبر الدردشة، قد يساعد تفصيل التسلسل قبل توليد أي SQL. للفرق التي تستخدم Koder.ai، وضع التخطيط (Planning Mode) مكان مناسب لكتابة التسلسل، واللقطات إلى جانب الاسترجاع قد تقلل المساحة المتضررة إذا حدث شيء غير متوقع أثناء النشر.
بعد الشحن، جدولة تنظيف الـ contract فورًا بينما السياق لا يزال طازجًا، حتى لا تتراكم الأعمدة القديمة وشيفرة التوافق المؤقتة لأشهر.
يُصبح تغيير المخطط خطيرًا عندما تتوقف شيفرة التطبيق وحالة قاعدة البيانات وتوقيت النشر عن التطابق.
أوضاع الفشل الشائعة:
استخدم نهج expand/contract الآمن:
هذا يحافظ على عمل نسختي التطبيق القديمة والجديدة أثناء التدرّج.
لأن النموذج قد يولّد SQL صحيحًا لكنه غير آمن لعبء عملك.
مخاطر خاصة بالذكاء الاصطناعي:
عامل مخرجات AI كمسودة واطلب خطة تشغيل وفحوص وخطوات استرجاع.
أدرج فقط الحقائق التي يعتمد عليها الترحيل:
CREATE TABLE ذات الصلة (مع الفهارس، FK، قيود UNIQUE/CHECK، المشغلات)القاعدة الافتراضية: افصلهم.
انقسام عملي:
تجميع كل شيء يجعل الأخطاء أصعب في التشخيص والاسترجاع.
اتّبع هذا النمط العملي:
ADD COLUMN ... NULL بدون قيمة افتراضية (سريع)NOT NULL فقط بعد التحققإضافة قيمة افتراضية غير فارغة فورًا قد تكون خطرة على بعض إصدارات Postgres لأنها قد تعيد كتابة الجدول بأكمله. إذا كنت بحاجة لقيمة فورية، اطلب ملاحظات عن سلوك القفل وخطة بديلة آمِنة.
اطلب:
CREATE INDEX CONCURRENTLY للجداول الكبيرة/الحارةللتأكد، تضمّن فحصًا سريعًا لوجود الفهرس واستخدامه (مثلاً قارن خطة EXPLAIN قبل/بعد في البيئة التجريبية).
استخدم NOT VALID أولًا ثم قم بالتحقق لاحقًا:
NOT VALID لتكون الخطوة الأولية أقل تعطيلًاVALIDATE CONSTRAINT في خطوة منفصلة عندما تكون جاهزًا لمراقبتههذا يفرض FK للكتابات الجديدة، مع السماح لك بالتحكم متى يحدث التحقق المكلف.
الملء العكسي الجيد يكون مجزأًا، قابلًا لإعادة التشغيل، وآمنًا ضد التكرار.
متطلبات عملية:
WHERE new_col IS NULL)الهدف الافتراضي للاسترجاع: استعادة توافق التطبيق بسرعة، حتى لو لم يُستعدّت البيانات تمامًا.
خطة استرجاع عملية يجب أن تتضمّن:
غالبًا ما يكون أبسط استرجاع هو إعادة التوجيه للقراءة إلى الحقل القديم مع ترك الأعمدة الجديدة مكانها.
هذا يمنع التخمين ويجبر الترتيب الصحيح.
هذا يجعل الملء العكسي قابلًا للتحمّل تحت الحمل الحقيقي.