الوصايا الست للشفرة الجيدة: اكتب رمزًا يصمد أمام اختبار الزمن

نشرت: 2022-03-11

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

كنتيجة لشبابها النسبي ، لا أعتقد أننا حصلنا على إجماع حتى الآن على ما هو التعريف المناسب لـ "الشفرة الجيدة" ، حيث يستمر هذا التعريف في التطور. سيقول البعض أن "الشفرة الجيدة" هي رمز بتغطية اختبارية 100٪. سيقول الآخرون أنه سريع للغاية وله أداء قاتل وسيعمل بشكل مقبول على أجهزة عمرها 10 سنوات.

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

لا تقبل أبدًا برمز "يعمل" فقط. اكتب كود متفوق.
سقسقة

الوصية رقم 1: تعامل مع كودك بالطريقة التي تريد أن يعاملك بها كود الآخرين

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

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

مثال على لغة يعتبرها الكثيرون "نظيفة" وقابلة للقراءة هي Python. تفرض اللغة نفسها مستوى معينًا من انضباط المساحة البيضاء ، كما أن واجهات برمجة التطبيقات المدمجة وفيرة ومتسقة إلى حد ما. ومع ذلك ، من الممكن إنشاء وحوش لا توصف. على سبيل المثال ، يمكن للمرء تحديد فئة وتعريف / إعادة تعريف / إلغاء تحديد أي وكل طريقة في تلك الفئة أثناء وقت التشغيل (يشار إليها غالبًا باسم ترقيع القرود). تؤدي هذه التقنية بشكل طبيعي إلى واجهة برمجة تطبيقات غير متسقة في أحسن الأحوال وفي أسوأ الأحوال يستحيل تصحيح أخطاء الوحش. قد يفكر المرء بسذاجة ، "بالتأكيد ، لكن لا أحد يفعل ذلك!" لسوء الحظ ، يفعلون ذلك ، ولا يستغرق تصفح pypi وقتًا طويلاً قبل أن تصطدم بمكتبات كبيرة (وشائعة!) تستخدم (ab) ترقيع القرود على نطاق واسع باعتباره جوهر واجهات برمجة التطبيقات الخاصة بهم. لقد استخدمت مؤخرًا مكتبة شبكات تتغير واجهة برمجة تطبيقاتها بالكامل اعتمادًا على حالة الشبكة للكائن. تخيل ، على سبيل المثال ، استدعاء client.connect() وأحيانًا تحصل على خطأ MethodDoesNotExist بدلاً من HostNotFound أو NetworkUnavailable .

الوصية رقم 2: الشفرة الجيدة يسهل قراءتها وفهمها ، جزئيًا وكليًا

يمكن قراءة الكود الجيد وفهمه بسهولة ، جزئيًا وكليًا ، من قبل الآخرين (وكذلك من قبل المؤلف في المستقبل ، في محاولة لتجنب متلازمة "هل كتبت ذلك حقًا؟" ).

أعني بكلمة "جزئيًا" أنه إذا فتحت بعض الوحدات أو الوظائف في الكود ، يجب أن أكون قادرًا على فهم ما يفعله دون الحاجة إلى قراءة بقية قاعدة التعليمات البرمجية أيضًا. يجب أن تكون بديهية وذاتية التوثيق قدر الإمكان.

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

بعض الأفكار الأخرى حول قابلية القراءة "المحلية":

  • تميل الشفرة المغلفة جيدًا إلى أن تكون أكثر قابلية للقراءة ، وتفصل الاهتمامات على كل مستوى.

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

  • الذكاء هو العدو. عند استخدام تقنيات أو نماذج أو عمليات خيالية (مثل قوائم الفهم أو العوامل الثلاثية) ، احذر من استخدامها بطريقة تجعل شفرتك أكثر قابلية للقراءة ، وليس فقط أقصر.

  • الاتساق شيء جيد. الاتساق في الأسلوب ، سواء من حيث كيفية وضع الأقواس ولكن أيضًا من حيث العمليات ، يحسن قابلية القراءة بشكل كبير.

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

الوصية رقم 3: يحتوي الرمز الجيد على تخطيط وبنية مدروسة جيدًا لجعل إدارة الحالة واضحة

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

الوصية رقم 4: الرمز الجيد لا يعيد اختراع العجلة ، إنه يقف على أكتاف العمالقة

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

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

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

فيما يلي بعض الأمثلة الأكثر شيوعًا للأشياء التي ربما لا يجب إعادة ابتكارها في العصر الحديث في مشروعك (ما لم تكن هذه مشاريعك).

قواعد بيانات

اكتشف أي من CAP تحتاجه لمشروعك ، ثم اختر قاعدة البيانات بالخصائص الصحيحة. لا تعني قاعدة البيانات MySQL بعد الآن ، يمكنك الاختيار من بين:

  • مخطط SQL "التقليدي": Postgres / MySQL / MariaDB / MemSQL / Amazon RDS ، إلخ.
  • متاجر القيمة الرئيسية: Redis / Memcache / Riak
  • NoSQL: MongoDB / كاساندرا
  • قواعد البيانات المستضافة: AWS RDS / DynamoDB / AppEngine Datastore
  • رفع الأحمال الثقيلة: Amazon MR / Hadoop (Hive / Pig) / Cloudera / Google Big Query
  • أشياء مجنونة: Erlang's Mnesia ، بيانات iOS الأساسية

طبقات تجريد البيانات

يجب ، في معظم الظروف ، ألا تكتب استفسارات أولية لأي قاعدة بيانات اخترت استخدامها. على الأرجح ، توجد مكتبة للجلوس بين قاعدة البيانات ورمز التطبيق الخاص بك ، وتفصل اهتمامات إدارة جلسات قاعدة البيانات المتزامنة وتفاصيل المخطط عن الكود الرئيسي. على الأقل ، يجب ألا يكون لديك استعلامات أولية أو SQL مضمنة في منتصف كود التطبيق الخاص بك. بدلاً من ذلك ، قم بلفها في دالة وقم بمركزية جميع الوظائف في ملف يسمى شيئًا واضحًا حقًا (على سبيل المثال ، "queries.py"). سطر مثل users = load_users() ، على سبيل المثال ، أسهل في القراءة من users = db.query(“SELECT username, foo, bar from users LIMIT 10 ORDER BY ID”) . يسهل هذا النوع من المركزية أيضًا الحصول على نمط متسق في استعلاماتك ، ويحد من عدد الأماكن التي يجب الانتقال إليها لتغيير الاستعلامات في حالة تغيير المخطط.

المكتبات والأدوات الشائعة الأخرى للنظر في الاستفادة منها

  • قائمة الانتظار أو خدمات الحانة / الفرعية. اختر ما يناسبك من موفري AMQP ، ZeroMQ ، RabbitMQ ، Amazon SQS
  • تخزين. Amazon S3 ، Google Cloud Storage
  • المراقبة: الجرافيت / الجرافيت المستضاف ، AWS Cloud Watch ، New Relic
  • تجميع / تجميع السجل. لوغلي ، سبلينك

تحجيم تلقائي

  • تحجيم تلقائي. Heroku ، AWS Beanstalk ، AppEngine ، AWS Opsworks ، المحيط الرقمي

الوصية رقم 5: لا تعبر التيارات!

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

الوصية رقم 6: عندما يكون ذلك ممكنًا ، دع الكمبيوتر يقوم بالعمل

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

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

  • Python: pylint ، pyflakes ، تحذيرات ، تحذيرات في Emacs
  • روبي: تحذيرات
  • جافا سكريبت: jslint

خاتمة

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