# اللعب بالشفرة: نهج فعال وكفؤ

<datetime class="hidden">2025-11-23T10:00</datetime>

<!-- category -- Software Development, Agile, Best Practices, Craftsmanship -->
**لماذا تعامل فرعك كصندوق رمل - وتلعب كعمل مشروع - تنتج برامجيات أفضل مع ضغط أقل.**

> **مُحِث الدالة:** معظم المطورين يحاولون أن يكونوا مثاليين في كل مرحلة من مراحل التطور، وهو يقتل كل من إبداعهم وإنتاجيتهم. هذه طريقة أفضل.

## أولاً

لقد كنت أبني برامج محترفة ل... حسناً، دعنا نقول فقط أنني أتذكر عندما "مُنحرفة" كانت فكرة جديدة

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

هذا ليس عرضياً **الإبداع والضغط لا يختلطان بشكل جيد**

لذا فقد طورت نهجاً يفصل بين الفوضويين، والاستكشاف الإبداعي، والإكتشاف الإبداعي، والتلميع، والتسليم الجاهز للإنتاج. أسميه ".[جعلها تعمل، تجعلها جميلة، قفلها إلى أسفل](/blog/makeitworkthenmakeitpretty)- ولكن في الواقع، انها عن إعطاء نفسك الإذن لتجربة دون وزن من الكمال معلقة فوقك.

[TOC]

## المراحل الثلاث

واسمحوا لي أن أكون واضحاً مقدماً: هذا ليس عن أن تكون مهملاً أو متجنباً للانضباط. إنه عن *تطبيق الضبط المناسب في الوقت المناسب*.

```mermaid
graph LR
    subgraph "Phase 1: Make It Work"
        A[New Requirement] --> B[Explore & Experiment]
        B --> C{Does It Work?}
        C -->|No| D[Try Different Approach]
        D --> B
        C -->|Yes| E[Basic Solution]
    end

    subgraph "Phase 2: Make It Pretty"
        E --> F[Refactor & Clean]
        F --> G[Apply SOLID Pragmatically]
        G --> H[Get Stakeholder Feedback]
        H --> I[Polished Solution]
    end

    subgraph "Phase 3: Lock It Down"
        I --> J[Add Tests]
        J --> K[Security Review]
        K --> L[Add Monitoring]
        L --> M[Production Ready]
    end

    style A stroke:#f59e0b,stroke-width:3px
    style E stroke:#0ea5e9,stroke-width:3px
    style I stroke:#8b5cf6,stroke-width:3px
    style M stroke:#10b981,stroke-width:4px
```

### المرحلة 1: جعلها تعمل

**الـ:** إلعب أولاً، تأكد من افتراضاتك، قم بتشغيل شيء ما

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

ما تحاول أن تكتشفه:

- هل يتصرف هذا API فعلاً في الطريقة التي تطالب بها الوثائق؟ (سبولر: نادراً ما يفعل ذلك)
- هل يمكنني حل هذه المشكلة بالأدوات التي لدي؟
- ما هي الاحتياجات الفعلية، وليس تلك المكتوبة في التذكرة؟
- هل هذه مشكلة لساعتين أم مشكلة لأسبوعين؟

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

**الفائدة الجانبية:** في الفرق البعيدة/السريعة، سلسلة ثابتة من "التقدم البدائي" يطمئن كل شخص أن العمل يتحرك - دون أن تضطر إلى أداء الإنتاجية في Slack.

هذه الحرية النفسية ضرورية. عندما تعرف أنه يمكنك رمي كل شيء بعيدا والبدء من جديد، تتخذ قرارات أكثر جرأة. جرب هذا النهج الغريب الذي من المحتمل أنه لن ينجح. أحيانا يعمل بشكل رائع. أحيانا تحتاج إلى قراءة مقالة، مشاهدة فيديو يوتيوب، فكر لفترة. تذكر: عملك هو تقديم برامجيات جيدة وقرارات جيدة، وليس الكتابة بسرعة. المدراء الجيدون يفهمون أنك أنت. **ليس منظّمة**.

```csharp
// Phase 1 code looks like this - and THAT'S OKAY
public async Task<Result> ProcessThing(Request req)
{
    // TODO: what if this is null?
    var data = await _api.GetSomething(req.Id);

    // This probably isn't right but let's see what happens
    var transformed = data.Items
        .Where(x => x.Status != "deleted") // Is this the right filter?
        .Select(x => new Thing { Name = x.Name }); // Missing loads of fields

    // HACK: hardcoded for now
    return new Result { Items = transformed.ToList(), Total = 42 };
}
```

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

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

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

استخدام حكمك. بعض الأحيان تحتاج التغذية المرتدة إلى **يُوضح**، ليس للموافقة على - المواصفات بالفعل فعلت ذلك. "المواصفات تقول 'الأخطاء يدون "الأخطاء برشاقة" - تفعل ذلك يعني إعادة المحاولة ثلاث مرات، أو فشل سريع وإخطار؟" هذه محادثة المرحلة الأولى.

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

كن عملياً، الهدف هو أفضل سمة، وليس نقاء العملية.

### المرحلة 2: اجعلها جميلة

**الـ:** الآن بما أنك تعرف ما تبنيه، *(الجيد الجيد)*.

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

الآن أنت تُنظّفُه:

```csharp
// Phase 2: Same logic, but actually maintainable
public async Task<ProcessingResult> ProcessItemsAsync(
    ProcessingRequest request,
    CancellationToken cancellationToken = default)
{
    ArgumentNullException.ThrowIfNull(request);

    var items = await _itemRepository.GetActiveItemsAsync(
        request.AccountId,
        cancellationToken);

    var processedItems = items
        .Where(item => item.Status != ItemStatus.Deleted)
        .Select(item => _mapper.ToProcessedItem(item))
        .ToList();

    return new ProcessingResult
    {
        Items = processedItems,
        TotalCount = processedItems.Count,
        ProcessedAt = _timeProvider.UtcNow
    };
}
```

هذا هو المكان الذي تقوم فيه بما يلي:

- تطبيق مبادئ *(أ)* (تعرفهم، لكن لا تعبدهم)
- & مُعطى مُعطيات مُزام
- 
- فكر في سطح API من وجهة نظر المتصل
- حل حالات الحافة التي اكتشفتها في المرحلة 1

**ومن الأهمية بمكان أن يكون هذا هو الوقت المناسب لردود فعل أصحاب المصلحة غير التقنيين.** الميزة الآن مرئية وقابلة للاستخدام، ولكنك لم تستثمر بعد ساعات اختبار الكتابة لها. إذا قالوا "في الواقع، أردناها أن تفعل x بدلاً من ذلك،

### المرحلة 3: اقفله

**الـ:** صلبه، اختبره، اجعله مرناً.

الآن، والآن فقط، تكتبون اختباراتكم، أضفوا فحوصات أمنية، نفذوا معالجة مناسبة للخطأ في حالات حافة الإنتاج، أضفوا المراقبة وعربات الحراسة.

لماذا الانتظار حتى الآن؟ **أخيراً تعرف ما الذي تختبره**.

الاختبارات المكتوبة في المرحلة 1 كانت ستكون خاطئة - أنت لم تفهم المشكلة بعد. الاختبارات المكتوبة في المرحلة 2 سيكون لها chorn - كنت لا تزال تصقل API. الاختبارات المكتوبة في المرحلة 3 هي *& حرّر*لأن التنفيذ مستقر.

```csharp
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(100)]
public async Task ProcessItemsAsync_WithValidRequest_ReturnsCorrectCount(
    int itemCount)
{
    // Arrange
    var items = _fixture.CreateMany<Item>(itemCount).ToList();
    _mockRepository
        .Setup(r => r.GetActiveItemsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
        .ReturnsAsync(items);

    // Act
    var result = await _sut.ProcessItemsAsync(
        new ProcessingRequest { AccountId = Guid.NewGuid() });

    // Assert
    result.TotalCount.Should().Be(itemCount);
    result.Items.Should().HaveCount(itemCount);
}

[Fact]
public async Task ProcessItemsAsync_WithNullRequest_ThrowsArgumentNullException()
{
    // Act & Assert
    await _sut.Invoking(s => s.ProcessItemsAsync(null!))
        .Should().ThrowAsync<ArgumentNullException>();
}

[Fact]
public async Task ProcessItemsAsync_ExcludesDeletedItems()
{
    // Arrange
    var items = new[]
    {
        new Item { Status = ItemStatus.Active },
        new Item { Status = ItemStatus.Deleted },
        new Item { Status = ItemStatus.Active }
    };
    _mockRepository
        .Setup(r => r.GetActiveItemsAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
        .ReturnsAsync(items);

    // Act
    var result = await _sut.ProcessItemsAsync(
        new ProcessingRequest { AccountId = Guid.NewGuid() });

    // Assert
    result.Items.Should().HaveCount(2);
}
```

وتشمل هذه المرحلة أيضاً ما يلي:

- الاستعراض الأمني (التوثيق والتفويض والتصديق على المدخلات)
- (ما الذي يحدث تحت الحمل؟)
- عدم تحليل نمط التحليل (ماذا لو كانت قاعدة البيانات بطيئة؟ ماذا لو كان مؤشر أسعار الصرف قد انتهى؟)
- الرصد (كيف سنعرف ما إذا كان هذا يكسر الإنتاج؟)

## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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

```mermaid
graph TB
    subgraph "Phase 1: Technical Voices"
        P1[Make It Work] --> T1[Senior Dev Review]
        T1 --> T2["Does the approach<br/>make sense?"]
        T2 --> T3["Any obvious<br/>architectural issues?"]
    end

    subgraph "Phase 2: Non-Technical Voices"
        P2[Make It Pretty] --> N1[Product Owner Demo]
        N1 --> N2["Does it meet<br/>the requirement?"]
        N2 --> N3["Is the UX<br/>intuitive?"]
    end

    subgraph "Phase 3: Ops/Security Voices"
        P3[Lock It Down] --> O1[Security Review]
        O1 --> O2[Operations Review]
        O2 --> O3["Will it hold up<br/>in production?"]
    end

    T3 --> P2
    N3 --> P3

    style T1 stroke:#3b82f6,stroke-width:2px
    style N1 stroke:#8b5cf6,stroke-width:2px
    style O1 stroke:#ef4444,stroke-width:2px
```

**المرحلة 1:** الأصوات التقنية فقط - كبار المصممين، المهندسين المعماريين. يمكنهم رؤية ما وراء الفوضى في شكل الحل. أصحاب المصلحة غير التقنيين سوف يفزعون عند الكود الخام ويسألون عن ألوان الأزرار عندما لا تزالون تكتشفون نموذج البيانات.

**المرحلة 2:** مرحباً بالأصوات غير التقنية. الميزات قابلة للاستخدام، والأزرار تفعل أشياء. الحصول على أصحاب المنتجات والمصممين المشاركة الآن - التغييرات لا تزال رخيصة، أنت لم تكتب الاختبارات بعد.

**المرحلة 3:** العمليات والأصوات الأمنية. "ماذا لو حصل هذا على 10x حركة مرور؟" "ماذا لو مر شخص ما بمدخلات خبيثة؟"

## الأفرع كجدر

هنا الشيء الذي غيّر حياتي التطويرية: **إلى أن ترفع مستوىً عاماً، يكون فرعك خاصاً جداً**.

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

أريدكم أن تعتبروا فرعكم الخاص كصندوق رمل، ملعب، مختبر حيث يتوقع الانفجارات ولا يتأذى أحد

```mermaid
graph TD
    A[Feature Branch Created] --> B[Experiment Freely]
    B --> C{Did it work?}
    C -->|No| D[Try Something Else]
    D --> B
    C -->|Yes| E[Clean Up the Mess]
    E --> F[Interactive Rebase]
    F --> G[Squash to Clean Commits]
    G --> H[Raise PR]
    H --> I[Public Code Review]

    style B stroke:#f59e0b,stroke-width:3px
    style D stroke:#f59e0b,stroke-width:2px
    style H stroke:#10b981,stroke-width:3px

    subgraph "Private - Go Mental"
        B
        C
        D
        E
    end

    subgraph "Public - Be Professional"
        H
        I
    end
```

وفي الممارسة العملية، يعني ذلك ما يلي:

- بشكل متكرر، حتى لو لم تجمع الشفرة
- كتابة `FIXME` وقد عقد مؤتمراً بشأن `TODO` تعليقات في كل مكان
- أَوْلَسْ أَوْلِسْ تَحْجِزُ شفّرَ في المكان بينما أنت تَستكشفُ
- يجب أن يكون متعدد "جرب هذا النهج"
- لا تقلق بشأن إرسال الرسائل (سَتَسْحقُ لاحقاً)

ثم، قبل أن تُعلّقُ a PR:

1. تنظيف الشفرة (المرحلة 2)
2. اختبارات إضافة (المرحلة 3)
3. إعادة بناء تفاعليّة لسحق كلّ ذلك التأريخ الفوض
4. اكتب رسالة واجبة

المستعرضون يرون رمزاً مهنياً نظيفاً مع سرد واضح. لا يرون الـ 6 بداية خاطئة، أو الـ 3 صباحاً "لماذا لا يعمل هذا العمل الدامي"

> **صندوق الرمل يبقى خاصّاً، العمل المُلمّع يصبح عامّاً.**

## لِمَ هذا يُخمِّل الاججج

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

> **"اللعب هو عمل مشروع"**

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

## (والسر القذر لدري)

ما الذي تفعله هذه العقلية الثلاثية المراحل بمبادئ التصميم الخاصة بك؟ باختصار: إنها تقتل العقائد. هكذا تغير تفكيري حول سوليد، DRY، و الواجهات في C#.

لقد ذكرت تطبيق مبادئ سوليد في المرحلة الثانية. اسمحوا لي أن أكون محدداً حول ما أعنيه.

Solid هي مجموعة جيدة من المبادئ. أنا أعلمهم. أنا أستخدمها. لكنني رأيت فرق تختفي من الثقوب المجردة من الأرانب باسم المسؤولية الفردية أو المفتوحة/المغلقة،

ها هي إخصائيتي التدريعيّة:

****

- لديك دليل على أنك ستحتاج إلى المرونة
- التجريد يجعل الشفرة أكثر وضوحاً
- أنت تبني شيئاً يستخدمه الآخرون

**لا تطبق SSLID عندما:**

- أنت تتنبأ بالمتطلبات المستقبلية
- هذا التجريد يزيد من التعقيد دون وضوح
- أنت تبنين الشفرة الداخلية التي تحافظين عليها فقط

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

```csharp
// Over-engineered SOLID worship
public interface IThingProcessor { }
public interface IThingProcessorFactory { }
public class ThingProcessorFactory : IThingProcessorFactory { }
public class ThingProcessor : IThingProcessor { }
public class ThingProcessorDecorator : IThingProcessor { }
public class ThingProcessorValidationDecorator : IThingProcessor { }

// What you probably actually need
public class ThingProcessor
{
    public async Task<Result> ProcessAsync(Thing thing)
    {
        // Just do the thing
    }
}
```

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

### المُسْتَمِر

وبينما نحن نذبح الأبقار المقدسة لنتحدث عن **ثانياً - المقدمة** (لا تكرر نفسك). ربما هو أكثر مبدأ تطبيقي في صناعتنا، وعندما يطبق بدون تفكير، يسبب ضرراً أكثر من الخير.

المشكلة هي: **ليس كل ازدواجية هي نفس نوع الإزدواد**.

عندما تبدو قطعتان من الشيفرة متشابهتين لكن تخدمان أغراض مختلفة، تستخرجهما إلى مجموعة مشتركة من الاقترانات التجريدية التي ينبغي أن تتطور بشكل مستقل. الآن عندما يحتاج أحد استخدام حالة إلى تغيير، فأنت إما:

1. إضافة بارامترات ومشروطات للتعامل مع القضيتين (جعل التجريد أكثر سوءاً)
2. كسر حالة الاستخدام الأخرى الأخرى
3. جاري نسخ الشفرة المشتركة إلى الخلف وتحريره (حذف DRY كان خاطئاً هنا)

```csharp
// "DRY" solution - looks clever, causes pain
public async Task<Result> ProcessEntity<T>(
    T entity,
    bool validateFirst = true,
    bool sendNotification = false,
    Func<T, bool>? customFilter = null,
    Action<T>? preProcess = null,
    Action<T>? postProcess = null) where T : IEntity
{
    // 50 lines of conditional spaghetti trying to handle
    // "processing" for Users, Orders, and Products
    // because they all had 3 similar lines once
}

// What you probably actually need
public async Task<Result> ProcessUser(User user) { /* 15 clear lines */ }
public async Task<Result> ProcessOrder(Order order) { /* 15 clear lines */ }
public async Task<Result> ProcessProduct(Product product) { /* 15 clear lines */ }
```

نعم، هناك بعض "الازدواجية" في النهج الثاني. لكن كل طريقة هي:

- من السهل فهمها في عزلة
- من السهل تعديله دون أن تكون له آثار جانبية
- يسهل حذف متى يذهب نوع ذلك الكيان بعيداً
- يسهل اختبارها بمدخلات ونواتج واضحة

نسخة DRY هي كابوس يجب الحفاظ عليها لأنها تحاول أن تكون كل الأشياء لجميع المتصلين. كل تغيير يتطلب فهم كل حالات الاستخدام. كل علة تُصلح تخاطر بكسر شيء آخر.

**قواعدي:** الإزدواج إلى أن ترى *نفس نفس* ثلاث مرات و أنت واثق من أنه يمثل *نفس نفس* مفهوم سيتطور معاً. حتى ذلك الحين، القليل من معجون النسخ لا بأس به. إنه ليس فشلاً أخلاقياً؛ إنه تحذير مناسب.

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

### الواجه نقطة الواجه بوصة C

وهنا بقرة مقدسة أخرى تحتاج إلى الذبح: **"كل فئة تحتاج إلى واجهة"**.

في الأيام الأولى من حقنة الاعتماد على شبكة الإنترنت، قرر شخص ما أن جعل شفرة قابلة للاختبار، كنت بحاجة إلى حقن الواجهات في كل مكان. المنطق كان: لا يمكنك أن تسخر من فئة الخرسانة، لذلك كل خدمة تحتاج. `IService` وقد عقد مؤتمراً بشأن `ServiceImpl`فجأة كل قاعدة شفرات C# كانت ملوثة بواجهات تنفيذية واحدة موجودة فقط للاختبار.

```csharp
// The interface tax - early 2010s style
public interface IUserService { }
public interface IOrderService { }
public interface IEmailService { }
public interface INotificationService { }
public interface IPaymentProcessor { }
public interface IInventoryManager { }

// Each with exactly ONE implementation
public class UserService : IUserService { }
public class OrderService : IOrderService { }
// ... you get the idea
```

لقد كانت هذه عملية برمجة للشحنات نحن كنا ندفع ضريبة معقدة من أجل *- - - - - - - - - - - -* (أ) *- - - - - - - - - - - -* "المرونة المستقبلية التي نادراً ما تتحقق"

**إليك الأمر، لست بحاجة لهذا بعد الآن.**

-أطر عصرية مثل: [النسخ(ه(](https://nsubstitute.github.io/) وقد عقد مؤتمراً بشأن [طراز MAQ](https://github.com/moq/moq4) والأهم من ذلك، أن حاوية المعلومات التصميمية لـ ASP.NET تعمل بشكل جيد تماماً مع الأنواع الخرسانية:

```csharp
// Modern approach - just register the concrete type
services.AddScoped<UserService>();
services.AddScoped<OrderService>();
services.AddScoped<EmailService>();

// In your controller or service
public class OrderController(OrderService orderService, UserService userService)
{
    // Primary constructor injection - clean and simple
}
```

لا توجد واجهات لا توجد مداخلات. `IOrderService`فقط الخدمة التي تحتاجها، الحقن مباشرة.

**"لكن ماذا عن الاختبار؟"** أسمعك تبكى

بالنسبة لاختبارات الوحدة، لديك خيارات:

1. & مُجِّر المُرْر الرئيسي `virtual` (واستخدام إطار يسخر منه)
2. استخدام الخدمة الحقيقية مع قاعدة بيانات اختبار (الاختبارات التكاملية غالباً ما تكون أكثر قيمة على أي حال)
3.  *عندما تحتاج إليه في الواقع للاختبار*

وهذه النقطة الثالثة ذات أهمية حاسمة: **أدوات إعادة التصنيع جيدة جداً في استخراج الواجهات**.

في الرايدر أو استوديو البصري، هو حرفياً `Ctrl+.` □ "استخراج الواجهة" وقد انتهيت. كل طريقة يتم استخراجها ، والصنف يتم تحديثه لينفذها ، ويمكنك العثور على/ استبدال جميع الاستخدامات عند الحاجة. إنها تأخذ ثوان.

```csharp
// Phase 1 & 2: Just write the class
public class OrderService
{
    public async Task<Order> CreateOrderAsync(CreateOrderRequest request) { ... }
    public async Task<Order?> GetOrderAsync(int orderId) { ... }
    public async Task CancelOrderAsync(int orderId) { ... }
}

// Phase 3: Need to mock it for testing? Extract interface in 2 seconds
public interface IOrderService
{
    Task<Order> CreateOrderAsync(CreateOrderRequest request);
    Task<Order?> GetOrderAsync(int orderId);
    Task CancelOrderAsync(int orderId);
}

public class OrderService : IOrderService { ... }
```

واتجاه عملي الآن: **الواجهات تأتي في المرحلة 3، وليس في المرحلة 1**.

خلال "إعملها" و"إجعلها جميلة" ، أنا فقط أكتب فصولاً. لا تجريد سابق لأوانه. لا توجد ضريبة واجهة على كل نوع. الشفرة أسهل ، أيسر في الإبحار (لا تقفز بين) `IFoo` وقد عقد مؤتمراً بشأن `Foo`()، وأسرع للكتابة.

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

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

```csharp
// Services I typically DO extract interfaces for (external boundaries)
public interface IPaymentGateway { }      // Third-party API
public interface IEmailSender { }         // External service
public interface IBlobStorage { }         // Cloud storage

// Services I typically DON'T need interfaces for (internal logic)
public class OrderValidator { }           // Pure logic, test directly
public class PriceCalculator { }          // Pure logic, test directly
public class OrderService { }             // Test with real repo + in-memory DB
```

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

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

## الـ مُحجّج

ومن المفارقة أن هذا النهج أكثر اتساقاً مع *الأصل الأصلي* Agile Plano أكثر من معظم العمليات "Agile" التي واجهتها. تشغيل البرمجيات بسرعة (المرحلة 1). الاستجابة للتغيير (المرحلتان 1-2). التعاون مع العملاء عندما تكون الميزة قابلة للإثبات (المرحلة 2). لا نقاط الركض، لا تتبع السرعة، لا مخططات دموية للحرق.

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

## متى لا تستخدم هذا النهج

يجب أن أكون صادقاً: هذا ليس دائماً النهج الصحيح.

**مُثبِثات علِل:** عندما تصلح حشرة معروفة، فإن أسلوب "تي دي دي" منطقي أكثر. اكتب اختباراً يعيد إنتاج الحشرة، أصلحها، إنتهت.

**مشاكل مفهومة جيداً:** إذا كنت تنفذ خوارزمية معيارية أو نمط استخدمته مئات المرات، فأنت لا تحتاج إلى مرحلة استكشاف.

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

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

لـ *(أكرب)* حيث تكون المتطلبات غامضة قليلاً، النهج التقني ليس واضحاً، وتحتاج إلى مساحة للتفكير - هذا النهج يعمل بشكل رائع.

## ملاحظة للمطورين المبتدئين

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

## الأسفل

**جعله يعمل، جعله جميل، قفل عليه.**

البداية بسيطة، التصميم مع البصيرة ولكن ليس المضاربة، التكرار بسرعة، فقط إضافة التعقيد عندما يؤتي ثماره.

> **"الازدواجية أرخص بكثير من التجريد الخطأ."**

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

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

> **توقف عن محاولة أن تكون مثالياً من الدقيقة الأولى أعط نفسك الإذن للعب**

---


## متك ذات ذات ذات ذات صلة م م م م م م م م م م م

إذا كان هذا رنين، يمكنك أيضا التمتع بهذه الوظائف ذات الصلة:

- **[كيف ابني برامجيات](/blog/makeitworkthenmakeitpretty)** - يتعمق في فلسفة الاختبار الكامنة وراء هذا النهج، مع أمثلة مفصلة لرموز المراحل الثلاث.
- **[(ما لم يحصلوا على أجرهم)](/blog/agile-standups-ceremony-tax)** -أخذي على لماذا المراسم ضريبة، وكيفية تقييم ما إذا كانت طقوسك السهلة تكسب صلاحيتها.
- **[الدالة الدالة الدالة: كتابة خاصيات الخاص معاملات التي تعمل](/blog/writingfeaturespecs)** - لأن المواصفات هي الأدوات، وليس الكتابات - ومعاملتها كوثائق حية هو السبيل الوحيد لبناء الشيء الصحيح.
- **[العمل على قواعد التراث](/blog/workingonlegacysystems)** - دروس عملية من عقود من حياة المقاول، بما في ذلك كيفية تطبيق هذه المبادئ عندما تتعلم نظام لم تبنيه.