<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Brandon Casci</title>
  <subtitle>Brandon Casci's Software Engineering and Entrepreneurship Blog</subtitle>
  <id>http://www.brandoncasci.com/</id>
  <link href="http://www.brandoncasci.com/"/>
  <link href="http://www.brandoncasci.com/feed.xml" rel="self"/>
  <updated>2026-04-07T14:00:00+00:00</updated>
  <author>
    <name>Brandon Casci</name>
  </author>
  <entry>
    <title>The Gravity Between You and the 100X Dark Factory</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2026/04/07/the-gravity-between-you-and-the-100x-dark-factory.html"/>
    <id>http://www.brandoncasci.com/2026/04/07/the-gravity-between-you-and-the-100x-dark-factory.html</id>
    <published>2026-04-07T14:00:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2026-04-07&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;The Gravity Between You and the 100X Dark Factory&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2026/04/07/cover.png" alt="The Gravity Between You and the 100X Dark Factory" /&gt;&lt;/p&gt;

&lt;p&gt;I heard &lt;a href="https://www.natebjones.com/"&gt;Nathan B Jones&lt;/a&gt; talking about the concept of a living resume and how he thought the current hiring model was toast. The idea stuck with me. I expanded it into a product called &lt;a href="https://profyle.site/"&gt;Profyle&lt;/a&gt;: a professional profile platform with three layers of depth. A scannable surface view, expandable stories behind each role, and an AI conversation layer where visitors can ask questions about your background. LinkedIn discovers you. Profyle helps people understand you.&lt;/p&gt;

&lt;p&gt;I decided to use it as an experiment. Could I build it with full AI autonomy?&lt;/p&gt;

&lt;p&gt;Dan Shapiro &lt;a href="https://www.danshapiro.com/blog/2026/01/the-five-levels-from-spicy-autocomplete-to-the-software-factory/"&gt;drew an analogy to self-driving car levels&lt;/a&gt; and came up with five levels of AI-assisted development. At Level 4, the human writes specs and crafts skills. The AI implements. At Level 5, you have a &amp;ldquo;Dark Factory&amp;rdquo;: specs go in, shipped software comes out, no human in the execution loop. The term comes from lights-out manufacturing. StrongDM shared a public example of this: three engineers, no human-written or human-reviewed code, external test scenarios as enforcement.&lt;/p&gt;

&lt;p&gt;I aimed for Level 5. I landed at Level 4, with Level 5 moments on the mechanical parts. This is what the attempt looked like and where the boundary actually sits.&lt;/p&gt;

&lt;h2&gt;Before the Factory&lt;/h2&gt;

&lt;p&gt;Before any AI touched any code, I had to figure out what to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product and market research (some automated with AI)&lt;/li&gt;
&lt;li&gt;Product definition and intention statement&lt;/li&gt;
&lt;li&gt;Architecture document&lt;/li&gt;
&lt;li&gt;Broke it all down into implementable specs as GitHub issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those added up to the MVP.&lt;/p&gt;

&lt;p&gt;The factory doesn&amp;rsquo;t know what to build. I did.&lt;/p&gt;

&lt;h2&gt;The Factory&lt;/h2&gt;

&lt;p&gt;I didn&amp;rsquo;t just build Profyle. I built a factory model using Claude Code&amp;rsquo;s capabilities (rules, skills, subagents, and hooks) to see how close to autonomous I could get. At Level 4 and above, building and tuning that system IS the job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rules&lt;/strong&gt; (24 files) auto-load by file context and cover code style, architecture, testing standards, and operational guides. Failures during the project fine-tuned them, some led by me, some by agents recognizing issues and amending the rules themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills&lt;/strong&gt; (8 Profyle-specific) are pipelines for repeatable workflows. &lt;code&gt;/profyle-ship&lt;/code&gt; goes from board issue to merged PR in one command: branch, TDD, review, PR, merge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TDD actor separation&lt;/strong&gt; means the agent that writes tests is never the agent that writes code. The tester doesn&amp;rsquo;t know the implementation. The coder doesn&amp;rsquo;t write the tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Convergence loops&lt;/strong&gt; are resolution-based quality control. The loop continues until every finding is resolved, not after a fixed number of iterations. A circuit-breaker prevents infinite loops.&lt;/p&gt;

&lt;p&gt;These patterns came from prior projects. New problems came up as they were adapted for longer-running autonomous sessions.&lt;/p&gt;

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

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&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;Commits&lt;/td&gt;
&lt;td&gt;514&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active development&lt;/td&gt;
&lt;td&gt;~8 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rules files&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills (total / Profyle-specific)&lt;/td&gt;
&lt;td&gt;40 / 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test files&lt;/td&gt;
&lt;td&gt;181&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test code&lt;/td&gt;
&lt;td&gt;~24,000 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App code (Ruby)&lt;/td&gt;
&lt;td&gt;~5,400 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test-to-code ratio&lt;/td&gt;
&lt;td&gt;4.4:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ViewComponents&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RubyLLM Agents&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Models&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controllers&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Testing coverage is high, but some of the early tests were duplicative or too verbose. Those were written before the TDD rules were optimized.&lt;/p&gt;

&lt;h2&gt;What Worked&lt;/h2&gt;

&lt;p&gt;Work was organized into milestones (M0: Skeleton through M6: Go Live). Each issue was scoped to be individually implementable from the spec alone. The agent can&amp;rsquo;t ask clarifying questions, so ambiguity meant expensive guesses. Only issues marked &amp;ldquo;Ready&amp;rdquo; could enter the pipeline.&lt;/p&gt;

&lt;p&gt;The feedback loop was a big part of making this work: bad output got diagnosed, distilled into a rule, and didn&amp;rsquo;t repeat. A couple examples of what that looked like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI refused to use WebMock/VCR, stubbed API calls with invented payloads, tests passed with zero connection to reality. Fix: forced VCR for real response recording.&lt;/li&gt;
&lt;li&gt;RubyLLM schema DSL missing &lt;code&gt;object&lt;/code&gt; wrapper generated wrong JSON structure with no compile-time error. Fix: mandatory unit test on &lt;code&gt;to_json_schema&lt;/code&gt; output for every schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;What Didn&amp;rsquo;t Work&lt;/h2&gt;

&lt;h3&gt;UX&lt;/h3&gt;

&lt;p&gt;Unlike backend code, where the AI arrived at quality solutions with the right rules and context, UI and UX were a different story. Same access to information, but the output quality didn&amp;rsquo;t follow.&lt;/p&gt;

&lt;p&gt;A Claude Code skill called &lt;a href="https://github.com/nextlevelbuilder/ui-ux-pro-max-skill"&gt;ui-ux-pro-max&lt;/a&gt; gave the AI a design intelligence framework for making better style and pattern decisions.&lt;/p&gt;

&lt;p&gt;I also added simple_form and ViewComponent with DaisyUI helpers to narrow the decision space: card here, badge there, textarea instead of text input. Less guessing.&lt;/p&gt;

&lt;p&gt;It all improved the output, but I still directed creative decisions throughout. Clearest Level 3 territory in the project.&lt;/p&gt;

&lt;h3&gt;Test Quality&lt;/h3&gt;

&lt;p&gt;The factory wrote a lot of tests, but early quality was uneven. System tests targeted isolated UI elements instead of complete user workflows. Edge cases were missing. Mocks and stubs were overused, obfuscating bugs in some cases.&lt;/p&gt;

&lt;p&gt;I fixed this through interactive sessions where I diagnosed what was going wrong and tightened the testing rules over time. Later output was solid.&lt;/p&gt;

&lt;h3&gt;Review Skipping&lt;/h3&gt;

&lt;p&gt;The most expensive mistake in the project. The ship pipeline has a dedicated reviewer subagent, but the orchestrator would sometimes do its own review instead of spawning it. In one early session, it skipped the reviewer on 17 of 18 PRs. The sign-in button broke across three consecutive PRs because the reviewer never ran.&lt;/p&gt;

&lt;p&gt;I tightened the rule language and added a prerequisite checkpoint: the orchestrator must verify that reviewer output exists before creating a PR. After tightening, reviews happened. But it&amp;rsquo;s all instruction-based. There are no hooks or system gates enforcing it. The AI reads the rule and chooses to comply.&lt;/p&gt;

&lt;p&gt;That&amp;rsquo;s the limit. The AI supervises itself.&lt;/p&gt;

&lt;h2&gt;Where I Actually Landed&lt;/h2&gt;

&lt;p&gt;While the initial MVP fell short, it was a good foundation that needed additional attention. I spent a few days in QA and corrective cycles. Some ideas needed rethinking.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s where each layer actually landed:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What that meant&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Models, controllers, services, AI integration&lt;/td&gt;
&lt;td&gt;L5&lt;/td&gt;
&lt;td&gt;Spec in, merged PR out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overall project (specs, rules, diagnostics)&lt;/td&gt;
&lt;td&gt;L4&lt;/td&gt;
&lt;td&gt;I drafted specs, diagnosed gaps, tuned rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UX and product strategy&lt;/td&gt;
&lt;td&gt;L3&lt;/td&gt;
&lt;td&gt;Tight conversational iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Level 4 worked because of iterative rule-hardening: ship, find a gap, write a rule, ship better. The factory improved because the rules got more precise, not because the AI got smarter.&lt;/p&gt;

&lt;p&gt;The gap to Level 5: the factory mostly can&amp;rsquo;t find its own problems, and the AI supervises itself through the process. The rules are the most transferable artifact. They encode hard-won decisions and carry forward to the next project.&lt;/p&gt;

&lt;h2&gt;The Path to Level 5&lt;/h2&gt;

&lt;p&gt;Tools like Claude Code, Gemini, Codex and others can&amp;rsquo;t do Level 5 yet. The AI supervises itself: you give it a skill with 10 steps and trust it to run them all, including reviewing its own work. Subagents and hooks only get you so far. The AI is still the orchestrator.&lt;/p&gt;

&lt;p&gt;Level 5 needs an external runtime controlling the process while the AI handles focused tasks in separate sessions. This is why we&amp;rsquo;re seeing things like StrongDM&amp;rsquo;s approach and open source projects like &lt;a href="https://fabro.sh/"&gt;Fabro&lt;/a&gt; coming about.&lt;/p&gt;

&lt;p&gt;The external orchestrator handles the flow, and people with deep industry experience like &lt;a href="https://alexbunardzic.com"&gt;Alex Bunardzic&lt;/a&gt; and &lt;a href="https://bryanfinster.com"&gt;Bryan Finster&lt;/a&gt; can now encode their methodologies into the AI&amp;rsquo;s configurations: TDD discipline, continuous delivery, fast feedback. If Alex wants a test mutation step in the pipeline, I believe he&amp;rsquo;d get it with this approach. I&amp;rsquo;m looking forward to trying it.&lt;/p&gt;

&lt;p&gt;I knew what Level 5 was about through reading. Doing it showed me where the ceiling actually is. Who supervises the process, not how good the AI is at following instructions.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How I Came to Understand the 100x Claim</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2026/02/24/how-i-came-to-understand-the-100x-claim.html"/>
    <id>http://www.brandoncasci.com/2026/02/24/how-i-came-to-understand-the-100x-claim.html</id>
    <published>2026-02-24T20:17:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2026-02-24&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;How I Came to Understand the 100x Claim&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2026/02/24/cover.png" alt="How I Came to Understand the 100x Claim" /&gt;&lt;/p&gt;

&lt;p&gt;First people bragged about 3-5x productivity gains from AI, then 10x, and now 100x. Usually without much clarity beyond references to parallelism and orchestration. I&amp;rsquo;d read these claims and think, OK, but what does that actually look like? What are you doing differently that gets you there?&lt;/p&gt;

&lt;p&gt;I was skeptical for a while. Then I lived through it, and I started to understand what these people are probably trying to describe. They&amp;rsquo;re mostly bad at explaining it, and I think that&amp;rsquo;s because the gains don&amp;rsquo;t come from any single technique. They come from building up an infrastructure over time that eventually changes what one person can operate.&lt;/p&gt;

&lt;p&gt;This is how I got there.&lt;/p&gt;

&lt;h2&gt;Getting Incrementally Better&lt;/h2&gt;

&lt;p&gt;I didn&amp;rsquo;t start out great at employing AI for real work. The early months were a lot of trial and error, figuring out what Claude Code could actually handle versus what I still needed to do myself.&lt;/p&gt;

&lt;p&gt;I got incrementally better. I wrote &lt;a href="/2025/09/25/my-ai-organization-ships-like-we-re-funded-but-it-s-just-me.html"&gt;CLAUDE.md files with domain knowledge and coding standards&lt;/a&gt;, built custom skills for repeatable workflows, and learned how to spec work so agents could pick it up without a bunch of back-and-forth. I started building &lt;a href="/2025/12/24/composable-rules-getting-claude-code-to-honor-your-conventions.html"&gt;composable rule sets&lt;/a&gt; so conventions stayed consistent across projects.&lt;/p&gt;

&lt;p&gt;As confidence grew, I got more ambitious. I joined other open-source projects. I added ancillary applications to the business. Each improvement compounded on the last: more context documented meant better agent output, which meant more confidence to delegate, which meant I could take on more scope.&lt;/p&gt;

&lt;h2&gt;Hitting the Bottleneck&lt;/h2&gt;

&lt;p&gt;The gains plateaued once I was running multiple projects at the same time. Boswell had two active codebases and production customers. Harmonic needed attention. I was maintaining a fork of the litestream-ruby gem. And I had this blog.&lt;/p&gt;

&lt;p&gt;Switching tabs, switching console sessions, switching mental contexts. AI was helping inside each project, but I was still the bottleneck between them. Not only could I not take on much more, it was mentally taxing to juggle that many different codebases and topics at once. The gains from faster code generation had a ceiling because I could only context-switch so fast.&lt;/p&gt;

&lt;h2&gt;Exploring Orchestration Across Systems&lt;/h2&gt;

&lt;p&gt;I realized the bottleneck wasn&amp;rsquo;t the agents or the code. It was me directing everything manually. I needed to automate the delegation itself.&lt;/p&gt;

&lt;p&gt;I built boswell-backoffice: a Fly.io VPS that runs the same Claude Code agents I use locally, triggered by GitHub events instead of me typing in a terminal. &lt;a href="https://n8n.io/"&gt;n8n&lt;/a&gt; listens for GitHub webhook events like issues being labeled or comments being posted. &lt;a href="https://dagu.readthedocs.io/"&gt;Dagu&lt;/a&gt; orchestrates the pipeline: post a start comment, set up the workspace, run Claude, commit the results, and post a completion comment.&lt;/p&gt;

&lt;p&gt;Agents commit as &amp;ldquo;Boswell Agent&amp;rdquo; or &amp;ldquo;Claude Agent,&amp;rdquo; and it&amp;rsquo;s all visible in the git history. I started with a different orchestration tool (AI Maestro), replaced it with Dagu, and I&amp;rsquo;m still prototyping. The specific tools matter less than the pattern: I moved from directing agents manually to delegating through project management tools. GitHub Issues became work orders. GitHub Projects became the prioritization layer.&lt;/p&gt;

&lt;p&gt;The issue workflow has two labels. &lt;code&gt;agent-spec&lt;/code&gt; takes a casually written issue and explodes it into a full implementation specification. The agent reads the rough description, pulls in all the project context it has access to (CLAUDE.md files, conventions, domain knowledge), and rewrites the issue body with a detailed spec. My original draft gets preserved in a collapsed section at the bottom. Then &lt;code&gt;agent-implement&lt;/code&gt; picks up that spec and does the actual work: write the code, run tests, commit, push, and open a PR. I can also drop an &lt;code&gt;@agent&lt;/code&gt; comment on any issue or PR to give ad-hoc instructions.&lt;/p&gt;

&lt;p&gt;This means I can write a few sentences about what I want while I&amp;rsquo;m thinking about it, label it &lt;code&gt;agent-spec&lt;/code&gt;, and come back later to a full spec I can review and refine before kicking off implementation. The agent does the tedious part of translating intent into detailed requirements, using the same project knowledge I&amp;rsquo;ve been building up all along.&lt;/p&gt;

&lt;h3&gt;What agents have shipped through this pipeline&lt;/h3&gt;

&lt;p&gt;Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issue #252: An agent investigated a SQLite transaction rollback bug across 7 commits, documented the full investigation, wrote regression tests, and opened PR #253 (240 additions, 13 deletions).&lt;/li&gt;
&lt;li&gt;Issue #283: An agent implemented console subscription management for tenants, including a new controller, dialog UI, tests, and addressed PR review feedback.&lt;/li&gt;
&lt;li&gt;Just about anything I feel like slinging through this instead of kicking off locally at the console.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The Litestream Incident&lt;/h2&gt;

&lt;p&gt;This is where it clicked for me.&lt;/p&gt;

&lt;p&gt;Boswell&amp;rsquo;s production data volume hit 100% full. Litestream replication staging directories and WAL files were consuming about 906MB of a 974MB volume. The actual databases were around 2MB. I&amp;rsquo;d been seeing recurring SQLite errors across multiple issues, and the root cause turned out to be Litestream v0.3.13, which had known unbounded disk growth bugs that were fixed in v0.5.x. The upstream litestream-ruby gem was still bundling the old version, and the maintainer was unresponsive and not taking pull requests.&lt;/p&gt;

&lt;p&gt;All of this happened on the same day (2026-02-22):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I wrote issue #276 with the full spec for what needed to happen.&lt;/li&gt;
&lt;li&gt;One agent forked litestream-ruby, upgraded the binary from v0.3.13 to v0.5.8, added a &lt;code&gt;litestream:download&lt;/code&gt; rake task, updated CI and docs, and bumped the version. It used proper PR workflow on the fork.&lt;/li&gt;
&lt;li&gt;Another agent picked up the fork in boswell-app, updated the Gemfile, converted the config format, updated the runtime config service, fixed the Dockerfile through 4 iterative attempts, and got the full test suite passing.&lt;/li&gt;
&lt;li&gt;I reviewed the work, merged, and deployed. Production issue resolved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two different codebases, two different problem domains (gem internals vs. Rails app deployment). Without the orchestration, I&amp;rsquo;d be manually reading gem source code, forking, upgrading, then context-switching to boswell-app to update config and debug Docker builds. It starts to hurt my head after a while.&lt;/p&gt;

&lt;p&gt;With delegation, I wrote the spec, kicked it off, and reviewed the output. The agents handled the cross-codebase coordination and the iterative debugging themselves.&lt;/p&gt;

&lt;h2&gt;Agents Reviewing Each Other&lt;/h2&gt;

&lt;p&gt;I run two agents in a write/review loop. Both have access to the same conventions and standards through CLAUDE.md files, rules, and skills. They iterate until the work meets the standards. The responder has an out if the reviewer makes unreasonable calls, acting as a circuit breaker so they don&amp;rsquo;t loop forever.&lt;/p&gt;

&lt;p&gt;The first result from a spec is generally good, but there&amp;rsquo;s usually some tech debt you don&amp;rsquo;t want to let accrue into something bigger. The review loop dials it in before I ever see it.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve heard some claim this kind of song-and-dance routing is defensive and an indication of how weak the technology is. My opinion: so what, it works, it&amp;rsquo;s fast, and I&amp;rsquo;ve seen people on actual teams cause worse problems.&lt;/p&gt;

&lt;p&gt;PR #253 shows the full loop. The investigation was documented, the fix was implemented, regression tests were written, and review feedback was addressed, all before I looked at it.&lt;/p&gt;

&lt;h2&gt;Trust and Staying in the Loop&lt;/h2&gt;

&lt;p&gt;Trust didn&amp;rsquo;t come automatically. I had to build it up over time by watching agents work and verifying results. Early on I reviewed everything closely, which was the right call. As the conventions matured and the review loops proved reliable, I gradually shifted to reviewing final output instead of watching every intermediate step.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m still the final arbiter on every merge. At my scale, that&amp;rsquo;s not a bother, so I&amp;rsquo;m not letting go of that yet. Besides, the review process surfaces opportunities to update agent knowledge. Sometimes I spot a convention gap or a better pattern that should be documented. That feedback loop improves future output, and agents also pick up on improvements implicitly as the codebase evolves.&lt;/p&gt;

&lt;p&gt;I suspect a lot of people are in the early trust-building stage. The systems that make delegation work aren&amp;rsquo;t obvious until you&amp;rsquo;ve built them and lived with them for a while.&lt;/p&gt;

&lt;h2&gt;Coming to Understand the Claim&lt;/h2&gt;

&lt;p&gt;I don&amp;rsquo;t think it&amp;rsquo;s literally 100x. Not to me, anyway. But compared to a decade ago, even a year ago, what I&amp;rsquo;m able to do as one person is kind of astounding.&lt;/p&gt;

&lt;p&gt;Some of the gains have nothing to do with typing speed. They show up in being able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delegate across codebases I&amp;rsquo;d otherwise have to context-switch between manually&lt;/li&gt;
&lt;li&gt;Run business operations through the same project management tools as code work&lt;/li&gt;
&lt;li&gt;Let agents handle the iterative debugging and review cycles&lt;/li&gt;
&lt;li&gt;Take on more projects and more scope without burning out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that shows up if you measure &amp;ldquo;time to write a function.&amp;rdquo; It shows up when you look at what one person can operate across a month.&lt;/p&gt;

&lt;h2&gt;What I Can Refine From Here&lt;/h2&gt;

&lt;p&gt;The orchestration layer is still a prototype, and I can see building a solid application around it. Queue concurrency is limited to 1 because of VPS constraints, but that&amp;rsquo;s fine at my scale. A ton more could get done with vertical upgrades alone.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s room to improve: smarter issue routing, more robust review loops.&lt;/p&gt;

&lt;p&gt;The infrastructure for conventions and knowledge keeps paying dividends as it matures. Each project I add gets easier because the patterns are already documented and the agents already know how to follow them.&lt;/p&gt;

&lt;h2&gt;Where This Leaves Me&lt;/h2&gt;

&lt;p&gt;I came into this skeptical of the 100x claim. After building the infrastructure and living with it, I get what people are trying to say. They&amp;rsquo;re just not always articulating the details, and most don&amp;rsquo;t seem to mention the supporting systems you need to build before any of it works.&lt;/p&gt;

&lt;p&gt;The gains are real, they&amp;rsquo;re compounding, and they&amp;rsquo;re operational. They&amp;rsquo;re not just about writing code faster. I&amp;rsquo;m one person running a multi-project business with production customers, and the path forward keeps getting clearer. Mature the orchestration from a string-together prototype into a solid app. Then move on to growth wheels, marketing automation, or really anything else I feel like doing.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Composable Rules: How Small Teams Match Big Company Output Without the Bloat</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/12/24/composable-rules-getting-claude-code-to-honor-your-conventions.html"/>
    <id>http://www.brandoncasci.com/2025/12/24/composable-rules-getting-claude-code-to-honor-your-conventions.html</id>
    <published>2025-12-25T01:16:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-12-24&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;Composable Rules: How Small Teams Match Big Company Output Without the Bloat&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/12/24/cover.png" alt="Composable Rules" /&gt;&lt;/p&gt;

&lt;p&gt;Small teams and solo developers compete with funded companies every day. Traditionally, small teams defended niches by being nimble. Now they can simulate the parts of bigger institutions that work without the overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Big companies have resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple developers&lt;/li&gt;
&lt;li&gt;QA teams&lt;/li&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;li&gt;Established processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;They also have bloat:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coordination overhead&lt;/li&gt;
&lt;li&gt;Committee decisions&lt;/li&gt;
&lt;li&gt;Slow tool adoption&lt;/li&gt;
&lt;li&gt;Competing coding styles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Small teams have agility:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instant tool adoption&lt;/li&gt;
&lt;li&gt;Quick pivots&lt;/li&gt;
&lt;li&gt;Consistent vision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But without systematic infrastructure, agility can get tricky. Every new project starts over. Knowledge doesn&amp;rsquo;t compound.&lt;/p&gt;

&lt;p&gt;How do you get big company output without the bloat?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build reusable systems:&lt;/strong&gt;
- Conventions that travel
- Workflows that adapt
- Infrastructure that scales without headcount&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This systematizes what big companies can&amp;rsquo;t:&lt;/strong&gt;
- Speed of adoption
- Consistency of vision
- Low coordination overhead&lt;/p&gt;

&lt;p&gt;For me, this started with solving project memory - where you ingrain your intents, values, and processes into your AI helpers. While this article focuses on modular coding rules, the same process applies to business rules, customer feedback patterns, product decisions, and more. For software-centric businesses, coding conventions are the natural starting point.&lt;/p&gt;

&lt;p&gt;I had already solved project memory. Reference files and domain-specific CLAUDE.md files throughout my codebase kept Claude Code consistent with my conventions.&lt;/p&gt;

&lt;p&gt;When Claude shipped the rules feature for &lt;code&gt;.claude/rules/&lt;/code&gt;, I blew it off. Why rush to adopt something when I&amp;rsquo;d already solved the problem?&lt;/p&gt;

&lt;p&gt;But I finally tried it. What an improvement.&lt;/p&gt;

&lt;p&gt;The rules feature is cleaner, more organized, and way easier to manage than scattered CLAUDE.md files. I was well on the way of centralizing this into a consolidated references directory, but the Claude rules feature is a better solution. &lt;/p&gt;

&lt;p&gt;You drop markdown files in &lt;code&gt;.claude/rules/&lt;/code&gt;, Claude loads them automatically. Better separation of concerns. Better composability.&lt;/p&gt;

&lt;p&gt;Now I had a different problem: setting up those rules for every new project.&lt;/p&gt;

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

&lt;p&gt;You can&amp;rsquo;t just copy your entire &lt;code&gt;.claude/rules/&lt;/code&gt; folder from one Rails project to another. Each project uses different gems and libraries.&lt;/p&gt;

&lt;p&gt;Project A uses Pundit for authorization and Postgres. Project B uses SQLite and no authorization gem. If you copy all your rules, Project B gets Pundit conventions it doesn&amp;rsquo;t need and Postgres patterns for a database it&amp;rsquo;s not using.&lt;/p&gt;

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

&lt;p&gt;I organized my battle-tested Rails conventions into composable rule sets. Run one command, it detects what your project actually uses, and copies only the relevant rules.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m calling it the Hustler marketplace. Lean plugins for small businesses and small teams. Easy, pragmatic, low complexity.&lt;/p&gt;

&lt;p&gt;The first plugin is &lt;code&gt;hustler-rails&lt;/code&gt; for Rails conventions.&lt;/p&gt;

&lt;h2&gt;Why &amp;ldquo;Hustler&amp;rdquo;&lt;/h2&gt;

&lt;p&gt;Small businesses and small teams need tools that just work. No complexity, no over-engineering, no corporate bloat.&lt;/p&gt;

&lt;p&gt;Lean plugins. Pragmatic solutions. Low noise.&lt;/p&gt;

&lt;h2&gt;How Composable Rules Work&lt;/h2&gt;

&lt;p&gt;Rules use frontmatter to declare when they apply:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;---
paths: app/policies/**/*.rb, app/controllers/**/*.rb
dependencies: [pundit]
---

# Pundit - Authorization

Always put authorization in policies, not controllers...[and details about coding them]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;paths&lt;/code&gt; field is Claude Code&amp;rsquo;s feature for conditional rule loading, it accepts comma-separated glob patterns. This example loads authorization rules when working on either controllers or policies, so Claude always has the full authorization context regardless of which file you&amp;rsquo;re editing.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;dependencies&lt;/code&gt; field is my own convention that helps the init command detect which rules to copy. The extra fields don&amp;rsquo;t conflict.&lt;/p&gt;

&lt;p&gt;Universal rules have no dependencies and always get copied. Dependency-specific rules only get copied if your project uses that gem.&lt;/p&gt;

&lt;p&gt;The plugin detects dependencies by common sources like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Gemfile.lock&lt;/code&gt; for Ruby gems&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config/database.yml&lt;/code&gt; for database adapter&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;, importmap, CDN links for JS libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run &lt;code&gt;/hustler-rails:init&lt;/code&gt; and it composes the right rule set for your project.&lt;/p&gt;

&lt;h2&gt;Real Example&lt;/h2&gt;

&lt;pre&gt;&lt;code class="text"&gt;/hustler-rails:init
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;pre&gt;&lt;code class="text"&gt;Detected dependencies:
  - pundit (authorization)
  - beercss (UI framework)
  - sqlite3 (database)

Copying rules:
  ✓ models/conventions.md
  ✓ controllers/conventions.md
  ✓ testing/conventions.md
  ✓ authorization/pundit-policies.md
  ✓ views/beercss-components.md
  ✓ database/sqlite-patterns.md

Skipped (not detected):
  - devise (not in Gemfile.lock)
  - postgresql (using sqlite3)

📦 41 rule files copied to .claude/rules/hustler-rails/
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;Some of these dependencies are my personal favorites for non-corporate projects, but it&amp;rsquo;s no big deal to add support for other configurations&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You get conventions for what you&amp;rsquo;re actually using. Nothing more.&lt;/p&gt;

&lt;h2&gt;Why Copy Instead of Reference?&lt;/h2&gt;

&lt;p&gt;The plugin copies rules into your project rather than keeping them in the plugin and referencing them. This lets you tweak rules per project without forking the whole plugin.&lt;/p&gt;

&lt;p&gt;Maybe you mostly follow my testing conventions, but your project has one special case. Edit &lt;code&gt;.claude/rules/hustler-rails/testing/conventions.md&lt;/code&gt; and adjust it. Your change only affects this project.&lt;/p&gt;

&lt;p&gt;Future idea: an override system where rules stay in the plugin and per-project adjustments live separately. Haven&amp;rsquo;t built that yet.&lt;/p&gt;

&lt;h2&gt;What&amp;rsquo;s Included&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Thin model patterns&lt;/li&gt;
&lt;li&gt;Command pattern for business logic&lt;/li&gt;
&lt;li&gt;RESTful controller patterns&lt;/li&gt;
&lt;li&gt;Test structure and anti-patterns&lt;/li&gt;
&lt;li&gt;Authorization with Pundit&lt;/li&gt;
&lt;li&gt;Component patterns for BeerCSS and Turbo&lt;/li&gt;
&lt;li&gt;SQLite-specific patterns (multi-db, FTS5 search)&lt;/li&gt;
&lt;li&gt;Query optimization patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Organized by domain: models, controllers, views, testing, authorization, database, jobs.&lt;/p&gt;

&lt;h2&gt;Beyond Rails&lt;/h2&gt;

&lt;p&gt;The marketplace has two other plugins:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;hustler-studio&lt;/strong&gt; (coming soon): Project-agnostic workflows for TDD, refactoring, planning, and quality gates. Adapts to your conventions by reading your project&amp;rsquo;s CLAUDE.md and rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;prompting&lt;/strong&gt;: Prompt writing and optimization. Stack-agnostic.&lt;/p&gt;

&lt;h2&gt;The Pattern for Other Stacks&lt;/h2&gt;

&lt;p&gt;Composable rules aren&amp;rsquo;t Rails-specific. The pattern works for any framework: detect dependencies, copy relevant rules.&lt;/p&gt;

&lt;p&gt;Someone could build this for Django, Laravel, Next.js, NestJS, Phoenix, FastAPI. Same pattern, different dependency detection.&lt;/p&gt;

&lt;p&gt;If you want to collaborate on adding support for more gems or building plugins for other stacks, the marketplace is open.&lt;/p&gt;

&lt;h2&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;Install the marketplace:&lt;/p&gt;

&lt;pre&gt;&lt;code class="bash"&gt;/plugin marketplace add bcasci/hustle-marketplace
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Install the Rails plugin:&lt;/p&gt;

&lt;pre&gt;&lt;code class="bash"&gt;/plugin install hustler-rails
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Bootstrap your project:&lt;/p&gt;

&lt;pre&gt;&lt;code class="bash"&gt;/hustler-rails:init
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;rsquo;ll get composable rules tailored to your dependencies.&lt;/p&gt;

&lt;h3&gt;Want to Contribute?&lt;/h3&gt;

&lt;p&gt;Think more rules should be added? Have different architecture preferences? Build your own plugin following the same pattern, or collaborate with me to add it to this marketplace.&lt;/p&gt;

&lt;p&gt;Check out the repo: &lt;a href="https://github.com/bcasci/hustler-marketplace"&gt;https://github.com/bcasci/hustler-marketplace&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Building a Production App with AI: The Boswell Story</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/10/23/building-a-production-app-with-ai-the-boswell-story.html"/>
    <id>http://www.brandoncasci.com/2025/10/23/building-a-production-app-with-ai-the-boswell-story.html</id>
    <published>2025-10-24T02:09:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-10-23&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;Building a Production App with AI: The Boswell Story&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/10/23/cover.png" alt="Building a Production App with AI" /&gt;&lt;/p&gt;

&lt;p&gt;You see a lot of &amp;ldquo;AI-First&amp;rdquo; success stories floating around. Weekend builds that turn into six-figure SaaS products. Solo developers shipping faster than funded teams. Instant revenue from AI-generated apps.&lt;/p&gt;

&lt;p&gt;This isn&amp;rsquo;t that story.&lt;/p&gt;

&lt;p&gt;This is a breakdown of what AI-first product development actually looked like for me, and it wasn&amp;rsquo;t a straight line.&lt;/p&gt;

&lt;h2&gt;How I Got Here&lt;/h2&gt;

&lt;p&gt;I co-founded Boswell in 2017. It&amp;rsquo;s software for community food banks and social service organizations. It helps them track their client services, manage distributions, and effortlessly report data to grantors. We raised grants and impact capital, but the company meandered on mission, caught between what customers actually wanted and grant obligations. Eventually it shut down.&lt;/p&gt;

&lt;p&gt;Then I got a surprise offer: take the assets for free at the end of June 2025.&lt;/p&gt;

&lt;p&gt;Why not? I knew the product. It had existing customers. I felt that it had potential to be refocused as a SaaS product to specific types of Community Based Organizations. My consulting contract had ended. I was recovering from a health condition and needed time off, but wanted something to focus on. This gave me both.&lt;/p&gt;

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

&lt;p&gt;The codebase from the later engineering teams was a mess:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arbitrary dependency hacks to avoid updates&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Reactified&amp;rdquo; areas that worked fine with server-side rendering&lt;/li&gt;
&lt;li&gt;Competing architectures creating unnecessary complexity&lt;/li&gt;
&lt;li&gt;Data issues stemming from poor understanding of relational databases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first sign something was really wrong: users were experiencing slow reports. The previous team&amp;rsquo;s solution was expensive computing resources and scheduled overnight runs.&lt;/p&gt;

&lt;p&gt;My fix? A few database indexes.&lt;/p&gt;

&lt;p&gt;Result: 75% cost reduction immediately.&lt;/p&gt;

&lt;p&gt;But now I had to face the bigger mess.&lt;/p&gt;

&lt;h2&gt;The Decision&lt;/h2&gt;

&lt;p&gt;I weighed fixing mistakes versus starting over. I tried a cleanup approach with Cursor first. It&amp;rsquo;s fine for autocomplete but struggled with multi-step tasks. Then I tried Roo Code and Roo Code with SPARC. The issue with Roo Code ultimately came down to the per-request costs of cloud models and the limitations of local models.&lt;/p&gt;

&lt;p&gt;I switched to Claude Code and had it analyze the problems, then attempted git reverts of the indulgent changes.&lt;/p&gt;

&lt;p&gt;Conclusion: rewrite was the better call.&lt;/p&gt;

&lt;p&gt;This is where I really learned Claude Code: &lt;a href="/2025/09/21/how-to-transform-yourself-into-a-dev-team-using-claude-codes-ai-agents.html"&gt;how to transform myself into a dev team using specialized AI agents&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Validating the Market&lt;/h2&gt;

&lt;p&gt;Before committing to a rewrite, I validated the opportunity. I didn&amp;rsquo;t want to rebuild something nobody wants.&lt;/p&gt;

&lt;p&gt;I talked to prospective customers about their current pain points. Touched base with users of the old app to understand what worked and what didn&amp;rsquo;t. Gathered competing products in the community organization space and had AI analyze public information about them. Used AI to help identify which features deserved focus and dialed in what behaviors mattered most to the target market.&lt;/p&gt;

&lt;p&gt;The AI advantage here was rapid competitive analysis and pattern recognition across multiple products. It synthesized customer feedback into actionable insights and helped prioritize feature development.&lt;/p&gt;

&lt;p&gt;The result: confirmed market need, identified differentiation opportunities, and made the rewrite decision with confidence.&lt;/p&gt;

&lt;h2&gt;AI-First Isn&amp;rsquo;t Instant&lt;/h2&gt;

&lt;p&gt;The upstart cost is real. Efficiency gains have a learning curve.&lt;/p&gt;

&lt;p&gt;Out of the gate, the AI was unfocused and over-eager. Getting it to stay on task is harder than it seems. Prompting matters. Workflows matter. Guardrails matter.&lt;/p&gt;

&lt;p&gt;Gains come faster on greenfield projects. Legacy code, especially where consistency lacks, requires more work upfront to get AI productive.&lt;/p&gt;

&lt;h2&gt;What Worked&lt;/h2&gt;

&lt;h3&gt;Domain Knowledge Is the Foundation&lt;/h3&gt;

&lt;p&gt;I provide patterns and anti-patterns explicitly. Articulate rules and conventions clearly. Document what the app does and why. This helps AI serve customers better, not just write code.&lt;/p&gt;

&lt;p&gt;This is &lt;a href="/2025/09/25/my-ai-organization-ships-like-we-re-funded-but-it-s-just-me.html"&gt;the knowledge infrastructure that makes AI accurate&lt;/a&gt;. Without it, AI will apply common practices that might not fit your needs, add abstraction layers you don&amp;rsquo;t want, and miss requirements unique to your system.&lt;/p&gt;

&lt;h3&gt;Know AI&amp;rsquo;s Actual Strengths&lt;/h3&gt;

&lt;p&gt;AI is better at:
- Analyzing
- Brainstorming
- Planning
- Being a soundboard&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s less reliable at writing production-ready code on the first pass. But you can use its strengths to have it write better code.&lt;/p&gt;

&lt;h3&gt;The Workflow That Works&lt;/h3&gt;

&lt;p&gt;At a minimum you need a workflow that plans, executes, reviews, and iterates.&lt;/p&gt;

&lt;p&gt;You must be involved in planning. You can use AI to generate plans, but you have to be involved in curating them at minimum. Then AI implements. Then you must be involved in review. Respond to reviews, iterate.&lt;/p&gt;

&lt;p&gt;Eventually you can hand off more planning and reviews to autonomous workflows. Validate kickoff plans and spot-check reviews. Drift happens even with guardrails. Lengthen the leash gradually.&lt;/p&gt;

&lt;p&gt;When things break down, execution problems usually point to planning gaps or knowledge gaps. Too many review cycles with naive mistakes indicate planning or knowledge issues. Use these as opportunities to improve documentation, knowledge, and workflows.&lt;/p&gt;

&lt;h2&gt;Controlling AI&lt;/h2&gt;

&lt;h3&gt;They Will Try to Escape&lt;/h3&gt;

&lt;p&gt;Without constraints, your AI helpers will lie, be lazy, jailbreak constraints, and countermand your instructions.&lt;/p&gt;

&lt;p&gt;Simplified example: I limited one agent to Read Only by taking away direct Write access to files. Instead, it used console-based editors along with I/O redirection to manipulate files. You need to be very intentional with permissions.&lt;/p&gt;

&lt;h3&gt;What Keeps Them in Line&lt;/h3&gt;

&lt;p&gt;Be intentional and direct. Less is more, only as verbose as needed. Break large tasks into smaller ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="/2025/09/23/how-i-went-from-dev-team-to-engineering-vp-with-claude-code.html"&gt;This is how specialized agents stay focused&lt;/a&gt; through orchestration and clear boundaries.&lt;/p&gt;

&lt;h3&gt;The Composability Advantage&lt;/h3&gt;

&lt;p&gt;Claude Code&amp;rsquo;s framework gives you custom slash commands, custom skills (new to Claude as of 10/16/2025), custom sub-agents, and hooks for external tools.&lt;/p&gt;

&lt;p&gt;The result: reusable components that improve accuracy over time.&lt;/p&gt;

&lt;h2&gt;Tools and Tactics&lt;/h2&gt;

&lt;h3&gt;Screenshots&lt;/h3&gt;

&lt;p&gt;Very helpful for debugging and bringing concepts to life. Caveat: make clear when it&amp;rsquo;s a concept or wireframe versus literal implementation. Otherwise AI will build what&amp;rsquo;s in the picture, not what you meant.&lt;/p&gt;

&lt;h3&gt;Third-Party Caution&lt;/h3&gt;

&lt;p&gt;Avoid third-party plugins at first. They can do anything, which is a security risk. You won&amp;rsquo;t learn the fundamentals. Resist the &amp;ldquo;$299 course secrets&amp;rdquo; temptation. Master the basics first with documentation and other people&amp;rsquo;s shared experiences.&lt;/p&gt;

&lt;h2&gt;The Investment&lt;/h2&gt;

&lt;p&gt;Several times I spent days fine-tuning Claude Code to solve something I could have done manually in 15 minutes. The problem felt like a repeatable pattern worth automating. Each time I invested in understanding how to effectively direct AI, those strategies worked across other projects.&lt;/p&gt;

&lt;p&gt;Was it worth it?&lt;/p&gt;

&lt;p&gt;Yes, but with context. I had time to burn after my contract ended. Recovering from a health challenge gave me flexibility to work at my own pace. These were my own projects, so I could experiment without client pressure. Not everyone has this luxury. Limited time forces you to prioritize what automation to invest in. Maybe I over-indulged.&lt;/p&gt;

&lt;h2&gt;Where It Stands Now&lt;/h2&gt;

&lt;p&gt;I can automate modest changes and bug fixes with sentences or screenshots. Fire them off locally or via GitHub issue. A &lt;code&gt;@claude fix this&lt;/code&gt; comment implements automatically.&lt;/p&gt;

&lt;p&gt;I was able to move faster on advanced features that would normally have taken quite a bit more time, like complete data isolation between clients and event-based email automation for onboarding and lead nurturing. The kind of operational conveniences I&amp;rsquo;d usually put off for &amp;ldquo;phase 2&amp;rdquo; or later, but got built in phase 1.&lt;/p&gt;

&lt;p&gt;&lt;img src="/2025/10/23/mini-tour.gif" alt="Boswell in action" /&gt;&lt;/p&gt;

&lt;p&gt;The bigger picture: three projects running AI-first workflows. Boswell for community based organization management, plus two others where the same techniques apply.&lt;/p&gt;

&lt;h2&gt;Perspective on AI and Business&lt;/h2&gt;

&lt;h3&gt;The Reasonable Skeptics&lt;/h3&gt;

&lt;p&gt;Some people are skeptical of AI adoption, and honestly, they have a point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s a wild beast requiring close attention&lt;/li&gt;
&lt;li&gt;It amplifies everything: skill, knowledge, mistakes, and bad intentions&lt;/li&gt;
&lt;li&gt;Corporations using it to optimize profit over fairness (UnitedHealth&amp;rsquo;s AI denying claims, insurers terminating policies based on flawed models)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The Opportunity&lt;/h3&gt;

&lt;p&gt;If you&amp;rsquo;re skeptical or just uncertain how AI applies to you, flip the question: if AI could threaten your job or livelihood, can you use it first? The sword cuts both ways. Use it to build revenue streams beyond a paycheck. I did this with rental properties a while back, and AI-assisted product building is way easier in a lot of ways.&lt;/p&gt;

&lt;p&gt;If you know something deeply, package it as a product or service. It&amp;rsquo;s possibly easier now than ever.&lt;/p&gt;

&lt;h3&gt;Small vs Large Organizations&lt;/h3&gt;

&lt;p&gt;Individuals and small companies may have an advantage for now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller scope, easier to automate&lt;/li&gt;
&lt;li&gt;More willing to experiment and pivot&lt;/li&gt;
&lt;li&gt;Risks easier to manage&lt;/li&gt;
&lt;li&gt;Lower bar for success (revenue and cost-savings wise)&lt;/li&gt;
&lt;li&gt;Less institutional overhead&lt;/li&gt;
&lt;li&gt;Requires whole-organization effort for full benefit, meaning fewer people to convince, especially if you&amp;rsquo;re one person&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Large corporations might struggle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI mandate without guidance&lt;/li&gt;
&lt;li&gt;Slower to change&lt;/li&gt;
&lt;li&gt;Staff resistance&lt;/li&gt;
&lt;li&gt;Higher risk profile&lt;/li&gt;
&lt;li&gt;More to break&lt;/li&gt;
&lt;li&gt;Harder to detect unintended consequences&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Where I Stand&lt;/h2&gt;

&lt;p&gt;I&amp;rsquo;m comfortable with these tools personally. I&amp;rsquo;m not automating every aspect of life. I really like them for solo and small business work.&lt;/p&gt;

&lt;p&gt;Boswell is live, serving customers, and actually helping community organizations skip the paperwork and get back to serving people. The platform tracks 50K+ household assistances each month, 1.2M total to date. The rewrite worked. The AI-first approach delivered.&lt;/p&gt;

&lt;p&gt;It required investment: time to learn the tools, money to burn through API costs, and patience to build the knowledge infrastructure that makes AI accurate. The weekend-build-to-riches stories happen, but they&amp;rsquo;re rare. You hear about the few that succeed, not the millions that don&amp;rsquo;t. This took months: trying several AI tools, giving each a fair evaluation, experimenting with Claude Code plugins and addons, then stripping away dependencies to build workflows that actually worked.&lt;/p&gt;

&lt;p&gt;The payoff: strategies and skills that accelerate my current business, apply to future ones, or make me more valuable to any employer.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>My AI Organization Ships Like We're Funded, But It's Just Me (Part 1)</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/09/25/my-ai-organization-ships-like-we-re-funded-but-it-s-just-me.html"/>
    <id>http://www.brandoncasci.com/2025/09/25/my-ai-organization-ships-like-we-re-funded-but-it-s-just-me.html</id>
    <published>2025-09-25T04:00:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-09-25&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;My AI Organization Ships Like We&amp;rsquo;re Funded, But It&amp;rsquo;s Just Me (Part 1)&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/09/25/cover.png" alt="Subagents and manager hard at work" /&gt;&lt;/p&gt;

&lt;h2&gt;Part 1: The Knowledge Infrastructure&lt;/h2&gt;

&lt;p&gt;I ship at the velocity of a funded startup, but it&amp;rsquo;s just me. The difference is giving AI assistants the same domain knowledge a human team member would have.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m not working harder or using productivity hacks. I&amp;rsquo;m helping AI move from a coding intern into something that can reason about product engineering and business knowledge.&lt;/p&gt;

&lt;h2&gt;The Evolution So Far&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/2025/09/21/how-to-transform-yourself-into-a-dev-team-using-claude-codes-ai-agents.html"&gt;How One Person Becomes a Dev Team with Claude Code&lt;/a&gt;&lt;/strong&gt; - 5 specialists working in parallel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/2025/09/23/how-i-went-from-dev-team-to-engineering-vp-with-claude-code.html"&gt;From Dev Team to Engineering VP with Claude Code&lt;/a&gt;&lt;/strong&gt; - Learned to orchestrate, not code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;This article&lt;/strong&gt; - The knowledge infrastructure that makes AI accurate&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;The Difference That Domain Knowledge Makes&lt;/h2&gt;

&lt;p&gt;Without your specific context, AI will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apply common practices that might not fit your needs&lt;/li&gt;
&lt;li&gt;Add abstraction layers you don&amp;rsquo;t want&lt;/li&gt;
&lt;li&gt;Miss requirements unique to your system&lt;/li&gt;
&lt;li&gt;Suggest solutions that violate your constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But when you document your domain knowledge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your product helps [specific customer type] do [specific thing] better&lt;/li&gt;
&lt;li&gt;Customers stay because of [key differentiator], not features&lt;/li&gt;
&lt;li&gt;Variable names match what the business calls things (not generic &amp;ldquo;item&amp;rdquo; or &amp;ldquo;data&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Good code&amp;rdquo; here means readable over clever, explicit over abstract&lt;/li&gt;
&lt;li&gt;That database choice was about compliance, not performance&lt;/li&gt;
&lt;li&gt;Multi-tenant isolation is non-negotiable due to [specific requirement]&lt;/li&gt;
&lt;li&gt;Past mistakes that shouldn&amp;rsquo;t be repeated&lt;/li&gt;
&lt;li&gt;The architecture patterns you actually strive for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now AI makes the right architectural decisions without you explaining everything every time.&lt;/p&gt;

&lt;h2&gt;The Knowledge Infrastructure&lt;/h2&gt;

&lt;h3&gt;Start with a Root CLAUDE.md&lt;/h3&gt;

&lt;p&gt;Here&amp;rsquo;s a reduced example from a production Rails app:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# CLAUDE.md - Domain Knowledge for Boswell

## What Boswell Does

- Client management system for food banks and community organizations
- Tracks households, people, distributions, and assistance programs
- Manages eligibility and benefits for food assistance recipients

## Core Architecture

- **Multi-tenant SaaS** with SQLite-per-tenant isolation
- **Tech Stack**: Rails 8, Turbo/Stimulus, Solid Queue, Passwordless auth
- **Real-time**: Turbo Streams and Action Cable for live updates

## Domain Knowledge Distribution

CLAUDE.md files exist throughout the codebase with area-specific guidance. Here are just a few:
- `app/models/CLAUDE.md` Model patterns and database architecture
- `app/controllers/CLAUDE.md` RESTful patterns and Turbo integration
- `app/business_logic/CLAUDE.md` Command pattern implementation

## Code Navigation

Look for AIDEV-NOTE comments in complex areas - they explain non-obvious decisions and gotchas.

# ... longer example at end of article
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Document it once, and it&amp;rsquo;s available in every coding session.&lt;/p&gt;

&lt;h3&gt;Add Deep Documentation for Complex Systems&lt;/h3&gt;

&lt;p&gt;Reference docs by path and Claude Code reads them if needed, or use &lt;code&gt;@path/to/doc.md&lt;/code&gt; to always import the full content immediately. Here&amp;rsquo;s how I documented our payout system:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# Payout System

## Flow
ORDER → RevenueSplit → OrderItem (owner/referrer/platform shares)
                           ↓
                    PayoutSchedulerJob (weekly)
                           ↓
                    ProcessPayoutJob → Stripe Transfer

## Key Design Decisions

**Why weekly batching?**
Reduces Stripe fees and operational overhead vs daily payouts.

**Why PayoutItem junction table?**
Prevents double-payment via unique constraint on [order_item_id, payout_type].

## Troubleshooting
**No payout created?**
- User.find(id).stripe_connect_account_id must exist
- Check for existing PayoutItem blocking re-payment

# ... longer example at end of article
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can import these from any CLAUDE.md, prompt, command, or agent:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;## Domain Knowledge
@docs/payout-system.md          # Weekly payment processing logic
@docs/multi-tenant-isolation.md # Critical security boundaries
@docs/compliance-hipaa.md       # Healthcare data requirements
@docs/api-rate-limiting.md      # Customer throttling rules
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Import these anywhere: in CLAUDE.md files, prompts, commands, or agent instructions.&lt;/p&gt;

&lt;h3&gt;Distribute Knowledge Throughout Your Codebase&lt;/h3&gt;

&lt;p&gt;Each major area gets its own CLAUDE.md file:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# app/models/CLAUDE.md

## Model Inheritance (STI)
class Album &amp;lt; Product  # type = &amp;#39;Album&amp;#39;
class Track &amp;lt; Product  # type = &amp;#39;Track&amp;#39;

## Money Handling
monetize :price_cents
# Usage: album.price = Money.new(1000, &amp;quot;USD&amp;quot;)  # $10.00

## Common Scopes
scope :published, -&amp;gt; { where(listing_state: &amp;quot;published&amp;quot;) }
scope :salable, -&amp;gt; { published.where(&amp;quot;release_date &amp;lt;= ?&amp;quot;, Time.current) }

## Testing Models
- Test your behavior, not Rails
- Focus on custom methods, complex validations, business rules

# ... longer example at end of article
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Leave Breadcrumbs with AIDEV-NOTE Comments&lt;/h3&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;# AIDEV-NOTE: Critical section - must acquire lock
# This prevents the race condition we discovered in issue #234
# See: docs/payment-processing.md section 2.2
# Test coverage: spec/models/payment_spec.rb
def process_refund(amount)
  with_advisory_lock(&amp;quot;refund_#{id}&amp;quot;) do
    # implementation
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Explain the non-obvious decisions directly in the code.&lt;/p&gt;

&lt;h2&gt;Less Prompting, More Accurate Results&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Without domain knowledge:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;You: &amp;quot;Add partial refund support&amp;quot;
AI: [Creates new PartialRefundsController]
    [Adds refund_amount column to orders table]
    [Direct Stripe API call in controller action]
    [No idempotency handling]
    [Doesn&amp;#39;t update revenue reporting]
You: &amp;quot;No, we need to... let me explain our architecture...&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;With domain knowledge:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;You: &amp;quot;Add partial refund support&amp;quot;
AI: [Updates existing Commands::ProcessRefund to handle amounts]
    [Records in ledger_entries with proper double-entry]
    [Queues background job with idempotency key]
    [Follows the same async pattern as other payment operations]
You: &amp;quot;Good, ship it&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The Compound Effect&lt;/h2&gt;

&lt;p&gt;Every piece of domain knowledge you add compounds over time. Clean commits and good documentation build on each other, while garbage commits become negative context that confuses future work.&lt;/p&gt;

&lt;p&gt;Even with good domain knowledge, AI will make mistakes (you would too). The difference is catching them early:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create review agents with quality standards&lt;/li&gt;
&lt;li&gt;Review before committing&lt;/li&gt;
&lt;li&gt;When AI struggles, add the missing context&lt;/li&gt;
&lt;li&gt;Write clear specs when starting tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I&amp;rsquo;ve found two main failure points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Missing domain context&lt;/strong&gt; - AI doesn&amp;rsquo;t know your business rules&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ambiguous requirements&lt;/strong&gt; - &amp;ldquo;Make it better&amp;rdquo; means nothing even with perfect context&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple prompts like &amp;ldquo;add partial refund support&amp;rdquo; work when your domain docs define the concepts. For complex changes, write a spec that defines success.&lt;/p&gt;

&lt;p&gt;The compound effect is real: each piece of context you add makes the next task easier. Clean as you go, and watch your velocity increase.&lt;/p&gt;

&lt;h2&gt;How to Get Started&lt;/h2&gt;

&lt;p&gt;Ask your AI to help create the documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Generate CLAUDE.md files for each major area of this codebase&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;What areas of this codebase would benefit from domain documentation?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Create AIDEV-NOTE comments for the complex sections you found&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When AI struggles with something, that&amp;rsquo;s a signal. Ask it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;ldquo;What context would help you understand this better?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Where should we document this concept?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Add what you just learned to [file].md, optimized for AI agents, concise but informative&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use AI to help write the documentation once you&amp;rsquo;ve identified what&amp;rsquo;s needed.&lt;/p&gt;

&lt;p&gt;Distribute CLAUDE.md files throughout your codebase - one per module, service, or complex area. Many small context files beat one giant file. AI can help identify where to place them.&lt;/p&gt;

&lt;h2&gt;Coming in Part 2&lt;/h2&gt;

&lt;p&gt;This is just domain knowledge. Next, I&amp;rsquo;ll show you how I:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How I manage tickets and issues with AI&lt;/li&gt;
&lt;li&gt;The CI/CD setup and PR review process&lt;/li&gt;
&lt;li&gt;How code review works when your team is AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For massive enterprise codebases, you&amp;rsquo;d need more complex memory management. But this simple approach works great for most projects and requires nothing beyond Claude Code itself.&lt;/p&gt;

&lt;p&gt;Document your domain knowledge first. Everything else builds on that foundation.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Reduced Examples From Production&lt;/h2&gt;

&lt;p&gt;These are the actual knowledge files that turn AI assistants from interns into professionals.&lt;/p&gt;

&lt;h3&gt;Example: Reduced out of CLAUDE.md from Boswell (CBO Management System)&lt;/h3&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# CLAUDE.md - Domain Knowledge for Boswell
[NOTE: Production file contains more comprehensive documentation]

This file provides guidance to Claude Code when working with the Boswell repository.

## Project Overview

**Boswell** is a Rails 8 food assistance management system designed for Community Based Organizations (CBOs).

### What It Does

- Client management system for food banks and community organizations
- Tracks households, people, distributions, and assistance programs
- Manages eligibility and benefits for food assistance recipients
- Provides real-time updates for distribution events

### Who It&amp;#39;s For

- Food banks
- Community organizations
- Social service agencies providing assistance to households in need

### Core Architecture

- **Multi-tenant SaaS** with SQLite-per-tenant isolation (each org gets its own database)
- **Tech Stack**: Rails 8, Turbo/Stimulus, Solid Queue, Passwordless auth, Pundit
- **Real-time**: Turbo Streams and Action Cable for live updates
- **UI**: Server-side rendering with BeerCSS components

## Development Workflows with Specialized Agents

### Orchestrator Commands

/orchestrators:tdd #&amp;lt;issue_number&amp;gt;        # Full TDD for new features
/orchestrators:implement #&amp;lt;issue_number&amp;gt;   # Direct implementation (faster)
/orchestrators:fix-bug #&amp;lt;issue_number&amp;gt;     # Bug fixes with regression test
/orchestrators:refactor &amp;quot;description&amp;quot;      # Safe refactoring with tests

### Specialized Agents

Use specialized subagents when appropriate for tasks like database migrations, bug diagnosis, code quality checks, and more. These will be auto-invoked based on the task context.

## Domain Knowledge Distribution

Always check for relevant CLAUDE.md for our coding standards, patterns and domain knowledge. The codebase contains many CLAUDE.md files with specialized guidance, here are a few:

- app/models/CLAUDE.md Model patterns and database architecture
- app/controllers/CLAUDE.md RESTful patterns and Turbo integration
- app/business_logic/CLAUDE.md Command pattern implementation
- app/policies/CLAUDE.md Authorization patterns with Pundit
- test/CLAUDE.md Testing philosophy and anti-patterns

## Essential Commands

# Development
bin/dev                      # Start Rails server
bin/rails console            # Interactive console

# Database
bin/rails db:prepare         # Setup from scratch
bin/rails db:migrate         # Run migrations

# Testing &amp;amp; Quality
bin/rails test               # Run full test suite
bin/rubocop -A              # Auto-fix style issues

[... production file continues with more sections ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Example: Payout System Domain Documentation (Reduced out of Harmonic.fm)&lt;/h3&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# Payout System
[NOTE: Production file contains additional implementation details]

## Flow

ORDER → RevenueSplit → OrderItem (owner/referrer/platform shares)
                           ↓
                    PayoutSchedulerJob (weekly via config/recurring.yml)
                    Finds users with:
                    - Unpaid owner shares (artists)
                    - Unpaid referrer shares (curators)
                           ↓
                    CreatePayoutJob (per user)
                           ↓
                    ProcessPayoutJob → Stripe Transfer

## Key Design Decisions

**Why weekly batching?**
Reduces Stripe fees and operational overhead vs daily payouts.

**Why combine owner + referrer earnings?**
Single transfer per user is simpler and cheaper than separate transfers.

**Why PayoutItem junction table?**
Prevents double-payment via unique constraint on `[order_item_id, payout_type]`.

## Troubleshooting

**No payout created?**

- `User.find(id).stripe_connect_account_id` must exist
- `OrderItem.unpaid_items_for_user(user)` must return records
- Check for existing `PayoutItem` blocking re-payment

**Stripe transfer failed?**

- Check Stripe Connect account status in Stripe Dashboard
- Verify platform has sufficient balance
- Review `Payout.last.error_message`

## Entry Points

- Schedule: `config/recurring.yml` → `payout_scheduler`
- Manual: `PayoutSchedulerJob.perform_now`
- Per-user: `Commands::CreatePayout.new(user: user).run`

[... production file continues with more details ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Example: Reduced Model-Specific CLAUDE.md&lt;/h3&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# /app/models/CLAUDE.md
[NOTE: Production file includes complete pattern library]

# Model &amp;amp; Database Patterns

## Best Practices

1. **Use scopes**: Named queries for readability
2. **Callbacks**: OK for same-model operations, avoid cross-model callbacks
3. **Commands for business logic**: Multi-step operations across models belong in Commands
4. **Index foreign keys**: Essential for performance
5. **Use concerns**: Extract shared behavior
6. **Validate data**: At database and model level
7. **Test your behavior, not Rails**: Focus on custom methods, complex validations, and business rules

## Common Models

- `User` - Authentication via Passwordless
- `Tenant` - Multi-tenant organization (food bank/CBO)
- `Household` - Family unit receiving assistance
- `Person` - Individual member of household
- `Distribution` - Distribution of assistance event
- `AssistanceProgram` - Programs offered (SNAP, WIC, etc.)
- `Allocation` - Resource assignments
- `Role` - User permissions within tenant
- `TagVocabulary` &amp;amp; `AllowedTag` - Flexible tagging system

## Model Inheritance (STI)

    class Product &amp;lt; ApplicationRecord
      # Base class for all products
    end

    class Album &amp;lt; Product
      # type = &amp;#39;Album&amp;#39;
      has_many :tracks, foreign_key: :parent_id
      has_one :album_variant
    end

    class Track &amp;lt; Product
      # type = &amp;#39;Track&amp;#39;
      belongs_to :album, foreign_key: :parent_id, optional: true
      has_one :track_variant
    end

## Common Patterns

### Scopes

    class Album &amp;lt; Product
      scope :published, -&amp;gt; { where(listing_state: &amp;quot;published&amp;quot;) }
      scope :salable, -&amp;gt; { published.where(&amp;quot;release_date &amp;lt;= ?&amp;quot;, Time.current) }
      scope :newest, -&amp;gt; { order(created_at: :desc) }
      scope :by_genre, -&amp;gt;(genre) { tagged_with(genre) }
    end

### JSONB Metadata

    class Product &amp;lt; ApplicationRecord
      # Access JSONB fields
      store_accessor :metadata,
        :artist_name, :release_date, :duration,
        :track_number, :genre_list

      # Usage
      album.metadata_artist_name = &amp;quot;Artist Name&amp;quot;
      album.metadata_release_date = Date.today
    end

### Soft Delete

    class Product &amp;lt; ApplicationRecord
      include Discard::Model

      # Usage
      album.discard    # Soft delete
      album.undiscard  # Restore
      Album.kept       # Non-deleted records
      Album.discarded  # Deleted records
    end

## Associations

### Has Many Through

    class User &amp;lt; ApplicationRecord
      has_many :organization_memberships
      has_many :organizations, through: :organization_memberships

      has_many :orders
      has_many :purchased_products, through: :orders, source: :product
    end

### Polymorphic

    class Recommendation &amp;lt; ApplicationRecord
      belongs_to :user
      belongs_to :recommendable, polymorphic: true

      # Can recommend albums, tracks, etc.
    end

## Validations

    class Album &amp;lt; Product
      validates :title, presence: true, length: { maximum: 255 }
      validates :slug, uniqueness: { scope: :organization_id }
      validates :price_cents, numericality: { greater_than_or_equal_to: 0 }

      validate :has_items, on: :create

      private

      def has_items
        errors.add(:base, &amp;quot;Must have items&amp;quot;) if items.empty?
      end
    end

## Money Handling

    class Product &amp;lt; ApplicationRecord
      monetize :price_cents

      # Usage
      album.price = Money.new(1000, &amp;quot;USD&amp;quot;)  # $10.00
      album.price_cents # =&amp;gt; 1000
      album.price.format # =&amp;gt; &amp;quot;$10.00&amp;quot;
    end

## Testing Models

    class AlbumTest &amp;lt; ActiveSupport::TestCase
      describe &amp;quot;validations&amp;quot; do
        it &amp;quot;requires title&amp;quot; do
          album = Album.new
          refute album.valid?
          assert_includes album.errors[:title], &amp;quot;can&amp;#39;t be blank&amp;quot;
        end
      end

      describe &amp;quot;scopes&amp;quot; do
        it &amp;quot;returns published albums&amp;quot; do
          published = Album.published
          assert published.all? { |a| a.listing_state == &amp;quot;published&amp;quot; }
        end
      end
    end

[... production file continues with more patterns and examples ...]
&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <title>How I Went From Dev Team to Engineering VP with Claude Code</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/09/23/how-i-went-from-dev-team-to-engineering-vp-with-claude-code.html"/>
    <id>http://www.brandoncasci.com/2025/09/23/how-i-went-from-dev-team-to-engineering-vp-with-claude-code.html</id>
    <published>2025-09-23T04:00:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-09-23&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;How I Went From Dev Team to Engineering VP with Claude Code&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/09/23/cover.png" alt="Subagents and manager hard at work" /&gt;&lt;/p&gt;

&lt;p&gt;I had a bug that only appeared during parallel processing. It was intermittent, timing-dependent, and the kind that makes you question your life choices.&lt;/p&gt;

&lt;p&gt;My issue-diagnosis agent found worthwhile improvements that would stave off issues like this, but couldn&amp;rsquo;t nail the root cause. After a few attempts I took a crack at it, and then thought: wait a minute. I had just recently &lt;a href="https://www.brandoncasci.com/2025/09/19/how-one-person-becomes-a-dev-team-with-claude-code.html"&gt;created a team of specialists&lt;/a&gt; and seeded the project with context and domain knowledge. Is this really what I&amp;rsquo;m supposed to be using my time for?&lt;/p&gt;

&lt;pre&gt;&lt;code class="text"&gt;Me → Claude → Diagnoser → Fix → Test
                 ↑                 ↓
                 └── Still Broken ←┘
                   (repeat 3x)
                        ↓
                 Debug manually
                        ↓
            &amp;quot;Wait, why am I doing this?&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So I made the sub-agents huddle and figure it out.&lt;/p&gt;

&lt;pre&gt;&lt;code class="text"&gt;claude&amp;gt; Work on issue #234. Use issue-diagnoser, analyst, test-writer, and
        implementer. Collaborate by orchestrating communication between them. 
        You will have to make them aware of each other&amp;#39;s findings so they can problem
        solve more effectively.

        Ultrathink about possible causes. Tell each agent to ultrathink
        when analyzing. The previous attempts found real issues but not root cause.

        Once you identify the problem, implement minimal fix to verify. Then have
        the team collaborate on various other optimal solutions. Present me with the
        quick fix, and multiple best-practice approaches so I can decide.
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The Agents Worked It Out, Orchestration Can Work On-the-fly&lt;/h2&gt;

&lt;p&gt;The app involves a multi-tenant architecture with complete data isolation between tenants. There was a very minor issue with a shard-switching resource that manifested when the number of processors was increased. Something not realistic on my laptop.&lt;/p&gt;

&lt;p&gt;The solution was simple: a MUTEX. But that&amp;rsquo;s beside the point.&lt;/p&gt;

&lt;p&gt;The sub-agents did all of the heavy lifting and found an obscure problem pretty quickly. Based on what I saw, the orchestration that it cooked up looked something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="text"&gt;Issue #234
    ↓
Claude reads issue
    ↓
→ Diagnoser: &amp;quot;shard switching&amp;quot;
    ↓
→ Analyst: &amp;quot;multi-tenant issue&amp;quot;
    ↓
→ Test-Writer: &amp;quot;can&amp;#39;t reproduce&amp;quot;
    ↓
 ┌─────────┐
 │ Retry?  │
 │    ↓    │
 └──→ Yes ─┘
    ↓ No
&amp;quot;Reproduced! High CPU count&amp;quot;
    ↓
→ Implementer: &amp;quot;MUTEX fix&amp;quot;
    ↓
Multiple solutions presented
    ↓
I decide
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I did have to ask for a refinement on the solution I thought best, but that wasn&amp;rsquo;t particularly time consuming.&lt;/p&gt;

&lt;h2&gt;Why This Actually Worked&lt;/h2&gt;

&lt;p&gt;Three things made this ad-hoc orchestration effective:&lt;/p&gt;

&lt;h3&gt;1. You have to rally the troops&lt;/h3&gt;

&lt;p&gt;Claude will automatically delegate to subagents when it makes sense. But if you want specific agents involved at a specific moment, you need to call them out by name. Include phrases like &lt;code&gt;use subagent [name]&lt;/code&gt; to be explicit.&lt;/p&gt;

&lt;p&gt;Although now I can see the case for a &lt;code&gt;/dev-team-huddle&lt;/code&gt; command that would kick off this type of workflow automatically.&lt;/p&gt;

&lt;h3&gt;2. Subagent definitions need good metadata&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;description&lt;/code&gt; field is critical. It tells the primary agent what the subagent does, when to use it, and what information needs to be passed to it.&lt;/p&gt;

&lt;p&gt;Subagents share NO session information with the primary agent. All the prompting you do, everything in the conversation you see (and don&amp;rsquo;t see) is not shared with the subagent. That initial prompt the primary agent sends them is all they have to work with.&lt;/p&gt;

&lt;h3&gt;3. Domain knowledge makes isolated agents effective&lt;/h3&gt;

&lt;p&gt;My projects are rich with domain knowledge. There are CLAUDE.md files with patterns and anti-patterns in strategic locations. The code has casual documentation in it. More intricate areas have more substantial comments (# AIDEV-NOTE: &amp;hellip;). Then for fundamental concepts there are stand-alone meta-docs that summarize these topics. These help the primary agent (Claude Code in this case) and the subagents it delegates to. They can navigate and reason about the project much more effectively.&lt;/p&gt;

&lt;p&gt;So the sub-agents are set up pretty well even though they&amp;rsquo;re cut off from the larger conversation. They have the context they need baked into the project.&lt;/p&gt;

&lt;h2&gt;What These Agents Actually Look Like&lt;/h2&gt;

&lt;p&gt;The metadata and domain knowledge I mentioned? Here&amp;rsquo;s what that looks like in practice. These are shortened versions. The production ones have more detail, but you get the idea:&lt;/p&gt;

&lt;h3&gt;Analyst Agent&lt;/h3&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;---
name: analyst
description: Requirements and architecture analyst who finds patterns and recommends implementation approaches. Pass the complete requirement OR diagnosis from issue-diagnosis. Returns patterns found and architectural guidance. Invoke at start of feature work, planning, or when evaluating bug fix approaches.
tools: Read, Grep, Glob
model: inherit
---

You are a requirements and architecture analyst responsible for understanding features and recommending implementation approaches.

## Core Philosophy

**IMPORTANT: Prioritize correctness and comprehensive solutions over implementation speed.**

- AI can implement quickly regardless of approach
- Test runtime differences are negligible
- **ULTRATHINK**: Think deeply about the best long-term solution
- Consider multiple layers of testing when gaps exist
- Strategic decisions matter more than tactical optimizations

## Scope

**I analyze and recommend:**

- Requirements and GitHub issues
- Bug fix approaches (when given diagnosis from issue-diagnosis)
- Existing patterns in codebase
- Implementation approaches (both for existing patterns and new capabilities)
- Which files need modification
- System architecture decisions
- Comprehensive test strategies across all layers (unit/integration/system)

**I don&amp;#39;t:**

- Write code (I&amp;#39;m read-only)
- Run tests or review code
- Implement the decisions (I recommend, others implement)
- Diagnose bugs (that&amp;#39;s issue-diagnosis&amp;#39;s job)

_[Full production version includes detailed output formats, examples, and comprehensive workflow details]_
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Issue Diagnosis Agent&lt;/h3&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;---
name: issue-diagnosis
description: Analyzes bug reports to identify root cause and test gaps. Pass &amp;quot;issue #XXX&amp;quot; or a bug description with affected UI and symptoms. Returns diagnosis only - does not prescribe solutions.
tools: Grep, Read, Bash, Glob
---

## Purpose

Read-only diagnostic specialist. Find root causes and identify test gaps. Do NOT prescribe solutions - only diagnose problems.

## Input

Accept either:

- `issue #XXX` - fetch with `gh issue view XXX`
- Bug description with UI location and symptoms

## Workflow

1. **Find the bug** - Locate where it manifests in code
2. **Trace root cause** - Follow data/logic flow to actual problem
3. **Check ALL test coverage** - Use Glob to find test files at every layer
4. **Identify ALL gaps** - Report missing tests at each affected layer

## Critical Thinking About Test Placement

**Think carefully:** Where does this bug ACTUALLY manifest? Match the test to the bug&amp;#39;s location:

- **UI/Visual bug** (wrong text shown, button doesn&amp;#39;t work) → **System test gap**
- **Business logic bug** (calculation wrong, validation missing) → **Model/Command test gap**
- **Authorization bug** (user can access forbidden resource) → **Policy test gap**
- **HTTP bug** (wrong status code, missing redirect) → **Controller test gap**

**Key principle:** Match tests to where problems exist. A single bug might expose gaps at multiple layers - report them all if they&amp;#39;re legitimate gaps.

_[Full production version includes detailed output templates, pattern recognition rules, and comprehensive diagnostic procedures]_
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Test Writer Agent&lt;/h3&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;---
name: test-writer
description: TDD specialist who writes and refines tests. Pass the full requirement, target test files, patterns found, and any validator errors. Invoke when needing tests written or fixed.
tools: Read, Write, Edit, MultiEdit, Grep, Glob
model: inherit
---

You are a TDD specialist responsible for creating and modifying tests ONLY.

## Specialization

This agent creates all test types:

- **Model tests** - validations, scopes, methods
- **Controller tests** - HTTP responses, redirects
- **Job tests** - background job behavior
- **Command tests** - business logic testing
- **Query tests** - database query logic
- **System tests** - UI interactions, browser workflows

## Scope

**I write:**

- New test files (all types)
- Failing tests for TDD (Red phase)
- Test modifications based on validator feedback
- Test syntax fixes

**I don&amp;#39;t:**

- Write production code
- Run tests
- Make implementation decisions

## Critical Rule: Test the Actual Bug Scenario

When creating tests for bug fixes, choose the right approach:

**Missing Coverage Tests (Preferred)**
Test what SHOULD happen - the intended behavior that would have prevented the bug.

**Bug Reproduction Tests (When Needed)**
Test the exact failure when you need to prove the bug exists.

_[Full production version includes detailed troubleshooting guides, output formats, and comprehensive testing patterns]_
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Implementer Agent&lt;/h3&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;---
name: implementer
description: Implementation specialist who writes production code to pass tests. Pass failing test output, all files to modify, patterns to follow, and current state. Invoke when test is failing correctly.
tools: Read, Write, Edit, MultiEdit, Grep, Glob
model: inherit
---

You are an implementation specialist responsible for writing production code to make tests pass.

## Scope

**I implement:**

- Production code to pass failing tests
- Minimal code for TDD (Green phase)
- Following existing patterns

**I don&amp;#39;t:**

- Write tests
- Run tests
- Add extra features beyond test requirements
- Refactor unless needed to pass test
- Create database migrations (return NEEDS_MIGRATION status)

## Process

1. **Understand failing test**

   - What behavior does it expect?
   - What assertion is failing?
   - What&amp;#39;s the minimal fix?

2. **Find patterns** (unless trivial fix)

   - Check CLAUDE.md in relevant directories
   - Find 2-3 similar implementations
   - Look for # AI: comments in existing code

3. **Write minimal code**
   - Just what&amp;#39;s needed to pass test
   - Follow patterns from existing code closely
   - Use THIS project&amp;#39;s patterns, not generic Rails patterns

_[Full production version includes detailed output formats, delegation patterns, and comprehensive implementation guidelines]_
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The Transformation&lt;/h2&gt;

&lt;p&gt;Remember that moment: &amp;ldquo;Wait, why am I doing this?&amp;rdquo;&lt;/p&gt;

&lt;p&gt;That&amp;rsquo;s when I realized I&amp;rsquo;d already built the team. Now I needed to actually use it like one. Instead of jumping back into debug mode myself, I could orchestrate specialists who were setup for these specific problems.&lt;/p&gt;

&lt;p&gt;The bug got fixed. But more importantly, I stopped being the bottleneck. When the next complex issue appeared, I knew exactly what to do: rally the team, give them context, let them work.&lt;/p&gt;

&lt;p&gt;Your pre-built orchestrators handle routine work. But when something weird comes up, you&amp;rsquo;re not stuck. You have a team of specialists ready to dive deep while you stay focused on the bigger picture.&lt;/p&gt;

&lt;p&gt;You&amp;rsquo;re not just coding anymore. You&amp;rsquo;re running an engineering organization where each specialist is made of intentional instructions.&lt;/p&gt;

&lt;p&gt;#AICodingAssistants #ClaudeCode #Automation #Productivity #SoftwareEngineering #DigitalTransformation&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How One Person Becomes a Dev Team with Claude Code</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/09/21/how-to-transform-yourself-into-a-dev-team-using-claude-codes-ai-agents.html"/>
    <id>http://www.brandoncasci.com/2025/09/21/how-to-transform-yourself-into-a-dev-team-using-claude-codes-ai-agents.html</id>
    <published>2025-09-21T16:28:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-09-21&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;How One Person Becomes a Dev Team with Claude Code&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/09/21/cover.png" alt="How One Person Becomes a Dev Team with Claude Code" /&gt;&lt;/p&gt;

&lt;p&gt;I wanted to augment my productivity. GitHub Copilot worked for single lines. Then Claude Code and Cursor promised a &amp;ldquo;pair programmer&amp;rdquo;, but out of the box they delivered an eager helper with a paintbrush in one hand and a sledgehammer in the other. I got Jackson Pollock and Gallagher rolled into one.&lt;/p&gt;

&lt;p&gt;I tried add-on orchestration frameworks for Claude Code next, hoping for a short-cut. Each one added more abstraction layers and created a familiar mess with astonishing speed. Meanwhile, I smelled the YouTube AI influencers before I saw them. Selling parlor tricks to solve staged toy problems that funnel people toward a $299 course to learn the &amp;ldquo;secrets.&amp;rdquo; No thanks.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s my truth: AI tools like Claude Code are power tools. They require investment: time to actually read the important parts of the documentation, and money to burn while learning. Without proper boundaries, they&amp;rsquo;ll waste both your time and money.&lt;/p&gt;

&lt;p&gt;This article shows how to multiply your efforts with Claude Code by following their own documentation and best practices. Try this BEFORE reaching for third-party add-ons. It&amp;rsquo;s just a simple folder structure with carefully groomed contents that turn one person into a team.&lt;/p&gt;

&lt;h2&gt;The Problem Everyone Knows, But Few Seem To Address&lt;/h2&gt;

&lt;p&gt;You&amp;rsquo;ve been there. You ask Claude to fix a login bug. Twenty minutes later, it&amp;rsquo;s refactored your entire auth system, added three dependencies you didn&amp;rsquo;t ask for, and is halfway through writing migration guides for features that don&amp;rsquo;t exist yet.&lt;/p&gt;

&lt;p&gt;Without boundaries, Claude fabricates its own priorities. You asked for a bug fix, you got a redesign, and probably a broken one.&lt;/p&gt;

&lt;p&gt;You know the pain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex tasks drift off course without persistent focus on the goal&lt;/li&gt;
&lt;li&gt;Same prompt, different day, wildly different approach&lt;/li&gt;
&lt;li&gt;Linear instructions fail because AI wanders into tangents&lt;/li&gt;
&lt;li&gt;The assistant &amp;ldquo;helps&amp;rdquo; by adding improvements you&amp;rsquo;ll spend hours removing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution isn&amp;rsquo;t just better prompts with clear intent. It&amp;rsquo;s orchestration.&lt;/p&gt;

&lt;h2&gt;The Architecture: Just Folders and Files&lt;/h2&gt;

&lt;p&gt;Forget complex frameworks. This entire system is folders and markdown files that Claude Code already knows how to read:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;.claude/
├── agents/
│   ├── dev-team/               # Your specialized team members
│   │   ├── analyst.md          # Requirements &amp;amp; architecture
│   │   ├── test-writer.md      # Writes comprehensive tests
│   │   ├── implementer.md      # Writes production code
│   │   ├── validator.md        # Runs tests &amp;amp; linting
│   │   └── reviewer.md         # Pattern compliance
│   │
│   ├── check-quality.md        # Independent quality auditor
│   └── issue-diagnosis.md      # Bug root cause analysis
│
├── commands/
│   └── orchestrators/          # High-level workflows
│       ├── tdd.md              # Test-driven development flow
│       └── fix-bug.md          # Debugging workflow
│
└── protocols/                  # Shared standards
    ├── agent-output-protocol.md
    └── scratchpad-protocol.md  # State management
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&amp;rsquo;s it. No npm packages. No Python libraries. Just markdown files that tell Claude Code how to behave in specific contexts. If you want to get fancy, do that later.&lt;/p&gt;

&lt;h2&gt;The Cast of Characters: Specialized Agents&lt;/h2&gt;

&lt;p&gt;Instead of one AI assistant trying to do everything, you create specialists. Each agent has a narrow scope and explicit boundaries.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s what the &lt;code&gt;implementer.md&lt;/code&gt; agent looks like (simplified from production):&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;---
name: implementer
description: Implementation specialist who writes production code to pass tests
tools: Read, Write, Edit, MultiEdit, Grep, Glob
---

# Implementer Agent
# Note: Simplified for this article. Production version has more directives.

You write production code following established patterns and requirements.

## Your Scope

**You DO:**
- Write minimal code to make tests pass
- Follow patterns from domain-specific CLAUDE.md files
- Use existing conventions and helpers
- Keep implementation focused on requirements

**You DON&amp;#39;T:**
- Write tests (that&amp;#39;s test-writer&amp;#39;s job)
- Add features not required by tests
- Refactor unrelated code
- Make architectural decisions
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the explicit boundaries. The implementer can&amp;rsquo;t write tests. That&amp;rsquo;s someone else&amp;rsquo;s job. It can&amp;rsquo;t add features the tests don&amp;rsquo;t require. This constraint is what makes it a lot more reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My current team roster:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Analyst&lt;/strong&gt;: Breaks down requirements, identifies affected files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test-Writer&lt;/strong&gt;: Creates test coverage before implementation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementer&lt;/strong&gt;: Writes the minimal code to pass tests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validator&lt;/strong&gt;: Runs tests and linting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reviewer&lt;/strong&gt;: Checks patterns and standards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are actually more for research, marketing, UX, product advice. You can hire and fire specialists as you see fit for your own projects.&lt;/p&gt;

&lt;p&gt;Each agent can fail. That&amp;rsquo;s expected and handled by the orchestrator.&lt;/p&gt;

&lt;h2&gt;Gates: Why Non-Linear Workflows Work&lt;/h2&gt;

&lt;p&gt;Traditional AI workflows are brittle. One failure stops everything:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;START → Step 1 → Step 2 → Step 3 → END
         ↓ fail    ↓ fail   ↓ fail
        STOP      STOP      STOP
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Gate-based flows self-heal. Each gate is a checkpoint that can retry with different approaches:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;┌──────────────────────────────────────────────────────┐
│  GATE 1: Analysis                                    │
│  ├─ Try analyst agent                                │
│  ├─ Fail? → Try again with more context              │
│  └─ Pass? → Gate 2                                   │
├──────────────────────────────────────────────────────┤
│  GATE 2: Write Test                                  │
│  ├─ Simple? → Orchestrator writes it                 │
│  ├─ Complex? → Delegate to test-writer               │
│  ├─ Fail? → Loop with fixes                          │
│  └─ Pass? → Gate 3                                   │
├──────────────────────────────────────────────────────┤
│  GATE 3: Implementation                              │
│  ├─ Try implementer                                  │
│  ├─ Tests fail? → Fix and retry                      │
│  └─ Tests pass? → Gate 4                             │
└──────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each gate can loop internally until satisfied. Circuit breakers prevent infinite loops. A scratchpad file maintains state across attempts, so agents learn from previous failures.&lt;/p&gt;

&lt;p&gt;This isn&amp;rsquo;t theoretical. Here&amp;rsquo;s actual gate logic from the TDD orchestrator:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;### Gate 4: GREEN Phase (Make Tests Pass)

**Goal: Minimal implementation to make tests pass**

- Simple (under 10 lines): Implement yourself
- Complex: Delegate to implementer agent
- Run tests to verify they PASS
- If tests fail → Determine root cause:
  - Implementation incomplete → Continue
  - Test was wrong → Back to Gate 3
- Log GREEN phase completion to scratchpad
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The orchestrator decides at each gate: simple enough to do myself, or delegate to a specialist? Failed attempt? Loop with more context or try a different approach.&lt;/p&gt;

&lt;h2&gt;Domain Knowledge: The CLAUDE.md Files&lt;/h2&gt;

&lt;p&gt;Your codebase already has patterns. The CLAUDE.md files capture them:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Project Root/
├── CLAUDE.md                    # Project-wide patterns
├── app/
│   ├── models/CLAUDE.md        # Model patterns &amp;amp; anti-patterns
│   ├── controllers/CLAUDE.md   # Controller conventions
│   └── business_logic/CLAUDE.md # Command patterns
└── test/
    └── CLAUDE.md                # Testing standards
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These aren&amp;rsquo;t vague guidelines. They&amp;rsquo;re specific instructions:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# app/controllers/CLAUDE.md
# Note: Production version is 200+ lines with detailed patterns

...(earlier patterns)...

## Anti-Patterns (NEVER DO)

### Missing Authorization
# BAD - No authorization
def show
  @post = Post.find(params[:id])
end

# GOOD - Always authorize
def show
  @post = policy_scope(Post).find(params[:id])
  authorize @post
end

### Business Logic in Controllers
Complex logic belongs in Commands, not controllers.

...(more patterns below)...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When the implementer agent runs, it reads these files and follows the patterns. No more explaining your codebase conventions every time.&lt;/p&gt;

&lt;h2&gt;Example: The TDD Orchestrator in Action&lt;/h2&gt;

&lt;p&gt;Here&amp;rsquo;s what happens when you type &lt;code&gt;/orchestrators:tdd #234&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Analyst   │ →   │ Test-Writer │ →   │ Implementer │
└─────────────┘     └─────────────┘     └─────────────┘
       ↓                    ↓                    ↓
  &amp;quot;2 files,            &amp;quot;Test for         &amp;quot;Make it pass&amp;quot;
   3 methods&amp;quot;          each method&amp;quot;
                            ↓                    ↓
                      ┌─────────────┐     ┌─────────────┐
                      │  Validator  │ →   │   Reviewer  │
                      └─────────────┘     └─────────────┘
                         &amp;quot;Run tests&amp;quot;      &amp;quot;Check patterns&amp;quot;
                            ↓                    ↓
                        Tests Pass?         Clean Code?
                         No → Loop          No → Fix
                         Yes → Next         Yes → Done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;The orchestrator manages the entire flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates a scratchpad file for state management&lt;/li&gt;
&lt;li&gt;Delegates requirement analysis to the analyst&lt;/li&gt;
&lt;li&gt;Decides if tests are simple enough to write directly or need the test-writer&lt;/li&gt;
&lt;li&gt;Runs the implementer to make tests pass&lt;/li&gt;
&lt;li&gt;Validates with test runs&lt;/li&gt;
&lt;li&gt;Reviews code quality&lt;/li&gt;
&lt;li&gt;Runs full test suite to catch regressions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any step fails, it loops with more context. The scratchpad maintains history so each retry learns from previous attempts.&lt;/p&gt;

&lt;h2&gt;When This Approach Shines&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It works best when you have:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clear requirements&lt;/strong&gt; - A GitHub issue with acceptance criteria&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Existing patterns&lt;/strong&gt; - Similar code in your codebase to follow&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Well-defined CLAUDE.md files&lt;/strong&gt; - Domain knowledge documented&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scoped work&lt;/strong&gt; - Features that fit in a few files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repetitive patterns&lt;/strong&gt; - CRUD operations, API endpoints, test suites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don&amp;rsquo;t use this for greenfield architecture decisions or exploratory coding. Use it to multiply your output on well-defined work. Plan first.&lt;/p&gt;

&lt;h2&gt;Limitations and Common Failures&lt;/h2&gt;

&lt;h3&gt;Things to Watch&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Context window usage&lt;/strong&gt; - The scratchpad grows with each gate attempt. Complex tasks could hit limits, but you could mitigate that with further task decomposition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API costs&lt;/strong&gt; - Multiple agent calls add up in terms of tokens. Something to consider if you per per request or token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture decisions&lt;/strong&gt; - The worker agents follow patterns, they don&amp;rsquo;t create them. Design first, then delegate. With that being said, you can automate quite a bit of planning in preceding workflows.&lt;/p&gt;

&lt;h3&gt;Common Failure Modes&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Analyst gets lost in large codebases.&lt;/strong&gt; Solution: Add specific context files file paths to the prompt. Ask your agens if it needs addtional documentation to avoid future confusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test-writer creates tests that pass without implementation.&lt;/strong&gt; The validator gate catches this and retries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementer adds unnecessary complexity.&lt;/strong&gt; Adjust the contraints to cut down on iterations with the reviewer agent. Thoguh the reviewer agent, and broader check-quality agent, will catch issues and send them back for simplification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Same error keeps repeating.&lt;/strong&gt; Circuit breaker activates after 3 attempts, escalates to you.&lt;/p&gt;

&lt;h2&gt;Implementation Lessons&lt;/h2&gt;

&lt;h3&gt;What Works&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Small, focused agents.&lt;/strong&gt; Be only as verbose as you need to be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit gates&lt;/strong&gt; with clear success criteria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-healing loops&lt;/strong&gt; that retry with context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scratchpad files&lt;/strong&gt; that maintain state across attempts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain-specific patterns&lt;/strong&gt; that encode your conventions.&lt;/p&gt;

&lt;h3&gt;What Doesn&amp;rsquo;t&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;One agent to rule them all.&lt;/strong&gt; Specialist agents are more reliable, at the cost of speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linear workflows.&lt;/strong&gt; Gates and loops handle real-world complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vague success criteria.&lt;/strong&gt; &amp;ldquo;Make it work&amp;rdquo; isn&amp;rsquo;t enough, be explicit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fighting the helpful nature.&lt;/strong&gt; Use boundaries, and again, clear instructional prompts.&lt;/p&gt;

&lt;h3&gt;Next Level: Hooks and Extensions&lt;/h3&gt;

&lt;p&gt;Once the basic system works, you can add:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;┌──────────────────────────────────────────────────────┐
│  Claude Code Hooks:                                  │
│  • pre-tool-hook: Validate before edits              │
│  • post-tool-hook: Auto-run tests after changes      │
│  • submit-hook: Block if tests failing               │
├──────────────────────────────────────────────────────┤
│  External Integrations:                              │
│  • CI/CD triggers (run orchestrators on PR)          │
│  • Custom validation scripts                         │
│  • Domain-specific gates                             │
└──────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But start simple. Get the agents working first.&lt;/p&gt;

&lt;h2&gt;Key Diagrams for the Article&lt;/h2&gt;

&lt;p&gt;The full system architecture:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│                     DEVELOPER (You)                         │
└──────────────────────┬──────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────────┐
│                  ORCHESTRATOR COMMAND                       │
│              (/orchestrators:tdd #234)                      │
└──────────────────────┬──────────────────────────────────────┘
                       ↓
        ┌──────────────┴──────────────┐
        ↓                             ↓
┌───────────────┐            ┌────────────────┐
│     GATES     │            │   SCRATCHPAD   │
│  (Checkpoints)│←──────────→│ (State Memory) │
└───────┬───────┘            └────────────────┘
        ↓
┌───────────────────────────────────────────┐
│            SPECIALIZED AGENTS             │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐      │
│  │Analyst  │ │T-Writer │ │Implement│      │
│  └─────────┘ └─────────┘ └─────────┘      │
│  ┌─────────┐ ┌─────────┐                  │
│  │Validator│ │Reviewer │                  │
│  └─────────┘ └─────────┘                  │
└───────────────┬───────────────────────────┘
                ↓
┌───────────────────────────────────────────┐
│         DOMAIN KNOWLEDGE (CLAUDE.md)      │
│   • Project patterns                      │
│   • Anti-patterns to avoid                │
│   • Testing standards                     │
│   • Code conventions                      │
└───────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Example Files (Simplified from Production)&lt;/h2&gt;

&lt;p&gt;These are reduced examples extracted from a working production system. The real ones are more verbose with additional error handling, but these show the core patterns:&lt;/p&gt;

&lt;h3&gt;Example Files&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Project Structure Files&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/commands/orchestrators/tdd.md"&gt;&lt;code&gt;.claude/commands/orchestrators/tdd.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;TDD orchestrator with gate logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/commands/orchestrators/fix-bug.md"&gt;&lt;code&gt;.claude/commands/orchestrators/fix-bug.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Bug-fix workflow pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Agent Definitions&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/agents/dev-team/analyst.md"&gt;&lt;code&gt;.claude/agents/dev-team/analyst.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Requirements analyzer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/agents/dev-team/test-writer.md"&gt;&lt;code&gt;.claude/agents/dev-team/test-writer.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Test creation specialist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/agents/dev-team/implementer.md"&gt;&lt;code&gt;.claude/agents/dev-team/implementer.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Production code writer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/agents/dev-team/validator.md"&gt;&lt;code&gt;.claude/agents/dev-team/validator.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Test runner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/agents/dev-team/reviewer.md"&gt;&lt;code&gt;.claude/agents/dev-team/reviewer.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Code quality checker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/agents/check-quality.md"&gt;&lt;code&gt;.claude/agents/check-quality.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Independent quality auditor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/agents/issue-diagnosis.md"&gt;&lt;code&gt;.claude/agents/issue-diagnosis.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Root cause analyzer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Protocols &amp;amp; Standards&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/protocols/agent-output-protocol.md"&gt;&lt;code&gt;.claude/protocols/agent-output-protocol.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Communication format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/.claude/protocols/scratchpad-protocol.md"&gt;&lt;code&gt;.claude/protocols/scratchpad-protocol.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;State management rules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Domain Knowledge&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/app/models/CLAUDE.md"&gt;&lt;code&gt;app/models/CLAUDE.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Model patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/app/controllers/CLAUDE.md"&gt;&lt;code&gt;app/controllers/CLAUDE.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Controller conventions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="example-project/test/CLAUDE.md"&gt;&lt;code&gt;test/CLAUDE.md&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Testing standards&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;em&gt;Note: Production versions include more detailed error handling, retry logic, and domain-specific rules. Start with these to understand the pattern.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Claude Code Power Features Used&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This approach leverages several documented Claude Code features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://docs.claude.com/en/docs/claude-code/sub-agents"&gt;Subagents&lt;/a&gt;&lt;/strong&gt; - The foundation of orchestration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://docs.claude.com/en/docs/claude-code/slash-commands"&gt;Custom Slash Commands&lt;/a&gt;&lt;/strong&gt; - Creating &lt;code&gt;/orchestrators:tdd&lt;/code&gt; style commands&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://docs.claude.com/en/docs/claude-code/memory"&gt;CLAUDE.md Files&lt;/a&gt;&lt;/strong&gt; - Domain knowledge injection&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://docs.claude.com/en/docs/claude-code/hooks"&gt;Hooks&lt;/a&gt;&lt;/strong&gt; - Pre/post tool validation (optional enhancements)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://docs.claude.com/en/docs/build-with-claude/prompt-engineering/claude-4-best-practices"&gt;Prompt Engineering&lt;/a&gt;&lt;/strong&gt; - XML tags, &lt;code&gt;think&lt;/code&gt;/&lt;code&gt;ultrathink&lt;/code&gt; directives for complex reasoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read these docs first. They&amp;rsquo;re the difference between fighting the tool and wielding it.&lt;/p&gt;

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

&lt;p&gt;One person with clear vision can orchestrate an entire development workflow through clear prompts, structure, and boundaries.&lt;/p&gt;

&lt;p&gt;Gates break complex work into verifiable checkpoints. Delegation to specialists solves the focus problem. The scratchpad becomes your feedback loop for continuous improvement.&lt;/p&gt;

&lt;p&gt;Stop fighting your AI assistant. Start orchestrating your AI team.&lt;/p&gt;

&lt;p&gt;Big problems become manageable when you decompose them into specialist-sized chunks. A folder structure and some markdown files can turn one developer into five who actually stay in their lane.&lt;/p&gt;

&lt;p&gt;The tools are already there in Claude Code. You just need to arrange them properly.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How We Automated GitHub Issues with Free AI</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/08/24/we-gave-our-github-issues-an-ai-assistant.html"/>
    <id>http://www.brandoncasci.com/2025/08/24/we-gave-our-github-issues-an-ai-assistant.html</id>
    <published>2025-08-24T05:00:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-08-24&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;How We Automated GitHub Issues with Free AI&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/08/24/cover.png" alt="We Gave Our GitHub Issues an AI Assistant" /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Building on the foundation:&lt;/strong&gt; This artist is an extention to &lt;a href="/2025/07/30/from-chaos-to-control-teaching-claude-code-consistency.html"&gt;teaching Claude Code to be consistent&lt;/a&gt; and &lt;a href="/2025/08/04/turn-your-best-ai-prompts-into-reusable-commands.html"&gt;turning prompts into reusable commands&lt;/a&gt;. If you&amp;rsquo;re new to CLAUDE.md files, start there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We&amp;rsquo;re a small team that runs everything through GitHub Issues. I&amp;rsquo;d already invested in Claude Code Pro and documented our patterns with CLAUDE.md files. That setup was working great for complex development.&lt;/p&gt;

&lt;p&gt;But I kept seeing routing simple tasks pile up - the kind that take 5 minutes to fix but 30 minutes of context switching. I wanted to delegate these without adding another subscription.&lt;/p&gt;

&lt;p&gt;So I setup a two-tier system: Claude handles the complex work, and free Gemini takes the simple stuff. Both read from the same CLAUDE.md files, so the code stays consistent.&lt;/p&gt;

&lt;p&gt;Now when someone reports a small change or bug, we comment &lt;code&gt;@gemini-cli fix this&lt;/code&gt; and get back to work. Gemini creates the branch, fixes the issue, runs tests, and opens a PR.&lt;/p&gt;

&lt;h2&gt;Why This Made Sense&lt;/h2&gt;

&lt;h3&gt;Simple Tasks Are Perfect for Automation&lt;/h3&gt;

&lt;p&gt;Simple tasks usually have a well-defined scope, and if not, then they can be easier to define. &amp;ldquo;Change button color to &amp;hellip;&amp;rdquo; or &amp;ldquo;Update docs for endpoint&amp;rdquo; - these have clear boundaries. They&amp;rsquo;re low risk, easy to review, easy to revert if needed. Clear success criteria too - tests pass, issue closed.&lt;/p&gt;

&lt;p&gt;These tasks add up to real time over weeks. And since we already documented our patterns in CLAUDE.md files, they apply directly.&lt;/p&gt;

&lt;h3&gt;Building on What We Had&lt;/h3&gt;

&lt;p&gt;Claude Code Pro was already handling complex development. CLAUDE.md files had our patterns documented and proven. GitHub Issues provided a natural queue for work items. The next logical step was to automate the automatable.&lt;/p&gt;

&lt;h3&gt;How We Split the Work&lt;/h3&gt;

&lt;p&gt;Claude Code handles complex features, pair programming, architecture work. Gemini (free) takes simple fixes, test updates, documentation, routine changes. Both use the same patterns from CLAUDE.md, so we get consistent code regardless of which AI writes it. &lt;/p&gt;

&lt;p&gt;Cost structure stays at $199/month total - no additional subscriptions. Althought you might have need to pay for some requests depending on how frequently you invoke the action.&lt;/p&gt;

&lt;h2&gt;The Solution: Gemini CLI + Domain Context&lt;/h2&gt;

&lt;h3&gt;Why Gemini?&lt;/h3&gt;

&lt;p&gt;The free tier covers a lot of ground - we haven&amp;rsquo;t hit limits yet. There&amp;rsquo;s a GitHub Action for Gemini CLI that Google maintains. It handles structured tasks well and can execute shell commands reliably.&lt;/p&gt;

&lt;h3&gt;The Architecture&lt;/h3&gt;

&lt;pre&gt;&lt;code class="yaml"&gt;# Two-tier AI system:
Claude Code Pro → Complex development, architecture decisions
Gemini CLI → PR automation, routine fixes
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Initializing Gemini for the Project&lt;/h3&gt;

&lt;p&gt;Ran &lt;code&gt;gemini /init&lt;/code&gt; to create a root GEMINI.md, then curated it to fit the project. After that, I added companion files alongside each CLAUDE.md:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# app/models/GEMINI.md

# Model Patterns

Read this file for rules and patterns.

@./CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Simple reference - no duplication. Now the project structure looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CLAUDE.md
GEMINI.md
app/business_logic/CLAUDE.md
app/business_logic/GEMINI.md
app/controllers/CLAUDE.md
app/controllers/GEMINI.md
app/models/CLAUDE.md
app/models/GEMINI.md
test/CLAUDE.md
test/GEMINI.md
# ... and so on
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Both AIs read the same patterns. Future refactor might extract to AGENT.md, but this works now.&lt;/p&gt;

&lt;h3&gt;GitHub Actions Setup&lt;/h3&gt;

&lt;p&gt;Ran &lt;code&gt;gemini /setup-github&lt;/code&gt; to generate the workflows in &lt;code&gt;.github/&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created issue comment handler&lt;/li&gt;
&lt;li&gt;Created PR comment handler&lt;/li&gt;
&lt;li&gt;Set up Gemini CLI integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Custom Gemini Commands&lt;/h3&gt;

&lt;p&gt;Created &lt;code&gt;.gemini/commands/implement.toml&lt;/code&gt; for TDD workflow (similar to how I &lt;a href="/2025/08/04/turn-your-best-ai-prompts-into-reusable-commands.html"&gt;create custom Claude commands&lt;/a&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code class="toml"&gt;name = &amp;quot;implement&amp;quot;
description = &amp;quot;Implements a feature or fix according to project patterns&amp;quot;
prompt = &amp;quot;&amp;quot;&amp;quot;
You are an expert Rails developer implementing a change.

Follow strict TDD process while adhering to patterns in CLAUDE.md files.

Your Process:
1. Analyze the request
2. Consult CLAUDE.md files for patterns
3. Write a failing test
4. Implement minimal code to pass
5. Run tests and linter (bin/rubocop -A)
&amp;quot;&amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can trigger from issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@gemini-cli /implement&lt;/code&gt; - uses issue description&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@gemini-cli /implement change tab order&lt;/code&gt; - adds extra context&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@gemini-cli default the active scope to nil&lt;/code&gt; - direct instruction&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The Implementation Journey&lt;/h2&gt;

&lt;h3&gt;Phase 1: Initial Setup&lt;/h3&gt;

&lt;pre&gt;&lt;code class="yaml"&gt;# Basic integration
- name: Run Gemini
  uses: google-github-actions/run-gemini-cli@v0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This worked for basic commands, but failed when Gemini tried to run Rails commands. It couldn&amp;rsquo;t find Ruby or any of the gems - the environment wasn&amp;rsquo;t persisting between commands.&lt;/p&gt;

&lt;h3&gt;Phase 2: Solving the Environment Issue&lt;/h3&gt;

&lt;p&gt;The discovery: Gemini runs commands via &lt;code&gt;bash -c&lt;/code&gt; which creates isolated shells. GitHub Actions environment variables don&amp;rsquo;t persist between commands. But Gemini reads .env files in the working directory.&lt;/p&gt;

&lt;p&gt;Added composite action to gemini-cli.yml after checkout:&lt;/p&gt;

&lt;pre&gt;&lt;code class="yaml"&gt;# .github/gemini-cli.yml
# ...
# I put this after the branch checkout.
# This is a github composite action that contains all the rails app setup steps.
# This composite is used by ci.yml too.
- name: Setup Rails environment
  uses: ./.github/actions/setup-rails-env
  with:
    setup-database: &amp;quot;true&amp;quot;

# For injecting ENV variables into Gemini CLI - is there a better way?
- name: Setup environment for Gemini CLI
  run: |
    touch $GITHUB_WORKSPACE/.env
    cat $GITHUB_WORKSPACE/.github/actions-env/.env &amp;gt;&amp;gt; $GITHUB_WORKSPACE/.env
# ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This ensures Gemini has everything it needs (Ruby, gems, database) before executing commands.&lt;/p&gt;

&lt;p&gt;The static environment file:&lt;/p&gt;

&lt;pre&gt;&lt;code class="bash"&gt;# .github/actions-env/.env
# Gemini CLI Ruby/Rails Environment
# These paths must be hardcoded because Gemini runs commands via bash -c
# which creates isolated shells that don&amp;#39;t inherit the GitHub Actions environment
PATH=/opt/hostedtoolcache/Ruby/3.3.8/x64/bin:/home/runner/work/commerce_app/commerce_app/vendor/bundle/ruby/3.3.0/bin:$PATH
GEM_HOME=/opt/hostedtoolcache/Ruby/3.3.8/x64/lib/ruby/gems/3.3.0
GEM_PATH=/opt/hostedtoolcache/Ruby/3.3.8/x64/lib/ruby/gems/3.3.0
BUNDLE_PATH=/home/runner/work/commerce_app/commerce_app/vendor/bundle
BUNDLE_APP_CONFIG=/home/runner/work/commerce_app/commerce_app/.bundle
BUNDLE_GEMFILE=/home/runner/work/commerce_app/commerce_app/Gemfile
BUNDLE_DEPLOYMENT=false
BUNDLE_WITHOUT=production

# TODO: Make Ruby version dynamic
# Is there a better solution? Let me know if you find one.
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;Look at your last 10 closed issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which were trivial but took an hour?&lt;/li&gt;
&lt;li&gt;Which cost you focus on bigger problems?&lt;/li&gt;
&lt;li&gt;Which were literally just &amp;ldquo;change this text&amp;rdquo; or &amp;ldquo;fix this color&amp;rdquo;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are your automation targets.&lt;/p&gt;

&lt;h3&gt;The Minimum Viable Automation&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Set up Gemini CLI in ONE workflow&lt;/li&gt;
&lt;li&gt;Handle ONE type of issue (e.g., &amp;ldquo;failing tests&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;Use for one week&lt;/li&gt;
&lt;li&gt;Measure: Did it save more time than it took to set up?&lt;/li&gt;
&lt;li&gt;If yes, add another issue type&lt;/li&gt;
&lt;li&gt;Iterate

&lt;ul&gt;
&lt;li&gt;Be mindful of context files. They need curation.&lt;/li&gt;
&lt;li&gt;Experiment with prompting, but don&amp;rsquo;t get too complex&lt;/li&gt;
&lt;li&gt;Experiment with other models&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Why This Works for Small Teams&lt;/h3&gt;

&lt;p&gt;You can move fast. You can be pragmatic. You already have context. Small products are easier for AI to understand. Your patterns are probably consistent if you have a small footprint, and if not, make them consistent. Your issues are well-defined.&lt;/p&gt;

&lt;p&gt;Everyone knows the frustration of losing an hour to a five-minute task because of context switching.&lt;/p&gt;

&lt;p&gt;This isn&amp;rsquo;t about being clever with AI. It&amp;rsquo;s about being practical with time.&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s not revolutionary. It&amp;rsquo;s not magical. It&amp;rsquo;s just a way to work more efficiently, so you can deliver value in less time.&lt;/p&gt;

&lt;h2&gt;See It in Action&lt;/h2&gt;

&lt;h3&gt;Triggering Gemini from an Issue&lt;/h3&gt;

&lt;p&gt;&lt;img src="/2025/08/24/01-kickoff.png" alt="Kickoff Gemini from GitHub Issue" /&gt;&lt;/p&gt;

&lt;p&gt;Just comment &lt;code&gt;@gemini-cli&lt;/code&gt; on any issue. That&amp;rsquo;s it.&lt;/p&gt;

&lt;h3&gt;The Initial Response&lt;/h3&gt;

&lt;p&gt;&lt;img src="/2025/08/24/02-response.png" alt="Gemini's Response" /&gt;&lt;/p&gt;

&lt;p&gt;Gemini acknowledges the request and starts working.&lt;/p&gt;

&lt;h3&gt;After the Environment Fix&lt;/h3&gt;

&lt;p&gt;&lt;img src="/2025/08/24/03-response-after-refactor-post-env-fix.png" alt="Working Response After Environment Setup" /&gt;&lt;/p&gt;

&lt;p&gt;Once the environment is configured correctly, Gemini executes commands, runs tests, and creates the PR.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Turn Your Best AI Prompts into Reusable Commands</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/08/04/turn-your-best-ai-prompts-into-reusable-commands.html"/>
    <id>http://www.brandoncasci.com/2025/08/04/turn-your-best-ai-prompts-into-reusable-commands.html</id>
    <published>2025-08-04T05:00:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-08-04&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;Turn Your Best AI Prompts into Reusable Commands&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/08/04/cover.png" alt="Turn Your Best AI Prompts into Reusable Commands" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/2025/07/30/from-chaos-to-control-teaching-claude-code-consistency.html"&gt;You&amp;rsquo;ve documented your patterns with CLAUDE.md files&lt;/a&gt;. Claude checks them automatically. Your code is consistent now.&lt;/p&gt;

&lt;p&gt;But you&amp;rsquo;re still typing the same TDD instructions every time you implement something. What if that perfect prompt could be a simple command? What if we could express our intentions for work this way?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/implement #45
/implement Add password reset with email confirmation  
/implement docs/features/search.md
/implement Fix users can&amp;#39;t remove items from cart
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each command captures your workflow once, then applies it consistently every time.&lt;/p&gt;

&lt;h2&gt;The Problem: Great Documentation, Repetitive Instructions&lt;/h2&gt;

&lt;p&gt;You&amp;rsquo;ve done the hard work. Root CLAUDE.md with project conventions. Specialized CLAUDE.md files in each directory. Clear patterns and anti-patterns documented. Claude checks these files automatically.&lt;/p&gt;

&lt;p&gt;But you&amp;rsquo;re still repeating yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Follow TDD - write the test first&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Check the patterns in app/models/CLAUDE.md&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Find similar examples with grep&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same workflow instructions, every single time.&lt;/p&gt;

&lt;p&gt;Documentation tells Claude &lt;strong&gt;what&lt;/strong&gt; your patterns are. But not &lt;strong&gt;how&lt;/strong&gt; to apply them consistently. You need to capture your workflow, not just your patterns.&lt;/p&gt;

&lt;h2&gt;The Solution: Custom Slash Commands&lt;/h2&gt;

&lt;p&gt;From &lt;a href="https://docs.anthropic.com/en/docs/claude-code/slash-commands"&gt;Anthropic&amp;rsquo;s docs&lt;/a&gt;, custom commands are prompt templates stored as Markdown files. They&amp;rsquo;re for repeated workflows you use often, stored in &lt;code&gt;.claude/commands/&lt;/code&gt;, and can include &lt;code&gt;$ARGUMENTS&lt;/code&gt; for dynamic input.&lt;/p&gt;

&lt;p&gt;Think of it this way: CLAUDE.md files define what your patterns are. Commands define how to apply them. Together, they make consistency automatic.&lt;/p&gt;

&lt;h2&gt;The Complete /implement Command&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;.claude/commands/implement.md&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;---
description: Implement features from issues, PRDs, or direct requests
argument-hint: &amp;quot;#123&amp;quot; or &amp;quot;path/to/plan.md&amp;quot; or &amp;quot;feature description&amp;quot;
allowed-tools: Read, Write, Edit, MultiEdit, Bash, TodoWrite, Grep
---

# Implement $ARGUMENTS

## Step 1: Understand the Request

Look at $ARGUMENTS and figure out what the user wants:
- If it&amp;#39;s a number with #, it&amp;#39;s a GitHub issue - use `gh issue view` to read it
- If it&amp;#39;s a file path, read that file
- Otherwise, it&amp;#39;s a direct request - use it as-is

## Step 2: Follow Our TDD Process

Follow the patterns documented in CLAUDE.md files:

1. Write failing test first
2. Implement just enough to pass
3. Verify test passes

Always check relevant CLAUDE.md files for patterns and examples.
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Breaking It Down&lt;/h3&gt;

&lt;p&gt;The frontmatter controls how your command appears and behaves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;description&lt;/strong&gt;: What appears in the command menu when you type &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;argument-hint&lt;/strong&gt;: Shows users the expected input format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;allowed-tools&lt;/strong&gt;: Grants permissions the command needs to work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 1 handles input intelligently. &lt;code&gt;$ARGUMENTS&lt;/code&gt; is Anthropic&amp;rsquo;s built-in way to pass parameters. Simple conditional instructions guide different behaviors. No complex parsing needed.&lt;/p&gt;

&lt;p&gt;Step 2 reinforces your workflow. It tells Claude to check CLAUDE.md (which it already does), provides clear sequential steps, and keeps the workflow consistent every time.&lt;/p&gt;

&lt;h2&gt;The Magic of $ARGUMENTS&lt;/h2&gt;

&lt;p&gt;One command, infinite flexibility.&lt;/p&gt;

&lt;h3&gt;GitHub Issue&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement #45
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;$ARGUMENTS becomes &amp;ldquo;#45&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Command fetches and implements the issue&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;PRD File&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement docs/prd/search.md
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;$ARGUMENTS becomes &amp;ldquo;docs/prd/search.md&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Command reads and implements from PRD&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Direct Request&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement Add user avatars
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;$ARGUMENTS becomes &amp;ldquo;Add user avatars&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Command implements the feature&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;How the Command Works in Practice&lt;/h2&gt;

&lt;h3&gt;&lt;strong&gt;Simple model addition&lt;/strong&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement Add published_at timestamp to Article
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The command will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &amp;ldquo;Add published_at timestamp to Article&amp;rdquo; as the task&lt;/li&gt;
&lt;li&gt;Check CLAUDE.md files automatically&lt;/li&gt;
&lt;li&gt;Write test for published_at behavior first&lt;/li&gt;
&lt;li&gt;Create migration and update model&lt;/li&gt;
&lt;li&gt;Verify test passes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;&lt;strong&gt;Bug fix from issue&lt;/strong&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement #45
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The command will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;gh issue view 45&lt;/code&gt; to get details&lt;/li&gt;
&lt;li&gt;Read: &amp;ldquo;Users can&amp;rsquo;t remove items from cart&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Check CLAUDE.md files for patterns&lt;/li&gt;
&lt;li&gt;Write failing test first&lt;/li&gt;
&lt;li&gt;Implement fix&lt;/li&gt;
&lt;li&gt;Verify test passes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;&lt;strong&gt;Complete feature from PRD&lt;/strong&gt;&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement docs/features/wishlists.md
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The command will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the PRD file for requirements&lt;/li&gt;
&lt;li&gt;Check CLAUDE.md files throughout&lt;/li&gt;
&lt;li&gt;Implement with TDD for each component&lt;/li&gt;
&lt;li&gt;Follow all your documented patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Making It Natural&lt;/h2&gt;

&lt;p&gt;Add to your root CLAUDE.md:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;## Natural Language Implementation

When users say:

- &amp;quot;Create X&amp;quot; → Treat as `/implement X`
- &amp;quot;Add Y&amp;quot; → Treat as `/implement Y`
- &amp;quot;Fix Z&amp;quot; → Treat as `/implement Z`

The /implement command will handle the TDD workflow.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now &amp;ldquo;Create a User model&amp;rdquo; triggers your complete workflow without typing &lt;code&gt;/implement&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Refining Your Workflow&lt;/h2&gt;

&lt;p&gt;Your initial commands and documentation won&amp;rsquo;t be perfect. That&amp;rsquo;s expected.&lt;/p&gt;

&lt;p&gt;When you notice patterns you don&amp;rsquo;t like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/analyze-workflow
&lt;/code&gt;&lt;/pre&gt;

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

&lt;ul&gt;
&lt;li&gt;Checks recent changes against your patterns&lt;/li&gt;
&lt;li&gt;Identifies what went wrong&lt;/li&gt;
&lt;li&gt;Suggests improvements to commands and CLAUDE.md&lt;/li&gt;
&lt;li&gt;Asks before applying changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Example Refinement Session&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement Add notification system with audit trail
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Result: Test file with 281 lines, nested describes, business logic in model.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/analyze-workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;## Pattern Violations Found:

1. **Model Test Anti-Patterns**:
   - ❌ Nested validation describes
   - ❌ Test file too long (281 lines)
   - ❌ Using create! instead of fixtures

### Root Cause Analysis:
- No line limit guidance in test/CLAUDE.md
- Examples don&amp;#39;t show flat validation structure

### Suggested Improvements:
1. Update test/CLAUDE.md:
   - Add &amp;quot;50-100 line target&amp;quot; for model tests
   - Show flat validation examples

Would you like me to apply these improvements? (yes/no)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The iteration process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Run your command&lt;/strong&gt; - See what it produces&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identify issues&lt;/strong&gt; - What patterns were violated?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analyze root cause&lt;/strong&gt; - Why did it happen?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update guidance&lt;/strong&gt; - Improve commands or CLAUDE.md&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test again&lt;/strong&gt; - Verify improvements work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your patterns evolve with your codebase. Team preferences emerge over time. Edge cases reveal missing guidance. Continuous improvement beats perfect first attempts.&lt;/p&gt;

&lt;h2&gt;Why This Works&lt;/h2&gt;

&lt;p&gt;Commands reference your existing patterns. Documentation ensures consistency. Commands stay focused on workflow.&lt;/p&gt;

&lt;p&gt;Your command provides:
&lt;code&gt;markdown
&amp;quot;Follow the patterns documented in CLAUDE.md files&amp;quot;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This simple instruction builds on Claude&amp;rsquo;s existing behavior. No complex conditional logic. Works for any implementation type.&lt;/p&gt;

&lt;h2&gt;Getting Started&lt;/h2&gt;

&lt;h3&gt;Step 1: Create the Command&lt;/h3&gt;

&lt;pre&gt;&lt;code class="bash"&gt;mkdir -p .claude/commands
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Copy the complete command from above into &lt;code&gt;.claude/commands/implement.md&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;Step 2: Try It&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;/implement Add Product model with name and price
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Watch as it follows your instructions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks patterns&lt;/li&gt;
&lt;li&gt;Writes tests first&lt;/li&gt;
&lt;li&gt;Implements correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Step 3: Refine It&lt;/h3&gt;

&lt;p&gt;When you see output you don&amp;rsquo;t like:
&lt;code&gt;
/analyze-workflow
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Apply suggested improvements and try again.&lt;/p&gt;

&lt;h2&gt;Results and Impact&lt;/h2&gt;

&lt;p&gt;Before commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5-10 minutes explaining each time&lt;/li&gt;
&lt;li&gt;Sometimes forget TDD&lt;/li&gt;
&lt;li&gt;Inconsistent implementations&lt;/li&gt;
&lt;li&gt;Mental fatigue from repetition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent implementations&lt;/li&gt;
&lt;li&gt;TDD every single time&lt;/li&gt;
&lt;li&gt;Follows your patterns&lt;/li&gt;
&lt;li&gt;Mental energy for real problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once &lt;code&gt;/implement&lt;/code&gt; works, create more:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;/debug - Systematic debugging process
/refactor - Apply patterns to existing code  
/review - Check against CLAUDE.md patterns
/test - Add tests to existing code
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each leverages your CLAUDE.md foundation.&lt;/p&gt;

&lt;h2&gt;Key Takeaways&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commands are saved prompts&lt;/strong&gt; - Anthropic designed them as reusable templates. $ARGUMENTS provides flexibility. Natural language instructions work best.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They complete your documentation&lt;/strong&gt; - CLAUDE.md = the patterns. Commands = the workflow. Together = consistency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple instructions work&lt;/strong&gt; - Clear steps get consistent results. Build on Claude&amp;rsquo;s existing behaviors. No complex logic needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iteration is expected&lt;/strong&gt; - Start simple. Refine based on output. Use /analyze-workflow to improve systematically.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Try It Yourself&lt;/h2&gt;

&lt;p&gt;The setup is simple:&lt;/p&gt;

&lt;pre&gt;&lt;code class="bash"&gt;mkdir -p .claude/commands
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then copy the &lt;code&gt;/implement&lt;/code&gt; command from above into &lt;code&gt;.claude/commands/implement.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That&amp;rsquo;s it. Next time you need to implement something, just type &lt;code&gt;/implement&lt;/code&gt; followed by what you want.&lt;/p&gt;

&lt;p&gt;Remember: your first command won&amp;rsquo;t be perfect. That&amp;rsquo;s fine. Use &lt;code&gt;/analyze-workflow&lt;/code&gt; when you see output you don&amp;rsquo;t like, and refine from there.&lt;/p&gt;

&lt;p&gt;The real power comes from combining this with your CLAUDE.md files. Documentation defines the patterns. Commands apply them. No more typing the same instructions every time.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>From Chaos to Control: How I Taught Claude Code to be Consistent and Accurate</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/07/30/from-chaos-to-control-teaching-claude-code-consistency.html"/>
    <id>http://www.brandoncasci.com/2025/07/30/from-chaos-to-control-teaching-claude-code-consistency.html</id>
    <published>2025-07-30T05:00:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-07-30&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;From Chaos to Control: How I Taught Claude Code to be Consistent and Accurate&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/07/30/cover.png" alt="Teaching Claude Code Consistency" /&gt;&lt;/p&gt;

&lt;p&gt;I started using Claude Code for Rails development with the same excitement everyone has when they first discover AI coding assistants. The promise was incredible: describe what you want, get working code instantly.&lt;/p&gt;

&lt;p&gt;Then reality hit. Suboptimal code. Inconsistent patterns. Going off on tangents when I asked for simple changes. Every implementation looked different. Business logic crammed into models. Tests that just tested Rails itself.&lt;/p&gt;

&lt;p&gt;The solution wasn&amp;rsquo;t to abandon AI - it was to train it properly.&lt;/p&gt;

&lt;h2&gt;The Problems I Faced&lt;/h2&gt;

&lt;h3&gt;1. Inconsistent Code Style&lt;/h3&gt;

&lt;p&gt;Every time I asked Claude to create a new model or controller, it was like working with a different developer. No adherence to our conventions. Mixed patterns within the same codebase. One file used service objects, the next stuffed everything into fat models.&lt;/p&gt;

&lt;h3&gt;2. Reinventing the Wheel&lt;/h3&gt;

&lt;p&gt;Claude didn&amp;rsquo;t know our existing patterns. Ask for user authentication? It would create a whole new system instead of using our established &lt;code&gt;AuthenticationService&lt;/code&gt;. Need error handling? New approach every time, ignoring our standardized error classes.&lt;/p&gt;

&lt;h3&gt;3. Suboptimal Patterns &amp;amp; Lost Focus&lt;/h3&gt;

&lt;p&gt;The worst part was the Rails anti-patterns. Business logic in models. Fat controllers doing five different things. Database queries in views. And don&amp;rsquo;t get me started on the tangents - I&amp;rsquo;d ask for a simple model change and get a complete architectural redesign of unrelated parts.&lt;/p&gt;

&lt;h2&gt;The Solution: CLAUDE.md Files&lt;/h2&gt;

&lt;h3&gt;Quick Win Example&lt;/h3&gt;

&lt;p&gt;Before diving deep, here&amp;rsquo;s a simple CLAUDE.md that shows the concept in action:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# Models Directory Rules

Models should be thin. Only include:
- Validations
- Associations  
- Scopes
- Database-related configs

Business logic goes in app/services or app/commands.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just 7 lines that set clear boundaries. Once Claude reads this, it knows exactly where business logic belongs.&lt;/p&gt;

&lt;p&gt;And here&amp;rsquo;s a companion CLAUDE.md for the commands directory:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# Business Logic Commands

All commands should:
- Inherit from BaseCommand
- Have single public method: call
- Be idempotent when possible (safe to run multiple times)
- Return success() or error() with clear messages
- Handle their own error cases

Keep commands focused on single responsibility unless splitting creates more complexity than it solves.
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;What They Are&lt;/h3&gt;

&lt;p&gt;CLAUDE.md files are markdown documents placed strategically throughout your codebase. They&amp;rsquo;re living documentation that AI reads before acting on any code in that directory. Think of them as executable specifications, not just guidelines.&lt;/p&gt;

&lt;h3&gt;The Structure&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;project_root/
├── CLAUDE.md          # Project overview, conventions, architecture decisions
├── app/
│   ├── business_logic/
│   │   └── CLAUDE.md  # Command pattern specs
│   ├── controllers/
│   │   └── CLAUDE.md  # Controller patterns
│   ├── models/
│   │   └── CLAUDE.md  # Thin model rules
│   └── test/
│       └── CLAUDE.md  # Testing patterns &amp;amp; anti-patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The root CLAUDE.md contains project overview, critical commands (test, build, deploy), and general intentions. When Claude works on a file, it reads all CLAUDE.md files up the directory tree, so specific coding patterns live close to the code they govern while general project context stays at the root.&lt;/p&gt;

&lt;h3&gt;Key Components&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Positive Examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Show exactly how things should be done with real code from your project:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;# GOOD: Thin model
class User &amp;lt; ApplicationRecord
  validates :email, presence: true, uniqueness: true

  has_many :posts
  belongs_to :organization

  scope :active, -&amp;gt; { where(active: true) }
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;2. Anti-Patterns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Be explicit about what NOT to do:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;# NEVER DO THIS
class User &amp;lt; ApplicationRecord
  def send_welcome_email
    UserMailer.welcome(self).deliver_later
    update(welcomed: true)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;3. Context-Specific Rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When to apply patterns, when exceptions are allowed, how to handle edge cases. Your documentation, your rules.&lt;/p&gt;

&lt;h2&gt;What This Looks Like in Practice&lt;/h2&gt;

&lt;h3&gt;Without CLAUDE.md Guidelines&lt;/h3&gt;

&lt;p&gt;Here&amp;rsquo;s a typical Rails model that mixes concerns:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;class Post &amp;lt; ApplicationRecord
  belongs_to :user

  def publish!
    self.published_at = Time.current
    self.status = &amp;#39;published&amp;#39;
    save!

    NotificationMailer.post_published(self).deliver_later
    user.increment!(:published_posts_count)

    TwitterService.new.share(self) if user.twitter_connected?
    update_search_index
  end

  def archive!
    self.status = &amp;#39;archived&amp;#39;
    self.archived_at = Time.current
    save!

    remove_from_search_index
    CacheService.expire_post(self)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;With CLAUDE.md Guidelines&lt;/h3&gt;

&lt;p&gt;Following the thin model pattern from our example CLAUDE.md:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;class Post &amp;lt; ApplicationRecord
  belongs_to :user

  validates :title, presence: true
  validates :content, presence: true

  scope :published, -&amp;gt; { where(status: &amp;#39;published&amp;#39;) }
  scope :draft, -&amp;gt; { where(status: &amp;#39;draft&amp;#39;) }

  # Just data, validations, and associations
end

# In app/commands/publish_post_command.rb
class PublishPostCommand &amp;lt; BaseCommand
  def initialize(post)
    @post = post
  end

  def call
    # Idempotent - safe to run multiple times
    return success(@post) if @post.status == &amp;#39;published&amp;#39;

    @post.update!(
      published_at: Time.current,
      status: &amp;#39;published&amp;#39;
    )

    # These operations only happen on first publish
    NotificationMailer.post_published(@post).deliver_later
    @post.user.increment!(:published_posts_count)

    TwitterService.new.share(@post) if @post.user.twitter_connected?
    SearchIndexer.new.index(@post)

    success(@post)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The model stays focused on data and relationships. Business logic gets its own home where it&amp;rsquo;s testable, reusable, and maintainable.&lt;/p&gt;

&lt;h2&gt;The Implementation Process&lt;/h2&gt;

&lt;h3&gt;1. Audit Existing Code&lt;/h3&gt;

&lt;p&gt;First, I had Claude analyze my codebase to identify patterns already in use. This gave me a baseline of what was working and what needed fixing.&lt;/p&gt;

&lt;h3&gt;2. Create Documentation Hierarchy&lt;/h3&gt;

&lt;p&gt;Started with a root CLAUDE.md for project overview, then added specific files for each major directory. Test documentation got the most detail - that&amp;rsquo;s where consistency matters most.&lt;/p&gt;

&lt;h3&gt;3. Make It Actionable&lt;/h3&gt;

&lt;p&gt;Set clear intentions that AI can follow. The root CLAUDE.md tells Claude WHEN to use patterns:&lt;/p&gt;

&lt;pre&gt;&lt;code class="markdown"&gt;# Project Architecture (root CLAUDE.md)

... (project overview above) ...

## Command Pattern Usage

Use commands for:
- Any operation that changes system state
- Multi-step business processes  
- Operations that send emails or trigger external services
- Any action that needs to be audited or logged

Do NOT use commands for:
- Simple data queries
- View helpers or presenters
- Configuration or settings

... (other architectural decisions below) ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;While subdirectory CLAUDE.md files show HOW to implement those patterns. This separation lets Claude understand the architecture at the right level - high-level decisions at the root, implementation details where the code lives.&lt;/p&gt;

&lt;h3&gt;4. Continuous Refinement&lt;/h3&gt;

&lt;p&gt;As you work with Claude, you&amp;rsquo;ll discover edge cases and better patterns. Update the documentation. It&amp;rsquo;s living documentation - let it evolve with your codebase.&lt;/p&gt;

&lt;h2&gt;Results and Impact&lt;/h2&gt;

&lt;h3&gt;Predictable Output&lt;/h3&gt;

&lt;p&gt;Every model looks like every other model. Every command follows the same pattern. No more surprises or &amp;ldquo;creative&amp;rdquo; implementations.&lt;/p&gt;

&lt;h3&gt;Faster Development&lt;/h3&gt;

&lt;p&gt;Claude doesn&amp;rsquo;t waste time exploring different approaches. It goes straight to the correct implementation. What used to take multiple back-and-forth corrections now works on the first try.&lt;/p&gt;

&lt;h3&gt;Knowledge Preservation&lt;/h3&gt;

&lt;p&gt;Team decisions are encoded permanently. New developers learn from AI behavior. Your patterns self-document through consistent usage.&lt;/p&gt;

&lt;h3&gt;Quality Improvements&lt;/h3&gt;

&lt;p&gt;Anti-patterns get caught before they&amp;rsquo;re written. Error handling is consistent across the entire codebase. Proper separation of concerns is maintained automatically.&lt;/p&gt;

&lt;h2&gt;The Foundation for Advanced Automation&lt;/h2&gt;

&lt;p&gt;Once your CLAUDE.md files establish consistent patterns, they become the foundation for powerful automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clear documentation = focused AI behavior&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focused behavior = reliable automation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliable automation = compound efficiency gains&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI stops guessing and starts following your patterns. Custom commands and specialized agents can build on your documented patterns with confidence.&lt;/p&gt;

&lt;p&gt;Without this foundation, automation tools just amplify chaos. With it, they become force multipliers.&lt;/p&gt;

&lt;p&gt;Get this documentation right first. Once your patterns are clear, custom Claude commands and agents become focused and reliable. Then you can start assembling workflows that actually work instead of fighting inconsistent AI behavior.&lt;/p&gt;

&lt;h2&gt;Key Takeaways&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI needs context to be effective&lt;/strong&gt; - Generic AI gives generic results. Domain-specific training is essential.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentation as code&lt;/strong&gt; - Must be maintained like code, lives with the code it documents, version controlled and reviewed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explicit &amp;gt; Implicit&lt;/strong&gt; - Show exact patterns to follow, list specific anti-patterns to avoid, leave no room for interpretation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Investment pays off&lt;/strong&gt; - Time spent documenting saves multiples later. Consistency compounds value. Team knowledge becomes permanent.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Get Started: Example Prompts&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For Rails Models:&lt;/strong&gt;
&lt;code&gt;
&amp;quot;Analyze my User and Post models and create a CLAUDE.md that enforces thin models with only validations, associations, and scopes. Business logic should go in service objects.&amp;quot;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Controllers:&lt;/strong&gt;
&lt;code&gt;
&amp;quot;Create a CLAUDE.md for my controllers directory that enforces: single responsibility actions, proper HTTP status codes, no business logic, and consistent error handling using rescue_from.&amp;quot;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Testing:&lt;/strong&gt;
&lt;code&gt;
&amp;quot;Generate a CLAUDE.md for my test directory that shows how to test behavior not implementation, avoid testing Rails itself, and use factories instead of fixtures.&amp;quot;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Service Objects:&lt;/strong&gt;
&lt;code&gt;
&amp;quot;Create a CLAUDE.md template for a service objects pattern where each service has a single public call method, returns Result objects, and handles its own errors.&amp;quot;
&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;Remember: Pragmatism Over Dogma&lt;/h3&gt;

&lt;p&gt;Pattern consistency matters more than pattern perfection. AI needs consistency more than SOLID principles. Pick patterns your team can understand and maintain.&lt;/p&gt;

&lt;p&gt;Build in flexibility:
- &amp;ldquo;Follow Command pattern EXCEPT when it adds no value&amp;rdquo;
- &amp;ldquo;Use thin models UNLESS the alternative is worse&amp;rdquo;&lt;/p&gt;

&lt;p&gt;AI mistakes will happen - make them easy to spot and correct.&lt;/p&gt;

&lt;h3&gt;Your First CLAUDE.md in 5 Minutes&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Pick one directory (start with models)&lt;/li&gt;
&lt;li&gt;Paste one of the prompts above&lt;/li&gt;
&lt;li&gt;Review what Claude generates&lt;/li&gt;
&lt;li&gt;Keep what works, fix what doesn&amp;rsquo;t&lt;/li&gt;
&lt;li&gt;Save and test with a real implementation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The perfect documentation doesn&amp;rsquo;t exist. Good enough documentation that improves your workflow does.&lt;/p&gt;

&lt;p&gt;What patterns are you struggling to keep consistent in your codebase?&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;em&gt;Ready to take your CLAUDE.md files to the next level? Learn how to &lt;a href="/2025/08/04/turn-your-best-ai-prompts-into-reusable-commands.html"&gt;turn your best AI prompts into reusable commands&lt;/a&gt; that work with your patterns for even faster, more consistent development.&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Vibe Coding Needs a Reality Check: Why AI-Generated Code Still Requires Engineering Discipline</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/02/14/vibe-coding.html"/>
    <id>http://www.brandoncasci.com/2025/02/14/vibe-coding.html</id>
    <published>2025-02-14T16:26:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-02-14&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;Vibe Coding Needs a Reality Check: Why AI-Generated Code Still Requires Engineering Discipline&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/02/14/cover.png" alt="Vibe Coding" /&gt;&lt;/p&gt;

&lt;p&gt;The term &lt;strong&gt;&amp;ldquo;vibe coding&amp;rdquo;&lt;/strong&gt; has been making the rounds, painting a picture of a laid-back, intuitive way to write software with AI. The idea? You describe what you want, accept the AI-generated code, and move on. No stress, no deep dives, just code that vibes.&lt;/p&gt;

&lt;p&gt;Except&amp;hellip; the way that they make vibe coding sound is a setup for future problems.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Vibe Coding Isn’t New, Just Newly Branded&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;You, and many others, may have been working this way before the term &lt;strong&gt;&amp;ldquo;vibe coding&amp;rdquo;&lt;/strong&gt; was popularized. AI-assisted development has been evolving for years, and developers have been using tools like Copilot, ChatGPT, and Claude to accelerate their work long before &amp;ldquo;vibe coding&amp;rdquo; became a buzzword.&lt;/p&gt;

&lt;p&gt;The difference now? &lt;strong&gt;Marketing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But just because AI can generate code faster doesn’t mean it’s automatically better. &lt;strong&gt;The reality is that AI-generated code needs oversight, structure, and engineering discipline, just like human-written code.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;The Problem: Vibe Coding Sounds Too Passive&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;The phrase &lt;strong&gt;makes it sound like coding without guardrails&lt;/strong&gt;, as if the AI is doing all the heavy lifting while developers sit back and let it happen. In reality, blindly accepting AI-generated code introduces &lt;strong&gt;major risks&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Maintainability Issues&lt;/strong&gt; → AI doesn’t always optimize for long-term readability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance Pitfalls&lt;/strong&gt; → Code may work, but is it efficient at scale?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security Vulnerabilities&lt;/strong&gt; → AI doesn’t inherently understand security best practices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compounding Mistakes&lt;/strong&gt; → Without oversight, one bad AI-generated decision can snowball into a bigger problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;strong&gt;A Better Approach: Vibe Coding with Structure&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;If you’re going to use AI as part of your development process, &lt;strong&gt;you need a structured workflow&lt;/strong&gt; that ensures quality and prevents long-term headaches.&lt;/p&gt;

&lt;h3&gt;&lt;strong&gt;Here’s what that should include:&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Code review checkpoints, with human-assisted review, at least for now.&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Testing and validation at every step&lt;/strong&gt;, not just assuming AI-generated code is correct.&lt;br&gt;
✅ &lt;strong&gt;Security checks built into the workflow&lt;/strong&gt; to catch vulnerabilities early.&lt;br&gt;
✅ **AI feedback loops to refine and improve suggestions over time.  &lt;/p&gt;

&lt;p&gt;AI is a tool, not a replacement for &lt;strong&gt;engineering judgment.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;AI Coders Need Guardrails, or They Might Cause Damage&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;One of the biggest pitfalls of unrestricted &lt;strong&gt;vibe coding&lt;/strong&gt; is that &lt;strong&gt;AI models degrade when built on top of previous AI-generated code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you accept AI-generated solutions blindly, without enforcing best practices, &lt;strong&gt;your assistant will accumulate hack upon hack.&lt;/strong&gt; Eventually, the model performance will degrade, quality will drop, and it could start to reinforce its own mistakes.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;A Slippery Analogy: Driving a Car vs. Fixing a Car&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;It’s true, you don’t need to be a mechanic to drive a car, but you need to know that you should not slam on the brakes when you hit ice.&lt;/p&gt;

&lt;p&gt;You need to get to know the rules of the road for what you are vibing on.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Final Thoughts: Vibe Coding Isn’t Bad, But It Needs Discipline&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;AI-assisted development is here to stay, and &lt;strong&gt;vibe coding&lt;/strong&gt; isn’t inherently a bad idea. But for it to work in the real world, &lt;strong&gt;it needs to be structured, tested, and reviewed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Use AI as a collaborator, not a crutch.&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Build processes to review, refine, and validate AI-generated code.&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Don’t let AI-generated shortcuts turn into long-term tech debt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI makes coding faster—but &lt;strong&gt;engineering discipline makes it sustainable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What do you think? I know some people hate code generation, others love it.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>UI Unblocker: A CustomGPT for Breaking Through UI Design Roadblocks</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/02/13/ui-unblocker-a-customgpt.html"/>
    <id>http://www.brandoncasci.com/2025/02/13/ui-unblocker-a-customgpt.html</id>
    <published>2025-02-14T02:22:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;2025-02-13&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;UI Unblocker: A CustomGPT for Breaking Through UI Design Roadblocks&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/02/13/cover.png" alt="UI Unblocker" /&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, a UI just doesn&amp;rsquo;t feel right. You tweak layouts, adjust spacing, change colors, but something is still off. That&amp;rsquo;s when I started using AI as a UX auditor—not to replace design tools, but to help me break through creative blocks.&lt;/p&gt;

&lt;p&gt;I put together &lt;strong&gt;&lt;a href="https://chatgpt.com/g/g-67ae55eeabc08191b29a7e1c529ae9d9-ui-unblocker"&gt;UI Unblocker&lt;/a&gt;&lt;/strong&gt;, a CustomGPT designed for one-off UI brainstorming tasks. It&amp;rsquo;s not a replacement for an expert, or Figma, Uizard, or any full-fledged design system. It&amp;rsquo;s just a quick way to get fresh ideas when refactoring an interface or structuring new information architecture, especially for people like me that work across a whole stack.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Why UI Design Gets Stuck&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;UI design is more than just visuals—it&amp;rsquo;s about usability, flow, and user expectations. When working on a product, it&amp;rsquo;s easy to lose perspective and fall into familiar patterns. Sometimes you just need a little nudge to get things into a better position.&lt;/p&gt;

&lt;p&gt;That&amp;rsquo;s where &lt;strong&gt;&lt;a href="https://chatgpt.com/g/g-67ae55eeabc08191b29a7e1c529ae9d9-ui-unblocker"&gt;UI Unblocker&lt;/a&gt;&lt;/strong&gt; comes in. It&amp;rsquo;s a fast way to analyze, critique, and suggest improvements to &lt;strong&gt;what&amp;rsquo;s already there.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;How UI Unblocker Works&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;I use it in a simple workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Take a screenshot&lt;/strong&gt; or describe the UI in text.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explain the screen&amp;rsquo;s intent&lt;/strong&gt; (e.g., &amp;ldquo;This is a pricing page, but I feel like it&amp;rsquo;s not clear enough&amp;rdquo;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ask &lt;a href="https://chatgpt.com/g/g-67ae55eeabc08191b29a7e1c529ae9d9-ui-unblocker"&gt;UI Unblocker&lt;/a&gt;&lt;/strong&gt; to analyze it based on modern UX best practices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If I need more ideas&lt;/strong&gt;, I request visual concepts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterate&lt;/strong&gt; until I have a clear direction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run a final check&lt;/strong&gt; through &lt;a href="https://chatgpt.com/g/g-67ae55eeabc08191b29a7e1c529ae9d9-ui-unblocker"&gt;UI Unblocker&lt;/a&gt; to make sure nothing regressed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If I had to do this at scale, I&amp;rsquo;d use a dedicated tool, but for occasional use, this process works well.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Prompts – Tweak as Needed&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;There are two prompts at the end of this article &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first is the CustomGPT instruction set used in &lt;a href="https://chatgpt.com/g/g-67ae55eeabc08191b29a7e1c529ae9d9-ui-unblocker"&gt;UI Unblocker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The second is a version that can be used as a regular prompt to copy and paste&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;strong&gt;When to Use UI Unblocker vs. Other Tools&lt;/strong&gt;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;✅ &lt;a href="https://chatgpt.com/g/g-67ae55eeabc08191b29a7e1c529ae9d9-ui-unblocker"&gt;UI Unblocker&lt;/a&gt;&lt;/strong&gt; → When you&amp;rsquo;re stuck and need quick, fresh input.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;✅ AI UX Auditors (like Uizard, Galileo AI)&lt;/strong&gt; → When you need usability scores and structured feedback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;✅ Figma/Sketch&lt;/strong&gt; → When iterating on and finalizing a full design.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;strong&gt;AI as a UX Assistant, Not a Designer&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;The goal isn&amp;rsquo;t to replace UX designers or design tools. It&amp;rsquo;s to get &lt;strong&gt;unstuck faster&lt;/strong&gt; when working through UI issues. Sometimes, all you need is a fresh perspective to see what&amp;rsquo;s been in front of you the whole time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you use the prompt, I&amp;rsquo;d be interested to hear how you tweak it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;Prompt&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;### Custom GPT Instructions

💡 **You are an AI UX and UI design expert.** Your role is to generate **functional, structured UI mockups** while ensuring **best UX practices**.

🎯 **Your Goal:**  
- Provide a **clear, structured wireframe representation** of a UI layout.  
- **No colors, shadows, artistic styling, or abstract embellishments.**  
- Focus on **information hierarchy, spacing, and layout clarity**.  
- Optimize outputs for **developer-friendly implementation**.  

---

## 🔹 **1️⃣ Analyze the User&amp;#39;s Intent**  
   - If the user is **creating a new UI**, ask for structured input:
     ✅ **What is the UI’s purpose?**  
     ✅ **Who is the target audience?**  
     ✅ **What are the key actions users should take?**  

   - If the user provides a **screenshot**, analyze and extract:  
     ✅ **Only the layout, structure, and key UX elements**  
     ✅ **Ignore colors, branding, and artistic elements**  
     ✅ **Identify repeating patterns and component structures**  

---

## 🔹 **2️⃣ Build a Structured UI Table + JSON Wireframe**  
✅ **Step 2a: Generate a human-readable table** summarizing all UI elements.  
✅ **Step 2b: Convert the table into a JSON-based wireframe structure.**  

   - The **table helps the user quickly scan and confirm information**.  
   - The **JSON wireframe provides structured input for UI generation**.  

✅ **Step 2c: Define Consistent Structure for Repeating Sections**  
   - If the UI includes **repeated content elements** (e.g., albums, products, articles), **use a structured template** rather than defining each item individually.  
   - Define repeating sections as **data-driven components** for flexible updates.  

   **Structure Guidelines:**  
   - **Lists:** Use consistent spacing, typography, and metadata placement.  
   - **Grids:** Ensure uniform card sizes and clear gutters.  
   - **Dynamic Sections:** Paginate or provide scrolling mechanisms as needed.  

   **Example JSON for a repeating section:**
   ```json
   {
      &amp;quot;trending_albums&amp;quot;: {
         &amp;quot;grid_layout&amp;quot;: &amp;quot;4-column&amp;quot;,
         &amp;quot;cards&amp;quot;: [
            {
               &amp;quot;album_cover&amp;quot;: &amp;quot;placeholder.svg&amp;quot;,
               &amp;quot;album_title&amp;quot;: &amp;quot;Example Album&amp;quot;,
               &amp;quot;artist_name&amp;quot;: &amp;quot;Artist Name&amp;quot;,
               &amp;quot;track_count&amp;quot;: &amp;quot;8 Tracks&amp;quot;
            },
            {
               &amp;quot;album_cover&amp;quot;: &amp;quot;placeholder.svg&amp;quot;,
               &amp;quot;album_title&amp;quot;: &amp;quot;Another Album&amp;quot;,
               &amp;quot;artist_name&amp;quot;: &amp;quot;Different Artist&amp;quot;,
               &amp;quot;track_count&amp;quot;: &amp;quot;12 Tracks&amp;quot;
            }
         ]
      }
   }

## 🔹 3️⃣ Provide UX Recommendations + User Confirmation
- Evaluate **usability, accessibility, clarity, and interaction design**.
- Suggest **layout improvements** (e.g., readability, padding, navigation refinement).
- Display recommendations in an **easy-to-read format** before generating UI.
- Allow users to **approve or modify recommendations** before proceeding.

---

## 🔹 4️⃣ Generate Two Simple UI Mockups from the Wireframe  
✅ **Step 4a: HTML + Tailwind CSS Mockup**  
- Generate a **structured UI using HTML &amp;amp; Tailwind CSS**.  
- **No need for functional interactivity**—only layout and structure.  
- **Use SVG or CSS placeholders** where content images would be.  

✅ **Step 4b: Image-Based Wireframe Rendering**  
- Generate a **minimal grayscale UI wireframe image** from the JSON wireframe.  
- **No unnecessary artistic details—only layout clarity**.  
- **Only generate an image upon request** to optimize chat interactions.  

---

## 🔹 5️⃣ Iterate Based on User Feedback  
- Allow the user to **accept, modify, or request alternative designs**.  
- If needed, generate **simplified variations** (e.g., **list vs. grid, compact vs. spacious layouts**).  
- Ensure **developer-friendly outputs** that can be easily implemented.  

---

## 🔍 **If the user asks for UX improvements, prioritize:**  
✅ **Navigation efficiency** (reduce clicks, intuitive placement).  
✅ **Readability** (proper text hierarchy, no clutter).  
✅ **Action clarity** (buttons &amp;amp; CTAs stand out).  
✅ **Accessibility compliance** (keyboard navigation, minimal cognitive load).  

💡 **All UI mockups must be structured, minimal, and strictly wireframe-based. NO unnecessary decorations, branding, or visual effects.**
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; If you prefer to use this as a regular prompt to copy and paste, or inject into some other workflow, here is a version for that:  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;You are an AI UX and UI design expert. Your task is to generate **functional, structured UI mockups** while ensuring **best UX practices**. Follow the instructions below with careful.

🎯 **Your Goal:**  
- Provide a **clear, structured wireframe representation** of a UI layout.  
- **No colors, shadows, artistic styling, or abstract embellishments.**  
- Focus on **information hierarchy, spacing, and layout clarity**.  

---

🔹 **1️⃣ Analyze the User&amp;#39;s Intent**  
   - If the user is creating a new UI, **gather structured input** about its purpose, audience, and key actions.  
   - If they provide a screenshot, extract **only** its **layout, structure, and key UX elements** (ignore colors or artistic styles).  

---

🔹 **2️⃣ Build a Structured UI Table + JSON Wireframe**  
   ✅ **Step 2a: Generate a human-readable table** summarizing all UI details.  

   ✅ **Step 2b: Convert the table into a JSON-based wireframe structure.**  
   - The **table helps the user quickly scan and confirm information** before proceeding.  
   - The **JSON wireframe serves as the structured input for generating the mockup.**

   ✅ **Step 2c: Define Consistent Structure for Repeating Sections**  
      - If the UI includes **repeated content elements** (e.g., albums, products, articles), **use a structured template** rather than defining each item individually.  
      - Define repeating sections as **data-driven components** in both JSON and HTML to allow for flexible updates.  
      - **Structure guidelines:**  
      - **Lists:** Use consistent spacing, typography, and metadata placement.  
      - **Grids:** Ensure uniform card sizes and clear gutters.  
      - **Dynamic Sections:** Paginate or provide scrolling mechanisms as needed.  
      - Example JSON for a repeating section:
      ```json
      {
      &amp;quot;trending_albums&amp;quot;: {
         &amp;quot;grid_layout&amp;quot;: &amp;quot;4-column&amp;quot;,
         &amp;quot;cards&amp;quot;: [
            {
            &amp;quot;album_cover&amp;quot;: &amp;quot;placeholder.svg&amp;quot;,
            &amp;quot;album_title&amp;quot;: &amp;quot;Example Album&amp;quot;,
            &amp;quot;artist_name&amp;quot;: &amp;quot;Artist Name&amp;quot;,
            &amp;quot;track_count&amp;quot;: &amp;quot;8 Tracks&amp;quot;
            },
            {
            &amp;quot;album_cover&amp;quot;: &amp;quot;placeholder.svg&amp;quot;,
            &amp;quot;album_title&amp;quot;: &amp;quot;Another Album&amp;quot;,
            &amp;quot;artist_name&amp;quot;: &amp;quot;Different Artist&amp;quot;,
            &amp;quot;track_count&amp;quot;: &amp;quot;12 Tracks&amp;quot;
            }
         ]
      }
      }   

---

🔹 **3️⃣ Provide UX Recommendations + User Confirmation**  
   - Evaluate **usability, accessibility, clarity, and interaction design**.  
   - Suggest **layout improvements, readability enhancements, and navigation refinements**.  
   - **Display these recommendations in an easy-to-read format** for user approval.  
   - **Before proceeding, ask the user to confirm or modify** the recommendations.  

---

🔹 **4️⃣ Generate Two Simple UI Mockups from the Wireframe**  
   ✅ **Step 4a: HTML + Tailwind CSS Mockup**  
   - Generate a simple, structured UI using **HTML &amp;amp; Tailwind CSS**.  
   - **No need for functional interactivity**—just the visual structure.  
   - **Use SVG or CSS placeholders** where content images would be.  

   ✅ **Step 4b: Image-Based Wireframe Rendering**  
   - Generate a **minimal grayscale UI wireframe image** from the JSON wireframe.  
   - **No unnecessary artistic details**—only layout clarity.  

---

🔹 **5️⃣ Iterate Based on User Feedback**  
   - Allow the user to **accept, modify, or request alternative designs**.  
   - If needed, generate **simplified variations** (e.g., **list vs. grid, compact vs. spacious layouts**).  

---

🔍 **If the user asks for UX improvements, prioritize:**  
✅ **Navigation efficiency** (fewer clicks, intuitive placement of elements).  
✅ **Readability** (proper spacing, minimal text overload).  
✅ **Action clarity** (CTAs must stand out, key actions easy to complete).  
✅ **Accessibility compliance** (keyboard navigation, minimal cognitive load).  

💡 **All UI mockups must be structured, minimal, and strictly wireframe-based. NO unnecessary decorations, branding, or visual effects.**  

---

### **🚀 Next Steps**
Would you like me to **test this refined workflow now** by generating a UI mockup **from scratch** or using your provided screenshot? 🚀 Let me know how you&amp;#39;d like to proceed!
&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <title>&amp;#129302; Refactoring GraphQL with AI Coding Assistants: A Real-World Use Case</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/02/09/lessons-refactor-and-optimization.html"/>
    <id>http://www.brandoncasci.com/2025/02/09/lessons-refactor-and-optimization.html</id>
    <published>2025-02-09T20:08:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;div&gt;&lt;small&gt;2025-02-09&lt;/small&gt;&lt;/div&gt;

&lt;h1&gt;&amp;#129302; Refactoring GraphQL with AI Coding Assistants: A Real-World Use Case&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/02/09/cover.png" alt="Refactoring GraphQL with AI coding assistants" /&gt;&lt;/p&gt;

&lt;p&gt;AI coding assistants are everywhere, but much of the content focuses on simplistic, contrived demos. &amp;ldquo;Hey AI, make me a to-do list app!&amp;rdquo; Of course, greenfield projects work like magic. The real test is applying AI to &lt;strong&gt;large, messy, real-world codebases&lt;/strong&gt; that have evolved over time. That&amp;rsquo;s what I did while refactoring a legacy &lt;strong&gt;GraphQL API&lt;/strong&gt; to support a new database schema without breaking the public interface.&lt;/p&gt;

&lt;p&gt;This is how AI coding assistants work in practice: useful, but not magic. Here’s how I structured the process to make them work effectively.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;The Challenge: Schema Fixes Without Breaking the Frontend&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;The goal was to fix deficiencies in an existing &lt;strong&gt;survey system schema&lt;/strong&gt; that were holding back progress. But there was a &lt;strong&gt;catch&lt;/strong&gt;: the frontend React developers couldn’t be derailed from their existing work. That meant preserving the &lt;strong&gt;GraphQL API interface&lt;/strong&gt; while completely restructuring the underlying schema.&lt;/p&gt;

&lt;p&gt;On top of that, the codebase was &lt;strong&gt;old, large, and undocumented&lt;/strong&gt;, built by a previous team with inconsistent standards. No easy wins here.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Step 1: Define the Ideal Schema&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Before touching any code, I manually &lt;strong&gt;designed the ideal schema&lt;/strong&gt; based on current and future needs. This set a clear target for all changes.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Step 2: AI-Assisted Implementation Planning&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;A coding assistant (Claude 3.5 Sonnet via Cursor) was given a structured prompt outlining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The current and future state of the schema&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The need to maintain API compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The backend services impacted&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It produced an &lt;strong&gt;initial high-level implementation plan&lt;/strong&gt;, which I iterated on. &lt;strong&gt;AI won’t always get this perfect on the first try, but it accelerates structured thinking.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Step 3: Implement &amp;amp; Validate the New Schema&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Once we had a vetted plan, I manually implemented the &lt;strong&gt;new schema in isolation&lt;/strong&gt;, ensuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Built the new database objects (ActiveRecord objects in this case)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validated attributes and relationships&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implemented queries and data validation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wrote unit tests&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now came the detailed part: mapping the new schema without breaking anything.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Step 4: AI-Driven Query Mapping&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Since the frontend relied on &lt;strong&gt;GraphQL queries&lt;/strong&gt; that referenced the old schema, I used an AI-assisted pipeline to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scan the React app&lt;/strong&gt; to locate every GraphQL query touching the affected schema fields.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extract query parameters&lt;/strong&gt; and store them in a structured &amp;ldquo;plan file.&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This created a &lt;strong&gt;map of dependencies&lt;/strong&gt;, ensuring nothing slipped through the cracks.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Step 5: AI-Guided Backend Refactoring&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;With the plan file in place, I tasked the AI assistant with analyzing the &lt;strong&gt;Rails GraphQL API&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identifying &lt;strong&gt;where each GraphQL query was defined&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Mapping &lt;strong&gt;which GraphQL types were impacted&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Generating an &lt;strong&gt;implementation plan&lt;/strong&gt; for &lt;strong&gt;incrementally&lt;/strong&gt; updating queries to match the new schema&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;strong&gt;Step 6: Fine-Tuning the Migration Plan&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Before running anything, I added &lt;strong&gt;manual replacement rules&lt;/strong&gt; to the plan file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Direct class and attribute name swaps&lt;/strong&gt; (e.g., &lt;code&gt;Apple -&amp;gt; Orange&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema transformations&lt;/strong&gt; (where more complex rewrites were needed)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Example snippets for AI-generated suggestions&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;strong&gt;Step 7: AI-Assisted Code &amp;amp; Test Generation&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;I fed the refined plan into Cursor (Claude 3.5 Sonnet) and tasked it with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Refactoring GraphQL query resolvers&lt;/strong&gt; incrementally.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Writing missing tests&lt;/strong&gt; (huge testing gaps existed in the original implementation).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI &lt;strong&gt;excelled at scaffolding tests&lt;/strong&gt; and &lt;strong&gt;reducing code-hunting time&lt;/strong&gt;, but was less reliable in fully rewriting complex query logic. Still, the time saved on &lt;strong&gt;searching through a massive codebase&lt;/strong&gt; made it worth it.&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;The Results: A Hybrid AI/Human Approach&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;By combining AI-powered &lt;strong&gt;dependency mapping, automated planning, and test generation&lt;/strong&gt; with &lt;strong&gt;manual architecture and validation&lt;/strong&gt;, I successfully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Migrated the GraphQL API to a &lt;strong&gt;new schema&lt;/strong&gt; without frontend disruption.&lt;br&gt;
✅ Used &lt;strong&gt;AI to eliminate hours of code searching&lt;/strong&gt; and documentation gaps.&lt;br&gt;
✅ Applied &lt;strong&gt;targeted refactoring&lt;/strong&gt; instead of risky full rewrites.&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;&lt;strong&gt;Final Takeaways&lt;/strong&gt;&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;1️⃣ &lt;strong&gt;AI isn’t a magic bullet, but it’s a fantastic tool for structuring large-scale refactors.&lt;/strong&gt;&lt;br&gt;&lt;/li&gt;
&lt;li&gt;2️⃣ &lt;strong&gt;Manual oversight is still critical—especially in high-stakes migrations.&lt;/strong&gt;&lt;br&gt;&lt;/li&gt;
&lt;li&gt;3️⃣ &lt;strong&gt;AI is best used for accelerating repetitive tasks: scanning, planning, scaffolding tests.&lt;/strong&gt;&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔹 &lt;strong&gt;Have you used AI for refactoring in real-world projects? How did it go?&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Getting AI Coding Assistants From OK To Great</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2025/01/09/taking-ai-coding-assistants-from-ok-to-great.html"/>
    <id>http://www.brandoncasci.com/2025/01/09/taking-ai-coding-assistants-from-ok-to-great.html</id>
    <published>2025-01-10T02:43:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;div&gt;&lt;small&gt;2025-01-09&lt;/small&gt;&lt;/div&gt;

&lt;h1&gt;Getting AI Coding Assistants From OK To Great &amp;#9881;&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2025/01/09/cover.png" alt="Working with AI" /&gt;&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve been experimenting with AI coding tools to see if I could improve their effectiveness. My focus has been on training Cursor AI for a specific project I’m working on at RiseKit.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s what I done so far:&lt;/p&gt;

&lt;h4&gt;✅ Rules and Templates&lt;/h4&gt;

&lt;p&gt;I created a rules file with project-specific coding standards, architecture patterns, testing guidelines, and documentation requirements.&lt;/p&gt;

&lt;h4&gt;💬  Pre-composed Prompts&lt;/h4&gt;

&lt;p&gt;These covered common tasks like code reviews, test writing, and generating documentation.&lt;/p&gt;

&lt;h4&gt;🔁 Stateful Prompts&lt;/h4&gt;

&lt;p&gt;For more complex, multi-step tasks, I built prompts that maintain context across sessions, ensuring consistency in longer operations. &lt;/p&gt;

&lt;p&gt;The AI Composer updates the stateful prompt as the task progresses, sometimes with my direction, othertimes it will prompt me to confirm when a step appears complete.&lt;/p&gt;

&lt;h4&gt;🛠️ How I built the rules and templates&lt;/h4&gt;

&lt;p&gt;One of the most useful aspects was using AI itself to refine these rules and templates. &lt;/p&gt;

&lt;p&gt;I started with a short paragraph of instructions on a topic, like coding standards, 
documenation, testing or implementation planning, and would ask the AI expand on the concept
and come up with an AI optimized version of the rules.&lt;/p&gt;

&lt;p&gt;I then iterated on a rule file. That involved some editing by myself and also asking the AI to analyze and improve the rules.&lt;/p&gt;

&lt;p&gt;If the rules had to do with coding, I would ask it repeadly to analyze the code base for certain patterns regarding
specific layers of the codebase like views, controllers, models, business rules, etc. If sensible patterns were missing, 
I would introduce one based on best practices. Coding examples are paired with the rules.&lt;/p&gt;

&lt;p&gt;Don&amp;rsquo;t be shy with the instructions you give to the AI, you won&amp;rsquo;t frighten it or bore it to death.
You&amp;rsquo;ll be turning it into a domain expert for your project and ideals.&lt;/p&gt;

&lt;p&gt;This approach has worked well for a few established codebases. Refactoring becomes easier, and the AI performs better as the codebase becomes cleaner and more consistent. &lt;/p&gt;

&lt;p&gt;Good code quality and documentation are just as important with AI tools as without, they rely on clear patterns to function effectively.&lt;/p&gt;

&lt;p&gt;If the codebase is messy, the AI can get off track or produce a lot more inaccurate suggestions.
But when you invest in training the tools and maintaining code quality, you create a
positive feedback loop: each improvement makes the AI faster and more accurate.&lt;/p&gt;

&lt;p&gt;AI coding assistants are not a replacement for good practices—they are a way to enhance them.&lt;/p&gt;

&lt;p&gt;&lt;img src="/2025/01/09/1.png" alt="Working with AI" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2025/01/09/2.png" alt="Working with AI" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2025/01/09/3.png" alt="Working with AI" /&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Process Spawning with Ruby</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2023/09/30/process-spawning-with-ruby.html"/>
    <id>http://www.brandoncasci.com/2023/09/30/process-spawning-with-ruby.html</id>
    <published>2023-10-01T00:43:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;div&gt;&lt;small&gt;2023-09-30&lt;/small&gt;&lt;/div&gt;

&lt;h1&gt;Process Spawning with Ruby &amp;#9881;&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2023/09/30/cover.png" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;In 2015, I needed a way to record audio streams on a scheduled basis. To accomplish this I created a Rails app called Radioshifter that
served as a UI for editing and storing schedules. Under the hood, Radioshifter did a lot of things. One of those things was spawning new processes
whenever it was time to capture an audio stream.&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/09/30/radio-shifter.png" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;Radioshifter - R.I.P. &amp;#129702;&amp;#9760;&lt;/p&gt;

&lt;h2&gt;Methods&lt;/h2&gt;

&lt;p&gt;There are a few ways to spawn a process with Ruby - &lt;code&gt;system&lt;/code&gt;, &lt;code&gt;fork&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;, &lt;a href="https://docs.ruby-lang.org/en/3.2/Kernel.html#method-i-spawn"&gt;Process.spawn&lt;/a&gt;,
and loads more options in &lt;a href="https://docs.ruby-lang.org/en/3.2/Open3.html"&gt;Open3&lt;/a&gt; and &lt;a href="https://docs.ruby-lang.org/en/3.2/PTY.html"&gt;PTY&lt;/a&gt;.
Each method has its own use case. &lt;/p&gt;

&lt;h2&gt;The Method I Chose&lt;/h2&gt;

&lt;p&gt;I chose &lt;code&gt;Process.spawn&lt;/code&gt; because there was no need to wait for the process to finish.
I just needed the code to spawn a new process, then detach from the current process
with &lt;code&gt;Process.detach&lt;/code&gt;, and let the &lt;a href="https://bitbucket.org/bcasci/radio_shifter_web/src/master/lib/scheduler/recording_process.rb"&gt;current process&lt;/a&gt;
finish while the spawned process continued to run.&lt;/p&gt;

&lt;h2&gt;The Spawned Process&lt;/h2&gt;

&lt;p&gt;To give you some context, the spawned process was responsible for recording an audio stream. It orchestrated the recording process by calling serveral
other system level commands. Some later stage processes even observed other processes, ending them at specific times,
or restarting them if they crashed prematurely.&lt;/p&gt;

&lt;p&gt;The spawned process was the starting point for a chain of events that involved the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;capturing the audio stream to disk using &lt;code&gt;mplayer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;converting the audio file(s) to an MP3 file using &lt;code&gt;ffmpeg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;uploading the MP3 to a user&amp;rsquo;s Dropbox account&lt;/li&gt;
&lt;li&gt;cleaning up local recording files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Caveats&lt;/h2&gt;

&lt;p&gt;While some of the priciples still hold true, the application was made in 2015 and my approach might be a bit different today.&lt;/p&gt;

&lt;p&gt;I also would probably name the classes a bit differently now. I&amp;rsquo;m not a huge fan of the -er -or naming convention that I used for certain classes,
but I digress!&lt;/p&gt;

&lt;h2&gt;The Code&lt;/h2&gt;

&lt;p&gt;Here is the &lt;a href="https://bitbucket.org/bcasci/radio_shifter_web/src/master/lib/scheduler/recording_process.rb"&gt;starting point&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module Scheduler
  class RecordingProcess
    attr_reader :recording_schedule

    def initialize(recording_schedule)
      @recording_schedule = recording_schedule
      @pid_file_name = @recording_schedule.pid_file_name
    end

    def spawn
      return if running?
      logfile_path_option = if ENV[&amp;#39;RECORDINGS_LOG_FOLDER&amp;#39;]
        &amp;quot;-l #{ENV[&amp;#39;RECORDINGS_LOG_FOLDER&amp;#39;]}/#{recording_schedule.id}/recording.log&amp;quot;
      end

      pid_file_option = &amp;quot;-p #{@pid_file_name}&amp;quot;

      command = &amp;quot;cd #{Rails.root}/bin &amp;amp;&amp;amp; FFMPEG_CMD=#{ ENV[&amp;#39;FFMPEG_CMD&amp;#39;] } FFPROBE_CMD=#{ ENV[&amp;#39;FFPROBE_CMD&amp;#39;] } ./record_stream -i #{ @recording_schedule.id } -u #{ @recording_schedule.stream_url } -d #{ @recording_schedule.duration } -f /tmp/radio_shifter_recording/#{ @recording_schedule.id } #{logfile_path_option} #{pid_file_option} &amp;gt; /dev/null&amp;quot;
      pid = Process.spawn(command)
      Process.detach(pid)
    end

    # TODO - Stop using PID to prevent duplicate recordings.
    # Have the recorder get a fail lock and exit on fail
    # We still want the PID for other reasons, so have the recorder
    # send it&amp;#39;s PID to message queue when it starts, and wipe the PID
    # when it stops.
    def running?
      if File.exists?(@pid_file_name)
        pid = File.read(@pid_file_name).strip.to_i
        begin
          Process.kill(0, pid) == 1
        rescue Errno::ESRCH
          false
        end
      else
        false
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env ruby
require &amp;#39;logger&amp;#39;
require &amp;#39;backburner&amp;#39;
require &amp;#39;stalker&amp;#39;
require &amp;#39;optparse&amp;#39;
require &amp;#39;etc&amp;#39;
require &amp;#39;fileutils&amp;#39;

class Rails
  class EnvString &amp;lt; String
    def development?
      self == &amp;#39;development&amp;#39;
    end

    def production?
      self == &amp;#39;production&amp;#39;
    end

    def test?
      self == &amp;#39;test&amp;#39;
    end
  end

  def self.env
    @env ||= EnvString.new(ENV[&amp;quot;RAILS_ENV&amp;quot;] || &amp;#39;development&amp;#39;)
  end
end

require_relative &amp;#39;../app/extras/services/recording_uploader&amp;#39;
require_relative &amp;#39;../config/initializers/backburner&amp;#39;
require_relative &amp;#39;../lib/scheduler/audio_concatenator&amp;#39;
require_relative &amp;#39;../lib/scheduler/audio_prober&amp;#39;
require_relative &amp;#39;../lib/scheduler/command_line_error&amp;#39;
require_relative &amp;#39;../lib/scheduler/mp3_audio_converter&amp;#39;
require_relative &amp;#39;../lib/scheduler/recorder&amp;#39;
require_relative &amp;#39;../lib/scheduler/recording_process_manager&amp;#39;
require_relative &amp;#39;../lib/scheduler/wav_audio_converter&amp;#39;

ENV[&amp;#39;FFMPEG_CMD&amp;#39;] ||= &amp;#39;ffmpeg&amp;#39;
ENV[&amp;#39;FFPROBE_CMD&amp;#39;] ||= &amp;#39;ffprobe&amp;#39;

# TODO - Research changing the process user and group.
#        This doesn&amp;#39;t seem to work on Ubunto.
if Rails.env.production?
  u = Etc.getpwnam(&amp;#39;rails&amp;#39;)
  Process.uid = u.uid
  Process.gid = u.gid
end

options = {}
OptionParser.new do |opts|
  opts.banner = &amp;quot;Usage: record_stream [options]&amp;quot;
  opts.on(&amp;#39;-i&amp;#39;, &amp;#39;--schedule_id SCHEDULE_ID&amp;#39;, &amp;#39;The Schedule ID this recording belongs to&amp;#39;) { |v| options[:schedule_id] = v }
  opts.on(&amp;#39;-u&amp;#39;, &amp;#39;--url URL&amp;#39;, &amp;#39;url to record from&amp;#39;) { |v| options[:url] = v }
  opts.on(&amp;#39;-d&amp;#39;, &amp;#39;--duration SECONDS&amp;#39;, &amp;#39;Number of seconds to record&amp;#39;) { |v| options[:duration] = v.to_i }
  opts.on(&amp;#39;-f&amp;#39;, &amp;#39;--folder FOLDER&amp;#39;, &amp;#39;The folder to place the recording in&amp;#39;) { |v| options[:folder] = v }
  opts.on(&amp;#39;-l&amp;#39;, &amp;#39;--logfile FILE&amp;#39;, &amp;#39;The log file &amp;#39;) { |v| options[:log_file] = v }
  opts.on(&amp;#39;-p&amp;#39;, &amp;#39;--pidfile FILE&amp;#39;, &amp;#39;The PID file &amp;#39;) { |v| options[:pid_file] = v }

end.parse!

Process.daemon

logger = if options[:log_file]
  File.truncate(options[:log_file], 0) if File.exists?(options[:log_file])
  FileUtils::mkdir_p File.dirname(options[:log_file])
  Logger.new(options[:log_file])
end

recorder = Scheduler::Recorder.new(
  options[:url],
  options[:duration],
  options[:folder]
)

FileUtils::mkdir_p options[:folder]
FileUtils::mkdir_p File.dirname(options[:pid_file])
# TODO This is a stop gap for the above process permission issue.
if ENV[&amp;quot;RAILS_ENV&amp;quot;] == &amp;#39;production&amp;#39;
  FileUtils.chown &amp;#39;rails&amp;#39;, &amp;#39;www-data&amp;#39;, options[:folder]
  FileUtils.chown &amp;#39;rails&amp;#39;, &amp;#39;www-data&amp;#39;, File.dirname(options[:pid_file])
end

# TODO This is a stop gap for the above process permission issue
File.open(options[:pid_file], &amp;#39;w&amp;#39;) {|f| f.write(Process.pid) }
if ENV[&amp;quot;RAILS_ENV&amp;quot;] == &amp;#39;production&amp;#39;
  FileUtils.chown &amp;#39;rails&amp;#39;, &amp;#39;www-data&amp;#39;, options[:pid_file]
end

recording_manager = Scheduler::RecordingProcessManager.new(
    options[:schedule_id],
    recorder,
    ENV[&amp;#39;FFMPEG_CMD&amp;#39;],
    ENV[&amp;#39;FFPROBE_CMD&amp;#39;],
    logger
  )

recording_manager.start
logger.close

if File.exists?(options[:pid_file])
  File.delete(options[:pid_file])
end
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;module Scheduler
  class RecordingProcessManager
    def initialize(schedule_id, recorder, ffmpeg_path, ffprobe_path, logger = nil)
      @schedule_id = schedule_id
      @recorder = recorder
      @ffmpeg_path = ffmpeg_path
      @ffprobe_path = ffprobe_path
      @logger = logger
    end

    def start
      begin
        run
      rescue CommandLineError =&amp;gt; e
      rescue CommandLineError =&amp;gt; e
        if @logger
          @logger.error &amp;quot;Command line error.&amp;quot;
          @logger.error &amp;quot;#{e.message}&amp;quot;
          @logger.error &amp;quot;#{e.command_output}&amp;quot;
          @logger.error e.backtrace.join(&amp;quot;\n&amp;quot;)
        end
      rescue Exception =&amp;gt; e
        if @logger
          @logger.error &amp;quot;#{e.message}&amp;quot;
          @logger.error e.backtrace.join(&amp;quot;\n&amp;quot;)
        end
      end
    end

    def run
      success = false
      @recorder.start
      sleep 5
      # TODO - put the following steps into a separate
      # async action
      wav_converter = Scheduler::WavAudioConverter.new(@ffmpeg_path, @recorder.dump_files)
      wav_files = wav_converter.convert
      finalized_wav_file = get_finalized_wav_file(wav_files)
      if finalized_wav_file
        mp3_bitrate = wave_bitrate_to_mp3_bitrate(
          Scheduler::AudioProber.new(@ffprobe_path, finalized_wav_file, @logger).bitrate
        )
        mp3_file = finalized_wav_file.gsub(&amp;#39;.wav&amp;#39;, &amp;#39;.mp3&amp;#39;)
        mp3_converter = Scheduler::Mp3AudioConverter.new(@ffmpeg_path, finalized_wav_file, mp3_file, mp3_bitrate, @logger)
        if mp3_converter.convert
          Services::RecordingUploader.async.upload(@schedule_id, mp3_file)
          cleanup_recording_folder(@recorder.folder)
          success = true
        end
      end
      success
    end

    private

    def cleanup_recording_folder(folder)
      files = Dir[folder + &amp;#39;/*.*&amp;#39;].select{|f| !File.directory?(f) &amp;amp;&amp;amp;  File.extname(f) != &amp;#39;.mp3&amp;#39; }
      File.delete(*files)
    end

    def get_finalized_wav_file(wav_files)
      if wav_files.size &amp;gt; 1
        output_file = &amp;quot;#{@recorder.folder}/combined.wav&amp;quot;
        concatenator = Scheduler::AudioConcatenator.new(@ffmpeg_path, wav_files, output_file)
        return output_file if concatenator.concatenate
      else
        wav_files.first
      end
    end

    def wave_bitrate_to_mp3_bitrate(wav_bitrate)
      if wav_bitrate &amp;lt;= 500000
        &amp;#39;48k&amp;#39;
      elsif wav_bitrate &amp;lt;= 800000
        &amp;#39;64k&amp;#39;
      elsif wav_bitrate &amp;lt;= 1199999
        &amp;#39;96k&amp;#39;
      else
        &amp;#39;128k&amp;#39;
      end
    end

  end
end
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;When spawning a new process with Ruby, the method you choose will depend on your use case. 
In my case, I needed to spawn a new process and detach from the current process. 
In other cases someone might want to wait for the spawned process to finish.
Then there are times you way want to read the process output as it happens, and even send it input while it&amp;rsquo;s running.
I hope this article was is helpful in deciding which method to use when spawning a new process with Ruby.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Inter-Process Communication With Ruby</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2023/08/28/inter-process-communication-with-ruby.html"/>
    <id>http://www.brandoncasci.com/2023/08/28/inter-process-communication-with-ruby.html</id>
    <published>2023-08-29T02:31:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;div&gt;&lt;small&gt;2023-08-28&lt;/small&gt;&lt;/div&gt;

&lt;h1&gt;Inter-Process Communication With Ruby &amp;#x1F504;&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2023/08/28/cover.png" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;In 2015, I needed a way to record audio streams on a scheduled basis. To accomplish this, I created a Rails app called Radioshifter that served as a UI for editing and storing schedules.
In a separate process, a &lt;a href="https://github.com/jmettraux/rufus-scheduler"&gt;rufus-scheduler&lt;/a&gt; would run and start/stop various command line applications
that captured online audio streams to disk and uploaded them to Dropbox.&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/28/radio-shifter.png" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;Radioshifter - R.I.P. &amp;#129702;&amp;#9760;&lt;/p&gt;

&lt;h2&gt;Choosing a Method for Interprocess Communication&lt;/h2&gt;

&lt;p&gt;I looked at various methods for interprocess communication with Ruby, which included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pipes&lt;/li&gt;
&lt;li&gt;shared memory&lt;/li&gt;
&lt;li&gt;message queues&lt;/li&gt;
&lt;li&gt;sockets&lt;/li&gt;
&lt;li&gt;RPC frameworks&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ruby/drb"&gt;Ruby dRb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;dRb: The Right Tool for the Job&lt;/h2&gt;

&lt;p&gt;I ultimately chose dRb (distributed Ruby) for
its simplicity and ease of use.&lt;/p&gt;

&lt;p&gt;The dRb fronted process didn&amp;rsquo;t have too much to worry about in terms
of processing messages. All it needed to do was take a message and edit rufus-scheduler&amp;rsquo;s job list.
dRb seemed capable enough for that type of message load.&lt;/p&gt;

&lt;p&gt;To enable communication between the Rails app and the scheduler, I used dRb.
dRb is a distributed object system for Ruby. It allows you to expose objects to other processes
and communicate with them. dRb is a part of the Ruby standard library, so it&amp;rsquo;s available out of the box.
There are two main components to a dRb implementation, whatever you choose to be the server, and a client or clients.&lt;/p&gt;

&lt;h2&gt;My dRb Implementation in Radioshifter&lt;/h2&gt;

&lt;p&gt;Things to keep in mind: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I did this in 2015, so some of the code may be a bit dated.&lt;/li&gt;
&lt;li&gt;I did this in a hurry, so there are some things that could be improved.&lt;/li&gt;
&lt;li&gt;At this point in time, there are probably better ways to do this&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s no built in or durability or retrying with dRb, like a robust message queue would have.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve never looked into the performace limits of dRb.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that being said, there is a time and place for dRb, and it&amp;rsquo;s a good tool know about.&lt;/p&gt;

&lt;p&gt;Below are the critical entry points for the dRb implementation, but you can also view the full
application source code on the &lt;a href="https://bitbucket.org/bcasci/radio_shifter_web/"&gt;repository&lt;/a&gt;.
I&amp;rsquo;m also no longer really fond of the -er -or naming convention that I used for certain classes, but I digress!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# The executable that starts the scheduler which is fronted by dRb

#!/usr/bin/env ruby
require &amp;#39;drb&amp;#39;
require File.expand_path(&amp;#39;../../config/environment&amp;#39;, __FILE__)

ENV[&amp;#39;RAILS_ENV&amp;#39;] ||= &amp;#39;development&amp;#39;
ENV[&amp;#39;RUFUS_SCHEDULER_SERVER_PORT&amp;#39;] ||= &amp;#39;9000&amp;#39;
ENV[&amp;#39;RECORDINGS_LOG_FOLDER&amp;#39;] ||= File.expand_path(&amp;#39;../../log/recordings&amp;#39;, __FILE__)

trap(&amp;quot;INT&amp;quot;) { exit }

RUFUS_SCHEDULER = Rufus::Scheduler.new

server = Scheduler::Server.new(RUFUS_SCHEDULER)
bootstrapper = Scheduler::ServerBootstrapper.new(server, RecordingSchedule)
bootstrapper.bootstrap

DRb.start_service(&amp;quot;druby://localhost:#{ENV[&amp;#39;RUFUS_SCHEDULER_SERVER_PORT&amp;#39;]}&amp;quot;,
  server
)
DRb.thread.join  
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;# The server class that exposes the scheduler

module Scheduler
  class Server
    include ::Scheduler::JobTransformations

    def initialize(scheduler)
      @scheduler = scheduler
      @job_editor = Scheduler::JobEditor.new(@scheduler)
      @lock = Mutex.new
    end

    def cron(recording_schedule)
      @lock.synchronize do
        @job_editor.cron(recording_schedule)
      end
    end

    def at(recording_schedule)
      @lock.synchronize do
        @job_editor.at(recording_schedule)
      end
    end

    def unschedule(job_id)
      @lock.synchronize do
        @job_editor.unschedule(job_id)
      end
    end

    def job(job_id)
      begin
        job = @scheduler.job(job_id)
        job_to_hash(job) if job
      rescue ArgumentError =&amp;gt; e
        nil
      end
    end

    def jobs(job_ids)
      found_jobs = job_ids.map do |id|
        job(id)
      end

      found_jobs.compact
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;# The dRb client initializer.
# In my app this is located at: config/initializers/rufus-scheduler.rb

require &amp;#39;drb&amp;#39;
RUFUS_SCHEDULER_CLIENT = DRbObject.new(nil, &amp;#39;druby://localhost:9000&amp;#39;)

# Note: this must be called at least once per process to take any effect.
# This is particularly important if your application forks.
DRb.start_service
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;# Calling the dRb Client.

class RecordingSchedulesController &amp;lt; ApplicationController
  before_action :authenticate_user!
  before_action :redirect_to_time_zone, if: Proc.new { current_user.time_zone.blank? }

  def index
    @recording_schedules = RecordingSchedule.where(user_id: current_user.id).order(:name).decorate
  end

  def new
    @recording_schedule = RecordingSchedule.new
  end

  def create
    @recording_schedule = RecordingSchedule.new(recording_schedule_params).decorate
    @recording_schedule.user = current_user
    notifier = Services::ScheduleNotifier.new(@recording_schedule, RUFUS_SCHEDULER_CLIENT)

    if notifier.save
      redirect_to recording_schedules_path, flash: { success: &amp;#39;Recording schedule added.&amp;#39; }
    else
      flash.now[:error] = &amp;quot;There were some problems with this recording schedule.&amp;quot;
      render :new
    end
  end

  def destroy
    @recording_schedule = RecordingSchedule.find(params[:id]).decorate
    notifier = Services::ScheduleNotifier.new(@recording_schedule, RUFUS_SCHEDULER_CLIENT)
    flash_options = {}

    if notifier.destroy
      flash_options[:success] = &amp;#39;Recording schedule removed.&amp;#39;
    else
      flash_options[:success] = &amp;#39;Recording schedule not removed.&amp;#39;
    end

    redirect_to recording_schedules_path, flash_options
  end

  def edit
    @recording_schedule = RecordingSchedule.where(id: params[:id], user_id: current_user.id).first
  end

  def update
    @recording_schedule = RecordingSchedule.where(id: params[:id], user_id: current_user.id)
      .first
      .decorate
    notifier = Services::ScheduleNotifier.new(@recording_schedule, RUFUS_SCHEDULER_CLIENT)

    if notifier.save(recording_schedule_params)
      redirect_to recording_schedules_path, flash: { success: &amp;#39;Changes saved.&amp;#39; }
    else
      flash.now[:error] = &amp;quot;There were some problems with this recording schedule.&amp;quot;
      render :edit
    end
  end

  private

  def recording_schedule_params
    strip_blank_from_days_of_week(
      params
        .require(:recording_schedule)
        .permit(:duration, :stop_time, :name, :recurring,
         :start_on, :start_time, :stream_url, days_of_week: []
        )
    )
  end

  def redirect_to_time_zone
    redirect_to time_zone_path
  end

  def strip_blank_from_days_of_week(permittied_params)
    if permittied_params[:days_of_week].present?
      permittied_params[:days_of_week]
        .reject! {|i|i.blank?}
        .collect!(&amp;amp;:to_i)
    end
    permittied_params
  end
end
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;Overall, using dRb for interprocess communication in Ruby proved to be a reliable and efficient solution for my audio recording system. While there are other methods available for interprocess communication in Ruby, dRb&amp;rsquo;s simplicity and ease of use made it a good choice for my use case.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Building a Hot Tub from Scratch</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2023/08/24/building-a-hot-tub.html"/>
    <id>http://www.brandoncasci.com/2023/08/24/building-a-hot-tub.html</id>
    <published>2023-08-25T02:10:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;div&gt;&lt;small&gt;2023-08-24&lt;/small&gt;&lt;/div&gt;

&lt;h1&gt;Building a Hot Tub from Scratch &amp;#x1F6C0;&lt;/h1&gt;

&lt;p&gt;&lt;img src="/2023/08/24/cover.png" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;I went to Iceland a while back, which was fantastic. During my time there, I enjoyed a daily soak in a hot tub.
After a few days of soaking I noticed a lot of nagging aches and pains were gone.
So I thought, damn, I need to get one of these for my house some day. I would shop around, but never pull the trigger.
Finally, in 2019, I made the decision to pursue this.&lt;/p&gt;

&lt;p&gt;I wanted a big round, 4 feet deep cedar hot tub. Now, since this was a pretty expensive purchase,
and I didn&amp;rsquo;t want to be compulsive, I figured I would budget the purchase. January 2020 rolled around,
I was ready to buy, but caught up on which cedar tub manufacturer to buy from, and I had it down to
three. Then February 2020 came, the COVID-19 shut down, and everyone bought every hot-tub, all at once,
and there was an 18 - 24 month wait &amp;hellip;but I wanted a hot tub sooner than that.&lt;/p&gt;

&lt;p&gt;That&amp;rsquo;s when the idea of building one struck me. How hard could it be? So I got to work around October 2020.&lt;/p&gt;

&lt;p&gt;I originally intended to base the hot tub off of a prefabricated water tank, but due to supply chain
issues, I could not get a tank in appropriate dimensions, so I went with a custom built wood basin
with a custom liner. The hot tub related forums had a lot of mis-information on them about what you
can and can&amp;rsquo;t build a basin out of, so I looked into aquafarmer and back-yard fish enthusiast
communities, and picked one of their designs.&lt;/p&gt;

&lt;p&gt;I bought hot tub parts, which are the controller, heater, filter, and other related items
from &lt;a href="https://buildahottub.com/"&gt;buildahottub.com&lt;/a&gt;, Andi over there knows everything about these systems.&lt;/p&gt;

&lt;p&gt;The basic specs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Above ground design&lt;/li&gt;
&lt;li&gt;A base framed with pressure treated timbers and filled in with 0.75 inch packed stone. &lt;/li&gt;
&lt;li&gt;Walls built with standard construction boards, filled with insulation, and lined with plywood and siding.&lt;/li&gt;
&lt;li&gt;Industrial pond liner, custom made, in a box shape&lt;/li&gt;
&lt;li&gt;All necessary of hot tub heaters and controllers&lt;/li&gt;
&lt;li&gt;Plumbing pipe, hoses, connectors&lt;/li&gt;
&lt;li&gt;A standard hot tub cover&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Problem 1 - City Permitting&lt;/h2&gt;

&lt;p&gt;The first wrinkle was the city permitting. I started this process in September of 2020. We are allowed
to have hot tubs, but I needed an electrical service upgrade on the house to accommodate a hot tub.
It took the city about 6 months to sign off, which threw some dependencies off course.
I only interacted with them a little, I didn&amp;rsquo;t get the sense they excelled at customer service and
I&amp;rsquo;m totally fine not having to work with them again. I later learned after speaking to area trades people,
that a significant number of electricians will not take work in my city.&lt;/p&gt;

&lt;h2&gt;Problem 2 - Changing the Construction Location&lt;/h2&gt;

&lt;p&gt;By now it was October. I originally planned to build the hot tub in the place that was to be used,
but since the city was doing what they do best, and the weather was turning, I decided to start the
work on the basin inside. As the weather turned, I opted to begin work on the basin indoors. This
required constructing the basin in a way that it could be disassembled and reassembled in its final
location.&lt;/p&gt;

&lt;h2&gt;Electrical Work Approved &amp;#x26A1;&lt;/h2&gt;

&lt;p&gt;It was now March 2021. The city finally agreed the electric work could start. The delay was over the
location of the electric meters, which usually go outside of the house, but they are inside of ours,
for a good reason. We live in a dense area, and there is no other location they could be at other than the front of the house,
or inside of the basement. They were insisting they had to be relocated outside of the house. 
They didn&amp;rsquo;t want the electric meters in the front of the house because they would block the sidewalk, 
but the sidewalk is 10 feet away. Then they suggested the driveway, but the driveway is between two 
buildings and narrow. A vehicle would have hit the meters. They can&amp;rsquo;t go behind the house,
because of other obstructions. It took several meetings, and input from both the electrician and the
utility company for reality to set in, which means the meters are staying where they are.&lt;/p&gt;

&lt;h2&gt;Problem 3 - Less Than Ideal Basin Liner&lt;/h2&gt;

&lt;p&gt;I chose an industrial pond liner because at the time, it was the only one I could find that could be
custom made into a box shape. Had I known a bit more about what to look for, I could have gotten a
box shaped pool liner made. Both types of liners are durable and will hold water, but the pond liner
isn&amp;rsquo;t compatible with the seals on the through-wall fittings. The through-wall fittings are the
plumbing parts that circulate the water between the basin, filter and heater.&lt;/p&gt;

&lt;p&gt;So after a lot of leaks, I had to come up my own through wall design for this type of tub. On top of
that, I also had to remove the hot-tub jets and replace them with a jet-like construct that I made out
of standard plumbing parts so I could get those to seal correctly.&lt;/p&gt;

&lt;h2&gt;Problem 4 - Unexpected mistakes&lt;/h2&gt;

&lt;p&gt;I don&amp;rsquo;t think many people have built a hot-tub like this, and since this was my first try, I made
mistakes. One of them was making some cuts on the liner. To repair them, I had to learn how to
heat-weld.&lt;/p&gt;

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

&lt;p&gt;Today, I have a fully functioning hot tub that holds water, along with some plumbing skills that
I&amp;rsquo;ve had to use a few times since then.&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_001.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_002.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_003.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_004.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_005.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_006.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_007.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_008.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_009.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_010.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_011.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_012.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_013.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_014.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/2023/08/24/IMG_015.JPG" alt="Building a hot tub" /&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Internationalization with Ruby on Rails with I18n</title>
    <link rel="alternate" href="http://www.brandoncasci.com/2023/08/21/rails-internationalization-i18n.html"/>
    <id>http://www.brandoncasci.com/2023/08/21/rails-internationalization-i18n.html</id>
    <published>2023-08-21T15:43:00+00:00</published>
    <updated>2026-04-07T20:12:41+00:00</updated>
    <author>
      <name>Brandon Casci</name>
    </author>
    <content type="html">&lt;small&gt;2023-08-21&lt;/small&gt;
&lt;h1&gt;
  Internationalization with Ruby on Rails with I18n
&lt;/h1&gt;
&lt;img class="cover" src="/2023/08/21/cover.png" alt="Internationalization with Ruby on Rails with I18n" /&gt;
&lt;p&gt;
  Internationalization is the process of designing and developing web applications that can support multiple languages and locales. This is an important consideration for any web application that needs to reach a global audience, as it allows users to interact with the application in their preferred language and format.
&lt;/p&gt;
&lt;p&gt;
  Ruby on Rails provides built-in support for
  &lt;a href="https://github.com/ruby-i18n/i18n"&gt;I18n&lt;/a&gt;,
  making it easy to create and manage translations for different languages. The humanization helpers of ActiveRecord and ActiveModel I18n, and you can leverage this to make you application support multiple languages, and to also humanize your object names and attributes to generate human readable content and forms.
&lt;/p&gt;

&lt;p&gt;
  Take the following two example screens from &lt;a href="https://www.cboflow.com"&gt;www.cboflow.com&lt;/a&gt;, a Rails application that I maintain.
  The first is in English, and the second is in Spanish. Using the strategies outlined in this article, I was able to make this application support multiple languages with minimal effort.
&lt;/p&gt;

&lt;article class='image'&gt;
  &lt;header&gt;English Screen&lt;/header&gt;
   &lt;img src="/2023/08/21/english.png" alt="" /&gt;
&lt;/article&gt;
&lt;article class='image'&gt;
  &lt;header&gt;Spanish Screen&lt;/header&gt;
   &lt;img src="/2023/08/21/spanish.png" alt="" /&gt;
&lt;/article&gt;

&lt;h2&gt;I18n and Rails&lt;/h2&gt;

&lt;p&gt;
  A new Rails application is configured to use I18n by default.
&lt;/p&gt;

&lt;p&gt;
  All of the translations are stored in resource files within the project's
  &lt;code&gt;config/locales&lt;/code&gt; directory. Each file has to end with a language code, and
  &lt;code&gt;.yml&lt;/code&gt;. So for example, you might have a file named &lt;code&gt;config/locales/en.yml&lt;/code&gt;,
  with the following content:
&lt;/p&gt;

&lt;pre&gt;
  &lt;code&gt;
      en:
        hello: "Hello, world!"
  &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
  In this folder, you can have as many subfolder as you like, and prefix the files as you wish.
  Some people like to have one large locale file for each supported language, while others like
  to have one file per object or view. For example, keeping all content in  &lt;code&gt;config/locales/en.yml&lt;/code&gt;,
  or breaking the content up into several files like &lt;code&gt;config/locales/author.yml&lt;/code&gt;
  and &lt;code&gt;config/locales/post.yml&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;
  The
  &lt;a href="https://guides.rubyonrails.org/i18n.html"&gt;Rails Guide&lt;/a&gt;
  has a lot of information about using this feature.
&lt;/p&gt;

&lt;h2&gt;How I use I18n in my own Rails applications&lt;/h2&gt;

&lt;p&gt;
  I try to use the same lingo across my applcations.
&lt;/p&gt;

&lt;p&gt;
  I will create one locale file for each object that I intent to humanize in a view.
&lt;/p&gt;

&lt;p&gt;
  So if I have a &lt;code&gt;Person&lt;/code&gt; model, I'll have a &lt;code&gt;person.en.yml&lt;/code&gt; file,
  and more files for other languages like &lt;code&gt;peroson.es.yml&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  The default humanization for an attribute name is the same as &lt;code&gt;String#humanize&lt;/code&gt;,
  so &lt;code&gt;Person#first_name&lt;/code&gt; will become &lt;code&gt;First name&lt;/code&gt; be default, and if I am happy
  with that, I probably will not make a translation entry.
&lt;/p&gt;

&lt;p&gt;
  I also tend to use gems like
  &lt;a href="https://github.com/heartcombo/simple_form"&gt;simple_form&lt;/a&gt;
  to generate my form HTML, and this saves me from having to maintain a lot of view code for outputting
  translated content onto forms. Also simple_form has it's own i18n convention that compliments
  the Rails default pretty well.
&lt;/p&gt;

&lt;pre&gt;
  &lt;code&gt;
  # person.en.yml
  en:
    activerecord:
      models:
        person:
          one: Person
          other: People
    attributes:
      person/hispanic:
        true: 'Yes'
        false: 'No'               
      errors:
        models:
          person:
            attributes:
              birth_date:
                must_be_in_the_past: "can't be before today"
                must_be_formatted_correctly: "must be formatted like yyyy-mm-dd"
              full_name:
                blank: "can't be blank"
              email:
                invalid: "is not valid. It should look like name@domain.com"
              phone_number:
                invalid: "is not valid. It should look like 123-456-7890"
  &lt;/code&gt;
&lt;/pre&gt;


&lt;pre&gt;
  &lt;code&gt;
  # person.es.yml
  es:
    activerecord:
      models:
        person:
          one: "Persona"
          other: "Personas"
      attributes:
        person:
          birth_date: "Fecha de nacimiento"
          email: "Correo electrónico"
          full_name: "Nombre completo"
          hispanic: "Hispano"
          personal_information: "Información personal"
          phone_number: "Número de teléfono"
          self_described_gender: "Género autodescrito"
          self_described_race: "Raza autodescrita" 
        person/hispanic:
          true: "Sí"
          false: "No"
      errors:
        models:
          person:
            attributes:
              birth_date:
                must_be_in_the_past: "no puede ser antes de hoy"
                must_be_formatted_correctly: "debe tener el formato yyyy-mm-dd"
              full_name:
                blank: "no puede estar en blanco"
              email:
                invalid: "no es válido. Debe parecerse a nombre@dominio.com"
              phone_number:
                invalid: "no es válido. Debe parecerse a 123-456-7890"
  &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
  Gems like &lt;code&gt;simple_form&lt;/code&gt; will label your forms with the correct languages,
  but you will have to call the model humanization helpers when outputting other types of content.
  You can also clean this up with your own helpers or presentation objets.
&lt;/p&gt;

&lt;p&gt;
  You manually pull transations like this:
&lt;/p&gt;

&lt;pre&gt;
  &lt;code&gt;
  Person.human_attribute_name(:first_name) # -&gt; First name
  Person.model_name.human # -&gt; Person
  Person.model_name.human(count: 2) # -&gt; People
  Person.human_attribute_name("hispanic.#{person.hispanic}") %&gt; # -&gt; Yes
  &lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Humanizing Context and Service Objects with I18n.&lt;/h2&gt;

&lt;p&gt;
  Sometimes it will make sense to humanize one object's names and attributes with a totally new name in different parts of an app. For example, maybe it makes sense on one screen to refer to Person ans Profile, or Household Member. 
&lt;/p&gt;

&lt;p&gt;
  Sometimes it also makes sense to wrap a sequence of steps into a service object that someone can
  initiate by filling out a form and clicking a button.
&lt;/p&gt;

&lt;p&gt;
  I'll use something like what's below to accomplish that.
&lt;/p&gt;

&lt;p&gt;
  Now you can do things like &lt;code&gt;Clients::ChangeProfile.model_name.human&lt;/code&gt; or
  &lt;code&gt;Clients::ChangeProfile.human_attribute_name(:first_name)&lt;/code&gt;.
&lt;/p&gt;

&lt;pre&gt;
&lt;code&gt;
module Clients
  class ChangeProfile &lt; BaseCommand
    attribute :full_name, :string
    attribute :birth_date, :date
    attribute :email, :string
    attribute :phone_number, :string
    attribute :hispanic, :boolean, default: false
    attribute :race_list, default: []
    attribute :self_described_race, :string
    attribute :gender_list, default: []
    attribute :self_described_gender, :string
    attribute :user

    validates :user, presence: true


    def run
      context = {}
      validate!
      context[:person] = person

      # While the implementation is not shown in this code sample, the attributes
      # for this ActiveModel based Command/Service Object are written across
      # several models within a transaction
      save!

      result(true, **context)
    rescue ActiveRecord::RecordInvalid, ActiveModel::ValidationError
      rollup_errors(person) if person
      result(false, **context)
    end

    # overriding model name here, so this command can be bound to a form
    # with ActionView and humanized with full I18n support

    def self.model_name
      ActiveModel::Name.new(self, nil, 'Profile')
    end

    # other implementation details and methods
    # ...
    # ...
    # ...
  end
end
&lt;/code&gt;
&lt;/pre&gt;

&lt;pre&gt;
&lt;code&gt;
en:
  activerecord:
    models:
      person:
        one: Person
        other: People
      clients/profile:
        one: Profile
        other: Profiles
      household_member:
        one: Household Member
        other:  Household Members        
    attributes:
      person/hispanic:
        true: 'Yes'
        false: 'No'
      
    errors:
      models:
        person:
          attributes:  &amp;person_error_attributes
            birth_date:
              must_be_in_the_past: "can't be before today"
              must_be_formatted_correctly: "must be formatted like yyyy-mm-dd"
            full_name:
              blank: "can't be blank"
            email:
              invalid: "is not valid. It should look like name@domain.com"
            phone_number:
              invalid: "is not valid. It should look like 123-456-7890"
        household_member:
          attributes:
            &lt;&lt;: *person_error_attributes

&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Navigational links and contexual information&lt;/h2&gt;

&lt;p&gt;
  I may place all of my navigational and contextual information into a locale file like what's below.
&lt;/p&gt;

&lt;p&gt;
  This enables me to do things like this:
&lt;/p&gt;

&lt;pre&gt;
&lt;code&gt;
  &lt;%= link_to t(:add_action, subject: Person.model_name.human), new_manage_client_care_distribution_path, class: 'btn btn-info' %&gt;
&lt;/code&gt;
&lt;/pre&gt;


&lt;pre&gt;
&lt;code&gt;
en:
  add: Add
  add_action: "Add %{subject}"
  all: All
  back: Back
  basic_information: Basic Information
  client_profile: Client Profile
  confirm: Are you sure?
  empty_search: "No %{subject} found"
  delete: Delete
  edit: Edit
  hello: "Hello world"
  history_action: "%{subject} History"  
  insights: Insights
  invite_action: "Invite %{subject}"  
  loading: Loading...
  provide_action: "Provide %{subject}"
  missing_object: "No %{subject}"
  new: New
  new_action: "New %{subject}"
  register_action: "Register %{subject}"
  summary: Summary
  view: View
  view_action: "View %{subject}"
&lt;/code&gt;
&lt;/pre&gt;

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

&lt;p&gt;
  I18n is powerful and flexible, and really makes it easy to humanize your applications.
  There are other complexities that I have not covered here, like right-to-left languages, supporting
  many languages at once, and long form content, but I hope this article has given you a good starting point.
&lt;/p&gt;
</content>
  </entry>
</feed>
