<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thoughtbot="https://thoughtbot.com/feeds/">
  <title>Giant Robots Smashing Into Other Giant Robots</title>
  <subtitle>Written by thoughtbot, your expert partner for design and development.
</subtitle>
  <id>https://robots.thoughtbot.com/</id>
  <link href="https://thoughtbot.com/blog"/>
  <link href="https://feed.thoughtbot.com" rel="self"/>
  <updated>2026-04-20T00:00:00+00:00</updated>
  <author>
    <name>thoughtbot</name>
  </author>
<entry>
  <title>Ship faster: How to unlock development speed in regulated industries </title>
  <link rel="alternate" href="https://thoughtbot.com/blog/ship-faster-how-to-unlock-development-speed-in-regulated-industries"/>
  <author>
    <name>Michelle Taute</name>
  </author>
  <id>https://thoughtbot.com/blog/ship-faster-how-to-unlock-development-speed-in-regulated-industries</id>
  <published>2026-04-20T00:00:00+00:00</published>
  <updated>2026-04-16T19:10:23Z</updated>
  <content type="html">&lt;p&gt;A lot of teams assume moving faster means hiring more developers or adopting the latest framework. But in our experience, the biggest drag on development speed is usually upstream. It’s how decisions get made, the way priorities are set, and whether there’s alignment (or misalignment) across teams.&lt;/p&gt;

&lt;p&gt;In heavily regulated industries like healthcare, the instinct is often to add more process when things slow down: more documentation, signoffs, and checkpoints. It makes sense on the surface, but what’s frequently missing is a shared way of working.&lt;/p&gt;

&lt;p&gt;We saw this firsthand when one of our clients expanded their offerings into the healthcare services space. When we came on board, teams had been missing deadlines, cross-team alignment was breaking down, and a new product initiative was stalling. Here’s how we helped them navigate change to overhaul a waterfall process and increase speed.&lt;/p&gt;
&lt;h2 id="the-waterfall-problem-more-documentation-less-speed"&gt;
  
    The waterfall problem: More documentation, less speed
  
&lt;/h2&gt;

&lt;p&gt;The most entrenched issue was waterfall culture. Before any coding could begin, product managers were producing lengthy, detailed product requirements documents (PRDs) that tried to anticipate every decision upfront. In regulated industries, there’s real cultural pressure to have everything documented and approved before moving. But those detailed docs become obsolete almost immediately, and they tend to become political artifacts. They’re things teams negotiate over rather than build from.&lt;/p&gt;

&lt;p&gt;When we embedded with this client, we broke that pattern deliberately. Rather than exhaustive PRDs, we kept requirements lightweight and iterated. A brief requirement went to the design lead and technical lead first. This allowed them to poke holes and surface unknowns before a developer touched anything.&lt;/p&gt;

&lt;p&gt;This was a political change as much as an operational one. Convincing stakeholders to adopt a lighter-touch approach required earning trust fast. To do this, we shipped early and visibly, and this success helped create buy-in around the process changes.&lt;/p&gt;
&lt;h2 id="start-by-getting-the-right-people-in-the-room"&gt;
  
    Start by getting the right people in the room
  
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of code, we typically &lt;a href="https://thoughtbot.com/blog/rapid-prototyping-with-claude-code-how-we-transformed-our-design-sprint-process"&gt;run a design sprint&lt;/a&gt;) with our clients. The first goal is alignment: We identify the actual decision makers, clarifying who has the final call, and establish an executive sponsor. This clarity helps prevent political friction that can derail cross-team initiatives later.&lt;/p&gt;

&lt;p&gt;With this client, there were multiple senior product managers overseeing their own roadmaps with little cross-team coordination. We met with the executive team and asked direct questions about who ultimately owned decisions. This helped us understand the management layers above where the work happened, so we knew who could help us move things forward.&lt;/p&gt;

&lt;p&gt;From there, we facilitated a &lt;a href="https://thoughtbot.com/playbook/designing/design-sprints/03-understand-exercises/problem-statement"&gt;problem statement exercise&lt;/a&gt;). The goal was deceptively simple: define who has the problem, what the problem really is, and why it matters. We kept refining it until the group landed on something they could rally around.&lt;/p&gt;

&lt;p&gt;That statement lived at the top of every &lt;a href="https://thoughtbot.com/blog/free-resource-product-design-sprint-figjam-template"&gt;FigJam board&lt;/a&gt; for the duration of the project. Whenever a new request surfaced, we’d ask: Does this address the problem we agreed to solve? It gave everyone a principled basis for saying no.&lt;/p&gt;
&lt;h2 id="make-the-big-picture-visible"&gt;
  
    Make the big picture visible
  
&lt;/h2&gt;

&lt;p&gt;One exercise that consistently generates cross-organizational buy-in is mapping the entire critical path, including every touchpoint, product team, and dependency, on a single board.&lt;/p&gt;

&lt;p&gt;With this client, we mapped the entire operating picture: every product, every team, every place the new work would borrow resources or touch existing systems. Invisible interdependencies became suddenly obvious. PMs who’d been quietly absorbing off-roadmap requests suddenly had evidence to escalate.&lt;/p&gt;
&lt;h2 id="change-management-is-part-of-the-work"&gt;
  
    Change management is part of the work
  
&lt;/h2&gt;

&lt;p&gt;Transitioning away from waterfall in a regulated industry isn’t just a process problem; it’s a change management problem. The documentation, approval chains, and signoffs exist for many reasons. The goal isn’t to abandon rigor; it’s to be rigorous about the right things at the right time.&lt;/p&gt;

&lt;p&gt;As an outside consulting team, we have a structural advantage. We’re not protecting our careers inside the organization. We can push back on scope, name the political dynamics slowing things down, and advocate for PMs getting squeezed between competing priorities. These are conversations internal teams often can’t have. We used that position deliberately with this client.&lt;/p&gt;
&lt;h2 id="what-we-leave-behind"&gt;
  
    What we leave behind
  
&lt;/h2&gt;

&lt;p&gt;We closed out this engagement by establishing a lightweight cross-team coordination structure. It was an as-needed touchpoint where all teams with a stake in a shared initiative could surface blockers, get decisions made, and flag dependencies before they became problems. It gave individual PMs a direct line to executive attention rather than things stalling at the manager level. While we modeled less process overall, we also left behind some techniques like this to help clear blocks as needed.&lt;/p&gt;

&lt;p&gt;The goal of any engagement isn’t just to ship code. It’s to model a way of working that teams carry forward long after we’ve handed things off.&lt;/p&gt;

&lt;p&gt;Ready to help your team move faster? &lt;a href="https://thoughtbot.com/services"&gt;Let’s design and develop great products while leveling up your team.&lt;/a&gt;&lt;/p&gt;

&lt;aside class="related-articles"&gt;&lt;h2&gt;If you enjoyed this post, you might also like:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/debugging-why-your-specs-have-slowed-down"&gt;Debugging Why Your Specs Have Slowed Down&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/from-idea-to-impact-the-role-of-rapid-prototyping-in-agetech"&gt;From idea to impact: The role of rapid prototyping in AgeTech&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/theme-based-iterations"&gt;Theme-Based Iterations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content>
  <summary>Slow development isn’t always a code problem. It’s often a process one, especially in regulated industries. Here’s how navigating change to embrace a shared agile process helped one of our clients start finishing ahead of schedule.</summary>
  <thoughtbot:auto_social_share>true</thoughtbot:auto_social_share>
</entry>
<entry>
  <title>PII filtering for RubyLLM with Top Secret</title>
  <link rel="alternate" href="https://thoughtbot.com/blog/ruby-llm-top-secret"/>
  <author>
    <name>Steve Polito</name>
  </author>
  <id>https://thoughtbot.com/blog/ruby-llm-top-secret</id>
  <published>2026-04-17T00:00:00+00:00</published>
  <updated>2026-04-16T18:23:15Z</updated>
  <content type="html">&lt;p&gt;If you’re building LLM-powered features in a regulated industry, sending
unfiltered PII to a third-party provider isn’t just risky, it may violate
compliance requirements like HIPAA or GDPR.&lt;/p&gt;

&lt;p&gt;That’s why we originally built &lt;a href="https://thoughtbot.com/blog/top-secret"&gt;Top Secret&lt;/a&gt;. However, when we first
released it, &lt;a href="https://rubyllm.com"&gt;RubyLLM&lt;/a&gt; was still in its early days, and I found I was
working with provider APIs directly, such as &lt;a href="https://github.com/alexrudall/ruby-openai"&gt;Ruby OpenAI&lt;/a&gt; or
&lt;a href="https://github.com/openai/openai-ruby"&gt;OpenAI Ruby&lt;/a&gt;. This meant I needed to manually orchestrate the
filtering and restoration process, which looked something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"openai"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"top_secret"&lt;/span&gt;

&lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenAI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="n"&gt;original_messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"Ralph lives in Boston."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"You can reach them at ralph@thoughtbot.com or 877-976-2687"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Filter all messages&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TopSecret&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;role: &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;user_messages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;chat_completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;model: :"gpt-5"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chat_completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;

&lt;span class="c1"&gt;# Restore the response from the mapping&lt;/span&gt;
&lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapping&lt;/span&gt;
&lt;span class="n"&gt;restored_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TopSecret&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FilteredText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;:).&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Even with RubyLLM, there was no way to filter and restore conversation
history, since &lt;code&gt;Chat#ask&lt;/code&gt; handles the message life cycle internally.&lt;/p&gt;
&lt;h2 id="enter-rubyllmtopsecret"&gt;
  
    Enter RubyLLM::TopSecret
  
&lt;/h2&gt;

&lt;p&gt;The introduction of &lt;a href="https://github.com/thoughtbot/ruby_llm-top_secret"&gt;RubyLLM::TopSecret&lt;/a&gt; aims to make
filtering sensitive information from RubyLLM chats as simple as possible. All
you need to do is wrap your existing in-memory chats with
&lt;code&gt;RubyLLM::TopSecret.with_filtering&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RubyLLM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TopSecret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_filtering&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyLLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"My name is Ralph and my email is ralph@thoughtbot.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;# The provider receives: "My name is [PERSON_1] and my email is [EMAIL_1]"&lt;/span&gt;
  &lt;span class="c1"&gt;# The response comes back with placeholders restored:&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; "Nice to meet you, Ralph!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Working with &lt;a href="https://rubyllm.com/rails/"&gt;ActiveRecord-backed chats&lt;/a&gt; is even easier. All
you need to do is add the &lt;code&gt;acts_as_filtered_chat&lt;/code&gt; class macro, and the gem will
take care of the rest.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;acts_as_chat&lt;/span&gt;
  &lt;span class="n"&gt;acts_as_filtered_chat&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you want more granular control, you can simply pass an &lt;code&gt;if:&lt;/code&gt;
condition. For example, you might only need to filter customer-facing
chats that process health data, but not internal admin conversations. A
simple way to model this would be to add a &lt;code&gt;filtered&lt;/code&gt; boolean column to
the &lt;code&gt;chats&lt;/code&gt; table:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;acts_as_chat&lt;/span&gt;
  &lt;span class="n"&gt;acts_as_filtered_chat&lt;/span&gt; &lt;span class="ss"&gt;if: :filtered?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In either case, the goal is to create an opt-in approach that just works. If you want to see it in action, check out this &lt;a href="https://youtu.be/gYz78UYYoLU"&gt;live demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re interested in exploring the gem yourself, follow the
&lt;a href="https://github.com/thoughtbot/ruby_llm-top_secret?tab=readme-ov-file#installation"&gt;installation&lt;/a&gt; guide and refer to the &lt;a href="https://github.com/thoughtbot/ruby_llm-top_secret?tab=readme-ov-file#usage"&gt;usage&lt;/a&gt; section for
a more detailed overview.&lt;/p&gt;

&lt;aside class="related-articles"&gt;&lt;h2&gt;If you enjoyed this post, you might also like:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/anonymizing-user-company-and-location-data-using"&gt;Anonymizing User, Company, and Location Data Using Faker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/switching-from-env-files-to-rails-credentials"&gt;Switching from ENV files to Rails Credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/how-to-use-chatgpt-to-find-custom-software-consultants"&gt;How to Use ChatGPT to Find Custom Software Consultants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content>
  <summary>Automatically filter sensitive information from your RubyLLM conversations before it reaches third-party providers.</summary>
  <thoughtbot:auto_social_share>true</thoughtbot:auto_social_share>
</entry>
<entry>
  <title>Giant Robots Podcast Ep 608:  Project Updates with Will, Chad and Sami</title>
  <link rel="alternate" href="https://thoughtbot.com/blog/giant-robots-podcast-ep-608-project-updates-with-will-chad-and-sami"/>
  <author>
    <name>Chad Pytel, Will Larry &amp;amp; Sami Birnbaum</name>
  </author>
  <id>https://thoughtbot.com/blog/giant-robots-podcast-ep-608-project-updates-with-will-chad-and-sami</id>
  <published>2026-04-16T00:00:00+00:00</published>
  <updated>2026-04-16T14:17:45Z</updated>
  <content type="html">Our hosts provide a peek behind the scenes as they discuss their work on recent thoughtbot projects.</content>
  <summary>Our hosts provide a peek behind the scenes as they discuss their work on recent thoughtbot projects.</summary>
  <thoughtbot:auto_social_share>false</thoughtbot:auto_social_share>
</entry>
<entry>
  <title>The AI stack I developed to help me context switch</title>
  <link rel="alternate" href="https://thoughtbot.com/blog/the-ai-stack-i-developed-to-help-me-context-switch"/>
  <author>
    <name>Daniel Kim</name>
  </author>
  <id>https://thoughtbot.com/blog/the-ai-stack-i-developed-to-help-me-context-switch</id>
  <published>2026-04-16T00:00:00+00:00</published>
  <updated>2026-04-15T20:29:22Z</updated>
  <content type="html">&lt;p&gt;As a product manager, my weeks are defined by context switching. Between working on a client project, internal initiatives, leadership meetings, 1:1s, &lt;a href="https://thoughtbot.com/playbook/our-company/time"&gt;investment time&lt;/a&gt;, or personal development, there’s a lot to hold at once. Each of those contexts has its own history, to-do lists, open questions, follow ups, and stakeholders.&lt;/p&gt;

&lt;p&gt;The cognitive overhead of holding all of that while still being able to show up fully prepared used to be genuinely exhausting.&lt;/p&gt;

&lt;p&gt;This is a quick guide to the system I’ve built to manage it. Of course this is a blog post about AI and I run the risk of sounding like a “996-vibe-codez-all-day” fanatic. But at the very least, it’s made a tangible difference in how present and prepared I feel day-to-day, and I hope some of the concepts or processes can be helpful to you too.&lt;/p&gt;

&lt;p&gt;Also, remember, like anything in life (and especially in product development) it’s not perfect, it’s still evolving, and my naming schemes are definitely corny. Enjoy.&lt;/p&gt;
&lt;h2 id="the-three-tools"&gt;
  
    The three tools
  
&lt;/h2&gt;

&lt;p&gt;The system runs on three tools working together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://workspace.google.com/solutions/ai/ai-note-taking/"&gt;Gemini Meeting Notes&lt;/a&gt; (or your AI meeting note taker of choice): auto-generates transcripts and structured summaries of every Google Meet call&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt;: local markdown note-taking app that serves as my personal knowledge base&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://support.claude.com/en/articles/9517075-what-are-projects"&gt;Claude Projects&lt;/a&gt;: separate AI contexts I maintain for each area of my work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tool solves a distinct problem. The value is in how they connect.&lt;/p&gt;
&lt;h2 id="gemini-meeting-notes-staying-present-in-meetings"&gt;
  
    Gemini Meeting Notes: staying present in meetings
  
&lt;/h2&gt;

&lt;p&gt;The biggest benefit here is simple: I rarely ever take notes during meetings.&lt;/p&gt;

&lt;p&gt;For me, it changed the quality of my participation entirely. Not splitting my attention between listening and typing has allowed me to ask better follow-up questions, read the room, catch the things that don’t get said explicitly. Most importantly, I can actively listen.&lt;/p&gt;

&lt;p&gt;Gemini runs in the background and handles the capture automatically. After each call, I get a structured summary and a full transcript. I don’t have to do anything during the meeting except show up and participate.&lt;/p&gt;
&lt;h2 id="obsidian-a-local-home-for-everything"&gt;
  
    Obsidian: a local home for everything
  
&lt;/h2&gt;

&lt;p&gt;Gemini notes are useful on their own, but raw meeting transcripts sitting in Google Drive aren’t a system and almost always end up in Google Drive purgatory. Obsidian is where I organize it all. If you’re not familiar with Obsidian, it’s basically an app that lets you organize and manage your files (mostly markdown) with a lot of bells and whistles.&lt;/p&gt;

&lt;p&gt;My Obsidian vault is structured around three contexts that map directly to how I actually work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client Projects&lt;/strong&gt; - one folder per engagement, each with a consistent set of files: project context, architecture notes, decision log, session notes, and a roadmap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal Projects&lt;/strong&gt; - thoughtbot team work, PM strategy, team lead responsibilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal&lt;/strong&gt; - fun projects, personal development, and tracking progress on aspirational lifelong goals like folding the laundry before doing another load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that sits a system layer: a file I called &lt;code&gt;HIVEMIND.md&lt;/code&gt; that serves as a master operating index, and a set of contextual modules covering things like domain knowledge, process standards, documentation standards, and writing guidelines.&lt;/p&gt;

&lt;p&gt;The structure is opinionated, but that’s the point. When every project folder looks the same, you spend zero time figuring out where to put something or where to look for it.
&lt;img src="https://images.thoughtbot.com/s1vdqvy8kgcixgekzvx6vpcqauzu_vault-structure-hierarchical.png" alt="An illustrated diagram with various colored areas. The top is a purple rectangle with the words System layer and rectangles it below that say Hivemind, Devmind, meta/modules and meta/modules/templates. These rectangles have arrows flowing out of it pointing toward a yellow rectangle that says Context layer with then has various labeled boxes below it. At the bottom is a green box that says Inbox layer with 2 arrows flowing out of it, pointing toward different yellow boxes."&gt;&lt;/p&gt;
&lt;h2 id="claude-maintaining-context-across-projects"&gt;
  
    Claude: maintaining context across projects
  
&lt;/h2&gt;

&lt;p&gt;The last piece is Claude Projects. On its own, Claude Projects is great if you keep all of the information and context within Claude. However, you start running into limitations once you have multiple projects because you’re essentially starting from scratch for every new project or chat. Also, the more you add to a Claude Project, the heavier every session becomes - burning through context limits faster over time.&lt;/p&gt;

&lt;p&gt;Obsidian connects to Claude Desktop via an MCP plugin, so Claude can read my vault directly. When I start a session, it first loads my &lt;code&gt;HIVEMIND.md&lt;/code&gt; which instructs Claude to only load whatever project files are relevant (this solves the issue of heavier Claude sessions). That handoff is what makes the whole system feel less like using a tool and more like working with someone who actually knows what’s going on.&lt;/p&gt;
&lt;h2 id="the-daily-handoff-gemini-to-obsidian"&gt;
  
    The daily handoff: Gemini to Obsidian
  
&lt;/h2&gt;

&lt;p&gt;I use a tool called &lt;a href="https://www.anthropic.com/product/claude-cowork"&gt;Claude Cowork&lt;/a&gt; in conjunction with a free community plug-in (Local REST API for Obsidian - &lt;a href="https://github.com/coddingtonbear/obsidian-local-rest-api"&gt;github repo&lt;/a&gt; or &lt;a href="https://obsidian.md/plugins?search=Local%20Rest%20API"&gt;Obsidian page&lt;/a&gt;) that runs a daily job to pull new Gemini notes from Google Drive into a dedicated inbox folder in Obsidian. The notes land automatically and I just have to review/file them into the right project folder.&lt;/p&gt;

&lt;p&gt;The best part is, even if I forgot to organize the meeting notes in Obsidian, the notes are still there and accessible for Claude to pull.&lt;/p&gt;
&lt;h2 id="the-part-that-actually-matters"&gt;
  
    The part that actually matters
  
&lt;/h2&gt;

&lt;p&gt;The system isn’t magic and I’m definitely not some jedi of the AI arts. Each tool is doing exactly one job and staying out of the way of the others. Gemini captures so I don’t have to. Obsidian organizes so nothing gets lost and context stays lean. Claude reads that context and picks up where we left off without me having to re-explain anything.&lt;/p&gt;

&lt;p&gt;But honestly, the part that made the biggest difference wasn’t any individual tool. It was deciding that I can be the best version of myself when I can actually be present. And whether you’re a fan of AI or not, I think it’s safe to say a lot of the tools that are available today make it easier than ever to do just that.&lt;/p&gt;

&lt;p&gt;Last bit of advice is if you want to try a version of this, don’t start with the whole stack. Start with whichever problem is loudest right now. Drowning in missed follow-ups from meetings? Try an AI note taker. Losing track of decisions across projects? Try Obsidian. Explaining the same context to Claude every session? Set up a Project with a HIVEMIND equivalent. Build the connections once each piece earns its place in your own workflow.&lt;/p&gt;

&lt;aside class="related-articles"&gt;&lt;h2&gt;If you enjoyed this post, you might also like:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/how-to-use-chatgpt-to-find-custom-software-consultants"&gt;How to Use ChatGPT to Find Custom Software Consultants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/from-idea-to-impact-the-role-of-rapid-prototyping-in-agetech"&gt;From idea to impact: The role of rapid prototyping in AgeTech&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/theme-based-iterations"&gt;Theme-Based Iterations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content>
  <summary>How I use Obsidian, Claude Projects, and Gemini Meeting Notes to stay present when my brain has too many tabs open.</summary>
  <thoughtbot:auto_social_share>true</thoughtbot:auto_social_share>
</entry>
<entry>
  <title>The age of snarky UI</title>
  <link rel="alternate" href="https://thoughtbot.com/blog/the-age-of-snarky-ui"/>
  <author>
    <name>Elaina Natario</name>
  </author>
  <id>https://thoughtbot.com/blog/the-age-of-snarky-ui</id>
  <published>2026-04-15T00:00:00+00:00</published>
  <updated>2026-04-14T20:12:31Z</updated>
  <content type="html">&lt;p&gt;My husband and I recently leased a new car, a Hyundai IONIQ. This is my first time driving a fully electric vehicle and also my first time in a car with such a gigantic touchscreen dashboard display. It feels futuristic, a far cry from my 2010 Ford Taurus.&lt;/p&gt;

&lt;p&gt;A few months into driving it, I encountered a notification on the dashboard. A big orange-outlined circle with a coffee cup icon and the words “Consider taking a break”. I had no idea what that meant. Had I been driving for too long and the car was suggesting I make a pit stop to stretch my legs? Was I going over the speed limit? Did I miss a turn? After a bit of research, I learned it was part of a driver attention system, detecting “irregular behavior” such as drifting out of your lane or not adjusting the steering wheel for a certain period of time. These behaviors may signify fatigue or distraction, thus the nudge (and the coffee cup).&lt;/p&gt;

&lt;figure&gt;
  &lt;img src="https://images.thoughtbot.com/iimv4gpr9eqsu1kipo7uoka73adz_car-alert-1.jpg" alt="A drawn illustration of a car dashboard with various driving stats and a circle to the right of the screen with a coffee cup icon and the words consider taking a break above it."&gt;
  &lt;img src="https://images.thoughtbot.com/a4852fftb4cmanze8ugvoqradisq_car-alert-2.jpg" alt="A drawn illustration of a person with long hair and an annoyed expression looking down while their hands are placed on steering wheel." style="margin-bottom: 1rem;"&gt;
  &lt;figcaption&gt;
    I figured it would be hypocritical to try and snap a photo of this alert &lt;em&gt;while&lt;/em&gt; driving, so here is an illustrated rendering of what it looks like and also what I look like when I see it.
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;But was I tired or distracted? No. I don’t always stay within my lane, especially during Spring season in New England, where I have to narrowly avoid potholes every ¼ mile. I’m less annoyed about it notifying me (and the irony of it needing me to look away from the road to tell me to pay attention). I’m annoyed because of the condescending tone and the fact that I had to do some digging to find out why my car was sending me this message. Can’t you just tell me “stay in your lane”? Why tiptoe around it, especially when coming from a car and not a person?&lt;/p&gt;

&lt;p&gt;Condescending or snarky UI copy isn’t a new concept. And I get it – as a business you want to come across more human, which sometimes means adding in humor or culturally relevant conversation patterns. But if that language obfuscates my understanding of the user experience or how to proceed, I’m going to leave very annoyed and not very amused.&lt;/p&gt;

&lt;p&gt;A few other examples I’ve encountered in the wild:&lt;/p&gt;
&lt;h2 id="confirmshaming"&gt;
  
    Confirmshaming
  
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.deceptive.design/types/confirmshaming"&gt;Confirmshaming&lt;/a&gt; aims to guilt you into opting in (or not opting out) of something. Instead of a “No thanks” or “Cancel” button in some type of upsell modal, you’ll get words like “I don’t want to save 20% on the world’s best mystery mayonnaise” or “&lt;a href="https://medium.com/design-bootcamp/the-devils-ux-dictionary-confirmshaming-4698372befe0"&gt;No, I’m fine with losing customers&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;The undertone: &lt;em&gt;You are naive about our offerings and are passing up something good.&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
  &lt;img src="https://images.thoughtbot.com/uu5i5ohu4l3x1jvdlxth1kxsst80_confirmshame-jorts.jpg" alt="A drawn illustration of a pop up modal with the words 'Enter your email and enjoy 20% off your entire order'. There is an image of jorts and an email input with a submit button. Below that are underlined words that read 'No thanks, I hate jorts.'" style="margin-bottom: 1rem;"&gt;
  &lt;figcaption&gt;
    Apologies to jorts lovers.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="paused-reminders"&gt;
  
    Paused reminders
  
&lt;/h2&gt;

&lt;p&gt;Apps like Duolingo and MyFitnessPal engage users with streaks, goal setting, and overall regular logging of your activity. You’ll get reminders through a push notification saying “Hey do the thing you said you’d do” as a way to keep you accountable. Ignore it for long enough, however, and get a final notification telling you they don’t think these reminders are working out for you so they’ll stop sending them. On the surface it seems like a kind gesture, but wording like that was off-putting enough for me to rage delete both of those apps years ago (maybe they’ve changed their copy since!).&lt;/p&gt;

&lt;p&gt;The undertone: &lt;em&gt;You are lazy and we’ve given up on you.&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
  &lt;img src="https://images.thoughtbot.com/3rooxspaksi8aomb1239twiqiuaa_reminder-1.jpg" alt="A drawn illustration of phone notification with the MyFitnessPal app icon and the words 'Hmm. Seems like these reminders aren't helpful. We're going to turn them off.'"&gt;
  &lt;img src="https://images.thoughtbot.com/ehjjedp8cyxqabcyt3f30dt6w7ad_reminder-2.jpg" alt="A drawn illustration of a hand with the thumb over a modal that asks 'Remove My FitnessPal?' There are three buttons underneath that say Remove from Home Screen, Delete App, and Cancel. The thumb is hovering above the Delete App button." style="margin-bottom: 1rem;"&gt;
  &lt;figcaption&gt;
    Sometimes you have to show your apps who is the boss around here and remove them from existence when they get too snarky.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="over-encouragement"&gt;
  
    Over-encouragement
  
&lt;/h2&gt;

&lt;p&gt;If you wear a smart watch and have notifications for various health trackers, you’ve probably been congratulated for simply walking around your house. Often they come through as encouraging, telling you how great you are for moving your body and taking 35% more steps than average. The reality is that unless you’re intentionally exercising, the messaging feels more like mockery. At that point, I’d prefer it to tell me I’m a disappointment.&lt;/p&gt;

&lt;p&gt;The undertone: &lt;em&gt;We are genuinely shocked you left the couch.&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
  &lt;img src="https://images.thoughtbot.com/xe4qj0a379kttht3nvkgcdtvrakt_encouragement.jpg" alt="A drawn illustration of a smart watch with the screen showing an arrow pointing up and the word Stand by it. Under it says 'You did it! Did you use your standing desk to actually stand today?' Below that is a button that says Dismiss." style="margin-bottom: 1rem;"&gt;
  &lt;figcaption&gt;
    What? You don’t get notifications like this, too?
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Logically I know the computers are not making a judgement about my opinions, behaviors, or abilities. But I do sometimes feel like my devices are snickering under their breath at what a dolt I am. And perhaps that is because I live in an area that is known for its candor and “kind but not nice” way of communicating. Brusk interactions like that can be off-putting or seemingly rude, but I much prefer it over polite sugar-coated messaging that lacks clarity.&lt;/p&gt;

&lt;aside class="related-articles"&gt;&lt;h2&gt;If you enjoyed this post, you might also like:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/once-bitten-twice-shy"&gt;Once Bitten Twice Shy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/internet-stop-being-so-internety"&gt;Internet: Stop being so Internety&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/design-101-stop-yelling"&gt;Design 101: Stop Yelling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content>
  <summary>Your devices are all judging you. They're just too polite to say it directly.</summary>
  <thoughtbot:auto_social_share>true</thoughtbot:auto_social_share>
</entry>
<entry>
  <title>AI in Focus: A new Claude Skill for Rails code audits</title>
  <link rel="alternate" href="https://thoughtbot.com/blog/ai-in-focus:a-new-claude-skill-for-rails-code-audits"/>
  <author>
    <name>Chad Pytel and Kate Young</name>
  </author>
  <id>https://thoughtbot.com/blog/ai-in-focus:a-new-claude-skill-for-rails-code-audits</id>
  <published>2026-04-13T00:00:00+00:00</published>
  <updated>2026-04-10T09:23:01Z</updated>
  <content type="html">&lt;p&gt;AI in Focus is our ongoing livestream where we get hands on leveraging AI in real product development. In this episode, Chad flew solo and picked up a thread from our previous episode &lt;a href="https://thoughtbot.com/blog/claude-code-skills-automating-fda-required-documentation-for-software-as-a-medical-device"&gt;Claude Code skills for FDA-style documentation&lt;/a&gt;. We had touched on the idea of using Claude skills for code quality and auditing, and fellow thoughtbotter Jose Blanco ran with it to ship an experimental Claude skill &lt;a href="https://thoughtbot.com/blog/audit-using-thoughtbot-best-practices-with-claude-skills"&gt;rails-audit-thoughtbot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this stream, Chad installed Jose’s experimental Claude skill and put it though it’s paces auditing our internal operations platform, which is a 10+ year old Rails codebase, with plenty of legacy surface area. You can &lt;a href="https://www.youtube.com/watch?v=wDgpcrRmPbg"&gt;watch the full replay on YouTube&lt;/a&gt; or read on for what we learned about auditing Rails apps using Claude skills.&lt;/p&gt;
&lt;h2 id="what-are-claude-skills"&gt;
  
    What are Claude Skills?
  
&lt;/h2&gt;

&lt;p&gt;Agent Skills are an open, file-based format maintained by Anthropic and &lt;a href="https://agentskills.io/specification"&gt;open to contributions from the community&lt;/a&gt;. At its most basic, a skill is a &lt;code&gt;SKILL.md&lt;/code&gt; file with YAML front matter, and a markdown body of instructions. &lt;a href="https://www.anthropic.com/news/skills"&gt;Anthropic has introduced skills&lt;/a&gt; across Claude, and Claude Code looks for them in your repo or on your local machine, usually under &lt;code&gt;~/.claude/skills/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s a bit like human-authored tool calling: The short front matter stays lightweight, and the content only loads when the skill is relevant, or if you invoke it explicitly.&lt;/p&gt;
&lt;h2 id="what-goes-into-rails-audit-thoughtbot"&gt;
  
    What goes into rails-audit-thoughtbot?
  
&lt;/h2&gt;

&lt;p&gt;Jose’s new experimental skill encodes thoughtbot-flavored Rails best practice. Let’s allow the code to speak for itself. Here’s the YAML from matter at the top of rails-audit-thoughtbot’s &lt;code&gt;SKILL.md&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rails-audit-thoughtbot&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Perform comprehensive code audits of Ruby on Rails applications based on thoughtbot best practices. Use this skill when the user requests a code audit, code review, quality assessment, or analysis of a Rails application. The skill analyzes the entire codebase focusing on testing practices (RSpec), security vulnerabilities, code design (skinny controllers, domain models, PORO with ActiveModel), Rails conventions, database optimization, and Ruby best practices. Outputs a detailed markdown audit report grouped by category (Testing, Security, Models, Controllers, Code Design, Views) with severity levels (Critical, High, Medium, Low) within each category.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, the long &lt;code&gt;description&lt;/code&gt; is what Claude Code uses to decide when the skill should be applied; the rest of the file stays unloaded until the skill runs.&lt;/p&gt;

&lt;p&gt;In developing this skill, Jose drew on our free books &lt;a href="https://thoughtbot.com/ruby-science"&gt;Ruby Science&lt;/a&gt; (code smells and fixes), &lt;a href="https://books.thoughtbot.com/assets/testing-rails.pdf"&gt;Testing Rails&lt;/a&gt;, as well as a security checklist (12 categories with detection patterns), and a report template so the audit output is structured and comparable between runs. Following the feedback from this livestream, he added &lt;a href="https://github.com/thoughtbot/rails-audit-thoughtbot/blob/main/references/rails_antipatterns.md"&gt;&lt;code&gt;references/rails_antipatterns.md&lt;/code&gt;&lt;/a&gt;, a reference drawn from Chad’s book &lt;a href="https://www.informit.com/store/rails-antipatterns-best-practice-ruby-on-rails-refactoring-9780321604811"&gt;Rails Antipatterns&lt;/a&gt; that extends the audit with external services, migrations, performance, and failure-handling patterns.&lt;/p&gt;

&lt;p&gt;The skill is open-source and available at &lt;a href="https://github.com/thoughtbot/rails-audit-thoughtbot"&gt;thoughtbot/rails-audit-thoughtbot&lt;/a&gt;. Feel free to clone or copy it into your skills directory and get ready to experiment.&lt;/p&gt;
&lt;h2 id="installing-and-running-the-skill"&gt;
  
    Installing and running the skill
  
&lt;/h2&gt;

&lt;p&gt;Chad started from a clean checkout of Hub: He created &lt;code&gt;~/.claude/skills&lt;/code&gt;, then cloned the &lt;code&gt;rails-audit-thoughtbot&lt;/code&gt; skills repo into that directory. With a Claude Code session running in the terminal, after some debugging, Chad was able to call the skill using its name slash-command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/rails-audit-thoughtbot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Tip from the stream:&lt;/strong&gt; you do not have to audit the whole app on day one. The skill supports a targeted run inside a session, for example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/rails-audit-thoughtbot audit controllers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do a code review on my models
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="what-the-hub-run-looked-like"&gt;
  
    What the Hub run looked like
  
&lt;/h2&gt;

&lt;p&gt;On a full-application pass, the skill took ~5 minutes to generate an audit report that summarized findings grouped by category (testing, security, models, controllers, code design, views) with assigned severities. Chad highlighted a few headline items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Large Model:&lt;/strong&gt; a textbook god class on &lt;code&gt;Person&lt;/code&gt; which runs to hundreds of lines and many public methods, aligning  with what the team already knew from past code reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PORO:&lt;/strong&gt; many service-style objects flagged for refactor toward ActiveModel-backed domain objects, per intentionally-opinionated &lt;a href="https://github.com/thoughtbot/rails-audit-thoughtbot/blob/main/references/poro_patterns.md"&gt;PORO&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing model specs&lt;/strong&gt; The skills treated “no &lt;code&gt;_spec&lt;/code&gt; per model file” as a gap but Chad dug in and noted this was a bit of a false-positive as view-model style objects had integration coverage instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Positives:&lt;/strong&gt; strong testing culture, good use of calculators, presenters, and query objects, no &lt;code&gt;:focus&lt;/code&gt; tags in RSpec (Chad recommends against committing &lt;code&gt;:focus&lt;/code&gt;: use line-number runs instead e.g. &lt;code&gt;.\spec_name.rb:34&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="where-human-judgment-still-matters"&gt;
  
    Where human judgment still matters
  
&lt;/h2&gt;

&lt;p&gt;The audit was promising but not perfect. A couple of areas where the skill could be improved included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composition vs. wider design:&lt;/strong&gt; recommendations leaned on extracting small objects with composition, which aligns with Ruby Science. However, for real refactors, Chad reminded us to ask whether the leverage is alongside or above a hot spot before following the first suggested carve-out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“Issues” that are really clean bills of health:&lt;/strong&gt; for external API wrappers, the skill correctly said “no changes required,” yet still surfaced them under severity report styling meant for problems. Chad noted that for onboarding to a new codebase, calling out “we looked here and it’s fine” is valuable, but the skill’s report template could be updated separate passes from findings so readers are not misled. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Throughout, the audit was pretty consistent with how we begin client audits at thoughtbot: Move fast for orientation, then validate each area with deep domain knowledge.&lt;/p&gt;
&lt;h2 id="how-a-thoughtbot-services-audit-goes-deeper"&gt;
  
    How a thoughtbot services audit goes deeper
  
&lt;/h2&gt;

&lt;p&gt;The stream’s run was overwhelmingly static analysis using Claude Code, which finished in ~5 minutes. Compared with 11–13 minutes for Hub’s full test suite alone, this was a fair tradeoff for a first pass. Chad contrasted the skill with what we typically do on client Rails audits: Run diagnostic tools like &lt;a href="https://github.com/pinzolo/rails-flog"&gt;rails-flog&lt;/a&gt;, &lt;a href="https://github.com/seattlerb/flay"&gt;flay&lt;/a&gt;, &lt;a href="https://github.com/flyerhzm/bullet"&gt;Bullet&lt;/a&gt; for N+1s, execute the test suite, and attach real coverage metrics, folding all of that into the written recommendations. Following this stream, Jose picked up on Chad’s recommendations and added agentic support for running &lt;a href="https://github.com/simplecov-ruby/simplecov"&gt;SimpleCov&lt;/a&gt;, and &lt;a href="https://github.com/whitesmith/rubycritic"&gt;RubyCritic&lt;/a&gt;, which rolls-up rails-flog, flay and other analytics tools.&lt;/p&gt;

&lt;p&gt;Chad also imagined CI hooks down the road: Not to block merges blindly, but to keep a living quality guide next to our style guide, maintain expert human oversight.&lt;/p&gt;
&lt;h2 id="test-it-out-yourself"&gt;
  
    Test it out yourself
  
&lt;/h2&gt;

&lt;p&gt;If you use Claude Code and maintain a Rails app, clone &lt;a href="https://github.com/thoughtbot/rails-audit-thoughtbot"&gt;rails-audit-thoughtbot&lt;/a&gt;, run a full or targeted audit, and see how the report reads on your codebase. We would love reactions on the &lt;a href="https://www.youtube.com/watch?v=wDgpcrRmPbg"&gt;YouTube replay&lt;/a&gt; as well as issues and PRs on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/thoughtbot/rails-audit-thoughtbot"&gt;rails-audit-thoughtbot&lt;/a&gt; is experimental. It is a starting point, not a finished product. We actively encourage open-source contributions: Open an issue or fork the repo and open a pull requests with ways you think this Claude skill can work better for your team.&lt;/p&gt;
&lt;h2 id="get-in-touch"&gt;
  
    Get in touch
  
&lt;/h2&gt;

&lt;p&gt;If your Claude Code skill audit surfaces more than your team has capacity for right now, &lt;a href="https://thoughtbot.com/hire-us"&gt;get in touch with us&lt;/a&gt;. We have been auditing and reviewing Rails apps for a long time, and are excited to help prioritize what actually moves the needle for your business.&lt;/p&gt;

&lt;aside class="related-articles"&gt;&lt;h2&gt;If you enjoyed this post, you might also like:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/how-to-use-chatgpt-to-find-custom-software-consultants"&gt;How to Use ChatGPT to Find Custom Software Consultants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/theme-based-iterations"&gt;Theme-Based Iterations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/this-week-in-open-source-6-30"&gt;This Week in Open Source (June 30, 2023)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content>
  <summary>Learn what happened when Chad installed the open source rails-audit-thoughtbot Claude skill, ran it on thoughtbot's decade-old internal Hub app, and walked through hits, misses, and how we'd tighten the skill over time.</summary>
  <thoughtbot:auto_social_share>true</thoughtbot:auto_social_share>
</entry>
</feed>
