<?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://dowdeswells.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dowdeswells.github.io/" rel="alternate" type="text/html" /><updated>2026-08-15T12:47:39+10:00</updated><id>https://dowdeswells.github.io/feed.xml</id><title type="html">Software Development Thoughts</title><subtitle>What&apos;s taking up my free time lately.</subtitle><entry><title type="html">Structured coding and refactoring</title><link href="https://dowdeswells.github.io/2026/08/15/ai-excursion-part-5.html" rel="alternate" type="text/html" title="Structured coding and refactoring" /><published>2026-08-15T00:00:00+10:00</published><updated>2026-08-15T00:00:00+10:00</updated><id>https://dowdeswells.github.io/2026/08/15/ai-excursion-part-5</id><content type="html" xml:base="https://dowdeswells.github.io/2026/08/15/ai-excursion-part-5.html"><![CDATA[<h2 id="the-goal">The goal</h2>
<p>The separation of concern skill seems to be working quite well. The code does seem to be structured in a way that I understand and I’m reasonably happy with the quality. There are, however, quite a few occurrences of repeated code. Also the size of the classes can get quite large. I wonder whether a skill could find any improvements?</p>

<p>I’m going to work on this with the assumption that the AI knows all the concepts. It just needs me to tell it my preferences and priorities.</p>

<p>My first attempt at skill is a very simple one. Focusing on modular design, cohesion and coupling. So as not to make the skill too long I will throw in there a reference to Robert Martin’s Clean Code.</p>

<h2 id="the-first-skill">The first skill</h2>
<p>Here is my first attempt at the skill.</p>

<details>
  <summary><strong>structured coding skill</strong></summary>
  <div class="snippet-content">
    
<h1 id="structured-coding-definition">Structured Coding Definition</h1>

<p>Consider the various levels of coding units, such as modules and classes and routines</p>

<h2 id="the-factors-of-well-structured-code-are">The factors of well structured code are:</h2>

<ol>
  <li>high cohesion - A routine should do one thing and do it well</li>
  <li>low coupling - Minimizing dependencies and direct connections between different modules.</li>
  <li>no repeating code - Identify coding in routines that are very similar and work to extract a parameterized, reusable unit</li>
</ol>

<h2 id="the-goals-of-structuring-are">The goals of structuring are:</h2>

<p>Aim for modules that are focused on the inside (High Cohesion) and independent on the outside (Low Coupling). Modules 
communicate through simple, well-defined interfaces without needing to know the internal workings of other modules.</p>

<h2 id="clean-code">Clean Code</h2>

<p>Reference Robert Martin’s (aka Uncle Bob) Clean Code</p>

  </div>
</details>

<h3 id="the-outcome">The outcome</h3>
<p>Running the skill via the agent on just a single class the CosmosEventStore produced the desired effect. The repeated code was removed, and some other changes were made, but nothing outside of the class, which is what I specified in the prompt.</p>

<h2 id="gang-of-four">Gang of Four</h2>
<p>I’m interested to know what I can get away with without writing too many words in the skill. I prompted the ai to look at the Cosmos event store and report on any Gang Of Four patterns that would go well with that code. The AI didn’t need any more explanation. It went through the Gang Of Four patterns individually and came up with a report. In the end the recommendation was a strategy and a builder. Unfortunately it then implemented them as internal sealed classes inside the main class, which only made the file bigger. I guess a skill somehow needs to apply judgement on file size and general reusability of utilities.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[The goal The separation of concern skill seems to be working quite well. The code does seem to be structured in a way that I understand and I’m reasonably happy with the quality. There are, however, quite a few occurrences of repeated code. Also the size of the classes can get quite large. I wonder whether a skill could find any improvements?]]></summary></entry><entry><title type="html">More thoughts on Grilling</title><link href="https://dowdeswells.github.io/2026/08/11/ai-excursion-part-4.html" rel="alternate" type="text/html" title="More thoughts on Grilling" /><published>2026-08-11T00:00:00+10:00</published><updated>2026-08-11T00:00:00+10:00</updated><id>https://dowdeswells.github.io/2026/08/11/ai-excursion-part-4</id><content type="html" xml:base="https://dowdeswells.github.io/2026/08/11/ai-excursion-part-4.html"><![CDATA[<h2 id="grilling-on-a-token-usage-requirement">Grilling on a token usage requirement</h2>
<p>I’ve just done a grilling session on a requirement to log token usage. Now that I have my blog up and running I can record my thoughts immediately. Firstly, while the grilling is powerful, if you deviate with a question, midstream it can get confusing as to what the AI has decided on your answers. Sometimes it needs to be told that you didn’t actually answer the question so that you can go back and review. Secondly, I was able to change my mind or introduce new or changed concepts, but I’m not fully convinced that this is a free flowing conversation.</p>

<p>As you watch the agents reasoning output you can sometimes see that it has made, or possibly made, a decision that you don’t agree with. For example in this case I could see that the batching of token usage records for a chat turn was not being considered. In the end the agent and I agreed this was a phase 2 concern. A pretty good agile outcome.</p>

<p>I think a full spec iteration would require multiple grilling sessions over the same spec, each with a limited scope for changes.</p>

<p>I wonder how a team would cope with the verification of a spec. Who would decide when the spec is complete and correct?</p>

<p>Maybe if you get so far down the wrong road it’s better to throw away and start again.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Grilling on a token usage requirement I’ve just done a grilling session on a requirement to log token usage. Now that I have my blog up and running I can record my thoughts immediately. Firstly, while the grilling is powerful, if you deviate with a question, midstream it can get confusing as to what the AI has decided on your answers. Sometimes it needs to be told that you didn’t actually answer the question so that you can go back and review. Secondly, I was able to change my mind or introduce new or changed concepts, but I’m not fully convinced that this is a free flowing conversation.]]></summary></entry><entry><title type="html">Methodology</title><link href="https://dowdeswells.github.io/2026/08/10/ai-excursion-part-2.html" rel="alternate" type="text/html" title="Methodology" /><published>2026-08-10T00:00:00+10:00</published><updated>2026-08-10T00:00:00+10:00</updated><id>https://dowdeswells.github.io/2026/08/10/ai-excursion-part-2</id><content type="html" xml:base="https://dowdeswells.github.io/2026/08/10/ai-excursion-part-2.html"><![CDATA[<h2 id="keeping-it-real">Keeping it real</h2>
<p>After a few Vibe Code sessions with the DeepSeek API it is very easy to see how an enthusiastic individual could lose track of the source code for the solution they are building. Vibe Coding is an informal habit. For a reproducible outcome some guardrails are needed. The key to this is building a methodology.</p>
<ul>
  <li>Human Validation: Verify the source code. In fact get to know it. Human in the loop is not enough. Human in control is the goal.</li>
  <li>Hardening: This is accomplished by developing a set of SKILLs for your AI agent. These are needed for requirements to specification and specification to implementation. Its an iterative task to get to a point where these SKILLs do the heavy lifting for the methodology.</li>
</ul>

<h3 id="ai-skills-development">AI Skills development</h3>
<p>There are a lot of skills out there. I started by looking at 2 in particular.</p>
<ul>
  <li><a href="https://github.com/mattpocock/skills" target="_blank">Matt Pocock</a></li>
  <li><a href="https://github.com/addyosmani/agent-skills" target="_blank">Addy Osmani</a></li>
</ul>

<p>The Matt Pocock Grill Me skill was a revelation. The iteration over the original prompt by DeepSeek was impressive.</p>

<p>The other skills were verbose and it was not apparant what value they were adding. To provide a deeper insight into this it’s better to develop them from scratch. This is, after all, a learning exercise.</p>

<p>I will focus on the specification to implementation since the Grill Me skill, even in its simplest form, produced a document of implementation slices. Here is the set from a session on adding OAuth to the expense tracker:</p>

<h3 id="oauth--bff-design-grilling-session-outcome">OAuth + BFF Design (grilling session outcome)</h3>
<blockquote>
  <p><strong>Status: implemented.</strong> All slices are done and green:</p>
  <ol>
    <li><strong>Core per-user isolation</strong> — owner-keyed store/business layer, <code class="language-plaintext highlighter-rouge">ICurrentUser</code> seam,
per-user sessions (implemented + deterministic tests).</li>
    <li><strong>BFF auth plumbing</strong> — OpenIdConnect + cookie auth, <code class="language-plaintext highlighter-rouge">/login</code> <code class="language-plaintext highlighter-rouge">/logout</code> <code class="language-plaintext highlighter-rouge">/api/user</code>,
<code class="language-plaintext highlighter-rouge">[Authorize]</code>, current-user middleware.</li>
    <li><strong>Dev identity provider</strong> — <strong>Keycloak in Docker</strong> (option 3, chosen after OpenIddict was
ruled out: no OpenIddict version since 3.x has in-memory stores, and all versions hard-require
HTTPS — see the blocked-attempt note below). Users/clients live in a realm import JSON;
the BFF’s <code class="language-plaintext highlighter-rouge">AUTH_AUTHORITY</code> points at it.</li>
    <li><strong>Frontend</strong> — login gate, user header, sign-out, 401 handling.</li>
    <li><strong>Tests</strong> — <code class="language-plaintext highlighter-rouge">ExpenseAgent.WebApi.Tests</code>: deterministic fake-auth BFF tests + a real
Keycloak e2e login test (skips when the container isn’t running).
The full suite is green with and without the Keycloak container.</li>
  </ol>
</blockquote>]]></content><author><name></name></author><summary type="html"><![CDATA[Keeping it real After a few Vibe Code sessions with the DeepSeek API it is very easy to see how an enthusiastic individual could lose track of the source code for the solution they are building. Vibe Coding is an informal habit. For a reproducible outcome some guardrails are needed. The key to this is building a methodology. Human Validation: Verify the source code. In fact get to know it. Human in the loop is not enough. Human in control is the goal. Hardening: This is accomplished by developing a set of SKILLs for your AI agent. These are needed for requirements to specification and specification to implementation. Its an iterative task to get to a point where these SKILLs do the heavy lifting for the methodology.]]></summary></entry><entry><title type="html">SKILL: Layered design for separation of concerns</title><link href="https://dowdeswells.github.io/2026/08/10/ai-excursion-part-3.html" rel="alternate" type="text/html" title="SKILL: Layered design for separation of concerns" /><published>2026-08-10T00:00:00+10:00</published><updated>2026-08-10T00:00:00+10:00</updated><id>https://dowdeswells.github.io/2026/08/10/ai-excursion-part-3</id><content type="html" xml:base="https://dowdeswells.github.io/2026/08/10/ai-excursion-part-3.html"><![CDATA[<h2 id="whats-it-all-about">What’s it all about</h2>
<p>I start with the end goal in mind. What i really need in the design phase is a clear set of interfaces that allow the parts of the software to be able to be iterated upon in isolation. A simple expression of this is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>agent layer  ──▶  business layer  ──▶  storage layer
 (translates)      (domain logic)      (persistence, injected)
</code></pre></div></div>

<p>Importantly the business layer does not depend on anything. For what it needs, it provides interfaces for the other layers.</p>

<h3 id="todo---security-testing-refactoring">ToDo - security, testing, refactoring</h3>
<p>At this point the general code architecture is addressed but specific cross cutting concerns are a TODO. Specifically, looking at the source code I can see repeated code. I will address this as a priority and then move on to security hardening and iterating over the various testing phases.</p>

<p>I also don’t like the Microsoft AIAgent being exposed in the interface of the Agent layer. It ties to the implementation as Microsoft Agent Framework.</p>

<p>Testing has been touched on in this current version but i want a clear progression of test scopes including the ability to run every test in a docker compose - something a ci pipeline would possibly include before a deploy except I fear this may be a bit too much for most runners due to the need to startup a local SLM.</p>

<p>The full document, at the time of writing looks like:</p>

<details>
  <summary><strong>separation of concerns</strong></summary>
  <div class="snippet-content">
    
<h1 id="layered-design-for-separation-of-concerns">Layered Design for Separation of Concerns</h1>

<p>A three-layer pattern for building AI-agent features cleanly. The rule of thumb:
<strong>each layer only knows about the layer directly beneath it</strong>, and nothing below
the agent layer knows that an AI exists.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>agent layer  ──▶  business layer  ──▶  storage layer
 (translates)      (domain logic)      (persistence, injected)
</code></pre></div></div>

<h2 id="the-three-layers">The three layers</h2>

<h3 id="1-business-layer--business-namespace-business">1. Business layer — <code class="language-plaintext highlighter-rouge">Business/</code> (namespace <code class="language-plaintext highlighter-rouge">…Business</code>)</h3>
<ul>
  <li>A class (<code class="language-plaintext highlighter-rouge">*Service</code>) with the <strong>business functionality</strong>: constructs domain
objects (with sensible defaults), enforces business rules, orchestrates
persistence through the injected store.</li>
  <li>A <strong>validation mechanism</strong> (<code class="language-plaintext highlighter-rouge">*Validator</code>) for the input parameters: required
fields, ranges, dates (e.g. reject future dates), defined enum values.</li>
  <li>A result type (<code class="language-plaintext highlighter-rouge">*Result</code>) for operation outcomes: success + domain object, or
a plain-language error. <strong>No emojis, no Markdown, no <code class="language-plaintext highlighter-rouge">AIFunction</code></strong> — this
layer returns domain objects only.</li>
</ul>

<h3 id="2-data-storage-layer--storage-namespace-storage">2. Data storage layer — <code class="language-plaintext highlighter-rouge">Storage/</code> (namespace <code class="language-plaintext highlighter-rouge">…Storage</code>)</h3>
<ul>
  <li>An <strong>interface</strong> (<code class="language-plaintext highlighter-rouge">I*Store</code> / <code class="language-plaintext highlighter-rouge">I*Repository</code>) plus a <strong>concrete implementation</strong>
(in-memory, EF Core, SQL, …).</li>
  <li><strong>Injected into the business object’s constructor</strong> — the business class never
news up its own store.</li>
  <li>Pure persistence. No business rules (no future-date checks, no amount
validation), no AI concepts. Swapping the implementation must not touch the
other layers.</li>
</ul>

<h3 id="3-agent-specific-layer--tools-namespace-tools">3. Agent-specific layer — <code class="language-plaintext highlighter-rouge">Tools/</code> (namespace <code class="language-plaintext highlighter-rouge">…Tools</code>)</h3>
<ul>
  <li>A <strong>translation class</strong> (<code class="language-plaintext highlighter-rouge">*ToolActions</code>): converts the AI tool’s raw string
parameters (dates, ids, enums) into typed values, calls the business object,
and formats the domain results into the Markdown/emoji responses the model reads.</li>
  <li>A <strong>tool-registration class</strong> (<code class="language-plaintext highlighter-rouge">*Tools</code>): creates the <code class="language-plaintext highlighter-rouge">AIFunction</code>s with a name
and a description (via <code class="language-plaintext highlighter-rouge">AIFunctionFactory.Create</code>) that tells the model when to
call the tool and what parameters it needs.</li>
  <li>All <code class="language-plaintext highlighter-rouge">AIFunction</code>, tool-description, and response-formatting concerns live here.</li>
</ul>

<h2 id="workflow-applying-this-to-a-new-feature">Workflow: applying this to a new feature</h2>

<ol>
  <li><strong>Model the domain</strong> — a shared entity in <code class="language-plaintext highlighter-rouge">Models/</code> (used by all three layers).</li>
  <li><strong>Storage layer</strong> — write the interface, then a concrete implementation.
Make it thread-safe if it is shared across sessions.</li>
  <li><strong>Business layer</strong> — result type → validator → service class with the store
injected in its constructor. Every business rule goes through the validator.</li>
  <li><strong>Agent layer</strong> — one translation method per tool (raw strings in → typed
business calls → formatted responses), registered with <code class="language-plaintext highlighter-rouge">AIFunctionFactory.Create</code>.</li>
  <li><strong>Wire it up</strong> — register store + service in DI, pass the service into the
agent factory. In this repo the agent project owns the composition root:
    <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ExpenseAgent/DependencyInjection/ExpenseTrackerServiceCollectionExtensions.cs</span>
<span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">ITransactionStore</span><span class="p">,</span> <span class="n">InMemoryTransactionStore</span><span class="p">&gt;();</span> <span class="c1">// interface (ExpenseLib) → impl (Expense.Infrastructure)</span>
<span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">TransactionService</span><span class="p">&gt;();</span> <span class="c1">// business layer (ExpenseLib), store injected by the container</span>
<span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">AIAgent</span><span class="p">&gt;(</span><span class="n">sp</span> <span class="p">=&gt;</span>
    <span class="n">sp</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">AgentFactory</span><span class="p">&gt;().</span><span class="nf">CreateExpenseTrackerAgent</span><span class="p">(</span>
        <span class="n">sp</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">TransactionService</span><span class="p">&gt;()));</span>
<span class="c1">// WebApi: builder.Services.AddExpenseTracker(agentConfig);</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Test each layer</strong> — deterministic tests that do NOT need an LLM or API key:
    <ul>
      <li>Business layer: unit tests against the service (construct, validate, edit, remove).</li>
      <li>Agent layer: invoke the <code class="language-plaintext highlighter-rouge">AIFunction</code> directly with a JSON-like argument
dictionary, bypassing the model entirely.</li>
    </ul>
  </li>
</ol>

<h2 id="copy-paste-templates">Copy-paste templates</h2>

<p>All templates live in <a href="templates/"><code class="language-plaintext highlighter-rouge">templates/</code></a> (C# / .NET + Microsoft.Extensions.AI).
Substitute the <code class="language-plaintext highlighter-rouge">Transaction*</code> names with your domain:</p>

<table>
  <thead>
    <tr>
      <th>Template</th>
      <th>File</th>
      <th>Replace</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Domain model</td>
      <td><a href="templates/domain-model.cs"><code class="language-plaintext highlighter-rouge">templates/domain-model.cs</code></a></td>
      <td><code class="language-plaintext highlighter-rouge">Transaction</code> → your entity</td>
    </tr>
    <tr>
      <td>Storage layer (interface + in-memory impl)</td>
      <td><a href="templates/storage-layer.cs"><code class="language-plaintext highlighter-rouge">templates/storage-layer.cs</code></a></td>
      <td><code class="language-plaintext highlighter-rouge">ITransactionStore</code>, <code class="language-plaintext highlighter-rouge">InMemoryTransactionStore</code></td>
    </tr>
    <tr>
      <td>Business layer (result + validator + service)</td>
      <td><a href="templates/business-layer.cs"><code class="language-plaintext highlighter-rouge">templates/business-layer.cs</code></a></td>
      <td><code class="language-plaintext highlighter-rouge">TransactionResult</code>, <code class="language-plaintext highlighter-rouge">TransactionValidator</code>, <code class="language-plaintext highlighter-rouge">TransactionService</code></td>
    </tr>
    <tr>
      <td>Agent layer (tool actions + AIFunction creation)</td>
      <td><a href="templates/agent-layer.cs"><code class="language-plaintext highlighter-rouge">templates/agent-layer.cs</code></a></td>
      <td><code class="language-plaintext highlighter-rouge">TransactionToolActions</code>, <code class="language-plaintext highlighter-rouge">TransactionTools</code></td>
    </tr>
  </tbody>
</table>

<h2 id="reference-implementation">Reference implementation</h2>

<p>This repository implements the pattern split across three projects — the AI-free core in
<code class="language-plaintext highlighter-rouge">ExpenseLib</code>, the concrete storage in <code class="language-plaintext highlighter-rouge">Expense.Infrastructure</code>, and the agent layer in
<code class="language-plaintext highlighter-rouge">ExpenseAgent</code> (which references the other two and composes everything):</p>

<ul>
  <li>Domain + business: <code class="language-plaintext highlighter-rouge">src/ExpenseAgent/ExpenseLib/</code> — <code class="language-plaintext highlighter-rouge">Models/Transaction.cs</code>,
<code class="language-plaintext highlighter-rouge">Business/TransactionService.cs</code>, <code class="language-plaintext highlighter-rouge">Business/TransactionValidator.cs</code>,
<code class="language-plaintext highlighter-rouge">Business/TransactionResult.cs</code> (namespace <code class="language-plaintext highlighter-rouge">ExpenseLib.*</code>, <strong>no AI references</strong>)</li>
  <li>Storage interface: <code class="language-plaintext highlighter-rouge">src/ExpenseAgent/ExpenseLib/Storage/ITransactionStore.cs</code></li>
  <li>Storage implementation: <code class="language-plaintext highlighter-rouge">src/ExpenseAgent/Expense.Infrastructure/InMemoryTransactionStore.cs</code></li>
  <li>Agent: <code class="language-plaintext highlighter-rouge">src/ExpenseAgent/ExpenseAgent/Tools/TransactionToolActions.cs</code>,
<code class="language-plaintext highlighter-rouge">src/ExpenseAgent/ExpenseAgent/Tools/TransactionTools.cs</code></li>
  <li>Wiring: <code class="language-plaintext highlighter-rouge">AgentFactory.CreateExpenseTrackerAgent(TransactionService)</code> + the composition root
<code class="language-plaintext highlighter-rouge">ExpenseAgent/DependencyInjection/ExpenseTrackerServiceCollectionExtensions.cs</code>
(<code class="language-plaintext highlighter-rouge">AddExpenseTracker</code>), called from <code class="language-plaintext highlighter-rouge">ExpenseAgent.WebApi/Startup.cs</code></li>
  <li>Tests: <code class="language-plaintext highlighter-rouge">ExpenseAgent.Tests/TransactionServiceTests.cs</code> (business layer),
<code class="language-plaintext highlighter-rouge">ExpenseAgent.Tests/TransactionToolTests.cs</code> (agent layer, no LLM)</li>
</ul>

<h2 id="rules-and-anti-patterns">Rules and anti-patterns</h2>

<ul>
  <li>❌ Agent tool methods reaching straight into the store — always go through the
business object.</li>
  <li>❌ Business layer returning <code class="language-plaintext highlighter-rouge">❌</code>/<code class="language-plaintext highlighter-rouge">✅</code>/Markdown strings — return a <code class="language-plaintext highlighter-rouge">*Result</code>;
the agent layer formats.</li>
  <li>❌ Validation (future dates, positive amounts, required fields) living in the
agent layer — it belongs in the <code class="language-plaintext highlighter-rouge">*Validator</code>, behind the business object.</li>
  <li>❌ Business layer referencing <code class="language-plaintext highlighter-rouge">AIFunction</code>, <code class="language-plaintext highlighter-rouge">Microsoft.Extensions.AI</code>, or tool
descriptions.</li>
  <li>❌ A store without an interface — the business object must receive storage by
injection.</li>
  <li>❌ Passing raw, unparsed strings into the business layer. Parsing
(string → <code class="language-plaintext highlighter-rouge">DateTime</code>/<code class="language-plaintext highlighter-rouge">Guid</code>/enum) is translation and lives in the agent layer;
validating parsed values against business rules lives in the business layer.</li>
</ul>

<h2 id="quick-check-before-you-call-it-done">Quick check before you call it done</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Does the business class construct and validate without any AI types?</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Is the store injected via constructor, not instantiated inside the business class?</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Do the agent tool actions only parse, translate, and format?</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Do deterministic tests cover the business rules without an LLM or API key?</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Can the storage implementation be swapped without touching the business or
    agent layers?</li>
</ul>

  </div>
</details>]]></content><author><name></name></author><summary type="html"><![CDATA[What’s it all about I start with the end goal in mind. What i really need in the design phase is a clear set of interfaces that allow the parts of the software to be able to be iterated upon in isolation. A simple expression of this is:]]></summary></entry><entry><title type="html">Developing with AI, developing for AI</title><link href="https://dowdeswells.github.io/2026/08/07/ai-excursion-part-1.html" rel="alternate" type="text/html" title="Developing with AI, developing for AI" /><published>2026-08-07T00:00:00+10:00</published><updated>2026-08-07T00:00:00+10:00</updated><id>https://dowdeswells.github.io/2026/08/07/ai-excursion-part-1</id><content type="html" xml:base="https://dowdeswells.github.io/2026/08/07/ai-excursion-part-1.html"><![CDATA[<h2 id="goal-agentic">Goal: Agentic</h2>
<p>I’ve decided to develop an application hosted on Azure. Developed, or should I say, co-developed with an AI agent and featuring some form of agentic processing.</p>

<p>The application is an expenses tracker. Something which has become slightly more pertinent given recent events.</p>

<p><strong>My goal is to develop the application, and also to develop the processes via which I will employ the AI agent to help me code.</strong></p>

<h2 id="coding-agent-harness-pidev">Coding Agent Harness: pi.dev</h2>
<p>At work I was using Copilot installed into Rider for .NET development.</p>

<p>Reviewing the outcome and upon researching what is available, it has become apparent that a more simple tool is needed. Less clutter. Something in a terminal, something full screen. Focusing on the task in hand - agent work vs output review.</p>

<p>This is provided by <a href="https://pi.dev" target="_blank">pi.dev</a></p>

<h2 id="coding-agent-deepseek">Coding Agent: DeepSeek</h2>
<p>pi.dev integrates simply with DeepSeek, just an environment variable with the DeepSeek API key
It seems pretty good so let’s start there.</p>

<h2 id="what-have-i-got-so-far">What have i got so far</h2>

<p><a href="https://expensetracker-app.thankfulbush-f2983888.australiaeast.azurecontainerapps.io/" target="_blank">The deployed Expense Tracker</a></p>

<p>I’ve been working on the code for a week. At this point I have a working expense tracker that runs an agentic loop with a register set of tools (mcp) that Add and edit expenses and income. The storage is implemented in a CosmosDB with an Event Sourcing design (which I will talk about later) which makes perfect sense for an auditable transaction record. There is a Github Action to deploy to an Azure Container App. OAuth users are provided by an Auth0 IDP.</p>

<h3 id="ai-agent-costs">AI Agent Costs</h3>
<p>DeepSeek is really cheap compared to the others. I’m impressed. My total spend over this period is about $1.30. I did try Kimi K3 for one small task - adding keycloak as the dev IDP in docker compose. After $2.50 it still hadn’t worked through the problems so I stopped pi.dev (simply type in a new prompt to “stop what you are doing”) and changed back to DeepSeek which proceeded to finish it off with a tiny cost.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Goal: Agentic I’ve decided to develop an application hosted on Azure. Developed, or should I say, co-developed with an AI agent and featuring some form of agentic processing.]]></summary></entry></feed>