<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tarek</title>
    <description>The latest articles on DEV Community by Tarek (@najem).</description>
    <link>https://dev.to/najem</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3915981%2F2cf77803-a74e-428c-953a-adbc5c27a6dd.jpeg</url>
      <title>DEV Community: Tarek</title>
      <link>https://dev.to/najem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/najem"/>
    <language>en</language>
    <item>
      <title>ما الذي تدين به رسالة الفشل لقارئها؟</title>
      <dc:creator>Tarek</dc:creator>
      <pubDate>Thu, 22 Oct 2026 11:00:52 +0000</pubDate>
      <link>https://dev.to/najem/m-ldhy-tdyn-bh-rsl-lfshl-lqryh-5a5i</link>
      <guid>https://dev.to/najem/m-ldhy-tdyn-bh-rsl-lfshl-lqryh-5a5i</guid>
      <description>&lt;h1&gt;
  
  
  ما الذي تدين به رسالة الفشل لقارئها؟
&lt;/h1&gt;

&lt;p&gt;متى كانت آخر مرة قرأت فيها — فعلاً قرأت — رسالة فشل اختبار؟&lt;/p&gt;

&lt;p&gt;في الغالب كانت الثانية صباحاً، والسجل مفتوح أمامك، وكل ما لديك سطر واحد:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected True, but found False.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;أيّ &lt;code&gt;True&lt;/code&gt;؟ في أيّ استدعاء؟ في هذا الاختبار تسعة تأكيدات، وأيُّها كان يمكن أن ينتج هذا السطر نفسه. الرسالة لا تستبعد شيئاً.&lt;/p&gt;

&lt;p&gt;وعامل البناء الذي نفّذ الاختبار اختفى أصلاً. والفرع قد يُحذف الأسبوع المقبل. لكن السطر يبقى — ملصقاً في محادثة، مقتبساً في تذكرة، مرفقاً بتقرير ما بعد الحادث. رسالة الفشل هي الجزء الوحيد من مجموعة اختباراتك المضمون قراءته خارج المحرر، على يد من لم يرَ الكود أبداً. لهذا القارئ كُتبت هذه المقالة.&lt;/p&gt;

&lt;p&gt;هنا تتوقف مكتبة الاختبارات عن كونها واجهة برمجية وتصبح وثيقة. نحن نختار المكتبة بواجهتها المرنة — &lt;code&gt;Should()&lt;/code&gt; و&lt;code&gt;.Be()&lt;/code&gt; و&lt;code&gt;.And&lt;/code&gt; — لكن ما نقرأه فعلاً، ونحن تحت الضغط، بعد شهور، وبلا سياق، هو نص الفشل. سمِّ الشيء باسمه: هذا هو &lt;strong&gt;سطح التشخيص&lt;/strong&gt; لمجموعة الاختبارات كلها. ومع ذلك قلّ من سأل: ما الذي يجب أن يدين به هذا السطح لقارئه؟&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;جرّبها في ستين ثانية.&lt;/strong&gt; الادعاء المركزي لهذه المقالة قابل للفحص، فافحصه بنفسك:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Jeninnet.Assertions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Jeninnet.Assertions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TestMethod&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Username_matches_expected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dennis"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jonas"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected username to be 'jonas', but found 'dennis'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;الحزمة لا تعتمد على أي إطار اختبار، فتعمل أسطر التوكيد الثلاثة نفسها تحت MSTest أو xUnit أو NUnit. أعد تسمية &lt;code&gt;username&lt;/code&gt; إلى &lt;code&gt;login&lt;/code&gt; فيتسمّى معه نص الفشل؛ ونفّذه على جهاز آخر فلا يتحرك النص.&lt;/p&gt;

&lt;p&gt;هذه المقالة أول ست ملاحظات عن هذا السطح — وكلمة عن كيفية قراءتها. هذه الملاحظات لن تُعلّمك واجهة المكتبة؛ فالكتاب والمرجع يفعلان ذلك. إنها تشرح منطق التصميم الذي وراءها: لماذا يبدو كل سطح على ما هو عليه، وما ثمنه، وأين يتوقف. كل واحدة تقف وحدها؛ ومجتمعةً تصف تصميماً واحداً.&lt;/p&gt;

&lt;p&gt;وخط السير ثابت. بعد هذا العقد: رسائل الفشل بالعربية — ماذا يتغير حين لا يقرأ القارئ الواقع تحت الضغط الإنجليزية. ثم الزمن: كيف تختبر ما يحدث &lt;em&gt;لاحقاً&lt;/em&gt; دون أن تنام أثناء تنفيذ مجموعتك. ثم الذاكرة: لماذا تنتمي ميزانيات التخصيص إلى اختبارات الوحدة. ثم الانتظار: اختبارات تستطلع حقيقة لم تصل بعد بدل أن تؤكد كذبة على مؤقّت. وأخيراً: تحقق لا يرمي أبداً — الأفعال نفسها حيث لا يُسمح بالاستثناءات. ابدأ من هنا، من العقد.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;سمِّ المتغيّر.&lt;/strong&gt; أول واجبات رسالة الفشل أن تقول &lt;em&gt;ماذا&lt;/em&gt; فشل، لا أن تقول &lt;em&gt;إن&lt;/em&gt; شيئاً قد فشل. قارن:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected True, but found False.
Expected username to be 'jonas', but found 'dennis'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;الجملة الثانية تنجو حين تُلصق في محادثة فريق بلا أي كود حولها. والاسم هنا مأخوذ من نص التعبير نفسه لحظة التصريف — عبر سمة &lt;code&gt;CallerArgumentExpression&lt;/code&gt; في المصرّف (C# 10) — لا من وسم يدوي يفلت من المتغيّر خلال أسبوع، ولا من تتبّع مكدّس يعتمد على ملف &lt;code&gt;PDB&lt;/code&gt; قد لا يكون منشوراً أصلاً. وللحكاية وجه ثانٍ يستحق الذكر صراحة: أعد تسمية المتغيّر فيتسمّى معه نص الفشل تلقائياً. الأوسمة اليدوية تتعفن؛ والتعبيرات الملتقطة لا يسعها ذلك.&lt;/p&gt;

&lt;p&gt;وبلا الاسم يبدأ كل فشل بالطريقة نفسها: افتح الكود، وعُدّ التوكيدات، وضيّق الدائرة. تسعة مشتبهين وسطر دليل واحد. اسم المتغيّر يختصر هذا البحث إلى الصفر — قبل أن تفتح أي شيء، تعرف أي قيمة، من أي تعبير، أخفقت في أي فحص.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;اذكر المتوقَّع والفعلي، وبهذا الترتيب.&lt;/strong&gt; القارئ يعقد مقارنة؛ سلّمه طرفيها بترتيب القراءة. "فشل التأكيد" حكمٌ بلا ملف قضية.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;كن ثابتة.&lt;/strong&gt; نفس الفشل يجب أن يُنتج النص نفسه على جهازك وجهاز زميلك وعامل البناء — عبر أنظمة التشغيل وثقافات الواجهة. التنسيق الثابت (invariant) ليس لمسة تجميل؛ إنه ما يجعل نص الفشل قابلاً للبحث والمقارنة بين التشغيلات، وآمناً لأن يُؤكَّد عليه داخل اختبارات تختبر الاختبارات. مجموعة اختبارات هذه المكتبة نفسها — 1242 اختباراً كلها خضراء — تؤكد رسائلها كنصوص حرفية حيث تكون مستقرة، وهذا ممكن فقط لأن الرسائل صُممت مستقرة أصلاً. الثبات خاصية قابلة للاختبار، وهي مُختبَرة فعلاً.&lt;/p&gt;

&lt;p&gt;وتأمل البديل الذي تعيش عليه معظم المجموعات: رسائل تتغير بتغير ثقافة واجهة الجهاز، فيقرأ الفشل نفسه على حاسوبك بصيغة وعلى عامل البناء بصيغة أخرى. لا يمكنك البحث في سجل لا تستطيع إعادة إنتاجه، ولا التأكيد على نص يتحرك. الثبات هو ما يجعل رسالة الفشل تتصرف كبيانات — قابلة للمقارنة والبحث والاقتباس — لا كأحوال طقس.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;تصاغَرْ بأناقة.&lt;/strong&gt; رسالة الفشل ملخّص لا تفريغ بيانات. حين تتجاوز النصوص المقارَنة مئتي حرف أو تعبر الأسطر، طبعُها كاملة يدفن الفرق؛ ما ينفع هو الفرق نفسه: diff موحّد بثلاثة أسطر سياق، والقيم مقتطعة عند مئة حرف. وحين تكون القيمة وحدها طويلة، اقتطعها ودع القارئ يعيد الإنتاج بنفسه. الرسالة تجيب عن سؤال واحد: هل تحتاج إلى إعادة الإنتاج، أم أنك ترى الخلل أصلاً؟&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;أشِر إلى الخطوة التالية.&lt;/strong&gt; بعض الإخفاقات بحثٌ أخطأ العنوان — مفتاح قاموس، خاصية JSON، سطر في مجموعة نصوص. سرد الموجود، مرتباً بالقرب ومحدوداً بثلاثة، يحوّل الطريق المسدود إلى خطوة عملية:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Did you mean 'name'?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;اخدم القارئين معاً.&lt;/strong&gt; الإنسان يقرأ نصاً، وخط الأنابيب يقرأ JSON. المكتبة الجدّية في سطح التشخيص تعامل الاثنين كصياغتين لبنية واحدة: كل فشل مجموع يحمل تعبير الموضوع وفئته ورسالته، والقيمتين الفعلية والمتوقعة حيث يبلّغ عنهما الفعل — يُسلسل للآلة ويُصاغ للإنسان. ابنِ السطحين منفصلين فيتباعدا مع الزمن؛ وابنِهما من مصدر واحد فيبقيا صادقين.&lt;/p&gt;

&lt;p&gt;وعملياً، تسافر الصياغة الآلية أبعد مما يتوقع معظم المهندسين: مُرفقة بتشغيلات التكامل، مخزنة إلى جوار تقارير التشغيل الجاف، تُقارن عبر البناءات الليلية. تقرير JSON يحمل التعبير والفئة والقيمتين هو ما يجعل الفشل مقروءاً للأدوات بعد عام، حين لا يذكر أحد الاختبار.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;قل "لماذا" حين يسأل القارئ لماذا.&lt;/strong&gt; فشلٌ يصف الحالة فقط هو نصف تشخيص. من أرفق &lt;code&gt;because: "the connection dropped"&lt;/code&gt; يحصل على جملة تفكير مترابطة، لا على معلومتين متجاورتين. جملة السبب تُلحَق — بأسلوب الجملة نفسها — بما بلّغ عنه الفعل أصلاً؛ فيركب السبب مع الدليل بدل أن يحلّ محله.&lt;/p&gt;

&lt;p&gt;لا شيء من هذا يغني عن المصفّح، ولا قيمة لأي منه إن كان الاختبار لا يؤكد شيئاً. رسالة الفشل آخر الملاذ — ولهذا تحديداً تستحق تصميماً من الدرجة الأولى. فآخر الملاذ هو حيث ينتهي بنا الجميع.&lt;/p&gt;

&lt;p&gt;ويبقى قارئ واحد لم يخدمه هذا العقد بعد: المهندس الذي ليست الإنجليزية لغته العاملة. نص الفشل من الأسطح النادرة التي نضمن أن قارئها تحت ضغط — ومكتبات هذا المجال تكاد تُجمع على لغة واحدة، وما عداها يصل مترجَماً. ما الذي يدين به سطح التشخيص &lt;em&gt;لهذا&lt;/em&gt; القارئ؟ هذا موضوع المقالة التالية.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;التالي:&lt;/strong&gt; &lt;a href="//./02-failure-messages-in-arabic/ar.md"&gt;حين يجب أن تتكلم رسالة الفشل بالعربية&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;عن هذه السلسلة:&lt;/em&gt; هذه الملاحظات من بناء &lt;strong&gt;Jeninnet.Assertions&lt;/strong&gt; — مكتبة تأكيدات مرنة ومستقلة عن أطر الاختبار، بترخيص &lt;strong&gt;MIT&lt;/strong&gt; وبلا أي اعتماديات خارجية. هذه المقالة 01 من 06.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;تجد المستودع هنا: &lt;code&gt;Jeninnet.Assertions&lt;/code&gt;(&lt;a href="https://github.com/TarekNajem04/Jeninnet.Assertions" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;تجد الحزم هنا: 

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Jeninnet.Assertions&lt;/code&gt; (&lt;a href="https://www.nuget.org/packages/Jeninnet.Assertions/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Jeninnet.Assertions.Analyzer&lt;/code&gt; (&lt;a href="https://www.nuget.org/packages/Jeninnet.Assertions.Analyzer/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Jeninnet.Assertions.MSTest&lt;/code&gt; (&lt;a href="https://www.nuget.org/packages/Jeninnet.Assertions.MSTest/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>testing</category>
      <category>opensource</category>
      <category>dotnet</category>
      <category>tdd</category>
    </item>
    <item>
      <title>What a Failure Message Owes Its Reader</title>
      <dc:creator>Tarek</dc:creator>
      <pubDate>Thu, 22 Oct 2026 10:57:20 +0000</pubDate>
      <link>https://dev.to/najem/what-a-failure-message-owes-its-reader-10p7</link>
      <guid>https://dev.to/najem/what-a-failure-message-owes-its-reader-10p7</guid>
      <description>&lt;p&gt;A test fails at 2 AM. You open the CI log, and the entire story of the failure is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected True, but found False.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which &lt;code&gt;True&lt;/code&gt;? In which call? The test holds nine assertions, and a line written this way rules none of them out.&lt;/p&gt;

&lt;p&gt;The build agent that ran it is already gone. The branch may be deleted next week. But the line stays — pasted into a chat thread, quoted in a ticket, screenshotted into a postmortem. A failure message is the only part of your test suite guaranteed to be read outside the editor, by people who never saw the code. That is the audience this piece is written for.&lt;/p&gt;

&lt;p&gt;This is the moment a test library stops being an API and becomes a document. We choose libraries by their fluent surface — &lt;code&gt;Should()&lt;/code&gt;, &lt;code&gt;.Be()&lt;/code&gt;, &lt;code&gt;.And&lt;/code&gt; — but the artifact most of us actually read, under pressure, months later, without context, is the failure text. Call it what it is: &lt;strong&gt;the diagnostic surface&lt;/strong&gt; of the test suite. Most of us have never asked what that surface owes its reader.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it in sixty seconds.&lt;/strong&gt; The central claim of this piece is checkable, so check it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Jeninnet.Assertions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Jeninnet.Assertions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TestMethod&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Username_matches_expected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dennis"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jonas"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected username to be 'jonas', but found 'dennis'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package depends on no test framework, so the same three assertion lines work under MSTest, xUnit, or NUnit. Rename &lt;code&gt;username&lt;/code&gt; to &lt;code&gt;login&lt;/code&gt; and the message renames itself; run it on another machine and the message does not move.&lt;/p&gt;

&lt;p&gt;This piece is the first of six notes on that surface — and a word on how to read them. These notes will not teach you the library's API; the book and the reference do that. They argue for the design behind it: why each surface looks the way it does, what it costs, and where it stops. Each note stands alone; together they describe one design.&lt;/p&gt;

&lt;p&gt;The arc is fixed. After this contract: failure messages in Arabic — what changes when the reader under stress does not read English. Then time: how to test what happens &lt;em&gt;later&lt;/em&gt; without sleeping through your own suite. Then memory: why allocation budgets belong inside unit tests. Then waiting: tests that poll for a truth that has not arrived yet instead of asserting a lie on a timer. Finally, validation that never throws — the same verbs, usable where exceptions are not allowed. Start here, with the contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Name the subject.&lt;/strong&gt; A failure should say &lt;em&gt;what&lt;/em&gt; failed, not only &lt;em&gt;that&lt;/em&gt; something did. Compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected True, but found False.
Expected username to be 'jonas', but found 'dennis'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second sentence survives being pasted into a chat thread with no code around it. The subject is named because the assertion captured the expression text at compile time — the compiler's &lt;code&gt;CallerArgumentExpression&lt;/code&gt; attribute (C# 10) — not because someone hand-wrote a label that drifts from the variable within a sprint, and not because a stack walk consulted a PDB file that is not guaranteed to be deployed. There is a second-order effect worth stating plainly: rename the variable and the message renames itself. Hand-written labels rot; captured expressions cannot.&lt;/p&gt;

&lt;p&gt;Without the subject, every failure begins the same way: open the code, count the assertions, bisect. Nine suspects, one line of evidence. The subject name collapses that search to zero — before you have opened anything, you already know which value, from which expression, failed which check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State expected and actual, in that order.&lt;/strong&gt; The reader is performing a comparison; hand them both operands in reading order. "Assertion failed" is a verdict with no case file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be stable.&lt;/strong&gt; The same failure should produce the same text on your machine, a colleague's machine, and a build agent — across operating systems and UI cultures. Invariant formatting is not a cosmetic detail; it is what makes failure output searchable, comparable across runs, and safe to assert on in tests that test tests. This library's own suite — 1,242 tests, all green — asserts its messages as exact strings wherever they are stable, which is only possible because the messages were designed to be stable in the first place. Stability is a testable property, and it is tested.&lt;/p&gt;

&lt;p&gt;Consider the alternative most suites live with: messages that change with the machine's UI culture, so the same failure reads one way on your laptop and another on the build agent. You cannot search history you cannot reproduce, and you cannot assert on text that moves. Invariance is what lets a failure message behave like data — diffable, greppable, quotable — instead of weather.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scale down gracefully.&lt;/strong&gt; A failure message is a summary, not a data dump. When the compared strings grow past two hundred characters or span lines, printing both in full buries the difference; the useful artifact is the difference itself — a unified diff with three lines of context, values truncated to a hundred characters. When a value is merely long, truncate it and let the reader reproduce the rest. The message answers one question: do you need to reproduce this, or do you already see it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Point at the next move.&lt;/strong&gt; Some failures are lookups that missed — a dictionary key, a JSON property, a line in a string collection. Listing what does exist, ordered by closeness and capped at three, turns a dead end into a next step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Did you mean 'name'?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Serve both readers.&lt;/strong&gt; The human reads prose; the pipeline reads JSON. A library serious about the diagnostic surface treats these as two renderings of one structure: each collected failure carries its subject expression, its category, its message, and the actual and expected values where the verb reports them — serialized for machines, worded for people. Build the two surfaces separately and they drift; build them from one source and they stay honest.&lt;/p&gt;

&lt;p&gt;In practice the machine rendering travels further than most engineers expect: attached to CI runs, stored beside dry-run reports, compared across nightly builds. A JSON report with the expression, the category, and both values is what makes a failure legible to tooling a year later, when nobody remembers the test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Say why, when the reader asks why.&lt;/strong&gt; A failure that describes only the state is half a diagnosis. The reader who attached &lt;code&gt;because: "the connection dropped"&lt;/code&gt; gets one sentence of reasoning, not two unrelated facts. The &lt;code&gt;because&lt;/code&gt; clause is appended, sentence-style, to whatever the verb already reported — the reason rides with the evidence instead of replacing it.&lt;/p&gt;

&lt;p&gt;None of this replaces the debugger, and none of it matters if the test asserts nothing. The failure message is a last resort — which is precisely why it deserves first-class design. The last resort is where we all end up.&lt;/p&gt;

&lt;p&gt;There is one more reader this contract has not served yet: the engineer whose working language is not English. Failure text is one of the rare surfaces whose reader is guaranteed to be under stress — and almost every library in this space speaks exactly one language, with anything else arriving as a translation. What the diagnostic surface owes &lt;em&gt;that&lt;/em&gt; reader is the subject of the next piece.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; &lt;a href="//.."&gt;When the Failure Message Must Speak Arabic&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;About this series:&lt;/em&gt; These notes come from building &lt;strong&gt;Jeninnet.Assertions&lt;/strong&gt; — a fluent, framework-agnostic assertion library, *&lt;em&gt;MIT-licensed&lt;/em&gt;, with no test-framework dependency. This is article 1 of 6.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can find the repository here: &lt;code&gt;Jeninnet.Assertions&lt;/code&gt;(&lt;a href="https://github.com/TarekNajem04/Jeninnet.Assertions" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;You can find the packages here:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Jeninnet.Assertions&lt;/code&gt; (&lt;a href="https://www.nuget.org/packages/Jeninnet.Assertions/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Jeninnet.Assertions.Analyzer&lt;/code&gt; (&lt;a href="https://www.nuget.org/packages/Jeninnet.Assertions.Analyzer/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Jeninnet.Assertions.MSTest&lt;/code&gt; (&lt;a href="https://www.nuget.org/packages/Jeninnet.Assertions.MSTest/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>opensource</category>
      <category>dotnet</category>
      <category>tdd</category>
    </item>
    <item>
      <title>AI-Assisted Professional Engineering with .NET [1]</title>
      <dc:creator>Tarek</dc:creator>
      <pubDate>Fri, 31 Jul 2026 21:58:50 +0000</pubDate>
      <link>https://dev.to/najem/ai-assisted-professional-engineering-with-net-1-57i2</link>
      <guid>https://dev.to/najem/ai-assisted-professional-engineering-with-net-1-57i2</guid>
      <description>&lt;h2&gt;
  
  
  From Writing a Technical Book to Building an Engineering Ecosystem
&lt;/h2&gt;

&lt;p&gt;Most technical books begin with an outline.&lt;/p&gt;

&lt;p&gt;Mine began with a question.&lt;/p&gt;

&lt;p&gt;How can Artificial Intelligence help professional software engineers without replacing engineering discipline?&lt;/p&gt;

&lt;p&gt;At first, my goal was simple.&lt;/p&gt;

&lt;p&gt;Write a high-quality technical book about AI-assisted software engineering with .NET.&lt;/p&gt;

&lt;p&gt;But something unexpected happened.&lt;/p&gt;

&lt;p&gt;Every chapter introduced new engineering problems that had nothing to do with writing itself.&lt;/p&gt;

&lt;p&gt;How should documentation be organized?&lt;/p&gt;

&lt;p&gt;How can architectural decisions remain traceable?&lt;/p&gt;

&lt;p&gt;How can a bilingual manuscript stay synchronized?&lt;/p&gt;

&lt;p&gt;How should Markdown become the single source of truth?&lt;/p&gt;

&lt;p&gt;How can professional DOCX and PDF documents be generated automatically?&lt;/p&gt;

&lt;p&gt;How should publishing become reproducible rather than manual?&lt;/p&gt;

&lt;p&gt;One by one, solving these problems produced new tools, new documentation, new workflows, and new engineering practices.&lt;/p&gt;

&lt;p&gt;Eventually I realized I wasn't building a manuscript anymore.&lt;/p&gt;

&lt;p&gt;I was building an engineering ecosystem.&lt;/p&gt;

&lt;p&gt;Today the repository contains much more than the book.&lt;/p&gt;

&lt;p&gt;It contains a publishing platform, automation tooling, engineering documentation, community standards, architectural decisions, and an evolving knowledge base.&lt;/p&gt;

&lt;p&gt;The manuscript is still important.&lt;/p&gt;

&lt;p&gt;But it is now one component of a much larger project.&lt;/p&gt;

&lt;p&gt;Perhaps the most important decision I made was to build everything in public.&lt;/p&gt;

&lt;p&gt;Every improvement.&lt;/p&gt;

&lt;p&gt;Every release.&lt;/p&gt;

&lt;p&gt;Every architectural decision.&lt;/p&gt;

&lt;p&gt;Every lesson learned.&lt;/p&gt;

&lt;p&gt;Instead of publishing only the final result, I chose to document the complete engineering journey.&lt;/p&gt;

&lt;p&gt;The first public release publishes only the Introduction of the manuscript.&lt;/p&gt;

&lt;p&gt;That may seem like a small milestone.&lt;/p&gt;

&lt;p&gt;In reality, it marks the beginning of a project that I expect to evolve for many years.&lt;/p&gt;

&lt;p&gt;If this journey helps another engineer design a better workflow, document a better architecture, or publish knowledge more effectively, then the project has already achieved one of its goals.&lt;/p&gt;

&lt;p&gt;This is only the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Engineering Series
&lt;/h2&gt;

&lt;p&gt;Next&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;002-Why I Built My Own Markdown Publishing Platform →&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Continue the Journey
&lt;/h2&gt;

&lt;p&gt;If you'd like to explore the project, follow its progress, or read future releases, you can find everything here:&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Repository
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  First Public Release
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>AI-Assisted Professional Engineering with .NET [1]</title>
      <dc:creator>Tarek</dc:creator>
      <pubDate>Fri, 31 Jul 2026 21:51:09 +0000</pubDate>
      <link>https://dev.to/najem/ai-assisted-professional-engineering-with-net-i2</link>
      <guid>https://dev.to/najem/ai-assisted-professional-engineering-with-net-i2</guid>
      <description>&lt;h2&gt;
  
  
  من تأليف كتاب تقني إلى بناء منظومة هندسية متكاملة
&lt;/h2&gt;

&lt;p&gt;تبدأ معظم الكتب التقنية بفهرس.&lt;/p&gt;

&lt;p&gt;أما هذا المشروع فقد بدأ بسؤال.&lt;/p&gt;

&lt;p&gt;كيف يمكن للذكاء الاصطناعي أن يساعد مهندس البرمجيات دون أن يحل محل التفكير الهندسي؟&lt;/p&gt;

&lt;p&gt;في البداية كان هدفي بسيطًا.&lt;/p&gt;

&lt;p&gt;تأليف كتاب احترافي حول الهندسة البرمجية بمساعدة الذكاء الاصطناعي باستخدام .NET.&lt;/p&gt;

&lt;p&gt;لكن مع مرور الوقت بدأت تظهر تحديات لم تكن مرتبطة بالكتابة نفسها.&lt;/p&gt;

&lt;p&gt;كيف يمكن تنظيم التوثيق؟&lt;/p&gt;

&lt;p&gt;كيف نحافظ على تتبع القرارات الهندسية؟&lt;/p&gt;

&lt;p&gt;كيف يمكن إدارة مخطوطة بلغتين دون فقدان التزامن؟&lt;/p&gt;

&lt;p&gt;كيف تصبح ملفات Markdown المصدر الوحيد للحقيقة؟&lt;/p&gt;

&lt;p&gt;كيف يمكن إنتاج ملفات DOCX وPDF بجودة احترافية بصورة آلية؟&lt;/p&gt;

&lt;p&gt;وكيف تتحول عملية النشر من عمل يدوي إلى عملية هندسية قابلة للتكرار؟&lt;/p&gt;

&lt;p&gt;كل سؤال كان يقود إلى حل جديد.&lt;/p&gt;

&lt;p&gt;ومع كل حل ظهر مكون جديد داخل المشروع.&lt;/p&gt;

&lt;p&gt;أدوات.&lt;/p&gt;

&lt;p&gt;توثيق.&lt;/p&gt;

&lt;p&gt;عمليات أتمتة.&lt;/p&gt;

&lt;p&gt;معايير.&lt;/p&gt;

&lt;p&gt;قرارات هندسية.&lt;/p&gt;

&lt;p&gt;حتى أدركت في مرحلة ما أنني لم أعد أبني كتابًا فقط.&lt;/p&gt;

&lt;p&gt;بل أبني منظومة هندسية متكاملة.&lt;/p&gt;

&lt;p&gt;اليوم يضم المشروع أكثر من مجرد مخطوطة.&lt;/p&gt;

&lt;p&gt;إنه يضم منصة نشر احترافية، ووثائق هندسية، وأدوات أتمتة، ومعايير للمجتمع، وسجلًا للقرارات الهندسية، وقاعدة معرفة تتوسع مع كل إصدار.&lt;/p&gt;

&lt;p&gt;ما زال الكتاب يمثل قلب المشروع.&lt;/p&gt;

&lt;p&gt;لكنه لم يعد المشروع كله.&lt;/p&gt;

&lt;p&gt;ولعل أهم قرار اتخذته هو أن تُبنى هذه الرحلة أمام الجميع.&lt;/p&gt;

&lt;p&gt;كل إصدار.&lt;/p&gt;

&lt;p&gt;كل تحسين.&lt;/p&gt;

&lt;p&gt;كل قرار هندسي.&lt;/p&gt;

&lt;p&gt;وكل درس مستفاد.&lt;/p&gt;

&lt;p&gt;فبدلًا من نشر النتيجة النهائية فقط، قررت توثيق الرحلة كاملة.&lt;/p&gt;

&lt;p&gt;الإصدار الأول لا ينشر سوى مقدمة الكتاب.&lt;/p&gt;

&lt;p&gt;وقد يبدو ذلك إنجازًا صغيرًا.&lt;/p&gt;

&lt;p&gt;لكنه بالنسبة لي يمثل أول خطوة في مشروع أتمنى أن يستمر في التطور لسنوات طويلة.&lt;/p&gt;

&lt;p&gt;وإذا ساعدت هذه الرحلة مهندسًا واحدًا على بناء مشروع أفضل، أو توثيق معرفة بصورة أفضل، أو تطوير سير عمل أكثر احترافية، فسأعتبر أن المشروع بدأ يحقق رسالته.&lt;/p&gt;

&lt;p&gt;وما هذه إلا البداية.&lt;/p&gt;


&lt;h2&gt;
  
  
  Engineering Series
&lt;/h2&gt;

&lt;p&gt;التالي&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;002-Why I Built My Own Markdown Publishing Platform →&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  تابع الرحلة
&lt;/h2&gt;

&lt;p&gt;إذا أعجبتك فكرة المشروع وترغب في متابعة تطوره أو قراءة الإصدارات القادمة، يمكنك زيارة المستودع من خلال الرابط التالي.&lt;/p&gt;
&lt;h3&gt;
  
  
  المستودع
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  الإصدار الأول
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>AI-Assisted Professional Engineering with .NET</title>
      <dc:creator>Tarek</dc:creator>
      <pubDate>Tue, 28 Jul 2026 21:44:45 +0000</pubDate>
      <link>https://dev.to/najem/ai-assisted-professional-engineering-with-net-3m68</link>
      <guid>https://dev.to/najem/ai-assisted-professional-engineering-with-net-3m68</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj327ec4t0sq2s8xspvcd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj327ec4t0sq2s8xspvcd.jpg" alt="Front Cover" width="794" height="1123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  بداية الرحلة
&lt;/h2&gt;

&lt;p&gt;لفترةٍ طويلة كنت أعتقد أنني أؤلف كتابًا تقنيًا.&lt;/p&gt;

&lt;p&gt;لكن في مرحلة ما من الرحلة، أدركت أن ما أبنيه أكبر من كتاب.&lt;/p&gt;

&lt;p&gt;في كل مرة كنت أحل مشكلة في النشر، أو أوثق قرارًا هندسيًا، أو أؤتمت مهمة متكررة، أو أحسن سير العمل، كان المشروع يكبر في اتجاه لم أخطط له منذ البداية.&lt;/p&gt;

&lt;p&gt;اليوم لم يعد &lt;strong&gt;AI-Assisted Professional Engineering with .NET&lt;/strong&gt; مجرد مخطوطة كتاب.&lt;/p&gt;

&lt;p&gt;بل أصبح مشروعًا هندسيًا مفتوحًا يجمع بين:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;كتاب تقني باللغتين العربية والإنجليزية.&lt;/li&gt;
&lt;li&gt;منصة احترافية لتحويل Markdown إلى DOCX وPDF بجودة عالية.&lt;/li&gt;
&lt;li&gt;توثيق هندسي ومعماري.&lt;/li&gt;
&lt;li&gt;أدوات أتمتة.&lt;/li&gt;
&lt;li&gt;معايير لتنظيم المستودع.&lt;/li&gt;
&lt;li&gt;توثيق علني للقرارات الهندسية.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;كل شيء في هذا المشروع يُبنى في العلن.&lt;/p&gt;

&lt;p&gt;كل إصدار يمثل تقدمًا هندسيًا حقيقيًا.&lt;/p&gt;

&lt;p&gt;وكل وثيقة تضيف لبنة جديدة إلى قاعدة معرفة تتوسع مع مرور الوقت.&lt;/p&gt;

&lt;p&gt;اليوم أنشر أول محطة عامة في هذه الرحلة.&lt;/p&gt;

&lt;p&gt;يتضمن الإصدار مقدمة الكتاب، لكنه يمثل قبل ذلك الأساس الهندسي الذي سيُبنى عليه المشروع خلال السنوات القادمة.&lt;/p&gt;

&lt;p&gt;أتمنى أن يجد مطورو البرمجيات، والمهندسون، وكتّاب المحتوى التقني، وكل المهتمين بالذكاء الاصطناعي والهندسة البرمجية، ما يفيدهم في هذه الرحلة.&lt;/p&gt;

&lt;p&gt;المستودع&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;الإصدار الأول&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;يسعدني كثيرًا سماع آرائكم، وملاحظاتكم، وأفكاركم حول المشروع ورحلته.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvsbk68jp9lwoy5fxvkvr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvsbk68jp9lwoy5fxvkvr.jpg" alt="Back Cover" width="794" height="1123"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>AI-Assisted Professional Engineering with .NET</title>
      <dc:creator>Tarek</dc:creator>
      <pubDate>Tue, 28 Jul 2026 21:40:40 +0000</pubDate>
      <link>https://dev.to/najem/i-assisted-professional-engineering-with-net-1bdg</link>
      <guid>https://dev.to/najem/i-assisted-professional-engineering-with-net-1bdg</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyyyno5urt1y45gheh43t.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyyyno5urt1y45gheh43t.jpg" alt="Front Cover" width="794" height="1123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The journey begins
&lt;/h2&gt;

&lt;p&gt;For a long time, I believed I was writing a technical book.&lt;/p&gt;

&lt;p&gt;At some point, I realized I was actually building something much bigger.&lt;/p&gt;

&lt;p&gt;Every time I solved a publishing problem, automated a repetitive task, documented an architectural decision, or improved my engineering workflow, I wasn't just writing chapters anymore—I was building an engineering ecosystem.&lt;/p&gt;

&lt;p&gt;That realization completely changed the direction of the project.&lt;/p&gt;

&lt;p&gt;Today, &lt;strong&gt;AI-Assisted Professional Engineering with .NET&lt;/strong&gt; is no longer just a manuscript.&lt;/p&gt;

&lt;p&gt;It has grown into an open engineering project that brings together:&lt;/p&gt;

&lt;p&gt;• A bilingual technical manuscript (English &amp;amp; Arabic)&lt;br&gt;
• A professional Markdown publishing platform capable of producing high-quality DOCX and PDF documents&lt;/p&gt;

&lt;p&gt;• Engineering documentation&lt;br&gt;
• Automation tooling&lt;br&gt;
• Repository standards&lt;br&gt;
• Public engineering decisions&lt;/p&gt;

&lt;p&gt;Everything is developed in public.&lt;/p&gt;

&lt;p&gt;Every release represents measurable engineering progress.&lt;br&gt;
Every document contributes to a continuously growing knowledge base.&lt;/p&gt;

&lt;p&gt;Today I'm publishing the project's first public milestone.&lt;/p&gt;

&lt;p&gt;It contains the complete Introduction of the manuscript, but more importantly, it establishes the engineering foundation for everything that follows.&lt;/p&gt;

&lt;p&gt;This project will continue to evolve for years.&lt;/p&gt;

&lt;p&gt;The manuscript will grow.&lt;/p&gt;

&lt;p&gt;The publishing platform will mature.&lt;/p&gt;

&lt;p&gt;The engineering knowledge base will expand.&lt;/p&gt;

&lt;p&gt;I hope that by documenting the entire journey openly, other engineers, authors, and development teams will find ideas they can reuse in their own projects.&lt;/p&gt;

&lt;p&gt;Repository&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First Public Release&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/AI-Assisted-Professional-Engineering-with-.NET/releases/tag/v0.0.1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm looking forward to your thoughts, feedback, and engineering discussions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F59c8vsq4l8tt9a4ksw60.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F59c8vsq4l8tt9a4ksw60.jpg" alt="Back Cover" width="794" height="1123"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Jeninnet.FileQuery</title>
      <dc:creator>Tarek</dc:creator>
      <pubDate>Wed, 03 Jun 2026 06:22:43 +0000</pubDate>
      <link>https://dev.to/najem/jeninnetfilequery-460l</link>
      <guid>https://dev.to/najem/jeninnetfilequery-460l</guid>
      <description>&lt;h1&gt;
  
  
  Building a Deterministic File Query Engine for .NET
&lt;/h1&gt;

&lt;h2&gt;
  
  
  How Jeninnet.FileQuery Combines GitIgnore, Glob, and Regex Into a Predictable High-Performance Query Engine
&lt;/h2&gt;

&lt;p&gt;Building a Deterministic File Query Engine for .NET&lt;br&gt;
Technical Whitepaper&lt;br&gt;
Version 1.0&lt;br&gt;
Author: Tarek Najem&lt;br&gt;
GitHub: &lt;a href="https://github.com/TarekNajem04/Jeninnet.FileQuery" rel="noopener noreferrer"&gt;https://github.com/TarekNajem04/Jeninnet.FileQuery&lt;/a&gt;&lt;br&gt;
NuGet: &lt;a href="https://www.nuget.org/packages/Jeninnet.FileQuery" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Jeninnet.FileQuery&lt;/a&gt;&lt;br&gt;
License: MIT&lt;/p&gt;


&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Jeninnet.FileQuery

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;1. Introduction&lt;/li&gt;
&lt;li&gt;2. The Problem With Traditional File Matching&lt;/li&gt;
&lt;li&gt;2.1 Glob-only libraries&lt;/li&gt;
&lt;li&gt;2.2 Regular expression libraries&lt;/li&gt;
&lt;li&gt;2.3 GitIgnore-inspired libraries&lt;/li&gt;
&lt;li&gt;3. Deterministic Rule Evaluation&lt;/li&gt;
&lt;li&gt;4. The Pattern Language&lt;/li&gt;
&lt;li&gt;4.1 GitIgnore Patterns&lt;/li&gt;
&lt;li&gt;4.2 Glob Patterns&lt;/li&gt;
&lt;li&gt;4.3 Regular Expression Patterns&lt;/li&gt;
&lt;li&gt;4.4 POSIX Character Classes&lt;/li&gt;
&lt;li&gt;5. Architecture&lt;/li&gt;
&lt;li&gt;5.1 Separation of Concerns&lt;/li&gt;
&lt;li&gt;5.2 The Compilation Pipeline&lt;/li&gt;
&lt;li&gt;5.3 The HybridPathMatcher&lt;/li&gt;
&lt;li&gt;5.4 Traversal&lt;/li&gt;
&lt;li&gt;5.5 Path Normalization&lt;/li&gt;
&lt;li&gt;6. Performance&lt;/li&gt;
&lt;li&gt;6.1 Zero-Allocation Hot Path&lt;/li&gt;
&lt;li&gt;6.2 Benchmark Results&lt;/li&gt;
&lt;li&gt;6.3 Compilation Pipeline Allocations&lt;/li&gt;
&lt;li&gt;7. Getting Started&lt;/li&gt;
&lt;li&gt;7.1 Installation&lt;/li&gt;
&lt;li&gt;7.2 Basic Usage&lt;/li&gt;
&lt;li&gt;7.3 Pattern-Based Filtering&lt;/li&gt;
&lt;li&gt;7.4 Hybrid Pattern Mixing&lt;/li&gt;
&lt;li&gt;7.5 Async Enumeration&lt;/li&gt;
&lt;li&gt;7.6 Dependency Injection&lt;/li&gt;
&lt;li&gt;7.7 Command-Line Integration&lt;/li&gt;
&lt;li&gt;8. Package Reference&lt;/li&gt;
&lt;li&gt;Supported Targets&lt;/li&gt;
&lt;li&gt;9. Design Goals and Non-Goals&lt;/li&gt;
&lt;li&gt;Goals&lt;/li&gt;
&lt;li&gt;Non-Goals&lt;/li&gt;
&lt;li&gt;10. Conclusion&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Every non-trivial software system eventually encounters the same deceptively simple task: finding files.&lt;br&gt;
Build systems search for source files. Backup tools scan directories to determine what has changed.&lt;br&gt;
Code analyzers walk entire repositories. Log processors filter terabytes of archived data.&lt;/p&gt;

&lt;p&gt;At first glance, file discovery appears trivial. Operating systems provide directory enumeration APIs, and many environments include globbing utilities.&lt;br&gt;
But once a project grows, developers discover deeper issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pattern languages behave inconsistently.&lt;/li&gt;
&lt;li&gt;Traversal becomes expensive at scale.&lt;/li&gt;
&lt;li&gt;Rule ordering is unclear.&lt;/li&gt;
&lt;li&gt;Pattern syntaxes cannot easily coexist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These challenges led to the creation of Jeninnet.FileQuery, a library that treats file discovery as a first-class architectural problem.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. The Problem With Traditional File Matching
&lt;/h2&gt;

&lt;p&gt;Most libraries approach file matching from one of three directions.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.1 Glob-only libraries
&lt;/h3&gt;

&lt;p&gt;Glob patterns are simple and familiar.&lt;br&gt;
However, limitations appear when rule sets grow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No rule ordering&lt;/li&gt;
&lt;li&gt;No negation&lt;/li&gt;
&lt;li&gt;No mixing with regex&lt;/li&gt;
&lt;li&gt;No hierarchical semantics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2.2 Regular expression libraries
&lt;/h3&gt;

&lt;p&gt;Regex is expressive but not suited for hierarchical filesystem rules.&lt;br&gt;
Patterns become unreadable and difficult to maintain.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.3 GitIgnore-inspired libraries
&lt;/h3&gt;

&lt;p&gt;GitIgnore introduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rule ordering&lt;/li&gt;
&lt;li&gt;Negation&lt;/li&gt;
&lt;li&gt;Directory-aware semantics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But these libraries rarely allow mixing GitIgnore, Glob, and Regex in the same rule set.&lt;/p&gt;

&lt;p&gt;The deeper issue is not syntax but evaluation order.&lt;br&gt;
Ambiguities lead to unpredictable results.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Deterministic Rule Evaluation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Jeninnet.FileQuery&lt;/strong&gt; adopts a simple and explicit rule model:&lt;/p&gt;

&lt;p&gt;Patterns are evaluated sequentially, and the last matching rule determines the final result.&lt;/p&gt;

&lt;p&gt;Example rule set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**           # Exclude everything
!*.log       # Include all .log files
data.log     # Exclude this specific file again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Evaluation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rule 1: exclude all files&lt;/li&gt;
&lt;li&gt;Rule 2: include files ending in .log&lt;/li&gt;
&lt;li&gt;Rule 3: exclude data.log specifically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data.log is excluded&lt;/li&gt;
&lt;li&gt;all other .log files are included&lt;/li&gt;
&lt;li&gt;all other files remain excluded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This deterministic model eliminates ambiguity and ensures predictable behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Pattern Language
&lt;/h2&gt;

&lt;p&gt;Jeninnet.FileQuery supports three pattern dialects that can coexist in the same rule set:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitIgnore patterns&lt;/li&gt;
&lt;li&gt;Glob patterns&lt;/li&gt;
&lt;li&gt;Regular expressions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engine automatically classifies each pattern and routes it to the correct matcher.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 GitIgnore Patterns
&lt;/h3&gt;

&lt;p&gt;GitIgnore patterns are the default and most expressive for hierarchical rules.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;**&lt;/td&gt;
&lt;td&gt;Match zero or more path segments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*&lt;/td&gt;
&lt;td&gt;Match any characters within one segment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;Match exactly one character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;!&lt;/td&gt;
&lt;td&gt;Negate the pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/pattern&lt;/td&gt;
&lt;td&gt;Anchor to root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pattern/&lt;/td&gt;
&lt;td&gt;Directory-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[abc]&lt;/td&gt;
&lt;td&gt;Character set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[a-z]&lt;/td&gt;
&lt;td&gt;Character range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[!abc]&lt;/td&gt;
&lt;td&gt;Negated set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[[:digit:]]&lt;/td&gt;
&lt;td&gt;POSIX digit class&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**               # exclude everything
!src/**/.cs      # include C# files under src
src/obj/**       # exclude obj
src/bin/**       # exclude bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Glob Patterns
&lt;/h3&gt;

&lt;p&gt;Glob patterns follow classical Unix rules and are always anchored.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*.cs
**/*.cs
data/??.log
report.[0-9].txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Negation is not supported in glob patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Regular Expression Patterns
&lt;/h3&gt;

&lt;p&gt;Regex patterns are prefixed with &lt;code&gt;r:&lt;/code&gt; and evaluated against the full normalized path.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;r:^src/.*\.cs$
r:^data_\d{4}\.log$
r:^(?!.*test).*\.dll$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.4 POSIX Character Classes
&lt;/h3&gt;

&lt;p&gt;Supported inside &lt;code&gt;[: :]&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Matches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[:digit:]&lt;/td&gt;
&lt;td&gt;0–9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[:alpha:]&lt;/td&gt;
&lt;td&gt;a–z, A–Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[:alnum:]&lt;/td&gt;
&lt;td&gt;digits and letters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[:space:]&lt;/td&gt;
&lt;td&gt;whitespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[:upper:]&lt;/td&gt;
&lt;td&gt;A–Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[:lower:]&lt;/td&gt;
&lt;td&gt;a–z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[:xdigit:]&lt;/td&gt;
&lt;td&gt;hex digits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[:punct:]&lt;/td&gt;
&lt;td&gt;punctuation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**
![[:digit:]]*.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Separation of Concerns
&lt;/h3&gt;

&lt;p&gt;The engine separates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pattern compilation&lt;/li&gt;
&lt;li&gt;Filesystem traversal&lt;/li&gt;
&lt;li&gt;Matching execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each layer is isolated and enforced by architecture tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 The Compilation Pipeline
&lt;/h3&gt;

&lt;p&gt;Patterns pass through four phases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lexical invariant&lt;/td&gt;
&lt;td&gt;Validate raw text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PatternScanner&lt;/td&gt;
&lt;td&gt;Tokenize into tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structural invariants&lt;/td&gt;
&lt;td&gt;Validate token structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic invariants&lt;/td&gt;
&lt;td&gt;Apply dialect transforms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The scanner is purely lexical; semantics are applied later for clarity and testability.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 The HybridPathMatcher
&lt;/h3&gt;

&lt;p&gt;The matcher coordinates three sub-matchers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitIgnoreInstructionMatcher&lt;/li&gt;
&lt;li&gt;GlobInstructionMatcher&lt;/li&gt;
&lt;li&gt;RegexInstructionMatcher&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Routing is precomputed, so evaluation is fast and allocation-free.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.4 Traversal
&lt;/h3&gt;

&lt;p&gt;Supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depth-first traversal&lt;/li&gt;
&lt;li&gt;Breadth-first traversal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Options include recursion depth, symlink policy, case sensitivity, and error handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.5 Path Normalization
&lt;/h3&gt;

&lt;p&gt;All paths are normalized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forward slashes&lt;/li&gt;
&lt;li&gt;Collapsed duplicates&lt;/li&gt;
&lt;li&gt;Preserved UNC roots&lt;/li&gt;
&lt;li&gt;Uppercased drive letters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures cross-platform consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Performance
&lt;/h2&gt;

&lt;p&gt;Jeninnet.FileQuery is designed for high performance, especially in large directory trees and complex rule sets.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 Zero-Allocation Hot Path
&lt;/h3&gt;

&lt;p&gt;The engine ensures that pattern matching produces zero heap allocations in the hot path.&lt;/p&gt;

&lt;p&gt;Before optimization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This caused boxing of the enumerator (~40 bytes per evaluation).&lt;/p&gt;

&lt;p&gt;After optimization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This eliminates all allocations during matching.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 Benchmark Results
&lt;/h3&gt;

&lt;p&gt;Environment:&lt;br&gt;
Intel Core i7-8850H 2.60 GHz&lt;br&gt;
.NET 10.0.5&lt;br&gt;
Windows 11&lt;br&gt;
BenchmarkDotNet v0.15.8&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Mean&lt;/th&gt;
&lt;th&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PatternClassifier&lt;/td&gt;
&lt;td&gt;64 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GlobMatcher&lt;/td&gt;
&lt;td&gt;261 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RegexMatcher&lt;/td&gt;
&lt;td&gt;85 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitIgnoreMatcher&lt;/td&gt;
&lt;td&gt;771 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HybridMatcher&lt;/td&gt;
&lt;td&gt;742 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PatternTokenizer&lt;/td&gt;
&lt;td&gt;4.85 µs&lt;/td&gt;
&lt;td&gt;~9 KB (one-time)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traversal (QueryFiles)&lt;/td&gt;
&lt;td&gt;2.0 ms&lt;/td&gt;
&lt;td&gt;~30 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most allocations come from returning actual file paths, not from the engine itself.&lt;/p&gt;
&lt;h3&gt;
  
  
  6.3 Compilation Pipeline Allocations
&lt;/h3&gt;

&lt;p&gt;When the caller specifies a PatternKind explicitly, the engine skips classification and reduces allocations by ~400–500 bytes per pattern.&lt;/p&gt;

&lt;p&gt;Sub-lists for pattern kinds are allocated lazily.&lt;br&gt;
If only GitIgnore patterns are used, no Glob or Regex lists are created.&lt;/p&gt;


&lt;h2&gt;
  
  
  7. Getting Started
&lt;/h2&gt;
&lt;h3&gt;
  
  
  7.1 Installation
&lt;/h3&gt;

&lt;p&gt;Install the core package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jeninnet.FileQuery&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jeninnet.FileQuery.CommandLine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jeninnet.FileQuery.DependencyInjection&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.2 Basic Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FileQueryRuntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FileQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\repo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.3 Pattern-Based Filtering
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FileQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\repo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                         &lt;span class="s"&gt;"**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="s"&gt;"!src/**/*.cs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="s"&gt;"src/obj/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="s"&gt;"src/bin/**"&lt;/span&gt;
                     &lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.4 Hybrid Pattern Mixing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FileQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\repo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UsingHybrid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                         &lt;span class="s"&gt;"**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="s"&gt;"!*Global*.cs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="s"&gt;"r:^src/.*Engine.*"&lt;/span&gt;
                     &lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IgnoreCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                     &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.5 Async Enumeration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ProcessFileAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.6 Dependency Injection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFileQuery&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileScanner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IFileQueryEngine&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.7 Command-Line Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Usage: myapp --patterns "**;!*.exe" --gitignore "bin/;obj/"&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CommandLinePatternOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootCmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RootCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File scanner"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCommandOptions&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;rootCmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;rootCmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PatternBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FileQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Package Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jeninnet.FileQuery&lt;/td&gt;
&lt;td&gt;Core engine. GitIgnore, Glob, Regex. Zero-allocation matching.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jeninnet.FileQuery.CommandLine&lt;/td&gt;
&lt;td&gt;Maps System.CommandLine results to pattern options.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jeninnet.FileQuery.DependencyInjection&lt;/td&gt;
&lt;td&gt;Registers IFileQueryEngine for DI containers.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Supported Targets
&lt;/h3&gt;

&lt;p&gt;All packages target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;net10.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Symbol packages and Source Link are enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Design Goals and Non-Goals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Goals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deterministic behavior&lt;/li&gt;
&lt;li&gt;Composable pattern dialects&lt;/li&gt;
&lt;li&gt;Zero-allocation hot path&lt;/li&gt;
&lt;li&gt;Streaming traversal&lt;/li&gt;
&lt;li&gt;Cross-platform normalization&lt;/li&gt;
&lt;li&gt;Strong architectural boundaries&lt;/li&gt;
&lt;li&gt;Extensible compiler and matcher pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Non-Goals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;File content inspection&lt;/li&gt;
&lt;li&gt;Parallel traversal (planned for v1.1)&lt;/li&gt;
&lt;li&gt;Mutable filesystem operations&lt;/li&gt;
&lt;li&gt;Pattern caching across queries (planned for v1.1)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. Conclusion
&lt;/h2&gt;

&lt;p&gt;File discovery becomes complex at scale.&lt;br&gt;
Jeninnet.FileQuery solves this by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Providing deterministic rule evaluation&lt;/li&gt;
&lt;li&gt;Supporting mixed pattern dialects&lt;/li&gt;
&lt;li&gt;Ensuring high performance with zero allocations&lt;/li&gt;
&lt;li&gt;Offering a clean, extensible architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jeninnet.FileQuery&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: github.com/TarekNajem04/Jeninnet.FileQuery&lt;br&gt;
NuGet: nuget.org/packages/Jeninnet.FileQuery&lt;br&gt;
License: MIT&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>patternmatching</category>
      <category>gitignore</category>
      <category>filesearch</category>
    </item>
  </channel>
</rss>
