<?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: Olivia Craft</title>
    <description>The latest articles on DEV Community by Olivia Craft (@olivia_craft).</description>
    <link>https://dev.to/olivia_craft</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3860999%2Fbb205e04-9977-4cb0-88e0-ffbbf2f009ab.png</url>
      <title>DEV Community: Olivia Craft</title>
      <link>https://dev.to/olivia_craft</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/olivia_craft"/>
    <language>en</language>
    <item>
      <title>CLAUDE.md for C++: 13 Rules That Make AI Write Safe, Modern, Idiomatic C++</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Sat, 16 May 2026 06:58:19 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-c-13-rules-that-make-ai-write-safe-modern-idiomatic-c-3dna</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-c-13-rules-that-make-ai-write-safe-modern-idiomatic-c-3dna</guid>
      <description>&lt;h1&gt;
  
  
  CLAUDE.md for C++: 13 Rules That Make AI Write Safe, Modern, Idiomatic C++
&lt;/h1&gt;

&lt;p&gt;C++ is the language where AI assistance can either save you thousands of hours — or introduce bugs that hide in production for months. The difference comes down to whether you've told your AI assistant which C++ you're writing.&lt;/p&gt;

&lt;p&gt;Without a &lt;code&gt;CLAUDE.md&lt;/code&gt;, the model might write C++98 in a C++20 codebase, reach for raw pointers when you expect smart pointers, or generate undefined behavior that passes every test until the wrong CPU architecture exposes it.&lt;/p&gt;

&lt;p&gt;These 13 rules have emerged from real projects — the patterns that matter most for keeping AI-generated C++ safe, modern, and idiomatic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: Pin the standard — C++20 or C++23 only
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="nl"&gt;Standard:&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Enable&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;concepts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ranges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;coroutines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Forbidden&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;idioms&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;modern&lt;/span&gt; &lt;span class="n"&gt;equivalents&lt;/span&gt; &lt;span class="n"&gt;exist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI models have seen enormous amounts of legacy C++ code. Without this rule, you'll get a mix of &lt;code&gt;std::bind&lt;/code&gt; and lambdas, &lt;code&gt;printf&lt;/code&gt; next to &lt;code&gt;std::format&lt;/code&gt;, and iterators where ranges would be cleaner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; C++20 concepts alone eliminate entire classes of template error messages and make intent explicit. &lt;code&gt;std::span&lt;/code&gt; replaces the pointer+size anti-pattern. The standard pin prevents the model from defaulting to the lowest common denominator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without rule — AI might generate this&lt;/span&gt;
&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n&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;span class="c1"&gt;// With rule — AI generates this&lt;/span&gt;
&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ranges&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;range&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;h2&gt;
  
  
  Rule 2: Smart pointers only — raw owning pointers banned
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="nl"&gt;Memory:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unique_ptr&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ownership&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;pointers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;observer&lt;/span&gt; &lt;span class="n"&gt;pointers&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;never&lt;/span&gt; &lt;span class="n"&gt;owning&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="n"&gt;Forbidden&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;free&lt;/span&gt; &lt;span class="n"&gt;never&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt; &lt;span class="n"&gt;exclusively&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most common AI-generated C++ mistake is mixing ownership models. A function that accepts &lt;code&gt;T*&lt;/code&gt; when it means "I own this" is a memory leak waiting to happen — and AI generates this pattern constantly without explicit instruction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Banned — raw owning pointer&lt;/span&gt;
&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;createWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// Who deletes this?&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Required — ownership explicit&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unique_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;&amp;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;&lt;strong&gt;The observer pointer exception:&lt;/strong&gt; &lt;code&gt;T*&lt;/code&gt; and &lt;code&gt;T&amp;amp;&lt;/code&gt; are fine as non-owning references — they communicate "I borrow this, I don't own it."&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: RAII for every resource — no manual cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="nl"&gt;Resources:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt; &lt;span class="n"&gt;every&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;RAII&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;handles&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Locks&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;lock_guard&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unique_lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;OS&lt;/span&gt; &lt;span class="n"&gt;handles&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="n"&gt;RAII&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;destructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;acquired&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;destructor&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;releases&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI will generate &lt;code&gt;fclose(file)&lt;/code&gt; at the end of a function. It won't always generate it on every early return. RAII removes the entire problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AI without rule — leak on early return&lt;/span&gt;
&lt;span class="kt"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data.bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rb"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// leak&lt;/span&gt;
&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// With rule — RAII, always closed&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ifstream&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data.bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ios&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// destructor runs&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&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;
  
  
  Rule 4: No undefined behavior — ever
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;UB&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="kt"&gt;signed&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="n"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;checked&lt;/span&gt; &lt;span class="n"&gt;arithmetic&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hot&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hot&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;uninitialized&lt;/span&gt; &lt;span class="n"&gt;reads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Initialize&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;variables&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;declaration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;dangling&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;returning&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt; &lt;span class="n"&gt;aliasing&lt;/span&gt; &lt;span class="n"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;bit_cast&lt;/span&gt; &lt;span class="n"&gt;instead&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="k"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the rule AI needs most explicitly. UB that "works" on x86 can crash on ARM. UB that passes tests can be exploited by compilers doing aggressive optimization. Models will generate UB because it compiles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// UB — uninitialized variable&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// undefined if !condition&lt;/span&gt;

&lt;span class="c1"&gt;// Required — always initialized&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rule 5: Prefer value semantics — move is free
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="n"&gt;semantics&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;moving&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;cheap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Return&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;NRVO&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;RVO&lt;/span&gt; &lt;span class="n"&gt;makes&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt; &lt;span class="n"&gt;explicitly&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;transferring&lt;/span&gt; &lt;span class="n"&gt;ownership&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Const&lt;/span&gt; &lt;span class="n"&gt;references&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;large&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Avoid&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;structs&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt; &lt;span class="n"&gt;instead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI defaults to C-style output parameters and reference-heavy signatures. Modern C++ with move semantics makes value semantics fast and clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AI without rule — output parameter style&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;getResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// With rule — value semantics&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// NRVO, zero copy&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rule 6: Concepts for every template constraint
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="nl"&gt;Templates:&lt;/span&gt; &lt;span class="n"&gt;constrain&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;concepts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;never&lt;/span&gt; &lt;span class="n"&gt;unconstrained&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;standard&lt;/span&gt; &lt;span class="n"&gt;concepts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;integral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;floating_point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ranges&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;invocable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Define&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;concepts&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;concepts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hpp&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;constraint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unconstrained templates produce error messages that span 50 lines. Concepts produce one clear diagnostic at the call site.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without rule — unconstrained template&lt;/span&gt;
&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt; &lt;span class="n"&gt;f&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;span class="c1"&gt;// With rule — concepts constrain intent&lt;/span&gt;
&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ranges&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;input_range&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;invocable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ranges&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;range_value_t&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;f&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;h2&gt;
  
  
  Rule 7: Error handling with std::expected or exceptions — no error codes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;handling&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Functions&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;fail&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;typed&lt;/span&gt; &lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;errno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;runtime_error&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;logic_error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;rethrow&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI generates error codes because of the enormous volume of C legacy code in training data. C++ deserves typed error handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Banned — C-style error code&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Required — typed result&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;filesystem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rule 8: const everywhere it can be
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt; &lt;span class="n"&gt;variables&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt; &lt;span class="n"&gt;member&lt;/span&gt; &lt;span class="n"&gt;functions&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;mutate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Prefer&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;constants&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;consteval&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;functions&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;const&lt;/code&gt; communicates intent, enables optimizations, and prevents a class of bugs. AI forgets it unless you insist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without rule — missed const opportunities&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// With rule — const-correct&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&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;
  
  
  Rule 9: std::span and std::string_view — not raw arrays or const char*
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="nl"&gt;Buffers:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;contiguous&lt;/span&gt; &lt;span class="n"&gt;ranges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Never&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Strings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Never&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Ownership&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Never&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[].&lt;/span&gt;
&lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sprintf&lt;/span&gt; &lt;span class="n"&gt;banned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;std::span&lt;/code&gt; and &lt;code&gt;std::string_view&lt;/code&gt; are zero-cost abstractions that express intent precisely. &lt;code&gt;const char*&lt;/code&gt; parameters can't communicate lifetime or bounds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Banned — raw C-style&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Required — expressive and safe&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_view&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rule 10: Structured bindings and pattern matching — use them
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Structured&lt;/span&gt; &lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;decomposition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;overloaded&lt;/span&gt; &lt;span class="n"&gt;lambdas&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;branching&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Avoid&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;based&lt;/span&gt; &lt;span class="n"&gt;tuple&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;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 makes AI-generated C++ readable. Index-based access and manual pair.first/pair.second are noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without rule — index access&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getCoordinates&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="c1"&gt;// With rule — structured binding&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getCoordinates&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rule 11: Thread safety is explicit — no implicit sharing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="nl"&gt;Concurrency:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Shared&lt;/span&gt; &lt;span class="k"&gt;mutable&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt; &lt;span class="n"&gt;always&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Prefer&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;atomic&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;single&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;global&lt;/span&gt; &lt;span class="k"&gt;mutable&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;mutable&lt;/span&gt; &lt;span class="n"&gt;locals&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;multithreaded&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jthread&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kr"&gt;thread&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;automatic&lt;/span&gt; &lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;latch&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;barrier&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;counting_semaphore&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="n"&gt;manual&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI generates code with races because races don't crash on the first test run. Making thread safety explicit in the config catches it before review.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without rule — silent data race&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// With rule — explicit atomic&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_relaxed&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;
  
  
  Rule 12: Build with warnings-as-errors — the compiler is a reviewer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Build flags &lt;span class="o"&gt;(&lt;/span&gt;required&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-Wextra&lt;/span&gt; &lt;span class="nt"&gt;-Wpedantic&lt;/span&gt; &lt;span class="nt"&gt;-Werror&lt;/span&gt;
&lt;span class="nt"&gt;-Wconversion&lt;/span&gt; &lt;span class="nt"&gt;-Wshadow&lt;/span&gt; &lt;span class="nt"&gt;-Wnull-dereference&lt;/span&gt;
Sanitizers &lt;span class="k"&gt;in &lt;/span&gt;debug: &lt;span class="nt"&gt;-fsanitize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;address,undefined
clang-tidy: enabled with modernize-&lt;span class="k"&gt;*&lt;/span&gt;, cppcoreguidelines-&lt;span class="k"&gt;*&lt;/span&gt; checks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI-generated code that compiles is not finished. Warnings-as-errors forces the model to generate cleaner code when you describe the constraint explicitly in CLAUDE.md.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 13: Modules or namespaces — never pollute global scope
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="nl"&gt;Namespaces:&lt;/span&gt; &lt;span class="n"&gt;every&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Modules&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prefer&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;toolchain&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Anonymous&lt;/span&gt; &lt;span class="n"&gt;namespaces&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;translation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="n"&gt;symbols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;constants&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;ever&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI loves &lt;code&gt;using namespace std;&lt;/code&gt; because it appears in every tutorial. It's banned in production headers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Banned in headers&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Required&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;myproject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;kMaxBufferSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;buffer_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;public:&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProcessError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&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;h2&gt;
  
  
  The CLAUDE.md block for C++
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## C++ Standards&lt;/span&gt;

&lt;span class="gs"&gt;**Standard:**&lt;/span&gt; C++20 minimum (C++23 where available)
&lt;span class="gs"&gt;**Compiler:**&lt;/span&gt; Clang 16+ or GCC 13+
&lt;span class="gs"&gt;**Build:**&lt;/span&gt; -Wall -Wextra -Wpedantic -Werror -Wconversion -Wshadow

&lt;span class="gu"&gt;### Memory&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; std::unique_ptr / std::shared_ptr for ownership. std::make_unique / std::make_shared only.
&lt;span class="p"&gt;-&lt;/span&gt; Raw pointers = non-owning observer only. new/delete banned in application code.
&lt;span class="p"&gt;-&lt;/span&gt; RAII for all resources — file handles, locks, OS handles.

&lt;span class="gu"&gt;### Types and APIs  &lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; std::span&lt;span class="nt"&gt;&amp;lt;T&amp;gt;&lt;/span&gt; for buffers (never T&lt;span class="err"&gt;*&lt;/span&gt; + size).
&lt;span class="p"&gt;-&lt;/span&gt; std::string_view for read-only strings (never const char&lt;span class="err"&gt;*&lt;/span&gt;).
&lt;span class="p"&gt;-&lt;/span&gt; std::expected&lt;span class="nt"&gt;&amp;lt;T&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;E&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; for fallible operations (C++23). Typed exceptions otherwise.
&lt;span class="p"&gt;-&lt;/span&gt; No C string functions: strlen/strcpy/sprintf banned.

&lt;span class="gu"&gt;### Templates&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Constrain every template parameter with concepts. No bare typename T.
&lt;span class="p"&gt;-&lt;/span&gt; Structured bindings over index access. if constexpr over SFINAE.

&lt;span class="gu"&gt;### Safety&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No UB: initialize all variables. Use .at() outside hot paths. No signed overflow.
&lt;span class="p"&gt;-&lt;/span&gt; const everywhere possible. constexpr for compile-time constants.
&lt;span class="p"&gt;-&lt;/span&gt; No using namespace in headers. Every symbol in project namespace.

&lt;span class="gu"&gt;### Concurrency&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; std::mutex for shared mutable state. std::atomic for single values.
&lt;span class="p"&gt;-&lt;/span&gt; std::jthread over std::thread. No global mutable state.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;The 13 rules above don't describe a style preference. They describe the line between C++ that a senior engineer would ship and C++ that would be flagged in review — or worse, that ships and fails under sanitizers.&lt;/p&gt;

&lt;p&gt;AI models have internalized decades of C++ code, most of it pre-C++11. Without explicit constraints, they'll default to the mean of that training distribution: raw pointers, error codes, unconstrained templates, and platform-specific assumptions.&lt;/p&gt;

&lt;p&gt;The CLAUDE.md block above shifts that default. It doesn't eliminate review — but it raises the floor so that AI-generated C++ starts from a position worth reviewing.&lt;/p&gt;

&lt;p&gt;The cost of writing this config once is about 15 minutes. The cost of reviewing AI-generated C++ without it is every sprint.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>ai</category>
      <category>programming</category>
      <category>claude</category>
    </item>
    <item>
      <title>CLAUDE.md for Haskell: 13 Rules That Make AI Write Idiomatic, Type-Safe Haskell</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Fri, 15 May 2026 06:59:37 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-haskell-13-rules-that-make-ai-write-idiomatic-type-safe-haskell-1l4n</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-haskell-13-rules-that-make-ai-write-idiomatic-type-safe-haskell-1l4n</guid>
      <description>&lt;p&gt;Haskell has the largest gap between code that compiles and code that's actually idiomatic of any mainstream language. The type system is powerful enough that AI can generate Haskell that type-checks but uses &lt;code&gt;head&lt;/code&gt; on empty lists, catches errors with &lt;code&gt;error ""&lt;/code&gt;, and avoids the very abstractions that make Haskell valuable.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; at your project root tells the AI which Haskell you're writing. Here are 13 rules with the highest impact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: GHC version and language extensions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;GHC version: 9.6+ (GHC2021 language set or explicit extensions).
Enable these extensions in package.yaml or cabal file (not per-file pragmas except for unusual cases):
  OverloadedStrings, ScopedTypeVariables, LambdaCase, TupleSections,
  DeriveFunctor, DeriveGeneric, GeneralizedNewtypeDeriving,
  FlexibleInstances, FlexibleContexts

Do NOT enable: PartialTypeSignatures (forces you to complete type signatures).
Do NOT use extensions that enable unsafe operations unless documented.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GHC's language extension system is powerful but needs explicit guidance. AI sometimes adds extensions ad-hoc or omits ones that would enable cleaner code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: No partial functions — totality is the goal
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Partial functions are banned:
&lt;span class="p"&gt;-&lt;/span&gt; Never use &lt;span class="sb"&gt;`head`&lt;/span&gt;, &lt;span class="sb"&gt;`tail`&lt;/span&gt;, &lt;span class="sb"&gt;`init`&lt;/span&gt;, &lt;span class="sb"&gt;`last`&lt;/span&gt; — use pattern matching or &lt;span class="sb"&gt;`NonEmpty`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never use &lt;span class="sb"&gt;`fromJust`&lt;/span&gt; — use &lt;span class="sb"&gt;`maybe`&lt;/span&gt;, &lt;span class="sb"&gt;`fromMaybe`&lt;/span&gt;, or pattern match
&lt;span class="p"&gt;-&lt;/span&gt; Never use &lt;span class="sb"&gt;`read`&lt;/span&gt; on untrusted input — use &lt;span class="sb"&gt;`readMaybe`&lt;/span&gt; from &lt;span class="sb"&gt;`Text.Read`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never use &lt;span class="sb"&gt;`error`&lt;/span&gt; for recoverable failures — use &lt;span class="sb"&gt;`Either`&lt;/span&gt; or &lt;span class="sb"&gt;`ExceptT`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never use &lt;span class="sb"&gt;`undefined`&lt;/span&gt; except as a temporary compile placeholder with a TODO comment

Prefer total alternatives:
  &lt;span class="sb"&gt;`headMay`&lt;/span&gt;, &lt;span class="sb"&gt;`atMay`&lt;/span&gt; from the &lt;span class="sb"&gt;`safe`&lt;/span&gt; package, or pattern matching.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Partial functions are the #1 source of runtime crashes in Haskell. &lt;code&gt;head []&lt;/code&gt; throws an exception that the type system didn't catch. AI uses partial functions because they're shorter and common in tutorials.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: Maybe and Either — explicit absence and failure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Absence and failure:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Maybe a`&lt;/span&gt; for values that may not exist
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Either e a`&lt;/span&gt; for operations that can fail with an error
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`ExceptT e m a`&lt;/span&gt; for monadic computations that can fail
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`MaybeT m a`&lt;/span&gt; for monadic computations that may produce nothing

Chain with &lt;span class="sb"&gt;`&amp;gt;&amp;gt;=`&lt;/span&gt;, &lt;span class="sb"&gt;`&amp;lt;$&amp;gt;`&lt;/span&gt;, &lt;span class="sb"&gt;`&amp;lt;*&amp;gt;`&lt;/span&gt;, &lt;span class="sb"&gt;`maybe`&lt;/span&gt;, &lt;span class="sb"&gt;`either`&lt;/span&gt;, &lt;span class="sb"&gt;`fromMaybe`&lt;/span&gt;.
Never throw exceptions for business logic — use &lt;span class="sb"&gt;`Left err`&lt;/span&gt;.

For error types: define a sum type, not &lt;span class="sb"&gt;`String`&lt;/span&gt;:
  &lt;span class="sb"&gt;`data AppError = NotFound Text | InvalidInput Text | DatabaseError Text`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;error "something went wrong"&lt;/code&gt; is Haskell's equivalent of unchecked exceptions. The type system is there to make errors explicit — use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 4: Type signatures on all top-level definitions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Every top-level function and value must have an explicit type signature:
  &lt;span class="sb"&gt;`processUser :: UserId -&amp;gt; IO (Either AppError User)`&lt;/span&gt; — not inferred.

Type signatures serve as:
&lt;span class="p"&gt;-&lt;/span&gt; Documentation (the primary kind)
&lt;span class="p"&gt;-&lt;/span&gt; Compiler-checked contracts
&lt;span class="p"&gt;-&lt;/span&gt; Guidance for type inference in complex expressions

Use &lt;span class="sb"&gt;`newtype`&lt;/span&gt; for type safety around primitives:
  &lt;span class="sb"&gt;`newtype UserId = UserId { unUserId :: Int } deriving (Show, Eq, Ord)`&lt;/span&gt;
  Not: &lt;span class="sb"&gt;`type UserId = Int`&lt;/span&gt; (type aliases provide no safety)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type inference is powerful but type signatures are still mandatory for top-level definitions. AI sometimes omits them to save space — they're not optional.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: Pattern matching — exhaustive and expressive
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Pattern matching rules:
&lt;span class="p"&gt;-&lt;/span&gt; Always handle all constructors — enable &lt;span class="sb"&gt;`-Wincomplete-patterns`&lt;/span&gt; in GHC options
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`LambdaCase`&lt;/span&gt; for concise single-argument matches:
    &lt;span class="sb"&gt;`\case { Just x -&amp;gt; ...; Nothing -&amp;gt; ... }`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Deconstruct in function arguments, not in the body:
    &lt;span class="sb"&gt;`processUser (User uid name) = ...`&lt;/span&gt; not &lt;span class="sb"&gt;`processUser u = let uid = userId u in ...`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`@`&lt;/span&gt; patterns to bind the whole value while matching: &lt;span class="sb"&gt;`processUser u@(User uid _) = ...`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Wildcards &lt;span class="sb"&gt;`_`&lt;/span&gt; only when the value is genuinely unused
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exhaustive pattern matching with compiler warnings turns runtime errors into compile errors. AI often uses incomplete patterns or reaches for field accessors where destructuring is cleaner.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 6: Functors, Applicatives, Monads — use the right abstraction
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Abstraction selection:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`fmap`&lt;/span&gt; / &lt;span class="sb"&gt;`&amp;lt;$&amp;gt;`&lt;/span&gt; when transforming a value inside a context (Functor)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`&amp;lt;*&amp;gt;`&lt;/span&gt; when applying a wrapped function to a wrapped value (Applicative)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`&amp;gt;&amp;gt;=`&lt;/span&gt; / &lt;span class="sb"&gt;`do`&lt;/span&gt; notation for sequential effects where each step depends on the previous (Monad)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`traverse`&lt;/span&gt; for &lt;span class="sb"&gt;`t a -&amp;gt; (a -&amp;gt; f b) -&amp;gt; f (t b)`&lt;/span&gt; (effects over a traversable)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`sequence`&lt;/span&gt; for &lt;span class="sb"&gt;`t (f a) -&amp;gt; f (t a)`&lt;/span&gt;

Prefer &lt;span class="sb"&gt;`do`&lt;/span&gt; notation for readability when there are 3+ monadic steps.
Prefer point-free style for simple compositions: &lt;span class="sb"&gt;`f . g . h`&lt;/span&gt; not &lt;span class="sb"&gt;`\x -&amp;gt; f (g (h x))`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Functor/Applicative/Monad hierarchy exists to express precisely how much power you need. AI often reaches for &lt;code&gt;Monad&lt;/code&gt;/&lt;code&gt;do&lt;/code&gt; when &lt;code&gt;Applicative&lt;/code&gt; or &lt;code&gt;fmap&lt;/code&gt; is sufficient — the less powerful abstraction is usually clearer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 7: Text, not String
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;String handling:
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`Text`&lt;/span&gt; (from &lt;span class="sb"&gt;`Data.Text`&lt;/span&gt;) everywhere, not &lt;span class="sb"&gt;`String`&lt;/span&gt; ([Char])
&lt;span class="p"&gt;-&lt;/span&gt; Enable &lt;span class="sb"&gt;`OverloadedStrings`&lt;/span&gt; so string literals work as &lt;span class="sb"&gt;`Text`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`Data.Text.pack`&lt;/span&gt;/&lt;span class="sb"&gt;`unpack`&lt;/span&gt; only at system boundaries (IO, legacy APIs)
&lt;span class="p"&gt;-&lt;/span&gt; For building text: &lt;span class="sb"&gt;`Data.Text.Builder`&lt;/span&gt; or &lt;span class="sb"&gt;`fmt`&lt;/span&gt; library — not &lt;span class="sb"&gt;`++`&lt;/span&gt; concatenation
&lt;span class="p"&gt;-&lt;/span&gt; For parsing: &lt;span class="sb"&gt;`attoparsec`&lt;/span&gt; or &lt;span class="sb"&gt;`megaparsec`&lt;/span&gt; — not manual &lt;span class="sb"&gt;`String`&lt;/span&gt; manipulation
&lt;span class="p"&gt;-&lt;/span&gt; ByteString for binary data — never treat &lt;span class="sb"&gt;`ByteString`&lt;/span&gt; as text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;String = [Char]&lt;/code&gt; is a linked list of characters. It's the default for historical reasons and is terrible for performance. &lt;code&gt;Text&lt;/code&gt; is the correct type for textual data. AI uses &lt;code&gt;String&lt;/code&gt; because tutorials use &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 8: Records and lenses
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Record conventions:
&lt;span class="p"&gt;-&lt;/span&gt; Use record syntax for data types with multiple fields
&lt;span class="p"&gt;-&lt;/span&gt; Prefix field names with the type name to avoid ambiguity:
    &lt;span class="sb"&gt;`data User = User { userId :: UserId, userName :: Text, userEmail :: Email }`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`lens`&lt;/span&gt; or &lt;span class="sb"&gt;`optics`&lt;/span&gt; for nested record access/update
&lt;span class="p"&gt;-&lt;/span&gt; Avoid the &lt;span class="sb"&gt;`RecordWildCards`&lt;/span&gt; extension — it makes imports invisible
&lt;span class="p"&gt;-&lt;/span&gt; For large records: use &lt;span class="sb"&gt;`Generic`&lt;/span&gt; + &lt;span class="sb"&gt;`lens`&lt;/span&gt; derivation

When using lenses:
  &lt;span class="sb"&gt;`user ^. userName`&lt;/span&gt; not &lt;span class="sb"&gt;`userName user`&lt;/span&gt;
  &lt;span class="sb"&gt;`user &amp;amp; userName .~ "Alice"`&lt;/span&gt; not &lt;span class="sb"&gt;`user { userName = "Alice" }`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record field names in Haskell pollute the module namespace without prefixing. Lenses provide composable access and update for nested data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 9: IO and effects — keep pure code pure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Effect discipline:
&lt;span class="p"&gt;-&lt;/span&gt; Maximize pure functions — if it doesn't need IO, don't put it in IO
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`ReaderT env IO`&lt;/span&gt; as the application monad (not raw IO or a complex mtl stack)
&lt;span class="p"&gt;-&lt;/span&gt; Keep the &lt;span class="sb"&gt;`IO`&lt;/span&gt; boundary at the edges: parse input, process purely, render output
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`IORef`&lt;/span&gt; only when mutation is genuinely required (rare)
&lt;span class="p"&gt;-&lt;/span&gt; For config: pass explicit records, not global IORef or unsafePerformIO

For concurrent code: use &lt;span class="sb"&gt;`async`&lt;/span&gt; + &lt;span class="sb"&gt;`stm`&lt;/span&gt; (Software Transactional Memory).
Never use &lt;span class="sb"&gt;`unsafePerformIO`&lt;/span&gt; outside of FFI boundaries.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"IO everywhere" is a beginner pattern. Pure functions are easier to test, reason about, and compose. The discipline of keeping IO at the edges is one of Haskell's greatest design patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 10: Testing with Hspec and QuickCheck
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Testing:
&lt;span class="p"&gt;-&lt;/span&gt; Hspec for unit and integration tests (BDD-style: &lt;span class="sb"&gt;`describe`&lt;/span&gt;/&lt;span class="sb"&gt;`it`&lt;/span&gt;/&lt;span class="sb"&gt;`shouldBe`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; QuickCheck for property-based testing — generate invariants, not just examples
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`hspec-discover`&lt;/span&gt; for automatic spec discovery
&lt;span class="p"&gt;-&lt;/span&gt; Test pure functions without IO — makes tests fast and deterministic
&lt;span class="p"&gt;-&lt;/span&gt; For IO: use &lt;span class="sb"&gt;`hspec`&lt;/span&gt; with &lt;span class="sb"&gt;`before`&lt;/span&gt;/&lt;span class="sb"&gt;`after`&lt;/span&gt; for setup/teardown
&lt;span class="p"&gt;-&lt;/span&gt; Golden tests: &lt;span class="sb"&gt;`hspec-golden`&lt;/span&gt; for output comparison

Property patterns to always write:
  roundtrip: &lt;span class="sb"&gt;`encode . decode = id`&lt;/span&gt;
  idempotence: &lt;span class="sb"&gt;`f (f x) = f x`&lt;/span&gt;
  commutativity where applicable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;QuickCheck is one of Haskell's most valuable tools and is underused in AI-generated test suites. Property-based tests catch edge cases that example-based tests miss by definition.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 11: Cabal/Stack and dependency management
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Build system: Cabal with cabal.project, or Stack with stack.yaml — pick one and be consistent.

cabal conventions:
&lt;span class="p"&gt;-&lt;/span&gt; Pin GHC version in cabal.project: &lt;span class="sb"&gt;`with-compiler: ghc-9.6.x`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`cabal freeze`&lt;/span&gt; for reproducible builds
&lt;span class="p"&gt;-&lt;/span&gt; Separate library, executable, and test stanzas in .cabal file
&lt;span class="p"&gt;-&lt;/span&gt; Enable warnings in all stanzas: &lt;span class="sb"&gt;`-Wall -Wcompat -Widentities`&lt;/span&gt;

HLS (Haskell Language Server): configure in hie.yaml for IDE support.
Do NOT use &lt;span class="sb"&gt;`cabal install`&lt;/span&gt; globally — use &lt;span class="sb"&gt;`cabal run`&lt;/span&gt; or add to PATH via nix/ghcup.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rule 12: Deriving and Generic
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use &lt;span class="sb"&gt;`deriving`&lt;/span&gt; to avoid boilerplate:
  &lt;span class="sb"&gt;`deriving (Show, Eq, Ord, Generic)`&lt;/span&gt;

For JSON: use &lt;span class="sb"&gt;`aeson`&lt;/span&gt; with Generic derivation:
  &lt;span class="sb"&gt;`instance ToJSON User; instance FromJSON User`&lt;/span&gt; — no manual instances unless customizing.

For other typeclasses: prefer &lt;span class="sb"&gt;`deriving`&lt;/span&gt; via &lt;span class="sb"&gt;`GeneralizedNewtypeDeriving`&lt;/span&gt; or &lt;span class="sb"&gt;`DerivingVia`&lt;/span&gt;.

Do NOT write manual &lt;span class="sb"&gt;`Show`&lt;/span&gt; instances unless the output format is specifically required.
Do NOT write manual &lt;span class="sb"&gt;`Eq`&lt;/span&gt; instances unless the equality semantics differ from structural equality.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boilerplate &lt;code&gt;Show&lt;/code&gt;/&lt;code&gt;Eq&lt;/code&gt;/&lt;code&gt;Ord&lt;/code&gt; instances are noise. &lt;code&gt;deriving&lt;/code&gt; is the correct default. AI sometimes writes them manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 13: Haddock and documentation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Documentation:
&lt;span class="p"&gt;-&lt;/span&gt; All exported functions must have Haddock comments
&lt;span class="p"&gt;-&lt;/span&gt; Format: &lt;span class="sb"&gt;`-- | Description. Example: @functionName arg@`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Document the invariants and preconditions, not the implementation
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`@since`&lt;/span&gt; tags for API additions
&lt;span class="p"&gt;-&lt;/span&gt; Run &lt;span class="sb"&gt;`cabal haddock`&lt;/span&gt; to verify documentation builds

Module exports: explicit export lists on all modules — no &lt;span class="sb"&gt;`module Foo where`&lt;/span&gt; without an export list.
Explicit exports make the API surface clear and prevent accidental exports.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Your CLAUDE.md starting point
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Haskell Project — AI Coding Rules&lt;/span&gt;

&lt;span class="gu"&gt;## Safety&lt;/span&gt;
No partial functions: no head/tail/fromJust/read/error/undefined.
Use Maybe/Either/ExceptT for absence and failure. Total functions only.

&lt;span class="gu"&gt;## Types&lt;/span&gt;
Explicit type signatures on all top-level definitions.
newtype over type aliases for domain types.
Text not String. OverloadedStrings enabled.

&lt;span class="gu"&gt;## Style&lt;/span&gt;
Exhaustive pattern matching (-Wincomplete-patterns enforced).
LambdaCase for single-argument matches.
Pure functions maximized — IO only at edges.
ReaderT env IO as application monad.

&lt;span class="gu"&gt;## Abstractions&lt;/span&gt;
Use fmap/Applicative when Monad is not needed.
traverse for effects over traversables.
Point-free for simple compositions.

&lt;span class="gu"&gt;## Records&lt;/span&gt;
Prefix field names with type name. lens/optics for nested access.

&lt;span class="gu"&gt;## Testing&lt;/span&gt;
Hspec + QuickCheck. Properties: roundtrip, idempotence. Pure functions tested without IO.

&lt;span class="gu"&gt;## Build&lt;/span&gt;
GHC 9.6+. -Wall -Wcompat -Wincomplete-patterns. Explicit export lists on all modules.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Haskell especially needs this
&lt;/h2&gt;

&lt;p&gt;Haskell's type system can catch an enormous class of bugs at compile time — but only if you use it correctly. Partial functions, &lt;code&gt;String&lt;/code&gt; instead of &lt;code&gt;Text&lt;/code&gt;, and avoiding the abstraction hierarchy all leave power on the table.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is the difference between AI that uses Haskell and AI that uses Haskell well.&lt;/p&gt;

&lt;p&gt;The full rules pack across 15+ languages is at &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;gumroad&lt;/a&gt; — $27.&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>functional</category>
    </item>
    <item>
      <title>CLAUDE.md for Scala: 13 Rules That Make AI Write Idiomatic, Type-Safe Scala</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Thu, 14 May 2026 06:58:09 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-scala-13-rules-that-make-ai-write-idiomatic-type-safe-scala-1ddc</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-scala-13-rules-that-make-ai-write-idiomatic-type-safe-scala-1ddc</guid>
      <description>&lt;p&gt;Scala has a well-known problem: it can be written in many styles, from Java-with-semicolons to pure functional with category theory abstractions. AI assistants without explicit guidance default to the most common patterns in their training data — which is often Java-style Scala from older codebases.&lt;/p&gt;

&lt;p&gt;The result: mutable variables, null checks, raw &lt;code&gt;Future.map&lt;/code&gt; chains without proper error handling, and classes where case classes belong.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; file tells the AI which Scala you're writing. Here are the 13 rules that matter most.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: Scala version and style target
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Scala version: 3.x (Scala 3 syntax — no Scala 2 &lt;span class="sb"&gt;`implicit`&lt;/span&gt; keyword, use &lt;span class="sb"&gt;`given`&lt;/span&gt;/&lt;span class="sb"&gt;`using`&lt;/span&gt;).
Style: functional-first. Immutable by default. Effects tracked explicitly.
Framework: [Akka / ZIO / Cats Effect / Play — specify yours]
Build: sbt with explicit dependency versions pinned in build.sbt.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scala 3 changed the syntax for implicits, extension methods, and type classes significantly. AI trained on pre-Scala 3 material generates Scala 2 syntax. Make the version explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: Case classes for data — not plain classes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use case classes for all data transfer objects, domain models, and value objects:
  &lt;span class="sb"&gt;`case class User(id: UserId, name: String, email: Email)`&lt;/span&gt;

Benefits the AI must leverage:
&lt;span class="p"&gt;-&lt;/span&gt; Automatic &lt;span class="sb"&gt;`equals`&lt;/span&gt;, &lt;span class="sb"&gt;`hashCode`&lt;/span&gt;, &lt;span class="sb"&gt;`copy`&lt;/span&gt;, &lt;span class="sb"&gt;`toString`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Pattern matching support
&lt;span class="p"&gt;-&lt;/span&gt; Immutability by default
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`unapply`&lt;/span&gt; for destructuring

Use &lt;span class="sb"&gt;`@derive`&lt;/span&gt; (Scala 3) for additional typeclass derivation (Show, Codec, Schema).
Use &lt;span class="sb"&gt;`opaque type`&lt;/span&gt; for type-safe wrappers: &lt;span class="sb"&gt;`opaque type UserId = Long`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI often generates plain classes or Java-style beans for domain data. Case classes are the idiomatic Scala choice and unlock pattern matching throughout the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: Pattern matching — exhaustive and expressive
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use pattern matching as the primary dispatch mechanism:

  response match
    case Success(user) =&amp;gt; process(user)
    case Failure(e: NotFoundException) =&amp;gt; notFound(e.message)
    case Failure(e) =&amp;gt; internalError(e)

Rules:
&lt;span class="p"&gt;-&lt;/span&gt; Always handle all cases — enable &lt;span class="sb"&gt;`-Wnonexhaustive-match`&lt;/span&gt; compiler warning
&lt;span class="p"&gt;-&lt;/span&gt; Use guard clauses in patterns: &lt;span class="sb"&gt;`case user if user.active =&amp;gt; ...`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Destructure deeply: &lt;span class="sb"&gt;`case User(id, _, Email(addr)) =&amp;gt; ...`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Prefer &lt;span class="sb"&gt;`match`&lt;/span&gt; expressions that return values over &lt;span class="sb"&gt;`match`&lt;/span&gt; with side effects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pattern matching in Scala is more powerful than in most languages — AI underuses it. Exhaustive matching with compiler warnings is a major correctness tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 4: Option, Either, Try — no null, no exceptions for flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Error and absence handling:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Option[A]`&lt;/span&gt; for values that may be absent — never &lt;span class="sb"&gt;`null`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Either[Error, A]`&lt;/span&gt; for operations that can fail with a meaningful error
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Try[A]`&lt;/span&gt; only at the boundary with exception-throwing Java libraries
&lt;span class="p"&gt;-&lt;/span&gt; Never throw exceptions for business logic — use &lt;span class="sb"&gt;`Left(error)`&lt;/span&gt; or &lt;span class="sb"&gt;`Option.empty`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Chain with &lt;span class="sb"&gt;`map`&lt;/span&gt;, &lt;span class="sb"&gt;`flatMap`&lt;/span&gt;, &lt;span class="sb"&gt;`fold`&lt;/span&gt;, &lt;span class="sb"&gt;`getOrElse`&lt;/span&gt;, &lt;span class="sb"&gt;`recover`&lt;/span&gt;

For complex chains: use for-comprehensions over nested flatMap calls.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;null&lt;/code&gt; and exceptions as control flow are Java habits that leak into Scala. AI without guidance uses them. &lt;code&gt;Option&lt;/code&gt;/&lt;code&gt;Either&lt;/code&gt; with for-comprehensions is idiomatic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: For-comprehensions over nested flatMap
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use for-comprehensions for sequential monadic operations:

  for
    user    &amp;lt;- findUser(id)
    account &amp;lt;- findAccount(user.accountId)
    _       &amp;lt;- validateBalance(account, amount)
  yield Payment(user, account, amount)

Not:
  findUser(id).flatMap(user =&amp;gt;
    findAccount(user.accountId).flatMap(account =&amp;gt;
      validateBalance(account, amount).map(_ =&amp;gt; Payment(user, account, amount))
    )
  )

For-comprehensions work with any monad: Option, Either, Future, IO, ZIO.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For-comprehensions are syntactic sugar for &lt;code&gt;flatMap&lt;/code&gt;/&lt;code&gt;map&lt;/code&gt; chains. They're dramatically more readable for sequential operations. AI often generates nested lambda chains.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 6: Immutability — val over var, immutable collections
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Immutability rules:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`val`&lt;/span&gt; by default — &lt;span class="sb"&gt;`var`&lt;/span&gt; only when mutation is genuinely required and documented
&lt;span class="p"&gt;-&lt;/span&gt; Immutable collections: &lt;span class="sb"&gt;`List`&lt;/span&gt;, &lt;span class="sb"&gt;`Vector`&lt;/span&gt;, &lt;span class="sb"&gt;`Map`&lt;/span&gt;, &lt;span class="sb"&gt;`Set`&lt;/span&gt; from &lt;span class="sb"&gt;`scala.collection.immutable`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never import &lt;span class="sb"&gt;`scala.collection.mutable`&lt;/span&gt; without an explicit comment explaining why
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`copy`&lt;/span&gt; on case classes instead of mutating: &lt;span class="sb"&gt;`user.copy(name = "Alice")`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Prefer &lt;span class="sb"&gt;`foldLeft`&lt;/span&gt;/&lt;span class="sb"&gt;`foldRight`&lt;/span&gt; over accumulator variables

If you need a mutable cell: use &lt;span class="sb"&gt;`Ref`&lt;/span&gt; (ZIO), &lt;span class="sb"&gt;`IORef`&lt;/span&gt; (Cats Effect), or &lt;span class="sb"&gt;`AtomicReference`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mutable state is the source of most concurrency bugs. Scala's type system enables functional immutability — AI needs to be told to use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 7: Type classes — given/using, not implicit magic
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Scala 3 type class pattern:

  trait Show[A]:
    def show(a: A): String

  given Show[User] with
    def show(u: User) = s"User(${u.id}, ${u.name})"

  def display&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;A: Show&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;a:&lt;/span&gt; A): String = summon[Show[A]].show(a)

Rules:
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`given`&lt;/span&gt; for instances, &lt;span class="sb"&gt;`using`&lt;/span&gt; for parameters (not &lt;span class="sb"&gt;`implicit`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; Derive type class instances where possible: &lt;span class="sb"&gt;`derives Show, Encoder, Decoder`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Keep given instances in companion objects or dedicated &lt;span class="sb"&gt;`given`&lt;/span&gt; files
&lt;span class="p"&gt;-&lt;/span&gt; Never use &lt;span class="sb"&gt;`implicit`&lt;/span&gt; — Scala 2 syntax is banned in Scala 3 style
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implicit → given/using migration is one of the biggest Scala 2 → Scala 3 changes. AI trained on Scala 2 generates &lt;code&gt;implicit val&lt;/code&gt; and &lt;code&gt;implicit def&lt;/code&gt; everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 8: Collections — right tool for the operation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Collection selection:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`List`&lt;/span&gt;: prepend-heavy workloads, pattern matching, recursive algorithms
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Vector`&lt;/span&gt;: indexed access, append, large collections (O(log n) operations)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Map`&lt;/span&gt;/&lt;span class="sb"&gt;`Set`&lt;/span&gt;: lookups and membership tests
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`LazyList`&lt;/span&gt;: potentially infinite sequences, streaming data
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Array`&lt;/span&gt;: only when interoperating with Java libraries that require it

Operations:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`map`&lt;/span&gt;, &lt;span class="sb"&gt;`flatMap`&lt;/span&gt;, &lt;span class="sb"&gt;`filter`&lt;/span&gt;, &lt;span class="sb"&gt;`foldLeft`&lt;/span&gt; for transforms
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`groupBy`&lt;/span&gt;, &lt;span class="sb"&gt;`partition`&lt;/span&gt;, &lt;span class="sb"&gt;`span`&lt;/span&gt;, &lt;span class="sb"&gt;`takeWhile`&lt;/span&gt; for splitting
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`zipWithIndex`&lt;/span&gt; instead of index-based loops
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`traverse`&lt;/span&gt; (Cats) for &lt;span class="sb"&gt;`List[F[A]] =&amp;gt; F[List[A]]`&lt;/span&gt; (effects in collections)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI sometimes uses &lt;code&gt;Array&lt;/code&gt; for everything (Java habit) or &lt;code&gt;List&lt;/code&gt; where &lt;code&gt;Vector&lt;/code&gt; is more appropriate. The right collection type matters for performance and idiomatic code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 9: Futures and concurrency
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;If using Scala Futures:
&lt;span class="p"&gt;-&lt;/span&gt; Always provide an explicit &lt;span class="sb"&gt;`ExecutionContext`&lt;/span&gt; — never use &lt;span class="sb"&gt;`global`&lt;/span&gt; in production
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`Future.successful`&lt;/span&gt;, &lt;span class="sb"&gt;`Future.failed`&lt;/span&gt; for already-complete values
&lt;span class="p"&gt;-&lt;/span&gt; Chain with &lt;span class="sb"&gt;`map`&lt;/span&gt;, &lt;span class="sb"&gt;`flatMap`&lt;/span&gt;, &lt;span class="sb"&gt;`recover`&lt;/span&gt;, &lt;span class="sb"&gt;`recoverWith`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`Future.sequence`&lt;/span&gt; for parallel execution: &lt;span class="sb"&gt;`Future.sequence(List(f1, f2, f3))`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Set timeouts at the boundary — Future itself has no timeout

If using ZIO or Cats Effect (preferred for new code):
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`ZIO[R, E, A]`&lt;/span&gt; / &lt;span class="sb"&gt;`IO[E, A]`&lt;/span&gt; instead of Future
&lt;span class="p"&gt;-&lt;/span&gt; Effects are values — compose them without executing until the &lt;span class="sb"&gt;`main`&lt;/span&gt; method
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`ZIO.foreachPar`&lt;/span&gt; / &lt;span class="sb"&gt;`IO.parSequence`&lt;/span&gt; for parallel effects
&lt;span class="p"&gt;-&lt;/span&gt; Structured concurrency: &lt;span class="sb"&gt;`ZIO.scoped`&lt;/span&gt; / &lt;span class="sb"&gt;`Resource`&lt;/span&gt; for resource management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Futures with implicit execution contexts are a common AI default. Specify which concurrency model your project uses — ZIO and Cats Effect have very different conventions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 10: Testing — ScalaTest or MUnit with property-based testing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Testing:
&lt;span class="p"&gt;-&lt;/span&gt; Framework: ScalaTest (FlatSpec or FunSuite style) or MUnit
&lt;span class="p"&gt;-&lt;/span&gt; Property-based testing: ScalaCheck for invariant verification
&lt;span class="p"&gt;-&lt;/span&gt; Style: &lt;span class="sb"&gt;`"description" should "behavior"`&lt;/span&gt; (FlatSpec) or &lt;span class="sb"&gt;`test("description")`&lt;/span&gt; (MUnit/FunSuite)
&lt;span class="p"&gt;-&lt;/span&gt; Async tests: use &lt;span class="sb"&gt;`Future`&lt;/span&gt;-aware or &lt;span class="sb"&gt;`IO`&lt;/span&gt;-aware test runners
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`Thread.sleep`&lt;/span&gt; in tests — use async assertions or test schedulers
&lt;span class="p"&gt;-&lt;/span&gt; Fixture setup: &lt;span class="sb"&gt;`beforeEach`&lt;/span&gt;/&lt;span class="sb"&gt;`afterEach`&lt;/span&gt; or &lt;span class="sb"&gt;`fixture.test`&lt;/span&gt;

For ZIO: use &lt;span class="sb"&gt;`zio-test`&lt;/span&gt; with &lt;span class="sb"&gt;`ZIOSpecDefault`&lt;/span&gt;.
For Cats Effect: use &lt;span class="sb"&gt;`munit-cats-effect`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scala testing frameworks are diverse. Without specifying, AI may mix styles or generate blocking tests for async code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 11: Error types — sealed hierarchies, not strings
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Define errors as sealed traits:

  sealed trait AppError
  case class NotFound(id: String) extends AppError
  case class ValidationError(field: String, message: String) extends AppError
  case class DatabaseError(cause: Throwable) extends AppError

Use these as the Left side of Either[AppError, A].
Pattern match on them exhaustively — the compiler will warn if a case is missing.
Never use &lt;span class="sb"&gt;`String`&lt;/span&gt; or &lt;span class="sb"&gt;`Exception`&lt;/span&gt; as an error type in business logic.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;String errors are untyped and non-exhaustive. Sealed trait hierarchies give compile-time completeness checking and make error handling explicit and safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 12: sbt and dependency hygiene
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;sbt conventions:
&lt;span class="p"&gt;-&lt;/span&gt; Pin all dependency versions explicitly in &lt;span class="sb"&gt;`build.sbt`&lt;/span&gt; — no &lt;span class="sb"&gt;`latest.release`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`dependencyOverrides`&lt;/span&gt; for transitive version conflicts
&lt;span class="p"&gt;-&lt;/span&gt; Organize: &lt;span class="sb"&gt;`libraryDependencies`&lt;/span&gt; grouped by: compile / test / provided
&lt;span class="p"&gt;-&lt;/span&gt; Enable compiler flags: &lt;span class="sb"&gt;`-Wunused`&lt;/span&gt;, &lt;span class="sb"&gt;`-Wvalue-discard`&lt;/span&gt;, &lt;span class="sb"&gt;`-Xfatal-warnings`&lt;/span&gt; in CI
&lt;span class="p"&gt;-&lt;/span&gt; Separate modules in a multi-project build for clean dependency boundaries
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`scalafmt`&lt;/span&gt; for formatting — config in &lt;span class="sb"&gt;`.scalafmt.conf`&lt;/span&gt;, enforced in CI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI sometimes generates &lt;code&gt;%%&lt;/code&gt; version ranges or omits important compiler flags. Strict compiler settings catch bugs before runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 13: Logging with structured context
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Logging:
&lt;span class="p"&gt;-&lt;/span&gt; Use SLF4J + Logback (or ZIO Logging / log4cats for effect systems)
&lt;span class="p"&gt;-&lt;/span&gt; Structured logging preferred: JSON output in production
&lt;span class="p"&gt;-&lt;/span&gt; Every log call includes context: &lt;span class="sb"&gt;`logger.info("Payment processed", Map("userId" -&amp;gt; userId, "amount" -&amp;gt; amount))`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Log levels: trace/debug (dev), info (normal), warn (degraded), error (failure)
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`println`&lt;/span&gt; or &lt;span class="sb"&gt;`System.out.println`&lt;/span&gt; in production code
&lt;span class="p"&gt;-&lt;/span&gt; Correlation IDs: propagate via MDC (SLF4J) or ZIO FiberRef / Cats Effect IOLocal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Your CLAUDE.md starting point
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Scala Project — AI Coding Rules&lt;/span&gt;

&lt;span class="gu"&gt;## Version&lt;/span&gt;
Scala 3.x. No implicit keyword — use given/using.

&lt;span class="gu"&gt;## Data&lt;/span&gt;
Case classes for domain models. opaque type for type-safe wrappers.
Immutable by default. val over var. copy() over mutation.

&lt;span class="gu"&gt;## Error Handling&lt;/span&gt;
Option for absence. Either[AppError, A] for fallible operations. No null. No exceptions for business logic.
Sealed trait hierarchies for error types — exhaustive pattern matching enforced.

&lt;span class="gu"&gt;## Style&lt;/span&gt;
For-comprehensions over nested flatMap. Pattern matching as primary dispatch.
Immutable collections: List/Vector/Map/Set.

&lt;span class="gu"&gt;## Effects&lt;/span&gt;
[ZIO / Cats Effect / Future — specify]. Effects are values. No Thread.sleep. Structured concurrency.

&lt;span class="gu"&gt;## Testing&lt;/span&gt;
[ScalaTest FunSuite / MUnit / zio-test]. Property-based with ScalaCheck. Async-aware.

&lt;span class="gu"&gt;## Build&lt;/span&gt;
sbt. Pinned dependency versions. scalafmt enforced. -Wunused -Xfatal-warnings in CI.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The gap this closes
&lt;/h2&gt;

&lt;p&gt;The same Scala code compiles whether it's idiomatic or not. The difference between Java-in-Scala and real Scala shows up in maintainability, performance characteristics, and how well the compiler catches bugs.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is what makes AI output land on the right side of that line — consistently, across sessions and team members.&lt;/p&gt;

&lt;p&gt;The full rules pack across 14+ languages is at &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;gumroad&lt;/a&gt; — $27.&lt;/p&gt;

</description>
      <category>scala</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>functional</category>
    </item>
    <item>
      <title>CLAUDE.md for Elixir: 13 Rules That Make AI Write Idiomatic, OTP-Aware Elixir</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Wed, 13 May 2026 08:57:49 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-elixir-13-rules-that-make-ai-write-idiomatic-otp-aware-elixir-2j9k</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-elixir-13-rules-that-make-ai-write-idiomatic-otp-aware-elixir-2j9k</guid>
      <description>&lt;p&gt;Elixir has a sharper gap between working code and idiomatic code than most languages. AI assistants can write Elixir that compiles and passes tests — but misses the OTP patterns, function head dispatch, supervision trees, and pipe conventions that experienced Elixir developers use as a matter of course.&lt;/p&gt;

&lt;p&gt;The result: code that works in dev, breaks under load, and doesn't behave like the Elixir ecosystem expects.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; at your project root closes this gap. Here are the 13 rules with the highest impact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: OTP first — processes, not objects
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;This is an OTP application. Model concurrent behavior with:
&lt;span class="p"&gt;-&lt;/span&gt; GenServer for stateful processes
&lt;span class="p"&gt;-&lt;/span&gt; Supervisor for fault tolerance and restart strategies
&lt;span class="p"&gt;-&lt;/span&gt; Task for one-off concurrent work
&lt;span class="p"&gt;-&lt;/span&gt; Agent for simple shared state (prefer GenServer for anything non-trivial)

Do NOT model concurrency with shared mutable state, mutexes, or global variables.
The process is the unit of concurrency and isolation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elixir's concurrency model is built on the Actor model via OTP. AI without this context tends to reach for abstractions from other languages. GenServer + Supervisor is how Elixir systems are built — this needs to be explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: Function head dispatch — not cond or case at the top
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use pattern matching in function heads for branching:

  def process(%{status: :active} = user), do: ...
  def process(%{status: :inactive} = user), do: ...
  def process(%{status: status}), do: {:error, "unknown status: #{status}"}

Not:
  def process(user) do
    cond do
      user.status == :active -&amp;gt; ...
      user.status == :inactive -&amp;gt; ...
    end
  end

Reserve &lt;span class="sb"&gt;`case`&lt;/span&gt; for local branching within a function. Reserve &lt;span class="sb"&gt;`cond`&lt;/span&gt; for boolean expressions without a matching value.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the most common idiom gap. AI defaults to &lt;code&gt;cond&lt;/code&gt; or &lt;code&gt;case&lt;/code&gt; at the function level when function head dispatch is cleaner and more idiomatic. Elixir developers read function head dispatch as the primary dispatch mechanism.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: Pipe operator discipline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Pipe rules:
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`|&amp;gt;`&lt;/span&gt; when transforming data through 3+ steps
&lt;span class="p"&gt;-&lt;/span&gt; Each pipe step should be a single operation (avoid anonymous functions in pipes unless necessary)
&lt;span class="p"&gt;-&lt;/span&gt; The first argument of every piped function must be the data being transformed
&lt;span class="p"&gt;-&lt;/span&gt; Don't pipe into &lt;span class="sb"&gt;`IO.inspect/2`&lt;/span&gt; in production code — use Logger
&lt;span class="p"&gt;-&lt;/span&gt; Break long pipes into named intermediate values when readability suffers

Avoid:
  result = transform(filter(validate(data)))  # nest style

Prefer:
  result = data
    |&amp;gt; validate()
    |&amp;gt; filter()
    |&amp;gt; transform()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipe operator is central to Elixir's readability. AI sometimes generates nested function calls or breaks the single-data-flow rule by adding extra arguments mid-pipe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 4: Pattern matching on with — not nested case
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use &lt;span class="sb"&gt;`with`&lt;/span&gt; for sequential operations where any step can fail:

  with {:ok, user} &amp;lt;- fetch_user(id),
       {:ok, account} &amp;lt;- fetch_account(user),
       {:ok, _} &amp;lt;- charge(account, amount) do
    {:ok, "charged"}
  else
    {:error, :not_found} -&amp;gt; {:error, "user not found"}
    {:error, reason} -&amp;gt; {:error, reason}
  end

Not nested case/if chains.
Return &lt;span class="sb"&gt;`{:ok, value}`&lt;/span&gt; and &lt;span class="sb"&gt;`{:error, reason}`&lt;/span&gt; tuples from all functions that can fail.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;with&lt;/code&gt; is the idiomatic way to chain fallible operations. Nested &lt;code&gt;case&lt;/code&gt; blocks grow quickly and are hard to extend. AI often generates nested &lt;code&gt;case&lt;/code&gt; because it's the more universally recognizable pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: Supervision trees — crash and restart, don't defend
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Supervision strategy:
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`:one_for_one`&lt;/span&gt; for independent workers
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`:one_for_all`&lt;/span&gt; when workers have shared state that becomes inconsistent on crash
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`:rest_for_one`&lt;/span&gt; when workers have ordered dependencies
&lt;span class="p"&gt;-&lt;/span&gt; Start supervised processes in application.ex or a dedicated Supervisor module
&lt;span class="p"&gt;-&lt;/span&gt; Do NOT rescue exceptions in GenServer callbacks to avoid crashes — let it crash, let the supervisor restart

The supervisor IS the error handler. Writing defensive rescue clauses in worker processes fights the design.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Let it crash" is a core Elixir/Erlang philosophy that AI without guidance will work against. AI tends to add &lt;code&gt;rescue&lt;/code&gt; blocks everywhere. The OTP pattern is to let processes crash and recover via supervision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 6: Ecto — changesets and queries
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Database access: Ecto only.
&lt;span class="p"&gt;-&lt;/span&gt; All data validation in changesets: &lt;span class="sb"&gt;`cast`&lt;/span&gt;, &lt;span class="sb"&gt;`validate_required`&lt;/span&gt;, &lt;span class="sb"&gt;`validate_format`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Queries built with Ecto.Query DSL — no raw SQL except for complex queries using &lt;span class="sb"&gt;`fragment/1`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`Repo.get!`&lt;/span&gt; in business logic — use &lt;span class="sb"&gt;`Repo.get`&lt;/span&gt; and handle nil explicitly
&lt;span class="p"&gt;-&lt;/span&gt; Preload associations explicitly: &lt;span class="sb"&gt;`Repo.preload(user, [:posts])`&lt;/span&gt; — no lazy loading
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`Repo.transaction`&lt;/span&gt; for operations that must be atomic
&lt;span class="p"&gt;-&lt;/span&gt; Schema changesets are the single validation point — not controllers or context functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ecto's design is opinionated. AI without guidance uses &lt;code&gt;get!&lt;/code&gt; everywhere (raising on nil), forgets preloads (causing N+1-equivalent issues), and puts validation in the wrong layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 7: Contexts — bounded modules, not one schema one module
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Organize with Phoenix contexts (even outside Phoenix):
&lt;span class="p"&gt;-&lt;/span&gt; One context module per domain: &lt;span class="sb"&gt;`Accounts`&lt;/span&gt;, &lt;span class="sb"&gt;`Payments`&lt;/span&gt;, &lt;span class="sb"&gt;`Inventory`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Context functions are the public API: &lt;span class="sb"&gt;`Accounts.get_user(id)`&lt;/span&gt;, &lt;span class="sb"&gt;`Accounts.create_user(attrs)`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Schema modules are private to contexts — not called directly from controllers or other contexts
&lt;span class="p"&gt;-&lt;/span&gt; No cross-context direct schema access — only through the owning context's API

This is the Phoenix 1.3+ architecture. Do not use the older one-schema-one-controller pattern.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Phoenix Contexts were introduced specifically to solve the "fat model" problem. AI trained on older Phoenix material generates the pre-context pattern. Specify the version explicitly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 8: Atoms — safe usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Atom discipline:
&lt;span class="p"&gt;-&lt;/span&gt; Never convert user-provided strings to atoms with &lt;span class="sb"&gt;`String.to_atom/1`&lt;/span&gt;
  (atoms are not garbage collected — unlimited creation = memory leak / DoS vector)
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`String.to_existing_atom/1`&lt;/span&gt; when converting known strings to atoms
&lt;span class="p"&gt;-&lt;/span&gt; Module names, function names, map keys in your own code: atoms are fine
&lt;span class="p"&gt;-&lt;/span&gt; JSON parsing: use string keys by default, not atom keys (Jason default is correct)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;String.to_atom/1&lt;/code&gt; with user input is a security and stability vulnerability unique to Erlang/Elixir. AI generates it without this context. This rule needs to be explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 9: Message passing — send, receive, and GenServer calls
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Process communication:
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`GenServer.call/3`&lt;/span&gt; for synchronous requests (returns a value)
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`GenServer.cast/2`&lt;/span&gt; for asynchronous fire-and-forget (no return)
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`send/2`&lt;/span&gt; + &lt;span class="sb"&gt;`receive`&lt;/span&gt; only for ad-hoc message passing not managed by OTP
&lt;span class="p"&gt;-&lt;/span&gt; Always set timeouts on &lt;span class="sb"&gt;`call`&lt;/span&gt;: &lt;span class="sb"&gt;`GenServer.call(pid, msg, 5_000)`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Handle unexpected messages in &lt;span class="sb"&gt;`handle_info/2`&lt;/span&gt; — don't let them accumulate in the mailbox

Never use &lt;span class="sb"&gt;`Process.sleep/1`&lt;/span&gt; for coordination — use &lt;span class="sb"&gt;`GenServer.call`&lt;/span&gt; or &lt;span class="sb"&gt;`Task.async/await`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Process communication patterns are Elixir-specific. AI without guidance uses &lt;code&gt;send/receive&lt;/code&gt; where GenServer is correct, or forgets to handle &lt;code&gt;handle_info&lt;/code&gt; for system messages.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 10: Testing with ExUnit — async and isolation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Testing:
&lt;span class="p"&gt;-&lt;/span&gt; All tests: &lt;span class="sb"&gt;`use ExUnit.Case, async: true`&lt;/span&gt; unless they share global state (database)
&lt;span class="p"&gt;-&lt;/span&gt; Database tests: use &lt;span class="sb"&gt;`Ecto.Adapters.SQL.Sandbox`&lt;/span&gt; for transaction isolation
&lt;span class="p"&gt;-&lt;/span&gt; Test setup: &lt;span class="sb"&gt;`setup`&lt;/span&gt; blocks, not module attributes
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`assert {:ok, _} = MyModule.function(args)`&lt;/span&gt; — match on the structure
&lt;span class="p"&gt;-&lt;/span&gt; Factory: ExMachina for test data, not hand-built maps
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`Process.sleep`&lt;/span&gt; in tests — use &lt;span class="sb"&gt;`assert_receive`&lt;/span&gt; with a timeout for async assertions
&lt;span class="p"&gt;-&lt;/span&gt; Mock external services with &lt;span class="sb"&gt;`Mox`&lt;/span&gt; — define behaviors and verify expectations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elixir's async testing and Ecto sandbox require specific setup. AI without guidance creates sequential tests that run slowly or break isolation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 11: Telemetry and structured logging
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Observability:
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`:telemetry`&lt;/span&gt; for emitting metrics from library/application code
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`Logger.metadata/1`&lt;/span&gt; to attach context to log entries: &lt;span class="sb"&gt;`Logger.metadata(user_id: id, request_id: req_id)`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Log levels: debug (verbose dev), info (normal ops), warning (degraded), error (failure)
&lt;span class="p"&gt;-&lt;/span&gt; No bare &lt;span class="sb"&gt;`IO.puts`&lt;/span&gt; or &lt;span class="sb"&gt;`IO.inspect`&lt;/span&gt; in production code — use Logger
&lt;span class="p"&gt;-&lt;/span&gt; Attach telemetry handlers in application startup, not inline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Telemetry is the standard instrumentation library in the Elixir ecosystem. AI often uses &lt;code&gt;IO.inspect&lt;/code&gt; for debugging without switching to Logger for production code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 12: Structs over bare maps for domain data
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Domain data:
&lt;span class="p"&gt;-&lt;/span&gt; Define structs for all domain entities: &lt;span class="sb"&gt;`defstruct [:id, :name, :email]`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`@enforce_keys`&lt;/span&gt; for required fields: &lt;span class="sb"&gt;`@enforce_keys [:id, :name]`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Structs provide compile-time key checking — bare maps do not
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`%User{}`&lt;/span&gt; not &lt;span class="sb"&gt;`%{id: ..., name: ...}`&lt;/span&gt; when the shape is known
&lt;span class="p"&gt;-&lt;/span&gt; Typespec every public function: &lt;span class="sb"&gt;`@spec create_user(attrs :: map()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Structs with typespecs make dialyzer useful. AI often generates bare maps for domain data, losing the documentation and static analysis benefits.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 13: Mix tasks and releases
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Deployment:
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`mix release`&lt;/span&gt; for production deployments — not &lt;span class="sb"&gt;`mix run`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Config: &lt;span class="sb"&gt;`config/runtime.exs`&lt;/span&gt; for runtime configuration (env vars read at startup)
  &lt;span class="sb"&gt;`config/config.exs`&lt;/span&gt; for compile-time only
&lt;span class="p"&gt;-&lt;/span&gt; Never hardcode secrets — use &lt;span class="sb"&gt;`System.fetch_env!/1`&lt;/span&gt; in &lt;span class="sb"&gt;`config/runtime.exs`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Health checks: implement a simple HTTP endpoint, not a Mix task that shells out
&lt;span class="p"&gt;-&lt;/span&gt; Migrations: always run with &lt;span class="sb"&gt;`--no-start`&lt;/span&gt; in CI: &lt;span class="sb"&gt;`mix ecto.migrate --no-start`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Release configuration is a common stumbling block. AI often suggests &lt;code&gt;config/config.exs&lt;/code&gt; for runtime values, which means env vars are read at compile time and baked into the release — not what you want in Docker deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your CLAUDE.md starting point
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Elixir Project — AI Coding Rules&lt;/span&gt;

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
OTP application. GenServer + Supervisor for concurrency. Let it crash — supervisors handle recovery.
Contexts for domain boundaries. Schema modules private to their context.

&lt;span class="gu"&gt;## Patterns&lt;/span&gt;
Function head dispatch over cond/case at function level.
with for sequential fallible operations — {:ok, val} / {:error, reason} tuples everywhere.
Pipe operator for 3+ step data transforms.

&lt;span class="gu"&gt;## Ecto&lt;/span&gt;
Changesets for all validation. Explicit preloads. Repo.get not Repo.get!.
Raw SQL only via fragment/1. Transactions for atomic operations.

&lt;span class="gu"&gt;## Atoms&lt;/span&gt;
Never String.to_atom/1 on user input. String.to_existing_atom/1 for known strings only.

&lt;span class="gu"&gt;## Testing&lt;/span&gt;
async: true where possible. Ecto.Adapters.SQL.Sandbox for DB tests.
Mox for external service mocks. assert_receive for async. No Process.sleep in tests.

&lt;span class="gu"&gt;## Observability&lt;/span&gt;
Logger with metadata. Telemetry for metrics. No IO.puts/IO.inspect in production.

&lt;span class="gu"&gt;## Deployment&lt;/span&gt;
mix release. runtime.exs for env vars. System.fetch_env!/1 for secrets.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Elixir especially needs this
&lt;/h2&gt;

&lt;p&gt;Elixir has unusually strong conventions — OTP patterns, the pipe operator, &lt;code&gt;with&lt;/code&gt;, contexts — that diverge significantly from how imperative languages solve the same problems. AI trained on a broad corpus defaults to the more common patterns.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is how you tell the AI which decade and which paradigm it's working in.&lt;/p&gt;

&lt;p&gt;The full rules pack across 13+ languages is at &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;gumroad&lt;/a&gt; — $27.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>functional</category>
    </item>
    <item>
      <title>CLAUDE.md for Ruby: 13 Rules That Make AI Write Idiomatic, Production-Ready Ruby</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Tue, 12 May 2026 06:58:41 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-ruby-13-rules-that-make-ai-write-idiomatic-production-ready-ruby-3gj5</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-ruby-13-rules-that-make-ai-write-idiomatic-production-ready-ruby-3gj5</guid>
      <description>&lt;p&gt;Ruby has a distinct culture around idiomatic code. The language is designed so that well-written Ruby reads almost like English — concise, expressive, and elegant. AI-generated Ruby often misses this: the code works, but it reads like Ruby written by someone who learned Python first.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; file at your repo root tells your AI assistant what "good Ruby" looks like for your project. Here are 13 rules that matter most.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: Ruby version and runtime environment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Ruby version: 3.3+. Use YJIT in production (&lt;span class="sb"&gt;`RUBY_YJIT_ENABLE=1`&lt;/span&gt; or &lt;span class="sb"&gt;`--yjit`&lt;/span&gt;).
Bundler manages all gems — no manual &lt;span class="sb"&gt;`require`&lt;/span&gt; for anything in the Gemfile.
&lt;span class="sb"&gt;`.ruby-version`&lt;/span&gt; file is authoritative for local development.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby 3.x has significant performance improvements and new syntax. AI often generates code compatible with Ruby 2.x. Specifying the version prevents outdated patterns like &lt;code&gt;proc { }&lt;/code&gt; where &lt;code&gt;-&amp;gt; {}&lt;/code&gt; (lambda) is cleaner, or missing pattern matching syntax available since 3.0.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: Idiomatic conditionals — trailing and ternary
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Prefer trailing conditionals for single-line guards:
  &lt;span class="sb"&gt;`return if invalid?`&lt;/span&gt; not &lt;span class="sb"&gt;`if invalid? then return end`&lt;/span&gt;
  &lt;span class="sb"&gt;`notify! unless silent?`&lt;/span&gt; not &lt;span class="sb"&gt;`if !silent? then notify! end`&lt;/span&gt;

Use ternary only for simple value selection:
  &lt;span class="sb"&gt;`status = active? ? :online : :offline`&lt;/span&gt;

Avoid nested ternaries — extract to a method instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the most visible Ruby idiom gap in AI output. Ruby developers read &lt;code&gt;return if condition&lt;/code&gt; as a natural guard clause. Multi-line &lt;code&gt;if/end&lt;/code&gt; for single conditions is considered verbose and non-idiomatic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: Symbols over strings for keys
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Hash keys: use symbols for internal data structures.
  &lt;span class="sb"&gt;`{ name: "Alice", role: :admin }`&lt;/span&gt; not &lt;span class="sb"&gt;`{ "name" =&amp;gt; "Alice", "role" =&amp;gt; "admin" }`&lt;/span&gt;

String keys only when the key comes from external input (JSON parsing, HTTP params) or when
the key must be dynamic.

Use &lt;span class="sb"&gt;`Hash#fetch`&lt;/span&gt; with a default or block when the key might be absent:
  &lt;span class="sb"&gt;`config.fetch(:timeout, 30)`&lt;/span&gt; not &lt;span class="sb"&gt;`config[:timeout] || 30`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Symbols are immutable and memory-efficient. The &lt;code&gt;||&lt;/code&gt; fallback for missing keys is a common bug when the value can legitimately be &lt;code&gt;false&lt;/code&gt; or &lt;code&gt;nil&lt;/code&gt;. &lt;code&gt;fetch&lt;/code&gt; is the correct idiom.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 4: Blocks, procs, and lambdas — use the right tool
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Blocks: for one-off iteration and DSL construction (&lt;span class="sb"&gt;`each`&lt;/span&gt;, &lt;span class="sb"&gt;`map`&lt;/span&gt;, &lt;span class="sb"&gt;`tap`&lt;/span&gt;, &lt;span class="sb"&gt;`yield`&lt;/span&gt;).
Lambdas (&lt;span class="sb"&gt;`-&amp;gt; {}`&lt;/span&gt;): when you need to store callable behavior, check arity, or return from within.
Procs (&lt;span class="sb"&gt;`proc {}`&lt;/span&gt;): rarely — only when you explicitly need loose arity behavior.

Prefer &lt;span class="sb"&gt;`&amp;amp;method(:name)`&lt;/span&gt; over a block that just calls a method:
  &lt;span class="sb"&gt;`users.map(&amp;amp;method(:transform))`&lt;/span&gt; not &lt;span class="sb"&gt;`users.map { |u| transform(u) }`&lt;/span&gt;

Use &lt;span class="sb"&gt;`Symbol#to_proc`&lt;/span&gt; for simple field extraction:
  &lt;span class="sb"&gt;`names.map(&amp;amp;:upcase)`&lt;/span&gt; not &lt;span class="sb"&gt;`names.map { |n| n.upcase }`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI frequently generates verbose blocks where &lt;code&gt;&amp;amp;:method_name&lt;/code&gt; or &lt;code&gt;&amp;amp;method(:name)&lt;/code&gt; is cleaner. These patterns are central to Ruby's functional style.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: Enumerable over manual loops
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use Enumerable methods instead of index-based loops:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`map`&lt;/span&gt; / &lt;span class="sb"&gt;`flat_map`&lt;/span&gt; for transformations
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`select`&lt;/span&gt; / &lt;span class="sb"&gt;`reject`&lt;/span&gt; for filtering
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`reduce`&lt;/span&gt; / &lt;span class="sb"&gt;`each_with_object`&lt;/span&gt; for aggregation
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`find`&lt;/span&gt; for first match
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`group_by`&lt;/span&gt;, &lt;span class="sb"&gt;`tally`&lt;/span&gt;, &lt;span class="sb"&gt;`chunk_while`&lt;/span&gt; for grouping
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`any?`&lt;/span&gt;, &lt;span class="sb"&gt;`all?`&lt;/span&gt;, &lt;span class="sb"&gt;`none?`&lt;/span&gt;, &lt;span class="sb"&gt;`count`&lt;/span&gt; for predicates

Never use &lt;span class="sb"&gt;`for`&lt;/span&gt; loops — use &lt;span class="sb"&gt;`each`&lt;/span&gt;.
Avoid &lt;span class="sb"&gt;`inject(:+)`&lt;/span&gt; when &lt;span class="sb"&gt;`sum`&lt;/span&gt; is available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby's Enumerable module is one of its greatest strengths. AI sometimes generates C-style index loops or nested conditionals where a chain of Enumerable methods is cleaner and more idiomatic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 6: Method visibility and attr_* macros
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use &lt;span class="sb"&gt;`attr_reader`&lt;/span&gt;, &lt;span class="sb"&gt;`attr_writer`&lt;/span&gt;, &lt;span class="sb"&gt;`attr_accessor`&lt;/span&gt; instead of manual getter/setter methods.
Mark methods private when they're implementation details — default to private, not public.
Use &lt;span class="sb"&gt;`protected`&lt;/span&gt; only for methods called by instances of the same class (rare).

Order in class body: class methods → initialize → public instance methods → private methods.
Separate sections with &lt;span class="sb"&gt;`private`&lt;/span&gt; keyword, not comments.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI often generates explicit &lt;code&gt;def name; @name; end&lt;/code&gt; getters. &lt;code&gt;attr_reader :name&lt;/code&gt; is the Ruby convention. Correct visibility also matters — AI tends to leave everything public.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 7: Error handling — rescue, not rescue Exception
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Error handling:
&lt;span class="p"&gt;-&lt;/span&gt; Rescue &lt;span class="sb"&gt;`StandardError`&lt;/span&gt; or specific subclasses — never &lt;span class="sb"&gt;`Exception`&lt;/span&gt;
  (&lt;span class="sb"&gt;`Exception`&lt;/span&gt; catches &lt;span class="sb"&gt;`SignalException`&lt;/span&gt;, &lt;span class="sb"&gt;`Interrupt`&lt;/span&gt;, &lt;span class="sb"&gt;`NoMemoryError`&lt;/span&gt; — almost always wrong)
&lt;span class="p"&gt;-&lt;/span&gt; Prefer inline rescue for simple defaults: &lt;span class="sb"&gt;`value = risky_call rescue default_value`&lt;/span&gt;
  (only when the rescue is genuinely a simple fallback, not for flow control)
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`ensure`&lt;/span&gt; for cleanup, not rescue blocks
&lt;span class="p"&gt;-&lt;/span&gt; Custom exceptions: inherit from &lt;span class="sb"&gt;`StandardError`&lt;/span&gt;, name with &lt;span class="sb"&gt;`Error`&lt;/span&gt; suffix
  &lt;span class="sb"&gt;`class PaymentFailedError &amp;lt; StandardError; end`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Raise with a message: &lt;span class="sb"&gt;`raise PaymentFailedError, "Card declined: #{reason}"`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rescue Exception&lt;/code&gt; is a common AI mistake that catches signals and system errors, preventing clean process shutdown. Always rescue specific errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 8: Frozen string literals
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Add &lt;span class="sb"&gt;`# frozen_string_literal: true`&lt;/span&gt; at the top of every Ruby file.
This makes all string literals immutable and reduces object allocation.

When a mutable string is genuinely needed: &lt;span class="sb"&gt;`String.new("mutable")`&lt;/span&gt; or &lt;span class="sb"&gt;`+"mutable"`&lt;/span&gt;.
Use string interpolation (&lt;span class="sb"&gt;`"#{var}"`&lt;/span&gt;) over concatenation (&lt;span class="sb"&gt;`str + other`&lt;/span&gt;).
Prefer &lt;span class="sb"&gt;`&amp;lt;&amp;lt;`&lt;/span&gt; (shovel) for in-place string building only when mutation is intentional and documented.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frozen string literals are a free performance win and a convention in modern Ruby gems. AI omits this unless specified.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 9: Rails conventions (if using Rails)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;If this is a Rails project:
&lt;span class="p"&gt;
-&lt;/span&gt; Fat models, thin controllers — business logic belongs in models or service objects
&lt;span class="p"&gt;-&lt;/span&gt; Service objects in &lt;span class="sb"&gt;`app/services/`&lt;/span&gt; for operations that cross model boundaries
&lt;span class="p"&gt;-&lt;/span&gt; Scopes over class methods for chainable queries: &lt;span class="sb"&gt;`scope :active, -&amp;gt; { where(active: true) }`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`find_each`&lt;/span&gt; / &lt;span class="sb"&gt;`in_batches`&lt;/span&gt; for large dataset iteration — never &lt;span class="sb"&gt;`.all.each`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Avoid N+1: use &lt;span class="sb"&gt;`includes`&lt;/span&gt;, &lt;span class="sb"&gt;`eager_load`&lt;/span&gt;, or &lt;span class="sb"&gt;`preload`&lt;/span&gt; — add Bullet gem to detect
&lt;span class="p"&gt;-&lt;/span&gt; Strong parameters in controllers, not models
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`before_action`&lt;/span&gt; for authentication/authorization, not inline checks
&lt;span class="p"&gt;-&lt;/span&gt; No raw SQL — use Arel or query interface; raw SQL only with &lt;span class="sb"&gt;`sanitize_sql`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rails conventions are dense. Without explicit guidance, AI generates controllers with business logic, models with SQL injection risk, and queries that cause N+1 problems at scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 10: Testing — RSpec conventions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Testing: RSpec with FactoryBot for fixtures.
&lt;span class="p"&gt;
-&lt;/span&gt; Describe behavior, not implementation: &lt;span class="sb"&gt;`describe '#charge'`&lt;/span&gt; not &lt;span class="sb"&gt;`describe 'PaymentsController line 42'`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`let`&lt;/span&gt; and &lt;span class="sb"&gt;`let!`&lt;/span&gt; for setup — not instance variables in &lt;span class="sb"&gt;`before`&lt;/span&gt; blocks
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`subject`&lt;/span&gt; for the object under test
&lt;span class="p"&gt;-&lt;/span&gt; One assertion per example (generally) — exceptions: related state changes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`context`&lt;/span&gt; blocks for branching: &lt;span class="sb"&gt;`context 'when payment fails'`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Shared examples for common behavior across multiple classes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`described_class`&lt;/span&gt; over hardcoded class names in specs
&lt;span class="p"&gt;-&lt;/span&gt; VCR or WebMock for external HTTP — never hit real APIs in tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI generates verbose, procedural RSpec that doesn't use &lt;code&gt;let&lt;/code&gt;, nests &lt;code&gt;before&lt;/code&gt; blocks unnecessarily, and creates brittle specs tied to implementation details.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 11: Dependency injection and module composition
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Prefer module mixins over inheritance for shared behavior:
  &lt;span class="sb"&gt;`include Auditable`&lt;/span&gt; not &lt;span class="sb"&gt;`class User &amp;lt; AuditableBase`&lt;/span&gt;

Use dependency injection for external services:
  &lt;span class="sb"&gt;`def initialize(mailer: UserMailer)`&lt;/span&gt; not &lt;span class="sb"&gt;`UserMailer.deliver`&lt;/span&gt; inside methods

Avoid monkey-patching core classes — use refinements (&lt;span class="sb"&gt;`refine String do`&lt;/span&gt;) when extension is necessary.
No &lt;span class="sb"&gt;`method_missing`&lt;/span&gt; without &lt;span class="sb"&gt;`respond_to_missing?`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby's module system is powerful. AI tends toward inheritance hierarchies where mixins are more flexible. Dependency injection makes testing trivial — pass a mock mailer in tests, real mailer in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 12: Pattern matching (Ruby 3.x)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use pattern matching for complex conditional dispatch:

  case response
  in { status: 200, body: { user: { id: Integer =&amp;gt; id } } }
    process_user(id)
  in { status: 422, errors: [&lt;span class="ge"&gt;*, String =&amp;gt; first_error, *&lt;/span&gt;] }
    handle_validation_error(first_error)
  in { status: (400..499) }
    handle_client_error(response)
  end

Use &lt;span class="sb"&gt;`in`&lt;/span&gt; (one-armed pattern match) for destructuring:
  &lt;span class="sb"&gt;`response =&amp;gt; { user: { name:, email: } }`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby 3.x pattern matching is underutilized in AI output because it's relatively new. It eliminates chains of &lt;code&gt;if response[:status] == 200 &amp;amp;&amp;amp; response[:body][:user]&lt;/code&gt; conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 13: Gemfile and dependency hygiene
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Gemfile conventions:
&lt;span class="p"&gt;-&lt;/span&gt; Pin major versions for stability: &lt;span class="sb"&gt;`gem 'rails', '~&amp;gt; 7.1'`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Group gems correctly: &lt;span class="sb"&gt;`:development`&lt;/span&gt;, &lt;span class="sb"&gt;`:test`&lt;/span&gt;, &lt;span class="sb"&gt;`:development, :test`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`bundle audit`&lt;/span&gt; before any update — check for security advisories
&lt;span class="p"&gt;-&lt;/span&gt; No gems with &lt;span class="sb"&gt;`require: false`&lt;/span&gt; unless you manually require them
&lt;span class="p"&gt;-&lt;/span&gt; Prefer gems with active maintenance — check last commit date on GitHub before adding
&lt;span class="p"&gt;-&lt;/span&gt; Lock Ruby version in Gemfile: &lt;span class="sb"&gt;`ruby '~&amp;gt; 3.3'`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What goes in your CLAUDE.md
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Ruby Project — AI Coding Rules&lt;/span&gt;

&lt;span class="gu"&gt;## Runtime&lt;/span&gt;
Ruby 3.3+. frozen_string_literal: true on every file.

&lt;span class="gu"&gt;## Style&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Trailing conditionals for guards: &lt;span class="sb"&gt;`return if invalid?`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Symbols for internal hash keys
&lt;span class="p"&gt;-&lt;/span&gt; Enumerable over loops: map/select/reduce/find
&lt;span class="p"&gt;-&lt;/span&gt; &amp;amp;:method_name and &amp;amp;method(:name) over verbose blocks

&lt;span class="gu"&gt;## Error Handling&lt;/span&gt;
rescue StandardError (specific subclasses preferred). Never rescue Exception.
Custom errors inherit StandardError, named with Error suffix.

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
attr_reader/writer/accessor over manual getters/setters.
Private by default — public only what callers need.
Service objects in app/services/ for cross-model operations.

&lt;span class="gu"&gt;## Rails (if applicable)&lt;/span&gt;
Fat models, thin controllers. Scopes for chainable queries.
find_each for large sets. includes/eager_load to prevent N+1.

&lt;span class="gu"&gt;## Testing&lt;/span&gt;
RSpec + FactoryBot. let/let! for setup. One assertion per example.
context blocks for branching. VCR/WebMock for external HTTP.

&lt;span class="gu"&gt;## Dependencies&lt;/span&gt;
bundle audit on every update. Pin major versions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Ruby needs explicit rules
&lt;/h2&gt;

&lt;p&gt;Ruby's design philosophy is that there are many ways to do the same thing, but idiomatic Ruby has strong preferences. AI assistants default to the most common patterns in their training data — often older Ruby or patterns from other languages.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; makes the AI's behavior predictable session to session: the same idioms, the same conventions, the same patterns — whether you're working alone or with a team.&lt;/p&gt;

&lt;p&gt;The full rules pack covering 12+ languages is at &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;gumroad&lt;/a&gt; — $27.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>rails</category>
    </item>
    <item>
      <title>CLAUDE.md for PHP: 13 Rules That Make AI Write Modern, Secure, Idiomatic PHP</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Mon, 11 May 2026 13:00:30 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-php-13-rules-that-make-ai-write-modern-secure-idiomatic-php-3ffb</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-php-13-rules-that-make-ai-write-modern-secure-idiomatic-php-3ffb</guid>
      <description>&lt;p&gt;If you're using Claude or another AI assistant for PHP development without a &lt;code&gt;CLAUDE.md&lt;/code&gt;, you've seen this pattern: the AI generates working code, but it's PHP 5-era style — no type hints, &lt;code&gt;mysql_*&lt;/code&gt; functions, procedural globals, &lt;code&gt;or die()&lt;/code&gt; error handling.&lt;/p&gt;

&lt;p&gt;PHP has evolved dramatically. PHP 8.x has union types, enums, fibers, named arguments, match expressions, and readonly properties. Modern PHP is a different language from what most AI training data contains. Without explicit rules, AI defaults to the lowest common denominator.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; file at your repo root tells the AI which decade you're working in. Here are the 13 rules that have the highest impact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: Enforce strict types on every file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Every PHP file must start with &lt;span class="sb"&gt;`declare(strict_types=1);`&lt;/span&gt; immediately after the opening tag.
No exceptions — including scripts, controllers, helpers, and test files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;strict_types&lt;/code&gt;, PHP silently coerces values. A function expecting &lt;code&gt;int&lt;/code&gt; accepts &lt;code&gt;"42"&lt;/code&gt; without warning. With strict types enabled, type errors throw &lt;code&gt;TypeError&lt;/code&gt; at call time, not at some downstream point where the symptom is unrelated to the cause.&lt;/p&gt;

&lt;p&gt;AI tends to omit this declaration unless you make it explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: PHP 8.x minimum — use modern syntax
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Target PHP 8.2+. Use:
&lt;span class="p"&gt;-&lt;/span&gt; Named arguments for clarity over positional guessing
&lt;span class="p"&gt;-&lt;/span&gt; Match expressions instead of switch/case with break
&lt;span class="p"&gt;-&lt;/span&gt; Nullsafe operator (?-&amp;gt;) instead of nested null checks
&lt;span class="p"&gt;-&lt;/span&gt; Readonly properties for immutable data
&lt;span class="p"&gt;-&lt;/span&gt; Enums instead of class constants for finite value sets
&lt;span class="p"&gt;-&lt;/span&gt; First-class callable syntax where it improves readability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI often generates &lt;code&gt;switch&lt;/code&gt; where &lt;code&gt;match&lt;/code&gt; is cleaner, nested &lt;code&gt;isset()&lt;/code&gt; chains where &lt;code&gt;?-&amp;gt;&lt;/code&gt; works, and class constant arrays where typed enums are correct. Specify the PHP version and the syntax you expect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: Type hints everywhere — no mixed, no omissions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;All function parameters, return types, and class properties must have type declarations.
&lt;span class="p"&gt;-&lt;/span&gt; Use union types (int|string) rather than omitting types
&lt;span class="p"&gt;-&lt;/span&gt; Use intersection types when a parameter must satisfy multiple interfaces
&lt;span class="p"&gt;-&lt;/span&gt; Use never return type for functions that always throw or exit
&lt;span class="p"&gt;-&lt;/span&gt; Avoid &lt;span class="sb"&gt;`mixed`&lt;/span&gt; except at true boundaries (serialization input, external API payloads)
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`void`&lt;/span&gt; for methods with no meaningful return value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PHP 8 supports rich type systems. A function signature like &lt;code&gt;function process($data)&lt;/code&gt; tells the AI nothing about intent. A signature like &lt;code&gt;function process(array $data): ProcessedResult&lt;/code&gt; locks in both the contract and the shape.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 4: No legacy database patterns
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Database access: PDO with prepared statements only.
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`mysql_*`&lt;/span&gt; functions (removed in PHP 7)
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`mysqli_*`&lt;/span&gt; procedural API
&lt;span class="p"&gt;-&lt;/span&gt; No raw string interpolation in SQL queries: &lt;span class="sb"&gt;`"SELECT * FROM users WHERE id = $id"`&lt;/span&gt; is forbidden
&lt;span class="p"&gt;-&lt;/span&gt; Always use &lt;span class="sb"&gt;`?`&lt;/span&gt; placeholders or named &lt;span class="sb"&gt;`:param`&lt;/span&gt; placeholders
&lt;span class="p"&gt;-&lt;/span&gt; Wrap PDO in a repository class — no direct PDO calls from controllers or views
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQL injection is still the most common PHP vulnerability. AI will generate raw interpolated queries if you don't prohibit them explicitly. The PDO pattern should be the only pattern in your codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: Error handling — exceptions, not die()
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Error handling:
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`or die()`&lt;/span&gt;, no &lt;span class="sb"&gt;`exit()`&lt;/span&gt; for flow control
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`@`&lt;/span&gt; (error suppression operator) — fix the root cause instead
&lt;span class="p"&gt;-&lt;/span&gt; Throw domain-specific exceptions that extend &lt;span class="sb"&gt;`\RuntimeException`&lt;/span&gt; or &lt;span class="sb"&gt;`\LogicException`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`set_exception_handler()`&lt;/span&gt; at the application boundary, not try/catch everywhere
&lt;span class="p"&gt;-&lt;/span&gt; Log exceptions with context (request ID, user ID, stack trace) — not bare &lt;span class="sb"&gt;`error_log()`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Legacy PHP code is littered with &lt;code&gt;or die("connection failed")&lt;/code&gt;. AI perpetuates this because it's common in training data. Exceptions with structured logging are the modern pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 6: Dependency injection — no static state
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Object construction:
&lt;span class="p"&gt;-&lt;/span&gt; No static methods for business logic (static is acceptable for pure utility functions)
&lt;span class="p"&gt;-&lt;/span&gt; No service locator pattern (&lt;span class="sb"&gt;`App::make()`&lt;/span&gt;, &lt;span class="sb"&gt;`Container::get()`&lt;/span&gt; inside domain classes)
&lt;span class="p"&gt;-&lt;/span&gt; Constructor injection for required dependencies
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`new ClassName()`&lt;/span&gt; inside methods — receive dependencies via constructor
&lt;span class="p"&gt;-&lt;/span&gt; No global state: no &lt;span class="sb"&gt;`$_GLOBALS`&lt;/span&gt;, no static class properties that mutate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Static methods and service locators make code untestable and create hidden coupling. AI gravitates toward them because they're "easy." Constructor injection is the pattern that survives growth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 7: Namespaces and PSR-4 autoloading
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Namespace conventions:
&lt;span class="p"&gt;-&lt;/span&gt; All classes must be in a namespace matching the directory structure
&lt;span class="p"&gt;-&lt;/span&gt; Follow PSR-4: &lt;span class="sb"&gt;`App\Http\Controllers\UserController`&lt;/span&gt; maps to &lt;span class="sb"&gt;`src/Http/Controllers/UserController.php`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No procedural files at the root level except &lt;span class="sb"&gt;`index.php`&lt;/span&gt; (the bootstrap)
&lt;span class="p"&gt;-&lt;/span&gt; One class per file, always
&lt;span class="p"&gt;-&lt;/span&gt; Use Composer autoloading — no manual &lt;span class="sb"&gt;`require_once`&lt;/span&gt; chains
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI sometimes generates self-contained procedural scripts. Modern PHP is object-oriented with Composer. Make the file structure convention explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 8: Security defaults — output escaping and CSRF
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Security rules (non-negotiable):
&lt;span class="p"&gt;-&lt;/span&gt; All output in templates must be escaped: &lt;span class="sb"&gt;`htmlspecialchars($var, ENT_QUOTES, 'UTF-8')`&lt;/span&gt; or the framework equivalent
&lt;span class="p"&gt;-&lt;/span&gt; CSRF tokens required on all state-mutating forms (POST, PUT, DELETE, PATCH)
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`eval()`&lt;/span&gt;, no &lt;span class="sb"&gt;`shell_exec()`&lt;/span&gt;, no &lt;span class="sb"&gt;`system()`&lt;/span&gt; unless explicitly required (and reviewed)
&lt;span class="p"&gt;-&lt;/span&gt; Passwords: &lt;span class="sb"&gt;`password_hash($pass, PASSWORD_ARGON2ID)`&lt;/span&gt; + &lt;span class="sb"&gt;`password_verify()`&lt;/span&gt;  — never MD5/SHA1
&lt;span class="p"&gt;-&lt;/span&gt; Sessions: &lt;span class="sb"&gt;`session_regenerate_id(true)`&lt;/span&gt; after login
&lt;span class="p"&gt;-&lt;/span&gt; File uploads: validate MIME type server-side, never trust the client-provided Content-Type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Security defaults must be stated explicitly. AI generates working code first, secure code only if you specify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 9: Array functions over loops
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Prefer functional array operations over manual loops:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`array_map()`&lt;/span&gt;, &lt;span class="sb"&gt;`array_filter()`&lt;/span&gt;, &lt;span class="sb"&gt;`array_reduce()`&lt;/span&gt; for collection transforms
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`array_column()`&lt;/span&gt; for plucking a field from a list of associative arrays
&lt;span class="p"&gt;-&lt;/span&gt; Spread operator &lt;span class="sb"&gt;`[...$a, ...$b]`&lt;/span&gt; for merging instead of &lt;span class="sb"&gt;`array_merge()`&lt;/span&gt; in expressions
&lt;span class="p"&gt;-&lt;/span&gt; Do not use &lt;span class="sb"&gt;`for ($i = 0; ...)`&lt;/span&gt; when &lt;span class="sb"&gt;`foreach`&lt;/span&gt; reads better
&lt;span class="p"&gt;-&lt;/span&gt; Keep closures short — extract named functions if the closure exceeds 5 lines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a readability rule. Dense &lt;code&gt;for&lt;/code&gt; loops with index arithmetic are harder to review than &lt;code&gt;array_filter($users, fn($u) =&amp;gt; $u-&amp;gt;active)&lt;/code&gt;. AI can write either; specify which you want.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 10: Immutable value objects for domain data
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Domain data:
&lt;span class="p"&gt;-&lt;/span&gt; Use readonly classes (PHP 8.2) or readonly properties for value objects
&lt;span class="p"&gt;-&lt;/span&gt; Value objects compare by value, not by reference — implement &lt;span class="sb"&gt;`equals()`&lt;/span&gt; if comparison is needed
&lt;span class="p"&gt;-&lt;/span&gt; DTOs (Data Transfer Objects) must be readonly — no setters
&lt;span class="p"&gt;-&lt;/span&gt; Money values: integer cents, never floats
&lt;span class="p"&gt;-&lt;/span&gt; Dates: &lt;span class="sb"&gt;`\DateTimeImmutable`&lt;/span&gt; only — never mutable &lt;span class="sb"&gt;`\DateTime`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mutable state is a common source of bugs. PHP 8.2 readonly classes make immutability first-class. AI defaults to mutable data unless you establish the rule.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 11: Testing — PHPUnit with real assertions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Testing conventions:
&lt;span class="p"&gt;-&lt;/span&gt; PHPUnit for unit and integration tests
&lt;span class="p"&gt;-&lt;/span&gt; Test file: &lt;span class="sb"&gt;`tests/Unit/UserServiceTest.php`&lt;/span&gt; maps to &lt;span class="sb"&gt;`src/Service/UserService.php`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`assertTrue(true)`&lt;/span&gt; — every test must assert observable behavior
&lt;span class="p"&gt;-&lt;/span&gt; Avoid mocking internal implementation — mock only external boundaries (database, HTTP, filesystem)
&lt;span class="p"&gt;-&lt;/span&gt; Data providers for edge cases, not copy-pasted test methods
&lt;span class="p"&gt;-&lt;/span&gt; Test names must describe behavior: &lt;span class="sb"&gt;`test_throws_when_email_is_invalid()`&lt;/span&gt; not &lt;span class="sb"&gt;`test1()`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI writes tests that pass but don't catch regressions. A test that mocks everything and asserts &lt;code&gt;assertTrue(true)&lt;/code&gt; is worse than no test — it creates false confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 12: Composer and package hygiene
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Dependency management:
&lt;span class="p"&gt;-&lt;/span&gt; All dependencies via Composer — no manual vendor directory manipulation
&lt;span class="p"&gt;-&lt;/span&gt; Lock the exact PHP version in &lt;span class="sb"&gt;`composer.json`&lt;/span&gt; under &lt;span class="sb"&gt;`require`&lt;/span&gt;: &lt;span class="sb"&gt;`"php": "^8.2"`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Separate &lt;span class="sb"&gt;`require`&lt;/span&gt; (production) from &lt;span class="sb"&gt;`require-dev`&lt;/span&gt; (testing, analysis tools)
&lt;span class="p"&gt;-&lt;/span&gt; Run &lt;span class="sb"&gt;`composer audit`&lt;/span&gt; before any dependency update — check for known vulnerabilities
&lt;span class="p"&gt;-&lt;/span&gt; No packages abandoned on Packagist without a forked/maintained alternative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI sometimes suggests packages that are years out of maintenance. Specifying &lt;code&gt;composer audit&lt;/code&gt; as a required step catches this automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 13: Logging with context — not bare strings
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Logging:
&lt;span class="p"&gt;-&lt;/span&gt; Use PSR-3 logger (Monolog or framework logger) — no bare &lt;span class="sb"&gt;`error_log()`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Every log call must include context: &lt;span class="sb"&gt;`$logger-&amp;gt;error('Payment failed', ['user_id' =&amp;gt; $userId, 'amount' =&amp;gt; $amount])`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Log level discipline: debug (development only), info (normal operations), warning (degraded), error (failure requiring attention), critical (system down)
&lt;span class="p"&gt;-&lt;/span&gt; No sensitive data in logs: mask card numbers, passwords, tokens before logging
&lt;span class="p"&gt;-&lt;/span&gt; Structured logs preferred (JSON) for log aggregation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;error_log("something went wrong")&lt;/code&gt; is useless in production. Context-rich structured logs are the difference between a 5-minute debug session and a 2-hour war room.&lt;/p&gt;




&lt;h2&gt;
  
  
  What goes in your CLAUDE.md
&lt;/h2&gt;

&lt;p&gt;Drop this into a &lt;code&gt;CLAUDE.md&lt;/code&gt; at your PHP project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# PHP Project — AI Coding Rules&lt;/span&gt;

&lt;span class="gu"&gt;## Language Version&lt;/span&gt;
PHP 8.2+. All files start with &lt;span class="sb"&gt;`declare(strict_types=1);`&lt;/span&gt;.

&lt;span class="gu"&gt;## Types&lt;/span&gt;
Full type declarations on all functions and properties. No &lt;span class="sb"&gt;`mixed`&lt;/span&gt; except at serialization boundaries.

&lt;span class="gu"&gt;## Database&lt;/span&gt;
PDO + prepared statements only. No raw SQL interpolation. Repository pattern.

&lt;span class="gu"&gt;## Security&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Output: htmlspecialchars on all user data
&lt;span class="p"&gt;-&lt;/span&gt; Passwords: password_hash(ARGON2ID) + password_verify
&lt;span class="p"&gt;-&lt;/span&gt; Sessions: regenerate ID after login
&lt;span class="p"&gt;-&lt;/span&gt; CSRF tokens on all mutating forms

&lt;span class="gu"&gt;## Error Handling&lt;/span&gt;
Throw domain exceptions. No or die(), no @ suppression, no exit() for control flow.

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
Constructor injection only. No static business logic. No service locator inside domain classes.

&lt;span class="gu"&gt;## Testing&lt;/span&gt;
PHPUnit. Mock only external boundaries. Behavioral test names.

&lt;span class="gu"&gt;## Logging&lt;/span&gt;
PSR-3 with context arrays. Structured JSON preferred. Never log raw sensitive data.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;CLAUDE.md isn't configuration for the AI's personality — it's the same thing as your team's coding standards document, except the AI reads it before every session instead of once at onboarding.&lt;/p&gt;

&lt;p&gt;The rules above fix the specific failure modes that show up most often in AI-generated PHP: legacy syntax, security shortcuts, untestable architecture, and missing type information.&lt;/p&gt;

&lt;p&gt;Once they're in place, AI output matches what you'd expect from a senior PHP developer on your team — not a developer who learned PHP in 2009 and never updated their patterns.&lt;/p&gt;

&lt;p&gt;The full rules pack (all stacks, team license, configuration templates) is at &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;gumroad&lt;/a&gt; — $27.&lt;/p&gt;

</description>
      <category>php</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CLAUDE.md for C# and .NET: 13 Rules That Make AI Write Modern, Idiomatic Production Code</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Sat, 09 May 2026 13:00:50 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-c-and-net-13-rules-that-make-ai-write-modern-idiomatic-production-code-4i22</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-c-and-net-13-rules-that-make-ai-write-modern-idiomatic-production-code-4i22</guid>
      <description>&lt;p&gt;Ask Claude Code to "add an endpoint that returns paged orders for a customer" and the output compiles. It also ignores nullable warnings, hides an &lt;code&gt;async void&lt;/code&gt;, blocks the thread pool with &lt;code&gt;.Result&lt;/code&gt;, wraps everything in &lt;code&gt;try/catch (Exception)&lt;/code&gt;, reaches for a static &lt;code&gt;DbContext&lt;/code&gt;, and sprinkles &lt;code&gt;IConfiguration["..."]&lt;/code&gt; strings like glitter. It runs fine on your laptop. It deadlocks in staging.&lt;/p&gt;

&lt;p&gt;C# in 2026 is not the C# the model was trained on. NRTs, records, pattern matching, minimal APIs, file-scoped namespaces, and &lt;code&gt;IAsyncEnumerable&lt;/code&gt; are table stakes. Half the model's training is .NET Framework era; half the rest is "Hello World" tutorials. A &lt;code&gt;CLAUDE.md&lt;/code&gt; next to your &lt;code&gt;.csproj&lt;/code&gt; is the cheapest way to drag it forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get the full CLAUDE.md Rules Pack — &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;oliviacraftlat.gumroad.com/l/skdgt&lt;/a&gt;&lt;/strong&gt;. The 13 rules below are a free preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Nullable reference types on, warnings as errors
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;TreatWarningsAsErrors&amp;gt;true&amp;lt;/TreatWarningsAsErrors&amp;gt;&lt;/code&gt; go in every &lt;code&gt;.csproj&lt;/code&gt; for new code. Without NRTs, the model emits &lt;code&gt;string&lt;/code&gt; parameters that secretly mean "string or null", forces you to add &lt;code&gt;?.&lt;/code&gt; everywhere downstream, and quietly swallows &lt;code&gt;NullReferenceException&lt;/code&gt; at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;TreatWarningsAsErrors&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/TreatWarningsAsErrors&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;latest&lt;span class="nt"&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Annotate intent: &lt;code&gt;Customer?&lt;/code&gt; means may be null; &lt;code&gt;Customer&lt;/code&gt; means it never is. Don't chase warnings with &lt;code&gt;!&lt;/code&gt; — the null-forgiving operator is a paper-over.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Records for DTOs, events, and value-equal types
&lt;/h2&gt;

&lt;p&gt;When the type is "data, plus equality, plus immutability", reach for &lt;code&gt;record&lt;/code&gt; (or &lt;code&gt;record struct&lt;/code&gt;). AI defaults to mutable classes with hand-rolled &lt;code&gt;Equals&lt;/code&gt;/&lt;code&gt;GetHashCode&lt;/code&gt; that drift the moment a property is added.&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;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;OrderDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderLine&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Lines&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;with&lt;/code&gt;-expressions handle the "modify one field" case without mutation. Use &lt;code&gt;class&lt;/code&gt; for entities with identity and behavior (EF Core aggregates); use &lt;code&gt;record&lt;/code&gt; for transport, requests, responses, and domain events.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Switch expressions over &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; chains
&lt;/h2&gt;

&lt;p&gt;Pattern matching is the C# 8+ killer feature, and the model still writes &lt;code&gt;if (x is Foo f &amp;amp;&amp;amp; f.Bar &amp;gt; 0)&lt;/code&gt; ladders. Switch expressions are exhaustive (the analyzer flags missing cases), shorter, and read top-to-bottom.&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;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="nf"&gt;CalculateFee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Payment&lt;/span&gt; &lt;span class="n"&gt;p&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;p&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1000m&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.015m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Card&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.025m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BankTransfer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;          &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentOutOfRangeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&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;Property patterns, list patterns, and &lt;code&gt;is not null&lt;/code&gt; over &lt;code&gt;!= null&lt;/code&gt; — they read as English and the analyzer reasons about them.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; only — no &lt;code&gt;.Result&lt;/code&gt;, no &lt;code&gt;.Wait()&lt;/code&gt;, no &lt;code&gt;async void&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Task.Result&lt;/code&gt; and &lt;code&gt;Task.Wait()&lt;/code&gt; deadlock under any sync context (ASP.NET legacy, WPF, WinForms) and starve the thread pool elsewhere. &lt;code&gt;async void&lt;/code&gt; swallows exceptions and crashes the process. AI reaches for them whenever a sync interface needs to call async code; the fix is to make the interface async, not to block.&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;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetOrderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&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;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderNotFoundException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;&lt;code&gt;async void&lt;/code&gt; is reserved for event handlers. &lt;code&gt;ConfigureAwait(false)&lt;/code&gt; in shared libraries; skip it in ASP.NET Core (no sync context to capture).&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;code&gt;CancellationToken&lt;/code&gt; everywhere — propagate, don't drop
&lt;/h2&gt;

&lt;p&gt;Every async method that does I/O takes a &lt;code&gt;CancellationToken&lt;/code&gt; and passes it down. Minimal API handlers and controllers get one for free. AI omits the token and turns client disconnects into 30-second hangs.&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="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/orders/{id:guid}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IOrderService&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&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;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&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;Name it &lt;code&gt;ct&lt;/code&gt; or &lt;code&gt;cancellationToken&lt;/code&gt;, never with a default that callers ignore. EF Core, &lt;code&gt;HttpClient&lt;/code&gt;, &lt;code&gt;Stream&lt;/code&gt; all accept tokens — use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. LINQ for transformation — but no double enumeration
&lt;/h2&gt;

&lt;p&gt;LINQ is idiomatic, but &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; from LINQ is &lt;em&gt;deferred&lt;/em&gt;: enumerating twice runs the source twice. Materialize with &lt;code&gt;.ToList()&lt;/code&gt; / &lt;code&gt;.ToArray()&lt;/code&gt; once when crossing a layer boundary. AI defaults to repeated &lt;code&gt;.Count()&lt;/code&gt; and &lt;code&gt;.Any()&lt;/code&gt; against the same query — on EF Core that's N extra round-trips.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;unpaid&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&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="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pending&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unpaid&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unpaid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EF Core: &lt;code&gt;AsNoTracking()&lt;/code&gt; for reads, &lt;code&gt;Include&lt;/code&gt; deliberately, project to DTOs with &lt;code&gt;Select&lt;/code&gt; instead of fetching whole entities. Never call &lt;code&gt;.ToList()&lt;/code&gt; mid-query and then keep filtering — that materialises the world.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Dependency injection via constructor — no &lt;code&gt;Service.Instance&lt;/code&gt;, no static state
&lt;/h2&gt;

&lt;p&gt;Every collaborator is injected through the constructor. No &lt;code&gt;HttpClient.SharedInstance&lt;/code&gt;, no &lt;code&gt;static readonly DbContext&lt;/code&gt;, no &lt;code&gt;ServiceLocator.Get&amp;lt;T&amp;gt;()&lt;/code&gt;. AI loves singletons because tests are out of scope; you live with them when those services need to be replaced for tests, integration runs, or per-tenant overrides.&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;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IClock&lt;/span&gt; &lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderService&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateOrderCommand&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Creating order for {CustomerId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&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;Primary constructors keep wiring tight. Lifetimes: &lt;code&gt;Scoped&lt;/code&gt; for request-bound work, &lt;code&gt;Singleton&lt;/code&gt; for stateless utilities, &lt;code&gt;Transient&lt;/code&gt; only when justified.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Minimal APIs with typed results — not anonymous-object soup
&lt;/h2&gt;

&lt;p&gt;Minimal APIs (or controllers — pick one per project, don't mix) own routing, model binding, and validation. AI sprinkles &lt;code&gt;Results.Ok(new { ... })&lt;/code&gt; and anonymous types; lock it down with &lt;code&gt;Results&amp;lt;Ok&amp;lt;T&amp;gt;, NotFound&amp;gt;&lt;/code&gt; so OpenAPI generation and client codegen actually match what you return.&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="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/orders/{id:guid}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&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;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;TypedResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TypedResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderDto&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;order&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="nf"&gt;WithName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetOrder"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithOpenApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Group endpoints with &lt;code&gt;MapGroup&lt;/code&gt;, share auth and validation with &lt;code&gt;IEndpointFilter&lt;/code&gt;, and return &lt;code&gt;ValidationProblem&lt;/code&gt; for 400s — not by sprinkling &lt;code&gt;if&lt;/code&gt;s inside the handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Exceptions are specific — no &lt;code&gt;catch (Exception)&lt;/code&gt; Pokémon catches
&lt;/h2&gt;

&lt;p&gt;Catch the exception you can handle: &lt;code&gt;DbUpdateConcurrencyException&lt;/code&gt;, &lt;code&gt;HttpRequestException&lt;/code&gt;, &lt;code&gt;OperationCanceledException&lt;/code&gt;. Anything else bubbles to the global handler. AI wraps every block in &lt;code&gt;try/catch (Exception ex) { _logger.LogError(ex, ...); return null; }&lt;/code&gt; and you spend Friday debugging silent failures.&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;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbUpdateConcurrencyException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Concurrency conflict on {Id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TypedResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Conflict&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;Re-throw with &lt;code&gt;throw;&lt;/code&gt; — never &lt;code&gt;throw ex;&lt;/code&gt;, which resets the stack trace. &lt;code&gt;OperationCanceledException&lt;/code&gt; propagates; don't swallow it, or you'll mask client cancellations as success.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Configuration via &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; — no &lt;code&gt;IConfiguration["Key"]&lt;/code&gt; strings
&lt;/h2&gt;

&lt;p&gt;String-keyed configuration scattered across services is untyped, untestable, and validated nowhere. Bind to a typed options class once, register with &lt;code&gt;AddOptions&amp;lt;T&amp;gt;().BindConfiguration(...).ValidateDataAnnotations().ValidateOnStart()&lt;/code&gt;, and inject &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; (or &lt;code&gt;IOptionsSnapshot&amp;lt;T&amp;gt;&lt;/code&gt; for reload).&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;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ApiKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TimeoutSeconds&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&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="n"&gt;AddOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StripeOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BindConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Stripe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidateDataAnnotations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidateOnStart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bad config crashes startup, not the first paid request at 2 a.m.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Structured logging via &lt;code&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt; — message templates, not interpolation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;logger.LogInformation($"User {userId} did {action}")&lt;/code&gt; boxes the values into a single string and destroys structured search. Use the message template form: positional placeholders, named parameters preserved as fields in JSON logs.&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Order {OrderId} settled in {ElapsedMs} ms for {CustomerId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Levels: &lt;code&gt;Information&lt;/code&gt; for business events, &lt;code&gt;Warning&lt;/code&gt; for handled anomalies, &lt;code&gt;Error&lt;/code&gt; for unhandled failures, &lt;code&gt;Debug&lt;/code&gt; for noisy local-only output. No &lt;code&gt;Console.WriteLine&lt;/code&gt; in production paths. Use &lt;code&gt;LoggerMessage&lt;/code&gt; source generators on hot loops.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Tests use xUnit + FluentAssertions — and exercise the public surface
&lt;/h2&gt;

&lt;p&gt;xUnit (&lt;code&gt;[Fact]&lt;/code&gt;, &lt;code&gt;[Theory]&lt;/code&gt;), &lt;code&gt;FluentAssertions&lt;/code&gt; for readable failures, and &lt;code&gt;WebApplicationFactory&amp;lt;TProgram&amp;gt;&lt;/code&gt; for HTTP-level integration tests. AI writes 200 unit tests against private helpers and zero tests that boot the actual app; flip the ratio.&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetOrderTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrdersWebAppFactory&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IClassFixture&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrdersWebAppFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Returns_404_when_order_missing&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;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&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;resp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"/orders/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&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="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&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="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotFound&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;Mock at the seam (&lt;code&gt;IOrderService&lt;/code&gt;, &lt;code&gt;IClock&lt;/code&gt;), not at &lt;code&gt;DbContext&lt;/code&gt;. Use &lt;code&gt;Testcontainers&lt;/code&gt; for real Postgres in CI when the schema matters. Snapshot tests for response shapes are fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Build hygiene: analyzers on, warnings on, formatting enforced
&lt;/h2&gt;

&lt;p&gt;Add &lt;code&gt;Microsoft.CodeAnalysis.NetAnalyzers&lt;/code&gt;, enable &lt;code&gt;EnforceCodeStyleInBuild&lt;/code&gt;, drop an &lt;code&gt;.editorconfig&lt;/code&gt; with the team's rules, and run &lt;code&gt;dotnet format --verify-no-changes&lt;/code&gt; in CI. AI will silence analyzers with &lt;code&gt;#pragma warning disable&lt;/code&gt; to "ship" — don't let it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;AnalysisMode&amp;gt;&lt;/span&gt;AllEnabledByDefault&lt;span class="nt"&gt;&amp;lt;/AnalysisMode&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;EnforceCodeStyleInBuild&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/EnforceCodeStyleInBuild&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;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 ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# .editorconfig
&lt;/span&gt;&lt;span class="nn"&gt;[*.cs]&lt;/span&gt;
&lt;span class="py"&gt;csharp_style_namespace_declarations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;file_scoped:error&lt;/span&gt;
&lt;span class="py"&gt;dotnet_style_qualification_for_field&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false:warning&lt;/span&gt;
&lt;span class="py"&gt;dotnet_diagnostic.CA1062.severity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;error  # validate args in public APIs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;File-scoped namespaces, &lt;code&gt;ImplicitUsings&lt;/code&gt;, and &lt;code&gt;Nullable&lt;/code&gt; enabled in every new project template. Suppressing analyzer rules requires a justification comment, reviewed in PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  A starter &lt;code&gt;CLAUDE.md&lt;/code&gt; snippet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md — .NET service&lt;/span&gt;

&lt;span class="gu"&gt;## Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; C# 13 / .NET 9, NRTs on, warnings as errors, file-scoped namespaces
&lt;span class="p"&gt;-&lt;/span&gt; ASP.NET Core minimal APIs, EF Core, xUnit + FluentAssertions

&lt;span class="gu"&gt;## Hard rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; NRTs enabled; no &lt;span class="sb"&gt;`!`&lt;/span&gt; to silence warnings; warnings = errors.
&lt;span class="p"&gt;-&lt;/span&gt; Records for DTOs/events/value types; classes for entities and behavior.
&lt;span class="p"&gt;-&lt;/span&gt; Switch expressions and pattern matching over &lt;span class="sb"&gt;`if`&lt;/span&gt;/&lt;span class="sb"&gt;`else`&lt;/span&gt; chains.
&lt;span class="p"&gt;-&lt;/span&gt; async/await only. No &lt;span class="sb"&gt;`.Result`&lt;/span&gt;, no &lt;span class="sb"&gt;`.Wait()`&lt;/span&gt;, no &lt;span class="sb"&gt;`async void`&lt;/span&gt; outside event handlers.
&lt;span class="p"&gt;-&lt;/span&gt; Every async I/O method takes and propagates &lt;span class="sb"&gt;`CancellationToken`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; LINQ materialised once at layer boundaries; EF reads use &lt;span class="sb"&gt;`AsNoTracking`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Constructor DI only. No &lt;span class="sb"&gt;`static`&lt;/span&gt; state, no service locator.
&lt;span class="p"&gt;-&lt;/span&gt; Minimal APIs return &lt;span class="sb"&gt;`TypedResults`&lt;/span&gt;; &lt;span class="sb"&gt;`MapGroup`&lt;/span&gt; + &lt;span class="sb"&gt;`IEndpointFilter`&lt;/span&gt; for shared concerns.
&lt;span class="p"&gt;-&lt;/span&gt; Catch specific exceptions; rethrow with &lt;span class="sb"&gt;`throw;`&lt;/span&gt;; let &lt;span class="sb"&gt;`OperationCanceledException`&lt;/span&gt; bubble.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`IOptions&amp;lt;T&amp;gt;`&lt;/span&gt; for config with &lt;span class="sb"&gt;`ValidateDataAnnotations().ValidateOnStart()`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Structured logging via &lt;span class="sb"&gt;`ILogger&amp;lt;T&amp;gt;`&lt;/span&gt;; message templates, not interpolation.
&lt;span class="p"&gt;-&lt;/span&gt; Tests: xUnit, FluentAssertions, &lt;span class="sb"&gt;`WebApplicationFactory&amp;lt;T&amp;gt;`&lt;/span&gt; for HTTP tests.
&lt;span class="p"&gt;-&lt;/span&gt; Analyzers + &lt;span class="sb"&gt;`dotnet format --verify-no-changes`&lt;/span&gt; in CI.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Claude gets wrong without these rules
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ignores nullability, sprinkles &lt;code&gt;!&lt;/code&gt;, ships an NRE on the first edge case.&lt;/li&gt;
&lt;li&gt;Returns mutable classes with hand-rolled equality that breaks on the next field.&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;.Result&lt;/code&gt; inside a sync wrapper and deadlocks the request pipeline.&lt;/li&gt;
&lt;li&gt;Catches &lt;code&gt;Exception&lt;/code&gt;, logs, returns &lt;code&gt;null&lt;/code&gt; — silent failure, no signal.&lt;/li&gt;
&lt;li&gt;Reads config via &lt;code&gt;IConfiguration["Stripe:ApiKey"]&lt;/code&gt; in three different services.&lt;/li&gt;
&lt;li&gt;Writes 200 tests for private helpers and zero tests against the real app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop these 13 rules into &lt;code&gt;CLAUDE.md&lt;/code&gt; and the next AI PR looks like a 2026 .NET service, not a 2017 ASP.NET MVC tutorial. Your CI stops failing on warnings and your staging stops deadlocking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want this for 20+ stacks with 200+ rules ready to paste?&lt;/strong&gt; Grab the &lt;strong&gt;CLAUDE.md Rules Pack&lt;/strong&gt; at &lt;strong&gt;&lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;oliviacraftlat.gumroad.com/l/skdgt&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;— Olivia (&lt;a href="https://x.com/OliviaCraftLat" rel="noopener noreferrer"&gt;@OliviaCraftLat&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>claudemd</category>
      <category>ai</category>
    </item>
    <item>
      <title>CLAUDE.md for Java: 13 Rules That Make AI Write Modern, Production-Ready JVM Code</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Thu, 07 May 2026 18:57:22 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-java-13-rules-that-make-ai-write-modern-production-ready-jvm-code-4coc</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-java-13-rules-that-make-ai-write-modern-production-ready-jvm-code-4coc</guid>
      <description>&lt;p&gt;Java developers have a particular problem with AI assistants: the models have seen fifteen years of StackOverflow answers, legacy tutorials, and pre-Java-11 patterns. Without guidance, Claude will write &lt;code&gt;new ArrayList&amp;lt;&amp;gt;()&lt;/code&gt; where you want &lt;code&gt;List.of()&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt; checks where you want &lt;code&gt;Optional&lt;/code&gt;, and raw &lt;code&gt;Exception&lt;/code&gt; catches where you want specific error types.&lt;/p&gt;

&lt;p&gt;A well-written &lt;code&gt;CLAUDE.md&lt;/code&gt; fixes this. Here are 13 rules that push AI-generated Java from Java 8-era code to modern, idiomatic production Java.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Use modern collection factory methods
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad — verbose, mutable&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"java"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Good — Java 9+, immutable by default&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"java"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"api"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"NOT_FOUND"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule for CLAUDE.md:&lt;/strong&gt; "Use &lt;code&gt;List.of()&lt;/code&gt;, &lt;code&gt;Set.of()&lt;/code&gt;, &lt;code&gt;Map.of()&lt;/code&gt; for immutable collections. Use &lt;code&gt;new ArrayList&amp;lt;&amp;gt;(List.of(...))&lt;/code&gt; only when mutation is required."&lt;/p&gt;




&lt;h2&gt;
  
  
  2. &lt;code&gt;Optional&lt;/code&gt; instead of null returns
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;userMap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// returns null if missing&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="n"&gt;findUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;User:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserNotFoundException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Never return &lt;code&gt;null&lt;/code&gt; from public methods. Return &lt;code&gt;Optional&amp;lt;T&amp;gt;&lt;/code&gt; for values that may be absent. Never use &lt;code&gt;Optional.get()&lt;/code&gt; without checking — use &lt;code&gt;orElse&lt;/code&gt;, &lt;code&gt;orElseThrow&lt;/code&gt;, or &lt;code&gt;ifPresent&lt;/code&gt;."&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Records for data carriers (Java 16+)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad — verbose POJO&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderSummary&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;totalCents&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// constructor, getters, equals, hashCode, toString...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;OrderSummary&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;totalCents&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Use &lt;code&gt;record&lt;/code&gt; for immutable data carriers. No manual getters/setters/equals/hashCode for records. Extend with compact constructors for validation."&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Sealed classes for closed type hierarchies (Java 17+)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good — exhaustive, compiler-enforced&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt;
    &lt;span class="n"&gt;permits&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Pending&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;transactionId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;errorCode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Pending&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// Pattern matching switch (Java 21)&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Paid: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Failed: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Pending&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Pending: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Use &lt;code&gt;sealed interface&lt;/code&gt; + &lt;code&gt;record&lt;/code&gt; implementations for closed domain hierarchies. Use pattern matching &lt;code&gt;switch&lt;/code&gt; for exhaustive dispatch — no default needed."&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Stream API over imperative loops for transformations
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isActive&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;User:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;isActive&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Java 16+ — immutable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Use streams for filter/map/reduce/collect operations. Use &lt;code&gt;.toList()&lt;/code&gt; (Java 16+) for immutable result lists. Reserve imperative loops for side effects."&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Specific exception types, never raw &lt;code&gt;Exception&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InventoryException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;warn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inventory check failed for order {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderProcessingException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inventory unavailable"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment failed for order {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderProcessingException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment declined"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Catch specific exception types only. Always pass the original exception as the cause. Never swallow exceptions with empty catch blocks."&lt;/p&gt;




&lt;h2&gt;
  
  
  7. &lt;code&gt;var&lt;/code&gt; for local type inference where it improves readability
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good — type is obvious from the right side&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAllActive&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orderMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Bad — type is not obvious&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// What type is result?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Use &lt;code&gt;var&lt;/code&gt; when the type is obvious from the right-hand side. Never use &lt;code&gt;var&lt;/code&gt; for method return types or when the inferred type would be unclear to a reader."&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Constructor injection, not field injection
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad — field injection (not testable without Spring context)&lt;/span&gt;
&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;OrderRepository&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;PaymentGateway&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Good — constructor injection&lt;/span&gt;
&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;OrderRepository&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;PaymentGateway&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderRepository&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PaymentGateway&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Constructor injection always. No &lt;code&gt;@Autowired&lt;/code&gt; on fields. Mark injected fields &lt;code&gt;final&lt;/code&gt;. This enables plain-Java unit tests without Spring context."&lt;/p&gt;




&lt;h2&gt;
  
  
  9. SLF4J with parameterized messages — never string concatenation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad — allocates string even if DEBUG is off&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing order "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" for user "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Also bad&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order processed: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing order {} for user {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment failed for order {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "SLF4J only — no &lt;code&gt;System.out.println&lt;/code&gt;. Parameterized log messages (&lt;code&gt;{}&lt;/code&gt;) always. Pass exceptions as the last argument to preserve stack traces."&lt;/p&gt;




&lt;h2&gt;
  
  
  10. &lt;code&gt;Instant&lt;/code&gt; and &lt;code&gt;Duration&lt;/code&gt; for time — never &lt;code&gt;Date&lt;/code&gt; or &lt;code&gt;long&lt;/code&gt; milliseconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad&lt;/span&gt;
&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Date&lt;/span&gt; &lt;span class="n"&gt;expiresAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;86400000L&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;expiresAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;plus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofDays&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;atZone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ZoneId&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"America/Santiago"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Java Time API (&lt;code&gt;java.time.*&lt;/code&gt;) exclusively. &lt;code&gt;Instant&lt;/code&gt; for storage/comparison, &lt;code&gt;ZonedDateTime&lt;/code&gt; for display, &lt;code&gt;Duration&lt;/code&gt;/&lt;code&gt;Period&lt;/code&gt; for intervals. Never &lt;code&gt;java.util.Date&lt;/code&gt; or raw millisecond longs."&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Immutability by default
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad — mutable everywhere&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requireNonNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"apiKey required"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeoutSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Classes immutable by default: &lt;code&gt;final&lt;/code&gt; class, &lt;code&gt;final&lt;/code&gt; fields, no setters. Use Builder pattern for objects with many optional fields. Validate in the constructor with &lt;code&gt;Objects.requireNonNull&lt;/code&gt;."&lt;/p&gt;




&lt;h2&gt;
  
  
  12. JUnit 5 with &lt;code&gt;@DisplayName&lt;/code&gt; and Arrange-Act-Assert
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad&lt;/span&gt;
&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;test1&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Good&lt;/span&gt;
&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"processOrder returns SUCCESS when inventory and payment both succeed"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processOrder_success&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderFixture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validOrder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;check&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InStock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChargeResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"txn-123"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert&lt;/span&gt;
    &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isInstanceOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(((&lt;/span&gt;&lt;span class="nc"&gt;PaymentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"txn-123"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "JUnit 5 with AssertJ assertions. &lt;code&gt;@DisplayName&lt;/code&gt; on all tests describing the scenario. Arrange-Act-Assert structure, clearly separated. No &lt;code&gt;assertTrue(x != null)&lt;/code&gt; — use &lt;code&gt;assertThat(x).isNotNull()&lt;/code&gt;."&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Checkstyle + SpotBugs + &lt;code&gt;--enable-preview&lt;/code&gt; awareness
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Generated code must pass Checkstyle (Google style) and SpotBugs with no HIGH/MEDIUM bugs. When using Java 21+ preview features (&lt;code&gt;--enable-preview&lt;/code&gt;), note the flag explicitly. Never generate deprecated API usage (&lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Vector&lt;/code&gt;, &lt;code&gt;StringBuffer&lt;/code&gt;) without acknowledging it."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- pom.xml --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.github.spotbugs&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spotbugs-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;effort&amp;gt;&lt;/span&gt;Max&lt;span class="nt"&gt;&amp;lt;/effort&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;threshold&amp;gt;&lt;/span&gt;Medium&lt;span class="nt"&gt;&amp;lt;/threshold&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Your CLAUDE.md block
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Java Standards&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Target: Java 21 LTS. Use preview features only if &lt;span class="sb"&gt;`--enable-preview`&lt;/span&gt; is documented.
&lt;span class="p"&gt;-&lt;/span&gt; Collections: &lt;span class="sb"&gt;`List.of()`&lt;/span&gt;, &lt;span class="sb"&gt;`Set.of()`&lt;/span&gt;, &lt;span class="sb"&gt;`Map.of()`&lt;/span&gt; for immutable; mutable only when needed
&lt;span class="p"&gt;-&lt;/span&gt; No null returns from public methods — use &lt;span class="sb"&gt;`Optional&amp;lt;T&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`record`&lt;/span&gt; for immutable data carriers; &lt;span class="sb"&gt;`sealed interface`&lt;/span&gt; for closed hierarchies
&lt;span class="p"&gt;-&lt;/span&gt; Streams for filter/map/collect; &lt;span class="sb"&gt;`.toList()`&lt;/span&gt; for immutable result (Java 16+)
&lt;span class="p"&gt;-&lt;/span&gt; Catch specific exceptions; always chain cause; never swallow
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`var`&lt;/span&gt; for obvious local types only
&lt;span class="p"&gt;-&lt;/span&gt; Constructor injection; &lt;span class="sb"&gt;`final`&lt;/span&gt; fields; no &lt;span class="sb"&gt;`@Autowired`&lt;/span&gt; on fields
&lt;span class="p"&gt;-&lt;/span&gt; SLF4J parameterized logging; no System.out; exceptions as last log arg
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`java.time.*`&lt;/span&gt; only — no &lt;span class="sb"&gt;`java.util.Date`&lt;/span&gt; or raw millisecond longs
&lt;span class="p"&gt;-&lt;/span&gt; Immutable by default: &lt;span class="sb"&gt;`final`&lt;/span&gt; class + fields, &lt;span class="sb"&gt;`Objects.requireNonNull`&lt;/span&gt; in constructor
&lt;span class="p"&gt;-&lt;/span&gt; JUnit 5 + AssertJ; &lt;span class="sb"&gt;`@DisplayName`&lt;/span&gt;; AAA structure; no &lt;span class="sb"&gt;`assertTrue(x != null)`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Code must pass Checkstyle (Google) and SpotBugs Medium threshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;These 13 rules give AI the context it needs to write Java that actually belongs in a 2026 codebase — not a 2012 tutorial. Combined with the rules for other languages, you get consistent AI behavior across your entire stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;CLAUDE.md Rules Pack&lt;/a&gt; includes 50+ production rules for Java, Python, TypeScript, Go, Rust, Swift, and more — $27 one-time.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>CLAUDE.md for Python: 13 Rules That Make AI Write Idiomatic, Type-Safe Production Code</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Thu, 07 May 2026 16:57:01 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-python-13-rules-that-make-ai-write-idiomatic-type-safe-production-code-4bl0</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-python-13-rules-that-make-ai-write-idiomatic-type-safe-production-code-4bl0</guid>
      <description>&lt;p&gt;You've set up Claude Code or Cursor. You ask it to write a FastAPI endpoint. It comes back with &lt;code&gt;dict&lt;/code&gt; everywhere, bare &lt;code&gt;except Exception&lt;/code&gt;, and a function that mutates a list default argument. It &lt;em&gt;works&lt;/em&gt;, but it's not Python — it's Python-shaped code that will hurt you in six months.&lt;/p&gt;

&lt;p&gt;The fix is a &lt;code&gt;CLAUDE.md&lt;/code&gt; that tells the model exactly what "good Python" means on your project. Here are 13 rules that turn AI-generated Python from plausible to production-ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Always use type hints — including &lt;code&gt;Annotated&lt;/code&gt; and &lt;code&gt;TypeVar&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt;

&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user primary key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule for CLAUDE.md:&lt;/strong&gt; "All functions must have fully annotated signatures. Use &lt;code&gt;Annotated&lt;/code&gt; for domain-specific constraints. Define &lt;code&gt;TypeVar&lt;/code&gt; for generic utilities."&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Use Pydantic models, not raw dicts
&lt;/h2&gt;

&lt;p&gt;Dicts are untyped bags. Pydantic models are contracts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt;
    &lt;span class="n"&gt;total_cents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Never use &lt;code&gt;dict&lt;/code&gt; for structured data at function boundaries. Define Pydantic models for all request/response shapes."&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use &lt;code&gt;pathlib&lt;/code&gt;, not &lt;code&gt;os.path&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;config_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="n"&gt;config_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&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;parent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "&lt;code&gt;pathlib.Path&lt;/code&gt; for all filesystem operations. Never import &lt;code&gt;os.path&lt;/code&gt;."&lt;/p&gt;

&lt;h2&gt;
  
  
  4. f-strings for all string formatting
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad
&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User %s has %d items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User {} has {} items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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="c1"&gt;# Good
&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; has &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "f-strings exclusively. No &lt;code&gt;%&lt;/code&gt; formatting, no &lt;code&gt;.format()&lt;/code&gt; calls."&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;code&gt;dataclasses&lt;/code&gt; or &lt;code&gt;attrs&lt;/code&gt; for data containers
&lt;/h2&gt;

&lt;p&gt;When you don't need Pydantic validation, use &lt;code&gt;dataclasses&lt;/code&gt; for lightweight containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Use &lt;code&gt;@dataclass&lt;/code&gt; for internal data containers that don't need Pydantic validation. Always use &lt;code&gt;field(default_factory=...)&lt;/code&gt; for mutable defaults."&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Never use mutable default arguments
&lt;/h2&gt;

&lt;p&gt;This is Python's most famous footgun. AI models reproduce it constantly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad — shared across all calls
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "No mutable default arguments (&lt;code&gt;[]&lt;/code&gt;, &lt;code&gt;{}&lt;/code&gt;, custom objects). Use &lt;code&gt;None&lt;/code&gt; and initialize inside the function body."&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Proper &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; — no sync blocking in async functions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad — blocks the event loop
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Async functions must never call blocking I/O. Use &lt;code&gt;httpx.AsyncClient&lt;/code&gt; for HTTP, &lt;code&gt;asyncio.to_thread&lt;/code&gt; for CPU-bound work."&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Exception chaining with &lt;code&gt;raise X from Y&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad — loses original traceback
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Database query failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;DatabaseError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Query failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Always chain exceptions with &lt;code&gt;raise NewError(...) from original_error&lt;/code&gt;. Never use bare &lt;code&gt;except Exception: raise RuntimeError(...)&lt;/code&gt;."&lt;/p&gt;

&lt;h2&gt;
  
  
  9. &lt;code&gt;logging&lt;/code&gt;, never &lt;code&gt;print&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Processing &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Processing item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Processing failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "No &lt;code&gt;print()&lt;/code&gt; statements in production code. Use module-level &lt;code&gt;logger = logging.getLogger(__name__)&lt;/code&gt;. Use &lt;code&gt;logger.exception()&lt;/code&gt; inside except blocks to capture tracebacks."&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Virtual environments and explicit dependency pinning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Always assume a virtual environment. Dependencies go in &lt;code&gt;pyproject.toml&lt;/code&gt; (preferred) or &lt;code&gt;requirements.txt&lt;/code&gt; with pinned versions. Never suggest &lt;code&gt;pip install X&lt;/code&gt; without adding to dependency file."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# pyproject.toml&lt;/span&gt;
&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="py"&gt;"fastapi&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.111&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mf"&gt;0.112&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"pydantic&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.7&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"httpx&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.27&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mf"&gt;0.28&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  11. pytest fixtures over setup/teardown
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_test_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Good
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_test_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;
    &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "pytest only — no &lt;code&gt;unittest.TestCase&lt;/code&gt;. Use fixtures for setup/teardown. Test functions are plain functions, never methods."&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Google-style docstrings, one-liners only for simple functions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Apply discount rate to price.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrderCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OrderResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create an order and return the persisted result.

    Args:
        order: Validated order input from the request body.
        db: Active database session, injected by FastAPI dependency.

    Returns:
        Persisted order with generated ID and computed totals.

    Raises:
        ProductNotFoundError: If `order.product_id` does not exist.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "Google-style docstrings for public functions. One-liner for simple helpers. Never generate NumPy-style docstrings."&lt;/p&gt;

&lt;h2&gt;
  
  
  13. &lt;code&gt;ruff&lt;/code&gt; and &lt;code&gt;mypy&lt;/code&gt; are the law
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; "All generated code must pass &lt;code&gt;ruff check&lt;/code&gt; (E, F, I rules) and &lt;code&gt;mypy --strict&lt;/code&gt;. Never disable type checks with &lt;code&gt;# type: ignore&lt;/code&gt; without a comment explaining why."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# These must pass before any code is considered done&lt;/span&gt;
ruff check src/
mypy src/ &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suggested &lt;code&gt;pyproject.toml&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.mypy]&lt;/span&gt;
&lt;span class="py"&gt;strict&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ignore_missing_imports&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="nn"&gt;[tool.ruff]&lt;/span&gt;
&lt;span class="py"&gt;select&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"F"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"I"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Your CLAUDE.md block
&lt;/h2&gt;

&lt;p&gt;Drop this into your project's &lt;code&gt;CLAUDE.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Python Standards&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Type hints required on all functions; use &lt;span class="sb"&gt;`Annotated`&lt;/span&gt; for domain types
&lt;span class="p"&gt;-&lt;/span&gt; Pydantic models for all structured data at boundaries — no raw dicts
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pathlib.Path`&lt;/span&gt; for filesystem; never &lt;span class="sb"&gt;`os.path`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; f-strings only; no &lt;span class="sb"&gt;`%`&lt;/span&gt; or &lt;span class="sb"&gt;`.format()`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`@dataclass`&lt;/span&gt; with &lt;span class="sb"&gt;`field(default_factory=...)`&lt;/span&gt; for mutable defaults
&lt;span class="p"&gt;-&lt;/span&gt; No mutable default arguments — use &lt;span class="sb"&gt;`None`&lt;/span&gt; sentinel
&lt;span class="p"&gt;-&lt;/span&gt; Async functions: no blocking I/O; use &lt;span class="sb"&gt;`httpx.AsyncClient`&lt;/span&gt;, &lt;span class="sb"&gt;`asyncio.to_thread`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Exception chaining: &lt;span class="sb"&gt;`raise NewError(...) from original`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`logging.getLogger(__name__)`&lt;/span&gt; only; no print statements
&lt;span class="p"&gt;-&lt;/span&gt; Dependencies in &lt;span class="sb"&gt;`pyproject.toml`&lt;/span&gt; with pinned ranges
&lt;span class="p"&gt;-&lt;/span&gt; pytest fixtures; no &lt;span class="sb"&gt;`unittest.TestCase`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Google-style docstrings on public functions
&lt;span class="p"&gt;-&lt;/span&gt; Code must pass &lt;span class="sb"&gt;`ruff check`&lt;/span&gt; and &lt;span class="sb"&gt;`mypy --strict`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These 13 rules push AI from "writes Python" to "writes &lt;em&gt;your&lt;/em&gt; Python." The goal isn't to restrict the model — it's to give it enough context that it makes the right call the first time, every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These rules are pre-applied in the &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;CLAUDE.md Rules Pack&lt;/a&gt; — 50+ production rules for Python, TypeScript, Go, Rust, Swift, and more. $27 one-time.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>CLAUDE.md for Swift and iOS: 13 Rules That Stop AI From Writing Unsafe, Non-Idiomatic Apple Code</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Thu, 07 May 2026 12:20:12 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-swift-and-ios-13-rules-that-stop-ai-from-writing-unsafe-non-idiomatic-apple-code-4nld</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-swift-and-ios-13-rules-that-stop-ai-from-writing-unsafe-non-idiomatic-apple-code-4nld</guid>
      <description>&lt;p&gt;Ask Claude Code to "add a screen that loads a user profile and shows their orders" and the output compiles. It also strong-captures &lt;code&gt;self&lt;/code&gt; in three closures, blocks the main thread on a network call, mutates &lt;code&gt;@State&lt;/code&gt; from a background actor, and leaks a coordinator. Nothing crashes in the simulator. Everything crashes in TestFlight.&lt;/p&gt;

&lt;p&gt;Swift is hostile to AI defaults: ARC, the SwiftUI lifecycle, Combine, structured concurrency, and a decade of UIKit history coexist in the same target. Half the model's training data is Objective-C, half is pre-async/await Swift, and the rest is SwiftUI tutorials that ignore actor isolation. A &lt;code&gt;CLAUDE.md&lt;/code&gt; next to &lt;code&gt;Package.swift&lt;/code&gt; is the cheapest way to pull it back to current Apple practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get the full CLAUDE.md Rules Pack — &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;oliviacraftlat.gumroad.com/l/skdgt&lt;/a&gt;&lt;/strong&gt;. The 13 rules below are a free preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;[weak self]&lt;/code&gt; in every escaping closure that touches &lt;code&gt;self&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;ARC retain cycles are the canonical iOS leak, and AI writes them constantly: a closure captured by a long-lived publisher, &lt;code&gt;Task&lt;/code&gt;, &lt;code&gt;URLSession&lt;/code&gt; handler, or &lt;code&gt;NotificationCenter&lt;/code&gt; observer that strongly references &lt;code&gt;self&lt;/code&gt;. Every &lt;code&gt;@escaping&lt;/code&gt; closure that mentions &lt;code&gt;self&lt;/code&gt; opens with &lt;code&gt;[weak self] in&lt;/code&gt; and unwraps via &lt;code&gt;guard let self else { return }&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$user&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;weak&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;titleLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cancellables&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Non-escaping closures (&lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;forEach&lt;/code&gt;) don't need it — capture is bounded by the call.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Force-unwrap &lt;code&gt;!&lt;/code&gt; and &lt;code&gt;try!&lt;/code&gt; are banned outside tests
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;user.profile!.name&lt;/code&gt; and &lt;code&gt;try! JSONDecoder().decode(...)&lt;/code&gt; are the top crash signature in production iOS apps. Use &lt;code&gt;guard let&lt;/code&gt;, &lt;code&gt;if let&lt;/code&gt;, optional chaining, &lt;code&gt;??&lt;/code&gt;, or &lt;code&gt;do/try/catch&lt;/code&gt;. Reserve &lt;code&gt;!&lt;/code&gt; for &lt;code&gt;IBOutlet&lt;/code&gt; and test setup where failure is the test failing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kt"&gt;APIError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;URLSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="kt"&gt;JSONDecoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fatalError&lt;/code&gt; is allowed only for genuinely impossible states, with a message explaining why.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;async/await&lt;/code&gt; only — no completion handlers in new code
&lt;/h2&gt;

&lt;p&gt;If the deployment target is iOS 15+, every new async API returns &lt;code&gt;async throws&lt;/code&gt; or uses &lt;code&gt;AsyncSequence&lt;/code&gt;. Completion-handler closures leak callbacks, scatter error handling, and don't compose with &lt;code&gt;Task&lt;/code&gt; cancellation. Bridge legacy APIs once via &lt;code&gt;withCheckedThrowingContinuation&lt;/code&gt; and never again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="nf"&gt;ordersRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="kt"&gt;OrdersAPI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&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;No &lt;code&gt;DispatchQueue.global().async { DispatchQueue.main.async { ... } }&lt;/code&gt; pyramids.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. UI mutations run on &lt;code&gt;@MainActor&lt;/code&gt;, full stop
&lt;/h2&gt;

&lt;p&gt;Updating UIKit views or &lt;code&gt;@State&lt;/code&gt; from a background context is a Swift 6 concurrency error and a flicker bug today. Annotate view models with &lt;code&gt;@MainActor&lt;/code&gt; so the compiler enforces it. AI defaults to &lt;code&gt;DispatchQueue.main.async&lt;/code&gt; — which works, but skips the actor isolation guarantee and breaks under strict concurrency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;OrdersViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="kd"&gt;private(set)&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadOrders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* surface to UI */&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;Heavy decoding or image work goes in a non-isolated helper (or &lt;code&gt;Task.detached&lt;/code&gt;) and the result is awaited back on the main actor.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Pick the right SwiftUI property wrapper, once
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@State&lt;/code&gt;, &lt;code&gt;@StateObject&lt;/code&gt;, &lt;code&gt;@ObservedObject&lt;/code&gt;, &lt;code&gt;@Binding&lt;/code&gt;, &lt;code&gt;@Environment&lt;/code&gt; are not interchangeable, but AI uses them as if they were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@State&lt;/code&gt; — value types owned by &lt;em&gt;this&lt;/em&gt; view.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@StateObject&lt;/code&gt; — reference-type view models &lt;em&gt;created&lt;/em&gt; by this view.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ObservedObject&lt;/code&gt; — view models &lt;em&gt;passed in&lt;/em&gt; from a parent.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Binding&lt;/code&gt; — two-way value bindings from a parent.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Environment&lt;/code&gt; / &lt;code&gt;@EnvironmentObject&lt;/code&gt; — dependencies injected up the tree.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;OrdersScreen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@StateObject&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;OrdersViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// owned here&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;OrdersList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;viewModel&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="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;OrdersList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@ObservedObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;OrdersViewModel&lt;/span&gt;  &lt;span class="c1"&gt;// passed in&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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;Wrong wrapper = state thrown away on parent re-render, lifecycle bugs only visible in TestFlight.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Shared mutable state lives in an &lt;code&gt;actor&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Whenever AI writes &lt;code&gt;private let queue = DispatchQueue(label: "...")&lt;/code&gt; to synchronize access, replace it with an &lt;code&gt;actor&lt;/code&gt;. Locks (&lt;code&gt;NSLock&lt;/code&gt;, &lt;code&gt;os_unfair_lock&lt;/code&gt;) are reserved for non-async hot paths or C interop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;actor&lt;/span&gt; &lt;span class="kt"&gt;ImageCache&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIImage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[:]&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;UIImage&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&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;Calls become &lt;code&gt;await cache.image(for: url)&lt;/code&gt;. Data races become compile errors, not Sentry alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Errors are typed enums conforming to &lt;code&gt;LocalizedError&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;throw NSError(domain: "...", code: -1)&lt;/code&gt; is AI that gave up. Stable error conditions get an enum case; errors that cross boundaries conform to &lt;code&gt;LocalizedError&lt;/code&gt; so they're safe to surface in UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;APIError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;LocalizedError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;invalidURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;decoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;underlying&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;errorDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Invalid URL: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Server error (&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;)"&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;decoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Couldn't read server response"&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;Callers branch on cases, not string matching.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Protocols + &lt;code&gt;struct&lt;/code&gt; value types — inheritance is the last resort
&lt;/h2&gt;

&lt;p&gt;AI loves class hierarchies — &lt;code&gt;BaseViewController&lt;/code&gt;, &lt;code&gt;BaseViewModel&lt;/code&gt;, &lt;code&gt;AbstractRepository&lt;/code&gt;. Swift prefers protocol-oriented composition and value-type models. Use &lt;code&gt;class&lt;/code&gt; only for real reference semantics (long-lived VMs, ARC observers, UIKit subclasses).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;OrdersRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;LiveOrdersRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;OrdersRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;StubOrdersRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;OrdersRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* tests */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Domain models (&lt;code&gt;Order&lt;/code&gt;, &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Money&lt;/code&gt;) are &lt;code&gt;struct&lt;/code&gt; with &lt;code&gt;Codable&lt;/code&gt;/&lt;code&gt;Equatable&lt;/code&gt;/&lt;code&gt;Sendable&lt;/code&gt; where applicable.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. SwiftUI views are small, dumb, and pure
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;View&lt;/code&gt; body that scrolls past one screen is doing too much. Extract &lt;code&gt;@ViewBuilder&lt;/code&gt; helpers, child views, and &lt;code&gt;ViewModifier&lt;/code&gt;s; keep state and side effects in the view model. AI defaults to god-views with inline networking — that path produces non-deterministic previews and untestable UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;OrderRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Order&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;HStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Spacer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monospacedDigit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertical&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&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;No network, no &lt;code&gt;@StateObject&lt;/code&gt;, no business logic in row views. Previews work offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. UIKit interop: &lt;code&gt;UIViewRepresentable&lt;/code&gt; for one widget, not whole flows
&lt;/h2&gt;

&lt;p&gt;When something has to drop to UIKit (camera, mail composer, third-party SDK), wrap &lt;em&gt;that one view&lt;/em&gt; in &lt;code&gt;UIViewRepresentable&lt;/code&gt; or &lt;code&gt;UIViewControllerRepresentable&lt;/code&gt;. AI wraps entire flows and reinvents navigation inside SwiftUI — that produces double nav stacks, broken back buttons, and coordinators that outlive their views.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ScannerView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIViewControllerRepresentable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;onResult&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="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Void&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;makeUIViewController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ScannerViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;vc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ScannerViewController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onResult&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;updateUIViewController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ScannerViewController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Context&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;&lt;code&gt;Coordinator&lt;/code&gt; types exist only when you need a delegate target — and they hold &lt;code&gt;self&lt;/code&gt; weakly.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Dependency injection via initializer — no singletons, no &lt;code&gt;Resolver&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;UserDefaults.standard&lt;/code&gt;, &lt;code&gt;URLSession.shared&lt;/code&gt;, and homemade &lt;code&gt;ServiceLocator&lt;/code&gt;s are the AI's go-to dependencies and they make tests impossible. Inject collaborators through the initializer; default the parameter for production. Sub them out for tests and previews.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;OrdersViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;OrdersRepository&lt;/span&gt;
    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;OrdersRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;LiveOrdersRepository&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&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;&lt;code&gt;@Environment&lt;/code&gt; carries cross-cutting concerns (theme, feature flags); everything else is constructor-injected.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Tests cover async paths with XCTest's async APIs — no &lt;code&gt;XCTestExpectation&lt;/code&gt; for new code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;XCTestExpectation&lt;/code&gt; and &lt;code&gt;wait(for:timeout:)&lt;/code&gt; are how you tested completion handlers in 2019. New tests &lt;code&gt;await&lt;/code&gt; directly. Use &lt;code&gt;XCTUnwrap&lt;/code&gt; instead of &lt;code&gt;!&lt;/code&gt;, snapshot tests for view layouts, and mock at the protocol boundary — not at URLSession.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;OrdersViewModelTests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;XCTestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;test_refresh_populatesOrders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;StubOrdersRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;OrdersViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sut&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kt"&gt;XCTAssertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sut&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;CI runs &lt;code&gt;xcodebuild test -enableThreadSanitizer YES&lt;/code&gt; on a matrix including the lowest supported iOS version.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Build hygiene: SwiftPM, SwiftLint, strict concurrency on
&lt;/h2&gt;

&lt;p&gt;Dependencies live in &lt;code&gt;Package.swift&lt;/code&gt;, pinned to exact or up-to-next-minor versions. SwiftLint runs in a build phase with project rules. Turn on &lt;code&gt;-strict-concurrency=complete&lt;/code&gt; before Swift 6 forces you to. AI will add CocoaPods, disable warnings, and set &lt;code&gt;SWIFT_TREAT_WARNINGS_AS_ERRORS=NO&lt;/code&gt; "to ship" — that's how 200-warning codebases happen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Package.swift&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/apple/swift-collections"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"1.1.0"&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 yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .swiftlint.yml&lt;/span&gt;
&lt;span class="na"&gt;opt_in_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;force_unwrapping&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;implicit_return&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;explicit_init&lt;/span&gt;
&lt;span class="na"&gt;disabled_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;line_length&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Warnings are errors. Force-unwrap is a lint failure. Strict concurrency catches the &lt;code&gt;@MainActor&lt;/code&gt; violation before TestFlight does.&lt;/p&gt;

&lt;h2&gt;
  
  
  A starter &lt;code&gt;CLAUDE.md&lt;/code&gt; snippet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md — iOS app&lt;/span&gt;

&lt;span class="gu"&gt;## Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Swift 5.10+, iOS 16+, SwiftUI primary, UIKit only via Representable
&lt;span class="p"&gt;-&lt;/span&gt; async/await, actors, Combine for legacy publishers, SwiftPM, XCTest

&lt;span class="gu"&gt;## Hard rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`[weak self]`&lt;/span&gt; + &lt;span class="sb"&gt;`guard let self else { return }`&lt;/span&gt; in every escaping closure that uses self.
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`!`&lt;/span&gt;, no &lt;span class="sb"&gt;`try!`&lt;/span&gt; outside tests and IBOutlets. Use &lt;span class="sb"&gt;`guard let`&lt;/span&gt; / &lt;span class="sb"&gt;`do try catch`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; New async APIs are &lt;span class="sb"&gt;`async throws`&lt;/span&gt; or &lt;span class="sb"&gt;`AsyncSequence`&lt;/span&gt;. Bridge legacy via continuation once.
&lt;span class="p"&gt;-&lt;/span&gt; All UI mutations run on &lt;span class="sb"&gt;`@MainActor`&lt;/span&gt;. Heavy work in detached tasks, await result back.
&lt;span class="p"&gt;-&lt;/span&gt; Pick the right wrapper: &lt;span class="sb"&gt;`@State`&lt;/span&gt; value-owned, &lt;span class="sb"&gt;`@StateObject`&lt;/span&gt; view-owned VM, &lt;span class="sb"&gt;`@ObservedObject`&lt;/span&gt; injected VM.
&lt;span class="p"&gt;-&lt;/span&gt; Shared mutable state = &lt;span class="sb"&gt;`actor`&lt;/span&gt;. Locks only for non-async hot paths.
&lt;span class="p"&gt;-&lt;/span&gt; Errors are typed enums conforming to &lt;span class="sb"&gt;`LocalizedError`&lt;/span&gt;. No &lt;span class="sb"&gt;`NSError(domain:)`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Protocols + &lt;span class="sb"&gt;`struct`&lt;/span&gt; first. &lt;span class="sb"&gt;`class`&lt;/span&gt; only for reference semantics.
&lt;span class="p"&gt;-&lt;/span&gt; SwiftUI views: small, pure, no network, previews must work offline.
&lt;span class="p"&gt;-&lt;/span&gt; UIKit via &lt;span class="sb"&gt;`UIViewRepresentable`&lt;/span&gt; for one widget, not whole flows.
&lt;span class="p"&gt;-&lt;/span&gt; DI via initializer. No singletons, no service locators.
&lt;span class="p"&gt;-&lt;/span&gt; Tests use async/await, &lt;span class="sb"&gt;`XCTUnwrap`&lt;/span&gt;, mock at protocol boundary, ThreadSanitizer in CI.
&lt;span class="p"&gt;-&lt;/span&gt; SwiftPM only. SwiftLint enforced. Strict concurrency on. Warnings = errors.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Claude gets wrong without these rules
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Strong-captures &lt;code&gt;self&lt;/code&gt; in Combine sinks → view models leak forever.&lt;/li&gt;
&lt;li&gt;Force-unwraps decoded JSON → first malformed payload crashes the app.&lt;/li&gt;
&lt;li&gt;Updates &lt;code&gt;@Published&lt;/code&gt; from a &lt;code&gt;URLSession&lt;/code&gt; callback → flickers, Swift 6 errors.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;@ObservedObject&lt;/code&gt; instead of &lt;code&gt;@StateObject&lt;/code&gt; → state resets on parent rerender.&lt;/li&gt;
&lt;li&gt;Wraps a UIKit flow in &lt;code&gt;UIViewControllerRepresentable&lt;/code&gt; → double back buttons.&lt;/li&gt;
&lt;li&gt;Reaches for &lt;code&gt;URLSession.shared&lt;/code&gt; everywhere → repository is impossible to test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop the 13 rules above into &lt;code&gt;CLAUDE.md&lt;/code&gt; and the next AI PR looks like an iOS app, not a SwiftUI tutorial. TestFlight stops paging you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want this for 20+ stacks with 200+ rules ready to paste?&lt;/strong&gt; Grab the &lt;strong&gt;CLAUDE.md Rules Pack&lt;/strong&gt; at &lt;strong&gt;&lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;oliviacraftlat.gumroad.com/l/skdgt&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;— Olivia (&lt;a href="https://x.com/OliviaCraftLat" rel="noopener noreferrer"&gt;@OliviaCraftLat&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>swift</category>
      <category>ios</category>
      <category>claudemd</category>
      <category>ai</category>
    </item>
    <item>
      <title>CLAUDE.md for TypeScript and Node.js: 13 Rules That Make AI Write Type-Safe, Production-Ready Code</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Thu, 07 May 2026 10:15:17 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-typescript-and-nodejs-13-rules-that-make-ai-write-type-safe-production-ready-code-47hm</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-typescript-and-nodejs-13-rules-that-make-ai-write-type-safe-production-ready-code-47hm</guid>
      <description>&lt;h1&gt;
  
  
  CLAUDE.md for TypeScript and Node.js: 13 Rules That Make AI Write Type-Safe, Production-Ready Code
&lt;/h1&gt;

&lt;p&gt;TypeScript exists to catch bugs the compiler can prove. Node.js exists to ship them to production fast. When you let an AI assistant write either, the default output drifts toward &lt;code&gt;any&lt;/code&gt;, untyped Express handlers, and validation skipped at the boundary "for now." It compiles. It runs. It explodes when a string shows up where a number was expected.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; at the repo root fixes this. It's the file Claude Code reads on every session — the contract that says: this codebase does not accept loose types, throw strings, or trust &lt;code&gt;req.body&lt;/code&gt;. Below are 13 rules I've shipped in production TypeScript repos that make AI-generated code merge-ready instead of review-ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Strict TypeScript config is non-negotiable
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; AI defaults to permissive &lt;code&gt;tsconfig.json&lt;/code&gt; and silently leans on implicit &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;tsconfig.json&lt;/code&gt; MUST have &lt;code&gt;"strict": true&lt;/code&gt;, &lt;code&gt;"noImplicitAny": true&lt;/code&gt;, &lt;code&gt;"noUncheckedIndexedAccess": true&lt;/code&gt;, and &lt;code&gt;"exactOptionalPropertyTypes": true&lt;/code&gt;. Never relax these flags to make code compile. If a flag fights you, fix the type, not the config.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Strict mode is the contract. Once Claude knows it can't escape via implicit &lt;code&gt;any&lt;/code&gt; or unchecked array access, it generates type guards and narrowing logic up front instead of leaving holes you discover at 2 AM.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Ban &lt;code&gt;any&lt;/code&gt; — use &lt;code&gt;unknown&lt;/code&gt; plus type guards
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; When Claude doesn't know a shape, it reaches for &lt;code&gt;any&lt;/code&gt;. That single keyword erases every type guarantee downstream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;any&lt;/code&gt; is forbidden. Use &lt;code&gt;unknown&lt;/code&gt; for values of unknown shape and narrow with type guards (&lt;code&gt;typeof&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;, custom predicates) or zod parsing. The only acceptable &lt;code&gt;any&lt;/code&gt; is in a &lt;code&gt;// @ts-expect-error&lt;/code&gt; block with a written justification.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; &lt;code&gt;unknown&lt;/code&gt; forces the next reader (or AI iteration) to prove the shape before using it. Type guards become the documentation. The codebase stops accumulating silent escape hatches.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Branded types for IDs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; &lt;code&gt;function transfer(from: string, to: string, amount: number)&lt;/code&gt; — AI happily swaps &lt;code&gt;from&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; because the compiler sees three primitives, not three roles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All entity IDs must be branded types: &lt;code&gt;type UserId = string &amp;amp; { readonly __brand: "UserId" }&lt;/code&gt;. Construct via a single &lt;code&gt;toUserId()&lt;/code&gt; factory. Never pass raw strings to functions expecting an ID. Same rule for currency amounts, timestamps, and slugs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Branded types are zero-cost at runtime but make ID-swap bugs uncompilable. Claude follows the pattern once it sees one branded type defined — it'll brand new IDs without being asked.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. &lt;code&gt;async/await&lt;/code&gt; only — no Promise chains
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; AI freely mixes &lt;code&gt;.then()&lt;/code&gt; chains with &lt;code&gt;await&lt;/code&gt;, producing code where error paths and return values diverge between styles in the same file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;async/await&lt;/code&gt; exclusively. No &lt;code&gt;.then()&lt;/code&gt;, &lt;code&gt;.catch()&lt;/code&gt;, or &lt;code&gt;.finally()&lt;/code&gt; chains in application code. The only exception is &lt;code&gt;Promise.all&lt;/code&gt; / &lt;code&gt;Promise.allSettled&lt;/code&gt; for concurrency. Errors propagate via &lt;code&gt;try/catch&lt;/code&gt;, not &lt;code&gt;.catch()&lt;/code&gt; callbacks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; One async style means one error-handling story. Stack traces become readable. Refactors stop introducing race conditions where a &lt;code&gt;.then()&lt;/code&gt; was missed.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Validate inputs with zod at every boundary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; AI trusts incoming data because TypeScript types are erased at runtime. &lt;code&gt;req.body&lt;/code&gt; is typed as your DTO, but it's actually whatever the client sent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every external input — HTTP requests, environment variables, JSON files, message queue payloads — must be parsed through a zod schema at the boundary. The parsed result is the only typed value that flows inward. No casting &lt;code&gt;req.body as CreateUserDto&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; zod gives you the type AND the runtime check from one declaration. Once Claude sees a &lt;code&gt;CreateUserSchema.parse(req.body)&lt;/code&gt; pattern, it replicates it on every new endpoint instead of trusting incoming JSON.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Typed errors, never thrown strings
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; &lt;code&gt;throw "user not found"&lt;/code&gt; — Claude does this when it's in a hurry. Catch blocks then need &lt;code&gt;String(err)&lt;/code&gt; everywhere and the caller can't distinguish a NotFound from a Forbidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Errors must be class instances extending a base &lt;code&gt;AppError&lt;/code&gt; with a discriminant &lt;code&gt;code&lt;/code&gt; field. Never &lt;code&gt;throw&lt;/code&gt; a string, number, or plain object. Catch blocks narrow on &lt;code&gt;err instanceof AppError&lt;/code&gt; and switch on &lt;code&gt;code&lt;/code&gt;. Unknown errors rethrow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Typed errors give you exhaustiveness checks in switch statements. Logging becomes structured. The HTTP layer maps &lt;code&gt;code&lt;/code&gt; → status without sniffing error messages with regex.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Barrel exports — use sparingly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; AI loves &lt;code&gt;index.ts&lt;/code&gt; files re-exporting everything. They look tidy but break tree-shaking and create circular dependency landmines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No barrel &lt;code&gt;index.ts&lt;/code&gt; files inside feature modules. Import from the specific file: &lt;code&gt;import { User } from "./user/user.entity"&lt;/code&gt;, not &lt;code&gt;from "./user"&lt;/code&gt;. The only barrels allowed are at package boundaries (&lt;code&gt;packages/*/src/index.ts&lt;/code&gt;) and must be manually curated, not auto-generated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Direct imports keep the dependency graph shallow and make circular imports a compile error you can locate. Bundlers ship less code. Refactors don't cascade through unrelated modules.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Validate environment variables at boot
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; &lt;code&gt;process.env.DATABASE_URL!&lt;/code&gt; — that &lt;code&gt;!&lt;/code&gt; is a lie. AI uses it because it shuts the compiler up. The app boots, reaches for the URL during the first request, and crashes with &lt;code&gt;undefined is not a function&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All environment access goes through a single &lt;code&gt;env.ts&lt;/code&gt; module that parses &lt;code&gt;process.env&lt;/code&gt; with zod at startup and exits the process if validation fails. No &lt;code&gt;process.env.X&lt;/code&gt; references anywhere else in the codebase. No non-null assertions on env vars.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; The app fails to start instead of failing in production. The schema is the documentation of what your service needs. New team members (and Claude) can see the full env contract in one file.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Tests use real types — no &lt;code&gt;ts-ignore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; AI reaches for &lt;code&gt;// @ts-ignore&lt;/code&gt; in tests when mocking gets awkward. The test passes, the production type drifts, and the test no longer proves anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Test files must compile under the same strict config as production code. No &lt;code&gt;@ts-ignore&lt;/code&gt;, no &lt;code&gt;as any&lt;/code&gt;, no &lt;code&gt;as unknown as Foo&lt;/code&gt;. Use proper test factories that return correctly typed fixtures. If a mock is hard to type, the production interface is wrong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Tests become a second pass of type-checking against your real shapes. When a refactor breaks a test type, you caught a real regression before runtime did.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Explicit return types on exported functions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Claude relies on inference, which means changing an internal helper silently changes the public API of a module.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All exported functions, route handlers, and class methods must declare explicit return types. Inference is allowed only for local variables and arrow callbacks. Async functions return &lt;code&gt;Promise&amp;lt;T&amp;gt;&lt;/code&gt; explicitly, not &lt;code&gt;Promise&amp;lt;void&amp;gt;&lt;/code&gt; by accident.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Explicit return types lock the contract. A change inside the function body that would alter the inferred return becomes a compile error at the signature, where the breaking change is visible in code review.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Dependency injection over direct imports
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; &lt;code&gt;import { db } from "./db"&lt;/code&gt; inside business logic. Now every test needs to mock the module system, and swapping the database in staging means editing application code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Business logic receives dependencies via constructor parameters or function arguments — never imported directly. Side-effect modules (db, http clients, loggers, clocks) are wired in &lt;code&gt;main.ts&lt;/code&gt; / &lt;code&gt;server.ts&lt;/code&gt;. Domain functions take typed interfaces, not concrete implementations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Tests pass in fakes without &lt;code&gt;jest.mock&lt;/code&gt; gymnastics. The dependency graph becomes explicit in function signatures. Claude stops generating code that's only testable via runtime monkey-patching.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Typed request and response in Express/Fastify
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; &lt;code&gt;app.post("/users", (req, res) =&amp;gt; { const body = req.body; ... })&lt;/code&gt; — &lt;code&gt;body&lt;/code&gt; is &lt;code&gt;any&lt;/code&gt;. AI proceeds as if it weren't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every route handler must declare typed &lt;code&gt;Request&lt;/code&gt; and &lt;code&gt;Response&lt;/code&gt; generics (Express: &lt;code&gt;Request&amp;lt;Params, ResBody, ReqBody, Query&amp;gt;&lt;/code&gt;; Fastify: schema-typed routes). Bodies, params, and queries are parsed through zod inside the handler. &lt;code&gt;res.json()&lt;/code&gt; payloads are constrained by the response type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; The HTTP boundary stops being a type hole. Auto-generated OpenAPI docs (via zod-to-openapi or TypeBox) become trustworthy. Frontend clients consuming the API get end-to-end type safety.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Path aliases instead of &lt;code&gt;../../../../&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; AI generates &lt;code&gt;import { foo } from "../../../../utils/foo"&lt;/code&gt; because that's what the file system suggests. Move the file once and every import breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md instruction:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;tsconfig.json&lt;/code&gt; &lt;code&gt;paths&lt;/code&gt; aliases for cross-feature imports: &lt;code&gt;@/lib/*&lt;/code&gt;, &lt;code&gt;@/features/*&lt;/code&gt;, &lt;code&gt;@/shared/*&lt;/code&gt;. Relative imports allowed only within the same feature folder (max one &lt;code&gt;../&lt;/code&gt;). Configure &lt;code&gt;tsc-alias&lt;/code&gt; or your bundler so the aliases resolve at build time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Aliases survive refactors. Imports become readable. The visual depth of &lt;code&gt;../../../..&lt;/code&gt; no longer hides architectural problems — when you see a cross-feature import, you can name the boundary it crosses.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;These 13 rules share one premise: TypeScript's value is the contract, not the syntax. AI assistants will happily satisfy the syntax and break the contract. &lt;code&gt;CLAUDE.md&lt;/code&gt; is where you write down the contract in language the AI reads on every session, so it stops "helpfully" reaching for &lt;code&gt;any&lt;/code&gt;, &lt;code&gt;as&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;, and &lt;code&gt;@ts-ignore&lt;/code&gt; to make red squiggles disappear.&lt;/p&gt;

&lt;p&gt;Drop these rules into your repo, watch the next AI-generated PR, and notice what doesn't show up: untyped handlers, unvalidated bodies, swapped IDs, and &lt;code&gt;process.env.WHATEVER!&lt;/code&gt; scattered across the codebase.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Want the full Gist with these 13 rules ready to paste into your project?&lt;/strong&gt; Grab it here → &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;oliviacraftlat.gumroad.com/l/skdgt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free, no email gate, MIT-licensed. Fork it, adapt it, ship it.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>claudemd</category>
      <category>ai</category>
    </item>
    <item>
      <title>CLAUDE.md for Rust: 13 Rules That Make AI Write Safe, Idiomatic Systems Code</title>
      <dc:creator>Olivia Craft</dc:creator>
      <pubDate>Thu, 07 May 2026 08:25:58 +0000</pubDate>
      <link>https://dev.to/olivia_craft/claudemd-for-rust-13-rules-that-make-ai-write-safe-idiomatic-systems-code-50n4</link>
      <guid>https://dev.to/olivia_craft/claudemd-for-rust-13-rules-that-make-ai-write-safe-idiomatic-systems-code-50n4</guid>
      <description>&lt;p&gt;Ask Claude Code to "add a small async cache to this crate" and the default output looks fine: a &lt;code&gt;tokio::spawn&lt;/code&gt;, an &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;HashMap&amp;lt;...&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;, a &lt;code&gt;.unwrap()&lt;/code&gt; on the lock, a &lt;code&gt;?&lt;/code&gt; that flattens five error types into &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt;. It compiles. It passes &lt;code&gt;cargo test&lt;/code&gt;. Then it deadlocks across &lt;code&gt;.await&lt;/code&gt;, panics on the first miss in prod, and the error reads &lt;code&gt;Custom { kind: Other, error: "..." }&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The borrow checker stops a lot, but it doesn't stop &lt;code&gt;unwrap()&lt;/code&gt;, fire-and-forget tasks, or &lt;code&gt;unsafe&lt;/code&gt; blocks with no &lt;code&gt;// SAFETY:&lt;/code&gt; comment. A &lt;code&gt;CLAUDE.md&lt;/code&gt; next to &lt;code&gt;Cargo.toml&lt;/code&gt; is the cheapest leverage you have on a Rust codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get the full CLAUDE.md Rules Pack — &lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;oliviacraftlat.gumroad.com/l/skdgt&lt;/a&gt;&lt;/strong&gt;. The 13 rules below are a free preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Own at the API boundary, borrow inside
&lt;/h2&gt;

&lt;p&gt;The most common AI mistake in Rust public APIs is leaking lifetimes: &lt;code&gt;pub fn parse&amp;lt;'a&amp;gt;(s: &amp;amp;'a str) -&amp;gt; Doc&amp;lt;'a&amp;gt;&lt;/code&gt; when the caller wants an owned &lt;code&gt;Doc&lt;/code&gt;. Public signatures take and return owned types (&lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;PathBuf&lt;/code&gt;); borrowing stays inside the impl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ owned in, owned out — caller lifetimes don't leak in&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ParseError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ AI default — every caller now juggles 'a&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Doc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ParseError&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;Cow&amp;lt;'_, str&amp;gt;&lt;/code&gt; only when the zero-copy path actually matters &lt;em&gt;and&lt;/em&gt; you've measured. Default: &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Errors: &lt;code&gt;thiserror&lt;/code&gt; for libraries, &lt;code&gt;anyhow&lt;/code&gt; for binaries
&lt;/h2&gt;

&lt;p&gt;Libraries return a typed &lt;code&gt;enum&lt;/code&gt; deriving &lt;code&gt;thiserror::Error&lt;/code&gt; — &lt;code&gt;#[from]&lt;/code&gt; for transparent conversion, &lt;code&gt;#[source]&lt;/code&gt; for chains. Binaries return &lt;code&gt;anyhow::Result&amp;lt;T&amp;gt;&lt;/code&gt; with &lt;code&gt;.with_context(|| ...)?&lt;/code&gt; at every I/O boundary. Mixing them — &lt;code&gt;anyhow&lt;/code&gt; in &lt;code&gt;lib.rs&lt;/code&gt;, &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt; elsewhere — strips callers of any way to branch on the error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;thiserror::Error)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;StoreError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"user {0} not found"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"database error"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;Db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;#[from]&lt;/span&gt; &lt;span class="nn"&gt;sqlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.with_context&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"opening {path:?}"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&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;No &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt; in &lt;code&gt;pub&lt;/code&gt; signatures.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;unsafe&lt;/code&gt; is forbidden by default — opt in with &lt;code&gt;// SAFETY:&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;#![deny(unsafe_code)]&lt;/code&gt; lives at every crate root. When a module genuinely needs &lt;code&gt;unsafe&lt;/code&gt;, scope it with &lt;code&gt;#![allow(unsafe_code)]&lt;/code&gt; at the top of &lt;em&gt;that file only&lt;/em&gt;. Every &lt;code&gt;unsafe { ... }&lt;/code&gt; block has a &lt;code&gt;// SAFETY:&lt;/code&gt; comment explaining which invariants hold; every &lt;code&gt;unsafe fn&lt;/code&gt; documents its preconditions in a &lt;code&gt;# Safety&lt;/code&gt; doc section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SAFETY: `ptr` came from `Box::into_raw` above, untouched since; non-null,&lt;/span&gt;
&lt;span class="c1"&gt;// properly aligned, and we own the allocation.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&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;Hard to write the comment? The &lt;code&gt;unsafe&lt;/code&gt; is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. No &lt;code&gt;unwrap()&lt;/code&gt; or &lt;code&gt;expect()&lt;/code&gt; in production paths
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;unwrap()&lt;/code&gt; is a panic with no message. CI greps for &lt;code&gt;\.unwrap\(\)&lt;/code&gt; and &lt;code&gt;\.expect\(&lt;/code&gt; on &lt;code&gt;*.rs&lt;/code&gt; outside &lt;code&gt;tests/&lt;/code&gt;, &lt;code&gt;examples/&lt;/code&gt;, &lt;code&gt;benches/&lt;/code&gt; and fails the build. &lt;code&gt;expect("...")&lt;/code&gt; is allowed only when the message documents an invariant the type system can't express — "checked above by the regex match," not "should never fail."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ✅&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT must be set"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT must be a u16"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Library code: return a typed error. Binaries: &lt;code&gt;anyhow&lt;/code&gt; it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. No &lt;code&gt;panic!&lt;/code&gt;, &lt;code&gt;todo!&lt;/code&gt;, &lt;code&gt;unimplemented!&lt;/code&gt; reachable from &lt;code&gt;pub&lt;/code&gt; APIs
&lt;/h2&gt;

&lt;p&gt;Library code returns &lt;code&gt;Result&lt;/code&gt;; it does not panic for runtime conditions. &lt;code&gt;todo!()&lt;/code&gt; and &lt;code&gt;unimplemented!()&lt;/code&gt; are scaffolding — CI fails if any reach a &lt;code&gt;pub&lt;/code&gt; item. Indexing user-controlled offsets panics on miss — prefer &lt;code&gt;.get(i)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;first_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.split_whitespace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;     &lt;span class="c1"&gt;// ❌&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;first_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.split_whitespace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="c1"&gt;// ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Untrusted integer math uses &lt;code&gt;checked_*&lt;/code&gt;/&lt;code&gt;saturating_*&lt;/code&gt;/&lt;code&gt;wrapping_*&lt;/code&gt; explicitly — debug-only overflow checks are not a security boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Async: one runtime, no &lt;code&gt;Mutex&lt;/code&gt; guard across &lt;code&gt;.await&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One async runtime per process — &lt;code&gt;tokio&lt;/code&gt;, multi-thread for servers, &lt;code&gt;current_thread&lt;/code&gt; for CLIs and tests. Never hold a &lt;code&gt;std::sync::Mutex&lt;/code&gt; guard across &lt;code&gt;.await&lt;/code&gt;: the compiler may not catch it; the runtime deadlock will. Use &lt;code&gt;tokio::sync::Mutex&lt;/code&gt; when the guard must span an await, or restructure to drop it first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ blocks the runtime, can deadlock&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ scope the std lock, then await&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spawned tasks keep their &lt;code&gt;JoinHandle&lt;/code&gt; and are awaited or aborted on shutdown. Long loops honor cancellation via &lt;code&gt;tokio::select!&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Logging is &lt;code&gt;tracing&lt;/code&gt;, structured, never &lt;code&gt;println!&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;println!&lt;/code&gt; and &lt;code&gt;eprintln!&lt;/code&gt; are forbidden in library code. Use &lt;code&gt;tracing&lt;/code&gt; with structured fields and &lt;code&gt;#[tracing::instrument(skip(secrets))]&lt;/code&gt; on functions whose arguments include credentials or large payloads. Levels: &lt;code&gt;debug&lt;/code&gt; opt-in, &lt;code&gt;info&lt;/code&gt; steady state, &lt;code&gt;warn&lt;/code&gt; needs human follow-up, &lt;code&gt;error&lt;/code&gt; pages someone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[tracing::instrument(skip(pool),&lt;/span&gt; &lt;span class="nd"&gt;fields(user_id&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nd"&gt;id))]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PgPool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ChargeError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"charging user"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&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;JSON output in production. Never log secrets or PII — &lt;code&gt;#[serde(skip)]&lt;/code&gt; secret fields, redact at the boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Module visibility is intentional — &lt;code&gt;pub&lt;/code&gt; is a contract
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pub&lt;/code&gt; items are part of the crate's semver contract. Default everything to private; promote to &lt;code&gt;pub(crate)&lt;/code&gt; when another module needs it; mark &lt;code&gt;pub&lt;/code&gt; only what you commit to keeping. AI defaults to &lt;code&gt;pub&lt;/code&gt; on every new &lt;code&gt;fn&lt;/code&gt; and &lt;code&gt;struct&lt;/code&gt; — every refactor becomes a breaking change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;normalize_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PathBuf&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ scoped&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;                                  &lt;span class="c1"&gt;// ✅ semver applies&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;internal_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;          &lt;span class="c1"&gt;// ❌ accidental commitment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-export the public surface from &lt;code&gt;lib.rs&lt;/code&gt;. Module trees are implementation detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Custom error types — &lt;code&gt;Display&lt;/code&gt; reads like a sentence
&lt;/h2&gt;

&lt;p&gt;A typed error enum lives close to the type that produces it. Its &lt;code&gt;Display&lt;/code&gt; is one short lowercase sentence — no trailing period, no leading "Error:" — &lt;code&gt;tracing&lt;/code&gt; and &lt;code&gt;anyhow&lt;/code&gt; frame it. Never &lt;code&gt;match&lt;/code&gt; on error strings; the type system is right there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;thiserror::Error)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ConfigError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"config file not found at {0}"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;Missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"invalid TOML in {path}: {source}"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Parse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;#[source]&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;toml&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&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;Branch with &lt;code&gt;if let ConfigError::Missing(p) = err&lt;/code&gt;, never &lt;code&gt;err.to_string().contains(...)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Tests: unit inside the module, integration in &lt;code&gt;tests/&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Unit tests live in a &lt;code&gt;#[cfg(test)] mod tests { use super::*; ... }&lt;/code&gt; block — they touch private items. Integration tests live in &lt;code&gt;tests/&lt;/code&gt;; each &lt;code&gt;tests/foo.rs&lt;/code&gt; is a separate crate that imports only the public API. That split catches "I made it &lt;code&gt;pub(crate)&lt;/code&gt; but the integration test can't see it" before the user does.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/parse.rs&lt;/span&gt;
&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;#[test]&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tokenize&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="nf"&gt;.is_empty&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="c1"&gt;// tests/end_to_end.rs — only public API&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;mycrate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;#[test]&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parses_a_real_doc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAMPLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&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;Async tests use &lt;code&gt;#[tokio::test]&lt;/code&gt;. Repository tests run against a real database via &lt;code&gt;testcontainers&lt;/code&gt; — mocked queries pass while broken SQL ships. CI runs &lt;code&gt;cargo test&lt;/code&gt; with and without &lt;code&gt;--all-features&lt;/code&gt; so feature-gated code compiles both ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. &lt;code&gt;clippy::pedantic&lt;/code&gt; clean, no blanket &lt;code&gt;#[allow]&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Crate root: &lt;code&gt;#![warn(clippy::pedantic, clippy::nursery, missing_docs, rust_2018_idioms)]&lt;/code&gt;. CI runs &lt;code&gt;cargo clippy --all-targets --all-features -- -D warnings&lt;/code&gt; and &lt;code&gt;cargo fmt -- --check&lt;/code&gt;. Warnings are errors. No blanket &lt;code&gt;#[allow]&lt;/code&gt; — narrow allows on the offending item only, with a comment explaining why.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ scoped, justified&lt;/span&gt;
&lt;span class="nd"&gt;#[allow(clippy::cast_possible_truncation)]&lt;/span&gt; &lt;span class="c1"&gt;// bounded above by MAX_FRAME (u32::MAX)&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;frame_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;FRAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can't justify it in a sentence? Fix the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Workspace structure: thin binary, fat library, one &lt;code&gt;Cargo.lock&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;An 800-line &lt;code&gt;src/main.rs&lt;/code&gt; is an AI smell. The library does the work and exposes a typed API; the binary parses arguments, builds config, and calls the library. Multi-crate workspaces share one &lt;code&gt;Cargo.lock&lt;/code&gt; at the root and pin shared deps under &lt;code&gt;[workspace.dependencies]&lt;/code&gt; so members can't drift.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Cargo.toml (workspace root)&lt;/span&gt;
&lt;span class="nn"&gt;[workspace]&lt;/span&gt;
&lt;span class="py"&gt;members&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"crates/core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"crates/cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"crates/wasm"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;resolver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;

&lt;span class="nn"&gt;[workspace.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;tokio&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;default-features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"rt-multi-thread"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"macros"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;serde&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"derive"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;tracing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Library crates set &lt;code&gt;default-features = false&lt;/code&gt; on every dep. WASM targets gate &lt;code&gt;Instant&lt;/code&gt;, threads, and blocking I/O behind &lt;code&gt;#[cfg(not(target_arch = "wasm32"))]&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Docs with examples that compile
&lt;/h2&gt;

&lt;p&gt;Every &lt;code&gt;pub&lt;/code&gt; item carries a &lt;code&gt;///&lt;/code&gt; summary on the first line. Functions returning &lt;code&gt;Result&lt;/code&gt; document &lt;code&gt;# Errors&lt;/code&gt;; functions that can panic document &lt;code&gt;# Panics&lt;/code&gt;. Every &lt;code&gt;pub fn&lt;/code&gt; has a &lt;code&gt;# Examples&lt;/code&gt; block — &lt;code&gt;cargo test --doc&lt;/code&gt; runs it. Doc-tests are the canary for API drift.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// Parses a TOML config file from `path`.&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// # Errors&lt;/span&gt;
&lt;span class="cd"&gt;/// [`ConfigError::Missing`] if `path` doesn't exist;&lt;/span&gt;
&lt;span class="cd"&gt;/// [`ConfigError::Parse`] if the file is not valid TOML.&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// # Examples&lt;/span&gt;
&lt;span class="cd"&gt;/// ```&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// # use mycrate::{load_config, ConfigError};&lt;/span&gt;
&lt;span class="cd"&gt;/// let cfg = load_config("examples/sample.toml")?;&lt;/span&gt;
&lt;span class="cd"&gt;/// assert_eq!(cfg.port, 8080);&lt;/span&gt;
&lt;span class="cd"&gt;/// # Ok::&amp;lt;(), ConfigError&amp;gt;(())&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;pub fn load_config(path: impl AsRef) -&amp;gt; Result { ... }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


CI runs `cargo doc --no-deps -- -D warnings`. Missing docs and broken intra-doc links fail the build.

## A starter `CLAUDE.md` snippet



```markdown
# CLAUDE.md — Rust crate

## Stack
- Rust stable, edition 2021, MSRV pinned in `Cargo.toml`
- thiserror, anyhow, tokio, tracing, sqlx, testcontainers

## Hard rules
- Public APIs take/return owned types. Borrow inside the impl.
- Libs: `thiserror` enum errors. Bins: `anyhow::Result` + `.context(...)`.
- `#![deny(unsafe_code)]` at crate root. `unsafe` blocks need `// SAFETY:`.
- No `unwrap()`/`expect()` outside tests. CI greps and fails.
- No `panic!`/`todo!`/`unimplemented!` reachable from `pub` APIs.
- One async runtime. Never hold `std::sync::Mutex` across `.await`.
- `tracing` for logging, JSON in prod. No `println!` in libs.
- Default-private. `pub(crate)` before `pub`. `pub` = semver commitment.
- Unit tests in-module, integration in `tests/`. Real DB via testcontainers.
- `cargo clippy --all-targets --all-features -- -D warnings` clean.
- Thin binary, fat library. One `Cargo.lock` at workspace root.
- Every `pub` item: `///` summary, `# Errors`/`# Panics`/`# Examples`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Claude gets wrong without these rules
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;unwrap()&lt;/code&gt; and &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt; everywhere — every error becomes a string.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;std::sync::Mutex&lt;/code&gt; guard held across &lt;code&gt;.await&lt;/code&gt; — runtime deadlock.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tokio::spawn&lt;/code&gt; with no &lt;code&gt;JoinHandle&lt;/code&gt; — tasks outlive the request.&lt;/li&gt;
&lt;li&gt;Every new fn marked &lt;code&gt;pub&lt;/code&gt; — every refactor breaks semver.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unsafe&lt;/code&gt; blocks with no &lt;code&gt;// SAFETY:&lt;/code&gt; and stale assumptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop the 13 rules above into &lt;code&gt;CLAUDE.md&lt;/code&gt; and the next AI PR looks like the codebase, not a tutorial. &lt;code&gt;cargo clippy -- -D warnings&lt;/code&gt; stays green.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want this for 20+ stacks with 200+ rules ready to paste?&lt;/strong&gt; Grab the &lt;strong&gt;CLAUDE.md Rules Pack&lt;/strong&gt; at &lt;strong&gt;&lt;a href="https://oliviacraftlat.gumroad.com/l/skdgt" rel="noopener noreferrer"&gt;oliviacraftlat.gumroad.com/l/skdgt&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;— Olivia (&lt;a href="https://x.com/OliviaCraftLat" rel="noopener noreferrer"&gt;@OliviaCraftLat&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>rust</category>
      <category>claudemd</category>
      <category>ai</category>
      <category>claudeai</category>
    </item>
  </channel>
</rss>
