مراجعة كود Go: ماذا سيقول الخبير ولماذا لن يمر كودك

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

كود Go الخاص بك: من "يعمل" إلى "مقبول"

\n\n

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

\n\n

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

\n\n

هيا بنا نحلل الأخطاء النموذجية في مراجعة كود Golang.

\n\n

1. معالجة الأخطاء: ليس فقط if err != nil

\n\n

المشكلة: تجاهل الأخطاء أو "return" عارية

\n\n

المشكلة الأكثر شيوعًا للمبتدئين هي معالجة الأخطاء غير الغنية بالمعلومات. مجرد إعادة الخطأ إلى الأعلى هو 50٪ من المهمة. الخبير سيسأل دائمًا: "ما الخطأ الذي حدث؟ أين؟".

\n\n
// سيئ: الخبير سيقول "أضف سياقًا"\nfunc ReadConfig(path string) (*Config, error) {\n    data, err := os.ReadFile(path)\n    if err != nil {\n        return nil, err // ❌ لا يوجد سياق\n    }\n    // ...\n}\n
\n\n

النهج الصحيح هو تغليف الأخطاء باستخدام fmt.Errorf والعلامة %w (للتحقق اللاحق عبر errors.Is أو errors.As).

\n\n
// جيد: الخبير سيوافق\nfunc ReadConfig(path string) (*Config, error) {\n    data, err := os.ReadFile(path)\n    if err != nil {\n        return nil, fmt.Errorf("read config file %s: %w", path, err) // ✅ سياق + سلسلة\n    }\n    // ...\n}\n
\n\n

المشكلة: تكرار if err != nil

\n\n

تشتهر Go بالإسهاب في معالجة الأخطاء. لكن هذا لا يعني أنه يجب عليك قبول النسخ واللصق. سيقترح الخبير استخراج الفحوصات المتكررة إلى دوال مساعدة أو استخدام نمط "errgroup" للـ goroutines.

\n\n
    \n
  • لا تفعل هذا: 10 مرات متتالية if err != nil { return ... } في دالة واحدة أمر طبيعي، ولكن إذا تكرر النمط في دوال مختلفة، فقد حان وقت إعادة الهيكلة.
  • \n
  • نصيحة الخبير: استخدم defer لإغلاق الموارد والتحقق من أخطاء الإغلاق (نعم، file.Close() يعيد خطأ أيضًا).
  • \n
\n\n

2. التزامن: سباقات البيانات والقنوات غير الصحيحة

\n\n

المشكلة: الوصول إلى متغير مشترك دون مزامنة

\n\n

Go هي لغة حيث الـ goroutines رخيصة وسباقات البيانات باهظة الثمن. سيلاحظ الخبير على الفور إذا كنت تكتب في map أو slice من عدة goroutines دون mutex.

\n\n
// سيئ: سباق بيانات\nvar counter int\nfor i := 0; i < 100; i++ {\n    go func() {\n        counter++ // ❌ سباق!\n    }()\n}\n
\n\n

الحل: استخدام sync.Mutex أو العمليات الذرية. لكن الخبير سيقول: "أليس من الأسهل استخدام القنوات؟".

\n\n
// جيد: القنوات للمزامنة\nch := make(chan int, 100)\nfor i := 0; i < 100; i++ {\n    go func() {\n        ch <- 1\n    }()\n}\nvar counter int\nfor i := 0; i < 100; i++ {\n    counter += <-ch // ✅ بدون سباقات\n}\n
\n\n

المشكلة: تسرب الـ goroutines

\n\n

هل نسيت إغلاق القناة؟ الـ goroutine ستعلق إلى الأبد. سيتحقق الخبير في مراجعة كود Go بالتأكيد مما إذا كان لديك goroutine معلقة بدون select مع ctx.Done().

\n\n
// سيئ: قد تعلق الـ goroutine إلى الأبد\ngo func() {\n    for msg := range ch {\n        process(msg)\n    }\n}()\n// إذا لم تُغلق ch أبدًا - تسرب ذاكرة\n
\n\n

النمط الصحيح هو دائمًا تمرير context.Context والاستماع إلى إشارة الإلغاء.

\n\n
// جيد: حماية من التسرب\ngo func(ctx context.Context) {\n    for {\n        select {\n        case msg, ok := <-ch:\n            if !ok {\n                return // القناة مغلقة\n            }\n            process(m                

المدونات

توصيات الكتب