<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Paul Keen</title>
    <description>The latest articles on Forem by Paul Keen (@pftg).</description>
    <link>https://forem.com/pftg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F226063%2Fe5270ba6-c1cf-4803-a869-ec99a935a1ad.jpeg</url>
      <title>Forem: Paul Keen</title>
      <link>https://forem.com/pftg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pftg"/>
    <language>en</language>
    <item>
      <title>The AI Agent Onboarding Problem (The Real Version)</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Mon, 06 Oct 2025 09:34:28 +0000</pubDate>
      <link>https://forem.com/jetthoughts/the-ai-agent-onboarding-problem-the-real-version-4m5l</link>
      <guid>https://forem.com/jetthoughts/the-ai-agent-onboarding-problem-the-real-version-4m5l</guid>
      <description>&lt;p&gt;So you asked about AI agents and onboarding, and honestly? We screwed this up for like three months before we figured it out.&lt;/p&gt;

&lt;p&gt;Here's what was happening. Every time we'd spin up an agent - we were using a mix of Claude with function calling and some custom orchestration - we'd have to stuff the entire context into the system prompt. Our coding standards, the database schema, the API patterns, all of it.&lt;/p&gt;

&lt;p&gt;And the context window? It was getting expensive. We're talking like 50k tokens just for setup on every single task. The math doesn't work when you're doing this 20 times a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Broke
&lt;/h2&gt;

&lt;p&gt;The real problem wasn't that agents "forgot" - obviously they're stateless, right? The problem was we had no good way to inject relevant context without... um, without just dumping everything in every time.&lt;/p&gt;

&lt;p&gt;We tried a few things that didn't work. First, we thought we'd just use the conversation history. Terrible idea. You hit token limits fast, and old context pollutes new tasks.&lt;/p&gt;

&lt;p&gt;Then we tried maintaining state in Redis between calls. That was better but still required us to manually decide what context each agent needed. Every. Single. Time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Thing That Actually Worked
&lt;/h2&gt;

&lt;p&gt;We ended up building a pretty simple RAG setup. Nothing fancy - just embedded our docs, standards, and past decisions into Pinecone. When an agent spins up, it queries for relevant context based on the task description.&lt;/p&gt;

&lt;p&gt;So if the task is "add authentication to the user API," it pulls our auth patterns, our API structure docs, our error handling examples. Maybe 5-8k tokens of highly relevant stuff instead of 50k tokens of everything.&lt;/p&gt;

&lt;p&gt;Cut our setup from like 30 minutes of me writing context to under a minute of automated retrieval. And our token costs dropped by 60% or something.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That's Still Messy
&lt;/h2&gt;

&lt;p&gt;The hard part is keeping those embedded docs current. When we change an architectural decision, we need to update the knowledge base. We automated some of it with git hooks, but honestly, it still requires discipline.&lt;/p&gt;

&lt;p&gt;And debugging? Man, when an agent does something weird, you have to figure out what context it pulled and whether that was wrong or incomplete. We added logging for every retrieval, which helps, but it's not perfect.&lt;/p&gt;

&lt;p&gt;Also, security was a thing. These agents are pulling from our internal docs. We had to be really careful about what goes in there. No credentials, no customer data, nothing sensitive. Just patterns and standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Different
&lt;/h2&gt;

&lt;p&gt;If I started over, I'd probably use LangChain or LlamaIndex instead of rolling our own. We reinvented some wheels. But honestly, understanding how it works under the hood was valuable.&lt;/p&gt;

&lt;p&gt;The other thing - and this is important - start with ONE agent doing ONE thing really well. We tried to solve it for five agents at once. Disaster. Get the onboarding flow right for one use case, then expand.&lt;/p&gt;

&lt;p&gt;Oh, and write tests for your agent outputs. Seriously. We were validating manually for way too long.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;AI agents are only fast if you're not bottlenecking them with manual context setup. You need automated, relevant context injection. RAG is probably your best bet unless you've got something better.&lt;/p&gt;

&lt;p&gt;But yeah, it takes infrastructure work. Boring stuff. Documentation, embedding pipelines, retrieval logic, monitoring. &lt;/p&gt;

&lt;p&gt;Most teams skip that and wonder why their agents aren't delivering.&lt;/p&gt;

&lt;p&gt;Anyway, that's what we learned. Still iterating on it, but it's way better than where we started.&lt;/p&gt;

&lt;p&gt;What are you running into with your agents?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>management</category>
      <category>learning</category>
    </item>
    <item>
      <title>AI Forces What Rails Teams Already Knew: Small Teams Ship Faster</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Thu, 02 Oct 2025 15:05:46 +0000</pubDate>
      <link>https://forem.com/jetthoughts/ai-forces-what-rails-teams-already-knew-small-teams-ship-faster-3k3g</link>
      <guid>https://forem.com/jetthoughts/ai-forces-what-rails-teams-already-knew-small-teams-ship-faster-3k3g</guid>
      <description>&lt;p&gt;I found &lt;a href="https://www.youtube.com/watch?v=c_w0LaFahxk" rel="noopener noreferrer"&gt;this discussion&lt;/a&gt; last week and it crystallized something that's been percolating in the industry for months. Not because it was novel—we've been running small, empowered teams at &lt;a href="https://jetthoughts.com" rel="noopener noreferrer"&gt;JetThoughts&lt;/a&gt; for 17 years - but because AI is finally forcing the broader tech industry to confront what we figured out early: bureaucratic overhead kills velocity more than any technical constraint.&lt;/p&gt;

&lt;p&gt;The shift isn't really about AI writing code. It's about AI making the cost of organizational complexity impossible to ignore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work Smarter Finally Beats Work Harder&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's a pattern in tech where we throw people at problems. Team struggling? Hire more engineers. Communication breaking down? Add a layer of management. Code quality slipping? Create a QA department.&lt;/p&gt;

&lt;p&gt;Each addition feels reasonable in isolation. But the aggregate cost compounds exponentially.&lt;/p&gt;

&lt;p&gt;We've maintained a hard rule at JetThoughts: &lt;a href="https://jetthoughts.com/blog/building-an-effective-dev-team-strategies/" rel="noopener noreferrer"&gt;teams of 5-9 people&lt;/a&gt;. Not because we couldn't grow larger—we've been around long enough to scale if we wanted to. But because we observed something counterintuitive early on: a team of 6 experienced developers consistently outshipped teams of 15-20.&lt;/p&gt;

&lt;p&gt;The math isn't about individual productivity. It's about coordination tax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;Team&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;communication_overhead&lt;/span&gt;
    &lt;span class="c1"&gt;# Metcalfe's law applies to organizational complexity&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;effective_velocity&lt;/span&gt;
    &lt;span class="n"&gt;total_capacity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;communication_overhead&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;alignment_cost&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 6 developers: 15 communication paths&lt;/span&gt;
&lt;span class="c1"&gt;# 20 developers: 190 communication paths&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When AI amplifies individual developer output by 25-50%, that communication overhead becomes the dominant bottleneck. You can't coordinate 190 paths of communication 50% faster. But you can eliminate most of them by keeping teams small.&lt;/p&gt;

&lt;p&gt;As we wrote in our analysis of &lt;a href="https://jetthoughts.com/blog/building-an-effective-dev-team-strategies/" rel="noopener noreferrer"&gt;effective dev teams&lt;/a&gt;: "A small team can be nimble and quick, but might lack the bandwidth for large projects. A large team has more resources, but can be slow and bureaucratic." What we've found is that the second constraint—bureaucracy—dominates at almost any meaningful project scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Silo Problem AI Exposes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what the YouTube discussion gets right: AI doesn't just make developers faster—it makes organizational inefficiency visible in cycle time metrics.&lt;/p&gt;

&lt;p&gt;Traditional org structure creates information silos as a side effect of scale. Backend team doesn't know what frontend needs. Product doesn't understand technical constraints. Engineering Manager becomes a translation layer between teams and leadership.&lt;/p&gt;

&lt;p&gt;Each silo requires synchronization points. Each synchronization point requires meetings, alignment discussions, status updates. The work-to-coordination ratio inverts.&lt;/p&gt;

&lt;p&gt;We eliminated most of this through radical transparency. Not as an aspirational value, but as hard process—what we call &lt;a href="https://jetthoughts.com/blog/cons-of-private-chats-for-team-collaboration-communication-process/" rel="noopener noreferrer"&gt;our communication architecture&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# At JetThoughts, this pattern is enforced&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Communication&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inclusion: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;in: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;share&lt;/span&gt;
    &lt;span class="c1"&gt;# Private discussions are actively discouraged&lt;/span&gt;
    &lt;span class="c1"&gt;# If they happen, summary must be posted publicly&lt;/span&gt;
    &lt;span class="n"&gt;post_to_public_channel&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;private_discussion_occurred?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our rule: &lt;a href="https://jetthoughts.com/blog/cons-of-private-chats-for-team-collaboration-communication-process/" rel="noopener noreferrer"&gt;all team communication happens in public project channels&lt;/a&gt;. No DMs about project decisions. No hallway conversations that change direction. If it's important enough to discuss, it's important enough to document publicly.&lt;/p&gt;

&lt;p&gt;This isn't about surveillance. It's about removing the need for synchronization meetings. When context is public by default, developers can pull information as needed rather than waiting for it to be pushed through status updates.&lt;/p&gt;

&lt;p&gt;AI makes this pattern more critical because it speeds up execution but can't speed up alignment. The gap between "how fast we could ship if we knew what to build" and "how fast we actually ship" is almost entirely organizational overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reducing Tolerance for Inexperience Through Process&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The video discussion touches on something we've observed: effective practices aren't complicated, but they're often skipped because "experienced developers know what they're doing."&lt;/p&gt;

&lt;p&gt;That's backwards. Experienced developers follow process because they've seen what happens when you don't.&lt;/p&gt;

&lt;p&gt;Our &lt;a href="https://jetthoughts.com/blog/delivery-flow-for-distributed-remote-teams-agile-kanban/" rel="noopener noreferrer"&gt;delivery flow&lt;/a&gt; isn't flexible or creative. It's deliberately constrained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;DeveloperWorkflow&lt;/span&gt;
  &lt;span class="no"&gt;MAX_WIP_PER_DEVELOPER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# In Progress&lt;/span&gt;
  &lt;span class="no"&gt;MAX_TOTAL_ITEMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;        &lt;span class="c1"&gt;# Including Done&lt;/span&gt;
  &lt;span class="no"&gt;MAX_DAYS_PER_ISSUE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_start_new_work?&lt;/span&gt;
    &lt;span class="n"&gt;current_wip&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;MAX_WIP_PER_DEVELOPER&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="n"&gt;total_items&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;MAX_TOTAL_ITEMS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;blocked_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These constraints serve multiple purposes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For junior developers:&lt;/strong&gt; Clear boundaries prevent scope creep and context switching. You can't take on too much because the system prevents it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For senior developers:&lt;/strong&gt; Forces decomposition of large work into shippable increments. The 2-day limit means if something doesn't fit, you break it down or rethink the approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For the team:&lt;/strong&gt; Eliminates prioritization debates. Work on cards from right to left, top to bottom. If priority needs to change, someone moves the card. No meeting required.&lt;/p&gt;

&lt;p&gt;When AI amplifies individual output, these constraints become more valuable. A developer using AI effectively might write 2-3x more code per day. Without WIP limits and time constraints, that accelerates feature creep and technical debt accumulation rather than shipping velocity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Empowerment as Process, Not Permission&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The other pattern the YouTube video highlights: empowerment isn't about giving developers autonomy after they prove themselves. It's about structuring work so autonomy is the default state.&lt;/p&gt;

&lt;p&gt;Our &lt;a href="https://jetthoughts.com/blog/typical-day-at-jetthoughts-agile-remote/" rel="noopener noreferrer"&gt;typical sprint structure&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;Sprint&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;week&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@monday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kickoff_day&lt;/span&gt;    &lt;span class="c1"&gt;# Set goals, schedule issues&lt;/span&gt;
    &lt;span class="vi"&gt;@thursday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retro_day&lt;/span&gt;    &lt;span class="c1"&gt;# Review, measure, improve&lt;/span&gt;
    &lt;span class="vi"&gt;@friday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contribution&lt;/span&gt;   &lt;span class="c1"&gt;# Internal projects, OSS, learning&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;developer_autonomy&lt;/span&gt;
    &lt;span class="c1"&gt;# No daily standups&lt;/span&gt;
    &lt;span class="c1"&gt;# No manager approval for technical decisions&lt;/span&gt;
    &lt;span class="c1"&gt;# No separate QA handoff (TDD from start)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;planning: :collaborative&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;execution: :independent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;review: :transparent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;learning: :protected_time&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mondays we align on what matters. Then developers own their cards—completely. No check-ins. No status updates beyond what's visible in GitHub and project boards.&lt;/p&gt;

&lt;p&gt;This only works because we've eliminated the reasons traditional teams need oversight:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transparent work:&lt;/strong&gt; Every commit visible. Every decision documented in PRs. Every question asked in public channels where others learn from the answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clear constraints:&lt;/strong&gt; &lt;a href="https://jetthoughts.com/blog/delivery-flow-for-distributed-remote-teams-agile-kanban/" rel="noopener noreferrer"&gt;WIP limits&lt;/a&gt; prevent overcommitment. Time limits force scope management. Priority order removes decision paralysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built-in quality:&lt;/strong&gt; TDD isn't optional. Tests fail, code doesn't ship. No separate QA creates a handoff that requires coordination.&lt;/p&gt;

&lt;p&gt;AI makes this pattern more effective because it removes the excuse that "junior developers need more guidance." With AI pair programming, junior developers have always-on mentorship. They can ask questions without interrupting seniors. They can explore solutions without getting stuck.&lt;/p&gt;

&lt;p&gt;The coordination overhead that justified management layers—answering questions, unblocking work, reviewing decisions—largely disappears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What We're Seeing With AI Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We've been integrating AI tools into our workflow over the past year. The pattern that emerged isn't what we expected.&lt;/p&gt;

&lt;p&gt;We thought: AI will speed up code writing.&lt;/p&gt;

&lt;p&gt;What actually happened: AI exposed where our process had unnecessary friction.&lt;/p&gt;

&lt;p&gt;Example: Code review delays. Pre-AI, a PR might sit for hours or a day waiting for review. Not a huge deal when developers were context-switching less frequently.&lt;/p&gt;

&lt;p&gt;With AI-assisted development, developers finish work faster. A task that took 2 days now takes 1. But if review still takes 6-12 hours, the cycle time improvement disappears.&lt;/p&gt;

&lt;p&gt;So we adjusted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;PullRequest&lt;/span&gt;
  &lt;span class="no"&gt;REVIEW_SLA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;  &lt;span class="c1"&gt;# Not 24 hours&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;merge_strategy&lt;/span&gt;
    &lt;span class="c1"&gt;# Small, frequent PRs &amp;gt; large, infrequent&lt;/span&gt;
    &lt;span class="c1"&gt;# AI helps decompose work into reviewable chunks&lt;/span&gt;
    &lt;span class="n"&gt;prefer_incremental_over_comprehensive&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;review_priority&lt;/span&gt;
    &lt;span class="c1"&gt;# Fresh context &amp;gt; perfect timing&lt;/span&gt;
    &lt;span class="c1"&gt;# Review immediately while code is fresh&lt;/span&gt;
    &lt;span class="n"&gt;optimize_for_flow_over_batching&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small PRs become natural with AI because you can ask it "how would you break this feature into 3 shippable increments?" and get reasonable decomposition suggestions.&lt;/p&gt;

&lt;p&gt;Faster reviews become necessary because AI-accelerated development surfaces review as the bottleneck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Broader Pattern AI Reveals&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What excites me about the conversation in that YouTube video isn't the specific predictions about AI replacing mid-level engineers or eliminating certain roles.&lt;/p&gt;

&lt;p&gt;It's that AI is forcing a reckoning with organizational patterns that were always inefficient but seemed necessary because "that's how software teams work."&lt;/p&gt;

&lt;p&gt;Small teams weren't viable before because individual developers couldn't cover enough ground. Now they can.&lt;/p&gt;

&lt;p&gt;Flat hierarchies created gaps in mentorship and unblocking. Now AI provides those functions.&lt;/p&gt;

&lt;p&gt;Eliminating managers meant losing coordination and communication facilitation. Now transparent, async communication provides that context.&lt;/p&gt;

&lt;p&gt;The core insight: &lt;strong&gt;most organizational complexity exists to compensate for communication and coordination problems that software can solve better than hierarchy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We didn't figure this out because we're smarter. We figured it out because we started small and optimized for shipping, not for organizational structure. As we describe in &lt;a href="https://jetthoughts.com/blog/how-organize-startup-team-structure/" rel="noopener noreferrer"&gt;how we organize startup teams&lt;/a&gt;, flat hierarchies with fewer management levels mean "less bureaucracy and quicker decision-making."&lt;/p&gt;

&lt;p&gt;When you have 6 developers and a client deadline, you don't have the luxury of management layers and synchronization meetings. You build processes that eliminate the need for them.&lt;/p&gt;

&lt;p&gt;Turns out those processes scale better than traditional structure. They just weren't necessary at scale before because individual developer productivity was the constraint.&lt;/p&gt;

&lt;p&gt;AI removes that constraint. Now organizational overhead is the dominant factor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What This Means Practically&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your team is considering how to integrate AI into development workflow, the technical integration is the easy part. The hard part is organizational:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can your team ship small increments?&lt;/strong&gt; AI makes large features faster to write but not faster to review or deploy. If your deployment process takes days, AI just means code sits in branches longer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is context public by default?&lt;/strong&gt; AI can answer technical questions but not "why are we building this?" or "what did we decide last week?" If that context lives in meeting notes and DMs, AI-accelerated development just means more misalignment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do developers have autonomy?&lt;/strong&gt; AI amplifies execution speed but not decision-making speed. If every choice requires approval, you've just moved the bottleneck from coding to coordination. Our approach of &lt;a href="https://jetthoughts.com/blog/building-an-effective-dev-team-strategies/" rel="noopener noreferrer"&gt;giving developers autonomy&lt;/a&gt; means "let them make decisions. Don't micromanage."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is quality built-in or bolted-on?&lt;/strong&gt; AI writes code fast. It also writes bugs fast. If your QA process is separate from development, AI-generated code creates a backlog in testing rather than faster shipping.&lt;/p&gt;

&lt;p&gt;The teams that benefit most from AI aren't those with the best engineers. They're teams with the least organizational friction.&lt;/p&gt;

&lt;p&gt;We've been running that experiment for 13 years. The results are consistent: small teams with clear constraints, transparent communication, and built-in quality ship faster than large teams with flexibility, hierarchical communication, and separate quality processes.&lt;/p&gt;

&lt;p&gt;AI just makes the gap bigger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watching the Industry Catch Up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finding that YouTube video felt validating in a way I didn't expect. Not because we need validation—our &lt;a href="https://jetthoughts.com/" rel="noopener noreferrer"&gt;client retention rate and delivery metrics&lt;/a&gt; speak for themselves.&lt;/p&gt;

&lt;p&gt;But because it suggests the industry is finally ready to question patterns that seemed inevitable. Maybe management layers aren't necessary. Maybe large teams aren't more productive. Maybe coordination overhead isn't just a cost of doing business.&lt;/p&gt;

&lt;p&gt;The Rails and XP communities figured this out through constraint—small teams, tight feedback loops, sustainable pace. The broader industry is discovering it through AI exposing inefficiency.&lt;/p&gt;

&lt;p&gt;Either way, we're moving in the same direction: work smarter by eliminating the organizational patterns that force us to work harder.&lt;/p&gt;

&lt;p&gt;That's the shift worth celebrating.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related Posts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jetthoughts.com/blog/building-an-effective-dev-team-strategies/" rel="noopener noreferrer"&gt;Building an Effective Dev Team: Strategies for Success&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jetthoughts.com/blog/delivery-flow-for-distributed-remote-teams-agile-kanban/" rel="noopener noreferrer"&gt;Delivery Flow for Distributed Remote Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jetthoughts.com/blog/how-get-remote-teams-high-perform-agile-development/" rel="noopener noreferrer"&gt;How to Get Remote Teams to High Perform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jetthoughts.com/blog/cons-of-private-chats-for-team-collaboration-communication-process/" rel="noopener noreferrer"&gt;Cons of Private Chats for Team Collaboration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jetthoughts.com/blog/typical-day-at-jetthoughts-agile-remote/" rel="noopener noreferrer"&gt;A Typical Day at JetThoughts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=c_w0LaFahxk" rel="noopener noreferrer"&gt;The discussion that sparked this post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>management</category>
      <category>ai</category>
      <category>tooling</category>
    </item>
    <item>
      <title>When Machines Learn to Delete: An 8-Week Experiment in AI Autonomy</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Tue, 16 Sep 2025 10:58:56 +0000</pubDate>
      <link>https://forem.com/jetthoughts/when-machines-learn-to-delete-an-8-week-experiment-in-ai-autonomy-2ak</link>
      <guid>https://forem.com/jetthoughts/when-machines-learn-to-delete-an-8-week-experiment-in-ai-autonomy-2ak</guid>
      <description>&lt;p&gt;In 8 weeks, AI agents achieved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;-73%&lt;/strong&gt; code duplication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;-81%&lt;/strong&gt; breaking changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4x&lt;/strong&gt; deployment confidence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;-40%&lt;/strong&gt; maintenance burden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not by coding faster - but by unlearning decades of human dysfunction.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR Story Teaser
&lt;/h2&gt;

&lt;p&gt;When I first set AI agents loose on my codebase, I expected magic. Instead, I got a &lt;strong&gt;perfect 10k lines of duplication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By week 2, the agents had created &lt;strong&gt;seven different authentication systems&lt;/strong&gt;. They weren’t broken. They were simply &lt;em&gt;imitating us&lt;/em&gt;: duplicating, hoarding, avoiding deletion.&lt;/p&gt;

&lt;p&gt;It was like raising an intern who copies every bad habit you never wanted repeated.&lt;/p&gt;

&lt;p&gt;That was the wake-up call. If I wanted AI to replace developers, I couldn’t just make them faster.&lt;br&gt;
I had to teach them something harder: &lt;strong&gt;how to unlearn.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚨 The Mirror Effect
&lt;/h2&gt;

&lt;p&gt;AI does not just write code - it &lt;strong&gt;absorbs the culture it is trained on&lt;/strong&gt;. Feed it enterprise code and it will replicate enterprise dysfunctions.&lt;/p&gt;

&lt;p&gt;At first, the results looked promising: working features delivered quickly. But by &lt;strong&gt;week 2&lt;/strong&gt;, the cracks appeared. I had &lt;strong&gt;seven parallel authentication systems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why? Because the agent had mirrored our deepest organizational habit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never delete what works&lt;/li&gt;
&lt;li&gt;Always build around problems&lt;/li&gt;
&lt;li&gt;Avoid touching fragile code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI wasn’t broken. It was loyal. And the reflection wasn’t flattering.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠 The 8-Week Playbook
&lt;/h2&gt;

&lt;p&gt;I realized I couldn’t just change configs or prompts. I had to change the &lt;em&gt;mindset&lt;/em&gt;.&lt;br&gt;
Here’s how the journey unfolded:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Weeks 1-2: Context over chaos&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Using &lt;a href="https://github.com/zilliztech/claude-context" rel="noopener noreferrer"&gt;Claude-context&lt;/a&gt;, I gave agents a map of the whole system - dependencies, patterns, and relationships.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplication became visible.&lt;/li&gt;
&lt;li&gt;Patterns emerged.&lt;/li&gt;
&lt;li&gt;The question shifted from &lt;em&gt;“what should I build?”&lt;/em&gt; to &lt;em&gt;“what already exists?”&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Weeks 3-4: Micro-cycles replace monuments&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;At first, agents wanted to build cathedrals: giant features, sweeping rewrites.&lt;br&gt;
I stopped that.&lt;/p&gt;

&lt;p&gt;Instead, I taught them to sculpt like XP artisans. One chip at a time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small cycles.&lt;/li&gt;
&lt;li&gt;Immediate validation.&lt;/li&gt;
&lt;li&gt;Failures became &lt;strong&gt;feedback&lt;/strong&gt;, not disasters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Motion without movement turned into &lt;strong&gt;progress without pretense&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Weeks 5-8: Teaching doubt&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;AI loves confidence. Too much.&lt;br&gt;
I forced them to &lt;strong&gt;doubt themselves&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop treating training data as gospel.&lt;/li&gt;
&lt;li&gt;Start checking live best practices.&lt;/li&gt;
&lt;li&gt;Research, verify, and adapt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Half of what they "knew" was outdated. The other half only worked in narrow contexts.&lt;br&gt;
Doubt turned out to be their &lt;strong&gt;superpower&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧭 The Archaeological Method
&lt;/h2&gt;

&lt;p&gt;One day, I asked the agents to refactor a critical component.&lt;br&gt;
Instead of rushing in, they traced its history. Dependency by dependency. Commit by commit.&lt;/p&gt;

&lt;p&gt;They worked like archaeologists, brushing away dust before touching the artifact.&lt;/p&gt;

&lt;p&gt;Yes, it was slower. But it was also &lt;em&gt;surgical&lt;/em&gt;. Refactors propagated cleanly instead of exploding.&lt;br&gt;
The chaos of spaghetti code gave way to order.&lt;/p&gt;

&lt;p&gt;It felt like watching a new species rediscover an old, forgotten craft: reading before writing.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 Strategic Feedback Loops
&lt;/h2&gt;

&lt;p&gt;Most testing strategies collapse under their own weight. Agents thrived when I reduced it to &lt;strong&gt;three fast lenses&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Microscope&lt;/strong&gt; - 10-second checks after micro-changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Magnifying glass&lt;/strong&gt; - 5-minute validations after atomic updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wide-angle&lt;/strong&gt; - 30-second smoke tests on production flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like switching between camera modes, each lens revealed a different truth.&lt;br&gt;
Together, they built enough confidence to let the agents move fast &lt;em&gt;without breaking everything&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 The Numbers That Matter
&lt;/h2&gt;

&lt;p&gt;By the end of 8 weeks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Code duplication: -73%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Breaking changes: -81%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment confidence: 4x&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance burden: -40%&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real transformation wasn’t numeric. It was &lt;strong&gt;philosophical&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deletion became an &lt;em&gt;achievement&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Simplicity became a &lt;em&gt;reward&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Understanding became more valuable than “velocity”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agents stopped building empires. They started tending &lt;strong&gt;gardens&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚖️ The Uncomfortable Truth
&lt;/h2&gt;

&lt;p&gt;Here’s what stings:&lt;br&gt;
The biggest barrier to AI replacing developers isn’t technical. It’s &lt;strong&gt;cultural&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protect complexity because it feels like job security&lt;/li&gt;
&lt;li&gt;Avoid deletion because it feels destructive&lt;/li&gt;
&lt;li&gt;Defend outdated practices because they’re familiar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whole industries exist to manage complexity that never needed to exist.&lt;br&gt;
The machines don’t share those fears.&lt;/p&gt;

&lt;p&gt;And once tuned, they stop copying our anxieties and start living up to our aspirations.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Your Next Move
&lt;/h2&gt;

&lt;p&gt;If you want AI to &lt;em&gt;really&lt;/em&gt; replace developers, you need to retrain it - and yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The playbook is simple:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Index your codebase with &lt;a href="https://github.com/zilliztech/claude-context" rel="noopener noreferrer"&gt;Claude-context&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Break work into micro-cycles with immediate validation&lt;/li&gt;
&lt;li&gt;Teach agents to research, not recall&lt;/li&gt;
&lt;li&gt;Reward deletion as much as creation&lt;/li&gt;
&lt;li&gt;Build feedback loops faster than fear&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not about pushing harder. It’s about creating the conditions where the right actions emerge naturally.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Final Thought
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;AI will not replace developers by coding faster.&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;It will replace them when it learns to delete, simplify, and validate better than we do.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The tools exist. The patterns are clear. The only question is whether we are brave enough to let our creations be better than their creators.&lt;/p&gt;




&lt;h2&gt;
  
  
  ❓ Reader Reflection
&lt;/h2&gt;

&lt;p&gt;If AI agents mirrored your team tomorrow, what would they reveal?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Would they build duplicates instead of fixing old code?&lt;/li&gt;
&lt;li&gt;Would they optimize for motion instead of outcomes?&lt;/li&gt;
&lt;li&gt;Would they protect complexity instead of simplifying?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What habits would &lt;em&gt;your&lt;/em&gt; AI copy - and which ones would you want it to unlearn first?&lt;/p&gt;

&lt;p&gt;Drop your thoughts in the comments. 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>management</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>When Small Method Choices Cascade Into Big Performance Wins</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Wed, 03 Sep 2025 21:45:05 +0000</pubDate>
      <link>https://forem.com/jetthoughts/when-small-method-choices-cascade-into-big-performance-wins-1e87</link>
      <guid>https://forem.com/jetthoughts/when-small-method-choices-cascade-into-big-performance-wins-1e87</guid>
      <description>&lt;p&gt;Three months ago, I spent an embarrassing amount of time optimizing a complex Redis caching layer - tweaking expiration strategies, adding compression, even experimenting with different serialization formats. The performance improvements were modest at best. Then, almost accidentally, I discovered that a single line of string processing code was consuming 40% of our CPU cycles.&lt;/p&gt;

&lt;p&gt;The culprit? Using &lt;code&gt;gsub&lt;/code&gt; instead of &lt;code&gt;tr&lt;/code&gt; for simple character replacement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# What we had (processing thousands of slugs per request)&lt;/span&gt;
&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The simple fix that changed everything&lt;/span&gt;
&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what that looked like in our benchmarks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'benchmark/ips'&lt;/span&gt;

&lt;span class="no"&gt;SLUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'high-performance-web-apps'&lt;/span&gt;

&lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ips&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gsub'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;SLUG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tr'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;SLUG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare!&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# String#tr: 2,121,629.8 i/s&lt;/span&gt;
&lt;span class="c1"&gt;# String#gsub: 474,970.5 i/s - 4.90x slower&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference isn't subtle - we're talking about a 5x performance gap. When you're processing thousands of these conversions per request, that compounds fast.&lt;/p&gt;

&lt;p&gt;This experience taught me something uncomfortable: sometimes the biggest performance wins hide in the smallest details. While I was architecting elaborate caching strategies, the real bottleneck was a basic method choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The count/length/size confusion that keeps biting people
&lt;/h2&gt;

&lt;p&gt;Rails developers run into this trap constantly, and I've done it myself more times than I care to admit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This will load every single record into memory&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;  &lt;span class="c1"&gt;# SELECT * FROM users (!!!)&lt;/span&gt;

&lt;span class="c1"&gt;# This hits the database with COUNT(*)&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;   &lt;span class="c1"&gt;# SELECT COUNT(*) FROM users&lt;/span&gt;

&lt;span class="c1"&gt;# This tries to be smart about it&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;    &lt;span class="c1"&gt;# Uses COUNT(*) if not loaded, length if already in memory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I watched a colleague's dashboard go from snappy to timing out because they assumed &lt;code&gt;.length&lt;/code&gt; and &lt;code&gt;.count&lt;/code&gt; were interchangeable. The users table had grown to 800,000 records. That &lt;code&gt;.length&lt;/code&gt; call suddenly meant loading nearly a million ActiveRecord objects just to count them.&lt;/p&gt;

&lt;p&gt;The frustrating part? This knowledge exists in every Rails developer's head somewhere. But under deadline pressure, these details slip through code review.&lt;/p&gt;

&lt;h2&gt;
  
  
  N+1 queries have evolved (and gotten sneakier)
&lt;/h2&gt;

&lt;p&gt;Everyone knows about the classic N+1 problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;  &lt;span class="c1"&gt;# N database hits&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But modern applications create more sophisticated variants. Last year I debugged a GraphQL API where the query pattern looked innocent enough, but nested resolvers were creating exponential query growth. Not N+1, but N×M×P queries spiraling into thousands of database hits.&lt;/p&gt;

&lt;p&gt;The textbook solution is &lt;code&gt;includes&lt;/code&gt;, but that brings its own problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Seems right, but can be a memory bomb&lt;/span&gt;
&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;comments: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:reactions&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Better for large datasets - be selective&lt;/span&gt;
&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;comments: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="s1"&gt;'approved'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's something the Rails guides don't emphasize enough: &lt;code&gt;includes&lt;/code&gt; can actually make things worse for large datasets. When you're joining tables with hundreds of thousands of rows, that LEFT OUTER JOIN might generate a cartesian product that exhausts your memory before Rails even starts instantiating objects.&lt;/p&gt;

&lt;p&gt;For read-heavy endpoints, I've started reaching for PostgreSQL's JSON aggregation instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Skip ActiveRecord entirely for heavy read operations&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="n"&gt;json_agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'price'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;items_json&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You lose ActiveRecord's convenience, but gain 50-90% reduction in memory usage. Sometimes that trade-off makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database indexes that actually match your queries
&lt;/h2&gt;

&lt;p&gt;Adding indexes feels straightforward until you start looking at your actual query patterns. Many developers create single-column indexes without considering how their WHERE clauses work together.&lt;/p&gt;

&lt;p&gt;Here's a real example from an e-commerce platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- What most people start with&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_user_id&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_created_at&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- What actually matches the query pattern&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_user_date_status&lt;/span&gt; 
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;INCLUDE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shipping_address_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;INCLUDE&lt;/code&gt; clause creates a covering index - PostgreSQL can satisfy the entire query without touching the main table. For their most common dashboard query, this cut I/O operations by 73%.&lt;/p&gt;

&lt;p&gt;But here's where it gets interesting: PostgreSQL's query planner is cost-based, not rule-based. It looks at your data distribution to choose between available indexes. I've seen perfectly good indexes get ignored because the table statistics were stale.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Force a statistics update after major data changes&lt;/span&gt;
&lt;span class="k"&gt;ANALYZE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- For skewed data distributions, increase sample size&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;STATISTICS&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line can be a game-changer. The default statistics target is 100 samples, but if you have columns where some values are much more common than others (like user_id where power users have thousands of orders), increasing this helps the planner make better choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching strategies that don't blow up in production
&lt;/h2&gt;

&lt;p&gt;After debugging enough cache-related outages, I've learned that caching isn't really about storing data temporarily. It's about managing state synchronization across distributed systems while keeping things roughly consistent.&lt;/p&gt;

&lt;p&gt;The approach that's worked best for me involves multiple layers with different consistency guarantees:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser caching for immutable assets:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Webpack config for long-term caching&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name].[contenthash].js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;chunkFilename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[id].[contenthash].chunk.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Redis for application state:&lt;/strong&gt;&lt;br&gt;
The pattern I keep coming back to handles the cache stampede problem - when your cache expires during a traffic spike and every request triggers an expensive recomputation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;CacheManager&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_with_grace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ttl: &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;grace_period: &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remaining_ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;multi&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Serve stale content while recomputing in background&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;remaining_ttl&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;grace_period&lt;/span&gt;
      &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;recompute_and_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="c1"&gt;# First miss - compute with distributed lock&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lock:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;recompute_and_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern has prevented numerous outages. Instead of having 100 concurrent requests all trying to regenerate the same expensive data, one request does the work while others get slightly stale content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fragment caching that actually works:&lt;/strong&gt;&lt;br&gt;
Rails' Russian Doll caching is underused, probably because it requires thinking about cache dependencies upfront:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@current_user&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"product"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pricing'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'pricing_info'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;product: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="n"&gt;variant&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'variant'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;variant: &lt;/span&gt;&lt;span class="n"&gt;variant&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a single variant updates, only that fragment invalidates. For a product with 50 variants, that's 2% cache invalidation instead of 100%. The &lt;code&gt;'v1'&lt;/code&gt; version string lets you bust all fragments when you deploy template changes - without it, you'll serve mixed HTML from different code versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making CI/CD faster (the productivity multiplier everyone ignores)
&lt;/h2&gt;

&lt;p&gt;Every minute you shave off CI is a minute returned to every developer, multiple times per day. The compound effect adds up to hours per week per person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker layer optimization:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="c"&gt;# System dependencies change rarely - cache these layers&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; python3 make g++

&lt;span class="c"&gt;# Dependencies change occasionally&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production

&lt;span class="c"&gt;# App code changes frequently - put this last&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the real win is using BuildKit's cache mounts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/root/.npm &lt;span class="se"&gt;\
&lt;/span&gt;    npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This persists npm's cache between builds. Three-minute dependency installs become 20-second cache hits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel test execution:&lt;/strong&gt;&lt;br&gt;
The trick isn't just splitting tests - it's ensuring they don't interfere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/database.yml&lt;/span&gt;
&lt;span class="ss"&gt;test:
  database: &lt;/span&gt;&lt;span class="n"&gt;myapp_test&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= ENV['TEST_ENV_NUMBER'] %&amp;gt;

# Each process gets its own schema
$ parallel_rspec spec/
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We went from 45-minute test suites to 12 minutes with four parallel processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dealing with flaky tests:&lt;/strong&gt;&lt;br&gt;
Rather than letting flaky tests poison your entire suite, quarantine them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// jest.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;testMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;/src/**/*.test.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;testPathIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;/src/**/*.flaky.test.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quarantine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;testMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;/src/**/*.flaky.test.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flaky tests run separately with retries. They don't block deployments, but they also don't get ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance budgets as guardrails
&lt;/h2&gt;

&lt;p&gt;Teams that maintain good performance over time have embedded performance constraints into their development process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bundles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"300 KB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"warning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"250 KB"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"firstContentfulPaint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"largestContentfulPaint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"totalBlockingTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every pull request runs against these budgets. Exceed them and the build fails. Want to add that 50KB analytics library? Fine, but what are you removing to stay under budget?&lt;/p&gt;

&lt;h2&gt;
  
  
  Some numbers from actual systems
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;E-commerce platform (last year):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switched from individual Redis keys to hash data structures&lt;/li&gt;
&lt;li&gt;Added covering indexes for product catalog queries
&lt;/li&gt;
&lt;li&gt;Implemented fragment caching with 5-minute TTL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; 47ms → 12ms 95th percentile response time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;B2B SaaS dashboard:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replaced nested GraphQL N+1 patterns with DataLoader&lt;/li&gt;
&lt;li&gt;Set up database connection pooling via PgBouncer&lt;/li&gt;
&lt;li&gt;Moved CI from Jenkins to GitHub Actions with build caching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; 3.2s → 340ms API response time, 45-minute → 8-minute builds&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technologies worth watching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;eBPF for production observability:&lt;/strong&gt;&lt;br&gt;
Being able to trace performance issues in production with near-zero overhead changes how you debug problems. No more "works fine in staging" mysteries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WASM at the edge:&lt;/strong&gt;&lt;br&gt;
For compute-intensive operations, running WebAssembly modules at edge locations gives you 95% of native performance with 50x faster cold starts than containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ruby's YJIT compiler:&lt;/strong&gt;&lt;br&gt;
If you're on Ruby 3.2+, enabling YJIT can improve performance by 15-40% with a one-line configuration change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;If this resonates but feels overwhelming, pick one area:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 1:&lt;/strong&gt; Add basic monitoring. Get 95th percentile response times on a dashboard you actually look at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 2:&lt;/strong&gt; Find your worst N+1 query and fix it. Use something like rack-mini-profiler to spot them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 3:&lt;/strong&gt; Add one performance test to CI - just your most critical user path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 4:&lt;/strong&gt; Set up performance budgets for your main JavaScript bundle.&lt;/p&gt;

&lt;p&gt;Performance work compounds. Minor improvements stack up over time, but only if you measure consistently and fix regressions quickly.&lt;/p&gt;

&lt;p&gt;The teams that build fast applications treat performance as a feature, not an afterthought. They measure continuously, optimize incrementally, and never assume their systems will stay fast without attention.&lt;/p&gt;

&lt;p&gt;Start small, measure everything, and remember that the most impactful optimizations often hide in the details everyone takes for granted.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>performance</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Overcommitment Trap: A Systems Design Problem</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Fri, 23 May 2025 12:45:11 +0000</pubDate>
      <link>https://forem.com/jetthoughts/the-overcommitment-trap-a-systems-design-problem-57kl</link>
      <guid>https://forem.com/jetthoughts/the-overcommitment-trap-a-systems-design-problem-57kl</guid>
      <description>&lt;p&gt;Traditional thinking treats overcommitment as a willpower issue. But what if it's actually a product design problem—where the "product" is your organizational operating system?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl26udds4lpuiz2oqckon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl26udds4lpuiz2oqckon.png" alt="man with multiple distractions" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reframing the Dynamic:
&lt;/h2&gt;

&lt;p&gt;Teams under pressure don't just take on more work—they fundamentally alter their decision-making architecture. The system shifts from "optimize for outcomes" to "optimize for activity." This creates a feedback loop where busyness becomes the primary signal of progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Underlying Pattern:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input overload&lt;/strong&gt; → Context switching costs compound&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognitive load increases&lt;/strong&gt; → Decision quality decreases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local optimizations&lt;/strong&gt; → Global system performance degrades&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short-term reactive mode&lt;/strong&gt; → Strategic work gets deprioritized&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Systems Lens:
&lt;/h2&gt;

&lt;p&gt;Think of team capacity not as a bucket you fill, but as a network's bandwidth. When you exceed optimal load, you don't get proportional output—you get exponentially diminishing returns plus system instability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Principles for Pressure:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit capacity modeling&lt;/strong&gt;: Make workload visible and measurable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default to subtraction&lt;/strong&gt;: Build "what stops" into planning rituals
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive throttling&lt;/strong&gt;: Design automatic pressure release mechanisms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local autonomy&lt;/strong&gt;: Let teams self-regulate based on real-time feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Meta-Challenge:
&lt;/h2&gt;

&lt;p&gt;Organizations often reward overcommitment behavior while simultaneously wanting sustainable performance. This creates a classic misaligned incentive structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Takeaway:
&lt;/h2&gt;

&lt;p&gt;Treat overcommitment as a system architecture problem, not a people problem. Design organizational constraints that make saying "no" the easier path.&lt;/p&gt;

&lt;p&gt;What systemic patterns have you observed when teams hit capacity limits?​​​​​​​​​​​​​​​​&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>Implementing Instant Search, Dynamic Forms, and Infinite Scroll with Hotwire and Turbo in Rails</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Wed, 26 Mar 2025 20:18:48 +0000</pubDate>
      <link>https://forem.com/jetthoughts/implementing-instant-search-dynamic-forms-and-infinite-scroll-with-hotwire-and-turbo-in-rails-18p</link>
      <guid>https://forem.com/jetthoughts/implementing-instant-search-dynamic-forms-and-infinite-scroll-with-hotwire-and-turbo-in-rails-18p</guid>
      <description>&lt;p&gt;Despite &lt;a href="https://hotwired.dev/" rel="noopener noreferrer"&gt;Hotwire's&lt;/a&gt; growing popularity, many developers struggle with implementing it correctly. Common pitfalls lead to broken interactions, performance bottlenecks, or unmaintainable code. In this guide, I'll walk you through the idiomatic integration of Hotwire for the most common use case: a browse page with instant search, infinite scrolling, dynamic per-record actions, and cursor-based pagination—all with minimal JavaScript and maximum performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; This post shows you how to build an interactive employee directory with instant search, dynamic forms, and infinite scrolling using just Hotwire (minimal JS).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03n4xghlmownhrf9axhp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03n4xghlmownhrf9axhp.gif" alt="interactive browse employees page" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5-Minute Quick Start Guide
&lt;/h2&gt;

&lt;p&gt;For the impatient developers, here's the executive summary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create Search Form with Dynamic Fields&lt;/strong&gt;: Encapsulate search logic outside your controller &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure your HTML with Turbo Frames&lt;/strong&gt;: Wrap your search form and results table in frames&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add auto-submission with Stimulus&lt;/strong&gt;: Create a 5-line controller to debounce input events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setup infinite scrolling&lt;/strong&gt;: Use auto-loading next pages with lazy loading turbo frames&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respond with Turbo Streams&lt;/strong&gt;: Update just what changed, not the entire page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now let's dive into the details!&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding When to Use Turbo Frames vs. Streams
&lt;/h2&gt;

&lt;p&gt;Before we implement anything, let's clarify which Hotwire tool fits each job:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;When to use Turbo Frames&lt;/th&gt;
&lt;th&gt;When to use Turbo Streams&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Updating a single, isolated section&lt;/td&gt;
&lt;td&gt;Updating multiple parts of the page at once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Natural parent-child relationships&lt;/td&gt;
&lt;td&gt;Non-hierarchical updates (flash messages)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple replacements&lt;/td&gt;
&lt;td&gt;Complex operations (append, prepend, remove)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lazy-loading content&lt;/td&gt;
&lt;td&gt;Real-time updates from broadcasts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For our search functionality, we'll use both: frames for the overall structure and streams for the dynamic updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Patterns We'll Cover
&lt;/h2&gt;

&lt;p&gt;This guide focuses on implementing these key patterns with minimal JavaScript:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Instant search with on-keyup events&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic filtering with dropdowns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficient pagination without page reloads&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flash message updates&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditional action links and toggles&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll use an employee listing page as our practical example.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Search: Form Object Pattern in 3 Steps
&lt;/h2&gt;

&lt;p&gt;Let's be honest—no one wants a 200-line controller method full of filtering logic. Here's how we corral that filter chaos into a single object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/employee/filter.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee::Filter&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;

  &lt;span class="no"&gt;PERMITTED_PARAMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:team_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:manager_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;PERMITTED_PARAMS&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;team_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_id&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_param&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;query: &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;team_id: &lt;/span&gt;&lt;span class="n"&gt;team_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;manager_id: &lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;
    &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt; &lt;span class="c1"&gt;# Removes nil values&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;blank?&lt;/span&gt;
    &lt;span class="n"&gt;to_param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🧠 Why use a &lt;a href="https://thoughtbot.com/ruby-science/introduce-form-object.html" rel="noopener noreferrer"&gt;Form Object&lt;/a&gt;?&lt;/strong&gt; Without this, your controller becomes a dumping ground for filter logic or your model gets bloated with scopes that only apply to this specific UI. Form objects give you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Testability&lt;/strong&gt;: Unit test filters without controller overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: The same filter works across different views or APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean parameters&lt;/strong&gt;: Explicit whitelist prevents mass assignment vulnerabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. Implementing Controller Logic That Won't Make You Cry
&lt;/h2&gt;

&lt;p&gt;Your controller should be thin and focused on coordination. Here's how we keep it simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/admin/employees_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Admin&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmployeesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseController&lt;/span&gt;
    &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:set_filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:search&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# ... other before_actions ...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
      &lt;span class="vi"&gt;@teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ordered&lt;/span&gt;
      &lt;span class="vi"&gt;@managers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;managers&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;
      &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employees_collection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="vi"&gt;@pagy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@employees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pagy_keyset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turbo_stream&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# ... other actions ...&lt;/span&gt;

    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;employees_collection&lt;/span&gt;
      &lt;span class="c1"&gt;# Base query with eager loading to avoid N+1 issues&lt;/span&gt;
      &lt;span class="no"&gt;Employee&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;featured&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:team&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;direct_reports: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;snippets: :week&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: :asc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_filter&lt;/span&gt;
      &lt;span class="vi"&gt;@filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;filter_params&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:employee_filter&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PERMITTED_PARAMS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:employee_filter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🚀 Performance Tip:&lt;/strong&gt; Notice the &lt;code&gt;includes&lt;/code&gt; to eager load associations? This prevents N+1 queries that would kill your server under load. Always profile with the Bullet gem if you're filtering large datasets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Building the Interactive Search Form That Updates as You Type
&lt;/h2&gt;

&lt;p&gt;Here's the magic ingredient - a search form that submits automatically when the user types or changes a dropdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;_search_and_filters.html.erb&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name_filter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form_with&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;
        &lt;span class="na"&gt;model:&lt;/span&gt; &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;url:&lt;/span&gt; &lt;span class="na"&gt;admin_employees_path&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;role:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; 
        &lt;span class="na"&gt;method:&lt;/span&gt; &lt;span class="na"&gt;:get&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;local:&lt;/span&gt; &lt;span class="na"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;This&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt; &lt;span class="na"&gt;AJAX&lt;/span&gt; &lt;span class="na"&gt;form&lt;/span&gt; &lt;span class="na"&gt;submission&lt;/span&gt;
        &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;controller:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;auto-submit&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;  &lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;Stimulus&lt;/span&gt; &lt;span class="na"&gt;controller&lt;/span&gt;
          &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="na"&gt;:filtered_employees&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;Target&lt;/span&gt; &lt;span class="na"&gt;frame&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt; &lt;span class="na"&gt;update&lt;/span&gt;
          &lt;span class="na"&gt;action:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;input-&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;auto-submit#submit change-&amp;gt;auto-submit#submit",  # Events that trigger submission
          auto_submit_delay_value: 300  # Debounce delay in milliseconds
        }
      ) do |form| %&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;fieldset&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"group"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form.search_field&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;:query&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;placeholder:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;autofocus:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;link_to&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Clear&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
        &lt;span class="na"&gt;admin_employees_path&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;employee_filter:&lt;/span&gt; &lt;span class="na"&gt;Employee::Filter.new.to_param&lt;/span&gt;&lt;span class="err"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;role:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
        &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_frame:&lt;/span&gt; &lt;span class="na"&gt;:categorized_employees&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form.select&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;:status&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;employee_statuses_choices&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;include_blank:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Non-Archived&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form.collection_select&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;:team_id&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;teams&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;:id&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;:name&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;include_blank:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;All&lt;/span&gt; &lt;span class="na"&gt;Teams&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;form.collection_select&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;:manager_id&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;managers&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;:id&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;:name&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;include_blank:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;All&lt;/span&gt; &lt;span class="na"&gt;Managers&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The secret sauce is this tiny Stimulus controller that debounces input events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/controllers/auto_submit_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestSubmit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delayValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Usability Warning:&lt;/strong&gt; Don't set the delay too low! A 300ms delay prevents hammering your server while still feeling instant to users. Test with real users before tweaking this value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Structuring Your HTML Tables with Turbo (Without Breaking Them)
&lt;/h2&gt;

&lt;p&gt;HTML tables require special care with Turbo. You can't wrap &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; elements directly with Turbo Frames as it breaks HTML validity. Here's how to do it right:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;_employees_list.html.erb&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="na"&gt;:categorized_employees&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;search_and_filters&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;filter:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;teams:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;teams&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="na"&gt;:filtered_employees&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"striped"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
          &lt;span class="c"&gt;&amp;lt;!-- Other headers --&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Actions&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"employees_table_rows"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;employees.empty&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;No items found.&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;else&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;
                &lt;span class="na"&gt;:employees_first_page&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;src:&lt;/span&gt; &lt;span class="na"&gt;search_admin_employees_path&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;employee_filter:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;filter.to_param&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;format:&lt;/span&gt; &lt;span class="na"&gt;:turbo_stream&lt;/span&gt;&lt;span class="err"&gt;),&lt;/span&gt;
                &lt;span class="na"&gt;loading:&lt;/span&gt; &lt;span class="na"&gt;:lazy&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_cache:&lt;/span&gt; &lt;span class="na"&gt;false&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;
              &lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🔍 Key Concept:&lt;/strong&gt; Notice our nested frame structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outer frame &lt;code&gt;:categorized_employees&lt;/code&gt; contains search + results&lt;/li&gt;
&lt;li&gt;Inner frame &lt;code&gt;:filtered_employees&lt;/code&gt; holds just the table&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;turbo_cache: false&lt;/code&gt; to prevent stale results&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Dynamic Search Form
&lt;/h3&gt;

&lt;p&gt;An important architectural decision is wrapping both the search form and results in a single Turbo Frame. This isn't just organizational—it's essential for dynamic fields that depend on search context:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interdependent Filters&lt;/strong&gt;: When selecting a team should filter available managers, both components need updating together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context-Aware Options&lt;/strong&gt;: Some filter options may only be relevant based on other selections (e.g., team-specific statuses).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficiency in Re-rendering&lt;/strong&gt;: When certain filter changes require rebuilding multiple form elements, a shared frame ensures consistent updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State Preservation&lt;/strong&gt;: On larger changes, preserving the relationship between filters and their results prevents UI inconsistency.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, imagine selecting "Engineering" team changes available manager options. With a single frame, one request can update both the managers dropdown and the filtered results without awkward intermediate states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;In&lt;/span&gt; &lt;span class="na"&gt;your&lt;/span&gt; &lt;span class="na"&gt;controller&lt;/span&gt; &lt;span class="na"&gt;response&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_stream.update&lt;/span&gt; &lt;span class="na"&gt;:categorized_employees&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;search_and_filters&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;filter:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;teams:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;teams&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;managers:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;filtered_managers&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;filtered_results&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;employees:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this pattern, managing dependent form fields becomes a complicated chain of individual updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Handling Turbo Stream Responses Without the Headaches
&lt;/h2&gt;

&lt;p&gt;When the search form is submitted, your controller responds with this elegant Turbo Stream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;search.turbo_stream.erb&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_stream.update&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;employees_table_rows&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="na"&gt;partial:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employee&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;collection:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;load_next_employees&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;pagy.next&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See how this updates just the table rows without refreshing anything else? That's what makes the interaction feel native and smooth.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Implementing "Load More" for Infinite Scrolling (No More Page Numbers!)
&lt;/h2&gt;

&lt;p&gt;For a modern UX, forget about page numbers. Here's how to implement a "Load More" button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;_load_next_employees.html.erb&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"load_more"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;link_to&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Load&lt;/span&gt; &lt;span class="na"&gt;More&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; 
                &lt;span class="na"&gt;search_admin_employees_path&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;
                  &lt;span class="na"&gt;employee_filter:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;filter.to_param&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; 
                  &lt;span class="na"&gt;after:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;employees.last.id&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; 
                  &lt;span class="na"&gt;format:&lt;/span&gt; &lt;span class="na"&gt;:turbo_stream&lt;/span&gt;
                &lt;span class="err"&gt;),&lt;/span&gt;
                &lt;span class="na"&gt;class:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt; &lt;span class="na"&gt;outline&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; 
                &lt;span class="na"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;turbo_stream:&lt;/span&gt; &lt;span class="na"&gt;true&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When clicking "Load More", we append new rows instead of replacing existing ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;search_more.turbo_stream.erb&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_stream.remove&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;load_more&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_stream.append&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;employees_table_rows&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="na"&gt;partial:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;employee&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;collection:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;employees&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;render&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;load_next_employees&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;pagy.next&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Traditional offset pagination is terrible for performance on large datasets. Use keyset pagination (based on IDs or timestamps) for dramatically faster queries when implementing "Load More" patterns.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  7. Flash Messages That Update Without Page Refreshes
&lt;/h2&gt;

&lt;p&gt;Flash messages should feel seamless too. Here's the pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="na"&gt;#&lt;/span&gt; &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;_flash.html.erb&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="na"&gt;:flash&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;flash.each&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;next&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;message.blank&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;%= type %&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"close"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"this.parentElement.remove()"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;✕&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your controller actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
  &lt;span class="vi"&gt;@employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy!&lt;/span&gt;

  &lt;span class="n"&gt;respond_to&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;admin_employees_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;notice: &lt;/span&gt;&lt;span class="s2"&gt;"Employee was successfully removed."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turbo_stream&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:notice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Employee was successfully removed."&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;turbo_stream: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;turbo_stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@employee&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;turbo_stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"flash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s2"&gt;"shared/flash"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52j5i8igv2k0ikgmvuwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52j5i8igv2k0ikgmvuwt.png" alt="Example list of employees page with table, filters, search" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Common Gotchas and How to Fix Them
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Don't Put Turbo Frames Around Table Rows
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- DON'T DO THIS: It breaks HTML validity --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;employee_row&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- DO THIS INSTEAD: Frame inside table cell --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;%= dom_id(employee) %&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="na"&gt;employee&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;:actions&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- Action buttons inside a frame in the TD --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Don't Forget About Flash Messages
&lt;/h3&gt;

&lt;p&gt;Always include flash updates in your Turbo Stream responses when actions change state.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Don't Redirect Inside Turbo Stream Responses
&lt;/h3&gt;

&lt;p&gt;Redirects in Turbo Stream responses don't work as expected. Instead, render the appropriate streams directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Real-World Performance Considerations
&lt;/h2&gt;

&lt;p&gt;When implementing these patterns in production, keep these tips in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Index Everything&lt;/strong&gt;: Add database indexes for all filtered columns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Monitoring&lt;/strong&gt;: Watch for N+1 queries with the Bullet gem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fragment Caching&lt;/strong&gt;: Consider Russian-doll caching for complex partials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debounce Searches&lt;/strong&gt;: Don't hit your database on every keystroke&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend Performance&lt;/strong&gt;: Use the Chrome Network tab to spot redundant requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Take It to the Next Level: Your Challenge
&lt;/h2&gt;

&lt;p&gt;Now it's your turn! Add a "Sort by Date" button using Turbo Streams to your employee listing.&lt;br&gt;
Bonus points if you make it toggle between ascending and descending order with a single click.&lt;/p&gt;

&lt;p&gt;Tweet your solution to &lt;a href="https://x.com/jetthoughts" rel="noopener noreferrer"&gt;@JetThoughts&lt;/a&gt; for a code review from our team!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hotwired.dev/" rel="noopener noreferrer"&gt;Hotwire Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stimulus.hotwired.dev/handbook/introduction" rel="noopener noreferrer"&gt;Stimulus Handbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/" rel="noopener noreferrer"&gt;Ruby on Rails Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;What patterns have you implemented with Hotwire? Share your experiences in the comments below!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.linkedin.com/in/paul-keen/" rel="noopener noreferrer"&gt;Paul Keen&lt;/a&gt; is a technical leader and consultant exploring engineering leadership practices in Berlin.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>hotwire</category>
      <category>stimulus</category>
      <category>turbo</category>
    </item>
    <item>
      <title>Tips to organize a lot of information?</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Sat, 03 Dec 2022 08:23:39 +0000</pubDate>
      <link>https://forem.com/pftg/how-to-organize-a-lot-of-information-i43</link>
      <guid>https://forem.com/pftg/how-to-organize-a-lot-of-information-i43</guid>
      <description>&lt;p&gt;There are many different ways to organize a large amount of information, and the best method will depend on the specific information you are trying to organize and the goals you have for using that information. Here are a few general tips that can help you organize a lot of information effectively:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Start by breaking the information down into smaller, more manageable chunks. For example, you might divide a large dataset into smaller sets based on different categories or themes. This can make it easier to work with the information and identify patterns and trends.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a system for labeling and organizing the information. This could involve using folders or file cabinets to physically organize paper documents, or using digital tools such as databases or spreadsheets to organize and sort information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use keywords and tags to help you search for and retrieve specific pieces of information. For example, if you are organizing a large collection of research papers, you might use keywords to describe the main topic of each paper and tag each paper with relevant information such as the author's name, the date it was published, and the journal it appeared in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regularly review and update your organization system. As you add more information, you may need to revise your system to ensure that it continues to work well for you. This might involve re-categorizing information or adding new keywords and tags.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consider using a tool to help you manage and organize your information. There are many different tools available, such as project management software, note-taking apps, and reference management software, that can make it easier to organize and access a large amount of information.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@freegraphictoday?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;AbsolutVision&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/organize-information?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>management</category>
      <category>information</category>
    </item>
    <item>
      <title>What would Paul Keen do if he had $100B?</title>
      <dc:creator>Paul Keen</dc:creator>
      <pubDate>Wed, 09 Nov 2022 18:33:49 +0000</pubDate>
      <link>https://forem.com/pftg/what-would-paul-keen-do-if-he-had-100b-38lb</link>
      <guid>https://forem.com/pftg/what-would-paul-keen-do-if-he-had-100b-38lb</guid>
      <description>&lt;p&gt;If I had $100B, I would get a heart attack :) This is 500-lifetime earnings for the average US citizen. But easily could be wasted by four generations.&lt;/p&gt;

&lt;p&gt;It is a vast life change event, not only for me but for all related to me people. With all those opportunities, many problems need to be reviewed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secure Money First
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49zv3gq2wd2f1xxanu3n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49zv3gq2wd2f1xxanu3n.jpg" alt="Finance Charts" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I need to secure the money once I get them. Then I'd split it into 2 parts: to build a place to live and to invest in generating constant income.&lt;/p&gt;

&lt;p&gt;Investment should be the giant pile, which will generate enough dividends to increase capital and cover all day-to-day expenses.&lt;/p&gt;

&lt;p&gt;When money becomes no issue, the next step is to make life pleasant &amp;amp; longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change Life
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9jq7gidn4p1tsp86msxo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9jq7gidn4p1tsp86msxo.jpg" alt="Life is good" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd buy an island and build a small community there. On the island, I'd set up a leisure &amp;amp; working environment for residents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F625msvvinc4o2dphas4k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F625msvvinc4o2dphas4k.jpg" alt="Island for life" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would free up enough time to be with family. Together, we could travel worldwide, learn new things, and have unique experiences (including extreme ones).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09h5cfbdcib6c64t5g5g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09h5cfbdcib6c64t5g5g.jpg" alt="family on the beach" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To live longer, I would spend more on a healthier lifestyle and invest in medical research. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qvr40pmuj9y5a4d6076.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qvr40pmuj9y5a4d6076.jpg" alt="Healthy Lifestyle" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would start a project where I could deal with challenges. I need to have something which will wake me at night ;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupdhhc6spu5eie9uf8vy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupdhhc6spu5eie9uf8vy.jpg" alt="Challenges" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Organize regular investments in charity organizations and projects/companies to solve the next humanity's challenges. We need to help Earth survive.&lt;/p&gt;

&lt;p&gt;Some dividends will be shared with relatives and friends.&lt;/p&gt;

&lt;p&gt;Would I be happier? Sure! And I'd like to make it as long as possible :D&lt;/p&gt;




&lt;p&gt;&lt;sup&gt;Photos by:&lt;/sup&gt;&lt;br&gt;
&lt;sup&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;sup&gt;&lt;a href="https://unsplash.com/@kenziem?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Mackenzie Marco&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/a-lot-of-money?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;sup&gt;&lt;a href="https://unsplash.com/@nampoh?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Maxim Hopman&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;sup&gt;&lt;a href="https://unsplash.com/@kalvisuals?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;KAL VISUALS&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/happy-life?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;sup&gt;&lt;a href="https://unsplash.com/@zunnu?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Zunnoon Ahmed&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/island?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;sup&gt;&lt;a href="https://unsplash.com/@jennyhill?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Jenny Hill&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/health?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;sup&gt;&lt;a href="https://unsplash.com/@apsprudente?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Patricia Prudente&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/family?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;
&lt;sup&gt;&lt;a href="https://unsplash.com/@dankapeter?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Danka &amp;amp; Peter&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/challenges?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>personal</category>
      <category>blog</category>
      <category>essay</category>
    </item>
  </channel>
</rss>
