<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://frankhaugen.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://frankhaugen.github.io/" rel="alternate" type="text/html" /><updated>2026-05-02T15:15:50+00:00</updated><id>https://frankhaugen.github.io/feed.xml</id><title type="html">Frank Haugen</title><subtitle>Open-source .NET libraries, compilers, and tooling — plus a dev log.</subtitle><author><name>Frank R. Haugen</name></author><entry><title type="html"></title><link href="https://frankhaugen.github.io/blog/2026/05/02/2026-03-30-channel-t-in-di-is-a-pipe-not-a-trick/" rel="alternate" type="text/html" title="" /><published>2026-05-02T15:15:50+00:00</published><updated>2026-05-02T15:15:50+00:00</updated><id>https://frankhaugen.github.io/blog/2026/05/02/2026-03-30-channel-t-in-di-is-a-pipe-not-a-trick</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2026/05/02/2026-03-30-channel-t-in-di-is-a-pipe-not-a-trick/"><![CDATA[<p>People hear “inject a <code class="language-plaintext highlighter-rouge">Channel&lt;T&gt;</code>” and assume it is cleverness for cleverness’ sake. Like we are trying to win a conference talk.</p>

<p>It is not.</p>

<h2 id="facts-what-systemthreadingchannels-gives-you">Facts: what <code class="language-plaintext highlighter-rouge">System.Threading.Channels</code> gives you</h2>

<p>Introduced to provide a <strong>producer/consumer</strong> primitive tuned for async code, <code class="language-plaintext highlighter-rouge">System.Threading.Channels</code> exposes <code class="language-plaintext highlighter-rouge">Channel&lt;T&gt;</code> with bounded / unbounded modes, explicit completion, and reader/writer halves that cooperate with <code class="language-plaintext highlighter-rouge">async</code>/<code class="language-plaintext highlighter-rouge">await</code> without busy-waiting.<sup id="fnref:channels-intro" role="doc-noteref"><a href="#fn:channels-intro" class="footnote" rel="footnote">1</a></sup></p>

<p>Useful mental model:</p>

<table>
  <thead>
    <tr>
      <th>Piece</th>
      <th>Role</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ChannelWriter&lt;T&gt;</code></td>
      <td>Producers complete / signal failure explicitly</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ChannelReader&lt;T&gt;</code></td>
      <td>Consumers <code class="language-plaintext highlighter-rouge">ReadAllAsync</code> or try-read patterns</td>
    </tr>
    <tr>
      <td>Bounded channel</td>
      <td>Backpressure when producers outrun consumers<sup id="fnref:bounded" role="doc-noteref"><a href="#fn:bounded" class="footnote" rel="footnote">2</a></sup></td>
    </tr>
    <tr>
      <td>Unbounded channel</td>
      <td>Memory becomes your implicit throttle (dangerous under load)</td>
    </tr>
  </tbody>
</table>

<p>Stephen Toub’s detailed posts on channels remain the best narrative explanation of design trade-offs inside .NET’s concurrency toolbox.<sup id="fnref:toub" role="doc-noteref"><a href="#fn:toub" class="footnote" rel="footnote">3</a></sup></p>

<h2 id="facts-why-dependency-injection-cares-about-lifetime">Facts: why Dependency Injection cares about lifetime</h2>

<p>Any shared mutable pipe must align with <strong>service lifetime</strong>:</p>

<ul>
  <li><strong>Singleton</strong> channel → every consumer sees one logical bus (great for in-process pub/sub).</li>
  <li><strong>Scoped</strong> channel → one pipe per request/scope (good when isolation matters).</li>
  <li><strong>Transient</strong> writers/readers without a stable channel → usually accidental complexity.</li>
</ul>

<p>Microsoft documents service lifetimes (<code class="language-plaintext highlighter-rouge">Singleton</code>, <code class="language-plaintext highlighter-rouge">Scoped</code>, <code class="language-plaintext highlighter-rouge">Transient</code>) and cautions about capturing scoped services in singletons—the same reasoning applies when the “service” is effectively an async queue.<sup id="fnref:di-lifetimes" role="doc-noteref"><a href="#fn:di-lifetimes" class="footnote" rel="footnote">4</a></sup></p>

<h2 id="opinion-labeled-honestly-what-this-library-is">Opinion (labeled honestly): what this library is</h2>

<p>A <strong>named pipe with async manners</strong> that the host already understands: backpressure, completion, multiple readers/writers — the stuff you would otherwise reinvent with <code class="language-plaintext highlighter-rouge">ConcurrentQueue</code> plus vibes.</p>

<p>Registering <code class="language-plaintext highlighter-rouge">Channel&lt;T&gt;</code> in DI is saying: <em>this subsystem talks to that subsystem through here, and the lifetime is explicit.</em></p>

<h2 id="what-it-is-not">What it is not</h2>

<p>It is not a replacement for messaging infrastructure. It is not Kafka in your process. It is not “use channels everywhere because channels are cool” — that way lies spaghetti with async characteristics.</p>

<h2 id="pitfalls-that-bite-real-systems">Pitfalls that bite real systems</h2>

<ol>
  <li><strong>Unbounded memory</strong> — unbounded channels absorb spikes until <code class="language-plaintext highlighter-rouge">OutOfMemoryException</code> becomes your profiler.</li>
  <li><strong>Forgotten completion</strong> — readers wait forever if writers never call <code class="language-plaintext highlighter-rouge">Complete</code>.</li>
  <li><strong>Blocking sync-over-async on channel edges</strong> — defeats the whole point; keep boundaries async.</li>
  <li><strong>Lifetime mismatch</strong> — singleton consumer holding scoped producer references leaks scope or starves work.</li>
</ol>

<h2 id="why-i-bothered-packaging-it">Why I bothered packaging it</h2>

<p>Because I kept writing the same three registrations in every project, and copy-paste across repos is how subtle bugs become <em>tradition</em>.</p>

<h2 id="tldr">TL;DR</h2>

<p>If the API feels boring, it is working. The interesting part is what you push through the channel — not the fact that DI can hand you the ends.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/dotnet/api/system.threading.channels">Microsoft Learn — System.Threading.Channels</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/core/extensions/dependency-injection">Microsoft Learn — Dependency injection in .NET</a></li>
  <li><a href="https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/">Stephen Toub — Introduction to System.Threading.Channels</a></li>
  <li><a href="https://github.com/frankhaugen/Frank.Channels.DependencyInjection">Frank.Channels.DependencyInjection (GitHub)</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:channels-intro" role="doc-endnote">
      <p>Microsoft Learn — <code class="language-plaintext highlighter-rouge">System.Threading.Channels</code> namespace overview. https://learn.microsoft.com/dotnet/api/system.threading.channels <a href="#fnref:channels-intro" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:bounded" role="doc-endnote">
      <p>Microsoft Learn — <code class="language-plaintext highlighter-rouge">BoundedChannelOptions</code> / full / dropped modes. https://learn.microsoft.com/dotnet/api/system.threading.channels.boundedchanneloptions <a href="#fnref:bounded" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:toub" role="doc-endnote">
      <p>Stephen Toub — <em>An Introduction to System.Threading.Channels</em> (MSDN Magazine / .NET Blog series on channels). https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/ <a href="#fnref:toub" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:di-lifetimes" role="doc-endnote">
      <p>Microsoft Learn — <em>Dependency injection in .NET</em> (service lifetimes). https://learn.microsoft.com/dotnet/core/extensions/dependency-injection#service-lifetimes <a href="#fnref:di-lifetimes" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author></entry><entry><title type="html">Bad security is worse than admitting you have none</title><link href="https://frankhaugen.github.io/blog/2026/05/02/bad-security-is-worse-than-none/" rel="alternate" type="text/html" title="Bad security is worse than admitting you have none" /><published>2026-05-02T00:00:00+00:00</published><updated>2026-05-02T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2026/05/02/bad-security-is-worse-than-none</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2026/05/02/bad-security-is-worse-than-none/"><![CDATA[<p>This showed up in my longer IRC saga, but it deserves its own line because people still argue about it wrong.</p>

<h2 id="facts-what-no-security-means-on-the-wire">Facts: what “no security” means on the wire</h2>

<p>Classic IRC predates ubiquitous TLS on the wire: protocol documents assume cleartext TCP unless an implementation layers transport security separately.<sup id="fnref:rfc2812" role="doc-noteref"><a href="#fn:rfc2812" class="footnote" rel="footnote">1</a></sup> Modern deployments often terminate TLS at proxies or use vendor-specific extensions—the baseline remains “know what you actually expose.”</p>

<p>TLS itself is standardized (today <strong>TLS 1.3</strong> in RFC 8446; TLS 1.2 still documented in RFC 5246 for historical comparison).<sup id="fnref:rfc8446" role="doc-noteref"><a href="#fn:rfc8446" class="footnote" rel="footnote">2</a></sup><sup id="fnref:rfc5246" role="doc-noteref"><a href="#fn:rfc5246" class="footnote" rel="footnote">3</a></sup> The lesson for application authors is not memorizing cipher suites—it is <strong>using maintained implementations</strong> (OS/BoringSSL/OpenSSL/.NET stack) instead of rolling bespoke crypto.</p>

<h2 id="facts-why-rolled-my-own-crypto-ages-poorly">Facts: why “rolled my own crypto” ages poorly</h2>

<p>MITRE <strong>CWE-327</strong> documents <em>Use of a Broken or Risky Cryptographic Algorithm</em>—including adopting obsolete primitives or constructing ad-hoc protocols.<sup id="fnref:cwe327" role="doc-noteref"><a href="#fn:cwe327" class="footnote" rel="footnote">4</a></sup> OWASP guidance repeatedly warns that custom cryptography combine-and-match tends to fail open under real attackers.<sup id="fnref:owasp-crypto" role="doc-noteref"><a href="#fn:owasp-crypto" class="footnote" rel="footnote">5</a></sup></p>

<p><strong>Related failure mode:</strong> downgrade attacks and oracle behaviors appear when authenticated encryption + proper key hierarchy are skipped—why TLS evolution focuses on authenticated AEAD ciphersuites and tight handshake rules.</p>

<h2 id="the-setup-irc-shaped-honesty">The setup (IRC-shaped honesty)</h2>

<p>IRC is text over TCP in the textbook telling. No encryption in the classic story. No authentication that survives a stiff breeze. If you squint, that is horrifying—but at least it is <em>honest</em> horrifying.</p>

<h2 id="the-worse-option">The worse option</h2>

<p>Rolling your own crypto-ish layer because you <em>feel</em> bad about plaintext — but doing it slightly wrong — is how you get <strong>false confidence</strong>. Users think they are safe. You think you are safe. The attacker thanks you for the predictable mistake.</p>

<p>This aligns with broader engineering consensus: prefer proven protocols implemented by specialists; document threat boundaries instead of improvising them.</p>

<h2 id="the-boring-correct-stance">The boring correct stance</h2>

<p>Either use a boring, audited stack end-to-end, or be loudly honest about what you are not protecting. “No security” with documentation is often more ethical than “security theater” with marketing.</p>

<p>Concrete checklist:</p>

<ol>
  <li>Name the adversary (network observer? malicious server? compromised client?).</li>
  <li>Pick <strong>TLS or Noise or QUIC</strong> from maintained stacks—not “AES + vibes.”</li>
  <li>Publish what you <strong>do not</strong> mitigate (social engineering, endpoint malware, etc.).</li>
</ol>

<h2 id="where-i-land">Where I land</h2>

<p>I would rather say “I am not the person to implement your threat model” than ship a half-understood cipher suite because sleep deprivation and hubris formed an alliance.</p>

<h2 id="tldr">TL;DR</h2>

<p>Admitting limits is not defeat. Shipping bad crypto because you could not stand the shame of plaintext — that is defeat with extra steps.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://datatracker.ietf.org/doc/html/rfc2812">RFC 2812 — IRC Client Protocol</a></li>
  <li><a href="https://datatracker.ietf.org/doc/html/rfc8446">RFC 8446 — TLS 1.3</a></li>
  <li><a href="https://datatracker.ietf.org/doc/html/rfc5246">RFC 5246 — TLS 1.2</a></li>
  <li><a href="https://cwe.mitre.org/data/definitions/327.html">CWE-327 — Broken/Risky Crypto Algorithm</a></li>
  <li><a href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html">OWASP — Cryptographic Storage Cheat Sheet</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:rfc2812" role="doc-endnote">
      <p>IETF RFC 2812 — Internet Relay Chat: Client Protocol (architecture / security considerations point to cleartext assumptions). https://datatracker.ietf.org/doc/html/rfc2812 <a href="#fnref:rfc2812" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rfc8446" role="doc-endnote">
      <p>IETF RFC 8446 — The Transport Layer Security (TLS) Protocol Version 1.3. https://datatracker.ietf.org/doc/html/rfc8446 <a href="#fnref:rfc8446" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rfc5246" role="doc-endnote">
      <p>IETF RFC 5246 — The Transport Layer Security (TLS) Protocol Version 1.2 (historical reference). https://datatracker.ietf.org/doc/html/rfc5246 <a href="#fnref:rfc5246" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:cwe327" role="doc-endnote">
      <p>MITRE CWE-327 — Use of a Broken or Risky Cryptographic Algorithm. https://cwe.mitre.org/data/definitions/327.html <a href="#fnref:cwe327" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:owasp-crypto" role="doc-endnote">
      <p>OWASP Cryptographic Storage Cheat Sheet / guidance on using standard algorithms correctly. https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html <a href="#fnref:owasp-crypto" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="security" /><category term="networking" /><category term="opinions" /><summary type="html"><![CDATA[CWE-327, TLS reality, IRC cleartext, and why theater loses to honesty.]]></summary></entry><entry><title type="html">Ten rules I break on purpose</title><link href="https://frankhaugen.github.io/blog/2026/04/20/ten-rules-i-break-on-purpose/" rel="alternate" type="text/html" title="Ten rules I break on purpose" /><published>2026-04-20T00:00:00+00:00</published><updated>2026-04-20T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2026/04/20/ten-rules-i-break-on-purpose</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2026/04/20/ten-rules-i-break-on-purpose/"><![CDATA[<p>These are not recommendations. They are confessions with attitude — each item names the <strong>cultural source</strong> of the rule so you know what you are rejecting.</p>

<ol>
  <li>
    <p><strong>“Never prefix your packages with your first name.”</strong> — NuGet guidance emphasizes discoverability and prefix reservations for <em>trusted publishers</em>, not outlawing personal prefixes—but the ecosystem norm skews corporate.<sup id="fnref:nuget-brand" role="doc-noteref"><a href="#fn:nuget-brand" class="footnote" rel="footnote">1</a></sup></p>
  </li>
  <li>
    <p><strong>“Always write the ADR before the code.”</strong> — Architecture Decision Records formalize intent before implementation spikes; the pattern was popularized to capture <em>why</em>, not block prototyping.<sup id="fnref:adr" role="doc-noteref"><a href="#fn:adr" class="footnote" rel="footnote">2</a></sup></p>
  </li>
  <li>
    <p><strong>“Avoid global state.”</strong> — Structured programming dogma (Dijkstra-era) and functional idioms both warn against implicit mutable globals—yet every GUI toolkit eventually exposes process-wide singletons (<code class="language-plaintext highlighter-rouge">Application.Current</code>). Trade honesty about deliberate globals vs accidental ones.</p>
  </li>
  <li>
    <p><strong>“One repo per concern.”</strong> — Martin Fowler’s <em>Monolith First</em> essay argues microservice partitioning often premature; polyrepos vs monorepos remain tooling choices, not virtues.<sup id="fnref:monolith-first" role="doc-noteref"><a href="#fn:monolith-first" class="footnote" rel="footnote">3</a></sup></p>
  </li>
  <li>
    <p><strong>“Never optimize early.”</strong> — Donald Knuth’s oft-misquoted line appears in his 1974 <em>Computing Surveys</em> paper on structured programming: small efficiencies can hurt readability <strong>unless you know the critical 3%</strong>—not “never optimize.”<sup id="fnref:knuth" role="doc-noteref"><a href="#fn:knuth" class="footnote" rel="footnote">4</a></sup></p>
  </li>
  <li>
    <p><strong>“Always use the framework’s blessed path.”</strong> — Framework vendors document golden paths sized for median apps; extension points exist precisely because blessed paths fail edge domains (games, compilers, protocol stacks).</p>
  </li>
  <li>
    <p><strong>“Write tests first.”</strong> — Kent Beck crystallized TDD as red/green/refactor loops; empirical teams blend test-after for exploratory spikes—method books describe ideals, delivery adapts.<sup id="fnref:tdd" role="doc-noteref"><a href="#fn:tdd" class="footnote" rel="footnote">5</a></sup></p>
  </li>
  <li>
    <p><strong>“Keep posts short.”</strong> — Attention-economy blogging advice; incompatible with posts that carry citations and narrative arcs—pick your audience.</p>
  </li>
  <li>
    <p><strong>“Never ship a rant.”</strong> — Engineering culture prefers sanitized communications; incident reviews prove emotionally honest narratives accelerate learning when paired with facts.</p>
  </li>
  <li>
    <p><strong>“Be consistent.”</strong> — PEP 8 famously quotes <em>“A foolish consistency is the hobgoblin of little minds”</em> to defend breaking style when readability improves—consistency serves comprehension, not uniformity.<sup id="fnref:pep8" role="doc-noteref"><a href="#fn:pep8" class="footnote" rel="footnote">6</a></sup></p>
  </li>
</ol>

<h2 id="the-actual-moral">The actual moral</h2>

<p>Rules are heuristics from someone else’s scars. Steal the scars, question the slogans, keep the compiler happy.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://martinfowler.com/bliki/MonolithFirst.html">Martin Fowler — Monolith First</a></li>
  <li><a href="https://martinfowler.com/bliki/TestDrivenDevelopment.html">Martin Fowler — Test-Driven Development</a></li>
  <li><a href="https://doi.org/10.1145/356635.356640">Knuth (1974) — DOI 10.1145/356635.356640</a></li>
  <li><a href="https://adr.github.io/">ADR GitHub</a></li>
  <li><a href="https://peps.python.org/pep-0008/">PEP 8</a></li>
  <li><a href="https://learn.microsoft.com/nuget/concepts/package-authoring-best-practices">Microsoft Learn — NuGet package authoring</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:nuget-brand" role="doc-endnote">
      <p>Microsoft Learn — <em>Package authoring best practices</em> (IDs, namespaces, consumer trust). https://learn.microsoft.com/nuget/concepts/package-authoring-best-practices <a href="#fnref:nuget-brand" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:adr" role="doc-endnote">
      <p>ADR GitHub organization — overview of Architecture Decision Records. https://adr.github.io/ <a href="#fnref:adr" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:monolith-first" role="doc-endnote">
      <p>Martin Fowler — <em>Monolith First</em> (2015). https://martinfowler.com/bliki/MonolithFirst.html <a href="#fnref:monolith-first" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:knuth" role="doc-endnote">
      <p>Donald E. Knuth — <em>Structured Programming with <code class="language-plaintext highlighter-rouge">go to</code> Statements</em>, ACM Computing Surveys 6(4), Dec 1974 (premature optimization discussion). https://doi.org/10.1145/356635.356640 <a href="#fnref:knuth" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:tdd" role="doc-endnote">
      <p>Kent Beck — <em>Test Driven Development: By Example</em> (Addison-Wesley). Summary reference via Martin Fowler’s bliki. https://martinfowler.com/bliki/TestDrivenDevelopment.html <a href="#fnref:tdd" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:pep8" role="doc-endnote">
      <p>PEP 8 — Style Guide for Python Code (consistency vs readability). https://peps.python.org/pep-0008/ <a href="#fnref:pep8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="opinions" /><category term="dotnet" /><summary type="html"><![CDATA[A contrarian checklist with receipts — where each slogan came from and why context matters.]]></summary></entry><entry><title type="html">If you can watch it compile, you might actually learn</title><link href="https://frankhaugen.github.io/blog/2026/02/22/if-you-can-watch-it-compile-you-might-learn/" rel="alternate" type="text/html" title="If you can watch it compile, you might actually learn" /><published>2026-02-22T00:00:00+00:00</published><updated>2026-02-22T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2026/02/22/if-you-can-watch-it-compile-you-might-learn</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2026/02/22/if-you-can-watch-it-compile-you-might-learn/"><![CDATA[<p>Most beginner stacks are designed to hide the pipeline: lexer, parser, binder, IL, runtime — all of that is “magic happens here” with a cartoon mascot and a green checkmark.</p>

<p><strong>RoboSharp</strong> is the opposite on purpose. The point is not the robot on the grid; the robot is the excuse. The point is that you can <em>see</em> the stages update while you edit: tokens, syntax, bound tree, IL, interpreter. Boring, finicky, real computer science — the stuff textbooks skip because it does not fit on a slide.</p>

<h2 id="facts-what-a-compiler-pipeline-usually-contains">Facts: what a compiler pipeline usually contains</h2>

<p>Textbooks divide compilation into predictable phases—names vary, but the skeleton is stable:</p>

<table>
  <thead>
    <tr>
      <th>Phase</th>
      <th>Removes ambiguity…</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Lexical analysis</td>
      <td>Characters → tokens (<code class="language-plaintext highlighter-rouge">if</code>, identifiers, literals).<sup id="fnref:dragon-ch1" role="doc-noteref"><a href="#fn:dragon-ch1" class="footnote" rel="footnote">1</a></sup></td>
    </tr>
    <tr>
      <td>Syntax analysis</td>
      <td>Tokens → parse tree / AST respecting grammar.<sup id="fnref:dragon-ch2" role="doc-noteref"><a href="#fn:dragon-ch2" class="footnote" rel="footnote">2</a></sup></td>
    </tr>
    <tr>
      <td>Semantic analysis</td>
      <td>Types, scopes, overload resolution — whether the AST <em>means</em> something legal.<sup id="fnref:dragon-ch4" role="doc-noteref"><a href="#fn:dragon-ch4" class="footnote" rel="footnote">3</a></sup></td>
    </tr>
    <tr>
      <td>Intermediate representation</td>
      <td>Portable IR before CPU-specific codegen (LLVM bitcode, .NET IL, etc.).</td>
    </tr>
    <tr>
      <td>Optimization + codegen</td>
      <td>Maps IR → machine code or VM bytecode.</td>
    </tr>
  </tbody>
</table>

<p><em>Engineering a Compiler</em> (Cooper &amp; Torczon) presents the same pipeline with engineering emphasis—trade-offs between IR shapes, passes, and diagnostics quality.<sup id="fnref:eco" role="doc-noteref"><a href="#fn:eco" class="footnote" rel="footnote">4</a></sup></p>

<p>For <strong>.NET</strong>, Roslyn exposes compiler APIs so tooling (IDE refactorings, analyzers, source generators) shares the same frontend as <code class="language-plaintext highlighter-rouge">csc</code>. Microsoft’s conceptual docs describe syntax trees, semantic models, and workspaces for building analyzers.<sup id="fnref:roslyn-conceptual" role="doc-noteref"><a href="#fn:roslyn-conceptual" class="footnote" rel="footnote">5</a></sup></p>

<h2 id="why-visibility-beats-just-use-the-api">Why visibility beats “just use the API”</h2>

<p>When the API is the curriculum, you learn <em>one vendor’s opinion</em>. When the pipeline is the curriculum, you learn <em>how languages actually work</em>, which transfers even when the framework du jour changes its mind next Tuesday.</p>

<h2 id="the-honest-downside">The honest downside</h2>

<p>It is more intimidating. Some people bounce off immediately because there is no dopamine tutorial path where you “build Instagram in ten minutes.” Good. Instagram already exists. I would rather have a smaller audience that sticks around because the machine finally clicked.</p>

<h2 id="tldr">TL;DR</h2>

<p>If you can watch it compile, you might hate it for an hour — then you might never unsee how much software you use every day was always doing this work under the hood.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/dotnet/csharp/roslyn-sdk/">Microsoft Learn — .NET Compiler Platform SDK</a></li>
  <li><a href="https://github.com/dotnet/roslyn/wiki">Roslyn GitHub — Wiki overview</a></li>
  <li><a href="https://www.pearson.com/en-us/subject-catalog/p/compilers-principles-techniques-and-tools/P200000003477">Aho et al. — <em>Compilers: Principles, Techniques, and Tools</em> (Pearson)</a></li>
  <li><a href="https://www.elsevier.com/books/engineering-a-compiler/cooper/978-0120884780">Cooper &amp; Torczon — <em>Engineering a Compiler</em></a></li>
  <li><a href="https://github.com/frankhaugen/RoboSharp">RoboSharp (GitHub)</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:dragon-ch1" role="doc-endnote">
      <p>Aho, Lam, Sethi, Ullman — <em>Compilers: Principles, Techniques, and Tools</em> (2nd ed., 2006), lexical analysis chapter overview. ISBN 978-0321486813 — publisher Pearson. <a href="#fnref:dragon-ch1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:dragon-ch2" role="doc-endnote">
      <p>Same — syntax analysis / parsing theory sections. <a href="#fnref:dragon-ch2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:dragon-ch4" role="doc-endnote">
      <p>Same — semantic analysis / type checking introduction. <a href="#fnref:dragon-ch4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:eco" role="doc-endnote">
      <p>Cooper &amp; Torczon — <em>Engineering a Compiler</em> (2nd ed., Morgan Kaufmann). https://www.elsevier.com/books/engineering-a-compiler/cooper/978-0120884780 <a href="#fnref:eco" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:roslyn-conceptual" role="doc-endnote">
      <p>Microsoft Learn — <em>The .NET Compiler Platform SDK (Roslyn APIs)</em> overview. https://learn.microsoft.com/dotnet/csharp/roslyn-sdk/ <a href="#fnref:roslyn-conceptual" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="compilers" /><category term="education" /><category term="RoboSharp" /><summary type="html"><![CDATA[RoboSharp exists because most "learn to code" stacks hide the interesting parts. Visibility is the feature.]]></summary></entry><entry><title type="html">The Frank.* prefix and other naming crimes</title><link href="https://frankhaugen.github.io/blog/2026/01/18/the-frank-prefix-and-other-naming-crimes/" rel="alternate" type="text/html" title="The Frank.* prefix and other naming crimes" /><published>2026-01-18T00:00:00+00:00</published><updated>2026-01-18T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2026/01/18/the-frank-prefix-and-other-naming-crimes</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2026/01/18/the-frank-prefix-and-other-naming-crimes/"><![CDATA[<p>If you browse my GitHub, you will notice a pattern so obvious it is almost a joke: <strong>Frank.</strong> everything. Frank.BedrockSlim. Frank.PulseFlow. Frank.WhateverIThoughtOfLastTuesday.</p>

<h2 id="facts-how-nuget-expects-packages-to-identify-themselves">Facts: how NuGet expects packages to identify themselves</h2>

<p>NuGet package IDs must follow predictable rules—length limits, allowed characters, no reserved prefixes unless you own them. Microsoft documents authoring guidelines and <strong>ID prefix reservation</strong> so consumers can trust who publishes <code class="language-plaintext highlighter-rouge">Microsoft.*</code>, <code class="language-plaintext highlighter-rouge">System.*</code>, etc.<sup id="fnref:nuget-id" role="doc-noteref"><a href="#fn:nuget-id" class="footnote" rel="footnote">1</a></sup></p>

<p>That matters because names are not cosmetic—they control restore graphs, typosquatting risk, and tooling assumptions.</p>

<h2 id="is-that-good-branding">Is that good branding?</h2>

<p>For <em>discoverability</em>, absolutely not. NuGet search is not going to surface “Frank” as a category unless you already know me. For <em>ownership</em>, though, it is weirdly practical: I can tell at a glance what I spawned, what I maintain, and what is allowed to be as opinionated as I want without pretending it is a neutral industry utility.</p>

<p>OSS ecosystems repeatedly debate <strong>namespaces vs vanity prefixes</strong>—npm scoped packages (<code class="language-plaintext highlighter-rouge">@org/pkg</code>) solved part of the collision surface; NuGet leans on prefix reservations + longer IDs instead.</p>

<h2 id="the-real-reason">The real reason</h2>

<p>Half of it is ego — I will not pretend otherwise. The other half is namespace hygiene in a world where every second word is <code class="language-plaintext highlighter-rouge">Microsoft.Extensions.SomethingSomething</code>. I want my packages to read like <strong>mine</strong>, not like a missing chapter of the framework docs.</p>

<h2 id="when-it-is-embarrassing">When it is embarrassing</h2>

<p>When someone asks “which Frank library does channels again?” and I have to mentally grep twelve repos. When I explain my work to a normal human and they think my legal name is a product line.</p>

<h2 id="when-i-would-still-do-it-again">When I would still do it again</h2>

<p>Because naming is the first API you ship. If <code class="language-plaintext highlighter-rouge">Frank.*</code> filters out people who cannot tolerate a little vanity, those people were never going to enjoy my commit messages either.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/nuget/concepts/package-authoring-best-practices">Microsoft Learn — NuGet package authoring</a></li>
  <li><a href="https://learn.microsoft.com/nuget/concepts/package-id-prefix-reservation">Microsoft Learn — Package ID prefix reservation</a></li>
  <li><a href="https://learn.microsoft.com/nuget/consume-packages/finding-and-choosing-packages">NuGet Gallery — search guidelines</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:nuget-id" role="doc-endnote">
      <p>Microsoft Learn — <em>Package authoring best practices</em> / identifier rules and prefix reservation. https://learn.microsoft.com/nuget/concepts/package-id-prefix-reservation <a href="#fnref:nuget-id" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="dotnet" /><category term="open-source" /><category term="opinions" /><summary type="html"><![CDATA[Yes, everything is Frank.Something. NuGet rules, discoverability tax, and why vanity namespaces persist.]]></summary></entry><entry><title type="html">WPF without XAML</title><link href="https://frankhaugen.github.io/blog/2024/06/01/wpf-without-xaml/" rel="alternate" type="text/html" title="WPF without XAML" /><published>2024-06-01T00:00:00+00:00</published><updated>2024-06-01T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2024/06/01/wpf-without-xaml</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2024/06/01/wpf-without-xaml/"><![CDATA[<p>People assume WPF means XAML. It doesn’t have to — you can drive the whole thing from C# if you accept a bit of ceremony and stop pretending <code class="language-plaintext highlighter-rouge">Application</code> is magic.</p>

<h2 id="facts-how-wpf-expects-an-application-to-boot">Facts: how WPF expects an application to boot</h2>

<p>Microsoft documents <code class="language-plaintext highlighter-rouge">System.Windows.Application</code> as the entry point for WPF UI—a message pump (<code class="language-plaintext highlighter-rouge">Run</code>) tied to Windows message semantics and <strong>STA threading</strong>.<sup id="fnref:wpf-app" role="doc-noteref"><a href="#fn:wpf-app" class="footnote" rel="footnote">1</a></sup></p>

<p>.NET console templates historically used <code class="language-plaintext highlighter-rouge">[STAThread]</code> on <code class="language-plaintext highlighter-rouge">Main</code> because COM and classic UI frameworks expected STA; WPF shares that heritage even when you host inside <code class="language-plaintext highlighter-rouge">Microsoft.Extensions.Hosting</code>.<sup id="fnref:stathread" role="doc-noteref"><a href="#fn:stathread" class="footnote" rel="footnote">2</a></sup></p>

<p>The <strong>Worker SDK</strong> (<code class="language-plaintext highlighter-rouge">Microsoft.NET.Sdk.Worker</code>) pulls in hosting primitives (<code class="language-plaintext highlighter-rouge">BackgroundService</code>, configuration, DI) documented under <em>.NET Generic Host</em>.<sup id="fnref:generic-host" role="doc-noteref"><a href="#fn:generic-host" class="footnote" rel="footnote">3</a></sup><sup id="fnref:worker-template" role="doc-noteref"><a href="#fn:worker-template" class="footnote" rel="footnote">4</a></sup></p>

<p>This pattern pairs:</p>

<table>
  <thead>
    <tr>
      <th>Piece</th>
      <th>Responsibility</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Host.CreateDefaultBuilder</code></td>
      <td>Logging, config, DI wiring</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">BackgroundService</code> (<code class="language-plaintext highlighter-rouge">WindowHost</code>)</td>
      <td>Keeps process alive while UI runs</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Application.Run(Window)</code></td>
      <td>Starts WPF dispatcher loop</td>
    </tr>
  </tbody>
</table>

<h2 id="why-would-you-use-wpf-without-xaml">Why would you use WPF without XAML</h2>

<p>The easy answer: I don’t like XAML. Even with MVVM it is fiddly XML that fights you the moment you want real nesting, dynamic trees, or anything that is not a designer-friendly toy demo.</p>

<p><strong>Tradeoff snapshot:</strong></p>

<table>
  <thead>
    <tr>
      <th>Approach</th>
      <th>Upside</th>
      <th>Downside</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>XAML + MVVM</td>
      <td>Designer tooling, markup/styles separation</td>
      <td>Verbosity + merge dictionaries rabbit holes</td>
    </tr>
    <tr>
      <td>Pure C# trees</td>
      <td>Full refactor tooling; dynamic UI trivial</td>
      <td>Verbose construction code</td>
    </tr>
    <tr>
      <td>Worker + Host</td>
      <td>Unified DI/config with headless services</td>
      <td>Extra hop vs classic <code class="language-plaintext highlighter-rouge">App.xaml</code> bootstrap</td>
    </tr>
  </tbody>
</table>

<h2 id="pitfalls-worth-naming">Pitfalls worth naming</h2>

<ol>
  <li><strong>Lifetime mismatch</strong> — treating <code class="language-plaintext highlighter-rouge">MainWindow</code> like a singleton when your DI scope says scoped invites ghosts after window close.</li>
  <li><strong>Async void on UI thread</strong> — still the easiest footgun in event handlers.</li>
  <li><strong>Shutdown semantics</strong> — forcing <code class="language-plaintext highlighter-rouge">Environment.Exit</code> (as below) is blunt; production apps usually coordinate <code class="language-plaintext highlighter-rouge">CancellationToken</code> + <code class="language-plaintext highlighter-rouge">IHostApplicationLifetime</code>.</li>
</ol>

<h2 id="how-to-get-started">How to get started?</h2>

<h4 id="1-use-the-dotnet-cli-or-visual-studio-project-creator-to-create-a-worker-project-and-change-the-csproj-to-look-something-like-this">1. Use the dotnet CLI or Visual Studio project creator to create a “Worker project” and change the .csproj to look something like this:</h4>

<p>Prefer a modern <code class="language-plaintext highlighter-rouge">TargetFramework</code> (<code class="language-plaintext highlighter-rouge">net8.0-windows</code> or newer LTS) unless you truly need <code class="language-plaintext highlighter-rouge">net5.0-windows</code>.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Project</span> <span class="na">Sdk=</span><span class="s">"Microsoft.NET.Sdk.Worker"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;PropertyGroup&gt;</span>
        <span class="nt">&lt;TargetFramework&gt;</span>net8.0-windows<span class="nt">&lt;/TargetFramework&gt;</span>
        <span class="nt">&lt;LangVersion&gt;</span>latest<span class="nt">&lt;/LangVersion&gt;</span>
        <span class="nt">&lt;Nullable&gt;</span>enable<span class="nt">&lt;/Nullable&gt;</span>
        <span class="nt">&lt;OutputType&gt;</span>Exe<span class="nt">&lt;/OutputType&gt;</span>
        <span class="nt">&lt;UseWpf&gt;</span>true<span class="nt">&lt;/UseWpf&gt;</span>
    <span class="nt">&lt;/PropertyGroup&gt;</span>
<span class="nt">&lt;/Project&gt;</span>
</code></pre></div></div>

<h4 id="2-add-an-appcs-that-extends-application">2. Add an <code class="language-plaintext highlighter-rouge">App.cs</code> that extends <code class="language-plaintext highlighter-rouge">Application</code></h4>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.Windows</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Demo.WpfApplication</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">App</span> <span class="p">:</span> <span class="n">Application</span>
    <span class="p">{</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="3-create-a-mainwindowcs-to-be-your-main-window">3. Create a <code class="language-plaintext highlighter-rouge">MainWindow.cs</code> to be your main window</h4>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.Windows</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Windows.Controls</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Demo.WpfApplication</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">MainWindow</span> <span class="p">:</span> <span class="n">Window</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">MainWindow</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">MainWindow</span><span class="p">(</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">MainWindow</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>

            <span class="kt">var</span> <span class="n">button</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Button</span> <span class="p">{</span> <span class="n">Content</span> <span class="p">=</span> <span class="s">"Log something"</span> <span class="p">};</span>
            <span class="n">button</span><span class="p">.</span><span class="n">Click</span> <span class="p">+=</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Something"</span><span class="p">);</span>

            <span class="n">Content</span> <span class="p">=</span> <span class="n">button</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="4-rename-workercs-to-windowhostcs-then-inject-only-the-iserviceprovider">4. Rename <code class="language-plaintext highlighter-rouge">Worker.cs</code> to <code class="language-plaintext highlighter-rouge">WindowHost.cs</code> then inject only the <code class="language-plaintext highlighter-rouge">IServiceProvider</code></h4>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Threading</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Demo.WpfApplication</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">WindowHost</span> <span class="p">:</span> <span class="n">BackgroundService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IServiceProvider</span> <span class="n">_serviceProvider</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">WindowHost</span><span class="p">(</span><span class="n">IServiceProvider</span> <span class="n">serviceProvider</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">_serviceProvider</span> <span class="p">=</span> <span class="n">serviceProvider</span><span class="p">;</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">ExecuteAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">stoppingToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">using</span> <span class="nn">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">_serviceProvider</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">();</span>
            <span class="kt">var</span> <span class="n">window</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">MainWindow</span><span class="p">&gt;();</span>
            <span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">App</span><span class="p">&gt;();</span>
            <span class="n">window</span><span class="p">.</span><span class="n">Closed</span> <span class="p">+=</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">Environment</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="m">666</span><span class="p">);</span> <span class="c1">// replace with graceful shutdown in real apps</span>

            <span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="n">window</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h4 id="5-then-finally-edit-programcs-to-look-like-this">5. Then finally edit Program.cs to look like this</h4>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Demo.WpfApplication</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="p">[</span><span class="n">STAThread</span><span class="p">]</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">host</span> <span class="p">=</span> <span class="n">Host</span><span class="p">.</span><span class="nf">CreateDefaultBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ConfigureServices</span><span class="p">((</span><span class="n">hostContext</span><span class="p">,</span> <span class="n">services</span><span class="p">)</span> <span class="p">=&gt;</span>
                <span class="p">{</span>
                    <span class="n">services</span><span class="p">.</span><span class="nf">AddLogging</span><span class="p">();</span>
                    <span class="n">services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">App</span><span class="p">&gt;();</span>
                    <span class="n">services</span><span class="p">.</span><span class="n">AddScoped</span><span class="p">&lt;</span><span class="n">MainWindow</span><span class="p">&gt;();</span>

                    <span class="n">services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">WindowHost</span><span class="p">&gt;();</span>
                <span class="p">});</span>

            <span class="n">host</span><span class="p">.</span><span class="nf">Build</span><span class="p">().</span><span class="nf">Run</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/dotnet/desktop/wpf/overview/">Microsoft Learn — WPF overview</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/api/system.windows.application">Microsoft Learn — Application</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/core/extensions/generic-host">Microsoft Learn — Generic Host</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/core/extensions/workers">Microsoft Learn — Worker Service template</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:wpf-app" role="doc-endnote">
      <p>Microsoft Learn — <code class="language-plaintext highlighter-rouge">Application</code> class (WPF application model). https://learn.microsoft.com/dotnet/api/system.windows.application <a href="#fnref:wpf-app" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:stathread" role="doc-endnote">
      <p>Microsoft Learn — <code class="language-plaintext highlighter-rouge">STAThreadAttribute</code>. https://learn.microsoft.com/dotnet/api/system.stathreadattribute <a href="#fnref:stathread" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:generic-host" role="doc-endnote">
      <p>Microsoft Learn — <em>.NET Generic Host</em>. https://learn.microsoft.com/dotnet/core/extensions/generic-host <a href="#fnref:generic-host" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:worker-template" role="doc-endnote">
      <p>Microsoft Learn — <code class="language-plaintext highlighter-rouge">BackgroundService</code> base class for long-running hosted services. https://learn.microsoft.com/dotnet/api/microsoft.extensions.hosting.backgroundservice <a href="#fnref:worker-template" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="WPF" /><category term="dotnet" /><summary type="html"><![CDATA[Worker + Generic Host + code-only windows. XAML optional; sanity less optional.]]></summary></entry><entry><title type="html">I just wanted to make an IRC client/server library</title><link href="https://frankhaugen.github.io/blog/2024/02/04/irc-client-server-journey/" rel="alternate" type="text/html" title="I just wanted to make an IRC client/server library" /><published>2024-02-04T00:00:00+00:00</published><updated>2024-02-04T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2024/02/04/irc-client-server-journey</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2024/02/04/irc-client-server-journey/"><![CDATA[<p>If you only read one paragraph: I wanted IRC. I got BedrockSlim, PulseFlow, Channels-in-DI, storage abstractions, CronJobs, workflows, a torrent client, and a game engine-shaped distraction. IRC is still a future fantasy. <strong>Skill issue?</strong> Partly. <strong>Microsoft gave me a machine gun of foot-guns?</strong> Also partly.</p>

<p>This is the story of how I wanted to make an IRC client/server library, but ended up making a workflow library, a Pub/Sub 
library, and a DI Channel library, a data persistence library, CronJobs, and a bunch of other things along the way, while still 
not having any idea how to make an IRC client/server library. Adopting a Torrent client along the way was interesting though.</p>

<h2 id="tldr">TL;DR</h2>

<p>I wanted to make an IRC client/server library, but ended up making an entire ecosystem of libraries, and still don’t have
any IRC client/server library to show for it.</p>

<h2 id="motivation">Motivation</h2>

<p>IRC is a protocol that has been around for a long time. It is simple, and it is still used today, and it is still relevant. 
It lends itself to be used in many different ways, and it is a good protocol to learn about networking, and to learn about 
low level stuff and efficient data/string manipulation. It is also a good protocol to learn about security, as it has no 
security. It is also a good protocol to learn about how to make a client/server library.</p>

<h2 id="facts-irc-specifications-worth-bookmarking">Facts: IRC specifications worth bookmarking</h2>

<p>The classic IRC protocol stack is defined across multiple RFCs—<strong>RFC 1459</strong> documents the original architecture (channels, operators, numeric replies), while <strong>RFC 2812</strong> focuses on client behaviour and explicitly discusses security considerations that boil down to “transport security is out of band unless you bolt something on.”<sup id="fnref:rfc1459" role="doc-noteref"><a href="#fn:rfc1459" class="footnote" rel="footnote">1</a></sup><sup id="fnref:rfc2812" role="doc-noteref"><a href="#fn:rfc2812" class="footnote" rel="footnote">2</a></sup></p>

<p>Modern deployments sometimes layer TLS (<code class="language-plaintext highlighter-rouge">ircs://</code>) or vendor-specific extensions—IRCv3 working-group drafts catalogue capability negotiation—but the baseline mental model remains <strong>cleartext channel semantics unless you prove otherwise</strong>.</p>

<p>Microsoft’s <strong><code class="language-plaintext highlighter-rouge">Microsoft.AspNetCore.Connections.Abstractions</code></strong>—what Bedrock.Framework showcased—is documented today under ASP.NET Core connection middleware / Kestrel extensibility; learning those primitives is orthogonal to IRC grammar but essential if you want composable socket pipelines in .NET.<sup id="fnref:aspnet-connections" role="doc-noteref"><a href="#fn:aspnet-connections" class="footnote" rel="footnote">3</a></sup></p>

<h2 id="the-idea">The Idea</h2>

<p>I wanted to make an IRC client/server library. I wanted to make it in a way that it would be easy to use, and easy to extend.
It should be .net, nuget distributed, and should be able to run on cross-platform. It should be close to out-of-the-box, but 
alse a framework that can be extended. It should be able to run as a server, and as a client, (in different packages of course).</p>

<h2 id="the-journey">The Journey</h2>

<p>First I looked at the existing libraries. There are a few, many are .net/.net core, but none of them are close to what I 
wanted. I am a big fan of Dependency Injection, and I wanted to make the library in a way that it would be easy to use with 
this pattern. I also wanted to make it in a way that it would be easy to use with modern .net core features like Channel&lt;&gt;, 
IHostedService, and CronJobs.</p>

<p>I started with the server. I wanted to make it in a way that it would be easy to use, and easy to extend. I wanted to make 
it so it would be easy to use with Dependency Injection, and I wanted to make it so it would be easy to use with modern .net,
and also wanted everything to be async. I wanted to make it so it would be easy to use with Channel&lt;&gt;, and I wanted to make 
it with mostly my own code, and not rely on too many third party libraries. Easy peasy, right? WRONG!</p>

<h3 id="where-it-started-to-go-wrong">Where it started to go wrong</h3>

<p>So I am on the .net stack, and I love it. Microsoft has done a great job with .net core, but its microsoft, and so they 
don’t give you A gun to shoot yourself in the foot, they give you a machine gun, or at least a box of guns. So I started 
looking at the System.Net.Sockets namespace, and I was like, “Oh, this is easy, I can do this.” And then I started looking 
into life cycle management, and I was like, “Oh, this NOT easy, I can NOT do this.”, is this skillissues? Yes, but also: The 
.net team hasn’t made it easy to work with sockets. I sank so low I asked a question on Reddit, /dotnet, and I got an answer,
from none other than David Fowler, he pointed me to a tech demo he had noted on his GitHub. This was a good start, and I 
found a library he had made called “Bedrock.Framework”. I started looking into it, and I was like, “Oh, this is easy, but oh 
so much stuff I don’t need, and oh so much stuff I don’t understand.” I started looking into the source code, and I did the 
rational thing, I copied it, and started to strip it down, didn’t like it, then I took a step back, and started to look at 
what he was doing and saw he was basically demonstrating how to use the new <code class="language-plaintext highlighter-rouge">Microsoft.AspNetCore.Connections.Abstractions</code>, 
so I pivoted, and now I have a TCP client/server library that is using the new <code class="language-plaintext highlighter-rouge">Microsoft.AspNetCore.Connections.
Abstractions</code> called “Frank.BedrockSlim”. I am happy with it. It is async, it is easy to use, and it is easy to extend. It 
uses a bear minimum of asp.net core low level stuff, and it is easy to use with Dependency Injection. The server does have a 
framework reference to asp.net core, but the client does not. I am happy with it, but I still don’t have an IRC 
client/server you might have noticed.</p>

<h3 id="servers-need-data-persistence">Servers need data persistence</h3>

<p>So I have a server, and I have a client, and I have a protocol, and I have a way to communicate, but servers usually need 
data. Sessions, users, channels, messages, etc. I started to look into how to do this, and I was like, “Oh, this is easy, I 
just use a database and Entity Framework Core”. Well, a database, even SQLite, is not always the correct answer. I wanted 
flexibility in my data persistence, because user profile data is nice to blob in a json, logs should be in a log file, and 
messages should perhaps be in a database. Well, I solved it by making a data persistence library called “Frank.DataStorage”. 
Now I have a common interface, and I have a few implementations, and I can use it with Dependency Injection. Now I’m just 5 
months into the project, and I still don’t have an IRC client/server library. I have something cool though.</p>

<h3 id="oooh-shiny">Oooh, shiny!</h3>

<p>Channel&lt;&gt;, is maybe the coolest thing added to .net core in the last few years. It is a way to pass data between threads, 
from one to many, from many to one, and it is async. I wanted to use it in my server, and I wanted to use it in my client, 
so it can be more reactive, and many things are workflow-like, maybe I should make a workflow library? Forshadowing? 
Channels seems to be awesome for in-memory Pub/Sub, and I wanted to make a library for that, and I did, and I called it 
<code class="language-plaintext highlighter-rouge">Frank.PulseFlow</code>. It is a simple Pub/Sub library that uses Channel&lt;&gt;, and it is easy to use with Dependency Injection. Oh, 
there is a need for DI Channel&lt;&gt; other places too, so I made a library for that, and I called it <code class="language-plaintext highlighter-rouge">Frank.Channels.
DependencyInjection</code>, (It’s really cool, you just register your type as a channel and you can inject the entire channel, the 
reader or writer, and looking at resource use its like nothing, AWESOME!). I’m getting a bit off track here, wasn’t I making an 
IRC client/server library?</p>

<h3 id="cronjobs">CronJobs</h3>

<p>I have contributed to a few open source projects, one of them is a library called <code class="language-plaintext highlighter-rouge">CronQuery</code>. It is a library that can very 
easily parse a cron expression from configuation, and start DI CronJobs. I wanted to use it in my server and client, and I 
wanted to use it a bit differently than it was intended, so I made a library called <code class="language-plaintext highlighter-rouge">Frank.CronJobs</code>. It is a library that 
don’t rely on the <code class="language-plaintext highlighter-rouge">Microsoft.Extensions.Configuration</code> namespace, and it is easy to use with Dependency Injection. I have a 
great deal of respect for open source, and so I copied the license header from the original library, and I have a link to it,
and even rewrote a lot of the more complex parts of the library, so I’m not just a copycat.</p>

<h3 id="workflows">Workflows</h3>

<p>I have been working with workflows for a long time, and I have been working with a lot of different workflow engines, I have 
even created a few myself. But I wanted to make a workflow engine that is built on Dependency Injection and modern .net core 
and that uses Channel&lt;&gt;, IHostedService, and CronJobs. I wanted to make it so it would be easy to use, and easy to extend. I 
wanted it to be simple, because I understand that workflows can be complex, but most of the time they are not, they are just 
a linear sequence of steps. So I made a library called <code class="language-plaintext highlighter-rouge">Frank.WorkflowEngine</code>. It is a simple workflow engine that is built 
with a lot of the previously mentioned libraries.</p>

<h3 id="security-whats-that">Security, what’s that?</h3>

<p>So, I remembered a tiny bit about IRC, NO SECURITY! Its text over TCP, and it is not encrypted, and it is not authenticated, 
and an IRC server should at least be a little secure, and so I started to look into how to make it secure, and I was like, 
“Oh, lets build a security library”, and I did, and I called it <code class="language-plaintext highlighter-rouge">Frank.Security</code>, (There’s a pattern to my naming scheme 
that is REALLY hard to deduce). What kind of security? Well, password hashing, passprashe generation, and a few other things.
How to use it with IRC? Well, he he… Actually securing and encrypting IRC is a bit more complex than I thought, and I can 
say that I’d rather not do it, and I can say that there is no end-to-end encryption, rather than doing it wrong. I have some 
experience with security, but low level networking security and cryptography is not my strong suit, and I don’t want to do it badly.</p>

<p>Side note: No security, and openness about it, is usually exponentially better than bad security. A bad implementation of 
encryption is worse than no encryption, as it gives a false sense of security, and opens one up to liability.</p>

<h3 id="torrents">Torrents</h3>

<p>How did I end up with a Torrent client? Well, I wanted to dowload some files, and I wanted to do it with a Torrent as it was 
a bunch of data science stuff, and it would take forever to download, so Torrent was the best option. I didn’t want to 
install anything, so I looked into Torrent Client libraries, and I found a few, and they were all pretty old and outdated, 
so I forked one, and I started to look into it, and spent a long time to get it into a usable state, and I called it <code class="language-plaintext highlighter-rouge">Frank.
TorrentClient</code>. It is a simple Torrent client that is built on Dependency Injection and modern .net core features and have a 
couple of UI projects that are built on AvaloniaUI and WPF. It needs a lot of work, but it is usable for now.</p>

<h3 id="a-game-frameworkengine-seriously-whats-wrong-with-you">A Game Framework/Engine? Seriously? What’s wrong with you?</h3>

<p>So, I used to be a game developer, (unity), but I was always fighting the engine. I wanted to buold my own engine, or rather 
my own framework, and I did, and I called it <code class="language-plaintext highlighter-rouge">Frank.GameEngine</code>. It is a simple game framework that is built on the idea of 
interchangeable parts, so you can use whatever rendering engine you want, and whatever physics engine you want, and it 
should just work. Its pretty jankey, and it needs a lot of work, especially the collision detection.</p>

<h2 id="conclusion">Conclusion</h2>

<p>So, I have made a lot of libraries, and I have made a lot of things, and I have learned a lot, and I have had a lot of “fun”,
yet I still don’t have an IRC client/server library. The journey has been interesting, and while I have not reached my 
endgoal, I have made an ecosystem of libraries that are easy to use, and easy to extend, and that are built on Dependency 
Injection and modern .net core features. This has helped me be a better developer, but I still want my IRC client/server 
library to be a reality. I have a lot of ideas, and I have a lot of code, and I have a lot of experience.</p>

<h2 id="the-future">The Future</h2>

<p>Will I ever make an IRC client/server library? I don’t know, and now I have seen a few other technologies besides TCP that 
might be better suited for making a chat server. There is a lot of cool stuff out there, and I have a lot of ideas, so for 
now I will just keep on making stuff and see where it takes me. I am very interested in code generation, and it would be 
benefical to make a code generator for the IRC client/server library, as there are a lot of commands and events, and LLMs is 
a thing I have been looking into, and that also might be a good fit for the IRC server library, because building in a 
moderation chat bot would be cool.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://datatracker.ietf.org/doc/html/rfc1459">RFC 1459 — IRC Protocol</a></li>
  <li><a href="https://datatracker.ietf.org/doc/html/rfc2812">RFC 2812 — IRC Client Protocol</a></li>
  <li><a href="https://learn.microsoft.com/aspnet/core/fundamentals/servers/kestrel">Microsoft Learn — ASP.NET Core / Kestrel</a></li>
  <li><a href="https://github.com/frankhaugen/Frank.BedrockSlim">Frank.BedrockSlim (GitHub)</a></li>
  <li><a href="https://ircv3.net/specs">IRCv3 specifications</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:rfc1459" role="doc-endnote">
      <p>IETF RFC 1459 — Internet Relay Chat Protocol. https://datatracker.ietf.org/doc/html/rfc1459 <a href="#fnref:rfc1459" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rfc2812" role="doc-endnote">
      <p>IETF RFC 2812 — Internet Relay Chat: Client Protocol. https://datatracker.ietf.org/doc/html/rfc2812 <a href="#fnref:rfc2812" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:aspnet-connections" role="doc-endnote">
      <p>Microsoft Learn — <em>Connections in ASP.NET Core</em> / related middleware docs (Bedrock-era demos mapped here). https://learn.microsoft.com/aspnet/core/fundamentals/servers/kestrel <a href="#fnref:aspnet-connections" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="blog" /><category term="story" /><category term="project" /><category term="IRC" /><summary type="html"><![CDATA[One goal, many NuGet packages, zero IRC. A tour of the ecosystem I built while avoiding the thing I set out to build.]]></summary></entry><entry><title type="html">Why I hate that I don’t document things</title><link href="https://frankhaugen.github.io/blog/2024/01/20/why-i-hate-that-i-dont-document-things/" rel="alternate" type="text/html" title="Why I hate that I don’t document things" /><published>2024-01-20T00:00:00+00:00</published><updated>2024-01-20T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2024/01/20/why-i-hate-that-i-dont-document-things</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2024/01/20/why-i-hate-that-i-dont-document-things/"><![CDATA[<p>I hate that I don’t document things. Not in an abstract “we should write more ADRs” way — in the <em>I am staring at my own repo and it might as well be someone else’s</em> way.</p>

<p>This post is part rant, part therapy, part <strong>actual spec</strong> for a thing called <code class="language-plaintext highlighter-rouge">simple-installer</code> because if I don’t write it down here, nobody will, including future me.</p>

<h2 id="the-project">The project</h2>

<p>It was just a small simple project: a simple installer tool called <code class="language-plaintext highlighter-rouge">simple-installer</code>. Pack a zip with metadata, install it. Nothing fancy.</p>

<p>It just needed to be able to:</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Pack a zip</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Unpack a zip</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Run as a CLI tool</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Be distributed through NuGet</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Run as a <code class="language-plaintext highlighter-rouge">dotnet</code> global tool</li>
</ul>

<h2 id="the-problem">The problem</h2>

<p>I got a lot done. Menus, zip in, zip out, CLI, NuGet. Then I hit “global tool” and realized I had <strong>no idea</strong> what to do next — and no breadcrumbs for how I got here in the first place.</p>

<p>It’s not perfect, but parts work. The problem is continuity: I don’t remember the decisions, and there is no documentation. No specs, no issues, no “why we chose this” anywhere except inside my skull, which is a terrible persistence layer.</p>

<h2 id="the-solution">The solution</h2>

<p>Write this blog post and treat it as living documentation for the rest of the project. Yes, that is a ridiculous place for specs. It is still better than nowhere.</p>

<h2 id="facts-kinds-of-documentation-that-actually-help">Facts: kinds of documentation that actually help</h2>

<p><strong>Opinion:</strong> any artifact beats brain-RAM. <strong>Fact:</strong> teams usually separate <em>intent</em> from <em>procedure</em>:</p>

<table>
  <thead>
    <tr>
      <th>Artifact</th>
      <th>What it captures</th>
      <th>When it pays off</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>README / overview</td>
      <td>What the thing is, how to run it</td>
      <td>First hour for you <em>and</em> strangers</td>
    </tr>
    <tr>
      <td>Decision records (ADR)</td>
      <td>Why you picked tradeoff A over B</td>
      <td>Six months later when someone asks “who chose this?”</td>
    </tr>
    <tr>
      <td>Issue / milestone</td>
      <td>Scoped work with acceptance hints</td>
      <td>Anything multi-step you cannot finish tonight</td>
    </tr>
    <tr>
      <td>Runbook</td>
      <td>Steps + rollback</td>
      <td>Anything that touches prod or user machines</td>
    </tr>
  </tbody>
</table>

<p>Architecture Decision Records were popularized for documenting significant technical choices in version control—short markdown files, immutable history, searchable blame.<sup id="fnref:adr" role="doc-noteref"><a href="#fn:adr" class="footnote" rel="footnote">1</a></sup></p>

<p>For shipping a CLI installer as a <strong>.NET tool</strong>, Microsoft documents the packaging model explicitly: your package must declare the <code class="language-plaintext highlighter-rouge">dotnet tool</code> asset, expose an entry assembly, and consumers install with <code class="language-plaintext highlighter-rouge">dotnet tool install -g &lt;packageId&gt;</code> (or use a manifest for local tools).<sup id="fnref:global-tool" role="doc-noteref"><a href="#fn:global-tool" class="footnote" rel="footnote">2</a></sup> NuGet’s own reference describes optional package types such as <code class="language-plaintext highlighter-rouge">DotnetTool</code> when authoring packages intended for that pipeline.<sup id="fnref:nuget-pack-type" role="doc-noteref"><a href="#fn:nuget-pack-type" class="footnote" rel="footnote">3</a></sup></p>

<p>None of that replaces a product spec—but it tells you <em>where in the official docs to look</em> once you admit you forgot what you were doing.</p>

<h2 id="practice-minimal-spec-outline-for-simple-installer">Practice: minimal spec outline for <code class="language-plaintext highlighter-rouge">simple-installer</code></h2>

<p>If you want something copy-pasteable:</p>

<ol>
  <li><strong>Goal</strong> — zip-backed package format + <code class="language-plaintext highlighter-rouge">manifest.json</code> schema version.</li>
  <li><strong>Commands</strong> — <code class="language-plaintext highlighter-rouge">pack</code>, <code class="language-plaintext highlighter-rouge">install</code>, <code class="language-plaintext highlighter-rouge">list</code>, <code class="language-plaintext highlighter-rouge">remove</code>, <code class="language-plaintext highlighter-rouge">update</code> (even if some are stubs).</li>
  <li><strong>Distribution</strong> — NuGet package layout today; <code class="language-plaintext highlighter-rouge">DotnetTool</code> package type target state.</li>
  <li><strong>Non-goals</strong> — MSI, services, elevation stories you refuse to own.</li>
  <li><strong>Open questions</strong> — global tool vs framework-dependent bundle; signing.</li>
</ol>

<p>That five-bullet skeleton is enough to stop thrashing.</p>

<h2 id="pitfalls-i-walked-into">Pitfalls I walked into</h2>

<ul>
  <li>Assuming “I’ll remember why this flag exists” (false—entropy wins).</li>
  <li>Shipping features without an issue number or ADR pointer (future you files a silent lawsuit).</li>
  <li>Mixing “blog post spec” with zero tests—this doc <em>helps humans</em>; automated checks still matter.</li>
</ul>

<h2 id="lets-get-started">Let’s get started</h2>

<h3 id="the-problem-we-are-trying-to-solve-the-specs">The problem we are trying to solve (the specs)</h3>

<p>Installers are unnecessary complex, and they usually have a lot of features that are not needed. So we want to make a simple installer tool that can do the following: Install a program, uninstall a program, update a program, and list installed programs.</p>

<h3 id="the-design">The design</h3>

<p>We want to make a CLI tool that can be used to install, uninstall, update and list installed programs. We want to make it as simple as possible, so we will use a simple zip file as the package format. The zip file will contain a <code class="language-plaintext highlighter-rouge">manifest.json</code> file that will contain the metadata for the package, and the files that will be installed.</p>

<h4 id="menu-flow">Menu flow</h4>

<pre><code class="language-mermaid">graph TD
    A[Start Installer] --&gt;|Check for ZIP file| B
    B --&gt;|Yes| C[Show Menu with Install Option]
    B --&gt;|No| D[Show Menu without Install Option]
    C --&gt;|Select Install| E[Install Process]
    C --&gt;|Select Pack| F[Pack Process]
    C --&gt;|Select Exit| G[Exit Installer]
    D --&gt;|Select Pack| F
    D --&gt;|Select Exit| G
    E --&gt; H[Return to Menu] -.-&gt; B
    F --&gt; |Pack completes| H 
    G --&gt; I[End Installer]
    H --&gt; C
</code></pre>

<blockquote>
  <p>[!NOTE]
Use Chat Gpt-4 to generate some C# code</p>
</blockquote>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.IO</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Spectre.Console</span><span class="p">;</span>

<span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
    <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Func</span><span class="p">&lt;</span><span class="kt">bool</span><span class="p">&gt;</span> <span class="n">isZipFilePresent</span> <span class="p">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="n">File</span><span class="p">.</span><span class="nf">Exists</span><span class="p">(</span><span class="s">"yourfile.zip"</span><span class="p">);</span>
        <span class="n">Action</span> <span class="n">showMenu</span> <span class="p">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="nf">ShowMenuWithSpectre</span><span class="p">(</span><span class="nf">isZipFilePresent</span><span class="p">());</span>

        <span class="nf">showMenu</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">static</span> <span class="k">void</span> <span class="nf">ShowMenuWithSpectre</span><span class="p">(</span><span class="kt">bool</span> <span class="n">isZipPresent</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">cmd</span> <span class="p">=</span> <span class="n">AnsiConsole</span><span class="p">.</span><span class="nf">Prompt</span><span class="p">(</span>
            <span class="k">new</span> <span class="n">SelectionPrompt</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()</span>
                <span class="p">.</span><span class="nf">Title</span><span class="p">(</span><span class="s">"Choose an [green]option[/]:"</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">AddChoices</span><span class="p">(</span><span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="s">"Pack"</span><span class="p">,</span> <span class="s">"Exit"</span> <span class="p">})</span>
                <span class="p">.</span><span class="nf">AddChoiceIf</span><span class="p">(</span><span class="n">isZipPresent</span><span class="p">,</span> <span class="s">"Install"</span><span class="p">));</span>

        <span class="k">switch</span> <span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">case</span> <span class="s">"Pack"</span><span class="p">:</span>
                <span class="nf">Pack</span><span class="p">();</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="k">case</span> <span class="s">"Exit"</span><span class="p">:</span>
                <span class="nf">Exit</span><span class="p">();</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="k">case</span> <span class="s">"Install"</span><span class="p">:</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">isZipPresent</span><span class="p">)</span>
                <span class="p">{</span>
                    <span class="nf">Install</span><span class="p">();</span>
                <span class="p">}</span>
                <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">static</span> <span class="k">void</span> <span class="nf">Pack</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">AnsiConsole</span><span class="p">.</span><span class="nf">MarkupLine</span><span class="p">(</span><span class="s">"[yellow]Packing...[/]"</span><span class="p">);</span>
        <span class="c1">// Packing logic here</span>
    <span class="p">}</span>

    <span class="k">static</span> <span class="k">void</span> <span class="nf">Install</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">AnsiConsole</span><span class="p">.</span><span class="nf">MarkupLine</span><span class="p">(</span><span class="s">"[green]Installing...[/]"</span><span class="p">);</span>
        <span class="c1">// Installation logic here</span>
    <span class="p">}</span>

    <span class="k">static</span> <span class="k">void</span> <span class="nf">Exit</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">AnsiConsole</span><span class="p">.</span><span class="nf">MarkupLine</span><span class="p">(</span><span class="s">"[red]Exiting...[/]"</span><span class="p">);</span>
        <span class="n">Environment</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="m">0</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/dotnet/core/tools/global-tools">Microsoft Learn — Global tools</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/core/tools/local-tools-how-to-manage">Microsoft Learn — How to manage local tools</a></li>
  <li><a href="https://learn.microsoft.com/nuget/create-packages/set-package-type">NuGet — Set a package type</a></li>
  <li><a href="https://adr.github.io/">ADR GitHub — Architecture Decision Records</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:adr" role="doc-endnote">
      <p>ADR GitHub organization — overview of Architecture Decision Records (popularized by Michael Nygard, 2011). https://adr.github.io/ <a href="#fnref:adr" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:global-tool" role="doc-endnote">
      <p>Microsoft Learn — <em>Global tools overview</em> and packaging guidance for .NET CLI tools. https://learn.microsoft.com/dotnet/core/tools/global-tools <a href="#fnref:global-tool" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:nuget-pack-type" role="doc-endnote">
      <p>NuGet docs — <em>Package Type reference</em> (<code class="language-plaintext highlighter-rouge">DotnetTool</code>, <code class="language-plaintext highlighter-rouge">Template</code>, etc.). https://learn.microsoft.com/nuget/create-packages/set-package-type <a href="#fnref:nuget-pack-type" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="documentation" /><category term="opinions" /><summary type="html"><![CDATA[Seventy-five percent done, zero percent remembered. A small installer tool and a big hole where the plan should be.]]></summary></entry><entry><title type="html">ML.NET video tutorials (notes)</title><link href="https://frankhaugen.github.io/blog/2023/08/15/mldotnet-video-tutorials-notes/" rel="alternate" type="text/html" title="ML.NET video tutorials (notes)" /><published>2023-08-15T00:00:00+00:00</published><updated>2023-08-15T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2023/08/15/mldotnet-video-tutorials-notes</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2023/08/15/mldotnet-video-tutorials-notes/"><![CDATA[<p>This is not a course. It is a pile of notes for videos I <em>want</em> to make before I get distracted by another library with <code class="language-plaintext highlighter-rouge">Frank.</code> in the name.</p>

<h2 id="facts-where-mlnet-sits-in-the-ecosystem">Facts: where ML.NET sits in the ecosystem</h2>

<p><strong>ML.NET</strong> is Microsoft’s .NET-first ML stack—data loading (<code class="language-plaintext highlighter-rouge">IDataView</code>), training pipelines, evaluation metrics, and deployment patterns documented under Microsoft Learn. <sup id="fnref:mlnet-intro" role="doc-noteref"><a href="#fn:mlnet-intro" class="footnote" rel="footnote">1</a></sup></p>

<p><strong>Model Builder</strong> (Visual Studio extension) and the <strong><code class="language-plaintext highlighter-rouge">mlnet</code> CLI</strong> automate common scenarios (classification, regression, recommendation, computer vision) so you can iterate without writing boilerplate trainers by hand. <sup id="fnref:model-builder" role="doc-noteref"><a href="#fn:model-builder" class="footnote" rel="footnote">2</a></sup></p>

<p><strong>ONNX</strong> interoperability matters because academic and industry workflows often train in Python then export; ML.NET documents the <strong>ONNX Runtime</strong> dependency and how to run ONNX models from .NET. <sup id="fnref:onnx-mlnet" role="doc-noteref"><a href="#fn:onnx-mlnet" class="footnote" rel="footnote">3</a></sup></p>

<p><strong>Ethics / optics:</strong> game-adjacent demos (aim bots, wall hacks as <em>pedagogy</em>) should foreground responsible disclosure—teach detection and adversarial robustness, not cheat distribution. Microsoft publishes <strong>Responsible AI</strong> principles and Azure Machine Learning documentation on fairness, interpretability, and human review—useful guardrails even for scratch tutorials. <sup id="fnref:rai-ms" role="doc-noteref"><a href="#fn:rai-ms" class="footnote" rel="footnote">4</a></sup> <sup id="fnref:rai-azure" role="doc-noteref"><a href="#fn:rai-azure" class="footnote" rel="footnote">5</a></sup></p>

<h2 id="concept">Concept</h2>

<p>Most ML.NET content falls into two buckets:</p>

<ol>
  <li>“Data scientists, look — C# exists.”</li>
  <li>“C# devs, you can be ML people too, here is a spreadsheet and a dream.”</li>
</ol>

<p>Both are fine. Neither is what I want to build. I want messier, more playful stuff: things you can break, things that feel like games, things where “don’t try this in prod” is the whole point. Yes, that includes the morally questionable territory of aim-bot-shaped pedagogy — <em>teaching</em>, not cheating. Calm down.</p>

<h2 id="episode-0">Episode 0</h2>

<p>Draft / scratch lives in the monorepo of chaos:</p>

<ul>
  <li><a href="https://github.com/frankhaugen/frankhaugen/blob/main/unsorted/from-github-pages/mldotnet-tutorials/mldotnet-tutorial-ep0.md">Episode 0 (markdown) on GitHub</a></li>
</ul>

<p>If that link rots, the internet deserved it.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/">Microsoft Learn — ML.NET documentation hub</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/how-does-mldotnet-work">Microsoft Learn — What is ML.NET?</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/automate-training-with-model-builder">Microsoft Learn — Model Builder</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/automate-training-with-cli">Microsoft Learn — ML.NET CLI</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/install-onnx-runtime">Microsoft Learn — Install ONNX Runtime to use with ML.NET</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/save-load-machine-learning-models-ml-net">Microsoft Learn — Save and load a model in ML.NET</a> (general persistence; ONNX loading is covered in ONNX-specific topics above)</li>
  <li><a href="https://github.com/microsoft/onnxruntime">ONNX Runtime (GitHub)</a></li>
  <li><a href="https://www.microsoft.com/ai/responsible-ai">Microsoft — Responsible AI</a></li>
  <li><a href="https://learn.microsoft.com/azure/machine-learning/concept-responsible-machine-learning">Microsoft Learn — Responsible machine learning (Azure ML)</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:mlnet-intro" role="doc-endnote">
      <p>Microsoft Learn — <em>What is ML.NET?</em> https://learn.microsoft.com/dotnet/machine-learning/how-does-mldotnet-work <a href="#fnref:mlnet-intro" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:model-builder" role="doc-endnote">
      <p>Microsoft Learn — <em>What is Model Builder?</em> https://learn.microsoft.com/dotnet/machine-learning/automate-training-with-model-builder <a href="#fnref:model-builder" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:onnx-mlnet" role="doc-endnote">
      <p>Microsoft Learn — <em>Install ONNX Runtime to use with ML.NET</em> (ONNX Runtime packages and GPU/CPU notes). https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/install-onnx-runtime <a href="#fnref:onnx-mlnet" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rai-ms" role="doc-endnote">
      <p>Microsoft — <em>Responsible AI</em> overview (principles and practices). https://www.microsoft.com/ai/responsible-ai <a href="#fnref:rai-ms" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:rai-azure" role="doc-endnote">
      <p>Microsoft Learn — <em>What is responsible machine learning?</em> (Azure Machine Learning). https://learn.microsoft.com/azure/machine-learning/concept-responsible-machine-learning <a href="#fnref:rai-azure" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="ML.NET" /><category term="tutorials" /><summary type="html"><![CDATA[Scratchpad for a tutorial series that refuses to be "hello iris" again.]]></summary></entry><entry><title type="html">Falling into madness during the 2020 ML.NET hackathon</title><link href="https://frankhaugen.github.io/blog/2020/11/20/mldotnet-hackathon-falling-into-madness/" rel="alternate" type="text/html" title="Falling into madness during the 2020 ML.NET hackathon" /><published>2020-11-20T00:00:00+00:00</published><updated>2020-11-20T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2020/11/20/mldotnet-hackathon-falling-into-madness</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2020/11/20/mldotnet-hackathon-falling-into-madness/"><![CDATA[<p><strong>Content warning:</strong> Python ecosystem trauma, Red Bull, and hubris.</p>

<h2 id="facts-what-we-thought-we-were-assembling">Facts: what we thought we were assembling</h2>

<p><strong>ML.NET</strong> is Microsoft’s machine-learning stack for .NET—training via CLI / Model Builder / notebooks and inference loaded into apps using standardized loaders for ONNX / TF formats depending on scenario.<sup id="fnref:mlnet-overview" role="doc-noteref"><a href="#fn:mlnet-overview" class="footnote" rel="footnote">1</a></sup></p>

<p><strong>ONNX</strong> describes models so frameworks can exchange graphs; ML.NET documents ONNX as a first-class interchange path alongside native trainers.<sup id="fnref:mlnet-onnx" role="doc-noteref"><a href="#fn:mlnet-onnx" class="footnote" rel="footnote">2</a></sup></p>

<p><strong>TensorFlow Object Detection API</strong> is Google’s Python-centric pipeline for training detectors—the tutorials assume coherent CUDA + protobuf stacks, which is <em>exactly</em> where “works on my laptop” dies.</p>

<p><strong>tensorflow-onnx (<code class="language-plaintext highlighter-rouge">tf2onnx</code>)</strong> is the community converter we leaned on to escape TensorFlow graphs toward ONNX—silent failures in converters are doubly painful because you stare at success logs while files never appear.<sup id="fnref:tf2onnx" role="doc-noteref"><a href="#fn:tf2onnx" class="footnote" rel="footnote">3</a></sup></p>

<p>This factual scaffolding does not make the pain fake—it explains why “marketing-simple ≠ dependency-simple.”</p>

<p>“Do something with ml.net” was the task given by the cool guys and gals from the ML.NET community. That’s easy enough, right? WRONG!!! It’s stressful and frustrating.</p>

<p>The idea was to use a game that have been re-created as Open Source in C#. Two alternatives was the most promising: OpenRA, and ManagedDoom, (Red Alert and Doom). We quickly found it to be above our skill level to to something with Red Alert, so Doom it was, and what an apt title for our little team.</p>

<h2 id="mlnet-plays-doom">ML.net plays Doom</h2>
<p>Having Ml.net play Doom seemed to be fun, and educational, it would also be a rare addition to the content people have been making, (not a lot of ml.net have been used for gaming, so it could really be something new and exciting). We found a great tutorial with code for object detection using onnx-models in ml.net, and having an algorithm that remembers how it moves forward, detects an enemy and learns that pattern can be achieved by other</p>

<h2 id="the-team">The team</h2>
<p>We were 4 people, (and beer), three “full” developers and one junior. The junior had some tutorials with machine learning under her belt and C# skills. One of the devs was mainly a Kotlin dev, and the remaining two was .net developers, one working for an AI/ML based fintech company. We called ourselves “Omega Force” because Frank already had created a GitHub organization with that name, and it’s cool. A small snag became apparent pretty quickly and that was that the Kotlin dev expected C# to be pretty simple, but found it confusing, and also other things came up that he had to take care of. One of the C# devs needed some RnR and threw in the towel almost immediately. There we were with 30% of the C# skills gone, and the moral-boosting Kotlin dev was suddenly also unavailable…</p>

<h2 id="keep-calm-and-code-on">Keep calm and code on</h2>
<p>A bit frustrated, we started attacking the issue, and five minutes after a good nights sleep it didn’t seem too bad, we had a nice example of how to use ml.net to identify objects in an image. The example used an object labeling tool called <code class="language-plaintext highlighter-rouge">labelimg</code> that require one to go through every image and created boxes around objects that are then tagged, and it saves an xml-file per image. Having sprites from Doom made it really easy to just write a few lines of C# code and make the XML based on the entire image being the “box” to be labeled. 1400 sprites took 15 minutes to process:2 minutes of manual tagging, 1 minute of thinking 10 minutes of programming, and 2 minutes for the app to run. Armed with XML-files and the sprites we got going trying to setup Anaconda, Tensorflow, NVidia CUDA-stuff, and a bunch more…</p>

<p>it didn’t go well…</p>

<h2 id="troubles-with-tensorflow">Troubles with Tensorflow</h2>
<p>When you look at tutorials and documentation it looks so easy, but we tried many times, but nothing seems to work… What do you do then??? Drink a lot, cry a lot, and yell at people for no reason a lot. It’s one thing to have a hard time doing something difficult, but when it seems so easy in the tutorials, making it seem like it takes ten minutes to get started. But error message like Python not finding <code class="language-plaintext highlighter-rouge">object_detection.utils</code> leads down a path of misery and self-loathing as you read the snarky answers to anyone brave enough to have asked a question online about it, (when you have done a <code class="language-plaintext highlighter-rouge">pip install object_detection</code> without errors, you expect it to just work). You finally build up the curage to ask the question to ask a question on stack overflow, and then there are no shortage of downvotes, (a small line encuraging downvoters to comment to help improve the question was edited away almost instantly), but you get that one comment that gets you over the hurdle, (turned out that ProtoBuf didn’t install correctly, and the errors are easy to miss).</p>

<p>A few more hours go on and we finally can start training… FOUR DAYS INTO THE HACKATHON!!! And the defaults take about 18 hours to train on a 95th percentile powerful computer… And paying a couple of hundred dollars for cloud computing to fastrack it, isn’t feasable</p>

<h2 id="houston-we-have-a-model">Houston we have a model</h2>
<p>So finally we had a model, (we created a barely trained one just to experiment), now it’s just converting it from TF to ONNX, and that can’t be hard, right? It’s not according to the documentation… And we run it, it takes 15 minutes, with the Red Bull to blood ratio being about 2:1 in our bodies, we gather around and and then our moment of elation ends when the model.onnx file isn’t there… Maybe it was saved someplace weird? Start searching C:\ for <code class="language-plaintext highlighter-rouge">*.onnx</code>, and get a bunch of false positives… Now the tears are starting to well up as one realizes that there are not a single line of actual, functional, (as in working, not paradigmic), code and there is 30 hours left of the hackathon… When looking at the source code for <code class="language-plaintext highlighter-rouge">tf2onnx</code>, it’s aparent that it just quits at some random point without an error message or any kind of logging… Should we give up? Should we surrender to the dispair?? It’s so tempting…</p>

<h2 id="scope-narrowing">Scope narrowing</h2>
<p>Having reduced to the bearest of scopes, it seems like a win to have created an aimbot, and learned to at least use the tools available, but even that seems too abitious 30 hours before the deadline… Maybe this rant, blog, whatever, is the only actual result of the entire catastrophy</p>

<h2 id="exploring-the-tensorflow-model">Exploring the Tensorflow model</h2>
<p>Using Netron to view the created <code class="language-plaintext highlighter-rouge">.pb</code>-model file, which took 17 hours to train, 3 days to even start the training, and now it seems wrong, having 1400 “nodes” as inputs, error messages about unknown types and takes ten minutes to open. Something is crazy wrong and noone have any ideas… The internet keeps presenting these steps as easy and straight forward yet it’s anything but. We had the idea that since Tensorflow and all this ML stuff is presented as such plug-n-play -processes that anyone can do it, it shouldn’t be too hard to get a model going. But with two people spending hours each day reading, experimenting, and watching online tutorials, the easy part seems to be a marketing ploy from Google, and that Google as a service is hiding anything indicating it’s not super-duper-awesome-easy to do ML training and conversion… Also, I HATE PYTHON! The language is clumsy, (significant whitespace is an abomination, and herecy against the natural order of the univers), but the entire ecosystem is so fragile that you update one thing, that updates something else, that then breaks the functionality of the <code class="language-plaintext highlighter-rouge">.py</code> file you try to run, and you end up going into actual libraries and tweaking code… How can people rave about Python as the greatest thing since the invention of fire, I have no idea…</p>

<h2 id="what-to-do-now">What to do now?</h2>
<p>The original MASH movie, (that spawned the excellent TV show with the same name), has a theme song that has been stuck in my head for most of this process, it’s lyrics is very fitting for the situation, (look it up). Maybe, just maybe, there is a chance that someone can help us? Mayby @luisquintanilla or @briacht can assist? They seem like very nice an knowledgable people, only one snag.. How to contack them? O.o</p>

<p>Turns out email is a powerful tool! Bri was reasonably quick to answer and forward our plea for help to Luis, who was very helpful, (I have yet to interact with a Microsoft employee that isn’t service-minded and likable). Luis got us “on track” but with very little time remaining, and only 1.5 people of the initial 4 left on the team, delivering anything seems impossible.</p>

<h2 id="oh-microsofts-model-builder-has-object-detection-3">Oh! Microsoft’s Model Builder has object detection &lt;3</h2>
<p>So as a last ditch effor to salvage this situation, I throw toghether some code to make Vott Json for my sprites, and lob it up into Azure, hoping I can afford it, (turns out that it’s not possible to setup anything really expensive for some reason). three hours later, it fails… I reduce the data complexity by only having one “label” and about 600 sprites. It takes almost two hours to finish, but when I plug in the model into my simplified WPF-app, (uses a still-image during development, in stead of capturing the game using OpenCvSharp), it give a 0,999876 score on the top left quadrant of the image. The area the model claim to have an enemy is</p>

<h2 id="the-surrender">The surrender</h2>
<p>Now it’s just me at 04:00 UTC with 4 hours left until the deadline and all there is to show for all this time is a lot of frustration, regret and self-loathing. I didn’t even get to drink as much beer as I planned… There will be no entry</p>

<p>, at least I have a lot of experiences to bring with me.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/how-does-mldotnet-work">Microsoft Learn — What is ML.NET?</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/save-load-machine-learning-models-ml-net">Microsoft Learn — ONNX models in ML.NET</a></li>
  <li><a href="https://github.com/onnx/tensorflow-onnx">tensorflow-onnx (GitHub)</a></li>
  <li><a href="https://tensorflow-object-detection-api-tutorial.readthedocs.io/en/latest/training.html">TensorFlow Object Detection API tutorial (ReadTheDocs)</a></li>
  <li><a href="https://github.com/fralik/ObjectDetection-MLNet">ML.NET object detection sample (GitHub — fralik)</a></li>
  <li><a href="https://github.com/sskodje/ScreenRecorderLib">ScreenRecorderLib (GitHub)</a></li>
  <li><a href="https://vbstudio.hu/en/blog/year-2019/20190317-Growing-an-AI-with-NEAT">VB Studio — Wolfenstein / NEAT article</a></li>
</ul>

<h2 id="useful-snippets">Useful snippets</h2>
<p>|Description||
|—|—|
|Convert .pd to .onnx comand|<code class="language-plaintext highlighter-rouge">python -m tf2onnx.convert  --input /exported-models/doom1/saved_model/saved_model.pb --inputs input_1:0 --outputs probs/Softmax:0 --output model2.onnx</code>|
|Train model|<code class="language-plaintext highlighter-rouge">python model_main_tf2.py --model_dir=models --pipeline_config_path=models/pipeline.config</code>|
|Convert <code class="language-plaintext highlighter-rouge">*-xml</code>s to tfrecords|<code class="language-plaintext highlighter-rouge">python generate_tfrecord.py -x images/train -l annotations/label_map.pbtxt -o annotations/train.record</code>|</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:mlnet-overview" role="doc-endnote">
      <p>Microsoft Learn — <em>What is ML.NET?</em>. https://learn.microsoft.com/dotnet/machine-learning/how-does-mldotnet-work <a href="#fnref:mlnet-overview" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:mlnet-onnx" role="doc-endnote">
      <p>Microsoft Learn — <em>Load ONNX models with ML.NET</em>. https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/save-load-machine-learning-models-ml-net <a href="#fnref:mlnet-onnx" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:tf2onnx" role="doc-endnote">
      <p>ONNX GitHub — <code class="language-plaintext highlighter-rouge">onnx/tensorflow-onnx</code> converter project. https://github.com/onnx/tensorflow-onnx <a href="#fnref:tf2onnx" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="ML.NET" /><category term="hackathon" /><category term="story" /><summary type="html"><![CDATA[Doom sprites, TensorFlow tears, ONNX that never showed up, and the gap between "ten minutes in the docs" and four days of your life.]]></summary></entry><entry><title type="html">Thoughts on time and date in applications</title><link href="https://frankhaugen.github.io/blog/2020/10/22/thoughts-on-time-and-date-in-applications/" rel="alternate" type="text/html" title="Thoughts on time and date in applications" /><published>2020-10-22T00:00:00+00:00</published><updated>2020-10-22T00:00:00+00:00</updated><id>https://frankhaugen.github.io/blog/2020/10/22/thoughts-on-time-and-date-in-applications</id><content type="html" xml:base="https://frankhaugen.github.io/blog/2020/10/22/thoughts-on-time-and-date-in-applications/"><![CDATA[<p>I’m making an app with chat, so time actually matters. Most advice online boils down to “pick a format” — cool, thanks — so I wrote this to park my own opinions somewhere I can link instead of re-explaining them in PR comments.</p>

<p><strong>Fair warning:</strong> this is not ISO-8601 fanfic. It is me being annoyed in a structured way.</p>

<h3 id="tldr">TL;DR</h3>

<p>Time and dates are hard. Store <strong>UTC instant</strong> (often as UNIX milliseconds or <code class="language-plaintext highlighter-rouge">DateTimeOffset</code> with explicit offset), render <strong>local</strong> with explicit timezone rules, and never pretend culture-specific strings round-trip.</p>

<hr />

<h2 id="facts-what-the-standards-actually-say">Facts: what the standards actually say</h2>

<p><strong>RFC 3339</strong> defines a profile of ISO 8601 for timestamps on the Internet—think <code class="language-plaintext highlighter-rouge">2012-10-03T12:13:00Z</code>. It exists precisely because naive date strings caused interoperability pain.<sup id="fnref:rfc3339" role="doc-noteref"><a href="#fn:rfc3339" class="footnote" rel="footnote">1</a></sup></p>

<p><strong>ISO 8601</strong> is the broader international standard for representing dates and times; many APIs colloquially say “ISO 8601” when they mean something closer to RFC 3339.<sup id="fnref:iso8601wiki" role="doc-noteref"><a href="#fn:iso8601wiki" class="footnote" rel="footnote">2</a></sup></p>

<p><strong>POSIX time</strong> counts seconds since the Unix epoch (<code class="language-plaintext highlighter-rouge">1970-01-01T00:00:00Z</code>), ignoring leap seconds in the usual <code class="language-plaintext highlighter-rouge">time_t</code> encoding—so “Unix seconds” is not always identical to civil atomic clocks, but it is what most databases and APIs mean when they say <code class="language-plaintext highlighter-rouge">unix</code>.<sup id="fnref:posix" role="doc-noteref"><a href="#fn:posix" class="footnote" rel="footnote">3</a></sup></p>

<p><strong>.NET specifics:</strong> <code class="language-plaintext highlighter-rouge">DateTimeOffset</code> captures an instant with an offset; plain <code class="language-plaintext highlighter-rouge">DateTime</code> can represent UTC, local, or “unspecified,” and mixing those modes is a historical footgun Microsoft documents explicitly.<sup id="fnref:dtbest" role="doc-noteref"><a href="#fn:dtbest" class="footnote" rel="footnote">4</a></sup> Internally .NET also exposes <strong>ticks</strong> (100 ns intervals since midnight <code class="language-plaintext highlighter-rouge">0001-01-01</code> UTC on the Gregorian calendar)—finer than milliseconds when you need monotonic-ish stamps inside one machine.<sup id="fnref:ticks" role="doc-noteref"><a href="#fn:ticks" class="footnote" rel="footnote">5</a></sup></p>

<p><strong>Popular culture check:</strong> Patrick McKenzie’s <em>Falsehoods programmers believe about time</em> catalogs edge cases (DST, leap seconds, historical calendar reforms) that invalidate “just parse the string” optimism.<sup id="fnref:falsehoods" role="doc-noteref"><a href="#fn:falsehoods" class="footnote" rel="footnote">6</a></sup></p>

<hr />

<h3 id="why-is-time-important">Why is time important?</h3>

<p>You might think that for a simple chat app, there’s no need to use time, but you should always include time. It’s a great way to sort, though this might be acheived by some auto-incremented number.</p>

<p><strong>Fact:</strong> totally ordered message IDs can sort without clock sync; <strong>practice:</strong> you still want human-visible ordering and reconciliation when replicas diverge—timestamps (with sane collision handling) remain the default hammer.</p>

<h3 id="what-datetime-format-to-use">What DateTime format to use?</h3>

<p>There are plenty of libraries to convert time and dates to whatever format you want, so why not just use what you use in your daily life? Let me show you:</p>

<p>Some examples of the same DateTime (seconds an milliseconds omitted, mostly)</p>

<table>
  <thead>
    <tr>
      <th>DateTime code</th>
      <th>Description</th>
      <th>My opinion</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2012-10-03T12:13:00Z</td>
      <td>UTC time friendly</td>
      <td> </td>
    </tr>
    <tr>
      <td>10-03-12 12:13PM</td>
      <td>US/UK short AM/PM</td>
      <td>Stupid, because counting 12 + 12 only makes sense when using</td>
    </tr>
    <tr>
      <td>10-03-12 12:13</td>
      <td>US/UK short 24H</td>
      <td>Internaly sencible, internationally stupid</td>
    </tr>
    <tr>
      <td>10-03-2012 12:13PM</td>
      <td>US/UK long AM/PM</td>
      <td>see above</td>
    </tr>
    <tr>
      <td>03-10-12 12:13</td>
      <td>Short international</td>
      <td>What I grew up using, but it’s confusing</td>
    </tr>
    <tr>
      <td>03-10-2012 12:13</td>
      <td>Long international</td>
      <td>The most sencible day-to-day format</td>
    </tr>
    <tr>
      <td>2012-10-03 12:13</td>
      <td>Sortable long international</td>
      <td>Very useful for sorting</td>
    </tr>
    <tr>
      <td>2012-40-3 12:13</td>
      <td>year-week-weekday international</td>
      <td>Very useful for class schedules</td>
    </tr>
    <tr>
      <td>2012-40-4 12:13</td>
      <td>year-week-weekday US</td>
      <td>Who thought it was a good idea to start the week on Sunday?</td>
    </tr>
    <tr>
      <td>201210031213</td>
      <td>sortable character safe</td>
      <td>Great for document naming or a timestamp on a database-row</td>
    </tr>
    <tr>
      <td>100320121213</td>
      <td>non-sortable character safe US</td>
      <td>NEVER use this</td>
    </tr>
    <tr>
      <td>1349266380</td>
      <td>UNIX time seconds</td>
      <td>Somewhat useful</td>
    </tr>
    <tr>
      <td>1349259180000</td>
      <td>UNIX time milliseconds</td>
      <td>THE best way to use date and time, (see bellow)</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>You might be confused by the “snowflaking” of the US in all of this, but it has it’s reasons, mostly the unwillingness to risk confusion among it’s citizens. However the US Military does use the the UTC time,</p>
</blockquote>

<p>You might say “hey, how about my country/culture’s calendar? This is awefully eurocentric”, and yes it is completely correct and a good point to make. Here’s some examples of calendars from around the world that is in use today:</p>

<table>
  <thead>
    <tr>
      <th>Date</th>
      <th>Decription</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>5773-Tishri-18</td>
      <td>Hebrew</td>
    </tr>
    <tr>
      <td>1433-Dhu I-Qa’da-18</td>
      <td>Islamic</td>
    </tr>
    <tr>
      <td>1391-Mehr-13</td>
      <td>Persian</td>
    </tr>
    <tr>
      <td>1934-Asvina-12</td>
      <td>Indian Civil</td>
    </tr>
    <tr>
      <td>Dragon-Dog-18</td>
      <td>Chinese variant 1 (not actually used)</td>
    </tr>
    <tr>
      <td>4710-8-18</td>
      <td>Chinese variant 2 (China actually use Gregorian)</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>there are scores upon scores of others, some only used by tribes in micronesia or in the himalayas, (that there’s probably a conversion for anyway, because developers love a challange, or some social anthropologist needs it in their research</p>
</blockquote>

<p>I will not denegrade the non-gregorian calanders, and they are useful for those who share them, but I don’t think I’ll be using Dragon-dog-18, (though if I were, I would have to have some logic to assume it’s the closest Dragon in the last 12 years)</p>

<h3 id="but-what-about-timezones">But what about Timezones?</h3>

<p>Short answer is: Ignore it, and use a universal time with conversion logic!</p>

<p>Great video about timezones every dev should watch:</p>

<p><a href="http://www.youtube.com/watch?feature=player_embedded&amp;v=-5wpm-gesOY" target="_blank" rel="noopener noreferrer"><img src="http://img.youtube.com/vi/-5wpm-gesOY/0.jpg" alt="YouTube: timezones for programmers" width="240" height="180" /></a></p>

<p>Tom Scott’s piece on YouTube walks through why civil time is political—worth pairing with the Olson/tz database maintained as IANA Time Zone Data.<sup id="fnref:tzdb" role="doc-noteref"><a href="#fn:tzdb" class="footnote" rel="footnote">7</a></sup></p>

<h2 id="conclusion-use-millisecond-unix-time">Conclusion: use millisecond UNIX time</h2>

<p>The computer counts in this time unit, (.net actually has a unit caller “ticks” that is MUCH smaller)</p>

<p><strong>Practice recap:</strong></p>

<ol>
  <li>Persist an <strong>unambiguous instant</strong> (UTC / offset-aware / epoch ms).</li>
  <li>Convert for display with <strong>explicit</strong> timezone rules (<code class="language-plaintext highlighter-rouge">TimeZoneInfo</code>, Noda Time, etc.).</li>
  <li>Never treat locale-formatted strings as canonical keys—they are lossy projections.</li>
</ol>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://datatracker.ietf.org/doc/html/rfc3339">RFC 3339</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/fundamentals/runtime-library/system/datetime-best-practices">Microsoft Learn — DateTime best practices</a></li>
  <li><a href="https://learn.microsoft.com/dotnet/standard/datetime/time-zone-overview">Microsoft Learn — Time zones</a></li>
  <li><a href="https://www.iana.org/time-zones">IANA Time Zone Database</a></li>
  <li><a href="https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time">Patrick McKenzie — Falsehoods programmers believe about time</a></li>
  <li><a href="https://www.youtube.com/watch?v=-5wpm-gesOY">Tom Scott — YouTube on timezones</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:rfc3339" role="doc-endnote">
      <p>IETF RFC 3339 — <em>Date and Time on the Internet: Timestamps</em>. https://datatracker.ietf.org/doc/html/rfc3339 <a href="#fnref:rfc3339" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:iso8601wiki" role="doc-endnote">
      <p>Wikipedia — <em>ISO 8601</em> (overview and examples; not the paid standard text itself). https://en.wikipedia.org/wiki/ISO_8601 <a href="#fnref:iso8601wiki" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:posix" role="doc-endnote">
      <p>Open Group Base Specifications — epoch and <code class="language-plaintext highlighter-rouge">time_t</code> discussion in POSIX / UNIX time material. https://pubs.opengroup.org/onlinepubs/9699919799/ <a href="#fnref:posix" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:dtbest" role="doc-endnote">
      <p>Microsoft Learn — <em>Best practices for working with DateTime and DateTimeOffset in .NET</em>. https://learn.microsoft.com/dotnet/fundamentals/runtime-library/system/datetime-best-practices <a href="#fnref:dtbest" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:ticks" role="doc-endnote">
      <p>Microsoft Learn — <em>DateTime.Ticks property</em>. https://learn.microsoft.com/dotnet/api/system.datetime.ticks <a href="#fnref:ticks" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:falsehoods" role="doc-endnote">
      <p>Patrick McKenzie (Kalzumeus Software), <em>Falsehoods programmers believe about time</em>. https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time <a href="#fnref:falsehoods" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:tzdb" role="doc-endnote">
      <p>IANA — <em>Time Zone Database</em> (often packaged as <code class="language-plaintext highlighter-rouge">tzdata</code>). https://www.iana.org/time-zones <a href="#fnref:tzdb" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Frank R. Haugen</name></author><category term="DateTime" /><category term="opinions" /><summary type="html"><![CDATA[Chat apps, calendars nobody agrees on, timezones, and why I reach for UNIX milliseconds when the world wants a pretty string.]]></summary></entry></feed>