Posts in Category: Coding

Trustworthy AI coding: writing and implementing plans

I haven’t posted in a while, as I’ve been working on several Claude Code skills to share. I’ve made my claude-plugins marketplace publicly available. There’s a lot there, and I’ll be covering them in a series of posts.

I’ll start with the write-plan and implement-plan skills. I wrote a post on iterative planning three months ago but it feels like a million years ago. A lot has changed since then. My argument back then was that no amount of up front planning and research was going to catch every problem, so letting Claude execute a plan without supervision did not seem wise to me. That reflected my experience at the time, working in both greenfield projects and large legacy codebases. Since then, the Claude Code models have kept improving, and my ability to coax increasingly robust work out of them has also steadily improved. My updated plan skills reflect that learning. I was also inspired by the RubyConf talk I wrote about earlier, about making AI coding output trustworthy by default, as well as other similar talks and articles.

Benefits of using write-plan

  1. Each step is small. The write-plan skill relies on my organize-commits skill to ensure each step is a small, and cohesive change. This makes code review easier for both humans and AI agents.
  2. After the plan is drafted, it gets an independent review. Doing code reviews is important, and doing plan reviews is equally important. First, the main session double-checks that the plan isn’t missing any decisions from the conversation. Then a subagent reads the plan cold, with no conversation context from the main session. It verifies the claims of the plan (e.g. how the existing code behaves, etc) and checks for gaps and inconsistencies. It then reports back to the main session with recommended corrections. If anything requires a decision from you, it asks. I’m always amazed at how much it finds, no matter how carefully the plan was drafted.
  3. You can specify how much you want to be engaged with the implementation. A plan has three options for an approval mode:
    • each step: you approve every change
    • before push: you review before a PR is created
    • draft PR: a Claude subagent does a “cold” review using my pr-review skill, the main session addresses any findings, and then opens a draft PR for your review

    If the plan includes a step where a decision is required from you, Claude will stop to ask you for it (such as a decision that can only be made after some data analysis), in any approval mode.

Benefits of using implement-plan

  1. Each step runs in a fresh subagent, so the session for the main agent stays small. A plan can involve many steps, and if the implementation is running unsupervised, you want to minimize how far the main session might get into the dumb zone. The main session instructs the subagents and gets reports of their work, rather than doing the work itself. So its judgment stays sharp across a long plan. The subagents cannot edit the plan and cannot commit. The main session keeps control of both.
    • There is one exception to this: if a change is small, the main session agent will do it, rather than incurring the start-up token costs of a subagent for minor work.
  2. It leverages the intelligence of the subagents. You can use a specialized coding agent if you have one, such as my rails-core agent. Departures from the plan are allowed, within the realm of implementation details. Just like a human coder, when a subagent starts implementing, it will notice things that may have been missed during planning, and it’s allowed to adjust accordingly. The subagent reads the whole plan, including the introductory Context section, so it knows about the decisions that informed the plan. At the end of each step, it reports any departures from the plan. It also reports any questions that came up, and if for any reason it gets stuck and cannot proceed after two attempts at fixes, it will stop and report the situation (I’ve never had this happen, but it’s there as a failsafe so the agent doesn’t churn and burn excessive tokens).
  3. Each steps ends with the main session doing a series of checks and updates:
    • Running tests and linters for the code that was changed
    • Optionally running a separate review subagent
    • Updating the status of the plan
    • Commiting the changes (it uses my commit-messages skill to write clear and concise messages, focusing on “the why” of the change, instead of reciting what was changed).
  4. Plan progress is documented. Each step is marked with a ✅ when it’s done, so your progress on the plan is clear. After completing a step you can stop and resume later if needed.
  5. It checks project documents and cleans up when it’s done. it reads CLAUDE.md, README.md, and any other standing documents and makes any needed changes (with your approval). It trims the implementation detail from the plan file, but keeps its Context section and a short record of each step, as they may be useful for future reference (you can of course delete them if you prefer).

The workflow for using the skills

  1. Determine the goals and scope. The write-plan skill is literally for writing the plan, so you will want to discuss the goals of the work before invoking it. I highly recommend Matt Pocock’s grilling and grill-with-docs skills to flush out all the details and possible gaps in what you want to do. Start the grilling session with a brain dump of what you want to do. The grilling skill then interviews you with a series of questions that drive you to reach a “mutual understanding” of the work to be done. I recommend scoping the conversation to what you would put in typical pull request. Alternately, if you are planning a large project, you can keep the conversation at a high level and write an “epic” level plan, which will encompass several smaller plans that you’ll flesh out later.
  2. Write the plan. This part is easy, as the write-plan skill will take the results of the grilling session and organize it into a well defined, step by step plan. The first time you use the skill, it will ask you where you prefer to save plan files, and it will record your preference per project. I’m not a fan of Claude’s default ~/.claude/plans directory, as I like to keep my plans with the relevant project, but it’s up to you.
  3. Compact or clear the session. The grilling and plan writing will use up a good chunk of a session’s context. Clearing or compacting the session after writing the plan gives you a fresh context for implementing the plan.
  4. Implement the plan! As noted above, it will follow the approval mode you selected for the plan.

Pairing with AI #5: making AI coding output trustworthy by default

The talk at RubyConf that made the biggest impression on me was Alicia Rojas’ about making the transition from humans reviewing AI written code, to AI also being responsible for all code review (a “dark factory,” although she doesn’t use that term). Where many of us are now (including me) is optimizing our AI workflow to write code better and faster, but now our bottleneck is human code review. She talks through the transition her team made and the framework they developed for AI to also do the code reviews. It’s an excellent and substantial talk, not hype.

Pairing with AI #4: my RubyConf talk

Video from my RubyConf talk on AI assisted coding is live! You can also see the slides.

My inspiration for the talk was all the advice out there saying you really have to do X, Y, or Z with AI coding. But what’s actually best depends on your context. To prepare for the talk, I interviewed several people working with AI in different kinds of organizations – from small startups to large legacy codebases – to get their perspectives.

Pairing with AI #3: iterative planning (or, no plan survives first contact with the code)

ℹ️ See my write-plan and implement-plan skills.

Update August 23, 2026: I’ve updated the skills. The updates are about the relationship between the plan file and a project’s standing documents (like CLAUDE.md). Details are in this comment on the write-plan gist and this one for implement-plan.

The pre-AI coding era: BDUF (big design up front) vs iterative planning

I spent the first 10 years or so of my career (going back to the 90s here, folks) in the Waterfall/BDUF era: long meetings discussing endless hypothetical scenarios, big planning documents covering months of work, missed deadlines, and deliverables that typically ended up misaligned with actual needs. The reasons for this frequent misalignment are now obvious, since our industry has matured since then (at least in some ways):

  1. There was no feedback until we wheeled out our giant project at the end, and by then it was too late.
  2. In planning, what people tell you they want is often not what they actually want or need (they may think they know, but since there’s no software yet for them to interact with, they’re guessing, and doing their best to answer all your planning questions).

This is what spawned the Agile movement, which started to gain traction in the early 2000s, and at the time it felt revolutionary. It turned Waterfall on its head, with short development and deliverable cycles and frequent feedback (Agile has unfortunately accreted its own layers of bureaucracy since then, but that’s another story).

The AI coding era: BDUF is back, but should it be?

In our new era of AI-assisted coding, BDUF is back: we can now generate planning documents and code faster and cheaper, so why not? You can develop your plan, walk away while AI works on it for a few hours, and then deliver. If what you delivered ends up needing changes, repeat the process. This is how the popular Superpowers skill for executing plans works (and most other AI planning skills I’ve seen): “Load plan, review critically, execute all tasks, report when complete.”

Pairing with AI #2: fixing Opus 5’s unintelligible output

ℹ️ See my ASD-STE100 (Simplified Technical English) output style for Claude Code

This post was originally going to be about AI-assisted planning, but if you’re a Claude Code user, you may be experiencing the same frustration I have recently with Opus 5: it’s often incomprensible. Others have been frustrated also. Matt Pocock posted the other day: “Christ, Opus, talk like a normal person. I built this app and I have no idea what it’s saying.”

Sometimes it veers from extremely dense technical language to just bizarre. Opus served up this gem while I was doing a PR review the other day (the code under review provides alternative text suggestions):

…Then a permissions misconfiguration degrades to “you don’t get the 👀 and the summary” instead of “you don’t get the review.” The suggestions are the product; the acknowledgment is garnish, and garnish shouldn’t be able to take the meal down.

So the suggestions are the product, which I think Opus is saying is also the meal, which apparently can be ruined by a permissions misconfiguration. Oh wait, does it mean it’s actually the acknowledgements (the garnish) that are the problem? And what is the 👀 emoji referring to?… What are we talking about again? 😵‍💫

Pairing with AI #1: brainstorming

ℹ️ Update August 23, 2026: I wrote this post a month ago, which is a long time in the AI coding world, and I have an updated recommendation: always use the grilling (or grill-with-docs) skill instead of brainstorm. Matt Pocock has recently updated grilling. I compared it head-to-head with the Superpowers brainstorm using a few different kinds of initial prompts, and consistently got a more thorough interview from grilling.

Typically the first step in my Claude Code assisted workflow is getting a rough idea ready for planning. Not the plan itself, but brainstorming any aspects I haven’t thought through yet, or identifying possible gaps. Popular skills for this are grilling and grill-with-docs from Matt Pocock’s “AI Hero” skills and brainstorming from the Superpowers skills. I’ve worked with both and they each are suited to different needs:

RailsConf 2018 highlights

I drafted this post after RailsConf in 2018, and very belatedly realized I never published it. So I’m finally getting around to it 😉. Reviewing it now, 8 years later, I’m pleasantly surprised at how relevant most of the talks still are.

Below are my favorite talks that I attended at RailsConf 2018. Since there were multiple tracks, there were plenty of talks I didn’t have the opportunity to see. For highlights shared by others, check out RailsConf 2018 – Top 10 Favorite Talks (from Al Tenhundfeld), Our Favorite Ruby on Rails Talks from RailsConf 2018 (Planet Argon), and Five things I learned at RailsConf 2018 (Stephen Giles). There’s also a full playlist of the RailsConf 2018 talks if you might to see even more.

This was my first time in Pittsburg, so I took some time out to walk around and see some of the city, so there are more pictures below too.

RSpec: 5 rules for using let effectively

let can enhance readability when used sparingly (1, 2, or maybe 3 declarations) in any given example group, but that can quickly degrade with overuse.
— RSpec Official Documentation

Motivation

I’ve been using Rspec since 2012 and in all this time I’ve never had a really clear picture of how to best use let. I don’t have this issue with any other aspect of Rspec.

Probably our most common Rubocop violation where I work is MultipleMemoizedHelpers (too many let calls). In previous consulting work, I’ve seen this in many other codebases as well.

What to do about it? On one side, you have Thoughtbot (the creators of FactoryBot) and other prominent members of the community arguing that you should never use let, because of the tangled messes they often see with let overuse in large codebases (eliminating let usage isn’t a practical option for us, given our multiple large legacy Rails applications and many teams). On the other side, you have the well regarded Better Specs site recommending let and of course the Rspec docs themselves, but their examples only cover simple cases. Then in the middle, there’s the quote at the top of this page: a somewhat cryptic comment tucked away in the Rspec code documentation, cautioning to use let sparingly.

But what does it mean to use let sparingly? What is the right strategy for reducing the number of MultipleMemoizedHelpers violations? Why should I care? After a lot of research, focusing primarily (but not solely) on advice from Rspec maintainers that I found in various corners of the internet, I formulated the 5 rules below to answer these questions.

Much of the advice in these rules is really about good habits in general with writing tests, through the lens of using let effectively. let is a tool. It’s up to you how to use it.

ℹ️ Note there’s a Claude Code skill waiting for you at the end of the post, so Claude will know how to use let effectively too.

Rails views, internationalization, special characters, and testing with Rspec

The problem

File this under small problems that take more time than they should to solve, and I couldn’t find an answer with a web search.

Let’s use a simple example. If you have text like this in your translation file (e.g. en.yml):

users:
  new:
    header: "Let's go!"

And then show it in a view template (e.g. app/views/users/new.html.erb):

<h3><%= t('.header') %></h3>

And then try to match it in an Rspec test, you’ll get an error that it couldn’t be found:

expect(response.body).to include(I18n.t('users.new.header'))

Failure/Error: expected "[...]Let&#39;s go![...]" to include "Let's go!"

Rspec with Rails 7 and System Tests

Hello world! It’s time for my first post in over 4 years.

I recently set up a new Rails 7 project with Rspec and looked online for tips, as one does. I’ve set up many Rails projects before, but not yet with Rails 7, and it’s been a while. The top result in Google for “rails 7 with rspec” is currently Adrian Valenzuela’s Setup RSpec on a fresh Rails 7 project. His post was really helpful for me shaking off the rust. So rather than writing another post that’s 80% the same, I’ll just share a few additional tips. Think of this post as a companion piece to Valenzuela’s.