<?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: Gnah</title>
    <description>The latest articles on DEV Community by Gnah (@gnah).</description>
    <link>https://dev.to/gnah</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%2F3849913%2Faa51cc08-4279-43f9-9863-e55ae7c73de0.png</url>
      <title>DEV Community: Gnah</title>
      <link>https://dev.to/gnah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gnah"/>
    <language>en</language>
    <item>
      <title>What AI Assistants Don't Know About Your .NET Stack</title>
      <dc:creator>Gnah</dc:creator>
      <pubDate>Sun, 29 Mar 2026 19:26:32 +0000</pubDate>
      <link>https://dev.to/gnah/what-ai-assistants-dont-know-about-your-net-stack-c56</link>
      <guid>https://dev.to/gnah/what-ai-assistants-dont-know-about-your-net-stack-c56</guid>
      <description>&lt;h1&gt;
  
  
  What AI Assistants Don't Know About Your .NET Stack (And Will Happily Reproduce)
&lt;/h1&gt;




&lt;p&gt;I've been doing .NET/WPF/SQL Server development for years. A few months ago I started experimenting with AI-assisted development — Claude Code, Cursor, that kind of thing. Not a full switch, just plugging them into my workflow and seeing where they helped and where they got me into trouble.&lt;/p&gt;

&lt;p&gt;Here's what I didn't expect: the AI assistants reproduce the exact same silent bugs that junior devs write. Not because they're dumb — they're genuinely impressive — but because they don't know your stack's quirks unless you tell them explicitly. They generate plausible, compilable, sometimes even elegant code that contains time-bombs.&lt;/p&gt;

&lt;p&gt;So I started documenting these. After a few months I had about 70 of them, which I've packaged into Claude Code and Cursor config files I use on every project. Here are seven of the nastiest ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. &lt;code&gt;ConfigureAwait(false)&lt;/code&gt; in a ViewModel silently kills your bindings
&lt;/h2&gt;

&lt;p&gt;You know the rule: use &lt;code&gt;ConfigureAwait(false)&lt;/code&gt; in library/service code. AI assistants know this rule too — so they apply it &lt;em&gt;everywhere&lt;/em&gt;, including ViewModels.&lt;/p&gt;

&lt;p&gt;The problem: if your ViewModel does &lt;code&gt;await SomeService().ConfigureAwait(false)&lt;/code&gt;, the continuation runs on a thread pool thread. Setting an &lt;code&gt;ObservableCollection&lt;/code&gt; from there throws &lt;code&gt;NotSupportedException&lt;/code&gt;. WPF swallows it internally. Your data never appears. No exception, no log, nothing in the output window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Services → &lt;code&gt;ConfigureAwait(false)&lt;/code&gt;, fine. ViewModels that set bound properties → never. This is one of those rules that needs to be in your AI's context, because it will confidently do the wrong thing every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. EF Core migration without &lt;code&gt;.Designer.cs&lt;/code&gt; — &lt;code&gt;MigrateAsync&lt;/code&gt; returns success and does nothing
&lt;/h2&gt;

&lt;p&gt;Missing the companion &lt;code&gt;.Designer.cs&lt;/code&gt; file for a migration? &lt;code&gt;MigrateAsync&lt;/code&gt; reports success. No exception. Schema is never updated. You find out in prod when your app crashes on a missing column.&lt;/p&gt;

&lt;p&gt;The AI generates migration classes fine. It doesn't always remind you that the designer file needs to exist too, and won't warn you when it's absent. &lt;code&gt;MigrateAsync&lt;/code&gt; treats it as "nothing to do."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always generate both files. Always check both exist before shipping. I added a startup assertion for this.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Multi-user + &lt;code&gt;SqlException&lt;/code&gt; 2714 = all pending migrations silently marked as applied
&lt;/h2&gt;

&lt;p&gt;This one burned us in production.&lt;/p&gt;

&lt;p&gt;User 1 runs the app, migrations apply fine. User 2 starts the app a few seconds later. They hit error 2714 ("object already exists") on a &lt;code&gt;CREATE TABLE&lt;/code&gt; that User 1 already created. Your exception handler catches it, figures the table exists, marks the migration as applied. Fine so far.&lt;/p&gt;

&lt;p&gt;Except &lt;code&gt;ALTER TABLE&lt;/code&gt; migrations after that one never run. The migration runner doesn't retry them — it assumes they're done because the batch was "handled." Missing columns. Every subsequent user is broken in ways that are very hard to diagnose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Pre-migration safety SQL that checks &lt;em&gt;all expected columns actually exist&lt;/em&gt; before calling &lt;code&gt;MigrateAsync&lt;/code&gt;. Don't trust the migration history table alone in multi-user scenarios.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. &lt;code&gt;_ = SomeAsync()&lt;/code&gt; swallows every exception
&lt;/h2&gt;

&lt;p&gt;Fire-and-forget looks clean. The AI loves it — it's concise, it compiles, it runs.&lt;/p&gt;

&lt;p&gt;Every exception thrown inside disappears. No log, no crash, no trace in any debugger. You find out weeks later when data is missing or an operation silently never completed. I've seen this pattern generated automatically for background cache refreshes, telemetry, cleanup jobs — exactly the places where you &lt;em&gt;need&lt;/em&gt; to know when things break.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Implement a &lt;code&gt;SafeFireAndForget&lt;/code&gt; extension that wraps the task, adds a continuation, and logs exceptions. Or use &lt;code&gt;Task.Run&lt;/code&gt; with an explicit &lt;code&gt;try/catch&lt;/code&gt;. Either way, never let the discard operator be your error handler.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. &lt;code&gt;ComboBox.SelectedItem&lt;/code&gt; — reference equality, not value equality
&lt;/h2&gt;

&lt;p&gt;Your binding is correct. The &lt;code&gt;SelectedItem&lt;/code&gt; is set in code. Nothing is selected in the UI.&lt;/p&gt;

&lt;p&gt;WPF compares object references, not values. If you load items from a DB into &lt;code&gt;ItemsSource&lt;/code&gt;, then separately reconstruct a "matching" object and assign it to &lt;code&gt;SelectedItem&lt;/code&gt;, WPF doesn't find a match. The objects are equal by value, but not by reference.&lt;/p&gt;

&lt;p&gt;The AI will generate this pattern naturally — it's what you'd write if you didn't know WPF internals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; After loading your collection, find the target item &lt;em&gt;within&lt;/em&gt; &lt;code&gt;ItemsSource&lt;/code&gt; by ID and assign that exact reference. Never assign a reconstructed object.&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;SelectedItem&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;FirstOrDefault&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;=&amp;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;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;loadedId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple fix, maddening to debug if you don't know why.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. &lt;code&gt;@@IDENTITY&lt;/code&gt; returns the trigger's insert, not yours
&lt;/h2&gt;

&lt;p&gt;Classic SQL Server trap that predates AI assistants by decades — but they reproduce it faithfully.&lt;/p&gt;

&lt;p&gt;Any trigger that inserts into another table resets &lt;code&gt;@@IDENTITY&lt;/code&gt; to &lt;em&gt;that&lt;/em&gt; table's identity value. You get back the wrong ID and silently associate records to the wrong parent. This affects roughly half the legacy stored procedures I've worked with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always use &lt;code&gt;SCOPE_IDENTITY()&lt;/code&gt; or the &lt;code&gt;OUTPUT&lt;/code&gt; clause in new code. Always. The AI doesn't know about your triggers unless you tell it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Wrong&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;IDENTITY&lt;/span&gt;

&lt;span class="c1"&gt;-- Right&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;SCOPE_IDENTITY&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. AI agent re-spawns cost 20–40K tokens each
&lt;/h2&gt;

&lt;p&gt;This one's specific to Claude Code with agent teams, but if you're going in that direction, it matters.&lt;/p&gt;

&lt;p&gt;Every time you spawn a new &lt;code&gt;Agent()&lt;/code&gt; instead of sending a message to an existing one, you burn 20–40K tokens for re-contextualization. That agent needs to re-read the codebase, the instructions, the state. Four unnecessary re-spawns in a session can cost you 160K tokens — before you've written a line.&lt;/p&gt;

&lt;p&gt;I hit this hard when I started automating multi-step workflows. The pattern that works: &lt;code&gt;TeamCreate&lt;/code&gt; at the start of a sprint, then &lt;code&gt;SendMessage&lt;/code&gt; to assign tasks to existing agents. Never re-spawn what you can message.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pattern behind all of these
&lt;/h2&gt;

&lt;p&gt;None of these are obscure. They're well-documented if you know to look. The issue is that AI assistants generate code from patterns, and these patterns &lt;em&gt;look right&lt;/em&gt; — they compile, they follow conventions, they pass code review if your reviewer doesn't know the specific trap.&lt;/p&gt;

&lt;p&gt;The fix isn't to distrust AI assistants. It's to make their context explicit. When I started encoding these gotchas into my Claude Code and Cursor configs — as named rules the assistant loads before generating anything — the failure rate dropped significantly. The assistant doesn't need to rediscover that &lt;code&gt;@@IDENTITY&lt;/code&gt; is unreliable if you've told it once, globally.&lt;/p&gt;

&lt;p&gt;I've got about 70 of these accumulated now, covering async patterns, WPF bindings, SQL Server edge cases, EF Core pitfalls, and agent orchestration. Packaged as reusable config files that I drop into any project.&lt;/p&gt;




&lt;p&gt;What are yours? Drop them in the comments — I'm particularly curious about anything SQL Server or WPF-specific that's bitten you with AI-generated code. The more we document these, the better our configs get.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>llm</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
