<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dan Guisinger</title>
    <description>The latest articles on DEV Community by Dan Guisinger (@dguisinger).</description>
    <link>https://dev.to/dguisinger</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1366515%2Fb2970319-665f-416e-b381-6f4f5449b766.jpeg</url>
      <title>DEV Community: Dan Guisinger</title>
      <link>https://dev.to/dguisinger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dguisinger"/>
    <language>en</language>
    <item>
      <title>Building a game in Unreal Engine with Kiro in two weeks (with no experience)</title>
      <dc:creator>Dan Guisinger</dc:creator>
      <pubDate>Thu, 23 Jul 2026 22:00:54 +0000</pubDate>
      <link>https://dev.to/dguisinger/building-a-game-in-unreal-engine-with-kiro-in-two-weeks-with-no-experience-3ao9</link>
      <guid>https://dev.to/dguisinger/building-a-game-in-unreal-engine-with-kiro-in-two-weeks-with-no-experience-3ao9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsphowvk8tz99f4mp0qlp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsphowvk8tz99f4mp0qlp.png" alt="Building a Game in Kiro" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been a professional C# and TypeScript developer for years. I know how to ship software. But game development? Unreal Engine? C++? None of that was in my vocabulary.&lt;/p&gt;

&lt;p&gt;Twenty years since I last wrote C++. Never opened Unreal Engine. Didn't know what a GameMode was or how physics simulation worked in a game engine. I had an idea for a game and two weeks of evening time.&lt;/p&gt;

&lt;p&gt;Just over two weeks and roughly 20,000 Kiro credits later, I have a playable 4v4 arena combat-sport game with probabilistic AI, full combat, disc physics, animation layering, in-game music, sound effects, an announcer, menus, and a match flow that runs start to finish.&lt;/p&gt;

&lt;p&gt;This isn't a tutorial. It's what happened when I pointed an AI coding agent at a domain I knew nothing about and iterated relentlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Game
&lt;/h2&gt;

&lt;p&gt;The project is a clone of &lt;a href="https://en.wikipedia.org/wiki/Deathrow_(video_game)" rel="noopener noreferrer"&gt;DeathRow&lt;/a&gt;, a 2002 Xbox game by Southend Interactive. Two teams of four in a futuristic arena, throwing a disc through a goal ring to score. You can also beat your opponents unconscious. Knock someone out and their team plays shorthanded. Win by outscoring, or just KO everyone.&lt;/p&gt;

&lt;p&gt;Here's what the original looked like:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ZbhT_TVOCtY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Kiro with Claude Opus 4.6 on macOS. Unreal Engine 5.8. The experimental Unreal MCP server connected so Kiro could interact directly with the editor: creating assets, configuring properties, placing actors in levels.&lt;/p&gt;

&lt;p&gt;Two steering files gave Kiro permanent context: one describing the game and my background, one covering architecture decisions and project structure. No game development experience. No Unreal tutorials. No C++ refresher. Just a description of what I wanted and a willingness to iterate.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Iteration Works
&lt;/h2&gt;

&lt;p&gt;I'd describe what I wanted at a high level. "I need a disc that bounces off walls with configurable physics and triggers a score when it enters a goal." Kiro figured out the Unreal-specific implementation. I'd play the result, describe what felt wrong. Kiro would diagnose and fix it.&lt;/p&gt;

&lt;p&gt;The work was phased: movement, then disc physics, then teams, combat, AI, game flow, polish. Each phase had a deliverable I could test by playing. Some phases spawned sub-phases when something wasn't right. I'd describe a gap and Kiro would go as deep as needed.&lt;/p&gt;

&lt;p&gt;I ran autopilot mode exclusively. Kiro wrote C++ classes, created Unreal assets via the MCP, configured inputs, wired up particle systems. My job was playing the result and saying "this doesn't feel right" or "what's next."&lt;/p&gt;

&lt;h2&gt;
  
  
  Week One: From Nothing to Playable
&lt;/h2&gt;

&lt;p&gt;The first week produced the core game. Arena, movement, disc physics with auto-targeting, four-player teams with switching, a full combat system with KOs, seven AI characters running behavior trees, match flow with scoring and halftime, a menu system, controller support, and basic audio feedback.&lt;/p&gt;

&lt;p&gt;It worked. But it felt mechanical. The AI was deterministic. Predictable patterns, robotic movement, no personality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Week Two: Making It Feel Like a Game
&lt;/h2&gt;

&lt;p&gt;I designed a probabilistic weight engine to replace the deterministic AI. I sketched out how decisions should be calculated: base weights modified by stacking multipliers for personality, team tendencies, game state, spatial awareness, and difficulty. I gave Kiro pseudo code showing the pipeline stages and how modifiers should compound. Kiro recognized the pattern, implemented it in C++, and wired it into the behavior trees.&lt;/p&gt;

&lt;p&gt;This broke everything. The new AI exercised code paths the old deterministic system never touched, and latent bugs surfaced everywhere. Disc physics bouncing off characters instead of passing through them. Animation states leaking into the wrong context. Catch volumes too small at higher speeds. These bugs had been there from day one, just never triggered. I spent hours describing symptoms and Kiro traced each one to its root cause.&lt;/p&gt;

&lt;p&gt;Beyond the AI, I added an energized disc mechanic. Hold the throw button to charge a power shot that instantly KOs opponents on impact (the namesake "deathrow" move). I overhauled the animation system to support priorities, interruptibility, and weighted variations. Catch animations now start before the disc arrives. The disc doesn't release until the throw animation hits its release point. Characters celebrate after scoring.&lt;/p&gt;

&lt;p&gt;For audio I used ElevenLabs for sound effects and announcer voice lines, Suno for in-game music. Still entirely AI-created, no human artists involved yet.&lt;/p&gt;

&lt;p&gt;Here's where it stands after just over two weeks and 20,000+ Kiro credits:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prototype status:&lt;/strong&gt; Combat is difficult because players move fast and there's no target lock for melee. Stamina tuning needs work. There are animation quirks and AI quirks I'm still chasing. Zero art, texturing, or modeling has been done. Everything you see is placeholder geometry and free Mixamo animations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Yzg52Fs6yvQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Kiro Carried Me
&lt;/h2&gt;

&lt;p&gt;I didn't know Unreal's class hierarchy, macro system, or memory model. Kiro wrote thousands of lines of C++ across dozens of files spanning every layer of the engine's framework. I never wrote a line of C++ myself.&lt;/p&gt;

&lt;p&gt;The things that would have taken me months to learn from documentation, Kiro just did. When the Unreal MCP couldn't handle something (and the experimental tool has plenty of gaps), Kiro explained exactly what I needed to do manually in the editor, step by step.&lt;/p&gt;

&lt;p&gt;The steering files grew with the project. Every time I hit a recurring failure pattern, I'd document it so Kiro would recognize the symptoms immediately in future sessions instead of rediscovering the fix from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Did
&lt;/h2&gt;

&lt;p&gt;My contribution was design and direction, not code. I wrote the game design. I played and described what felt wrong. I designed the probability system and gave Kiro pseudo code to implement. I made architecture decisions when options were presented. I generated audio with ElevenLabs and Suno. I followed Kiro's instructions for the manual editor steps. I said "good enough, move on" when polish wasn't worth more time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;About 20,000 credits over two weeks of evening work, Claude Opus 4.6 exclusively. Over 80 C++ files. 150+ property-based tests (written by Kiro). Zero lines of C++ written by me.&lt;/p&gt;

&lt;p&gt;At current pricing that's the Kiro Power tier ($200/month for 10,000 credits, overages at $0.04/credit). For two weeks of what would otherwise be many months of learning, I'd make that trade every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;The hard part isn't getting something working. It's getting it working &lt;em&gt;well&lt;/em&gt;. The first week was fast because everything was new and "it does the thing" was the bar. The second week was slower because the bar became "it does the thing and feels right," and that meant chasing bugs that had been invisible until the AI started exercising edge cases.&lt;/p&gt;

&lt;p&gt;The skill that mattered most wasn't programming. It was describing what I was seeing clearly enough that Kiro could diagnose it. "The disc bounces backward off a teammate" leads to a fix. "Passing doesn't work" leads to a conversation.&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>gamedev</category>
      <category>ai</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Calling a Public API Gateway Custom Domain from a Private VPC (Without Reconfiguring Your Clients)</title>
      <dc:creator>Dan Guisinger</dc:creator>
      <pubDate>Wed, 28 Jan 2026 20:43:54 +0000</pubDate>
      <link>https://dev.to/dguisinger/calling-a-public-api-gateway-custom-domain-from-a-private-vpc-without-reconfiguring-your-clients-2n35</link>
      <guid>https://dev.to/dguisinger/calling-a-public-api-gateway-custom-domain-from-a-private-vpc-without-reconfiguring-your-clients-2n35</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;a href="https://medium.com/@dguisinger/calling-a-public-api-gateway-custom-domain-from-a-private-vpc-b395f861ffa3" rel="noopener noreferrer"&gt;Originally published on Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There’s a deceptively simple requirement that AWS API Gateway still doesn’t support cleanly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use a public, regional API Gateway with a custom domain, and allow private VPC workloads to call that same domain without changing client configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve tried this, you’ve probably been sent in circles—by documentation, AI tools, and even AWS recommendations—toward solutions that either don’t deploy or don’t work.&lt;/p&gt;

&lt;p&gt;This post documents what actually breaks, why most suggested approaches fail, and the pragmatic workaround that finally solved it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;You have a public API Gateway endpoint exposed as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That domain is used everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browsers&lt;/li&gt;
&lt;li&gt;External consumers&lt;/li&gt;
&lt;li&gt;Shared SDKs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You also have private workloads (Lambda, ECS, EC2) running in isolated subnets with no NAT gateway. Those workloads need to call the same hostname, not an execute-api URL, and not an environment-specific variant.&lt;/p&gt;

&lt;p&gt;Changing the base URL internally would fracture client configuration and defeat the point of having a stable public domain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the obvious solutions fail
&lt;/h2&gt;

&lt;p&gt;The first approach most people try is an interface VPC endpoint for execute-api, combined with a Route 53 private hosted zone that resolves api.yourdomain.com to the endpoint inside the VPC.&lt;/p&gt;

&lt;p&gt;DNS-wise, this works. Traffic routes privately. The request never touches the internet.&lt;/p&gt;

&lt;p&gt;But the TLS handshake fails.&lt;/p&gt;

&lt;p&gt;At that point, nothing reaches API Gateway:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No API is identified&lt;/li&gt;
&lt;li&gt;No routing occurs&lt;/li&gt;
&lt;li&gt;No authentication runs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TLS negotiation fails before HTTP exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  “Just use private custom domains”
&lt;/h2&gt;

&lt;p&gt;This recommendation comes up constantly—and it’s simply incorrect for this scenario.&lt;/p&gt;

&lt;p&gt;API Gateway private custom domains only work with private API endpoints. Domain associations explicitly require the API itself to be private. You cannot attach a private custom domain to a public regional API.&lt;/p&gt;

&lt;p&gt;CloudFormation rejects it.&lt;br&gt;
The console won’t allow it.&lt;br&gt;
There is no workaround.&lt;/p&gt;

&lt;p&gt;This is a product limitation, not a configuration error.&lt;/p&gt;


&lt;h2&gt;
  
  
  “Use the execute-api endpoint internally”
&lt;/h2&gt;

&lt;p&gt;That works technically, but it forces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different base URLs per environment&lt;/li&gt;
&lt;li&gt;API IDs and stage names in clients&lt;/li&gt;
&lt;li&gt;Special-case logic in shared SDKs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For systems designed around a single public domain, this is architectural debt.&lt;/p&gt;


&lt;h2&gt;
  
  
  The real problem
&lt;/h2&gt;

&lt;p&gt;Once you strip away the noise, the issue is very small and very specific.&lt;/p&gt;

&lt;p&gt;When you access API Gateway through a VPC endpoint, the endpoint presents a TLS certificate for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*.execute-api.{region}.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if you connect using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The certificate chain is valid.&lt;br&gt;
The issuer is trusted.&lt;br&gt;
The only failure is &lt;strong&gt;hostname mismatch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And because TLS fails, API Gateway never sees the request at all.&lt;/p&gt;


&lt;h2&gt;
  
  
  The pragmatic workaround
&lt;/h2&gt;

&lt;p&gt;If you accept that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are connecting to an AWS-owned service&lt;/li&gt;
&lt;li&gt;Over a VPC endpoint&lt;/li&gt;
&lt;li&gt;Using AWS-issued certificates&lt;/li&gt;
&lt;li&gt;With DNS fully under your control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…then the fix is to &lt;strong&gt;explicitly accept execute-api certificates&lt;/strong&gt; when the only TLS error is a name mismatch.&lt;/p&gt;

&lt;p&gt;This preserves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One public domain&lt;/li&gt;
&lt;li&gt;One client configuration&lt;/li&gt;
&lt;li&gt;Private routing without NAT&lt;/li&gt;
&lt;li&gt;Full encryption in transit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnli4un2ue6zb09bypb6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnli4un2ue6zb09bypb6.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Applying this in .NET
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Configuring the HTTP client
&lt;/h2&gt;

&lt;p&gt;This is the missing piece you called out earlier.&lt;br&gt;
The TLS behavior must be applied when registering the HttpClient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ApiClient"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureForVpcEndpoint&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any SDK or service using this client will now tolerate API Gateway’s certificate when accessed via a VPC endpoint.&lt;/p&gt;




&lt;h3&gt;
  
  
  The HTTP client handler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHttpClientBuilder&lt;/span&gt; &lt;span class="nf"&gt;ConfigureForVpcEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IHttpClientBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigurePrimaryHttpMessageHandler&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SocketsHttpHandler&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;SslOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SslClientAuthenticationOptions&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;RemoteCertificateValidationCallback&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                    &lt;span class="n"&gt;ValidateApiGatewayCertificate&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;endraw&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;


&lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="n"&gt;Certificate&lt;/span&gt; &lt;span class="n"&gt;validation&lt;/span&gt;
&lt;span class="p"&gt;{%&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="p"&gt;%}&lt;/span&gt;


&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="n"&gt;csharp&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ValidateApiGatewayCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;X509Certificate&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;X509Chain&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SslPolicyErrors&lt;/span&gt; &lt;span class="n"&gt;sslPolicyErrors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sslPolicyErrors&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SslPolicyErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sslPolicyErrors&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SslPolicyErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RemoteCertificateNameMismatch&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="n"&gt;certificate&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"execute-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does not disable TLS validation. It narrowly allows a known, trusted AWS certificate that fails hostname validation only because API Gateway cannot present your custom-domain certificate over a VPC endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this isn’t as scary as it looks
&lt;/h2&gt;

&lt;p&gt;This is safe because &lt;em&gt;it’s constrained&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The trust chain is still validated&lt;/li&gt;
&lt;li&gt;Only AWS API Gateway certificates are accepted&lt;/li&gt;
&lt;li&gt;DNS is private and controlled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traffic never leaves AWS’s network&lt;/p&gt;

&lt;p&gt;This is not a blanket “ignore SSL errors” hack—it’s compensating for a missing feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real takeaway
&lt;/h2&gt;

&lt;p&gt;There is currently &lt;strong&gt;no supported way&lt;/strong&gt; to combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;public regional API Gateway&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;custom domain&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private VPC access&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;single, stable hostname&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Correct TLS without client changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Until AWS fills that gap, this workaround is the cleanest solution I’ve found—and the only one that doesn’t require re-architecting clients around internal-only URLs.&lt;/p&gt;

&lt;p&gt;Sometimes the hardest problems aren’t misconfigurations.&lt;br&gt;
They’re the seams where cloud abstractions stop.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;em&gt;About the author&lt;/em&gt;&lt;/strong&gt;: &lt;a href="https://danguisinger.com" rel="noopener noreferrer"&gt;Dan Guisinger&lt;/a&gt; is a consultant specializing in system and security architecture on AWS.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>apigateway</category>
      <category>vpc</category>
      <category>security</category>
    </item>
    <item>
      <title>Building FluentDynamoDB in 45 Days with Kiro - A Hackathon Story</title>
      <dc:creator>Dan Guisinger</dc:creator>
      <pubDate>Thu, 04 Dec 2025 07:45:11 +0000</pubDate>
      <link>https://dev.to/dguisinger/building-fluentdynamodb-in-45-days-with-kiro-a-hackathon-story-41i9</link>
      <guid>https://dev.to/dguisinger/building-fluentdynamodb-in-45-days-with-kiro-a-hackathon-story-41i9</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the story of how a massive .NET DynamoDB library went from an empty repo to production-ready in 45 days.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I started building &lt;a href="https://fluentdynamodb.dev" rel="noopener noreferrer"&gt;FluentDynamoDB&lt;/a&gt;, I expected it to take months or even a year to reach a usable state. It’s an extremely large .NET library: source generators, expression translation, composite keys, geospatial indexing, encryption, stream handling, and thousands of tests across nine packages.&lt;/p&gt;

&lt;p&gt;Instead… it took 45 days.&lt;/p&gt;

&lt;p&gt;The only reason that was possible is Kiro.&lt;/p&gt;

&lt;p&gt;This post covers what I built, how Kiro enabled it, and why the project literally could not exist without an AI-augmented workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview: FluentDynamoDB
&lt;/h2&gt;

&lt;p&gt;FluentDynamoDB is a modern, strongly-typed DynamoDB toolkit for .NET. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source-generated entity models&lt;/li&gt;
&lt;li&gt;Lambda-expression to DynamoDB request translation&lt;/li&gt;
&lt;li&gt;Composite key modeling&lt;/li&gt;
&lt;li&gt;Encryption + S3-backed blob storage&lt;/li&gt;
&lt;li&gt;A complete stream-processing framework&lt;/li&gt;
&lt;li&gt;Geospatial lookup via GeoHash, S2, and H3&lt;/li&gt;
&lt;li&gt;Thousands of automated tests&lt;/li&gt;
&lt;li&gt;Nine NuGet packages that work together as a unified ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a “weekend hackathon” project.&lt;br&gt;
It is a production-grade library, built fast — because the workflow was different.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Kiro Workflow — Requirements → Design → Tasks
&lt;/h2&gt;

&lt;p&gt;Kiro’s “Spec” system is the most important part of how this project got built.&lt;/p&gt;

&lt;h3&gt;
  
  
  requirements.md → What the system must do
&lt;/h3&gt;

&lt;p&gt;Every major feature begins with EARS-style requirement statements defining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expected behaviors&lt;/li&gt;
&lt;li&gt;constraints&lt;/li&gt;
&lt;li&gt;edge cases&lt;/li&gt;
&lt;li&gt;business rules and functional intent&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  design.md → How it should be built
&lt;/h3&gt;

&lt;p&gt;This is where architectural decisions took shape:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source generation boundaries&lt;/li&gt;
&lt;li&gt;lambda-expression normalization rules&lt;/li&gt;
&lt;li&gt;composite key representation&lt;/li&gt;
&lt;li&gt;consistency models&lt;/li&gt;
&lt;li&gt;geospatial data structures&lt;/li&gt;
&lt;li&gt;AOT safety considerations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  tasks.md → What Kiro should implement
&lt;/h3&gt;

&lt;p&gt;Each task references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the requirement it fulfills&lt;/li&gt;
&lt;li&gt;the design decision that governs how it should be implemented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kiro then generates the implementation while staying aligned to the spec.&lt;/p&gt;

&lt;p&gt;Working alone, that scale is nearly impossible without something like Kiro.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardest Part: Geospatial Encoding with Multiple LLMs
&lt;/h2&gt;

&lt;p&gt;I had zero geospatial background going in. I knew GeoHash existed, but that was the extent of it.&lt;/p&gt;

&lt;p&gt;Kiro made it possible to build a complete geospatial feature set — but not without major challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Auto Model + GeoHash
&lt;/h3&gt;

&lt;p&gt;Using Kiro’s default “Auto” model, it implemented GeoHash reasonably well. But during development, Kiro suggested:&lt;/p&gt;

&lt;p&gt;“You may also want to consider Google S2 or Uber H3 for geospatial precision.”&lt;/p&gt;

&lt;p&gt;Great idea... in theory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: First Attempts at S2 and H3 (Failure)
&lt;/h3&gt;

&lt;p&gt;I let Kiro implement S2 and H3, and both implementations failed badly. It produced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inconsistent cell conversions&lt;/li&gt;
&lt;li&gt;mismatched bit shifts&lt;/li&gt;
&lt;li&gt;incorrect spherical coordinate handling&lt;/li&gt;
&lt;li&gt;decoding errors on specific edge-case coordinates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Auto-mode models simply got lost in the complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Switching to Claude Opus 4.5 + the Clear-Thought MCP
&lt;/h3&gt;

&lt;p&gt;When Anthropic released Claude Opus 4.5, I retried the work with a different approach:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Break the problem into steps
&lt;/h3&gt;

&lt;p&gt;Using the clear-thought MCP plugin, I forced structured workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;normalization rules&lt;/li&gt;
&lt;li&gt;intermediate coordinate transforms&lt;/li&gt;
&lt;li&gt;step-by-step verification&lt;/li&gt;
&lt;li&gt;invariants for S2 and H3 cell relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Opus’s reasoning was noticeably stronger
&lt;/h3&gt;

&lt;p&gt;It could explain concepts coherently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hilbert curves&lt;/li&gt;
&lt;li&gt;space-filling patterns&lt;/li&gt;
&lt;li&gt;face/cell transformations&lt;/li&gt;
&lt;li&gt;spherical geometry relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. But we still had edge cases
&lt;/h3&gt;

&lt;p&gt;A handful of stubborn coordinates would not encode/decode correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 4: Deep Debugging Using Reference C Code
&lt;/h3&gt;

&lt;p&gt;This is where Kiro became absolutely essential.&lt;/p&gt;

&lt;p&gt;I loaded the original Google S2 and Uber H3 C reference code into the Kiro workspace and had Opus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read them side-by-side&lt;/li&gt;
&lt;li&gt;trace execution paths&lt;/li&gt;
&lt;li&gt;compare intermediate values&lt;/li&gt;
&lt;li&gt;identify mismatched conversions&lt;/li&gt;
&lt;li&gt;reason through floating-point differences&lt;/li&gt;
&lt;li&gt;reconcile the two implementations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After several cycles, it found the issues - subtle mathematical transformation details I could not have discovered without deep math knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;S2 + H3 now both work in FluentDynamoDB.&lt;/p&gt;

&lt;p&gt;And I will be very blunt:&lt;/p&gt;

&lt;p&gt;I do not have the mathematical or domain background to implement S2 or H3 manually. This feature simply would not exist in FluentDynamoDB without Kiro.&lt;/p&gt;

&lt;p&gt;It also consumed over &lt;strong&gt;1,000 Kiro credits&lt;/strong&gt; on this set of Specs alone — but it was worth every one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steering Documents: Quietly Powerful
&lt;/h2&gt;

&lt;p&gt;This project didn’t need as many steering rules as full-stack work,&lt;br&gt;
but one rule was essential:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“After any public API change, update the corresponding documentation and append a note to the documentation changelog.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because I have a separate Kiro workspace that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reads that changelog&lt;/li&gt;
&lt;li&gt;updates the web documentation&lt;/li&gt;
&lt;li&gt;keeps everything in sync&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is still an experiment, but so far it has been extremely successful.&lt;br&gt;
It ensures documentation never lags behind the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Scale (Powered by Kiro)
&lt;/h2&gt;

&lt;p&gt;Here’s what was produced during Kiroween:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;160,000+ lines of C#&lt;/li&gt;
&lt;li&gt;100,000+ lines of documentation, specs, and steering documents&lt;/li&gt;
&lt;li&gt;~4,000 automated tests&lt;/li&gt;
&lt;li&gt;9 NuGet packages&lt;/li&gt;
&lt;li&gt;5 example applications&lt;/li&gt;
&lt;li&gt;40+ Specs&lt;/li&gt;
&lt;li&gt;Used Multiple LLMs per feature&lt;/li&gt;
&lt;li&gt;Over 10,000 Kiro credits consumed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the output of a multi-team engineering project —&lt;br&gt;
delivered by one person using AI-augmented development —&lt;br&gt;
for a total cost of around $400.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=4-SI6YgSX_s" rel="noopener noreferrer"&gt;Kiroween Hackathon Video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/oproto/fluent-dynamodb" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned During Kiroween
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. AI-assisted engineering isn’t the future, it’s the present.
&lt;/h3&gt;

&lt;p&gt;This wasn’t theory. This was shipping real software.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Distributed system features become accessible to developers who lack domain expertise.
&lt;/h3&gt;

&lt;p&gt;The S2/H3 experience is proof.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Specs &amp;gt; prompts.
&lt;/h3&gt;

&lt;p&gt;Kiro’s structured workflow is genuinely transformative.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Good engineering + good AI &amp;gt; either alone.
&lt;/h3&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;FluentDynamoDB’s first stable NuGet release is coming soon.&lt;br&gt;
We'll be continuing to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add runtime DynamoDB schema validation&lt;/li&gt;
&lt;li&gt;improve our FluentResults coverage over the full API surface&lt;/li&gt;
&lt;li&gt;build more sample applications&lt;/li&gt;
&lt;li&gt;keep scaling the spec-driven development workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re participating in Kiroween, I’d love to compare notes.&lt;/p&gt;

&lt;p&gt;— Dan&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>dynamodb</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
