<?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-17T00:00:00+00:00</updated>
  <author>
    <name>thoughtbot</name>
  </author>
<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>
<entry>
  <title>You, too, can make a boilerplate</title>
  <link rel="alternate" href="https://thoughtbot.com/blog/you-too-can-make-a-boilerplate"/>
  <author>
    <name>Elaina Natario</name>
  </author>
  <id>https://thoughtbot.com/blog/you-too-can-make-a-boilerplate</id>
  <published>2026-04-10T00:00:00+00:00</published>
  <updated>2026-04-09T16:35:12Z</updated>
  <content type="html">&lt;p&gt;Recently, we released &lt;a href="https://github.com/thoughtbot/roux"&gt;Roux&lt;/a&gt;, a set of native CSS styles and architecture opinions. This was born from years of collecting CSS patterns, opinions, and general best practices that worked on virtually any project, no matter the framework or industry. And it’s something we continue to add to and adjust as CSS changes and our own use cases shift.&lt;/p&gt;

&lt;p&gt;Roux is what we’d call a boilerplate – a generic and reusable piece of content (in this case, code), that can be used in any relevant project without much alteration. It serves as a solid foundation to build from, without having to start from scratch.&lt;/p&gt;

&lt;p&gt;Do you have one or two patterns you’ve used from project to project? Great! You, too, can build a boilerplate.&lt;/p&gt;
&lt;h2 id="what-is-a-boilerplate"&gt;
  
    What is a boilerplate?
  
&lt;/h2&gt;

&lt;p&gt;Most of my favorite design terminology, like gutters, drop caps, pull quotes, come from newspapers and print journalism. Boilerplate is one of those…well, sort of.&lt;/p&gt;

&lt;p&gt;Originally it’s a term from steamships in the 1800s. They were sheets of rolled steel to make boilers to heat water. These boilers generated steam for the ship’s engine.&lt;/p&gt;

&lt;p&gt;By the late 1800s/early 1900s, some newspapers were using reclaimed steamship boilerplates for &lt;a href="https://en.wikipedia.org/wiki/Hot_metal_typesetting"&gt;hot metal typesetting&lt;/a&gt; in their printing presses. Linotype machines (among a few others) allowed for this typesetting, pouring molten lead into a mold, and producing multiple typesets from that mold. Perhaps there was an article or an advertisement that you’d want to send out to multiple newspapers to set in their printing as they chose. These more widely became known as “boilerplates”, which were more conventional content that could be applied to a number of publications geographically.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src="https://images.thoughtbot.com/nb8szbksrl50ulsyuqmo7zgr35mw_linotype.jpg" alt="A gray slab of metal with various lines of serif type set as part of that metal. The slab is against a black background." style="margin-bottom: 1rem;"&gt;
  &lt;figcaption&gt;
    A Linotype-set printing form, courtesy of the &lt;a href="https://collection.sciencemuseumgroup.org.uk/objects/co8040279/small-printing-forme-representing-linotype-set-matter"&gt;Science Museum Group&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The term has since grown from its newspaper beginnings, to being used in contract law with “boilerplate clauses” and now in code.&lt;/p&gt;
&lt;h2 id="how-it-works-in-code"&gt;
  
    How it works in code
  
&lt;/h2&gt;

&lt;p&gt;In development, boilerplates are generally defined as reusable bits of code, agnostic to the type of project. It’s something you can start a codebase with or insert along the way as needed. Some are extremely opinionated, some are looser. Some are very thorough, and some are sparse. What it all boils down to (heh) is having something that is useful to you that works in various contexts.&lt;/p&gt;

&lt;p&gt;Boilerplates can be useful by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standardizing opinions, rules, and best practices by your team’s agreed-upon conventions into a reusable starting point

&lt;ul&gt;
&lt;li&gt;Which in turn can ensure a consistent user experience with predictable and standardized interactions and patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reducing decision fatigue because foundational choices are made (like CSS architecture), freeing you up to focus on more impactful work&lt;/li&gt;
&lt;li&gt;Minimizing human error by reducing or eliminating the risk of manual mistakes with repetitive setup tasks&lt;/li&gt;
&lt;li&gt;Accelerating onboarding that allows you or your team to jump into a familiar structure rather than inferring conventions from scratch&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-you-might-approach-writing-your-own"&gt;
  
    How you might approach writing your own
  
&lt;/h2&gt;

&lt;p&gt;You probably already have a handful of code bits or content that you’re writing in the same or similar way over and over again – you just haven’t documented it. Take a look at your own corpus of code and see what emerges.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit what you repeatedly build after looking at past projects and identifying a few patterns, structures, or sections you’re commonly recreating.&lt;/li&gt;
&lt;li&gt;Capture the annoying or painful bits. Useful boilerplates can solve the setup steps you may dread the most.&lt;/li&gt;
&lt;li&gt;Cherry pick from your best work with successful past projects. Sometimes that’s taking patterns in their entirety or frankensteining together bits from various projects.

&lt;ul&gt;
&lt;li&gt;For example, I had a modal element where I liked the slide in transition I did in one project and another where I felt it had better accessibility support, so I mushed them together into one pattern.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Strip out the project-specific details for the parts you want to use and leave what’s universally useful.&lt;/li&gt;
&lt;li&gt;Include comments, notes, or annotations that explain why things are set up the way they are. This may be useful not just for other people reading your boilerplate, but future you who has since forgotten why you decided on a specific structure or syntax, etc.&lt;/li&gt;
&lt;li&gt;Treat it as a living document and update it as your opinions and standards evolve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, know that &lt;strong&gt;it doesn’t have to be complicated or involved&lt;/strong&gt;. Your boilerplate can be just a few patterns that are helpful to just you. It doesn’t even have to be code – it can be designs or layouts that you build prototypes with; it could be structures for documentation that you commonly write out. And while we did just talk about iterating on your boilerplate, it can absolutely be something that you write once and never adjust.&lt;/p&gt;
&lt;h2 id="boilerplates-in-the-age-of-ai"&gt;
  
    Boilerplates in the age of AI
  
&lt;/h2&gt;

&lt;p&gt;Why bother writing out a boilerplate if Claude or any other LLMs can do it for you? The output reflects patterns it’s trained on, which are not necessarily your patterns. It doesn’t know you or your team’s specific decisions you’ve made over the years. Articulating what good and useful looks like for you forces you to refine those standards and create a more solid source of truth.&lt;/p&gt;
&lt;h2 id="sharing-your-patterns"&gt;
  
    Sharing your patterns
  
&lt;/h2&gt;

&lt;p&gt;Even if your boilerplate may only have a narrow use case for yourself, opening it up to feedback can sharpen your own thinking. Others might spot a gap or inconsistency you’ve been too close to notice. Sharing can also help others adopt your useful patterns in their own work. More broadly, it invites people to build, adapt, and improve it. You may surprise yourself at how novel your own workflows are to others!&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/using-machine-learning-to-answer-questions-from-internal-documentation"&gt;Using Machine Learning to Answer Questions from Internal Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/expressing-the-problem"&gt;Expressing the problem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content>
  <summary>Adapt your own patterns and processes into a reusable piece of content.</summary>
  <thoughtbot:auto_social_share>true</thoughtbot:auto_social_share>
</entry>
</feed>
