<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ronan O Ciosoig</title><link>https://ronanoc.dev/</link><description>Recent content on Ronan O Ciosoig</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 04 Oct 2026 23:30:43 +0000</lastBuildDate><atom:link href="https://ronanoc.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>Don't Make It Public. Give It a Boundary</title><link>https://ronanoc.dev/articles/2026/dont-make-it-public/</link><pubDate>Sun, 04 Oct 2026 23:30:43 +0000</pubDate><guid>https://ronanoc.dev/articles/2026/dont-make-it-public/</guid><description>&lt;figure&gt;&lt;img src="https://ronanoc.dev/articles/2026/dont-make-it-public/image-1.jpeg"&gt;&#10;&lt;/figure&gt;&#10;&#10;&lt;p&gt;A module can already be right about what’s internal and still be wrong about what’s public. In this article I explore what should go in the API for a pure SwiftUI module, the syntax to make it work, and the reasoning behind it.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;This is the third part of a series about how to enhance validation in the agentic loop. &lt;em&gt;See the accompanying code repo on &lt;em&gt;&lt;a href="https://github.com/ronanociosoig/Shopapp"&gt;&lt;em&gt;Github here&lt;/em&gt;&lt;/a&gt;&lt;/em&gt;.&lt;/em&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h2 id="faster-ux-review"&gt;Faster UX review&lt;/h2&gt;&#10;&lt;p&gt;I was working on the checkout flow. It was a funnel of the following screens: shopping cart, address, delivery and extras, payment method, card entry, processing, and confirmation. What I needed to iterate on back then was a new payment method. It was tedious to have to click through all the screens each time, so I decided to add a scenario in the micro-app for payment methods, giving direct access to each step.&lt;/p&gt;</description></item><item><title>Don't Trust Your Stub. Trust the Transcript.</title><link>https://ronanoc.dev/articles/2026/dont-trust-your-stub/</link><pubDate>Wed, 09 Sep 2026 13:41:53 +0000</pubDate><guid>https://ronanoc.dev/articles/2026/dont-trust-your-stub/</guid><description>&lt;figure&gt;&lt;img src="https://ronanoc.dev/articles/2026/dont-trust-your-stub/image-1.jpeg"&#10;&#9;&#9;&#9;alt="Ticket stubs"&gt;&lt;figcaption&gt;&#10;&#9;&#9;&#9;&lt;p&gt;Ticket stubs&lt;/p&gt;&#10;&#9;&#9;&lt;/figcaption&gt;&#10;&lt;/figure&gt;&#10;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;See the accompanying code repo on &lt;a href="https://github.com/ronanociosoig/Shopapp"&gt;Github here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h2 id="the-stub-that-used-to-be-true"&gt;The Stub That Used to Be True&lt;/h2&gt;&#10;&lt;p&gt;Here’s a test that used to live in ShopApp, the companion project for this series of articles.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fetchProducts decodes the remote response&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fetchProductsDecodesResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="kr"&gt;throws&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MockNetworkClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StoreProduct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stubs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DefaultStoreRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;StoreProduct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the standard for a reliable unit test — explicit state, fast feedback, no simulator required. It loads a stubbed response object — a Swift array standing in for what the API returns. It was testing that the repository decodes a JSON array into StoreProduct values, that caching works, and that the URL gets built right. It was not checking that StoreProduct.stubs return what the actual server response is. The stubs were written by a coding agent against the API contract at the time it was defined in the spec.&lt;/p&gt;</description></item><item><title>AI Writes Fast. Your Safety Net Needs to Be Faster.</title><link>https://ronanoc.dev/articles/2026/ai-writes-fast/</link><pubDate>Sat, 01 Aug 2026 12:37:28 +0000</pubDate><guid>https://ronanoc.dev/articles/2026/ai-writes-fast/</guid><description>&lt;figure&gt;&lt;img src="https://ronanoc.dev/articles/2026/ai-writes-fast/image-1.jpeg"&gt;&#10;&lt;/figure&gt;&#10;&#10;&lt;p&gt;I’ve been writing code for the past year switching between Copilot, Claude Code and Codex (sorry Cursor, it just didn’t happen). With these tools, building a flow of screens for an app — the UI, the model, a first pass at test coverage — takes minutes instead of days.&lt;/p&gt;&#10;&lt;p&gt;What hasn’t gotten any faster is what happens next: the review process of the code changes before merging it into a large, live production project. It has to be manually reviewed line by line. Am I confident that an agent-generated PR is correct? Definitely not. It generates plausible code that compiles, but often has a subtle change that isn’t correct. In general, the higher velocity output in generated code has led to more bugs — AI-generated code creates 1.7x more problems than human-written code, per CodeRabbit’s analysis.&lt;/p&gt;</description></item></channel></rss>