Window.pipedriveLeadboosterConfig = { القاعدة: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', الإصدار: 2, } ؛(الدالة () { var w = نافذة إذا كان (w.LeadBooster) { console.warn('LeadBooster موجود بالفعل') } وإلا { { w.LeadBooster = { q: [], على: دالة (ن، ح) { { هذا.q.push({ t: 'o'، n: n، n: n، h: h }) }, الزناد: الدالة (n) { هذا.q.push({ t: 't'، n: n: n }) }, } } })() GraphQL روبي. ماذا عن الأداء؟ - The Codest
The Codest
  • نبذة عنا
  • الخدمات
    • تطوير البرمجيات
      • تطوير الواجهة الأمامية
      • تطوير الواجهة الخلفية
    • Staff Augmentation
      • مطورو الواجهة الأمامية
      • مطورو الواجهة الخلفية
      • مهندسو البيانات
      • مهندسو السحابة
      • مهندسو ضمان الجودة
      • أخرى
    • استشاري
      • التدقيق والاستشارات
  • الصناعات
    • التكنولوجيا المالية والمصرفية
    • E-commerce
    • أدتك
    • التكنولوجيا الصحية
    • التصنيع
    • الخدمات اللوجستية
    • السيارات
    • إنترنت الأشياء
  • القيمة مقابل
    • CEO
    • CTO
    • مدير التوصيل
  • فريقنا
  • دراسات الحالة
  • اعرف كيف
    • المدونة
    • اللقاءات
    • ندوات عبر الإنترنت
    • الموارد
الوظائف تواصل معنا
  • نبذة عنا
  • الخدمات
    • تطوير البرمجيات
      • تطوير الواجهة الأمامية
      • تطوير الواجهة الخلفية
    • Staff Augmentation
      • مطورو الواجهة الأمامية
      • مطورو الواجهة الخلفية
      • مهندسو البيانات
      • مهندسو السحابة
      • مهندسو ضمان الجودة
      • أخرى
    • استشاري
      • التدقيق والاستشارات
  • القيمة مقابل
    • CEO
    • CTO
    • مدير التوصيل
  • فريقنا
  • دراسات الحالة
  • اعرف كيف
    • المدونة
    • اللقاءات
    • ندوات عبر الإنترنت
    • الموارد
الوظائف تواصل معنا
السهم الخلفي العودة إلى الوراء
2021-06-30
تطوير البرمجيات

GraphQL روبي. ماذا عن الأداء؟

The Codest

توماش زكاراديك

مهندس التطوير

GraphQL، مثل أي تقنية، لها مشاكلها، بعضها ناتج بشكل مباشر عن البنية وبعضها مطابق لما نراه في أي تطبيق آخر. ومع ذلك، فإن الحلول مختلفة تمامًا.

لعرض المشكلة، دعنا نفترض بنية التطبيق التالية:

https://drive.google.com/file/d/1N4sWPJSls0S8FFHbpHCUVHBNBpEuSsyz/view

وهنا الاستعلام المقابل في GraphQL لتحميل البيانات. نقوم بجلب جميع الروابط، إلى جانب الملصق وروابطه المضافة إلى النظام,

{
  جميع الروابط {
    المعرف
    url
    الوصف
    تم الإنشاء في
    تم النشر بواسطة {
      المعرف
      الاسم
      الروابط {
        المعرف
      }
    }
  }
}

كما هو موضح أدناه، يمكننا أن نرى هنا مشكلة n + 1 الكلاسيكية في العلاقات.

تحميل الروابط (0.4 مللي ثانية) حدد "الروابط".* من "الروابط" ترتيب حسب تم الإنشاء_في_تصنيف
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل المستخدم (0.3 مللي ثانية) حدد تحديد "المستخدمين".* من "المستخدمين" حيث "المستخدمون"."id" = ؟ LIMIT ?  [[["المعرف"، 40]، ["LIMIT"، 1]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل الروابط (0.3 مللي ثانية) حدد "الروابط".* من "الروابط" حيث "الروابط"."user_id" = ؟  [["User_id"، 40]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل المستخدم (0.1 مللي ثانية) حدد تحديد "المستخدمين".* من "المستخدمين" حيث "المستخدمون"."id" = ? LIMIT ?  [[["المعرف"، 38]، ["LIMIT"، 1]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل الروابط (0.1 مللي ثانية) حدد "الروابط".* من "الروابط" حيث "الروابط"."user_id" = ؟  [["User_id"، 38]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل المستخدم (0.2 مللي ثانية) حدد تحديد "المستخدمين".* من "المستخدمين" حيث "المستخدمون"."id" = ? LIMIT ?  [[["المعرف"، 36]، ["LIMIT"، 1]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل الروابط (0.1 مللي ثانية) حدد "الروابط".* من "الروابط" حيث "الروابط"."user_id" = ؟  [["User_id"، 36]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل المستخدم (0.1 مللي ثانية) حدد تحديد "المستخدمين".* من "المستخدمين" حيث "المستخدمون"."id" = ? LIMIT ?  [[["المعرف"، 34]، ["LIMIT"، 1]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل الروابط (0.2 مللي ثانية) حدد "الروابط".* من "الروابط" حيث "الروابط"."user_id" = ؟  [["User_id"، 34]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل المستخدم (0.1 مللي ثانية) حدد تحديد "المستخدمين".* من "المستخدمين" حيث "المستخدمون"."id" = ? LIMIT ?  [[["المعرف"، 32]، ["LIMIT"، 1]]

في هذه الحالة، يعمل تمامًا مثل هذه القطعة من الكود:
link.all.all.map(&:user).map(&:links).

يبدو أننا نعرف حل المشكلة: يتضمن الرابط(المستخدم: ::الروابط).map(&:user).map(&:user).map(&:links)ولكن هل ستنجح حقًا؟ دعنا نتحقق من ذلك!

للتحقق من الإصلاح، قمتُ بتغيير GraphQL استعلام لاستخدام حقول قليلة فقط ولا علاقة لها.

{
  جميع الروابط {
    المعرف
    url
    الوصف
    تم إنشاؤه في
  }
}

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

تتم المعالجة بواسطة GraphqlController#execute كما */*
  المعلمات: {"استعلام"=>"{n allLinks {n allLinks {n idn urln URLn descriptionn createdAtn }n}"، "Graphql"=>{"استعلام"=>"{n allLinks {n idn urln الوصفn createAtn }n}"}}}}}}
  تحميل الروابط (0.3 مللي ثانية) حدد "الروابط".* من "الروابط" ترتيب حسب تم إنشاؤها_في_تصنيف
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  تحميل المستخدم (0.3 مللي ثانية) حدد "المستخدمين".* من "المستخدمين" حيث يكون "المستخدمون"."id" في (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["معرف"، 40]، ["معرف"، 38]، ["معرف"، 36]، ["معرف"، 34]، ["معرف"، 32]، ["معرف"، 30]، ["معرف"، 28]، ["معرف"، 26]، ["معرف"، 24]، ["معرف", 22]، ["المعرف"، 20]، ["المعرف"، 20]، ["المعرف"، 18]، ["المعرف"، 16]، ["المعرف"، 14]، ["المعرف"، 12]، ["المعرف"، 10]، ["المعرف"، 8]، ["المعرف"، 6]، ["المعرف"، 4]، ["المعرف"، 2]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  ) [ [["معرف المستخدم"، 2]، ["معرف المستخدم"، 4]، ["معرف المستخدم"، 6]، ["معرف المستخدم"، 8]، ["معرف المستخدم"، 10]، ["معرف المستخدم"، 12]، ["معرف المستخدم"، 14]، ["معرف المستخدم"، 16]، ["معرف المستخدم"، 18]، ["معرف المستخدم"، 20], ["معرف المستخدم"، 22]، ["معرف المستخدم"، 24]، ["معرف المستخدم"، 26]، ["معرف المستخدم"، 28]، ["معرف المستخدم"، 30]، ["معرف المستخدم"، 32]، ["معرف المستخدم"، 34]، ["معرف المستخدم"، 36]، ["معرف المستخدم"، 38]، ["معرف المستخدم"، 40]]
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
تم إكمال 200 موافق في 39 مللي ثانية (المشاهدات: 0.7 مللي ثانية | السجل النشط: 0.9 مللي ثانية | التخصيصات: 8730)

في GraphQL،يتم حل مثل هذه المشاكل بشكل مختلف، ببساطة عن طريق تحميل البيانات على دفعات، بافتراض أن البيانات مطلوبة عند وضعها في الاستعلام. إنه مثل هذا التحميل البطيء. واحدة من أكثر المكتبات شيوعًا هي https://github.com/Shopify/graphql-batch/.

لسوء الحظ، فإن تركيبه ليس خاليًا من المتاعب كما قد يبدو. وتتوفر أدوات تحميل البيانات هنا: https://github.com/Shopify/graphql-batch/tree/master/examples، أعني محمل السجل وفئةمحمل الرابطة الفصل. دعونا نثبت بشكل كلاسيكي جوهرة 'Graphql-batch' ثم إضافتها إلى مخططنا، وكذلك أدوات التحميل:

# graphql-ruby/app/graphqql/graphql_tutorial_schema.rb
فئة GraphqlTutorialSchema < GraphQL::Schema
  أنواع الاستعلام::QueryType
  أنواع الطفرات::نوع الطفرات
  استخدام GraphQL::دفعة
  ...
نهاية

وأنواعنا:

# graphql-ruby/app/graphql/types/link_type.rb
الوحدة النمطية أنواع
  صنف نوع الرابط <العقدة الأساسية
    الحقل :تم الإنشاء_في, DateTimeType, فارغ: خطأ
    الحقل :url، سلسلة، فارغ: خطأ
    الحقل :الوصف، سلسلة، فارغة: خطأ
    الحقل :posted_by، UserType، فارغ: خطأ، الطريقة: :مستخدم
    الحقل :أصوات، [الأنواع::نوع التصويت]، فارغ: خطأ

    تعريف المستخدم
      اللوادر::RecordLoader.for(المستخدم).load(object.user_id)
    النهاية
  النهاية
النهاية

# graphql-ruby/app/graphqql/types/user_type.rb
الوحدة النمطية أنواع
  صنف UserType <العقدة الأساسية
    الحقل :تم الإنشاء_في, DateTimeType, فارغ: خطأ
    الحقل :اسم، سلسلة، فارغة: خطأ
    الحقل :البريد الإلكتروني، خيط، فارغ: خطأ
    الحقل :أصوات، [نوع التصويت]، فارغ: خطأ
    الحقل :روابط، [نوع الرابط]، فارغ: خطأ

    تعريف الروابط
      اللوادر::AssociationLoader.for(المستخدم، :روابط).load(كائن)
    نهاية
  النهاية
النهاية

نتيجة لاستخدام أدوات التحميل، نقوم بتجميع البيانات والاستعلام عن البيانات في استعلامي sql بسيطين:

تم بدء POST POST "/graphql" ل ::1 في 2021-06-16 22:40:17 +0200
   (0.1 مللي ثانية) حدد sqlite_version (*)
تتم المعالجة بواسطة GraphqlController#execute باسم */*
  المعلمات: {"استعلام"=>"{n allLinks {n allLinks {n idn urln urln الوصفn createAtn publishedBy {n idn nnamen الروابط {n idn }n }n }n }n}}"، "Graphql"=>{"استعلام"=>"{n allLinks {n idn urln الوصفn createAtn نشرت بواسطة {n idn n n nnameen الروابط {n idn }n }n }n }n }n}n}"}}}}}
  تحميل الروابط (0.4 مللي ثانية) حدد "روابط".* من "روابط"
  ↳ التطبيق/المتحكمون/جرافكل_كونترولر.rb:5:في 'تنفيذ'
  ) [["المعرف"، 2]، ["المعرف"، 4]، ["المعرف"، 6]، ["المعرف"، 8]، ["المعرف"، 10]، ["المعرف"، 12]، ["المعرف"، 14]، ["المعرف"، 16]، ["المعرف"، 18]، ["المعرف"، 20], ["المعرف"، 22]، ["المعرف"، 24]، ["المعرف"، 26]، ["المعرف"، 28]، ["المعرف"، 30]، ["المعرف"، 32]، ["المعرف"، 34]، ["المعرف"، 36]، ["المعرف"، 38]، ["المعرف"، 40]]
  ↳ التطبيق/الجرافكل/المحملون/record_Loader.rb:12:في 'perform'
  ) [ [["معرف المستخدم"، 2]، ["معرف المستخدم"، 4]، ["معرف المستخدم"، 6]، ["معرف المستخدم"، 8]، ["معرف المستخدم"، 10]، ["معرف المستخدم"، 12]، ["معرف المستخدم"، 14]، ["معرف المستخدم"، 16]، ["معرف المستخدم"، 18]، ["معرف المستخدم"، 20], ["معرف المستخدم"، 22]، ["معرف المستخدم"، 24]، ["معرف المستخدم"، 26]، ["معرف المستخدم"، 28]، ["معرف المستخدم"، 30]، ["معرف المستخدم"، 32]، ["معرف المستخدم"، 34]، ["معرف المستخدم"، 36]، ["معرف المستخدم"، 38]، ["معرف المستخدم"، 40]]
  ↳ التطبيق/الجرافكل/المحملون/association_loader.rb:46:في 'تحميل_الترابط'
تم الإكمال 200 موافق في 62 مللي ثانية (المشاهدات: 1.3 مللي ثانية | السجل النشط: 1.8 مللي ثانية | التخصيصات: 39887)

هناك أيضًا حلول أخرى تحل هذه المشكلة، مثل:

https://github.com/exAspArk/batch-loader#basic-example

تعقيد الاستعلامات

N + 1 الاستعلامات ليست كل شيء، في GraphQL يمكننا نقل السمات التالية بحرية. بشكل افتراضي، يتم تعيينه على 1. قد يكون هذا في بعض الأحيان أكثر من اللازم بالنسبة للخادم، خاصةً في حالة يمكننا فيها تداخل البيانات بحرية. كيف نتعامل معها؟ يمكننا الحد من تعقيد الاستعلام، ولكن للقيام بذلك، نحتاج أيضًا إلى تحديد تكلفتها في السمات. بشكل افتراضي يتم ضبطها على 1. نضبط هذه التكلفة باستخدام التعقيد: حيث يمكننا إدخال البيانات: الحقل: الروابط، [نوع الرابط]، باطل: خطأ، التعقيد: 101. إذا كان التقييد هو العمل الفعلي، فلا يزال عليك إدخال الحد الأقصى لمخططك:

فئة GraphqlTutorialSchema < GraphQL::Schema
  أنواع الاستعلام::نوع الاستعلام
  أنواع الطفرات::نوع الطفرات
  استخدام GraphQL::دفعة
  الحد الأقصى للتعقيد 100
  ...
نهاية

التتبع

GraphQL يعالج الاستعلامات بشكل مختلف، والتتبع ليس بهذه البساطة إذا ما قورن بما يمكننا القيام به محليًا. لسوء الحظ، لن يخبرنا المحلل المصغر للرف أو سجل SQL العادي بكل شيء ولن يشير إلى أي جزء من الاستعلام مسؤول عن شريحة زمنية معينة. في حالة GraphQL-Ruby، يمكننا استخدام الحلول التجارية المتاحة هنا: https://graphql-ruby.org/queries/tracingأو محاولة إعداد التتبع الخاص بنا. أدناه، يبدو المقتطف أدناه كمتتبع محلي.

# lib/my_custom_tracer.rb
صنف MyCustomTracer  'graphql.lex',
    'تحليل' => 'graphql.parse',
    'التحقق من الصحة' => 'graphql.validate',
    'analy_query' => 'graphql.analy_query',
    'analy_multiplex' => 'graphql.analy_multiplex',
    'execute_multiplex' => 'graphql.execute_multiplex',
    'execute_query' => 'graphql.execute_query',
    'execute_query_query_lazy' => 'graphql.execute_query_lazy'
  }

  def platform_trace(platform_key, key, _data, &block)
    البدء = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
    النتيجة =: block.call
    المدة = ::Process.clock_gettime(:::Process::CLOCK_MONONOTONIC) - البداية
    مراقبة(platform_key، مفتاح، مفتاح، المدة)
    النتيجة
  النهاية

  تعريف platform_field_key(النوع، الحقل)
    "Graphql.#{type.Graphql_name}.#{field.Graphql_name}"
  نهاية

  def platform_authorized_key (النوع)
    "graphql.authorized.#{type.Graphql_name}"
  النهاية

  ديف platform_resolve_type_key(النوع)
    "graphql.resol_type.#{type.graphql_name}"
  النهاية

  def observe(platform_key, key, duration)
    العودة إذا كان المفتاح = = 'مصرح به'

    يضع "platform_key: #{platform_key}، المفتاح: #{key}، المدة: #{(المدة * 1000).round (5)} مللي ثانية".
  النهاية
النهاية

التثبيت بسيط للغاية أيضًا، تحتاج إلى تضمين معلومات التتبع في المخطط متتبع (MyCustomTracer.new) التكوين. كما في المثال أدناه:

# graphql-ruby/app/graphqql/graphql_tutorial_schema.rb
فئة GraphqlTutorialSchema < GraphQL::Schema
  أنواع الاستعلام::QueryType
  أنواع الطفرات::نوع الطفرات
  استخدام GraphQL::دفعة
  المتتبع(MyCustomTracer.new)
  ...
نهاية

يبدو الناتج من هذا التتبع بهذا الشكل:

تم بدء POST POST "/graphql" ل ::1 في 2021-06-17 22:02:44 +0200
   (0.1 مللي ثانية) حدد sqlite_version (*)
تتم المعالجة بواسطة GraphqlController#execute باسم */*
  المعلمات: {"الاستعلام"=>"{n allLinks {n allLinks {n idn urln urln الوصفn createdAtn نشرت بواسطة {n idn n n n n n اسم الروابط {n idn }n }n }n }n}"، "Graphql"=>{"الاستعلام"=>"{n allLinks {n idn urln الوصفn engedAtn نشرت بواسطة {n idn n n n n n n n روابط {n idn }n }n }n }n }n}"}}}}}}
platform_key: Graphql.lex، المفتاح: lex، المدة: 0.156 مللي ثانية
النظام الأساسي_المفتاح: graphql.parse، المفتاح: parse، المدة: 0 156 مللي ثانية: 0.108 مللي ثانية
النظام الأساسي_المفتاح: Graphql.validate، المفتاح: تحقق من الصحة، المدة: 0.537 مللي ثانية: 0.537 مللي ثانية
النظام الأساسي_المفتاح: graphqql.analy_query، المفتاح: analy_query، المدة: 0.123 مللي ثانية: 0.123 مللي ثانية
النظام الأساسي_المفتاح: graphql.analy_multiplex، المفتاح: analy_multiplex، المدة: 0.159 مللي ثانية: 0.159 مللي ثانية
  تحميل الروابط (0.4 مللي ثانية) حدد "روابط".* من "روابط"
  ↳ التطبيق/الجرافكل/الجرافكل_توتوريال_شيما.rb:21:في 'تتبع_المنصة'
النظام الأساسي_المفتاح: Graphql.execute_query، المفتاح: execute_query، المدة: 15.562 مللي ثانية
  ↳ التطبيق/الجرافكل/المحملون/record_loader.rb:12:في 'perform'
  ↳ التطبيق/الجرافكل/المحمّلون/محمّل_الارتباط.rb:46:في 'تحميل_الارتباط المسبق'
النظام الأساسي_المفتاح: Graphql.execute_query_lazy، المفتاح: execute_query_lazy، المدة: 14.12 مللي ثانية
النظام الأساسي_المفتاح: Graphql.Graphql.execute_multiplex، المفتاح: execute_multiplex، المدة: 14.12 مللي ثانية: 31.11 مللي ثانية
تم الإكمال 200 موافق في 48 مللي ثانية (المشاهدات: 1.2 مللي ثانية | ActiveRecord: 2.0 مللي ثانية | التخصيصات: 40128)

الملخص

GraphQL ليست تقنية جديدة بعد الآن، ولكن حلول مشاكلها ليست موحدة تمامًا إذا لم تكن جزءًا من المكتبة. إن تطبيق هذه التكنولوجيا في المشروع الكثير من الفرص للتفاعل مع الواجهة الأمامية، وأنا شخصيًا أعتبرها نوعية جديدة فيما يتعلق بما تقدمه REST API.

لماذا يجب عليك (على الأرجح) استخدام Typescript

كيف لا تقتل مشروعاً بممارسات الترميز السيئة؟

استراتيجيات جلب البيانات في NextJS

مقالات ذات صلة

تطوير البرمجيات

إنشاء تطبيقات ويب مستقبلية: رؤى من فريق خبراء The Codest

اكتشف كيف تتفوق شركة The Codest في إنشاء تطبيقات ويب تفاعلية قابلة للتطوير باستخدام أحدث التقنيات، وتقديم تجارب مستخدم سلسة عبر جميع المنصات. اكتشف كيف تقود خبرتنا التحول الرقمي والأعمال التجارية...

ذا كوديست
تطوير البرمجيات

أفضل 10 شركات لتطوير البرمجيات في لاتفيا

تعرّف على أفضل شركات تطوير البرمجيات في لاتفيا وحلولها المبتكرة في أحدث مقالاتنا. اكتشف كيف يمكن لهذه الشركات الرائدة في مجال التكنولوجيا المساعدة في الارتقاء بأعمالك.

thecodest
الحلول المؤسسية وحلول التوسعة

أساسيات تطوير برمجيات جافا: دليل للاستعانة بمصادر خارجية بنجاح

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

thecodest
تطوير البرمجيات

الدليل الشامل للاستعانة بمصادر خارجية في بولندا

إن الطفرة في outsourcing في بولندا مدفوعة بالتقدم الاقتصادي والتعليمي والتكنولوجي، مما يعزز نمو تكنولوجيا المعلومات والمناخ الملائم للأعمال.

ذا كوديست
الحلول المؤسسية وحلول التوسعة

الدليل الكامل لأدوات وتقنيات تدقيق تكنولوجيا المعلومات

تضمن عمليات تدقيق تكنولوجيا المعلومات وجود أنظمة آمنة وفعالة ومتوافقة. تعرف على المزيد حول أهميتها من خلال قراءة المقال كاملاً.

The Codest
ياكوب جاكوب جاكوبوفيتش CTO وشريك مؤسس CTO

اشترك في قاعدة معارفنا وابقَ على اطلاع على آخر المستجدات في قطاع تكنولوجيا المعلومات.

    نبذة عنا

    The Codest - شركة دولية لتطوير البرمجيات لها مراكز تقنية في بولندا.

    المملكة المتحدة - المقر الرئيسي

    • المكتب 303 ب، 182-184 شارع هاي ستريت نورث E6 2JA
      لندن، إنجلترا

    بولندا - مراكز التكنولوجيا المحلية

    • مجمع مكاتب فابريتشنا المكتبي، أليجا
      بوكوجو 18، 31-564 كراكوف
    • سفارة الأدمغة، كونستروكتورسكا
      11, 02-673 02-673 وارسو، بولندا

      The Codest

    • الصفحة الرئيسية
    • نبذة عنا
    • الخدمات
    • دراسات الحالة
    • اعرف كيف
    • الوظائف
    • القاموس

      الخدمات

    • استشاري
    • تطوير البرمجيات
    • تطوير الواجهة الخلفية
    • تطوير الواجهة الأمامية
    • Staff Augmentation
    • مطورو الواجهة الخلفية
    • مهندسو السحابة
    • مهندسو البيانات
    • أخرى
    • مهندسو ضمان الجودة

      الموارد

    • حقائق وأساطير حول التعاون مع شريك خارجي لتطوير البرمجيات
    • من الولايات المتحدة الأمريكية إلى أوروبا: لماذا تقرر الشركات الأمريكية الناشئة الانتقال إلى أوروبا؟
    • مقارنة مراكز تطوير التكنولوجيا في الخارج: تك أوفشور أوروبا (بولندا)، آسيان (الفلبين)، أوراسيا (تركيا)
    • ما هي أهم التحديات التي تواجه CTOs ومديري تكنولوجيا المعلومات؟
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • شروط استخدام الموقع الإلكتروني

    جميع الحقوق محفوظة © 2025 بواسطة The Codest. جميع الحقوق محفوظة.

    arArabic
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek arArabic