جودة الشيفرة
هناك مجموعةٌ متنوعةٌ من الأدوات والتقنيات التي تدعم المطورين في كتابة كودٍ عالي الجودة. في هذه المحاضرة، سنغطي:
- التنسيق (Formatting)
- الفحص (Linting)
- الاختبار (Testing)
- خطافات ما قبل الالتزام (Pre-commit hooks)
- التكامل المستمر (Continuous integration)
- مشغِّلات الأوامر (Command runners)
وكموضوعٍ إضافي، سنغطي أيضًا التعبيرات النمطية (regular expressions)، وهو موضوعٌ عرضيٌّ له تطبيقاتٌ في جودة الكود (مثلًا لتشغيل مجموعةٍ فرعيةٍ من الاختبارات المطابقة لنمطٍ معيّن) وكذلك في مجالاتٍ أخرى مثل بيئات التطوير المتكاملة (مثلًا للبحث والاستبدال).
ستكون العديد من هذه الأدوات خاصةً باللغة (مثلًا أداة الفحص والتنسيق Ruff للغة بايثون). وفي بعض الحالات، ستتعدد اللغات المدعومة في أداةٍ واحدة (مثلًا أداة تنسيق الكود Prettier). ومع ذلك، فإن المفاهيم شبه عالمية — يمكنك إيجاد أدوات تنسيق الكود وأدوات الفحص ومكتبات الاختبار وما إلى ذلك لأي لغة برمجة.
التنسيق (Formatting)
تعمل أدوات التنسيق التلقائي (auto-formatters) على تجميل صيغة الكود السطحية تلقائيًا. وبهذا، يمكنك التركيز على المشكلات الأعمق والأكثر تحديًا، بينما تتعامل أداة التنسيق التلقائي مع التفاصيل المملة مثل اتساق استخدام ' مقابل " للنصوص، ووجود مسافات حول العوامل الثنائية (x + y بدلًا من x+y)، وترتيب جمل import بشكلٍ مرتّب، وتجنّب الأسطر الطويلة. من الفوائد الرئيسية لأدوات تنسيق الكود أنها توحّد نمط الكود بين جميع المطورين العاملين على قاعدة الكود.
بعض الأدوات مثل Prettier قابلة للتكوين بدرجةٍ كبيرة؛ يجب عليك تسجيل ملف التكوين في التحكم في الإصدارات الخاص بمشروعك. أما الأدوات الأخرى، مثل Black وgofmt، فتمتلك إمكانية تكوينٍ محدودةٍ أو معدومة، للحد من الانشغال بالتفاصيل التافهة (bikeshedding).
يمكنك إعداد تكامل مع بيئة التطوير مع أداة تنسيق الكود الخاصة بك، بحيث يُنسَّق كودك تلقائيًا أثناء كتابتك أو عند حفظك لملف. ويمكنك أيضًا إضافة ملف EditorConfig إلى مشروعك، والذي يُبلغ بيئة التطوير بإعداداتٍ معيّنةٍ على مستوى المشروع مثل حجم الإزاحة (indent) لكل نوع ملف.
الفحص (Linting)
تُجري أدوات الفحص (linters) تحليلًا ثابتًا (تحليل الكود دون تشغيله) للعثور على الأنماط المعاكسة (antipatterns) والمشكلات المحتملة في كودك. تتعمّق هذه الأدوات أبعد من المنسّقات التلقائية، فتبحث أبعد من الصيغة السطحية. ويختلف عمق التحليل حسب الأداة.
تأتي أدوات الفحص مزوّدةً بقوائم من القواعد (rules)، مع إعداداتٍ مسبقةٍ يمكن تكوينها على مستوى المشروع. بعض قواعد الفحص تُنتج نتائج إيجابية خاطئة (false positives)، لذا يمكنك تعطيلها على مستوى ملفٍ أو سطرٍ معيّن.
ستمتلك أدوات الفحص الجيدة مساعدةً أو وثائق مدمجة تشرح كل قاعدة فحص — ما الذي تبحث عنه القاعدة، ولماذا هي سيئة، وما البديل الأفضل لنمط الكود. على سبيل المثال، انظر إلى وثائق قاعدة SIM102 في Ruff التي تكتشف جمل if المتداخلة دون داعٍ في كود بايثون.
بعض أدوات الفحص لا تكتشف المشكلات فحسب، بل يمكنها أيضًا إصلاح بعضها تلقائيًا.
وبصرف النظر عن أدوات الفحص الخاصة باللغة، هناك أداةٌ أخرى قد تكون مفيدةً وهي semgrep، وهي أداة "بحث دلالي" (semantic grep) تعمل على مستوى شجرة النحو المجرد (AST) (بدلًا من مستوى الأحرف كما في grep) وتدعم لغاتٍ كثيرة. يمكنك استخدام semgrep لكتابة قواعد فحصٍ مخصصةٍ بسهولةٍ لمشاريعك. على سبيل المثال، إذا أردت منع استدعاء subprocess.Popen(..., shell=True) الخطير في بايثون، يمكنك العثور على هذا النمط بالأمر:
semgrep -l python -e "subprocess.Popen(..., shell=True, ...)"
الاختبار (Testing)
اختبار البرمجيات تقنيةٌ معياريةٌ لزيادة ثقتك في صحة كودك. تكتب كودًا، ثم تكتب كودًا يُشغّل الكود الذي كتبته ويُثير خطأً إذا لم يعمل الكود كما هو متوقَّع.
يمكنك كتابة اختباراتٍ لقطعٍ من الكود على مستوياتٍ مختلفةٍ من الدقة: اختبارات الوحدة (unit tests) للدوال الفردية، و_اختبارات التكامل_ (integration tests) للتفاعل بين الوحدات أو الخدمات، و_اختبارات وظيفية_ (functional tests) للسيناريوهات الشاملة من البداية إلى النهاية. يمكنك ممارسة التطوير الموجه بالاختبارات (test-driven development)، حيث تكتب الاختبارات قبل كتابة أي كود تنفيذ. وعندما تجد أخطاءً في كودك، يمكنك كتابة اختبارات الانحدار (regression tests)، لتلتقط انكسار الوظيفة في المستقبل إن حدث. ويمكنك كتابة اختبارات قائمة على الخصائص (property-based tests)، التي رائدها QuickCheck في لغة Haskell، والمطبَّقة في مكتباتٍ كثيرة، مثل Hypothesis للغة بايثون. أي منهجٍ للاختبار هو الصحيح يعتمد على مشروعك؛ وغالبًا ستعتمد مزيجًا من بعضها.
إذا كان برنامجك يمتلك تبعياتٍ خارجيةً مثل قاعدة بياناتٍ أو واجهة برمجة تطبيقات ويب (web API)، فقد يكون من المفيد محاكاة (mock) تلك التبعيات في اختباراتك، بدلًا من جعل كودك يتفاعل مع تبعياتٍ من جهاتٍ ثالثة أثناء الاختبار.
تغطية الكود (Code coverage)
تغطية الكود مقياسٌ يمكنك من خلاله قياس مدى جودة اختباراتك. تنظر تغطية الكود إلى أي أسطرٍ من كودك تُنفَّذ عند تشغيل اختباراتك، بحيث يمكنك التأكد من تغطية جميع مسارات الكود. يمكن لأدوات تغطية الكود أن تُظهر لك التغطية سطرًا بسطرٍ لإرشادك في كتابة الاختبارات. وتوفر خدماتٌ مثل Codecov واجهات ويبٍ لتتبع وعرض تغطية الكود عبر تاريخ المشروع.
ومثل أي مقياس، فإن تغطية الكود ليست مثالية؛ لا تبالغ في التركيز على التغطية، وركّز على كتابة اختباراتٍ عالية الجودة.
خطافات ما قبل الالتزام (Pre-commit hooks)
تعمل خطافات ما قبل الالتزام في Git، والتي يجعلها إطار العمل pre-commit أسهل، على تشغيل الكود المحدد من قِبل المستخدم تلقائيًا قبل كل التزامٍ في Git. تستخدم المشاريع خطافات ما قبل الالتزام عادةً لتشغيل المنسّقات وأدوات الفحص، وأحيانًا الاختبارات، تلقائيًا قبل كل التزام، لضمان أن الكود الملتزَم يطابق نمط الكود الخاص بالمشروع وخالٍ من مشكلاتٍ معيّنة.
التكامل المستمر (Continuous integration)
يمكن لخدمات التكامل المستمر (CI) مثل GitHub Actions تشغيل سكربتاتٍ لك في كل مرةٍ تدفع فيها الكود (أو على كل طلب سحب (pull request)، أو وفق جدولٍ زمني). يستخدم المطورون خدمات CI عادةً لتشغيل أدوات جودة الكود بما في ذلك المنسّقات وأدوات الفحص والاختبارات. بالنسبة للغات المترجمة، يمكنك التأكد من أن الكود يترجم؛ وبالنسبة للغات المطبوعة ثابتًا، يمكنك التأكد من نجاح فحص الأنواع. يمكن لتشغيل CI عند كل دفعٍ لالتزاماتٍ جديدة التقاط الأخطاء المُدخلة على النسخة الرئيسية من الكود؛ ويمكن لتشغيله على طلبات السحب التقاط المشكلات في المساهمات؛ ويمكن لتشغيله وفق جدولٍ زمنيٍ التقاط المشكلات في التبعيات الخارجية (مثلًا، إطلاق مطوّرٍ تغييرًا معطّلًا بصيغة متوافقة مع SemVer).
ولأن سكربتات CI تعمل بشكلٍ منفصلٍ عن أجهزة المطورين، يمكنك بسهولةٍ تشغيل مهامٍ طويلة المدة هناك. يمكن الاستفادة من ذلك، على سبيل المثال، في تشغيل مصفوفة (matrix) من الاختبارات عبر أنظمة تشغيلٍ وإصدارات لغاتٍ مختلفة للتأكد من أن البرنامج يعمل بشكلٍ صحيحٍ عبر جميعها.
بشكلٍ عام، لن يُجري السكربت الذي يعمل في CI تغييراتٍ مباشرةً على كودك: سيشغّل الأدوات في وضع "الفحص فقط" (check-only) بدلًا من وضع "الإصلاح" (fix)، فعلى سبيل المثال، سيثير المنسّق التلقائي خطأً عندما لا يتوافق الكود مع التنسيق.
غالبًا ما تتضمن المستودعات شارات الحالة (status badges) في README الخاص بها، تعرض حالة CI وغيرها من المعلومات مثل تغطية الكود. على سبيل المثال، أدناه حالة البناء الحالية لموقع Missing Semester.
مدقّق الروابط الخاص بنا، والذي يستخدم إجراء GitHub proof-html، يفشل غالبًا، عادةً بسبب مشكلاتٍ في مواقع الويب من جهاتٍ ثالثة. ومع ذلك، فقد ساعدنا في التقاط وإصلاح العديد من الروابط المعطلة (أحيانًا بسبب أخطاءٍ إملائية، وفي أغلب الأحيان بسبب نقل مواقع ويبٍ لمحتواها دون إضافة توجيهات (redirects) أو اختفاء مواقع ويب).
طريقةٌ جيدةٌ لتعلم تفاصيل خدمات CI والمنسّقات وأدوات الفحص ومكتبات الاختبار هي من خلال الأمثلة. ابحث عن مشاريع مفتوحة المصدر عالية الجودة على GitHub — وكلما كان المشروع أكثر تشابهًا مع مشروعك في لغة البرمجة والمجال والحجم والنطاق وما إلى ذلك، كان ذلك أفضل — وادرس ملفاتها مثل pyproject.toml و.github/workflows/ وDEVELOPMENT.md وغيرها من الملفات ذات الصلة.
النشر المستمر (Continuous deployment)
يستفيد النشر المستمر من بنية CI التحتية لـ_نشر_ التغييرات فعلًا. على سبيل المثال، يستخدم مستودع Missing Semester النشر المستمر إلى GitHub Pages بحيث عندما نُجري git push لملاحظات المحاضرات المحدّثة، يُبنى الموقع ويُنشر تلقائيًا. ويمكنك بناء أنواعٍ أخرى من القطع (artifacts) في CI، مثل الملفات الثنائية للتطبيقات أو صور Docker للخدمات.
مشغِّلات الأوامر (Command runners)
مشغِّلات الأوامر مثل just تبسّط مهمة تشغيل الأوامر في سياق المشروع. عندما تبني بنيةً تحتيةً لجودة الكود لمشروعك، لا تريد أن تُجبر مطوّريك على حفظ أوامر مثل uv run ruff check --fix. مع مشغِّل الأوامر، يمكن أن يتحول هذا إلى just lint، ويمكن أن تمتلك استدعاءاتٍ مماثلةً مثل just format وjust typecheck وما إلى ذلك، لجميع الأدوات المختلفة التي قد يريد المطور تشغيلها لمشروعك.
بعض مديري المشاريع أو الحزم الخاصة بلغاتٍ معيّنة يمتلكون دعمًا مدمجًا لهذه الوظيفة، مما يعني أنك لست بحاجةٍ إلى استخدام أداةٍ مستقلةٍ عن اللغة مثل just. على سبيل المثال، يدعم قسم scripts في ملف package.json لـnpm (Node.js) وقسم tool.hatch.envs.*.scripts في ملف pyproject.toml لـHatch (بايثون) هذه الوظيفة.
التعبيرات النمطية (Regular Expressions)
التعبيرات النمطية، والمختصرة عادةً بـ"regex"، هي لغةٌ تُستخدم لتمثيل مجموعاتٍ من النصوص (strings). تُستخدم أنماط التعبيرات النمطية عادةً لمطابقة الأنماط (pattern matching) في سياقاتٍ مختلفةٍ مثل أدوات سطر الأوامر وبيئات التطوير المتكاملة. على سبيل المثال، يدعم ag أنماط التعبيرات النمطية للبحث عبر قاعدة الكود (مثلًا، سيجد ag "import .* as .*" جميع الاستيرادات المعاد تسميتها في بايثون)، ويدعم go test خيارَ -run [regexp] لاختيار مجموعةٍ فرعيةٍ من الاختبارات. علاوةً على ذلك، تمتلك لغات البرمجة دعمًا مدمجًا أو مكتباتٍ من جهاتٍ ثالثةً لمطابقة التعبيرات النمطية، لذا يمكنك استخدام التعبيرات النمطية لوظائفٍ مثل مطابقة الأنماط والتحقق (validation) والتحليل (parsing).
للمساعدة في بناء الحدس، إليك أدناه بعض الأمثلة على أنماط التعبيرات النمطية. في هذه المحاضرة، نستخدم صيغة التعبيرات النمطية في بايثون. توجد نكهاتٌ متعددةٌ للتعبيرات النمطية، مع اختلافاتٍ طفيفةٍ بينها، خاصةً في الوظائف الأكثر تطورًا. يمكنك استخدام مختبرٍ عبر الإنترنت للتعبيرات النمطية مثل regex101 لتطوير وتصفية أخطاء التعبيرات النمطية.
abc--- يطابق النص الحرفي "abc".missing|semester--- يطابق النص "missing" أو النص "semester".\d{4}-\d{2}-\d{2}--- يطابق التواريخ بصيغة YYYY-MM-DD، مثل "2026-01-14". وراء ضمان أن النص يتكون من أربعة أرقام وشرطة ورقمين وشرطة ورقمين، فإن هذا لا يتحقق من صحة التاريخ، لذا "2026-01-99" يطابق هذا النمط أيضًا..+@.+--- يطابق عناوين البريد الإلكتروني، وهي نصوصٌ تحتوي على بعض النصوص ثم "@" ثم بعض النصوص الأخرى. هذا يُجري فقط التحقق الأساسي جدًا ويطابق نصوصًا مثل "nonsense@@@email". يوجد تعبيرٌ نمطيٌّ يطابق عناوين البريد الإلكتروني دون نتائج إيجابية أو سلبية خاطئة لكنه غير عملي.
صيغة التعبيرات النمطية
يمكنك العثور على دليلٍ شاملٍ لصيغة التعبيرات النمطية في هذه الوثائق (أو أحد الموارد العديدة الأخرى المتاحة عبر الإنترنت). إليك بعض اللبنات الأساسية:
abcيطابق النص الحرفي، عندما لا تمتلك الأحرف أي معنىً خاص (في هذا المثال، "abc").يطابق أي حرفٍ واحد[abc]يطابق حرفًا واحدًا موجودًا بين القوسين (في هذا المثال، "a" أو "b" أو "c")[^abc]يطابق حرفًا واحدًا باستثناء تلك الموجودة بين القوسين (مثلًا "d")[a-f]يطابق حرفًا واحدًا موجودًا في النطاق المشار إليه بين القوسين (مثلًا "c"، لكن ليس "q")a|bيطابق أيًّا من النمطين (مثلًا "a" أو "b")\dيطابق أي حرفٍ رقمي (مثلًا "3")\wيطابق أي حرفٍ من حروف الكلمات (مثلًا "x")\bيطابق أي حدٍّ (boundary) للكلمات (مثلًا، في النص "missing semester"، يطابق قبل "m" مباشرةً، وبعد "g" مباشرةً، وقبل "s" مباشرةً، وبعد "r" مباشرةً)(...)يطابق مجموعةً من نمطٍ ما...?يطابق صفرًا أو واحدًا من نمطٍ، مثلwords?لمطابقة "word" أو "words"...*يطابق أي عددٍ من نمطٍ، مثل.*لمطابقة أي عددٍ من أي حرف...+يطابق واحدًا أو أكثر من نمطٍ، مثل\d+لمطابقة أي عددٍ غير صفريٍّ من الأرقام...{N}يطابق N بالضبط من نمطٍ، مثل\d{4}لأربعة أرقام\.يطابق نقطةً حرفيةً "."\\يطابق شرطةً مائلةً خلفيةً حرفيةً "\"^يطابق بداية السطر$يطابق نهاية السطر
مجموعات الالتقاط والمراجع (Capture groups and references)
إذا استخدمت مجموعات التعبيرات النمطية (...)، يمكنك الإشارة إلى الأجزاء الفرعية من المطابقة لأغراض الاستخراج أو البحث والاستبدال. على سبيل المثال، لاستخراج الشهر فقط من تاريخٍ بصيغة YYYY-MM-DD، يمكنك استخدام كود بايثون التالي:
>>> import re
>>> re.match(r"\d{4}-(\d{2})-\d{2}", "2026-01-14").group(1)
'01'
في محرر النصوص الخاص بك، يمكنك استخدام مجموعات الالتقاط المرجعية في أنماط الاستبدال. قد تختلف الصيغة بين بيئات التطوير المتكاملة. على سبيل المثال، في VS Code، يمكنك استخدام متغيراتٍ مثل $1 و$2 وما إلى ذلك، وفي Vim، يمكنك استخدام \1 و\2 وما إلى ذلك، للإشارة إلى المجموعات.
القيود
اللغات النمطية (regular languages) قويةٌ لكنها محدودة؛ فهناك فئاتٌ من النصوص لا يمكن التعبير عنها بتعبيرٍ نمطيٍّ معياري (مثلًا، من غير الممكن كتابة تعبيرٍ نمطيٍّ يطابق مجموعة النصوص {a^n b^n | n ≥ 0}، أي مجموعة النصوص المكونة من عددٍ من أحرف "a" متبوعًا بنفس العدد من أحرف "b"؛ وبشكلٍ أكثر عمليةً، اللغات مثل HTML ليست لغاتٍ نمطية). عمليًا، تدعم محركات التعبيرات النمطية الحديثة ميزاتٍ مثل النظرة الأمامية (lookahead) والمراجع الخلفية (backreferences) التي توسع الدعم أبعد من اللغات النمطية، وهي مفيدةٌ للغاية من الناحية العملية، لكن من المهم معرفة أنها ما تزال محدودة في قدرتها التعبيرية. بالنسبة للغاتٍ الأكثر تطورًا، قد تحتاج إلى محركات تحليلٍ (parsers) أكثر قدرةً (لمثالٍ واحد، انظر pyparsing، وهو محرر تحليل PEG).
تعلُّم التعبيرات النمطية
نوصي بتعلُّم الأساسيات (ما غطّيناه في هذه المحاضرة)، ثم الاطلاع على مراجع التعبيرات النمطية كلما احتجتها، بدلًا من حفظ اللغة بأكملها.
يمكن لأدوات الذكاء الاصطناعي التخاطبية أن تكون فعالةً في مساعدتك على توليد أنماط التعبيرات النمطية. على سبيل المثال، جرّب مطالبة نموذج اللغة المفضل لديك بالاستعلام التالي:
Write a Python-style regex pattern that matches the requested path from log lines from Nginx. Here is an example log line:
169.254.1.1 - - [09/Jan/2026:21:28:51 +0000] "GET /feed.xml HTTP/2.0" 200 2995 "-" "python-requests/2.32.3"
تمارين
- هيّئ منسّقًا وأداة فحصٍ وخطافات ما قبل الالتزام لمشروعٍ تعمل عليه. إذا كان لديك الكثير من الأخطاء: يجب أن يتولى التنسيق التلقائي أخطاء التنسيق. أما بالنسبة لأخطاء الفحص، فجرّب استخدام وكيل ذكاء اصطناعي لإصلاح جميع أخطاء الفحص. تأكد من أن وكيل الذكاء الاصطناعي يمكنه تشغيل أداة الفحص وملاحظة النتائج، بحيث يمكنه العمل في حلقةٍ تكراريةٍ لإصلاح جميع المشكلات. راجع النتائج بعناية للتأكد من أن الذكاء الاصطناعي لا يكسر كودك!
- تعلّم مكتبة اختبارٍ للغةٍ تعرفها واكتب اختبار وحدةٍ لمشروعٍ تعمل عليه. شغّل أداة تغطية الكود، وولّد تقرير تغطيةٍ منسّقًا بصيغة HTML، ولاحظ النتائج. هل يمكنك العثور على الأسطر المغطاة؟ ستكون تغطية كودك منخفضةً جدًا على الأغلب. جرّب كتابة بعض الاختبارات يدويًا لتحسينها. جرّب استخدام وكيل ذكاء اصطناعي لتحسين التغطية؛ تأكد من أن وكيل البرمجة يمكنه تشغيل الاختبارات مع التغطية وإنتاج تقرير تغطيةٍ سطرًا بسطرٍ، بحيث يعرف أين يركز. هل الاختبارات التي يولّدها الذكاء الاصطناعي جيدةٌ فعلًا؟
- هيّئ تكاملًا مستمرًا ليعمل عند كل دفعٍ لمشروعٍ تعمل عليه. اجعل CI يشغّل التنسيق والفحص والاختبارات. اكسر كودك عن قصد (مثلًا، أدخل مخالفةً لقولب الفحص)، وتأكد من أن CI يلتقطها.
- جرّب كتابة نموذج تعبيرٍ نمطي واستخدم أداة سطر الأوامر
grepللعثور على تواجداتsubprocess.Popen(..., shell=True)في كودك. الآن، جرّب "كسر" نموذج التعبير النمطي. هل ما زال semgrep يطابق بنجاح الكود الخطير الذي يعثّر استدعاء grep الخاص بك؟ - تمرّن على البحث والاستبدال بالتعبيرات النمطية في بيئة التطوير أو محرر النصوص الخاص بك عن طريق استبدال علامات القوائم النقطية (bullet markers)
-الخاصة بـMarkdown بعلامات*في ملاحظات المحاضرة هذه. لاحظ أن مجرد استبدال جميع أحرف "-" في الملف سيكون خاطئًا، إذ توجد استخداماتٌ عديدةٌ لذلك الحرف لا تكون علامات قوائم نقطية. - اكتب تعبيرًا نمطيًّا لالتقاط من بنى JSON من الشكل
{"name": "Alyssa P. Hacker", "college": "MIT"}الاسم (مثلًا،Alyssa P. Hackerفي هذا المثال). تلميح: في محاولتك الأولى، قد ينتهي بك الأمر إلى كتابة تعبيرٍ نمطيٍّ يستخرجAlyssa P. Hacker", "college": "MIT؛ اقرأ عن المحددات الجشعة (greedy quantifiers) في وثائق التعبيرات النمطية لبايثون لمعرفة كيفية إصلاح ذلك.- اجعل نموذج التعبير النمطي يعمل حتى في الحالات التي يحتوي فيها الاسم على حرف
"(يمكن تخطي علامات الاقتباس المزدوجة في JSON بـ\"). - لا نوصي باستخدام التعبيرات النمطية لمشكلات التحليل المتطورة عمليًا. اكتشف كيفية استخدام محرك تحليل JSON في لغة البرمجة الخاصة بك لهذه المهمة. اكتب برنامج سطر أوامرٍ يأخذ كمدخلٍ، على stdin، بنية JSON من الشكل الموصوف أعلاه، ويُخرج، على stdout، الاسم. يجب أن تحتاج فقط إلى سطرين من الكود للقيام بذلك. في بايثون، يمكنك القيام بذلك بسهولةٍ في سطرٍ واحدٍ من الكود إلى جانب
import json.
- اجعل نموذج التعبير النمطي يعمل حتى في الحالات التي يحتوي فيها الاسم على حرف