<?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: Kamil Chmielewski</title>
    <description>The latest articles on DEV Community by Kamil Chmielewski (@kamilchm).</description>
    <link>https://dev.to/kamilchm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F63219%2Fdc83e917-7319-49a6-a104-c225f95e67ef.jpeg</url>
      <title>DEV Community: Kamil Chmielewski</title>
      <link>https://dev.to/kamilchm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kamilchm"/>
    <language>en</language>
    <item>
      <title>Let It Crash - Turning Failure into Your Most Reliable Signal</title>
      <dc:creator>Kamil Chmielewski</dc:creator>
      <pubDate>Mon, 04 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/kamilchm/let-it-crash-turning-failure-into-your-most-reliable-signal-2d7i</link>
      <guid>https://dev.to/kamilchm/let-it-crash-turning-failure-into-your-most-reliable-signal-2d7i</guid>
      <description>&lt;h2&gt;
  
  
  The Story: Thirteen Failures, One Flashback
&lt;/h2&gt;

&lt;p&gt;I shipped &lt;strong&gt;thirteen&lt;/strong&gt; broken releases before I remembered the one-line fix.&lt;/p&gt;

&lt;p&gt;Each failure looked different—misleading CLI output, race conditions, unmet dependencies—but the aftermath felt identical: a new layer of Bash gymnastics glued on top of the old one.&lt;/p&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%2Fxd5a4qnnsrduwfzj8b61.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%2Fxd5a4qnnsrduwfzj8b61.png" alt="GitHub workflow history showing thirteen failed releases with red X marks, followed by one successful release with green checkmark" width="763" height="1266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By version &lt;code&gt;1.1.17&lt;/code&gt; my release script looked over-engineered: a multi-line retry loop packed with debug &lt;code&gt;echo&lt;/code&gt;s, four separate &lt;code&gt;grep&lt;/code&gt;/&lt;code&gt;sed&lt;/code&gt; filters, and a blind 10-second sleep between attempts. Nothing worked.&lt;/p&gt;

&lt;p&gt;Version &lt;code&gt;1.1.18&lt;/code&gt; swapped 26 lines for just 8:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Simple retry loop — succeeds when the dependency appears.&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..12&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;cargo publish &lt;span class="nt"&gt;-p&lt;/span&gt; zod_gen_derive &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;10
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It passed on the first run.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;I maintain two Rust crates: &lt;code&gt;zod_gen&lt;/code&gt; and &lt;code&gt;zod_gen_derive&lt;/code&gt;. The second depends on the first, so a release needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Publish &lt;strong&gt;&lt;code&gt;zod_gen&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Wait until crates.io accepts it.&lt;/li&gt;
&lt;li&gt;Publish &lt;strong&gt;&lt;code&gt;zod_gen_derive&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Where It Went Wrong
&lt;/h3&gt;

&lt;p&gt;I paired with an AI agent to automate the sequence. Here's where things went sideways: the AI kept proposing &lt;em&gt;reasonable-sounding&lt;/em&gt; incremental fixes. "Let's add the &lt;code&gt;--registry&lt;/code&gt; flag." "Maybe we need better pattern matching." "How about more debug output?" Each suggestion looked logical in isolation, so I kept saying yes.&lt;/p&gt;

&lt;p&gt;This created a dangerous feedback loop. I wasn't thinking about the problem the way I would if coding manually—I was just reviewing and approving AI suggestions. The script ballooned through versions &lt;code&gt;1.1.8&lt;/code&gt; → &lt;code&gt;1.1.17&lt;/code&gt;, adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cargo search&lt;/code&gt;, &lt;code&gt;cargo info --registry&lt;/code&gt;, and half-dozen flags.&lt;/li&gt;
&lt;li&gt;Three layers of regex filters plus debug logging.&lt;/li&gt;
&lt;li&gt;Sleep statements tuned by guesswork.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reality? I'd preached "let it crash" for years, but I thought this was a trivial script we could create in minutes and forget about it. I completely off-loaded the thinking to my AI assistant for this "small thing." The AI's incremental "improvements" kept me in a rabbit hole I would never have entered manually.&lt;/p&gt;

&lt;p&gt;Result? A trail of tags on crates.io—and zero successful dependent publishes.&lt;/p&gt;

&lt;p&gt;Only after thirteen failures did the old Erlang lesson resurface.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Principle: Failure as Signal, Not Enemy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Engineers Avoid Failure
&lt;/h3&gt;

&lt;p&gt;Engineers hate red. We instinctively add checks to &lt;em&gt;avoid&lt;/em&gt; the red X before it appears. That reflex feels safe, yet it breeds hidden costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; Each avoidance layer creates more states to debug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brittleness:&lt;/strong&gt; Tools change output; your regex doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time drain:&lt;/strong&gt; You debug the detection logic &lt;em&gt;and&lt;/em&gt; the real problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Red flags that signal "prediction gone wild":&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Let's just parse the CLI output first …"&lt;/li&gt;
&lt;li&gt;Multiple health-check endpoints before every API call.&lt;/li&gt;
&lt;li&gt;Deeply nested &lt;code&gt;if/else&lt;/code&gt; chains sprinkled with sleeps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you spot those, you're predicting instead of observing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Erlang Alternative
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Erlang/OTP&lt;/strong&gt; runtime treats process failure as routine. A crashed process restarts under a supervisor; the system stays healthy. Joe Armstrong's mantra: &lt;em&gt;"Let it crash."&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For the technical details:&lt;/strong&gt; Fred Hébert's &lt;a href="https://ferd.ca/the-zen-of-erlang.html" rel="noopener noreferrer"&gt;"The Zen of Erlang"&lt;/a&gt; explains beautifully how Erlang's supervision trees and error handling work in practice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Translate that philosophy to automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attempt the operation &lt;em&gt;now&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If it fails &lt;em&gt;and the failure is recoverable&lt;/em&gt;, wait and retry.&lt;/li&gt;
&lt;li&gt;Success ends the loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it works universally:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A genuine failure is the most accurate readiness probe you will ever find.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fewer moving parts&lt;/strong&gt; → fewer bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-correcting:&lt;/strong&gt; The loop ends itself when conditions are right.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear feedback:&lt;/strong&gt; The error message tells you exactly &lt;em&gt;what&lt;/em&gt; is missing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Working Implementation
&lt;/h3&gt;

&lt;p&gt;Here's the final Bash snippet that finally shipped &lt;code&gt;zod_gen_derive&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..12&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="c"&gt;# 2 minutes total&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;cargo publish &lt;span class="nt"&gt;-p&lt;/span&gt; zod_gen_derive&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"zod_gen_derive published successfully!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;0
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Dependency not ready—retrying in 10s…"&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;10
  &lt;span class="k"&gt;fi
done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Failed to publish zod_gen_derive after 12 attempts"&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retry budget&lt;/strong&gt; prevents infinite loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immediate feedback&lt;/strong&gt; (&lt;code&gt;cargo publish&lt;/code&gt;'s own error) shows why it failed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No parsing&lt;/strong&gt;—we trust the tool to tell the truth.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Broader Applications: When to Let It Crash
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Decision Framework
&lt;/h3&gt;

&lt;p&gt;Before building prediction logic, ask yourself:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Am I trying to predict whether an operation will succeed?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is the failure recoverable and informative?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Would the failure tell me exactly what I'm trying to detect?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If yes to all three → let the operation fail and retry.&lt;/p&gt;

&lt;p&gt;The pattern scales from shell scripts to distributed systems—failure as signal works at every level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompting Your AI Assistant to "Let It Crash"
&lt;/h3&gt;

&lt;p&gt;LLMs seem to default to "failure avoidance" patterns. The "let it crash" philosophy is probably too niche for their general training—most programming tutorials teach elaborate error checking, not embracing failure as signal.&lt;/p&gt;

&lt;p&gt;This means we need to explicitly guide them with prompts. Here are my initial attempts (still experimenting):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If the operation can fail safely, propose a retry loop instead of a readiness check.

Prefer capturing and reacting to the real error message over predicting success.

Show me a version that removes parsing and relies on direct retries (let-it-crash style).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Full disclosure:&lt;/strong&gt; These prompts are untested. I just learned this lesson myself and will be refining them as I inevitably get kicked again by "avoid failure" thinking. Consider them starting points, not proven solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Thirteen failed attempts tried to &lt;em&gt;avoid&lt;/em&gt; failure; one succeeded by &lt;em&gt;trusting&lt;/em&gt; it. The core lesson: failure itself is often the most reliable signal you can get. Instead of building elaborate prediction mechanisms, let the operation tell you when it's ready by trying it directly.&lt;/p&gt;

&lt;p&gt;The AI collaboration angle was just how I ended up forgetting this principle—but the principle itself is universal and timeless.&lt;/p&gt;

&lt;p&gt;Next time you automate a workflow, start with a naive attempt, watch it crash, and wrap it in a controlled retry. Your future self (and your CI budget) will thank you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kamil.chm.ski/let-it-crash-lesson" rel="noopener noreferrer"&gt;Read the original article on Chm.ski Labs Blog →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>development</category>
      <category>devops</category>
      <category>ai</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Bridging Rust and TypeScript with zod_gen - End-to-End Type Safety at Cimatic</title>
      <dc:creator>Kamil Chmielewski</dc:creator>
      <pubDate>Fri, 25 Jul 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/kamilchm/bridging-rust-and-typescript-with-zodgen-end-to-end-type-safety-at-cimatic-5539</link>
      <guid>https://dev.to/kamilchm/bridging-rust-and-typescript-with-zodgen-end-to-end-type-safety-at-cimatic-5539</guid>
      <description>&lt;p&gt;Building [Cimatic]'s MVP sent me down several technical paths—some more interesting than others. The big architectural picture deserves its own post next week (subscribe to the RSS feed to catch it), but here's a smaller piece that turned out pretty useful: unified types between Rust and TypeScript. Write a struct &lt;strong&gt;once&lt;/strong&gt; in Rust, then use it safely everywhere – API, server-side rendering, front-end validation, tests, docs.&lt;/p&gt;

&lt;p&gt;As a side-effect, I ended up building two small crates that handle this—turned out pretty useful:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://crates.io/crates/zod_gen" rel="noopener noreferrer"&gt;&lt;code&gt;zod_gen&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Library + helpers for building &lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; schemas in Rust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://crates.io/crates/zod_gen_derive" rel="noopener noreferrer"&gt;&lt;code&gt;zod_gen_derive&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;proc-macro&lt;/code&gt; implementing &lt;code&gt;#[derive(ZodSchema)]&lt;/code&gt; so the boilerplate disappears&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Together they let me &lt;strong&gt;infer&lt;/strong&gt; Zod validation &lt;em&gt;and&lt;/em&gt; TypeScript types directly from the Rust structures that already power the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You know how it goes. You start with clean Rust structs for your API:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rust structs defined the DB models and HTTP payloads.&lt;/li&gt;
&lt;li&gt;TypeScript interfaces duplicated that shape on the front-end.&lt;/li&gt;
&lt;li&gt;Zod schemas duplicated it again for runtime validation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Three sources of truth → triple maintenance pain. Change a field in Rust? Better remember to update the TypeScript interface. Add validation? Don't forget the Zod schema. Miss one? You get those annoying type mismatches that could've been caught at build time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution in 30 Seconds
&lt;/h2&gt;

&lt;p&gt;The idea was simple: make the Rust struct the source of truth for everything else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// backend/src/models/user.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;zod_gen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ZodSchema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;zod_gen_derive&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ZodSchema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(ZodSchema,&lt;/span&gt; &lt;span class="nd"&gt;serde::Serialize,&lt;/span&gt; &lt;span class="nd"&gt;serde::Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;is_admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&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;That's it. One derive macro.&lt;/p&gt;

&lt;p&gt;Running a tiny generator binary…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run &lt;span class="nt"&gt;-p&lt;/span&gt; generate_zod_types  &lt;span class="c"&gt;# part of Cimatic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…spits out &lt;strong&gt;&lt;code&gt;frontend/types/generated/user.ts&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generated User.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;is_admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No manual syncing. No drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inside the Crates
&lt;/h2&gt;

&lt;p&gt;I kept the design deliberately simple. Two small crates, each with a focused job:&lt;/p&gt;

&lt;h3&gt;
  
  
  zod_gen – the core
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ZodSchema&lt;/code&gt; trait – every Rust type that can emit a schema.&lt;/li&gt;
&lt;li&gt;Helpers (&lt;code&gt;zod_string&lt;/code&gt;, &lt;code&gt;zod_enum&lt;/code&gt;, &lt;code&gt;zod_object&lt;/code&gt;, …) return ready-to-embed Zod snippets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ZodGenerator&lt;/code&gt; – collect many schemas and write a single &lt;code&gt;.ts&lt;/code&gt; bundle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  zod_gen_derive – the icing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Parses structs (&lt;em&gt;named fields&lt;/em&gt;) &amp;amp; enums (&lt;em&gt;unit variants&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Generates a &lt;code&gt;ZodSchema&lt;/code&gt; impl that calls back into &lt;strong&gt;zod_gen&lt;/strong&gt; helpers.&lt;/li&gt;
&lt;li&gt;Fails fast on unsupported shapes so your build never lies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both crates are MIT licensed and &amp;lt;20 KB combined. I wanted something you could drop into any project without thinking twice about dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Worked Out
&lt;/h2&gt;

&lt;p&gt;The migration went smoothly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Replace&lt;/strong&gt; old hand-rolled TS interfaces with generated ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swap&lt;/strong&gt; &lt;code&gt;z.object({...})&lt;/code&gt; declarations for imports from &lt;code&gt;generated/*.ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete&lt;/strong&gt; 1,300 lines of duplicated type and schema definitions – 🔥.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Build time impact: +0.4 s (proc-macros are cached anyway).&lt;br&gt;
Runtime impact: zero.&lt;br&gt;
Bug impact: &lt;em&gt;vanished&lt;/em&gt; – type inference works at both ends now.&lt;/p&gt;

&lt;p&gt;The best part? That nagging feeling of "did I remember to update the frontend types?" just disappeared. The compiler handles it now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Only supports &lt;strong&gt;named-field structs&lt;/strong&gt; and &lt;strong&gt;unit enums&lt;/strong&gt; right now. Tuple structs/enums are on the todo list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This covers all the cases I've needed so far. Most API types are just structs with named fields anyway. KISS!&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Coming next: &lt;strong&gt;"SSRUI + Partials Architecture"&lt;/strong&gt;&lt;br&gt;
where I'll show how [Cimatic] renders HTML on the server, streams small HTML &lt;em&gt;partials&lt;/em&gt; for dynamic updates, and why I ditched a fat CSR build entirely. That's where the real architectural magic happens.&lt;/p&gt;

&lt;p&gt;If you end up trying these crates out, I'd love to hear what you build with them. Always curious to see how others solve the same problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/cimatic/zod_gen" rel="noopener noreferrer"&gt;https://github.com/cimatic/zod_gen&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://docs.rs/zod_gen" rel="noopener noreferrer"&gt;https://docs.rs/zod_gen&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy hacking! 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kamil.chm.ski/bridging-rust-and-typescript-with-zod_gen" rel="noopener noreferrer"&gt;Read the original article on Chm.ski Labs Blog →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>development</category>
      <category>rust</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Vibe Coding is Cheap, Show Me The Demo: How I Code with Playwright UI as My Co-Pilot</title>
      <dc:creator>Kamil Chmielewski</dc:creator>
      <pubDate>Thu, 17 Jul 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/kamilchm/vibe-coding-is-cheap-show-me-the-demo-how-i-code-with-playwright-ui-as-my-co-pilot-3090</link>
      <guid>https://dev.to/kamilchm/vibe-coding-is-cheap-show-me-the-demo-how-i-code-with-playwright-ui-as-my-co-pilot-3090</guid>
      <description>&lt;p&gt;In the era of AI-generated code, vibe coding has made writing code effortless but proving it works crucial. Learn Show Me The Demo Development - a methodology that treats demos as specifications using Playwright UI for real-time feedback.&lt;/p&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%2F2cmkr55d24f0zrex8lps.webp" 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%2F2cmkr55d24f0zrex8lps.webp" alt="Developer turning from Vibe Coding to Playwright" width="750" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Evolution of My Development Philosophy
&lt;/h2&gt;

&lt;p&gt;Throughout my career—first as an engineer, then as a product manager—I practiced what I called "Demo-Driven Development." This approach stemmed from a core belief that shaped my entire career: &lt;strong&gt;our work is only done when the user says so&lt;/strong&gt;. We're not producing code—we're solving problems. This user-centric philosophy meant every sprint was planned around the next demo where I tried to show how we were solving a user's problem, every backlog item was prioritized by its demo impact, and every feature was built with the end presentation in mind. The demo wasn't just a showcase—it was the north star that guided all development decisions.&lt;/p&gt;

&lt;p&gt;Now, as a solo founder building MicroSaaS products, I've pushed this philosophy to its logical extreme. I'm no longer just the product manager planning demos; I'm the developer, QA engineer, designer, and marketer all rolled into one. This reality has forced me to evolve my approach into what I now call &lt;strong&gt;Show Me The Demo Development&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Solo Founders Can't Afford Bugs
&lt;/h2&gt;

&lt;p&gt;When you're building alone, every bug is a crisis. There's no QA team to catch regressions, no dedicated support team to handle customer complaints, and no development team to quickly patch issues. If I ship a broken feature, I'm the one who has to drop everything to fix it. As a solo founder building multiple products, I can't afford that kind of disruption.&lt;/p&gt;

&lt;p&gt;This constraint led me to a crucial realization: &lt;strong&gt;the scenarios I need for product demos are exactly the scenarios I need for regression testing&lt;/strong&gt;. Why write tests separately when the demo flow IS the test?&lt;/p&gt;

&lt;p&gt;This becomes even more critical when working with AI agents to modify your code. While AI is incredibly capable of writing code, it can also break existing functionality in unexpected ways and unexpected places. Without solid regression guardrails around the process, you'll find yourself running in circles, fixing one thing while unknowingly breaking another. Believe me, I've been there a few times in the last month alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Show Me The Demo Development
&lt;/h2&gt;

&lt;p&gt;We're living in the era of vibe coding, where developers rely on AI to generate code without reviewing the output. Vibe coding—the practice of letting AI write code based on rough intentions and good vibes—has made code generation effortless but verification crucial. When AI can generate endless code based on intentions alone, the old saying "talk is cheap, show me the code" has evolved into "vibe coding is cheap, show me the demo." Proving your software actually works has become more important than just writing it. Show Me The Demo Development embraces this vibe coding reality by making working demonstrations the foundation of your development process.&lt;/p&gt;

&lt;p&gt;Just as Infrastructure-as-Code (IaC) revolutionized DevOps by treating infrastructure as versioned, testable code, Show Me The Demo Development treats your product demonstrations as first-class development artifacts. Like Configuration-as-Code (CaC) ensures consistent environments, and Policy-as-Code (PaC) codifies business rules, this methodology codifies your user scenarios.&lt;/p&gt;

&lt;p&gt;Show Me The Demo Development is built on a simple premise: every feature starts as a Playwright test that demonstrates the complete user scenario. Unlike Behavior-Driven Development (BDD) which focuses on creating business-readable specifications for stakeholder communication, Show Me The Demo Development creates visual, interactive demonstrations that can be directly shown to customers and prospects. While BDD helps teams align on requirements, Show Me The Demo Development creates actual sales materials—though it's especially powerful for solo founders.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Show Me The Demo Workflow
&lt;/h3&gt;

&lt;p&gt;Here's how I actually work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open Playwright UI alongside my code editor&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the complete scenario flow&lt;/strong&gt; - codifying what the user does and the expected outcomes as my specifications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the scenario in Playwright UI&lt;/strong&gt; to see which parts fail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement the functionality&lt;/strong&gt; (often with AI assistance) to make the scenario pass&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refine the scenario&lt;/strong&gt; with more detailed checks and edge cases as needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's like TDD, but instead of unit tests, I'm building complete user journeys. The Playwright UI becomes my real-time feedback loop, showing me exactly what the user will experience.&lt;/p&gt;

&lt;p&gt;You can even point an AI agent to run the tests via command line and ask them to fix failing tests. Just watch carefully that they don't change the demo scenario code to make tests pass—the scenarios are your specifications and should remain unchanged.&lt;/p&gt;

&lt;h3&gt;
  
  
  But What About the Test Pyramid?
&lt;/h3&gt;

&lt;p&gt;I know what you're thinking: "This violates the Test Pyramid! E2E tests are slow and brittle. You should be running fast unit tests during development, not heavy browser automation."&lt;/p&gt;

&lt;p&gt;You're absolutely right about traditional E2E testing. But Show Me The Demo Development isn't traditional E2E testing—it's specification-driven development that happens to use browser automation. The key difference is that I've developed several techniques to make these tests run fast enough for real-time development feedback.&lt;/p&gt;

&lt;p&gt;I'm not abandoning unit tests entirely, but for solo founders building B2B SaaS products, the demo scenarios ARE the most critical tests. When your entire business model depends on live customer demonstrations, ensuring those scenarios work perfectly is more valuable than having 100% unit test coverage on utility functions.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'll be sharing my specific techniques for fast Playwright development workflows in an upcoming blog post. Subscribe to my RSS feed to get notified when it's published—these optimizations are crucial for making this approach practical.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Real Example: Building GitHub Actions One-Click Analytics
&lt;/h3&gt;

&lt;p&gt;When building &lt;a href="https://cimatic.io" rel="noopener noreferrer"&gt;Cimatic&lt;/a&gt; (my GitHub Actions analytics platform), I started with this scenario:&lt;/p&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%2Fmi4uzcc7ju62z8evci3t.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%2Fmi4uzcc7ju62z8evci3t.png" alt="Development Session in Action" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Left side:&lt;/strong&gt; The test scenario and the implementation open in Vim&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right side top:&lt;/strong&gt; Playwright UI, with the browser window showing the scenario&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right side Bottom:&lt;/strong&gt; AI agent CLI and a terminal showing logs from services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This visual demonstrates the core concept: writing the demo scenario first, then building the product to make the scenario pass.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Magic of Incremental Development
&lt;/h3&gt;

&lt;p&gt;With Playwright UI open, I can run just the first few lines, see the page load (or fail), then code the &lt;code&gt;/analyze&lt;/code&gt; route. Run again, see the form appear, then implement the form handling. Each step builds on the previous one, and I always know exactly what the user experience looks like.&lt;/p&gt;

&lt;p&gt;This approach has several unexpected benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;👁️ &lt;strong&gt;Immediate Visual Feedback&lt;/strong&gt;. Unlike unit tests, I see the actual UI as I build it.&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Real Browser Environment&lt;/strong&gt;. No mocking—I'm testing in the same environment users will experience.&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Demo-Ready Code&lt;/strong&gt;. When the test passes, I have a working demo scenario.&lt;/li&gt;
&lt;li&gt;🛡️ &lt;strong&gt;Regression Safety Net&lt;/strong&gt;. Future changes can't break the core user journey without me knowing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  From Test to Demo Video in Minutes
&lt;/h2&gt;

&lt;p&gt;Here's an unexpected bonus I discovered: once my scenario test is working, creating a demo video becomes trivial. Just like Documentation-as-Code keeps docs in sync with code, my demos stay perfectly synchronized with actual functionality. I just:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Record the Playwright test execution&lt;/strong&gt; using screen recording&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add voiceover&lt;/strong&gt; explaining what's happening&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit for pacing&lt;/strong&gt; and add captions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a polished product demo that shows real functionality, not mockups or staged data. Customers see exactly what they'll get.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As a solo founder, I can't afford to build features that don't work, create demos that don't convert, or waste time on separate testing infrastructure. Show Me The Demo Development solves all three problems with a single approach.&lt;/p&gt;

&lt;p&gt;Working this way feels incredibly productive because I don't need to manually click through the same demo flow for the 82nd time while coding—this alone is life-changing! Every change is instantly visible and testable, I'm building sales materials as I code, and regressions are caught immediately instead of in production.&lt;/p&gt;

&lt;p&gt;Like how Security-as-Code embeds security into every development step, Show Me The Demo Development embeds customer validation into every feature. Every line of code I write serves multiple purposes: it implements the feature, tests the user experience, and prepares the demo. This isn't just efficient—it's essential for competing against well-funded teams while working alone.&lt;/p&gt;

&lt;p&gt;The next time you're building a feature, try opening Playwright UI alongside your editor. Write the scenario first, then make it real. You might find, like I did, that your best development tool was hiding in your testing toolkit all along.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Want to see Show Me The Demo Development in action? I'm documenting my journey building multiple MicroSaaS products using this approach. Follow along as I build &lt;a href="https://cimatic.io" rel="noopener noreferrer"&gt;Cimatic&lt;/a&gt; (GitHub Actions analytics), and other products using Show Me The Demo Development.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kamil.chm.ski/vibe-coding-cheap-show-me-demo" rel="noopener noreferrer"&gt;Read the original article on Chm.ski Labs Blog →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>playwright</category>
      <category>testing</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
