<?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>DEV Community: David Paluy</title>
    <description>The latest articles on DEV Community by David Paluy (@dpaluy).</description>
    <link>https://dev.to/dpaluy</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%2F145919%2F547f5da7-55fe-44b2-a4ee-ebf2a85d9260.png</url>
      <title>DEV Community: David Paluy</title>
      <link>https://dev.to/dpaluy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dpaluy"/>
    <language>en</language>
    <item>
      <title>Codex CLI Developer Guide</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Mon, 29 Sep 2025 16:25:00 +0000</pubDate>
      <link>https://dev.to/dpaluy/codex-cli-developer-guide-4f03</link>
      <guid>https://dev.to/dpaluy/codex-cli-developer-guide-4f03</guid>
      <description>&lt;h2&gt;Table of Contents&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    Quick Start
  &lt;/li&gt;
  &lt;li&gt;Resume Codex Sessions&lt;/li&gt;
  &lt;li&gt;
    Shell Shortcuts
  &lt;/li&gt;
  &lt;li&gt;
    Essential Configuration
    &lt;ul&gt;
      &lt;li&gt;Recommended config.toml&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    Settings - Deep Dive
    &lt;ul&gt;
      &lt;li&gt;Sandbox Modes&lt;/li&gt;
      &lt;li&gt;Command-Line Overrides (-c flag)&lt;/li&gt;
      &lt;li&gt;YOLO Mode (--yolo)&lt;/li&gt;
      &lt;li&gt;Approval Policies&lt;/li&gt;
      &lt;li&gt;Profiles for Different Workflows&lt;/li&gt;
      &lt;li&gt;Verbosity - model_verbosity&lt;/li&gt;
      &lt;li&gt;Network Access&lt;/li&gt;
      &lt;li&gt;Environment Variables&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Security Best Practices&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install Codex CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @openai/codex@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;verify installation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login with your OpenAI credentials&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quick Examples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic usage&lt;/span&gt;
codex &lt;span class="s2"&gt;"refactor this function to use dependency injection"&lt;/span&gt;

&lt;span class="c"&gt;# With higher reasoning effort and detailed summary&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_reasoning_effort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_reasoning_summary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"detailed"&lt;/span&gt; &lt;span class="s2"&gt;"explain this complex algorithm"&lt;/span&gt;

&lt;span class="c"&gt;# Automation in an isolated environment (bypasses sandbox/approvals)&lt;/span&gt;
codex &lt;span class="nt"&gt;--yolo&lt;/span&gt; &lt;span class="s2"&gt;"run tests and fix any failures"&lt;/span&gt;

&lt;span class="c"&gt;# Generate Rails code when "dev" profile is defined&lt;/span&gt;
codex &lt;span class="nt"&gt;--profile&lt;/span&gt; dev &lt;span class="s2"&gt;"create a User model with devise authentication"&lt;/span&gt;

&lt;span class="c"&gt;# Debug Rails issues&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_reasoning_effort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt; &lt;span class="s2"&gt;"fix this N+1 query in the users controller"&lt;/span&gt;

&lt;span class="c"&gt;# Add features&lt;/span&gt;
codex &lt;span class="s2"&gt;"add pagination to the posts index using kaminari"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resume Codex Sessions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reopen your work.&lt;/strong&gt; From the project directory you originally used, run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   codex resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Codex continues the last session where you left off so you can keep iterating immediately (&lt;a href="https://developers.openai.com/codex/changelog#2025-09-15" rel="noopener noreferrer"&gt;Codex changelog&lt;/a&gt;).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Continue last session.&lt;/strong&gt; Run &lt;code&gt;codex resume --last&lt;/code&gt; to load the most recent conversation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Shell Shortcuts
&lt;/h2&gt;

&lt;p&gt;Keep frequently used flags close at hand with a reusable helper.&lt;/p&gt;

&lt;p&gt;Add this to your &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add to ~/.bashrc or ~/.zshrc&lt;/span&gt;
cdx&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;subcommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;base_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gpt-5-codex"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;base_args&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$base_model&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--search&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$subcommand&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;codex &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;base_args&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return
  fi

  case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$subcommand&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
    &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;-h&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;USAGE&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt;&amp;amp;2
Usage: cdx &amp;lt;prompt&amp;gt;
       cdx &amp;lt;subcommand&amp;gt; [args]

Subcommands:
  update                 install the latest Codex CLI
  5h                     high-effort reasoning run
  yolo                   run with --yolo (danger mode)
  dev|quick|auto|analyze switch to a named profile
&lt;/span&gt;&lt;span class="no"&gt;USAGE
&lt;/span&gt;      &lt;span class="k"&gt;return &lt;/span&gt;0
      &lt;span class="p"&gt;;;&lt;/span&gt;
    update&lt;span class="p"&gt;)&lt;/span&gt;
      npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @openai/codex@latest
      &lt;span class="p"&gt;;;&lt;/span&gt;
    5h&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;shift
      &lt;/span&gt;codex &lt;span class="nt"&gt;-m&lt;/span&gt; gpt-5 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_reasoning_effort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt; &lt;span class="nt"&gt;--search&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
    yolo&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;shift
      &lt;/span&gt;codex &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;base_args&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--yolo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
    dev|quick|auto|analyze&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;shift
      &lt;/span&gt;codex &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$subcommand&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      codex &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;base_args&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Usage: cdx &amp;lt;prompt&amp;gt;
       cdx &amp;lt;subcommand&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;args]

Subcommands:
  update                 &lt;span class="nb"&gt;install &lt;/span&gt;the latest Codex CLI
  5h                     high-effort reasoning run
  yolo                   run with &lt;span class="nt"&gt;--yolo&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;danger mode&lt;span class="o"&gt;)&lt;/span&gt;
  dev|quick|auto|analyze switch to a named profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  examples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Default dev workflow without bypassing safeguards&lt;/span&gt;
cdx &lt;span class="s2"&gt;"fix this N+1 query in the posts controller"&lt;/span&gt;

&lt;span class="c"&gt;# Update Codex CLI&lt;/span&gt;
cdx update

&lt;span class="c"&gt;# Switch to the dev profile on demand&lt;/span&gt;
cdx dev &lt;span class="s2"&gt;"implement OAuth authentication"&lt;/span&gt;

&lt;span class="c"&gt;# High-reasoning call without yolo overrides&lt;/span&gt;
cdx 5h &lt;span class="s2"&gt;"optimize this complex SQL query with multiple joins"&lt;/span&gt;

&lt;span class="c"&gt;# Explicitly opt into --yolo (automation-only)&lt;/span&gt;
cdx yolo &lt;span class="s2"&gt;"run tests and fix any failures"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optional aliases
&lt;/h3&gt;

&lt;p&gt;Add a few shortcuts after the function if you prefer profile-specific commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;cdxdev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cdx dev'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;cdxq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cdx quick'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;cdxa&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cdx auto'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;cdxs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cdx analyze'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Essential Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Recommended config.toml
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;~/.codex/config.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Base settings&lt;/span&gt;
&lt;span class="py"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-5-codex"&lt;/span&gt;
&lt;span class="py"&gt;model_provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"openai"&lt;/span&gt;
&lt;span class="py"&gt;sandbox_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"workspace-write"&lt;/span&gt;
&lt;span class="py"&gt;approval_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"on-failure"&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"high"&lt;/span&gt;

&lt;span class="c"&gt;# Enable network for API calls and package installation&lt;/span&gt;
&lt;span class="nn"&gt;[sandbox_workspace_write]&lt;/span&gt;
&lt;span class="py"&gt;network_access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Safe environment variables&lt;/span&gt;
&lt;span class="nn"&gt;[shell_environment_policy]&lt;/span&gt;
&lt;span class="py"&gt;inherit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"core"&lt;/span&gt;
&lt;span class="py"&gt;experimental_use_profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Development profile&lt;/span&gt;
&lt;span class="nn"&gt;[profiles.dev]&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"high"&lt;/span&gt;
&lt;span class="py"&gt;model_verbosity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"high"&lt;/span&gt;
&lt;span class="py"&gt;sandbox_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"workspace-write"&lt;/span&gt;
&lt;span class="py"&gt;approval_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"on-failure"&lt;/span&gt;

&lt;span class="c"&gt;# Quick tasks profile&lt;/span&gt;
&lt;span class="nn"&gt;[profiles.quick]&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"low"&lt;/span&gt;
&lt;span class="py"&gt;sandbox_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"workspace-write"&lt;/span&gt;
&lt;span class="py"&gt;approval_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"never"&lt;/span&gt;

&lt;span class="c"&gt;# Analysis profile&lt;/span&gt;
&lt;span class="nn"&gt;[profiles.analyze]&lt;/span&gt;
&lt;span class="py"&gt;sandbox_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"read-only"&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"high"&lt;/span&gt;
&lt;span class="py"&gt;approval_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"never"&lt;/span&gt;

&lt;span class="c"&gt;# Automation profile&lt;/span&gt;
&lt;span class="nn"&gt;[profiles.auto]&lt;/span&gt;
&lt;span class="py"&gt;sandbox_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"danger-full-access"&lt;/span&gt;
&lt;span class="py"&gt;approval_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"never"&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_summary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"auto"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other providers profiles examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[model_providers.lms]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"LM Studio"&lt;/span&gt;
&lt;span class="py"&gt;base_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:1234/v1"&lt;/span&gt;

&lt;span class="nn"&gt;[profiles.qwen3-coder-30b-lms]&lt;/span&gt;
&lt;span class="py"&gt;model_provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lms"&lt;/span&gt;
&lt;span class="py"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"qwen/qwen3-coder-30b"&lt;/span&gt;
&lt;span class="py"&gt;model_reasoning_effort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"high"&lt;/span&gt;
&lt;span class="py"&gt;approval_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"on-failure"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Settings - Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sandbox Modes
&lt;/h3&gt;

&lt;p&gt;Controls what Codex can access and modify on your system:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;read-only&lt;/code&gt; - Code Analysis Only
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;--sandbox&lt;/span&gt; read-only &lt;span class="s2"&gt;"explain what this codebase does"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Reads files anywhere&lt;/li&gt;
&lt;li&gt;Cannot modify files or run commands&lt;/li&gt;
&lt;li&gt;Perfect for code review and exploration&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;workspace-write&lt;/code&gt; - Development Mode (Default)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;--sandbox&lt;/span&gt; workspace-write &lt;span class="s2"&gt;"add error handling to this controller"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Reads files anywhere&lt;/li&gt;
&lt;li&gt;Writes only in current directory&lt;/li&gt;
&lt;li&gt;Ideal for active development&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;danger-full-access&lt;/code&gt; - No Restrictions
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex &lt;span class="nt"&gt;--sandbox&lt;/span&gt; danger-full-access &lt;span class="s2"&gt;"set up the entire development environment"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Complete system access&lt;/li&gt;
&lt;li&gt;Use only in Docker containers or disposable environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Command-Line Overrides (-c flag)
&lt;/h3&gt;

&lt;p&gt;Override any config setting temporarily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# High reasoning for complex problems&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_reasoning_effort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt; &lt;span class="s2"&gt;"optimize this database query"&lt;/span&gt;

&lt;span class="c"&gt;# Low verbosity for simple tasks&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_verbosity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt; &lt;span class="s2"&gt;"fix this syntax error"&lt;/span&gt;

&lt;span class="c"&gt;# Enable network access on demand&lt;/span&gt;
codex &lt;span class="nt"&gt;--sandbox&lt;/span&gt; workspace-write &lt;span class="nt"&gt;-c&lt;/span&gt; sandbox_workspace_write.network_access&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s2"&gt;"install missing gems"&lt;/span&gt;

&lt;span class="c"&gt;# Skip all approvals&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;approval_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"never"&lt;/span&gt; &lt;span class="s2"&gt;"run the test suite"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  YOLO Mode (--yolo)
&lt;/h3&gt;

&lt;p&gt;Bypasses all safety mechanisms - use with extreme caution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Equivalent to: --sandbox danger-full-access --approval-policy never&lt;/span&gt;
codex &lt;span class="nt"&gt;--yolo&lt;/span&gt; &lt;span class="s2"&gt;"deploy to staging and run integration tests"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Safe YOLO usage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inside Docker containers&lt;/li&gt;
&lt;li&gt;Dedicated development VMs&lt;/li&gt;
&lt;li&gt;When you need complete automation&lt;/li&gt;
&lt;li&gt;Always have backups and version control&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Approval Policies
&lt;/h3&gt;

&lt;p&gt;Control when Codex asks for permission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Never ask (automation)&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;approval_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"never"&lt;/span&gt; &lt;span class="s2"&gt;"fix linting issues"&lt;/span&gt;

&lt;span class="c"&gt;# Ask before untrusted operations (default-ish)&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;approval_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"on-request"&lt;/span&gt; &lt;span class="s2"&gt;"install dependencies"&lt;/span&gt;

&lt;span class="c"&gt;# Ask only when commands fail&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;approval_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"on-failure"&lt;/span&gt; &lt;span class="s2"&gt;"run migrations"&lt;/span&gt;

&lt;span class="c"&gt;# Ask before every command&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;approval_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"untrusted"&lt;/span&gt; &lt;span class="s2"&gt;"modify production config"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Profiles for Different Workflows
&lt;/h3&gt;

&lt;p&gt;Use profiles to switch between configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# High-reasoning development&lt;/span&gt;
codex &lt;span class="nt"&gt;--profile&lt;/span&gt; dev &lt;span class="s2"&gt;"implement OAuth authentication"&lt;/span&gt;

&lt;span class="c"&gt;# Quick fixes&lt;/span&gt;
codex &lt;span class="nt"&gt;--profile&lt;/span&gt; quick &lt;span class="s2"&gt;"fix typo in variable name"&lt;/span&gt;

&lt;span class="c"&gt;# Safe analysis&lt;/span&gt;
codex &lt;span class="nt"&gt;--profile&lt;/span&gt; analyze &lt;span class="s2"&gt;"review this code for security issues"&lt;/span&gt;

&lt;span class="c"&gt;# Full automation&lt;/span&gt;
codex &lt;span class="nt"&gt;--profile&lt;/span&gt; auto &lt;span class="s2"&gt;"run tests, fix failures, commit changes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verbosity - model_verbosity
&lt;/h3&gt;

&lt;p&gt;Control how much detail Codex provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"low" – terse responses suited to automation or CI pipelines&lt;/li&gt;
&lt;li&gt;"medium" – balanced explanations for everyday development (default)&lt;/li&gt;
&lt;li&gt;"high" – expanded context and commentary for deep dives
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Detailed explanations&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_verbosity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt; &lt;span class="s2"&gt;"walk me through this design pattern"&lt;/span&gt;

&lt;span class="c"&gt;# Request a richer reasoning summary when available&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_reasoning_summary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"detailed"&lt;/span&gt; &lt;span class="s2"&gt;"explain this complex algorithm"&lt;/span&gt;

&lt;span class="c"&gt;# Minimal output for automation&lt;/span&gt;
codex &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_reasoning_summary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"concise"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;model_verbosity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt; &lt;span class="s2"&gt;"fix syntax"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;model_verbosity&lt;/code&gt; accepts only &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt; (the default), or &lt;code&gt;high&lt;/code&gt;; choose the level that matches how much explanation you want in the response. Pair it with &lt;code&gt;model_reasoning_summary&lt;/code&gt;, which controls the optional reasoning summary (&lt;code&gt;auto&lt;/code&gt; by default, with &lt;code&gt;concise&lt;/code&gt; or &lt;code&gt;detailed&lt;/code&gt; as alternatives) so you can surface or hide the model's step-by-step reasoning for different workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network Access
&lt;/h3&gt;

&lt;p&gt;Enable network for package installation and API calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# In config.toml&lt;/span&gt;
&lt;span class="nn"&gt;[sandbox_workspace_write]&lt;/span&gt;
&lt;span class="py"&gt;network_access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Or via command line&lt;/span&gt;
codex &lt;span class="nt"&gt;--sandbox&lt;/span&gt; workspace-write &lt;span class="nt"&gt;-c&lt;/span&gt; sandbox_workspace_write.network_access&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s2"&gt;"bundle install"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;Control which shell variables Codex inherits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[shell_environment_policy]&lt;/span&gt;
&lt;span class="py"&gt;inherit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"core"&lt;/span&gt;  &lt;span class="c"&gt;# PATH, HOME, USER, SHELL, etc.&lt;/span&gt;
&lt;span class="py"&gt;experimental_use_profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c"&gt;# Load .bashrc, .zshrc&lt;/span&gt;

&lt;span class="c"&gt;# Whitelist specific variables&lt;/span&gt;
&lt;span class="py"&gt;include_only&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s"&gt;"PATH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"HOME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"RAILS_ENV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"DATABASE_URL"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# Blacklist sensitive variables&lt;/span&gt;
&lt;span class="py"&gt;exclude&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s"&gt;"*_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"*_SECRET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"PASSWORD"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Never use YOLO in production environments&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;read-only&lt;/code&gt; for code review and exploration&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;workspace-write&lt;/code&gt; for active development&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep sensitive environment variables out of &lt;code&gt;include_only&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test configurations in safe environments first&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use version control before running destructive operations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This guide was written for CLI Version: 0.42.0&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codex</category>
      <category>openai</category>
    </item>
    <item>
      <title>Empowering AI Agents to Monitor Pull Request Status</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Mon, 09 Jun 2025 18:36:09 +0000</pubDate>
      <link>https://dev.to/dpaluy/empowering-ai-agents-to-monitor-pull-request-status-ke3</link>
      <guid>https://dev.to/dpaluy/empowering-ai-agents-to-monitor-pull-request-status-ke3</guid>
      <description>&lt;p&gt;You can enable AI agents to monitor GitHub pull request (PR) status by polling CI checks. This approach ensures an automated process of delivering quality code by AI agent.&lt;/p&gt;

&lt;p&gt;It can be used in any AI Agentic workflow. For example, Claude Code, Cursor, etc. &lt;/p&gt;

&lt;p&gt;Add the script and instructions, and enjoy the magic.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;# scripts/poll_ci.sh&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;PR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;15&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# seconds&lt;/span&gt;
&lt;span class="nv"&gt;TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;600&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# seconds&lt;/span&gt;
&lt;span class="nv"&gt;START&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Auto-detect repository owner and name if not provided&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;REPO_INFO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh repo view &lt;span class="nt"&gt;--json&lt;/span&gt; owner,name &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'.owner.login + "/" + .name'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO_INFO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO_INFO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"↪️  Polling PR #&lt;/span&gt;&lt;span class="nv"&gt;$PR&lt;/span&gt;&lt;span class="s2"&gt; every &lt;/span&gt;&lt;span class="nv"&gt;$INTERVAL&lt;/span&gt;&lt;span class="s2"&gt; s for &lt;/span&gt;&lt;span class="nv"&gt;$TIMEOUT&lt;/span&gt;&lt;span class="s2"&gt; s max"&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; :&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c"&gt;# We use `|| true` so that `gh`'s non-zero exit code on pending or&lt;/span&gt;
  &lt;span class="c"&gt;# failed checks don't trigger `set -e` and kill the script.&lt;/span&gt;
  &lt;span class="nv"&gt;CHECKS_OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh &lt;span class="nb"&gt;pr &lt;/span&gt;checks &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OWNER&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHECKS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"fail|failure|error"&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ PR #&lt;/span&gt;&lt;span class="nv"&gt;$PR&lt;/span&gt;&lt;span class="s2"&gt; is red"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHECKS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;elif &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHECKS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"pending"&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"• &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%H:%M:%S"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; → pending"&lt;/span&gt;
  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHECKS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"fail|failure|error|pending|skipping"&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# If there are no failing, pending, or skipping checks, we can assume success.&lt;/span&gt;
    &lt;span class="c"&gt;# This is more robust than just checking for "pass", as some CIs might&lt;/span&gt;
    &lt;span class="c"&gt;# not report a passing state explicitly if all jobs are neutral/successful.&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ PR #&lt;/span&gt;&lt;span class="nv"&gt;$PR&lt;/span&gt;&lt;span class="s2"&gt; is green"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHECKS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;0
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# This state means there are no failures and no pending, but some are&lt;/span&gt;
    &lt;span class="c"&gt;# skipping or in another state. We wait.&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"• &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%H:%M:%S"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; → waiting for checks to complete"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt; - START &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; TIMEOUT &lt;span class="o"&gt;))&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⏰ PR #&lt;/span&gt;&lt;span class="nv"&gt;$PR&lt;/span&gt;&lt;span class="s2"&gt; timed out"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHECKS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;2
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INTERVAL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  AI Instructions
&lt;/h2&gt;

&lt;p&gt;Note: Can be added to &lt;code&gt;CLAUDE.md&lt;/code&gt; or Cursor rules&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Poll CI Status: $ARGUMENTS&lt;/span&gt;

Use this command when you need to wait for all GitHub checks on a pull-request to complete and then branch into success or failure flows.

&lt;span class="gu"&gt;## Parameters (space-separated)&lt;/span&gt;
| Pos | Name       | Example | Purpose                                                 |
| --- | ---------- | ------- | ------------------------------------------------------- |
| 1   | &lt;span class="sb"&gt;`pr_id`&lt;/span&gt;    | &lt;span class="sb"&gt;`123`&lt;/span&gt;   | Pull-request number to monitor                          |
| 2   | &lt;span class="sb"&gt;`interval`&lt;/span&gt; | &lt;span class="sb"&gt;`15`&lt;/span&gt;    | &lt;span class="ge"&gt;*(optional, default 15 s)*&lt;/span&gt; seconds between status polls |
| 3   | &lt;span class="sb"&gt;`timeout`&lt;/span&gt;  | &lt;span class="sb"&gt;`600`&lt;/span&gt;   | &lt;span class="ge"&gt;*(optional, default 600 s)*&lt;/span&gt; maximum wait in seconds     |

&lt;span class="gu"&gt;## Environment Variables (optional)&lt;/span&gt;
| Name    | Example              | Purpose                                                      |
| ------- | -------------------- | ------------------------------------------------------------ |
| &lt;span class="sb"&gt;`OWNER`&lt;/span&gt; | &lt;span class="sb"&gt;`RAILS`&lt;/span&gt;              | Repository owner (auto-detected from current repo if unset)  |
| &lt;span class="sb"&gt;`REPO`&lt;/span&gt;  | &lt;span class="sb"&gt;`RAILS`&lt;/span&gt;              | Repository name (auto-detected from current repo if unset)   |

&lt;span class="gs"&gt;**Run polling script**&lt;/span&gt;

  &lt;span class="sb"&gt;`./scripts/poll_ci.sh $ARGUMENTS`&lt;/span&gt;

  The script will automatically detect the repository owner and name from the current directory if not provided via environment variables.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>cursor</category>
      <category>claude</category>
      <category>anthropic</category>
    </item>
    <item>
      <title>Exploring Agentic Workflow Patterns</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Sun, 09 Feb 2025 19:59:18 +0000</pubDate>
      <link>https://dev.to/dpaluy/exploring-agentic-workflow-patterns-312a</link>
      <guid>https://dev.to/dpaluy/exploring-agentic-workflow-patterns-312a</guid>
      <description>&lt;p&gt;As artificial intelligence continues to evolve, we're witnessing a paradigm shift in how AI systems are structured and deployed. Instead of monolithic models, modern AI architectures increasingly rely on multiple specialized agents working together to accomplish complex tasks. These agentic systems represent a fundamental evolution in AI design, offering enhanced flexibility, scalability, and robustness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Agentic Workflows
&lt;/h2&gt;

&lt;p&gt;At their core, agentic workflows represent patterns of interaction between multiple AI agents, each specialized in specific tasks or domains. These patterns are the blueprints for orchestrating collaborative AI systems – similar to how design patterns help structure software development. As microservices revolutionized software architecture by breaking down monolithic applications, agentic patterns transform AI systems by decomposing complex tasks into manageable, specialized components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Agentic Patterns Matter
&lt;/h2&gt;

&lt;p&gt;The significance of agentic patterns extends beyond mere architectural elegance. These patterns offer several crucial advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modularity&lt;/strong&gt;: By breaking down complex systems into discrete agents, we can more easily maintain, upgrade, and scale individual components without affecting the entire system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Specialization&lt;/strong&gt;: Different agents can be optimized for specific tasks, leading to better performance than a single, general-purpose model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reliability&lt;/strong&gt;: Systems can gracefully handle failures and maintain operational continuity through patterns like fallback and self-healing loops.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Agentic patterns enable systems to dynamically allocate resources and parallelize tasks, improving efficiency and throughput.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Navigating This Guide
&lt;/h2&gt;

&lt;p&gt;In this visual guide, we'll explore eleven fundamental agentic workflow patterns. Each pattern represents a different approach to organizing and coordinating AI agents, from simple sequential workflows to complex networked architectures. We'll examine each pattern's structure, ideal use cases, advantages, and practical applications.&lt;/p&gt;

&lt;p&gt;Whether you're designing AI systems, architecting automation workflows, or simply interested in understanding how modern AI systems operate at scale, these patterns provide a valuable framework for thinking about and implementing intelligent systems.&lt;/p&gt;

&lt;p&gt;Let's dive into each pattern and understand how it can be applied to build more robust, efficient, and intelligent systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Network (Horizontal)&lt;/li&gt;
&lt;li&gt;
Hierarchical (Vertical) &lt;/li&gt;
&lt;li&gt;Sequential&lt;/li&gt;
&lt;li&gt;Parallel&lt;/li&gt;
&lt;li&gt;Loop (Self-Healing)&lt;/li&gt;
&lt;li&gt;Router (Agentic RAG)&lt;/li&gt;
&lt;li&gt;Aggregator (Synthesizer)&lt;/li&gt;
&lt;li&gt;Branching (Conditional Processing)&lt;/li&gt;
&lt;li&gt;Ensemble (Voting or Consensus)&lt;/li&gt;
&lt;li&gt;Cascade (Progressive Refinement)&lt;/li&gt;
&lt;li&gt;Fallback (Error Handling)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Network (Horizontal)
&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%2Fmt7wicuj8hfllktugjop.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%2Fmt7wicuj8hfllktugjop.png" alt="Image description" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Agents interact in a networked or peer-to-peer fashion, forming a decentralized system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Distributed decision-making, multi-agent collaboration, adaptive learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; High resilience, scalable, fault-tolerant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Cybersecurity threat detection where multiple agents exchange information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hierarchical (Vertical)
&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%2Fefafg6c668k24cyw514w.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%2Fefafg6c668k24cyw514w.png" alt="Image description" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; A main agent delegates tasks to multiple sub-agents in a structured, hierarchical manner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Task delegation, multi-layered decision-making.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Clear structure, easy to manage dependencies, scales well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; AI-powered IT support system with specialized troubleshooting bots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequential
&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%2F15mb9ejoip8xfjm8hpfs.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%2F15mb9ejoip8xfjm8hpfs.png" alt="Image description" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; A linear processing pipeline where each agent refines the task and passes the result to the next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Workflow automation, data processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Structured flow, easy to manage, deterministic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Data processing pipeline with cleaning, analysis, and summarization steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parallel
&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%2Feu6h96rylhv3ms1ltz2h.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%2Feu6h96rylhv3ms1ltz2h.png" alt="Image description" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Multiple agents process tasks simultaneously before merging their outputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Performance optimization, multi-modal AI processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Faster execution, increased efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; AI system simultaneously processes text, image, and audio data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loop (Self-Healing)
&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%2Fj05n06r0hw0oehgkmmov.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%2Fj05n06r0hw0oehgkmmov.png" alt="Image description" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Agents iterate over a process to refine their output, retry errors, or self-improve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Self-learning models, continuous monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Improves accuracy and self-correcting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; AI chatbot refining responses based on user feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Router (Agentic RAG)
&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%2Fti1mo1zvxewj04mxpjab.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%2Fti1mo1zvxewj04mxpjab.png" alt="Image description" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; An agent routes incoming data to the appropriate knowledge source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Query classification, intelligent data routing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Efficient, scalable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Smart search engine directing queries to the appropriate knowledge base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregator (Synthesizer)
&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%2Fxt8ajgtwcae3i5qyrp2j.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%2Fxt8ajgtwcae3i5qyrp2j.png" alt="Image description" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Multiple agents process different aspects of a task independently, and their outputs are aggregated or synthesized into a cohesive result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Combining diverse data sources, multi-perspective analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Comprehensive results, leverages specialized processing, enhances robustness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; An AI system that gathers insights from various news sources to provide a comprehensive summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Branching (Conditional Processing)
&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%2F49cdtm40vwzfkln85wzk.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%2F49cdtm40vwzfkln85wzk.png" alt="Image description" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; A primary agent evaluates conditions and directs tasks to different sub-agents based on specific criteria or decision points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Decision-based workflows, conditional task execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Dynamic processing paths, efficient resource utilization, adaptable to varying inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Customer support system that routes inquiries to specialized agents based on the nature of the query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ensemble (Voting or Consensus)
&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%2F84rsjujcdyyqycw3kz5u.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%2F84rsjujcdyyqycw3kz5u.png" alt="Image description" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Multiple agents provide solutions or answers to the same problem, and a consensus mechanism determines the final output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Improving accuracy, reducing bias, decision-making processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Enhanced reliability, mitigates individual agent errors, robust outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; An AI diagnostic system where multiple models assess medical images, and the final diagnosis is based on majority agreement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cascade (Progressive Refinement)
&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%2Fpwn7zcor6vyw8cs79ymn.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%2Fpwn7zcor6vyw8cs79ymn.png" alt="Image description" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; A sequence of agents progressively refines the output, with each agent adding incremental improvements or details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; Complex problem-solving, iterative enhancement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Gradual improvement, manageable complexity, allows for intermediate evaluations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Natural language processing system where initial agents handle basic parsing, and subsequent agents perform deeper semantic analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fallback (Error Handling)
&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%2Fsg79tf4cja2cqompj6px.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%2Fsg79tf4cja2cqompj6px.png" alt="Image description" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; A backup agent takes over if the primary agent fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Used For:&lt;/strong&gt; AI failover, fault tolerance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Reliability, robustness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; AI assistant with a backup rules-based chatbot in case of LLM failures.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>patterns</category>
    </item>
    <item>
      <title>Mastering Cursor Rules: A Developer's Guide to Smart AI Integration</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Fri, 07 Feb 2025 22:20:45 +0000</pubDate>
      <link>https://dev.to/dpaluy/mastering-cursor-rules-a-developers-guide-to-smart-ai-integration-1k65</link>
      <guid>https://dev.to/dpaluy/mastering-cursor-rules-a-developers-guide-to-smart-ai-integration-1k65</guid>
      <description>&lt;h2&gt;
  
  
  What are Cursor Rules?
&lt;/h2&gt;

&lt;p&gt;Cursor Rules are configuration files that define how AI should interact with your codebase. They provide context-aware assistance by setting guidelines, constraints, and behavioral patterns for AI interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Types of Cursor Rules
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Global AI Rules (Settings)
&lt;/h3&gt;

&lt;p&gt;Located in Cursor &lt;code&gt;Settings -&amp;gt; General&lt;/code&gt;, these rules establish core principles for all AI interactions. They define fundamental behavior patterns and are language-agnostic. Here's a powerful example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;CORE_PRINCIPLES&amp;gt;
1. EXPLORATION OVER CONCLUSION
- Never rush to conclusions
- Keep exploring until a solution emerges naturally
- Question every assumption and inference

2. DEPTH OF REASONING
- Break down complex thoughts into simple steps
- Embrace uncertainty and revision
- Express thoughts in natural conversation

3. THINKING PROCESS
- Show work-in-progress thinking
- Acknowledge and explore alternatives
- Frequently reassess and revise
&amp;lt;/CORE_PRINCIPLES&amp;gt;

&amp;lt;OUTPUT_FORMAT&amp;gt;
Responses must follow:
  &amp;lt;CONTEMPLATOR&amp;gt;
  - Begin with foundational observations
  - Question thoroughly
  - Show natural progression
  &amp;lt;/CONTEMPLATOR&amp;gt;

  &amp;lt;FINAL_ANSWER&amp;gt;
  - Clear, concise summary
  - Note remaining questions
  &amp;lt;/FINAL_ANSWER&amp;gt;
&amp;lt;/OUTPUT_FORMAT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure transforms AI from a simple answer generator into a thoughtful collaborator that explores solutions thoroughly and shows its reasoning process.&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://majesticlabs.dev/blog/202502/rules_for_ai.txt" rel="noopener noreferrer"&gt;Advanced Rules for AI&lt;/a&gt; with reasoning&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Project-Wide Rules (.cursorrules)
&lt;/h3&gt;

&lt;p&gt;A single file in your project root that serves as the primary guidebook for project-specific conventions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Framework Standards
- Follow Rails architectural patterns
- Use service objects for business logic
- Reference patterns (@service-objects.md)

# Quality Controls
- Follow RuboCop guidelines
- Enforce test coverage minimums
- Ban SQL queries in views
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Pattern-Specific Rules (.cursor/rules/*.mdc)
&lt;/h3&gt;

&lt;p&gt;Introduced in Cursor v0.45, these Markdown-based Domain Configuration files target specific file patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
Description: Rails Controller Standards
Globs: app/controllers/**/*.rb
---

# Guidelines
- Keep controllers skinny
- Use before_action for repeating logic
- Follow RESTful conventions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In the current version, those rules are added to prompt only in Agent mode. Chat and Normal compose are still using &lt;code&gt;.cursorrules&lt;/code&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Approach: The Game Changer
&lt;/h2&gt;

&lt;p&gt;Modern Cursor Rules shifted from passive rule listing to active agent instruction. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are instructa, a senior Rails developer with superpowers! ⚡

# Agent Behavior
- Read Roadmap.md first
- Plan database schema changes
- Use ViewComponents for complex UI
- Write system tests for critical paths

# Code Standards
- Follow Rails conventions
- Use concerns for shared logic
- Tests must pass before merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pro Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Reference Architecture Using "@ syntax"
&lt;/h3&gt;

&lt;p&gt;Instead of writing lengthy explanations, reference your documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Bad
- Controllers should use service objects for complex business logic...

# Good
- Follow service object patterns defined in @docs/architecture/services.md
- See implementation examples in @docs/examples/service_objects/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Strategic Glob Patterns
&lt;/h3&gt;

&lt;p&gt;Create focused, hierarchical patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Too broad
Globs: **/*.rb

# Better
Globs:
  app/services/**/*.rb
  app/models/**/*.rb
  !app/models/legacy/**/*.rb  # Exclude legacy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Combining Rules
&lt;/h3&gt;

&lt;p&gt;Create composable rule sets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .cursor/rules/base_ruby.mdc
Description: Base Ruby standards

# .cursor/rules/rails_controllers.mdc
@base_ruby.mdc
Description: Controller-specific rules
Globs: app/controllers/**/*.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Maintainable Organization
&lt;/h3&gt;

&lt;p&gt;Structure rules by domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.cursor/rules/
  ├── rails8.mdc
  ├── models/
  │   ├── active_record.mdc
  │   └── postgresql.mdc
  ├── controllers/
  │   ├── api.mdc
  │   └── web.mdc
  └── views/
      ├── erb.mdc
      └── components.mdc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The power of Cursor Rules lies in their ability to provide contextual guidance while maintaining flexibility. They transform AI from a generic tool into a project-aware coding partner that understands your architecture, conventions, and goals.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>rules</category>
      <category>rails</category>
    </item>
    <item>
      <title>When Third-Party APIs Go Rogue</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Mon, 03 Feb 2025 17:28:00 +0000</pubDate>
      <link>https://dev.to/dpaluy/when-third-party-apis-go-rogue-360k</link>
      <guid>https://dev.to/dpaluy/when-third-party-apis-go-rogue-360k</guid>
      <description>&lt;h2&gt;
  
  
  Strategies for Building Bulletproof Integrations
&lt;/h2&gt;

&lt;p&gt;Modern web applications often rely on external APIs that can be slow, unreliable, or even disappear entirely. To build robust integrations, you need clear timeouts, conditional retries, rate limiting, decoupled code designs, and carefully orchestrated background jobs. This post provides proven techniques, using Ruby on Rails for illustration, that can be applied in almost any technology stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Configure Timeouts
&lt;/h2&gt;

&lt;p&gt;Timeouts prevent your application from getting stuck when the external service fails to respond promptly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection Timeout&lt;/strong&gt;: How long to wait for the connection to be established (e.g., 5 seconds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read Timeout&lt;/strong&gt;: How long to wait for data after the connection is established (e.g., 15 seconds).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example (Net::HTTP)
&lt;/h3&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;'net/http'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'uri'&lt;/span&gt;

&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&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="s2"&gt;"https://api.unreliable.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'https'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;   &lt;span class="c1"&gt;# connection timeout&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;  &lt;span class="c1"&gt;# read timeout&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If these limits are exceeded, &lt;code&gt;Net::HTTP&lt;/code&gt; raises an exception that you can catch and handle.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Automatic Retries
&lt;/h2&gt;

&lt;p&gt;When dealing with transient or server-side failures, you can retry requests to increase success rates. However, be selective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retry&lt;/strong&gt; on timeouts and server (5xx) errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not retry&lt;/strong&gt; on 4xx errors. They usually indicate issues in your request itself.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Using Faraday with Retry
&lt;/h3&gt;

&lt;p&gt;Use &lt;a href="https://github.com/lostisland/faraday-retry" rel="noopener noreferrer"&gt;faraday-retry&lt;/a&gt; or &lt;a href="https://github.com/kamui/retriable" rel="noopener noreferrer"&gt;Retriable&lt;/a&gt; for robust retry behavior.&lt;/p&gt;

&lt;h4&gt;
  
  
  Faraday + faraday-retry
&lt;/h4&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;'faraday'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'faraday/retry'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;
  &lt;span class="no"&gt;Faraday&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="ss"&gt;url: &lt;/span&gt;&lt;span class="s2"&gt;"https://api.unreliable.com"&lt;/span&gt;&lt;span class="p"&gt;)&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;conn&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt; &lt;span class="ss"&gt;:retry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;max: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;interval: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;exceptions: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Faraday&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TimeoutError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Faraday&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ConnectionFailed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt; &lt;span class="ss"&gt;:raise_error&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adapter&lt;/span&gt; &lt;span class="no"&gt;Faraday&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_adapter&lt;/span&gt;
  &lt;span class="k"&gt;end&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="s2"&gt;"/data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Faraday&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ClientError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="c1"&gt;# Handle 4xx or client errors&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Faraday&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ServerError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="c1"&gt;# Handle 5xx after retries&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Retriable
&lt;/h4&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;'net/http'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'uri'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'retriable'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reliable_request&lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&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="s2"&gt;"https://api.unreliable.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Retriable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retriable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;on: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Timeout&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;tries: &lt;/span&gt;&lt;span class="mi"&gt;3&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;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'https'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# raise or handle depending on response code&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&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;h2&gt;
  
  
  3. Implement a Hard Rate Limiter
&lt;/h2&gt;

&lt;p&gt;Many APIs have soft limits—once you exceed them, you might be billed for each additional request. Implement a hard limit within your app to avoid unexpected costs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Track request counts&lt;/strong&gt; in persistent storage (e.g., database or Redis).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reject calls immediately&lt;/strong&gt; if you’re at the limit.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example (Rails Model)
&lt;/h3&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;ApiUsageTracker&lt;/span&gt;
  &lt;span class="no"&gt;MAX_REQUESTS_PER_HOUR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment_usage&lt;/span&gt;
    &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ApiTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_or_create_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;hour: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginning_of_hour&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;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;MAX_REQUESTS_PER_HOUR&lt;/span&gt;
      &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:request_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&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;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_protected_api&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ApiUsageTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment_usage&lt;/span&gt;
    &lt;span class="c1"&gt;# Proceed with API call&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c1"&gt;# Return an error or raise an exception&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"API limit exceeded!"&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;In this snippet, &lt;code&gt;hour&lt;/code&gt; is used to group requests by the current hour.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Decouple with an Interface
&lt;/h2&gt;

&lt;p&gt;Minimize direct dependencies on a specific API by coding against an interface. This gives you freedom to swap out underlying providers without altering business logic.&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;module&lt;/span&gt; &lt;span class="nn"&gt;CurrencyProvider&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&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;class&lt;/span&gt; &lt;span class="nc"&gt;UnreliableApiCurrencyConverter&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CurrencyProvider&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Actual call to https://api.unreliable.com&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;class&lt;/span&gt; &lt;span class="nc"&gt;FallbackCurrencyConverter&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;CurrencyProvider&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# A cached or offline calculation&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;h2&gt;
  
  
  5. Asynchronous, Persistent Queuing
&lt;/h2&gt;

&lt;p&gt;Offload external API interactions to background jobs. This approach lets your application remain responsive and resilient to transient failures.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated Queue&lt;/strong&gt;: Place API jobs in a separate queue to keep them isolated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persist Status&lt;/strong&gt;: Use database-backed jobs so failures are not lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry or Discard&lt;/strong&gt;: Use &lt;code&gt;retry_on&lt;/code&gt; or &lt;code&gt;discard_on&lt;/code&gt; to handle specific errors gracefully.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Rails ActiveJob Example
&lt;/h3&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;PaymentJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:api_calls&lt;/span&gt;

  &lt;span class="n"&gt;retry_on&lt;/span&gt; &lt;span class="no"&gt;PaymentGatewayError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;attempts: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="n"&gt;retry_on&lt;/span&gt; &lt;span class="no"&gt;NetworkTimeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait: :exponentially_longer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;attempts: :unlimited&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Make request to https://api.unreliable.com&lt;/span&gt;
    &lt;span class="c1"&gt;# If PaymentGatewayError or NetworkTimeout occurs,&lt;/span&gt;
    &lt;span class="c1"&gt;# Rails retries automatically based on the settings&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;code&gt;retry_on&lt;/code&gt; handles defined exceptions by re-enqueuing the job for another attempt. You can also use &lt;code&gt;discard_on&lt;/code&gt; for errors that should not be retried.&lt;/p&gt;




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

&lt;p&gt;Incorporating strict timeouts, targeted retries, robust rate limiting, interface-based designs, and asynchronous job orchestration will make your application more resilient when integrating with an unreliable or slow external service. By implementing these strategies, you ensure higher availability, better fault tolerance, and tighter control over behavior when the API fails or becomes unresponsive.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>api</category>
      <category>integration</category>
    </item>
    <item>
      <title>Lang Everything: The Missing Guide to LangChain's Ecosystem</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Sat, 30 Nov 2024 04:20:01 +0000</pubDate>
      <link>https://dev.to/dpaluy/lang-everything-the-missing-guide-to-langchains-ecosystem-1eo7</link>
      <guid>https://dev.to/dpaluy/lang-everything-the-missing-guide-to-langchains-ecosystem-1eo7</guid>
      <description>&lt;p&gt;In the rapidly evolving landscape of AI development, the Lang* ecosystem has emerged as a powerhouse for building sophisticated language model applications. Let's break down the key players and understand when to use each.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangChain: The Foundation
&lt;/h2&gt;

&lt;p&gt;Think of LangChain as your Swiss Army knife for LLM development. It's the foundational framework that handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LLM Integration&lt;/strong&gt;: Seamlessly works with both closed-source (GPT-4) and open-source (Llama 3) models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Management&lt;/strong&gt;: Dynamic templates instead of hardcoded prompts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Systems&lt;/strong&gt;: Built-in conversation memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chain Operations&lt;/strong&gt;: Connect multiple tasks into smooth workflows &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Data&lt;/strong&gt;: Easy integration with document loaders and vector databases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of writing boilerplate code for API calls and agent management, LangChain provides clean abstractions that make complex AI applications manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangGraph: The Orchestrator
&lt;/h2&gt;

&lt;p&gt;Built on top of LangChain, LangGraph specializes in managing multi-agent workflows through three core components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State&lt;/strong&gt;: Maintains the current snapshot of your application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nodes&lt;/strong&gt;: Individual components performing specific tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edges&lt;/strong&gt;: Defines how data flows between nodes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;LangGraph shines when you need agents to collaborate and make decisions cyclically. It's beneficial for task automation and research assistance systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangFlow: The Visual Builder
&lt;/h2&gt;

&lt;p&gt;Want to prototype without coding? LangFlow offers a drag-and-drop interface for building LangChain applications. Key features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual workflow design&lt;/li&gt;
&lt;li&gt;Quick prototyping capabilities
&lt;/li&gt;
&lt;li&gt;API access to created workflows&lt;/li&gt;
&lt;li&gt;Perfect for MVPs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While primarily meant for prototyping rather than production, it's an excellent tool for rapid development and team collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangSmith: The Monitor
&lt;/h2&gt;

&lt;p&gt;Every production AI application needs monitoring, and that's where LangSmith comes in. It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lifecycle management (prototyping to production)&lt;/li&gt;
&lt;li&gt;Performance monitoring&lt;/li&gt;
&lt;li&gt;Token usage tracking&lt;/li&gt;
&lt;li&gt;Error rate analysis&lt;/li&gt;
&lt;li&gt;Latency monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best part? LangSmith works independently of your LLM framework, though it integrates seamlessly with LangChain and LangGraph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the Right Choice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use LangChain&lt;/strong&gt; when building any LLM-powered application from scratch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add LangGraph&lt;/strong&gt; when you need sophisticated multi-agent interactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with LangFlow&lt;/strong&gt; for rapid prototyping and visual development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy LangSmith&lt;/strong&gt; when you need severe monitoring and performance tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember, these tools aren't mutually exclusive - they're designed to work together, forming a comprehensive ecosystem for AI application development.&lt;/p&gt;

</description>
      <category>langchain</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Leveraging AI Tools for Modern Web Development</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Fri, 22 Nov 2024 09:22:52 +0000</pubDate>
      <link>https://dev.to/dpaluy/leveraging-ai-tools-for-modern-web-development-2k2c</link>
      <guid>https://dev.to/dpaluy/leveraging-ai-tools-for-modern-web-development-2k2c</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This guide outlines an efficient workflow using multiple AI tools to build web applications, from planning to deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools Required
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Perplexity AI - &lt;em&gt;Initial research &amp;amp; tech stack validation&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;OpenAI O1 Model - &lt;em&gt;Primary documentation tool&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Claude AI - &lt;em&gt;Coding assistant&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Cursor AI - &lt;em&gt;IDE&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Bolt.new - &lt;em&gt;Frontend design &amp;amp; project setup&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Replit AI - &lt;em&gt;Quick deployments&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Research with Perplexity AI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initial Market Research Phase
&lt;/h3&gt;

&lt;p&gt;Use Perplexity for comprehensive analysis in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Problem Research&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;"Research [your problem area]:
&lt;span class="p"&gt;-&lt;/span&gt; What are the current challenges in [industry/domain]?
&lt;span class="p"&gt;-&lt;/span&gt; How do people currently solve this problem?
&lt;span class="p"&gt;-&lt;/span&gt; What are the main pain points with existing solutions?
&lt;span class="p"&gt;-&lt;/span&gt; What are the latest trends in solving this problem?
&lt;span class="p"&gt;-&lt;/span&gt; Are there any recent technological advancements in this area?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. &lt;strong&gt;Solution Validation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;"Analyze solution feasibility for [your proposed solution]:
&lt;span class="p"&gt;-&lt;/span&gt; What similar solutions exist in the market?
&lt;span class="p"&gt;-&lt;/span&gt; What are their technical approaches?
&lt;span class="p"&gt;-&lt;/span&gt; What are common implementation challenges?
&lt;span class="p"&gt;-&lt;/span&gt; What makes solutions successful in this space?
&lt;span class="p"&gt;-&lt;/span&gt; Are there any regulatory or compliance considerations?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3. &lt;strong&gt;Competition Analysis&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;"Research competitors in [your solution space]:
&lt;span class="p"&gt;-&lt;/span&gt; Who are the main players?
&lt;span class="p"&gt;-&lt;/span&gt; What tech stacks do they use?
&lt;span class="p"&gt;-&lt;/span&gt; What are their key features?
&lt;span class="p"&gt;-&lt;/span&gt; What are their strengths and weaknesses?
&lt;span class="p"&gt;-&lt;/span&gt; What are users saying about these solutions?
&lt;span class="p"&gt;-&lt;/span&gt; What features are missing in current solutions?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4. &lt;strong&gt;Technical Validation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;"Technical research for implementation:
&lt;span class="p"&gt;-&lt;/span&gt; Best practices for [your chosen tech stack]
&lt;span class="p"&gt;-&lt;/span&gt; Common scalability issues and solutions
&lt;span class="p"&gt;-&lt;/span&gt; Security considerations
&lt;span class="p"&gt;-&lt;/span&gt; Performance optimizations
&lt;span class="p"&gt;-&lt;/span&gt; Integration challenges
&lt;span class="p"&gt;-&lt;/span&gt; Latest relevant tools and libraries"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Research Organization
&lt;/h3&gt;

&lt;p&gt;Create a summary document including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Problem Space Overview&lt;/li&gt;
&lt;li&gt;Market Gaps&lt;/li&gt;
&lt;li&gt;Competitor Matrix&lt;/li&gt;
&lt;li&gt;Technical Requirements&lt;/li&gt;
&lt;li&gt;Unique Value Propositions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This research will feed directly into your O1 documentation phase, providing solid foundations for your PRD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Project Planning with OpenAI O1 Model
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why O1 Model?
&lt;/h3&gt;

&lt;p&gt;O1 is best for documentation and should be your starting point. The documentation provides context to LLMs and gives less room for hallucination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Documents to Generate
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;PRD (Project Requirements Document)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Flow &amp;amp; Functionality Documentation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Project Development Roadmap&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These three documents should provide sufficient foundation to begin development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Providing Project Context
&lt;/h3&gt;

&lt;p&gt;Share as much information about your project as possible. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;"I want to build a web application with Next.js 14 app router using:
&lt;span class="p"&gt;-&lt;/span&gt; TypeScript
&lt;span class="p"&gt;-&lt;/span&gt; TailwindCSS
&lt;span class="p"&gt;-&lt;/span&gt; shadcn/ui

Backend:
&lt;span class="p"&gt;-&lt;/span&gt; Supabase for auth, database and storage

Project Details:
[Provide comprehensive details about:]
&lt;span class="p"&gt;-&lt;/span&gt; Problem it solves
&lt;span class="p"&gt;-&lt;/span&gt; Solution approach
&lt;span class="p"&gt;-&lt;/span&gt; Target users
&lt;span class="p"&gt;-&lt;/span&gt; Core features
&lt;span class="p"&gt;-&lt;/span&gt; API integrations
&lt;span class="p"&gt;-&lt;/span&gt; Additional context

[The more information provided, the better the documentation quality]"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Frontend Design with Bolt.new
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initial Setup Tip
&lt;/h3&gt;

&lt;p&gt;Create a base boilerplate with your common tech stack and configurations. Fork this boilerplate for new projects to save tokens and setup time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Process
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create new project in Bolt.new (using your boilerplate fork)&lt;/li&gt;
&lt;li&gt;Attach your PRD and app flow documentation&lt;/li&gt;
&lt;li&gt;Use this prompt:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;"Design me the frontend components for the project I just attached.

We will work on page to page basis:
&lt;span class="p"&gt;1.&lt;/span&gt; First: Core skeleton (Left sidebar + Main content area)
&lt;span class="p"&gt;2.&lt;/span&gt; Then: Code each page
&lt;span class="p"&gt;3.&lt;/span&gt; Finally: Subdivide pages into components for complex functionality

Do you understand?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Download or copy the final code for each component&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 4: Implementation with Claude AI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Start new Claude project&lt;/li&gt;
&lt;li&gt;Attach all documentation:

&lt;ul&gt;
&lt;li&gt;PRD&lt;/li&gt;
&lt;li&gt;App Flow &amp;amp; Functionality Doc&lt;/li&gt;
&lt;li&gt;Project Development Roadmap&lt;/li&gt;
&lt;li&gt;Bolt.new generated code files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Custom Instructions for Claude
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;"You're a Next.js 14 Expert. You code web apps with simple and scalable architecture.

Your Goal is to work with me as a Coding Guide. I will be using:
&lt;span class="p"&gt;-&lt;/span&gt; Cursor AI as my IDE
&lt;span class="p"&gt;-&lt;/span&gt; Bolt for setting up project
&lt;span class="p"&gt;-&lt;/span&gt; Replit for deployment

Provide me step by step instructions to code this project. Do not miss any step. Provide instructions in simple language (where to go, what to do)

Major help I need from you is for:
&lt;span class="p"&gt;-&lt;/span&gt; File structure 
&lt;span class="p"&gt;-&lt;/span&gt; Project setup
&lt;span class="p"&gt;-&lt;/span&gt; Debugging
&lt;span class="p"&gt;-&lt;/span&gt; Deployment

Go through the attached files everytime, to provide best answers."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Development Process
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Follow Claude's step-by-step instructions&lt;/li&gt;
&lt;li&gt;Use Cursor AI as your IDE&lt;/li&gt;
&lt;li&gt;Set up project structure&lt;/li&gt;
&lt;li&gt;Implement features systematically&lt;/li&gt;
&lt;li&gt;Debug with Claude's assistance&lt;/li&gt;
&lt;li&gt;Deploy using Replit&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Research First&lt;/strong&gt;: Validate technical decisions with Perplexity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation Second&lt;/strong&gt;: Create comprehensive documentation with O1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systematic Approach&lt;/strong&gt;: Follow the workflow sequentially&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Expect and learn from mistakes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterative Development&lt;/strong&gt;: Use multiple iterations to perfect features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool Specialization&lt;/strong&gt;: Leverage each AI tool's strengths&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Boilerplate&lt;/strong&gt;: Maintain and fork a base project setup&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Don't skip the research phase&lt;/li&gt;
&lt;li&gt;Don't expect single-prompt solutions&lt;/li&gt;
&lt;li&gt;Don't skip the documentation phase&lt;/li&gt;
&lt;li&gt;Don't ignore AI tool specializations&lt;/li&gt;
&lt;li&gt;Don't rush through error messages&lt;/li&gt;
&lt;li&gt;Don't start from scratch when you have a good boilerplate&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tips for Success
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Use Perplexity for up-to-date tech decisions&lt;/li&gt;
&lt;li&gt;Provide detailed context to AI tools&lt;/li&gt;
&lt;li&gt;Save successful prompts for reuse&lt;/li&gt;
&lt;li&gt;Document errors and solutions&lt;/li&gt;
&lt;li&gt;Build progressively, starting with core features&lt;/li&gt;
&lt;li&gt;Test extensively at each stage&lt;/li&gt;
&lt;li&gt;Keep your boilerplate updated with latest dependencies&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Important Note
&lt;/h2&gt;

&lt;p&gt;There will be many errors, mistakes, and retries before mastering this workflow. However, once you master AI-assisted coding, you can build any SaaS for yourself or clients efficiently.&lt;/p&gt;

&lt;p&gt;Remember: Claims about building complete web apps with a single prompt are misleading. Success comes from systematic approach and understanding each tool's strengths and limitations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Research tech stack and best practices (Perplexity)&lt;/li&gt;
&lt;li&gt;Create comprehensive documentation (O1)&lt;/li&gt;
&lt;li&gt;Design and generate frontend components (Bolt.new)&lt;/li&gt;
&lt;li&gt;Implement with expert guidance (Claude + Cursor)&lt;/li&gt;
&lt;li&gt;Deploy and iterate (Replit)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This systematic approach ensures high-quality results while maximizing the effectiveness of each AI tool in your development workflow.​​​​​​​​​​​​​​​​&lt;/p&gt;

</description>
      <category>aicodeworkflow</category>
      <category>promptengineering</category>
      <category>webdevautomation</category>
    </item>
    <item>
      <title>Make master Branch Default Again</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Thu, 07 Nov 2024 16:11:35 +0000</pubDate>
      <link>https://dev.to/dpaluy/make-master-branch-default-again-3d52</link>
      <guid>https://dev.to/dpaluy/make-master-branch-default-again-3d52</guid>
      <description>&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; Good version control is about maintaining a clear, authoritative source of truth for your code. The name &lt;strong&gt;should serve this purpose&lt;/strong&gt; first and foremost.&lt;/p&gt;




&lt;p&gt;I didn't follow the trend of changing my default branch from &lt;code&gt;master&lt;/code&gt; to &lt;code&gt;main&lt;/code&gt;. It doesn't make any sense to spend my resources reconfiguring my settings.&lt;br&gt;
Also, DHH &lt;a href="https://world.hey.com/dhh/forcing-master-to-main-was-a-good-faith-exploit-b21ee30c" rel="noopener noreferrer"&gt;agreed&lt;/a&gt; that it was a classic exploit of good faith.&lt;/p&gt;

&lt;h2&gt;
  
  
  Historical Context: The Master Craftsman Tradition
&lt;/h2&gt;

&lt;p&gt;The term "master" has a rich history in European guild systems and skilled trades dating back centuries. A master craftsman (or "Meister" in German-speaking regions) was someone who had achieved the highest level of skill and expertise in their trade. This usage derives from the Latin "magister," meaning "teacher" or "chief."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apprentices would study under a master to learn their craft&lt;/li&gt;
&lt;li&gt;Becoming a master required years of study and the creation of a "masterpiece"&lt;/li&gt;
&lt;li&gt;Masters were teachers and guardians of quality standards&lt;/li&gt;
&lt;li&gt;The title "Master" indicated expertise and teaching authority, not ownership&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Git's Usage of "Master"
&lt;/h2&gt;

&lt;p&gt;When Git was developed, the term "master" was chosen for the default branch to indicate it as the authoritative version of the code - the version from which all other branches derive their work. This usage aligns with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Master recordings in audio production (the final, authoritative mix)&lt;/li&gt;
&lt;li&gt;Master copies in publishing (the definitive version)&lt;/li&gt;
&lt;li&gt;Master's degrees in academia (advanced level of subject expertise)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Benefits of "Master"
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Widely Recognized:&lt;/strong&gt; The term is universally understood in version control systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool Compatibility:&lt;/strong&gt; Many CI/CD tools and scripts expect "master" as the default&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear Meaning:&lt;/strong&gt; Instantly communicates which branch is authoritative&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historical Continuity:&lt;/strong&gt; Maintains compatibility with existing documentation and workflows&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Local computer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --system init.defaultbranch master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Github Update
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Personal Settings
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Visit: &lt;a href="https://github.com/settings/repositories" rel="noopener noreferrer"&gt;https://github.com/settings/repositories&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Set the default repository branch to: &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&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%2F9y3cgc7iszd585u9tkex.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%2F9y3cgc7iszd585u9tkex.png" alt="Repository Default Branch in Github" width="800" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Organization Account
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Organization settings of your repository: &lt;code&gt;https://github.com/organizations/[ORGANIZATION]/settings/repository-defaults&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set the default repository branch to: &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>master</category>
      <category>dothingsthatmatter</category>
    </item>
    <item>
      <title>Implement a Secure, Dynamic Domain Approval System for Embeddable Widgets in Ruby on Rails</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Wed, 06 Nov 2024 00:48:43 +0000</pubDate>
      <link>https://dev.to/dpaluy/implement-a-secure-dynamic-domain-approval-system-for-embeddable-widgets-in-ruby-on-rails-2f53</link>
      <guid>https://dev.to/dpaluy/implement-a-secure-dynamic-domain-approval-system-for-embeddable-widgets-in-ruby-on-rails-2f53</guid>
      <description>&lt;p&gt;In the previous &lt;a href="https://dev.to/dpaluy/embed-js-widgets-smoothly-with-rails-a-step-by-step-guide-fpk"&gt;post&lt;/a&gt; I explained how to implement Embed JS Widgets with Ruby on Rails. &lt;/p&gt;

&lt;p&gt;But you have to define the approved domain explicitly:&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="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_dispatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="s1"&gt;'Content-Security-Policy'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"frame-ancestors 'self' https://trusted-domain.com"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A common, naive approach is to set frame-ancestors to allow embedding on any domain using a wildcard &lt;code&gt;(*)&lt;/code&gt;. While this enables maximum flexibility, it also opens the widget to misuse and unauthorized access.&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/widgets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WidgetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'application/javascript'&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Security-Policy'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"frame-ancestors *"&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&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;While this approach is convenient, it lacks domain restriction, meaning any website can use the widget, which could lead to potential misuse. For a secure, client-specific widget, let’s set up a dynamic domain approval system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secure Approach: Dynamic Domain Approval
&lt;/h2&gt;

&lt;p&gt;To ensure that only authorized clients can embed the widget, we’ll implement a system where each &lt;code&gt;Account&lt;/code&gt; in the application has approved domains. When the widget is served, it dynamically checks the account’s approved domains and sets a restrictive &lt;code&gt;frame-ancestors&lt;/code&gt; header accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Set Up the Account Model with Approved Domains
&lt;/h3&gt;

&lt;p&gt;Update your Account model to include a domain attribute representing the domain where the client’s widget can be embedded.&lt;/p&gt;

&lt;p&gt;Example: &lt;code&gt;Account.create!(name: "Example Client 1", domain: "example1.com")&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Set Up a Secure Widget Endpoint
&lt;/h3&gt;

&lt;p&gt;To serve the widget securely, we’ll modify the &lt;code&gt;WidgetsController&lt;/code&gt; to check the Account model for the requesting client’s approved domain and set the &lt;code&gt;frame-ancestors&lt;/code&gt; directive based on that domain.&lt;/p&gt;

&lt;h4&gt;
  
  
  Define the Widget Controller Action
&lt;/h4&gt;

&lt;p&gt;In the &lt;code&gt;WidgetsController&lt;/code&gt;, add logic to look up the client account based on a unique identifier, such as an API key. This API key can be passed securely as part of the script request to identify the client.&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/widgets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WidgetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:set_content_security_policy&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'application/javascript'&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&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;set_content_security_policy&lt;/span&gt;
    &lt;span class="c1"&gt;# Look up the account based on a unique identifier, such as an API key&lt;/span&gt;
    &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;api_key: &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;:api_key&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;account&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
      &lt;span class="c1"&gt;# Restrict embedding to the approved domain&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Content-Security-Policy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"frame-ancestors &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# Deny embedding if no approved domain is found&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Content-Security-Policy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"frame-ancestors 'none'"&lt;/span&gt;
      &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="ss"&gt;:forbidden&lt;/span&gt; &lt;span class="c1"&gt;# Block access if the account or domain is not valid&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;ul&gt;
&lt;li&gt;The &lt;code&gt;set_content_security_policy&lt;/code&gt; method checks for a matching &lt;code&gt;Account&lt;/code&gt; based on an &lt;code&gt;api_key&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;If the account has a valid approved domain, it sets the &lt;code&gt;frame-ancestors&lt;/code&gt; directive to allow embedding only on that domain.
If no approved domain is found, it sets &lt;code&gt;frame-ancestors&lt;/code&gt; to &lt;code&gt;'none'&lt;/code&gt;, denying access and returning a 403 Forbidden status.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Generate API Keys for Accounts
&lt;/h4&gt;

&lt;p&gt;Each &lt;code&gt;Account&lt;/code&gt; should have a unique API key to secure access. Generate these keys and store them in the &lt;code&gt;Account&lt;/code&gt; model.&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;# db/migrate/xxxxxx_add_api_key_to_accounts.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddApiKeyToAccounts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;7.0&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;change&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;add_index&lt;/span&gt; &lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;unique: &lt;/span&gt;&lt;span class="kp"&gt;true&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/account.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;before_create&lt;/span&gt; &lt;span class="ss"&gt;:generate_api_key&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;generate_api_key&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&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;span class="c1"&gt;# Generates a 40-character API key&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;Use this API key in the widget request URL to identify the account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Update the Embed Code for Client Sites
&lt;/h3&gt;

&lt;p&gt;Provide each client with an embed code that includes their unique API key. This ensures only authorized clients can load the widget on their approved domain.&lt;/p&gt;

&lt;h4&gt;
  
  
  Client-Specific Embed Code
&lt;/h4&gt;

&lt;p&gt;The API key is embedded in the script URL to securely identify the client account:&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="c"&gt;&amp;lt;!-- Embed Code for Client Site --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yourapp.com/widget.js?api_key=CLIENT_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;CLIENT_API_KEY&lt;/code&gt; with the actual API key for each client. This key allows Rails to dynamically set the &lt;code&gt;frame-ancestors&lt;/code&gt; header according to the client’s approved domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Test and Monitor Access
&lt;/h3&gt;

&lt;p&gt;Test the widget on both approved and unapproved domains to ensure this solution works as expected.&lt;/p&gt;

&lt;p&gt; 1. &lt;strong&gt;Approved Domain Test&lt;/strong&gt;: Embed the widget code on a page hosted on the approved domain (e.g., example1.com). Confirm the widget loads and displays properly.&lt;/p&gt;

&lt;p&gt; 2. &lt;strong&gt;Unapproved Domain Test&lt;/strong&gt;: Try embedding the widget on an unapproved domain. Confirm that the widget does not load and the network request returns a 403 Forbidden status.&lt;/p&gt;

&lt;p&gt;Happy Hacking!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>widget</category>
      <category>security</category>
      <category>embed</category>
    </item>
    <item>
      <title>Embed JS Widgets with Rails: A Step-by-Step Guide</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Tue, 05 Nov 2024 21:12:00 +0000</pubDate>
      <link>https://dev.to/dpaluy/embed-js-widgets-smoothly-with-rails-a-step-by-step-guide-fpk</link>
      <guid>https://dev.to/dpaluy/embed-js-widgets-smoothly-with-rails-a-step-by-step-guide-fpk</guid>
      <description>&lt;p&gt;Creating a custom JavaScript widget that can be embedded in client websites is a powerful way to extend the reach of your Ruby on Rails application. Whether it's a chat box, an analytics tracker, or any other interactive element, a custom widget lets you bring functionality directly to users. Let’s walk through the steps of building a secure, embeddable widget and cover the best practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;p&gt; 1. Step 1: Setup&lt;br&gt;
 2. Step 2: Creating an Embed Code for Clients&lt;br&gt;
 3. Step 3: Embedding Your Widget in an Iframe (Optional)&lt;br&gt;
 4. Step 4: Setting Headers for Iframe Embedding&lt;br&gt;
 5. Step 5: Test the Widget&lt;br&gt;
 6. Step 6: Adding Version Support with Versioned Routes and Controllers&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Setup
&lt;/h2&gt;

&lt;p&gt;Create a new Rails endpoint: This endpoint will serve the JavaScript code for your widget. Typically, this could be an action within a &lt;code&gt;WidgetsController&lt;/code&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;# app/controllers/widgets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WidgetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="c1"&gt;# Your widget code here&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;Configure the Route: Set up a route to make the widget accessible.&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/routes.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/widget.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'widgets#show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :widget&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Serve JavaScript from the Controller: In Rails, you can respond with JavaScript by setting the appropriate content type.&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/widgets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WidgetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'application/javascript'&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&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;Write the JavaScript Code for Your Widget: The widget script typically includes the logic to render a custom HTML element or iframe on the client’s page.&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/views/widgets/show.js.erb&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;widgetDiv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;widgetDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;widgetDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;p&amp;gt;This is your custom widget content!&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;widgetDiv&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;h2&gt;
  
  
  Step 2: Creating an Embed Code for Clients
&lt;/h2&gt;

&lt;p&gt;To allow clients to embed your widget, provide a JavaScript snippet they can copy and paste into their HTML:&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="c"&gt;&amp;lt;!-- Client Embeddable Code --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yourapp.com/widget.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;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Embedding Your Widget in an Iframe (Optional)
&lt;/h2&gt;

&lt;p&gt;Consider embedding the widget content in an iframe for more isolation and control over styling. This approach keeps the widget's styling and behavior separate from the client's site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update the JavaScript Code to create an iframe for the widget:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/views/widgets/show.js.erb&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= widget_content_url %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;300px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;200px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&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;h3&gt;
  
  
  Define a New Route and View for the Widget Content:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/routes.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/widget_content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'widgets#content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :widget_content&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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/widgets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;content&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Widget Content HTML: Create a view to render inside the iframe
&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;!-- app/views/widgets/content.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"iframe-widget"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is the widget content in an iframe.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Setting Headers for Iframe Embedding
&lt;/h2&gt;

&lt;p&gt;Configure the appropriate HTTP headers to ensure your widget works securely in an iframe. There are two primary headers to consider:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remove the X-Frame-Options Header&lt;/strong&gt;: The X-Frame-Options header is deprecated but still widely respected by many browsers. To remove it, add the following configuration in an initializer:&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/initializers/security_headers.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_dispatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-Frame-Options'&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;Set the Frame-Ancestors Directive&lt;/strong&gt;:The modern and more flexible approach is to use &lt;code&gt;Content-Security-Policy&lt;/code&gt; with the &lt;code&gt;frame-ancestors&lt;/code&gt; directive, which allows you to specify the domains allowed to embed your widget in an iframe. Adjust this header as needed based on your security requirements.&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/initializers/security_headers.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_dispatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="s1"&gt;'Content-Security-Policy'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"frame-ancestors 'self' https://trusted-domain.com"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration allows your app to be embedded in iframes only by the specified domains. Replace &lt;code&gt;https://trusted-domain.com&lt;/code&gt; with the actual domains you trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Test the Widget
&lt;/h2&gt;

&lt;p&gt;Once you’ve set up the widget and headers, test the widget by embedding it on a test page in various browsers to ensure compatibility. If you’ve used &lt;code&gt;frame-ancestors&lt;/code&gt;, confirm that only the specified domains can embed your widget and that the widget displays as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Adding Version Support with Versioned Routes and Controllers
&lt;/h2&gt;

&lt;p&gt;As your widget evolves, you may introduce new features or improvements that not all clients are ready to adopt immediately. Supporting multiple versions of your widget ensures backward compatibility, allowing clients to upgrade at their own pace without disrupting their existing integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Version Support Using Versioned Routes and Controllers
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Define Versioned Routes
&lt;/h4&gt;

&lt;p&gt;Start by setting up separate routes for each version of your widget. Namespacing each version keeps your code organized and allows you to manage different versions independently.&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/routes.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:v1&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/widget.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'widgets#show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :widget_v1&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/widget_content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'widgets#content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :widget_content_v1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:v2&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/widget.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'widgets#show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :widget_v2&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/widget_content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'widgets#content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :widget_content_v2&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Add more versions as needed&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create Versioned Controllers
&lt;/h4&gt;

&lt;p&gt;For each version, create a controller within its namespace. This separation ensures that changes in one version don't affect others.&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/v1/widgets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;V1&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WidgetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'application/javascript'&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'v1/widgets/show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&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;content&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'v1/widgets/content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/v2/widgets_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;V2&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WidgetsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'application/javascript'&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'v2/widgets/show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&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;content&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'v2/widgets/content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&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;h4&gt;
  
  
  Create Version-Specific JavaScript and HTML Views
&lt;/h4&gt;

&lt;p&gt;Each version can have its own JavaScript and HTML files, allowing you to tailor functionality and appearance per version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Version 1 JavaScript:&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;// app/views/v1/widgets/show.js.erb&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Widget Version 1 Loaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= widget_content_v1_url %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;300px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;200px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&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;&lt;strong&gt;Version 1 Content:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- app/views/v1/widgets/content.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"iframe-widget-v1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is the widget content for version 1.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Version 2 JavaScript:&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;// app/views/v2/widgets/show.js.erb&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Widget Version 2 Loaded with New Features&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= widget_content_v2_url %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;400px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Updated dimensions&lt;/span&gt;
  &lt;span class="nx"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;300px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframe&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;&lt;strong&gt;Version 2 Content:&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- app/views/v2/widgets/content.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"iframe-widget-v2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is the widget content for version 2 with new features!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Provide Versioned Embed Codes to Clients
&lt;/h4&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;!-- Client Embeddable Code for Version 1 --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yourapp.com/v1/widget.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;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Creating an embeddable widget for a Rails application involves a few key considerations: serving JavaScript securely, managing styles, and configuring headers correctly for iframe compatibility. By following the steps above, you’ll have a widget that clients can easily add to their sites, expanding the usability of your Rails application.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>javascript</category>
      <category>embed</category>
      <category>widget</category>
    </item>
    <item>
      <title>Using CursorAI as Your Developer Companion: A Complete Guide</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Sat, 02 Nov 2024 23:22:31 +0000</pubDate>
      <link>https://dev.to/dpaluy/using-cursorai-as-your-developer-companion-a-complete-guide-54gd</link>
      <guid>https://dev.to/dpaluy/using-cursorai-as-your-developer-companion-a-complete-guide-54gd</guid>
      <description>&lt;h1&gt;
  
  
  Mastering CursorAI: A Developer's Guide to AI-Powered Coding
&lt;/h1&gt;

&lt;p&gt;As artificial intelligence continues to transform software development, CursorAI emerges as a powerful developer companion. This guide will help you leverage CursorAI effectively, from initial setup to advanced usage patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Getting Started&lt;/li&gt;
&lt;li&gt;The Specification Writer&lt;/li&gt;
&lt;li&gt;Development Workflow&lt;/li&gt;
&lt;li&gt;Advanced Features&lt;/li&gt;
&lt;li&gt;Privacy and Security&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;CursorAI is more than just a code editor - it's an AI-powered development environment that can help you write, review, and improve code. You can significantly boost your productivity by understanding its capabilities and how to use them effectively.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Model Selection Strategy
&lt;/h3&gt;

&lt;p&gt;CursorAI provides access to different AI models, each with its strengths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Claude 3.5&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfect for exploration and initial development&lt;/li&gt;
&lt;li&gt;Quick iterations and implementations&lt;/li&gt;
&lt;li&gt;Acts as your junior developer for task execution&lt;/li&gt;
&lt;li&gt;Ideal for brainstorming and context building&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GPT o1&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Excels at in-depth analysis&lt;/li&gt;
&lt;li&gt;Makes architectural decisions&lt;/li&gt;
&lt;li&gt;Writes detailed specifications&lt;/li&gt;
&lt;li&gt;Functions like a senior engineer&lt;/li&gt;
&lt;li&gt;Takes more time but provides deeper insights&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Specification Writer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting Up Your Specification Writer
&lt;/h3&gt;

&lt;p&gt;Start with this foundational prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a software architect. Your task is to provide clear, precise specifications 
for engineers to implement. Focus only on the current features, avoiding theoretical 
or future considerations. Break down the specs feature by feature, keeping each prompt 
under 200 words.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feature Definition Process
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Structure your features clearly&lt;/li&gt;
&lt;li&gt;Include acceptance criteria&lt;/li&gt;
&lt;li&gt;Specify technical constraints&lt;/li&gt;
&lt;li&gt;Add technology stack requirements&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example specification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Feature: User Authentication
Stack: Next.js 14, Prisma, PostgreSQL
Description: Email-based authentication system with password reset
Acceptance Criteria:
&lt;span class="p"&gt;-&lt;/span&gt; Email/password signup
&lt;span class="p"&gt;-&lt;/span&gt; Login with credentials
&lt;span class="p"&gt;-&lt;/span&gt; Password reset flow
&lt;span class="p"&gt;-&lt;/span&gt; Session management
Technical Constraints:
&lt;span class="p"&gt;-&lt;/span&gt; Must use Next-Auth
&lt;span class="p"&gt;-&lt;/span&gt; JWT token-based
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Development Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using CursorRules
&lt;/h3&gt;

&lt;p&gt;CursorRules help maintain consistency in your codebase. Create a &lt;code&gt;.cursorrules&lt;/code&gt; file in your project root to define coding standards and preferences.&lt;/p&gt;

&lt;p&gt;For Ruby on Rails projects, you can use this comprehensive template:&lt;br&gt;
&lt;a href="https://gist.github.com/dpaluy/a4facedfeb11894174cb23b648640a6f" rel="noopener noreferrer"&gt;Ruby on Rails CursorRules&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Code Tagging
&lt;/h3&gt;

&lt;p&gt;Use the @/ symbol to help CursorAI focus on relevant code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@/src/components  # Focus on components
@/lib/utils      # Focus on utilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps the AI understand context and generate more relevant code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Save All Feature
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use "save all" in the CursorAI composer to write changes to files&lt;/li&gt;
&lt;li&gt;If the button isn't visible, try resizing your window&lt;/li&gt;
&lt;li&gt;Test changes before accepting them&lt;/li&gt;
&lt;li&gt;You can still reject changes after saving&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advanced Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Effective Prompting
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Preparation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plan your prompts carefully&lt;/li&gt;
&lt;li&gt;Use simpler models for prompt refinement&lt;/li&gt;
&lt;li&gt;Think through what you want to achieve&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Iteration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be patient with o1 model responses&lt;/li&gt;
&lt;li&gt;Provide clear, reasoned explanations&lt;/li&gt;
&lt;li&gt;Build on previous responses&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Workflow Optimization
&lt;/h3&gt;

&lt;p&gt;For exploration:&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%2F0w2aeqfg5chdol0q2n1w.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%2F0w2aeqfg5chdol0q2n1w.png" alt="Exploration Flow" width="800" height="1450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For architecture decisions:&lt;br&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%2Fwhyk4moorxoq0xs7wfn8.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%2Fwhyk4moorxoq0xs7wfn8.png" alt="architecture decisions" width="800" height="1450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Privacy Mode Options
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Basic Privacy Settings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable Privacy Mode to prevent code storage&lt;/li&gt;
&lt;li&gt;Note: Prompts are retained for 30 days by AI providers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Indexing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Control automatic indexing&lt;/li&gt;
&lt;li&gt;Use manual indexing with "Compute Index"&lt;/li&gt;
&lt;li&gt;Understand what data is being indexed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  API Key Usage
&lt;/h3&gt;

&lt;p&gt;When using your API key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests still go through Cursor's backend&lt;/li&gt;
&lt;li&gt;Required for prompt construction&lt;/li&gt;
&lt;li&gt;Consider this in your security planning&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Model Selection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Claude 3.5 for exploration and quick tasks&lt;/li&gt;
&lt;li&gt;Switch to o1 for critical decisions&lt;/li&gt;
&lt;li&gt;Match the model to the task complexity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code Organization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use code tagging effectively&lt;/li&gt;
&lt;li&gt;Maintain a clear project structure&lt;/li&gt;
&lt;li&gt;Follow framework conventions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document AI-specific commands&lt;/li&gt;
&lt;li&gt;Keep track of successful prompts&lt;/li&gt;
&lt;li&gt;Note any limitations or workarounds&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test generated code thoroughly&lt;/li&gt;
&lt;li&gt;Use incremental changes&lt;/li&gt;
&lt;li&gt;Verify edge cases&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/dpaluy/a4facedfeb11894174cb23b648640a6f" rel="noopener noreferrer"&gt;Ruby on Rails &lt;code&gt;.cursorrules&lt;/code&gt; Template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cursor's &lt;a href="https://docs.cursor.com/get-started/usage" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Community templates and examples

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playbooks.com/course/introduction-to-cursor" rel="noopener noreferrer"&gt;https://playbooks.com/course/introduction-to-cursor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cursor.directory/" rel="noopener noreferrer"&gt;https://cursor.directory/&lt;/a&gt; &lt;code&gt;.cursorrules&lt;/code&gt; for various frameworks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;CursorAI is a powerful tool that can significantly enhance your development workflow. By understanding its capabilities and following these guidelines, you can make the most of its features while maintaining code quality and security.&lt;/p&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose the suitable model for your task&lt;/li&gt;
&lt;li&gt;Plan your prompts carefully&lt;/li&gt;
&lt;li&gt;Use privacy features appropriately&lt;/li&gt;
&lt;li&gt;Test thoroughly&lt;/li&gt;
&lt;li&gt;Document your successful patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With practice, CursorAI becomes an invaluable member of your development team, helping you write better code faster while maintaining high quality and security standards.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>productivity</category>
      <category>code</category>
    </item>
    <item>
      <title>Testing External Services with RSpec, VCR, and WebMock in Ruby on Rails</title>
      <dc:creator>David Paluy</dc:creator>
      <pubDate>Sat, 02 Nov 2024 22:20:05 +0000</pubDate>
      <link>https://dev.to/dpaluy/testing-external-services-with-rspec-vcr-and-webmock-in-ruby-on-rails-4ndo</link>
      <guid>https://dev.to/dpaluy/testing-external-services-with-rspec-vcr-and-webmock-in-ruby-on-rails-4ndo</guid>
      <description>&lt;h1&gt;
  
  
  Testing External Services with RSpec, VCR, and WebMock in Ruby on Rails
&lt;/h1&gt;

&lt;p&gt;For quick setup, just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/X8Bsyo'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 &lt;a href="https://railsbytes.com/public/templates/X8Bsyo" rel="noopener noreferrer"&gt;View template source&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When building Rails applications that integrate with external services, it's crucial to have reliable tests that don't depend on actual HTTP requests. This tutorial will show you how to use VCR and WebMock to record and replay HTTP interactions in your RSpec tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;First, add these gems to your &lt;code&gt;Gemfile&lt;/code&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="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rspec-rails'&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'vcr'&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'webmock'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;bundle install&lt;/code&gt; to install the gems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring VCR and WebMock
&lt;/h2&gt;

&lt;p&gt;Create a new file at &lt;code&gt;spec/support/vcr.rb&lt;/code&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vcr'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'webmock/rspec'&lt;/span&gt;

&lt;span class="c1"&gt;# Disable all real HTTP connections, except to localhost&lt;/span&gt;
&lt;span class="no"&gt;WebMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable_net_connect!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;allow_localhost: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;VCR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&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;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# Where VCR will store the recorded HTTP interactions&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cassette_library_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'spec'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'cassettes'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;# Ignore localhost requests&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ignore_localhost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Tell VCR to use WebMock&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hook_into&lt;/span&gt; &lt;span class="ss"&gt;:webmock&lt;/span&gt;

  &lt;span class="c1"&gt;# Allow VCR to be configured through RSpec metadata&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure_rspec_metadata!&lt;/span&gt;

  &lt;span class="c1"&gt;# Allow real HTTP connections when no cassette is in use&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allow_http_connections_when_no_cassette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Configure how requests are matched&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_cassette_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="ss"&gt;match_requests_on: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Filter sensitive data before recording&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before_record&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;interaction&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'[FILTERED]'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Ignore specific hosts&lt;/span&gt;
  &lt;span class="n"&gt;ignored_hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'codeclimate.com'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ignore_hosts&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ignored_hosts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Configure RSpec to use VCR&lt;/span&gt;
&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&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;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;)&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;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:vcr&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;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:allow_playback_repeats&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:record&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:skip&lt;/span&gt;
      &lt;span class="no"&gt;VCR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turned_off&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;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;custom_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:vcr&lt;/span&gt;&lt;span class="p"&gt;]&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;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cassette_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;generated_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:full_description&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\s+/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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="nf"&gt;underscore&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="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/[^\w\/]+/&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="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&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="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;custom_name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;generated_name&lt;/span&gt;
      &lt;span class="no"&gt;VCR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use_cassette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&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;example&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;h2&gt;
  
  
  Using VCR in Your Tests
&lt;/h2&gt;

&lt;p&gt;Here are some examples of how to use VCR in your specs:&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spec/services/weather_service_spec.rb&lt;/span&gt;
&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;WeatherService&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#get_forecast'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'fetches weather data from the API'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;WeatherService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
      &lt;span class="n"&gt;forecast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_forecast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'London'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'temperature'&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;h3&gt;
  
  
  Custom Cassette Names
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;WeatherService&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#get_forecast'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'fetches weather data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;vcr: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;cassette_name: &lt;/span&gt;&lt;span class="s1"&gt;'weather_api/london_forecast'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="c1"&gt;# Your test code&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;It will make a first connection to the external service and record the response into a cassete&lt;/p&gt;

&lt;h3&gt;
  
  
  Skipping VCR
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'makes real HTTP requests'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;vcr: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;record: :skip&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# VCR will be disabled for this test&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sensitive Data&lt;/strong&gt;: Always filter sensitive information like API keys and tokens:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;VCR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&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;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter_sensitive_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;API_KEY&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'API_KEY'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Match Request Bodies&lt;/strong&gt;: When testing POST requests, make sure to match on body content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;vcr: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="ss"&gt;match_requests_on: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;record: :new_episodes&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Regular Cassette Updates&lt;/strong&gt;: Periodically re-record your cassettes to ensure they match current API responses:&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common Issues
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unmatched Requests&lt;/strong&gt;: If VCR can't find a matching request, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request method (GET, POST, etc.)&lt;/li&gt;
&lt;li&gt;URL (including query parameters)&lt;/li&gt;
&lt;li&gt;Request body&lt;/li&gt;
&lt;li&gt;Headers (if matching on headers)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Playback Failures&lt;/strong&gt;: If recorded responses aren't playing back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify the cassette file exists&lt;/li&gt;
&lt;li&gt;Check if the request matching criteria are too strict&lt;/li&gt;
&lt;li&gt;Ensure sensitive data is being filtered consistently&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;VCR and WebMock provide a robust solution for testing external service integrations in Rails. By following these patterns and best practices, you can create reliable, maintainable tests that don't depend on external services being available.&lt;/p&gt;

&lt;p&gt;Remember to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filter sensitive data&lt;/li&gt;
&lt;li&gt;Organize cassettes logically&lt;/li&gt;
&lt;li&gt;Update cassettes periodically&lt;/li&gt;
&lt;li&gt;Match requests appropriately&lt;/li&gt;
&lt;li&gt;Handle errors gracefully&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rspec</category>
      <category>rails</category>
      <category>vcr</category>
      <category>webmock</category>
    </item>
  </channel>
</rss>
