<?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: Denis Zontak</title>
    <description>The latest articles on DEV Community by Denis Zontak (@dzontak).</description>
    <link>https://dev.to/dzontak</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%2F4198554%2F4a6985cf-9879-426d-94f4-c3872ced87ec.jpg</url>
      <title>DEV Community: Denis Zontak</title>
      <link>https://dev.to/dzontak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dzontak"/>
    <language>en</language>
    <item>
      <title>Break It on Purpose</title>
      <dc:creator>Denis Zontak</dc:creator>
      <pubDate>Sun, 25 Oct 2026 00:29:37 +0000</pubDate>
      <link>https://dev.to/dzontak/break-it-on-purpose-9b0</link>
      <guid>https://dev.to/dzontak/break-it-on-purpose-9b0</guid>
      <description>&lt;p&gt;&lt;em&gt;An AI pipeline that catches its own bugs, explains them, and fixes them while a human makes two decisions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By Denis Zontak, written with Claude. Originally published at &lt;a href="https://zontakllc.com/articles/break-it-on-purpose" rel="noopener noreferrer"&gt;zontakllc.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A bug in the search box
&lt;/h2&gt;

&lt;p&gt;At 17:45 on October 24, 2026, the search box on our staging site started to crash. We had put the bug there on purpose: one line in a database read skipped archived clients, so a search that found their entries failed with &lt;code&gt;Cannot read properties of undefined (reading 'name')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nobody told the system. Forty-two seconds after the first error, a fix request was waiting in our pipeline. Thirty-one minutes after the first error, a pull request was ready for review: a one-line fix, and a test that proves it. A person made two decisions in between.&lt;/p&gt;

&lt;p&gt;This is how that works, what went wrong on the way, and what it cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build it yourself
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I built it because it's cool. I have full control over what it does and how it does it. As they say, homemade beats pretty much anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That control matters most where trust is thin. AI writes most of the code here, so the question is not whether it makes mistakes, but what catches them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI is a powerful tool. A system that can hallucinate, or lose track in a large context, is a reason not to trust it blindly. But in practice I do trust it: I trust that it can make a mistake, and that it can fix it too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is why there are only two human gates:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It can make a mistake and fix it, so I check the plan and the result, and let it work in between.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Two tools and two gates
&lt;/h2&gt;

&lt;p&gt;Two small services do the work. &lt;strong&gt;Night Watch&lt;/strong&gt; watches the site's logs and checks, opens incidents, asks an AI to triage them, and alerts by email, Telegram and Slack. &lt;strong&gt;Day Shift&lt;/strong&gt; turns a request into a reviewed pull request: it writes a spec, builds in its own workspace, runs the project's checks, has a second agent review the change, and deploys to staging.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fokn42h0u5c1md9z2j9rp.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%2Fokn42h0u5c1md9z2j9rp.png" alt="The self-heal loop: 8 steps, 2 human gates" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A person decides only two things: is this the right fix (the spec), and is it good enough to ship (the sign-off). The flow itself is plain code, every agent step is checked by something other than itself, and everything is written down, so the pipeline can stop and resume without doing work twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drills: breaking staging on purpose
&lt;/h2&gt;

&lt;p&gt;The staging copy of UseHourly has a drill switch. One command breaks one thing for at most 15 minutes: the health check answers 503, or answers slowly, or one data call fails. The switch only works on the staging build, only with a long token, and every start and stop writes a log line.&lt;/p&gt;

&lt;p&gt;The first three drills taught more than any test suite:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Drill&lt;/th&gt;
&lt;th&gt;What we saw&lt;/th&gt;
&lt;th&gt;What we fixed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Caught in about a minute, and triage guessed the cause unprompted. Not labelled as a drill, and no email.&lt;/td&gt;
&lt;td&gt;Night Watch read the request line instead of the app's own log line. The saved mail key was a pasted command.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Drill label and email worked, but 16 emails arrived in 4 minutes.&lt;/td&gt;
&lt;td&gt;No catch-up mail on start, no reopening of old incidents, one thread per failure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Exactly two emails: one after triage, one saying Healed. One Telegram message.&lt;/td&gt;
&lt;td&gt;Nothing. This is the shape we wanted.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A drill is cheap, and it checks the parts no unit test reaches: the logs, the alerts, the person reading them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We use drills like a unit test for the whole pipeline: how it responds to different errors and situations, and how we would handle them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flood of the second drill was a lesson of its own:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It was annoying at first, but I was also excited that the process worked and the alerts were coming in. A mixed bag, as they say.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The bug drill, minute by minute
&lt;/h2&gt;

&lt;p&gt;A switch is not a bug, so the honest fix for a switch drill is "turn it off". For a real test we planted a real slip on a throwaway branch, as if a person had made it, and wrote down the right fix before anything ran. Then we let it go.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time (EDT)&lt;/th&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;17:45:24&lt;/td&gt;
&lt;td&gt;First 500: a search hits an archived client's entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:46:02&lt;/td&gt;
&lt;td&gt;Night Watch triage says code fix, citing the stack trace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:46:06&lt;/td&gt;
&lt;td&gt;Day Shift receives it as request R-2, 42 seconds after the first error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:48:55&lt;/td&gt;
&lt;td&gt;Spec v2 waits for a human. It found the place but not the cause, and wanted a weak test that hides the row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:52:17&lt;/td&gt;
&lt;td&gt;Sent back with one note: the cause is the new archived filter, and the entry must come back with its client name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:53:23&lt;/td&gt;
&lt;td&gt;Spec v3 matches the answer key. Approved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:55:57&lt;/td&gt;
&lt;td&gt;One-line fix; the reproduce test fails first, then the checks and code review pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:56:57&lt;/td&gt;
&lt;td&gt;Fix deployed to staging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:06–18:10&lt;/td&gt;
&lt;td&gt;Replay: 10 of 10 searches return the entry with its client name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:16:31&lt;/td&gt;
&lt;td&gt;Pull request opened for sign-off&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The fix matched the answer key line for line. The human did exactly what the gate is for: one send-back with the cause, one approval.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What surprised me most was how fast it was. Forty-two seconds from the first error to a fix request on my desk, and I hadn't touched anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What broke, and what we learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Silence is not proof.&lt;/strong&gt; Day Shift waited for Night Watch to say the incident was healed. But Night Watch closed a log incident only when it saw the errors stop, and with no traffic on staging there was nothing to see. After 15 minutes Day Shift decided "not healed" and built the same fix a second time. We closed the incident by hand, citing our replay as evidence, and the second build passed. The lesson: healing must be shown by replaying the request that failed, not by the absence of errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A spec writer must read the code it is fixing.&lt;/strong&gt; The spec writer read the main branch, not the branch with the bug, so it had to guess the cause. It got the place right and the reason wrong. One human note fixed it, but the tool should not need that note.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alerts need budgets.&lt;/strong&gt; Twenty emails a day sounded generous until one bad start used sixteen of them. Drills and real incidents should not share one budget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small things hide in plain sight.&lt;/strong&gt; A test that listed files with git found zero files when run from a git hook, and failed for a reason that had nothing to do with the code. A key saved as a pasted command looked fine until the first email bounced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;The bug drill cost $2.45 of agent credit and took 31 minutes from the first error to a pull request. Without the two waits we now know how to remove, it would take about 12.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;First error to fix request&lt;/td&gt;
&lt;td&gt;42 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First error to pull request&lt;/td&gt;
&lt;td&gt;31 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent cost, bug drill (R-2)&lt;/td&gt;
&lt;td&gt;$2.45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent cost, first feature ticket (R-1, CSV export)&lt;/td&gt;
&lt;td&gt;$3.09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human actions in the bug drill&lt;/td&gt;
&lt;td&gt;1 send-back, 1 approve, 1 merge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emails per drill, before and after the fix&lt;/td&gt;
&lt;td&gt;16, then 2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We also loaded staging with 10,000 realistic accounts: 2.57 million entries and 66,881 invoices, 1.74 GB. Every page stayed under 0.2 s of data time for the heaviest account. The one slow page, an owner-only report, went from 2.9 s to 0.27 s after two small indexes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;The next step is to stop counting on luck and start keeping score.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Honest healing:&lt;/strong&gt; replay the failing request after a fix, and close a log incident after a quiet period that follows a deploy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synthetic users:&lt;/strong&gt; short journeys on staging every few minutes (sign in, log time, search, open the week), so a bug shows up without real traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A bug zoo:&lt;/strong&gt; a set of planted bugs of different kinds, each with an answer key written before the run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A nightly scorecard:&lt;/strong&gt; one bug a night through the whole loop, graded on whether triage found the cause, whether the spec named it, whether the fix passed a hidden check, and what it cost.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is one honest number, such as "healed 7 of 10, median 14 minutes, $3 each". Day Shift and Night Watch will be open source once that number is worth showing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Next comes the next level: full automation, and resolving issues on its own. The sky's the limit, really. We'll start small and go big.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A word to other studios
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you have a website or an app that needs maintenance, that needs to be watched over diligently, you can benefit from these tools. They're elegant and pleasant to look at, and that's all I'll say. There's more, but that will be a surprise when you try it.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>testing</category>
      <category>automation</category>
    </item>
    <item>
      <title>What it costs to start an open source project (my real numbers)</title>
      <dc:creator>Denis Zontak</dc:creator>
      <pubDate>Wed, 21 Oct 2026 15:48:45 +0000</pubDate>
      <link>https://dev.to/dzontak/what-it-costs-to-start-an-open-source-project-my-real-numbers-33cn</link>
      <guid>https://dev.to/dzontak/what-it-costs-to-start-an-open-source-project-my-real-numbers-33cn</guid>
      <description>&lt;p&gt;When I started working on UseHourly, I was just going to be the only user. But then somehow I decided it should be an open source project for other people to use. So in reality, since I'm hosting it, this project is no longer totally free for me. It's actually going to cost me money if people do start using it.&lt;/p&gt;

&lt;p&gt;Here is what it actually costs me, line by line.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I pay every month
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Cost a month&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare (hosting and database)&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;The minimum paid plan. It runs the app and stores the data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resend (email)&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;td&gt;I upgraded to the Pro plan so I don't run into the email limits.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain (usehourly.app)&lt;/td&gt;
&lt;td&gt;$1 for the first year, about $12 a year after that (about $1 a month)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Continuous integration (CircleCI)&lt;/td&gt;
&lt;td&gt;$0 (free plan)&lt;/td&gt;
&lt;td&gt;Runs the checks and deploys when I merge.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI coding assistant (Claude)&lt;/td&gt;
&lt;td&gt;$20 on the Pro plan, which should be enough for most people. I pay $100 for Max because I work on about five projects at the same time, so UseHourly is only part of that.&lt;/td&gt;
&lt;td&gt;It wrote most of the code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Running it (hosting, email, domain)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;about $26 a month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Running it plus the assistant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;about $46 a month on the $20 plan&lt;/strong&gt; (about $126 with the $100 plan I use for all my projects)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What was free
&lt;/h2&gt;

&lt;p&gt;Things I use that cost me nothing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub for the code, in a public repository.&lt;/li&gt;
&lt;li&gt;CircleCI, on its free plan, for running the checks and deploying.&lt;/li&gt;
&lt;li&gt;Automatic checks on every pull request (GitHub Actions), a security scan (CodeQL) and dependency updates (Dependabot).&lt;/li&gt;
&lt;li&gt;A bot-protection widget for sign-up (Cloudflare Turnstile), ready but switched off.&lt;/li&gt;
&lt;li&gt;npm, dev.to and Medium for sharing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing that surprised me: running UseHourly costs about $26 a month. The most expensive part is not the servers. It's the AI assistant I used to build it: $20 a month for most people. I pay $100 for the bigger plan, but I use it for about five projects at the same time, not just this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I think it will cost with more users
&lt;/h2&gt;

&lt;p&gt;I wanted to know what happens if people really do start using it, so I looked up the prices and made an estimate. These are estimates, not measurements. I assumed every user is active, about 20 days a month, with a small number of requests per day.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Today&lt;/th&gt;
&lt;th&gt;1,000 users&lt;/th&gt;
&lt;th&gt;10,000 users&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare (hosting and database)&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;about $5&lt;/td&gt;
&lt;td&gt;about $5 to $6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email (Resend Pro)&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rough total a month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;about $25&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;about $25&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;about $25 to $30&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I was surprised by this. I thought the $5 plan would only handle about 100 users. By my estimate it covers thousands of active users, because the free amounts that come with the plan are large. The email plan is the cost that is already real. The prices are from Cloudflare's and Resend's own pricing pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  What worries me
&lt;/h2&gt;

&lt;p&gt;What worries me when many people sign up is that I'm going to end up with a bill of more than $100 for this thing. I guess I can afford it, but I'd rather not. I'd rather this be sponsored, really.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'll do if it gets expensive
&lt;/h2&gt;

&lt;p&gt;If it got very expensive, I'll probably have to hold some kind of fundraiser. Like if it's over $100. I already have sponsorship links on my site. Hopefully I would just start by posting a message to all users, asking if they can donate a little bit to cover the monthly cost, if it's too large.&lt;/p&gt;

&lt;p&gt;I built a small notice for exactly this. It's off right now. If I switch it on, people see a short note on the Today page saying what hosting costs, with a link to my sponsors page. It only shows inside the app. I don't send marketing email, and I don't want to start.&lt;/p&gt;

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

&lt;p&gt;So that's what it costs to start an open source project, in my case. It's free to use, and it's not free for me. Right now it's about $26 a month to run, counting hosting, email and the domain, and my estimate says it stays about that even with thousands of users. If I'm wrong and it ever gets past $100, I'll say so openly, and I'll ask for help.&lt;/p&gt;

&lt;p&gt;If you use it and it helps you, you can sponsor it on GitHub: &lt;a href="https://github.com/sponsors/Zontak-LLC" rel="noopener noreferrer"&gt;https://github.com/sponsors/Zontak-LLC&lt;/a&gt;. And if you've paid for a free project of your own, I'd like to hear what it cost you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was dictated by me. Claude, an AI assistant, helped put it in order, looked up the prices, made the cost estimate and drafted a few sentences, which I edited. The numbers about what I pay are mine.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cloudflare</category>
      <category>sideprojects</category>
      <category>indiehackers</category>
    </item>
    <item>
      <title>Guardrails so AI-written code doesn't turn into spaghetti</title>
      <dc:creator>Denis Zontak</dc:creator>
      <pubDate>Wed, 21 Oct 2026 14:05:14 +0000</pubDate>
      <link>https://dev.to/dzontak/guardrails-so-ai-written-code-doesnt-turn-into-spaghetti-3a21</link>
      <guid>https://dev.to/dzontak/guardrails-so-ai-written-code-doesnt-turn-into-spaghetti-3a21</guid>
      <description>&lt;p&gt;I like open source. I've been using open source projects for many years, and there are some very, very good ones. I wanted to try building one myself, just to see how to set it all up.&lt;/p&gt;

&lt;p&gt;At the time I was doing some freelance work for a friend at a company called MagSmith (magsmith.com), and I needed to manage my timesheets. I looked at some of the open source time trackers, the top three alternatives: solidtime, Kimai and TimeTagger, and they're okay. But I don't like it when a product says it's free but has a bunch of paid stuff on top. You want to use a feature and it ends up requiring an upgrade. I hate that.&lt;/p&gt;

&lt;p&gt;So I wrote my own, the way I like it. It's called UseHourly, and I use it myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  My first AI coding experience
&lt;/h2&gt;

&lt;p&gt;My first AI coding experience came at work. We started using AI about a year ago. I started with an AI coding assistant in the IDE, and at that time AI was still not there. It couldn't produce code sophisticated enough for what we needed, and it made way too many mistakes. But it was helpful.&lt;/p&gt;

&lt;p&gt;About six months ago, around the time when the Claude Opus model came out, it became good enough. It could implement the code completely from a Jira ticket, and then implement a list of tests by itself, like Cucumber tests for the integration tests. All by itself.&lt;/p&gt;

&lt;p&gt;That was my first experience, and I love it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I added guardrails
&lt;/h2&gt;

&lt;p&gt;When I started UseHourly, I was kind of vibe coding it. Nothing was really going bad yet. It wasn't spaghetti code yet, because it's a reasonably small project. But I know that in a larger project it becomes a problem. I know this from experience.&lt;/p&gt;

&lt;p&gt;So instead of letting it keep running code until it becomes spaghetti, I added some guardrails to let it know what not to do and what to do. Check for code smells, write good code, maintain good coding practices, enforcement, and stuff like that. Basically, how to keep a clean, tidy code base with well-written code and good test coverage. That's the goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did
&lt;/h2&gt;

&lt;p&gt;First, I wrote the rules down in a file called CLAUDE.md, in the project. The assistant reads it every time it starts. It says how the project is built, how money and dates are stored, and what "done" means.&lt;/p&gt;

&lt;p&gt;Second, I made one command that runs all the checks. It's the only definition of "the checks". I run it, the CI runs it, and the git hooks run it, so nobody can say "it passed on my side". If it fails, the work isn't done.&lt;/p&gt;

&lt;p&gt;Third, I turned the rules into checks that fail the build. No &lt;code&gt;any&lt;/code&gt; types, no ignoring the type checker, no TODO without an issue number, no copy-pasted code, no unused files, and a minimum test coverage that can only go up, never down. The AI can't talk its way past them. If it takes a shortcut, the build says no and it fixes it.&lt;/p&gt;

&lt;p&gt;Fourth, I write the tests the way a person uses the app. At one point the AI rewrote a screen and a button did nothing. The tests passed, because none of them ever clicked it. So I added a test that clicks every button on every page, and a rule that every bug fix comes with a test that fails without the fix.&lt;/p&gt;

&lt;p&gt;And now I review the code before I commit it, not after. A fresh reviewer reads the change cold, because the assistant that wrote the code is the worst reader of it.&lt;/p&gt;

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

&lt;p&gt;So that's my story. I love open source, and I love working with AI coding assistants. But you can't just let the AI keep going. On a bigger project it turns into spaghetti, and I know that from experience.&lt;/p&gt;

&lt;p&gt;So I put guardrails in place. Rules that tell it what to do and what not to do, and checks that make sure it does. The goal is a clean, tidy code base with well-written code and good test coverage.&lt;/p&gt;

&lt;p&gt;I've turned these checks into a small tool, so anyone can add them to their own project. It's open source: &lt;a href="https://github.com/Zontak-LLC/railing" rel="noopener noreferrer"&gt;https://github.com/Zontak-LLC/railing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I dictated this article and had Claude help me put it in order and check the facts against my code.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>opensource</category>
      <category>codequality</category>
    </item>
  </channel>
</rss>
