كانت مهامنا تتنفذ بعشوائية: كيف أنقذنا ‘الفرز الطوبولوجي’ من جحيم الاعتماديات المتشابكة؟

يوم لا يُنسى في مكتبي: قهوة باردة وكود عالق

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

صراخ الإشعارات لم يتوقف: “المهمة X فشلت!”، وبعدها بدقائق: “فشل في المهمة Y”. كان الفريق في حالة ضياع. المبرمج المسؤول عن المهمة X يصرخ: “المهمة فشلت عشان المهمة Z لسا ما جهزت الملفات المطلوبة!”. ويرد عليه زميله: “بس Z بتستنى نتيجة من المهمة A اللي أنت لسا ما شغلتها!”. فوضى عارمة، أو كما نقول بلهجتنا “عجقة ما إلها أول من آخر”.

كنا كمن يحاول بناء بيت من الطوب، لكن كل عامل يضع طوبة في مكان عشوائي دون انتظار من يسبقه. جلستُ على كرسيي، أخذت نفساً عميقاً، وأغلقت كل النوافذ المفتوحة على شاشتي. المشكلة لم تكن في كود المهام نفسها، بل في ترتيب تنفيذها. كانت المشكلة مشكلة “اعتماديات” (Dependencies). وهنا، لمعت في رأسي فكرة قديمة من أيام الجامعة، خوارزمية أنيقة وبسيطة اسمها: الفرز الطوبولوجي (Topological Sort).

شو قصة ‘الفرز الطوبولوجي’ هاد؟

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

تبسيط المفهوم: من وصفة الكنافة إلى الكود

تخيل أنك تريد تحضير الكنافة النابلسية على أصولها. هناك خطوات واضحة لا يمكن تجاوزها:

  1. تجهيز العجينة وفركها بالسمنة.
  2. فرد نصف العجينة في الصينية.
  3. وضع الجبنة.
  4. تغطيتها بالنصف الآخر من العجينة.
  5. خبزها في الفرن حتى تتحمر.
  6. قلبها في صينية التقديم.
  7. تشريبها بالقطر الساخن.

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

الأساس النظري: الرسوم البيانية الموجهة غير الدائرية (DAGs)

في عالم الخوارزميات، نحن نمثل هذه الاعتماديات باستخدام ما يسمى بـ “الرسم البياني الموجه غير الدائري” أو (Directed Acyclic Graph – DAG). لا تدع الاسم يخيفك، فالأمر بسيط:

  • رسم بياني (Graph): مجموعة من النقاط (Nodes/Vertices) التي تمثل المهام، وخطوط (Edges) تصل بينها تمثل الاعتماديات.
  • موجه (Directed): كل خط له اتجاه. سهم من المهمة (أ) إلى المهمة (ب) يعني أن (أ) يجب أن تنتهي قبل أن تبدأ (ب).
  • غير دائري (Acyclic): أهم شرط! لا يمكن أن توجد حلقة. أي لا يمكن أن تعتمد (أ) على (ب)، و(ب) على (ج)، ثم (ج) تعود وتعتمد على (أ). هذا سيؤدي إلى حلقة لا نهائية من الانتظار، وهو ما نسميه “الاعتمادية الدائرية” (Circular Dependency)، وهو العدو اللدود للفرز الطوبولوجي.

إذن، الفرز الطوبولوجي هو خوارزمية تأخذ هذا الرسم البياني (DAG) وتعطيك ترتيباً خطياً واحداً (أو أكثر) ممكناً للمهام.

طيب يا أبو عمر، كيف بتشتغل هاي الخوارزمية؟

هناك عدة طرق لتطبيقها، لكن أشهرها وأكثرها عملية، من وجهة نظري، هي خوارزمية “كان” (Kahn’s Algorithm). هي بسيطة ومنطقية جداً.

المكونات الأساسية: شو بيلزمنا؟

لتطبيق الخوارزمية، نحتاج إلى تمثيل الرسم البياني، وأهم شيء هو تتبع “درجة الدخول” (In-degree) لكل مهمة. درجة الدخول هي ببساطة عدد المهام التي تشير إلى مهمة معينة (أي عدد المهام التي يجب أن تنتهي قبلها).

الطريقة خطوة بخطوة (خوارزمية كان)

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

  1. حساب درجة الدخول (In-degree): نمر على كل المهام ونحسب كم سهم يدخل إليها.
  2. تجهيز الطابور (Queue): ننشئ طابوراً (queue) ونضع فيه كل المهام التي درجة دخولها تساوي صفر. هذه هي المهام التي يمكن أن نبدأ بها فوراً.
  3. بدء المعالجة: طالما أن الطابور ليس فارغاً:
    • اسحب مهمة من الطابور (لنسميها “المهمة الحالية”).
    • أضف “المهمة الحالية” إلى قائمتنا النهائية المرتبة.
    • لكل مهمة “مجاورة” (أي كل مهمة كانت تعتمد على “المهمة الحالية”):
      • نقص درجة دخولها بمقدار 1 (لأننا انتهينا من أحد متطلباتها).
      • إذا أصبحت درجة دخول المهمة “المجاورة” تساوي صفر، أضفها إلى الطابور.
  4. التحقق من النتيجة: بعد انتهاء الحلقة، إذا كان عدد المهام في قائمتنا المرتبة يساوي العدد الإجمالي للمهام، فقد نجحنا! أما إذا كان أقل، فهذا يعني وجود “حلقة دائرية” في الاعتماديات، والخوارزمية لم تستطع معالجة كل المهام.

ورجينا الكود، يا حج!

الكلام النظري جميل، لكن الكود هو الذي يوضح كل شيء. إليك مثال بسيط بلغة Python يطبق خوارزمية كان:


from collections import deque

def topological_sort(graph):
    """
    يقوم بتنفيذ الفرز الطوبولوجي على رسم بياني موجه.
    الرسم البياني يمثل كقاموس حيث المفاتيح هي العقد (المهام)
    والقيم هي قوائم بالعقد التي تعتمد عليها.
    مثال: graph = {'C': ['A', 'B'], 'D': ['C']}
    """
    
    # الخطوة 0: بناء قائمة مجاورة وحساب درجة الدخول
    in_degree = {node: 0 for node in graph}
    # ملاحظة: سنحتاج إلى كل العقد، حتى التي لا تعتمد على شيء
    all_nodes = set(graph.keys())
    for dependencies in graph.values():
        for node in dependencies:
            all_nodes.add(node)
    
    in_degree = {node: 0 for node in all_nodes}
    adj_list = {node: [] for node in all_nodes}

    for node, dependencies in graph.items():
        for dep in dependencies:
            # السهم من dep إلى node
            adj_list[dep].append(node)
            in_degree[node] += 1

    # الخطوة 1: إيجاد كل العقد التي درجة دخولها صفر
    queue = deque([node for node in all_nodes if in_degree[node] == 0])
    
    sorted_order = []
    
    # الخطوة 2: بدء المعالجة
    while queue:
        current_node = queue.popleft()
        sorted_order.append(current_node)
        
        # لكل جار، قلل درجة دخوله
        for neighbor in adj_list[current_node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)
    
    # الخطوة 3: التحقق من وجود حلقة دائرية
    if len(sorted_order) == len(all_nodes):
        return sorted_order
    else:
        return "خطأ: الرسم البياني يحتوي على حلقة دائرية!"

# --- مثال الاستخدام ---
# لنعد إلى مشكلة فريقنا
tasks = {
    'المهمة X': ['المهمة Z'],    # X تعتمد على Z
    'المهمة Y': ['المهمة X'],    # Y تعتمد على X
    'المهمة Z': ['المهمة A'],    # Z تعتمد على A
    'المهمة A': []             # A لا تعتمد على شيء
}

sorted_tasks = topological_sort(tasks)
print("الترتيب الصحيح لتنفيذ المهام:")
print(sorted_tasks)
# الإخراج المتوقع (أحد الاحتمالات): ['المهمة A', 'المهمة Z', 'المهمة X', 'المهمة Y']

وين كمان ممكن نستخدمها؟

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

  • أنظمة البناء (Build Systems): أدوات مثل `make` أو `Maven` أو `Gradle` تستخدمها لتحديد أي الملفات يجب ترجمتها (compile) أولاً.
  • مديرو الحزم (Package Managers): عندما تقوم بتثبيت مكتبة باستخدام `npm` أو `pip`، يقوم مدير الحزم بحل شجرة الاعتماديات باستخدام الفرز الطوبولوجي ليثبت المكتبات بالترتيب الصحيح.
  • جدولة المهام (Task Schedulers): في أنظمة التشغيل أو أدوات مثل Apache Airflow، لتنفيذ سلسلة من المهام بالترتيب الصحيح.
  • المتطلبات الدراسية في الجامعات: لإنشاء مسار دراسي صحيح، لا يمكنك أخذ “خوارزميات 2” قبل “خوارزميات 1”.
  • جداول البيانات (Spreadsheets): عندما تغير قيمة في خلية `A1`، يجب إعادة حساب كل الخلايا التي تعتمد عليها (`=A1*2`) بالترتيب الصحيح.

نصايح من الخُبرة (من الشايب)

بعد سنوات من التعامل مع هذه المشاكل، اسمحوا لي أن أقدم لكم بعض النصائح العملية:

  1. اكتشاف الحلقات هو الأهم: تأكد دائماً من أن الكود الخاص بك يكتشف الحلقات الدائرية. هذه هي أكبر مصدر للمشاكل، والإبلاغ عنها بوضوح يوفر ساعات من التنقيح (debugging).
  2. لا يوجد حل واحد “صحيح”: قد يكون هناك عدة ترتيبات طوبولوجية صالحة. إذا كانت المهمة (أ) والمهمة (ب) لا تعتمدان على شيء، فيمكن البدء بأي منهما. لا تبحث عن “الترتيب الأمثل” بل عن “ترتيب صالح”.
  3. ارسمها لترىها: عندما تشعر بالضياع في الاعتماديات، أحضر قلماً وورقة (أو استخدم سبورة) وارسم المهام والأسهم بينها. مرات كثيرة، “الرسمة بتوضح اللي ألف سطر كود ما بيوضحه”.
  4. تعامل مع البيانات جيداً: تأكد من أن هيكل البيانات الذي يمثل الرسم البياني (graph) صحيح ونظيف. القمامة في المدخلات تعني قمامة في المخرجات (Garbage in, garbage out).

الخلاصة: من الفوضى إلى النظام

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

فيا صديقي المبرمج، المرة الجاي اللي بتشوف فيها الدنيا “معجوقة” والمهام بتضرب ببعض، خذ نفس عميق، ارسم الاعتماديات، وجرب الفرز الطوبولوجي. يمكن يكون هو فنجان القهوة الساخن اللي محتاجه الكود تبعك. 😉

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

ذكاء اصطناعي

كانت نماذجنا تتقادم في صمت: كيف أنقذتنا ‘مراقبة انحراف النموذج’ (Model Drift) من جحيم القرارات المتدهورة؟

في هذه المقالة، يشارككم أبو عمر قصة من أرض الواقع عن كيفية تدهور أداء نماذج الذكاء الاصطناعي مع مرور الوقت، وهي ظاهرة تُعرف باسم "انحراف...

27 مايو، 2026 قراءة المزيد
برمجة وقواعد بيانات

كانت تغييرات قاعدة بياناتنا فوضى عارمة: كيف أنقذتنا أدوات الترحيل (Database Migrations) من جحيم “التعديل اليدوي على الإنتاج”؟

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

27 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت إعداداتنا السحابية كتاباً مفتوحاً: كيف أنقذتنا أدوات CSPM من جحيم الثغرات الصامتة؟

أشارككم قصة حقيقية من قلب المعركة التقنية، عندما كادت غلطة بسيطة في الإعدادات السحابية أن تكلفنا كل شيء. اكتشفوا كيف كانت أدوات إدارة وضع الأمان...

27 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كانت إجاباتي في المقابلات السلوكية كارثية: كيف أنقذتني ‘طريقة STAR’ من جحيم ‘حدثنا عن موقف صعب’؟

هل تتجمد عندما يُطلب منك 'الحديث عن موقف صعب' في المقابلات؟ كنت مثلك تمامًا! في هذه المقالة، أشارككم قصتي مع المقابلات السلوكية وكيف حولتني 'طريقة...

27 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت قاعدة بياناتنا على وشك الانهيار: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات المتكررة؟

أشارككم قصة حقيقية من قلب المعركة التقنية، عندما كادت قاعدة بياناتنا أن تنهار تحت ضغط الاستعلامات المتكررة. اكتشفوا كيف كان التخزين المؤقت (Caching) هو طوق...

27 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

كانت بياناتنا البنكية سجينة: كيف أنقذتنا واجهات برمجة التطبيقات المفتوحة (Open Banking) من جحيم الأنظمة المغلقة؟

أنا أبو عمر، مطور برمجيات فلسطيني، وأروي لكم كيف كنا نعاني من سجن بياناتنا المالية في البنوك. في هذه المقالة، سأشارككم قصة تحرير هذه البيانات...

27 مايو، 2026 قراءة المزيد
البودكاست