<?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: Jessica Temporal</title>
    <description>The latest articles on DEV Community by Jessica Temporal (@jesstemporal).</description>
    <link>https://dev.to/jesstemporal</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F238241%2F6069ae7c-7b26-40f6-ad35-31377af2d653.jpeg</url>
      <title>DEV Community: Jessica Temporal</title>
      <link>https://dev.to/jesstemporal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jesstemporal"/>
    <language>en</language>
    <item>
      <title>Preview Auth0 Changes Safely with Deploy CLI Dry Run</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Tue, 16 Jun 2026 22:25:31 +0000</pubDate>
      <link>https://dev.to/auth0/preview-auth0-changes-safely-with-deploy-cli-dry-run-38hn</link>
      <guid>https://dev.to/auth0/preview-auth0-changes-safely-with-deploy-cli-dry-run-38hn</guid>
      <description>&lt;p&gt;Managing your Auth0 tenant configuration as code shouldn't be a "guess and check" process. In this video, I'll demonstrate how to use the &lt;em&gt;Auth0 Deploy CLI's Dry Run&lt;/em&gt; feature. Learn how to preview exactly which resources will be created, updated, or deleted before you ever touch your production environment.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What You'll Learn&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to configure the Auth0 Deploy CLI with a &lt;code&gt;config.json&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The proper syntax for exporting your current tenant configuration to YAML.&lt;/li&gt;
&lt;li&gt;How to use the &lt;code&gt;--dry-run&lt;/code&gt; flag to validate local changes.&lt;/li&gt;
&lt;li&gt;Best practices for keeping sensitive credentials out of your Git history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Resources&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📄 &lt;a href="https://github.com/auth0/auth0-deploy-cli/blob/master/docs/using-dry-run.md" rel="noopener noreferrer"&gt;Auth0 Deploy CLI Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;a href="https://github.com/auth0/auth0-deploy-cli/tree/master" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tooling</category>
      <category>ai</category>
      <category>security</category>
      <category>cli</category>
    </item>
    <item>
      <title>How to Decode, Encode, and Validate JWTs inside Claude Code</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Fri, 29 May 2026 17:42:15 +0000</pubDate>
      <link>https://dev.to/auth0/how-to-decode-encode-and-validate-jwts-inside-claude-code-240b</link>
      <guid>https://dev.to/auth0/how-to-decode-encode-and-validate-jwts-inside-claude-code-240b</guid>
      <description>&lt;p&gt;&lt;em&gt;Stop context switching and start debugging.&lt;/em&gt; Let me show you how to supercharge your AI agent with JWT Skills. Learn how to decode, encode, and validate JSON Web Tokens (JWTs) directly within your terminal session using Claude Code. Whether you're troubleshooting an expired token or testing a custom claim, these tools bring security insights right into your development flow.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What You'll Learn:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to install the &lt;code&gt;jwt-skills&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Decoding tokens to inspect headers, payloads, and claims.&lt;/li&gt;
&lt;li&gt;Identifying security flags like expired tokens or PII in payloads.&lt;/li&gt;
&lt;li&gt;Generating test tokens with custom claims for edge-case testing.&lt;/li&gt;
&lt;li&gt;Validating tokens against JWKS endpoints for production-level checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🛠 &lt;em&gt;&lt;a href="https://github.com/jsonwebtoken/jwt-skills" rel="noopener noreferrer"&gt;Get the Skills&lt;/a&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;em&gt;&lt;a href="https://jwt.io" rel="noopener noreferrer"&gt;JWT Deep Dive&lt;/a&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Manage Your Auth0 Tenants Faster with the Gemini CLI Extension</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Wed, 29 Apr 2026 21:01:35 +0000</pubDate>
      <link>https://dev.to/auth0/manage-your-auth0-tenants-faster-with-the-gemini-cli-extension-2dh8</link>
      <guid>https://dev.to/auth0/manage-your-auth0-tenants-faster-with-the-gemini-cli-extension-2dh8</guid>
      <description>&lt;p&gt;Stop switching between your browser and terminal to manage identity. In this video, I'll show you how to integrate the &lt;em&gt;Auth0 MCP Server&lt;/em&gt; directly into your &lt;em&gt;Gemini CLI&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Learn how to leverage AI to query your applications, initialize tenants, and manage APIs using natural language commands without leaving your development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to find the Auth0 extension on the official Gemini page.&lt;/li&gt;
&lt;li&gt;The one-command installation process for the Auth0 MCP server.&lt;/li&gt;
&lt;li&gt;Authenticating and initializing your Auth0 environment via CLI.&lt;/li&gt;
&lt;li&gt;Using natural language to list applications and create new API resources.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/auth0/auth0-mcp-server" rel="noopener noreferrer"&gt;💻 Auth0 MCP Server GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/docs/get-started/auth0-mcp-server" rel="noopener noreferrer"&gt;🛠️ Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/blog/auth0-mcp-server-in-gemini-cli-extensions" rel="noopener noreferrer"&gt;📝 Auth0 Blog Post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>tooling</category>
      <category>gemini</category>
    </item>
    <item>
      <title>How to Use Auth0 Agent Skills in Claude Code &amp; AI Coding Assistants</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Mon, 06 Apr 2026 18:27:59 +0000</pubDate>
      <link>https://dev.to/auth0/how-to-use-auth0-agent-skills-in-claude-code-ai-coding-assistants-56e5</link>
      <guid>https://dev.to/auth0/how-to-use-auth0-agent-skills-in-claude-code-ai-coding-assistants-56e5</guid>
      <description>&lt;p&gt;Tired of your AI coding assistant hallucinating APIs or writing insecure auth patterns? In this video, I'll show you how to use Auth0 Agent Skills to teach your AI assistant (like Claude Code or GitHub Copilot) how to implement Auth0 correctly. Say goodbye to XSS vulnerabilities and manual JWT decoding—ship production-ready, secure authentication from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why standard AI coding assistants struggle with secure authentication.&lt;/li&gt;
&lt;li&gt;How to install Auth0 Agent Skills via NPX or Claude Code plugins.&lt;/li&gt;
&lt;li&gt;The difference between Core Skills and SDK Skills (React, Next.js, etc.).&lt;/li&gt;
&lt;li&gt;A side-by-side comparison of "hallucinated" code vs. secure Auth0 patterns.&lt;/li&gt;
&lt;li&gt;How to implement production-ready auth in Next.js using Agent Skills.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;📝 &lt;a href="https://auth0.com/blog/auth0-agent-skills-ai-coding-assistants/" rel="noopener noreferrer"&gt;Read the full blog post&lt;/a&gt;&lt;br&gt;
🛠️ &lt;a href="https://auth0.com/docs/quickstart/agent-skills" rel="noopener noreferrer"&gt;Auth0 Documentation&lt;/a&gt;&lt;br&gt;
💻 &lt;a href="https://github.com/auth0/agent-skills" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Auth0 MCP Server Extension for Gemini CLI</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Fri, 27 Mar 2026 11:00:00 +0000</pubDate>
      <link>https://dev.to/auth0/auth0-mcp-server-extension-for-gemini-cli-405m</link>
      <guid>https://dev.to/auth0/auth0-mcp-server-extension-for-gemini-cli-405m</guid>
      <description>&lt;p&gt;The Auth0 MCP Server is now listed on the official Gemini CLI extensions page. This means the Auth0 MCP Server is now directly installable through Gemini CLI with one command, allowing you to authenticate to Auth0 directly from your Gemini CLI session and load tenant information automatically.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/iiSuv8BnPC0"&gt;
  &lt;/iframe&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  What the Auth0 MCP Server Extension Provides
&lt;/h2&gt;

&lt;p&gt;The extension packages the Auth0 MCP Server for Gemini CLI and adds three integration layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discoverability&lt;/strong&gt;: Listed on &lt;a href="https://geminicli.com/extensions" rel="noopener noreferrer"&gt;geminicli.com/extensions&lt;/a&gt;, searchable by name, installable without manual configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication Commands&lt;/strong&gt;: Built-in slash commands for Auth0 tenant management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/auth0:init&lt;/code&gt; - Device authorization flow with tenant selection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/auth0:logout&lt;/code&gt; - Session termination&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/auth0:session&lt;/code&gt; - Current authentication status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Context Injection&lt;/strong&gt;: After authentication, Gemini gains your tenant information so the AI can query applications, APIs, connections, actions, and logs without requiring manual tenant specification in every prompt.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation and Setup
&lt;/h2&gt;

&lt;p&gt;Install the extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gemini extensions &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/auth0/auth0-mcp-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the command successfully finishes you should see a message stating &lt;code&gt;Extension “Auth0” installed successfully&lt;/code&gt;:&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%2Ffzi13ha0j201bszply11.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%2Ffzi13ha0j201bszply11.png" alt="Terminal showing the installation of the Auth0 MCP server as a Gemini CLI extension"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initialize the Auth0 MCP Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/auth0:init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to allow the command to run when prompted. The server will run automatically.&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%2Fr7q3o4dmnm02utqeu4ji.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%2Fr7q3o4dmnm02utqeu4ji.png" alt="Gemini CLI terminal showing /auth0:init command with permission prompt to allow command execution"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll authenticate via &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow" rel="noopener noreferrer"&gt;device code flow&lt;/a&gt; to select your tenant:&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%2Frnik2h6j061y0y9brksu.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%2Frnik2h6j061y0y9brksu.png" alt="Auth0 device authorization screen displaying device code and instructions to complete authentication"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And confirm the permissions:&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%2F0wx8dzfzrmbnntzeaj7b.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%2F0wx8dzfzrmbnntzeaj7b.png" alt="Auth0 authorization screen showing requested permissions for Auth0 MCP Server extension"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once authenticated, you should see a message within Gemini saying the Auth0 MCP Server is configured and to restart Gemini CLI to see the changes:&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%2F10jdvkxmdmkufvebcm26.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%2F10jdvkxmdmkufvebcm26.png" alt="Gemini CLI terminal displaying successful Auth0 MCP Server initialization with tenant connection confirmed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Refresh the MCP server list with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/mcp refresh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemini now has Auth0 context. Ask: "show me my applications" and the AI will receive the structured information about your applications, which Gemini CLI will display as a structured tool call result:&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%2Fwrab4gmufeqlx8rtwf8m.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%2Fwrab4gmufeqlx8rtwf8m.png" alt="Gemini CLI tool call output showing structured JSON data of Auth0 applications from the MCP server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And since Gemini now understands your tenant structure, existing configurations, and naming conventions, it can also show you the same information in a more readable format:&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%2F7n6pl8wgu6ipiazokouq.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%2F7n6pl8wgu6ipiazokouq.png" alt="Gemini CLI displaying formatted, human-readable list of Auth0 applications with names, types, and client IDs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Before this extension, using the Auth0 MCP Server with Gemini CLI required manual server configuration, environment variable setup, and custom initialization scripts. The extension collapses that into a single install command and three slash commands.&lt;/p&gt;

&lt;p&gt;More importantly: context persistence. Once authenticated, every Gemini session knows your Auth0 environment. You're not re-explaining your tenant structure or copy-pasting app IDs. The AI assistant operates with the same tenant awareness you have.&lt;/p&gt;

&lt;p&gt;This is the same Auth0 MCP Server that powers VS Code integrations, now packaged for Gemini CLI's extension model. Same capabilities, different CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The Auth0 MCP Server supports tenant management, application configuration, API setup, and log analysis. For implementation details and the full MCP Server feature set, &lt;a href="https://github.com/auth0/auth0-mcp-server?tab=readme-ov-file#%EF%B8%8F-supported-tools" rel="noopener noreferrer"&gt;see the list on GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The extension is available now at &lt;a href="https://geminicli.com/extensions" rel="noopener noreferrer"&gt;geminicli.com/extensions&lt;/a&gt;. Install, authenticate, and start managing Auth0 tenants through natural language.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>tutorial</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Demystifying OAuth Security: State vs. Nonce vs. PKCE</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Fri, 13 Mar 2026 16:17:44 +0000</pubDate>
      <link>https://dev.to/auth0/demystifying-oauth-security-state-vs-nonce-vs-pkce-2eo2</link>
      <guid>https://dev.to/auth0/demystifying-oauth-security-state-vs-nonce-vs-pkce-2eo2</guid>
      <description>&lt;p&gt;Confused by the random strings in your OAuth URLs? You aren't alone. Many developers think &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;nonce&lt;/code&gt;, and &lt;code&gt;code_challenge&lt;/code&gt; (PKCE) are redundant—but skipping just one could leave your users' accounts wide open to attackers like "Eve." In this video, I'll break down why these three parameters are like three different locks on three different doors. We’ll look at real-world attack scenarios and show you exactly how each one keeps your app secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡  What You’ll Learn:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The State Parameter:&lt;/strong&gt; How to prevent Cross-Site Request Forgery ($CSRF$) attacks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Nonce Parameter:&lt;/strong&gt; Why ID tokens need protection against Replay attacks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PKCE (Proof Key for Code Exchange):&lt;/strong&gt; Protecting mobile and single-page apps from Authorization Code Injection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implementation Strategy:&lt;/strong&gt; Why you should use all three instead of picking just one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔗 Links:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://auth0.com/blog/demystifying-oauth-security-state-vs-nonce-vs-pkce" rel="noopener noreferrer"&gt;Read the full blog post by Andrea Chiarelli&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce" rel="noopener noreferrer"&gt;Auth0 Docs - Why PKCE?&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://oauth.net/2/security-topics/" rel="noopener noreferrer"&gt;OAuth 2.0 Security Best Practices&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you enjoy this content and want to learn more about identity, security, and access management, subscribe to our channel! &lt;/p&gt;

&lt;p&gt;Have a topic you'd like to see covered? Let us know if the comments below 👀 &lt;/p&gt;

</description>
      <category>oauth</category>
      <category>security</category>
      <category>software</category>
    </item>
    <item>
      <title>Automating CSS versioning in staging through GitHub Actions</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Tue, 03 Feb 2026 06:00:00 +0000</pubDate>
      <link>https://dev.to/jesstemporal/automating-css-versioning-in-staging-through-github-actions-obo</link>
      <guid>https://dev.to/jesstemporal/automating-css-versioning-in-staging-through-github-actions-obo</guid>
      <description>&lt;p&gt;While setting up a staging environment in a new hosting platform, I ran into an issue where static assets were aggressively cached with no straightforward way to invalidate them. This made staging deploys unreliable and validating changes slow.&lt;/p&gt;

&lt;p&gt;I could have spent hours fighting with cache headers and purge APIs, but there’s a simpler approach. Rather than fighting the cache, I leaned into patterns that avoid invalidation entirely and make staging behaviour explicit and predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;This staging environment was behaving badly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static assets were cached aggressively&lt;/li&gt;
&lt;li&gt;Cache purging is unavailable&lt;/li&gt;
&lt;li&gt;Deploys appear as “successful” but changes were not visible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I was stuck with a cache that I couldn’t purge and changes I needed to validate somewhere other than my local machine. So I figured it was time to version the styling scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioned static assets
&lt;/h2&gt;

&lt;p&gt;One reliable way to bypass aggressive caching is to change the asset URL on every deploy.&lt;/p&gt;

&lt;p&gt;Static assets are referenced with a deploy-specific version, in my case I went for short git SHA.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/static/css/base.css?v=6ea4bbe"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This was enough to fix the problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CDNs cache by URL.&lt;/li&gt;
&lt;li&gt;A new URL guarantees a cache miss.&lt;/li&gt;
&lt;li&gt;No reliance on purge APIs or cache headers.&lt;/li&gt;
&lt;li&gt;Simple, deterministic behaviour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach works well for staging, where correctness matters more than caching efficiency. Then my next challenge was: &lt;em&gt;how to get this into my deployment without me manually editing the URLs every single time?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Label-gated staging deploys
&lt;/h2&gt;

&lt;p&gt;I can’t remember to update URLs manually every single time, so instead of suffering every time my CSS wouldn’t update accordingly, I adjusted my code and added a step in my GitHub Actions to take care of this for me.&lt;/p&gt;

&lt;p&gt;Since I’m the sole developer on this project, my staging deploys are explicitly controlled using pull request labels.&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%2Fumu4bpp40qzogltgc7ng.webp" 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%2Fumu4bpp40qzogltgc7ng.webp" alt="Pull request with preview label applied to enable staging deployment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pull request is deployed to staging only when the &lt;code&gt;preview&lt;/code&gt; is applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Actions
&lt;/h2&gt;

&lt;p&gt;In case you want to replicate this for yourself, here’s how to do it. The steps are pretty simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run your tests;&lt;/li&gt;
&lt;li&gt;If tests pass deploy the app to the staging environment&lt;/li&gt;
&lt;li&gt;Make a comment on your PR so you know the version of the CSS that should be live&lt;/li&gt;
&lt;li&gt;Enjoy QA-ing your staged deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First the setup for your action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests and stage changes&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;opened&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;synchronize&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;reopened&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;labeled&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unlabeled&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This names the action and tells it which Pull Requests to look at, in my case all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Tests running
&lt;/h3&gt;

&lt;p&gt;I want to make sure that all pull requests pass my test suite, so the first job checks out the pull request, installs the dependencies and creates necessary files, then runs the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout PR branch&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install uv&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;astral-sh/setup-uv@v7&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uv sync --extra dev&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup test environment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cp .env.example .env&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uv run pytest&lt;/span&gt;

  &lt;span class="na"&gt;deploy-staging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This guarantees code quality in all pull requests before I even consider deployment.&lt;/p&gt;

&lt;p&gt;With a successful test we can move on to deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Stage the deployment
&lt;/h3&gt;

&lt;p&gt;The deployment job only needs to run when the &lt;code&gt;preview&lt;/code&gt; label is included.&lt;/p&gt;

&lt;p&gt;Then a neat trick: you can get the SHA for the deployment with &lt;code&gt;github.sha&lt;/code&gt; and write it out to a file, in this case &lt;code&gt;.deploy_sha&lt;/code&gt; and once the code is sent to the cloud it can use that file to read the information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;

  &lt;span class="na"&gt;deploy-staging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Only run after tests pass&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;contains(github.event.pull_request.labels.*.name, 'preview')&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout PR branch&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install uv&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;astral-sh/setup-uv@v7&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set deploy SHA&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "$" | cut -c1-7 &amp;gt; .deploy_sha&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to staging&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CLOUD_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$&lt;/span&gt;
          &lt;span class="na"&gt;CLOUD_APP_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# your deploy command here&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;My code also needed to account for that. So first I created a function in my FastAPI app to grab the first few characters of the SHA from either the environment variable or the &lt;code&gt;.deploy_sha&lt;/code&gt; file. I also set a fall back to &lt;code&gt;dev&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Deploy SHA for cache busting - check env var first, then file, fallback to "dev"
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_deploy_sha&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get deploy SHA from environment or .deploy_sha file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;sha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEPLOY_SHA&lt;/span&gt;&lt;span class="sh"&gt;"&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;sha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Try reading from file (created during CI/CD deploy)
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.deploy_sha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Automatically make the deploy_sha available in all templates
&lt;/span&gt;&lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deploy_sha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_deploy_sha&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The environment variable is used in production which normally doesn’t change unless I see some weird caching I’m not expecting to, whereas while developing locally the fallback takes action and in staging we use the file.&lt;/p&gt;

&lt;p&gt;Finally the HTML template looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/static/css/base.css?v="&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Since we pass the &lt;code&gt;deploy_sha&lt;/code&gt; automatically as part of the globals variables for templates, any page will have the information they need when the application is being built.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Get a comment
&lt;/h3&gt;

&lt;p&gt;Finally, I wanted to get a comment I can see both:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;That the deployment is live&lt;/li&gt;
&lt;li&gt;The hash I should look for in case I notice some discrepancies between what I’m seeing and the deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To this I added a final step to the &lt;code&gt;deploy-staging&lt;/code&gt; job with the following code:&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="err"&gt;#&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="nx"&gt;deploy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;staging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;

      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Comment&lt;/span&gt; &lt;span class="nx"&gt;staging&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;PR&lt;/span&gt;
        &lt;span class="nx"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;v7&lt;/span&gt;
        &lt;span class="kd"&gt;with&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="o"&gt;|&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&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;marker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;!-- staging-deploy --&amp;gt;&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n🚀 **Staging deploy complete** \n\nPreview: $\n\nCommit: &lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Find existing comment&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listComments&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
              &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;issue_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&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;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&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;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateComment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;comment_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;body&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="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;issue_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;body&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="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;Since I also didn’t want every new commit to generate a new comment, I used an HTML comment to mark the message:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;marker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;!-- staging-deploy --&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Since comments render Markdown the HTML shows up in edit mode but gets hidden when displayed.&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%2F5fqlqbph4pm85ho5d51q.webp" 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%2F5fqlqbph4pm85ho5d51q.webp" alt="GitHub Actions comment showing staging deployment URL and commit SHA"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is what that looks like as the final step of the &lt;code&gt;deploy-staging&lt;/code&gt; is completed:&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%2F2z3hw80tefl3ov7ckegz.webp" 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%2F2z3hw80tefl3ov7ckegz.webp" alt="Staging deployment comment on pull request with deployment link and version SHA"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Trade-offs of this approach
&lt;/h2&gt;

&lt;p&gt;This gave me full control over which pull request is staged and my staging represents “currently under review” making it easier for me to even QA the changes myself.&lt;/p&gt;

&lt;p&gt;The only downside from this approach is that I can’t have multiple ephemeral per-pull-request environments since I can only make one PR deployment at a time but that works well for my development workflow.&lt;/p&gt;

&lt;p&gt;The goal is clarity and control, not maximum automation.&lt;/p&gt;

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

&lt;p&gt;Versioned static assets and label-gated deploys solved my staging cache problem. Now CSS files get a git SHA in the URL, GitHub Actions handles deployments when I apply the &lt;code&gt;preview&lt;/code&gt; label, and I always know which version is live. No cache invalidation needed.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>git</category>
      <category>deployments</category>
      <category>css</category>
    </item>
    <item>
      <title>AI-powered software development flow: Lessons from shipping My Yarn Stash</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Tue, 03 Feb 2026 05:00:00 +0000</pubDate>
      <link>https://dev.to/jesstemporal/ai-powered-software-development-flow-lessons-from-shipping-my-yarn-stash-3bpb</link>
      <guid>https://dev.to/jesstemporal/ai-powered-software-development-flow-lessons-from-shipping-my-yarn-stash-3bpb</guid>
      <description>&lt;p&gt;When I started building &lt;a href="https://myyarnstash.app" rel="noopener noreferrer"&gt;&lt;strong&gt;My Yarn Stash&lt;/strong&gt;&lt;/a&gt;, I wasn’t trying to prove AI could build an app for me.&lt;/p&gt;

&lt;p&gt;I wanted to answer a practical question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What actually changes when you treat AI tools as long-term collaborators across planning, implementation, and design?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not with demos or toy examples, but with a real product that has users, persistent data, billing, authentication, and migrations—all the unglamorous constraints that come with shipping software meant to stick around.&lt;/p&gt;

&lt;p&gt;What follows is a breakdown of how AI fit into each phase, where it helped, where it failed, and what I learned or was reminded about tool boundaries by building something real.&lt;/p&gt;

&lt;h2&gt;
  
  
  The day the AI deleted my database
&lt;/h2&gt;

&lt;p&gt;Early in the project, I asked a coding agent to help with a schema change. It confidently suggested:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rm yarn.db
# Then recreate with new schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I let it.&lt;/p&gt;

&lt;p&gt;Gone. All the test data I’d been building up. Hours of manual entry to stress-test edge cases. Deleted in a single command because the AI treated the database like a throwaway artifact.&lt;/p&gt;

&lt;p&gt;The AI didn’t behave incorrectly. It did exactly what made sense if you assume databases are ephemeral. The problem was that &lt;strong&gt;I&lt;/strong&gt; hadn’t established guardrails.&lt;/p&gt;

&lt;p&gt;That moment reminded me to both pay more attention and adjust how I collaborated with AI tools. I needed to be very clear about constraints.&lt;/p&gt;

&lt;p&gt;Later I added rules to my copilot instructions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NEVER delete database files&lt;/li&gt;
&lt;li&gt;ALWAYS run migrations instead&lt;/li&gt;
&lt;li&gt;Before any destructive operation, run backups first&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And went on to recreate my database. The AI didn’t change its behaviour. I changed mine.&lt;/p&gt;

&lt;p&gt;This became the pattern for the entire project: &lt;strong&gt;AI amplified my clarity and punished my ambiguity and lack of attention.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The database deletion reminded me of something fundamental: vague instructions produce unpredictable results. That lesson shaped how I approached every other phase of this project moving forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate conversations, separate contexts
&lt;/h2&gt;

&lt;p&gt;After the database incident, I knew I needed better constraint management. That started with how I structured conversations.&lt;/p&gt;

&lt;p&gt;I treated ChatGPT like a colleague I’d grab for specific discussions.&lt;/p&gt;

&lt;p&gt;Each feature got its own conversation thread:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One for billing and entitlements&lt;/li&gt;
&lt;li&gt;One for the AI extraction flow&lt;/li&gt;
&lt;li&gt;One for soft-delete patterns&lt;/li&gt;
&lt;li&gt;One for launch strategy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wasn’t about organization. It was about &lt;strong&gt;context management&lt;/strong&gt;.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2y50nst46jd3c5mrlg8l.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2y50nst46jd3c5mrlg8l.png" alt="schema showing separation of conversations in different chats" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When everything lives in one chat, the AI starts to get slow and confused, that’s when constant “summarizing history” increase time to answers, so as an engineer leveraging AI, you have to dutifully manage the context window. A billing conversation shouldn’t bleed into UI discussions. Implementation details shouldn’t pollute planning threads.&lt;/p&gt;

&lt;p&gt;Separate chats forced clarity. Each conversation had a single purpose, which made it easier to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stay focused on one problem&lt;/li&gt;
&lt;li&gt;Evaluate outputs against specific goals&lt;/li&gt;
&lt;li&gt;Make decisions without noise&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What those conversations actually looked like
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Branding: From vague to concrete&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started with “I want it to feel cozy and calm.” Through discussion, we identified edge cases I hadn’t considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text baked into images would need rework every time pricing changed&lt;/li&gt;
&lt;li&gt;Realistic imagery would read as generic stock photos&lt;/li&gt;
&lt;li&gt;“Organizing” visuals imply judgment. Better to frame as “inventory tracking”&lt;/li&gt;
&lt;li&gt;Gradients applied inconsistently would create visual noise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conversation forced specificity. By the end, I had constraints like “no text in images” and “single vertical gradient system” that my coding agent could implement reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Account page: Surfacing failure modes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When planning the account page, ChatGPT helped identify non-obvious problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Timezone drift&lt;/strong&gt; : Automatically updating timezone when users travel would silently change project dates. Solution: only update on explicit user action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empty state guilt&lt;/strong&gt; : Showing “0 finished projects” or prompting users to “finish your first project” creates pressure. Solution: gentle empty state with no call to action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These weren’t implementation details. They were experience risks that would erode trust if missed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Launch strategy: Reframing the goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I initially thought about launch as a visibility event: where to post, how to announce.&lt;/p&gt;

&lt;p&gt;The discussion reframed it as a &lt;strong&gt;state change&lt;/strong&gt; : from “only I use this” to “other people can use this safely.”&lt;/p&gt;

&lt;p&gt;That shifted focus from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“How many people see this?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“What breaks when the first real users arrive?”&lt;/li&gt;
&lt;li&gt;“Is this experience internally consistent?”&lt;/li&gt;
&lt;li&gt;“Where can people get confused or succeed quickly?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I deprioritized announcements and spent more time on onboarding clarity instead.&lt;/p&gt;

&lt;p&gt;Every useful insight got distilled into markdown. Feature specs, constraints, architectural decisions: all rewritten in my own words with the help of ChatGPT, structured for clarity, stored alongside the code.&lt;/p&gt;

&lt;p&gt;This forced two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I had to actually understand the decision&lt;/li&gt;
&lt;li&gt;The output became durable and reviewable&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Stack decisions: What I chose vs. what I asked about
&lt;/h2&gt;

&lt;p&gt;With clear conversation boundaries established, I could make better stack decisions. I came into the project with strong opinions about some parts of the stack and no opinion at all about others.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqp58nhkrqpqjmbjqoeq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqp58nhkrqpqjmbjqoeq.png" alt="stack chosen" width="800" height="714"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I decided before asking AI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auth0&lt;/strong&gt; for authentication (familiar, reliable, scalable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; as the web framework (Python, async, good docs, also familiar and with great Auth0 support)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; for database and storage (Postgres with good tooling and prod database at 0 cost)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vanilla JavaScript&lt;/strong&gt; and &lt;strong&gt;handwritten CSS&lt;/strong&gt; to start instead of a framework (less abstraction, easier to reason about with AI and for me to follow along)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend for emailing users:&lt;/strong&gt; a welcome email for the users and another to notify myself when new user signed up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replicate:&lt;/strong&gt; to provide AI features like the label extraction information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These choices weren’t about AI compatibility. They were about reducing cognitive load and keeping a codebase I could jump in without the help of AI if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I asked AI to help evaluate:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Payment gateway&lt;/strong&gt; : I had no strong opinion. We discussed options and landed on &lt;strong&gt;Polar&lt;/strong&gt; for simplicity and developer experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment platform&lt;/strong&gt; : I wanted something low-cost that I already understood. Heroku fit both criteria.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern here matters: I brought constraints and context to the conversation instead of asking “what should I use?” in a vacuum.&lt;/p&gt;

&lt;p&gt;AI didn’t tell me what to build with. It helped me think through trade-offs for the decisions I hadn’t made yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning which model does what well
&lt;/h2&gt;

&lt;p&gt;Stack decisions settled, the next challenge was figuring out which AI tool to use when. Understanding model strengths came from repeated experience across different contexts.&lt;/p&gt;

&lt;p&gt;I didn’t use one AI tool for everything. That was deliberate. Through building, I found a flow that worked for me:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqtdb5pivooz9cl0fgj83.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqtdb5pivooz9cl0fgj83.png" alt="mermaid chart of a flow" width="800" height="1075"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT for planning and exploration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most planning conversations happened in ChatGPT in the mobile app. I used it on my phone during downtime to think through features, identify edge cases, and explore design decisions before writing any code.&lt;/p&gt;

&lt;p&gt;The mobile accessibility mattered. I could sketch out billing logic while having my coffee in the morning or map out future features during a commute. By the time I grabbed my computer to code, I’d already worked through the conceptual problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot Agents for straightforward work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well-defined low-complexity features and bugs went to GitHub issues and I would let Copilot handle implementation through PRs asynchronously by assigning Copilot to a given issue. It freed me up to work on more-complex and high value things while Copilot handled the implementation of the “no-brainer” tasks.&lt;/p&gt;

&lt;p&gt;When agents got stuck online, I’d download the code and ask Claude Opus to step in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VS Code Copilot for local development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used VS Code with Copilot in different modes and with models depending on the work:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Claude Haiku:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mostly in Plan mode;&lt;/li&gt;
&lt;li&gt;Quick iterations when following up on features locally;&lt;/li&gt;
&lt;li&gt;Good for refactoring and adjusting implementation details that didn’t require more scope other than one file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Claude Opus:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mostly in Agent mode;&lt;/li&gt;
&lt;li&gt;Complex situations with many moving pieces&lt;/li&gt;
&lt;li&gt;Used when precision and accuracy mattered more than speed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No matter the model though, they were always constrained by explicit conventions in &lt;a href="http://copilot-instructions.md" rel="noopener noreferrer"&gt;&lt;code&gt;copilot-instructions.md&lt;/code&gt;&lt;/a&gt;. The lesson wasn’t “use the best model.” It was knowing not only &lt;strong&gt;when to switch&lt;/strong&gt; models and modes but also &lt;strong&gt;why switching&lt;/strong&gt; should happen.&lt;/p&gt;

&lt;p&gt;That judgment is a skill. AI tools don’t remove it.&lt;/p&gt;

&lt;p&gt;The database deletion happened because I let my guard down. I got comfortable with AI-assisted coding and stopped questioning the suggestions. It was a good reminder that different contexts need different constraints. By this phase of the project, I’d learned to match tool capabilities to task requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functional first, pretty later (because I’m not a designer)
&lt;/h2&gt;

&lt;p&gt;With implementation flowing more smoothly, I turned to design. This required a different kind of clarity.&lt;/p&gt;

&lt;p&gt;I deliberately delayed visual design work. Not because of anything AI-related, but because &lt;strong&gt;CSS and JavaScript are not my strong suit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I focused on making things functional first: flows, data models, interactions. Once those settled, I could tackle design from a more stable foundation. It didn’t look ugly but it clearly wasn’t beautiful:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/UulQpVyeEMs"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;My first exploration with &lt;a href="https://stitch.withgoogle.com" rel="noopener noreferrer"&gt;Stitch&lt;/a&gt;, Google’s Gemini-based tool that transforms text descriptions into UI designs, showed me how powerful it was. I dropped the link of the working app and asked for a redesign. It made something pretty, but it didn’t look like &lt;em&gt;my&lt;/em&gt; product.&lt;/p&gt;

&lt;p&gt;So I used ChatGPT to put together the branding guidelines: tone, colour direction, what feelings I wanted the UI to convey. Once I had that document written down, I went back to Stitch with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screenshots of the functional app&lt;/li&gt;
&lt;li&gt;The branding document&lt;/li&gt;
&lt;li&gt;Specific constraints like “redesign this following the brand guidelines”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result you see below:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/VHyfaJH969o"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;That combination worked. The design felt right because I’d done the thinking first. And did I mention it now supports dark mode as well?&lt;/p&gt;

&lt;p&gt;The lesson: &lt;strong&gt;Tools work better when you’ve already established intent.&lt;/strong&gt; Just like the database needed constraints, design tools needed direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this experience taught me about building with AI
&lt;/h2&gt;

&lt;p&gt;Going through all these steps I noticed a few patterns that go beyond individual tool choices. These are a few things I’m carrying forward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. AI doesn’t replace judgment. It makes judgment more important&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The database deletion wasn’t an AI failure. It was a clarity failure. Once I added explicit constraints, the tools became more reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Context boundaries matter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Separate conversations for separate concerns reduced drift and forced focus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Model switching is a learned skill&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Understanding when to use a lightweight model versus a heavy one came from repeated friction, not documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Design still requires taste&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI can accelerate iteration, but you need to know what you’re aiming for. “Make it pretty” produces generic results. “Make it feel cozy and trustworthy” with examples gives you something to evaluate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Shipping something real teaches more than demos ever could&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most AI tool examples live in a world where you can always start over. Production software doesn’t. That constraint forced honesty about where AI helps and where it doesn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for learning AI tools
&lt;/h2&gt;

&lt;p&gt;I always believed developers do better when they learn the limitations of the tools they use when building software. Specially since development is no-longer my day job, this project became an outlet for experiment and trying new tools and it helped shape how I evaluate and talk about AI tools.&lt;/p&gt;

&lt;p&gt;Demos have their place in my world, but nothing beats the actual experience of building something and learn by doing.&lt;/p&gt;

&lt;p&gt;Building My Yarn Stash gave me a even clearer sense of where AI accelerates work and where it introduces risk. Not from reading about it, but from hitting the boundaries myself.&lt;/p&gt;

&lt;p&gt;If you’re trying to find your footing with AI tools: &lt;strong&gt;build something real.&lt;/strong&gt; Solve a problem you face often, try the new tools and find the flow that works for you.&lt;/p&gt;

&lt;p&gt;Step away for a moment from the tutorials and courses and build your own project free of the constraints instructors purposely impose in order to teach and give students a good experience. Something with users, data you can’t lose, and decisions that compound over time.&lt;/p&gt;

&lt;p&gt;You’ll learn more from one broken migration than from a hundred perfect prompts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>chatgpt</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Auth0 for AI Agents is now generally available!</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Mon, 24 Nov 2025 16:32:18 +0000</pubDate>
      <link>https://dev.to/auth0/auth0-for-ai-agents-is-now-generally-available-29el</link>
      <guid>https://dev.to/auth0/auth0-for-ai-agents-is-now-generally-available-29el</guid>
      <description>&lt;p&gt;Hey DEV Community! 👋&lt;/p&gt;

&lt;p&gt;If you're building AI agents right now (and honestly, who isn't?), you've probably hit the auth problem. You know the one - where the quickest path to getting your agent working is to just hardcode some API keys and move on. It works great... until you need to actually ship to production.&lt;/p&gt;

&lt;p&gt;Today, we're excited to share that &lt;strong&gt;Auth0 for AI Agents is now generally available&lt;/strong&gt;, and it's designed to solve exactly this problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/signup?onboard_app=auth_for_aa&amp;amp;ocid=701KZ000000cXXxYAM_aPA4z0000008OZeGAM?utm_source=devto&amp;amp;utm_campaign=amer_namer_usa_all_ciam_dev_dg_plg_auth0_display_devto_display_aud_Q4_GAlaunch_nov2025_utm2&amp;amp;utm_medium=cpc&amp;amp;utm_id=aNKWR000002Z1ph4AC" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Auth0 for AI Agents&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/YypMEnCetqI"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with Hardcoded Credentials
&lt;/h2&gt;

&lt;p&gt;Let's be real: when you're prototyping an AI agent with LangChain or LlamaIndex, hardcoded credentials are the path of least resistance. Your agent needs to access Slack, GitHub, Google Calendar, or your own APIs, and frameworks make it easy to just plug in those keys.&lt;/p&gt;

&lt;p&gt;But production is a different story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when your agent needs to act on behalf of different users with different permissions?&lt;/li&gt;
&lt;li&gt;How do you handle token refreshing across 30+ different apps?&lt;/li&gt;
&lt;li&gt;How do you let users approve critical actions (like making purchases) without giving your agent carte blanche?&lt;/li&gt;
&lt;li&gt;How do you ensure your RAG-powered agent only accesses documents the user actually has permission to see?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't edge cases - they're fundamental requirements for any AI agent that's going to interact with real user data and take real actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;Auth0 for AI Agents gives you four key capabilities:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;User Authentication&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Secure and scalable &lt;a href="https://auth0.com/ai/docs/intro/user-authentication" rel="noopener noreferrer"&gt;User Authentication&lt;/a&gt; allows you to identify who's talking to your agent and give it secure access to your first-party APIs. Your agent can access user-specific data like order history, preferences, or chat logs - all scoped to the right permissions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Token Vault&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://auth0.com/features/token-vault" rel="noopener noreferrer"&gt;Token Vault&lt;/a&gt; handles OAuth flows with 30+ pre-integrated apps (GitHub, Slack, Google Workspace, and more) plus any custom OAuth provider you want to connect. It manages access tokens, refresh tokens, and the whole lifecycle automatically. Your agent requests a connection, the user authorizes once, and you never have to think about token management again.&lt;/p&gt;

&lt;p&gt;The SDK detects when a tool call needs authentication, pauses execution, prompts the user to authenticate, stores the token securely, and resumes automatically. On subsequent calls, it just works.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Asynchronous Authorization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Your agent can work in the background and only interrupt the user when it needs approval for critical actions. Using &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-initiated-backchannel-authentication-flow" rel="noopener noreferrer"&gt;Client-Initiated Backchannel Authentication (CIBA)&lt;/a&gt;, you can send approval requests via email or Auth0 Guardian (SMS coming soon). &lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;FGA for RAG&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When your agent uses Retrieval Augmented Generation to search through documents, it needs to respect access controls. &lt;a href="https://auth0.com/ai/docs/get-started/authorization-for-rag" rel="noopener noreferrer"&gt;Fine-Grained Authorization for RAG&lt;/a&gt; ensures that users only get answers from documents they actually have permission to access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;AI agents are moving from demos to production. The difference between a hackathon project and a real product often comes down to handling auth correctly. We built Auth0 for AI Agents because we kept hearing from developers that this was the hard part - not the LLM integration, not the prompt engineering, but the secure, user-scoped access to real systems.&lt;/p&gt;

&lt;p&gt;This isn't about adding features. It's about removing blockers so you can ship production-ready AI agents without building your own auth infrastructure from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework Support
&lt;/h2&gt;

&lt;p&gt;We've built SDKs for the frameworks you're already using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangChain&lt;/strong&gt; (Python &amp;amp; JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LlamaIndex&lt;/strong&gt; (Python &amp;amp; JavaScript)
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare AI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Firebase Genkit&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vercel AI SDK&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each SDK handles the OAuth dance automatically, so you can focus on building your agent's capabilities, not wrestling with authentication flows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;Our free tier includes two connected apps in Token Vault, async authorization, and all the core features you need to start building. As you scale, we have self-service plans that grow with you.&lt;/p&gt;

&lt;p&gt;Early-stage startups can apply for one year of Auth0 free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/signup?onboard_app=auth_for_aa&amp;amp;ocid=701KZ000000cXXxYAM_aPA4z0000008OZeGAM?utm_source=devto&amp;amp;utm_campaign=amer_namer_usa_all_ciam_dev_dg_plg_auth0_display_devto_display_aud_Q4_GAlaunch_nov2025_utm2&amp;amp;utm_medium=cpc&amp;amp;utm_id=aNKWR000002Z1ph4AC" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Start Building Today&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>auth0</category>
      <category>agents</category>
      <category>security</category>
      <category>ai</category>
    </item>
    <item>
      <title>Field Notes: Hacktoberfest 2025, Week 5</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Sat, 15 Nov 2025 05:00:00 +0000</pubDate>
      <link>https://dev.to/jesstemporal/field-notes-hacktoberfest-2025-week-5-88p</link>
      <guid>https://dev.to/jesstemporal/field-notes-hacktoberfest-2025-week-5-88p</guid>
      <description>&lt;p&gt;After GitHub Universe, recovering from a nasty cold, and a lot of work, I know I’m a bit late but here it goes: Final week of Hacktoberfest 2025 report!&lt;/p&gt;

&lt;h2&gt;
  
  
  GitFichas
&lt;/h2&gt;

&lt;p&gt;After the “&lt;em&gt;big slow down&lt;/em&gt;”, we actually saw some more engagement by the community with 5 pull requests in Hacktoberfest’s last week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 PRs by the community 

&lt;ul&gt;
&lt;li&gt;3 merged&lt;/li&gt;
&lt;li&gt;2 open&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I, on the other hand, got Copilot to work on a few issues 👀: 35 pull requests by AI, all merged. This meant great progress was made and that huge bump in the last days of October in the burn up chart below.&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%2Fmndzw3hf6vw0g10pa4cu.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%2Fmndzw3hf6vw0g10pa4cu.png" alt="Burnup chart showing GitFichas progress for week 5" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The gap between closed and open issues is still there. I still plan to finish migrating the hand-drawn cards to mermaid before the end of the year but I also want to reach the 100 cards published number, let’s see how it goes.&lt;/p&gt;

&lt;p&gt;Here’s the distribution of pull requests worked on in the last week of Hacktoberfest:&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%2Fv43ciosdwldgmeimjncj.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%2Fv43ciosdwldgmeimjncj.png" alt="Bar chart showing pull request type distribution for week 5 of Hacktoberfest 2025" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at the pull requests for the whole month of October, you can see that shift between a lot of translation pull requests in the beginning of the month to more migrations at the end of October in the chart below:&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%2F4vra8to2fg9jjrf1qwul.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%2F4vra8to2fg9jjrf1qwul.png" alt="Line chart showing weekly distribution of pull requests throughout October 2025" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As far as issues go, the distribution is similar. Some PRs closed more than one issue and some PRs had no issues attached to them, so the numbers vary a bit between merged PRs and closed issues, but the distribution is similar on both:&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%2Fpfxck6ell3hslawo4f02.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%2Fpfxck6ell3hslawo4f02.png" alt="Line chart showing weekly distribution of closed issues throughout October 2025" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other projects
&lt;/h2&gt;

&lt;p&gt;I went back to those PRs I left hanging and worked on them. And now I’m off to work on other stuff, especially some content I want to work on through the end of this year beginning of next one.&lt;/p&gt;




&lt;p&gt;With this report we close a successful Hacktoberfest 2025. I was not expecting so many people interested in contributing to GitFichas, and I was very happy to review every pull request that came through.&lt;/p&gt;

&lt;p&gt;I hope the folks contributing also had an amazing time as I did.&lt;/p&gt;

&lt;p&gt;Happy hacking and see you next year 🎃&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>ai</category>
      <category>git</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Hacktoberfest 2025: Diário de Campo, Semana 5</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Sat, 15 Nov 2025 04:00:00 +0000</pubDate>
      <link>https://dev.to/jesstemporal/hacktoberfest-2025-diario-de-campo-semana-5-16m</link>
      <guid>https://dev.to/jesstemporal/hacktoberfest-2025-diario-de-campo-semana-5-16m</guid>
      <description>&lt;p&gt;Depois do GitHub Universe, de me recuperar de uma gripe horrível, e de muito trabalho, eu sei que estou meio atrasada mas aqui vai: relatório da última semana da Hacktoberfest 2025!&lt;/p&gt;

&lt;h2&gt;
  
  
  GitFichas
&lt;/h2&gt;

&lt;p&gt;Depois da “&lt;em&gt;grande desaceleração&lt;/em&gt;”, a comunidade voltou a se engajar mais com 5 pull requests na última semana da Hacktoberfest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 PRs da comunidade 

&lt;ul&gt;
&lt;li&gt;3 mergeados&lt;/li&gt;
&lt;li&gt;2 abertos&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Já eu, consegui colocar o Copilot pra trabalhar em algumas issues 👀: 35 pull requests feitos por IA, todos mergeados. Isso resultou num progresso incrível e naquele grande pico no final de outubro que você vê no gráfico burnup abaixo.&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%2Fmndzw3hf6vw0g10pa4cu.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%2Fmndzw3hf6vw0g10pa4cu.png" alt="Gráfico burnup mostrando o progresso do GitFichas para a semana 5" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A diferença entre issues fechadas e abertas ainda continua lá. Minha meta ainda é terminar de migrar as fichas desenhadas à mão para mermaid antes do final do ano, mas também quero chegar nas 100 fichas publicadas, vamos ver o que vai dar.&lt;/p&gt;

&lt;p&gt;Assim ficou a distribuição dos pull requests na última semana da Hacktoberfest:&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%2Fv43ciosdwldgmeimjncj.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%2Fv43ciosdwldgmeimjncj.png" alt="Gráfico de barras mostrando a distribuição de tipos de pull request para a semana 5 da Hacktoberfest 2025" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Olhando pro mês de outubro como um todo, dá pra ver bem aquela mudança de foco: no começo do mês rolaram muitos pull requests de tradução, e no final foram mais migrações, como mostra o gráfico abaixo:&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%2F4vra8to2fg9jjrf1qwul.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%2F4vra8to2fg9jjrf1qwul.png" alt="Gráfico de linha mostrando a distribuição semanal de pull requests ao longo de outubro de 2025" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sobre as issues, o padrão é parecido. Alguns PRs resolveram várias issues de uma vez e outros não estavam associados a nenhuma issue específica, então os números ficaram um pouco diferentes entre PRs mergeados e issues fechadas, mas a tendência geral é a mesma:&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%2Fpfxck6ell3hslawo4f02.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%2Fpfxck6ell3hslawo4f02.png" alt="Gráfico de linha mostrando a distribuição semanal de issues fechadas ao longo de outubro de 2025" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Outros projetos
&lt;/h2&gt;

&lt;p&gt;Voltei naqueles PRs que tinha deixado pra lá e consegui finalizar eles. Agora vou focar em outras coisas, principalmente uns conteúdos que quero terminar durante o final desse ano e começo do ano novo.&lt;/p&gt;




&lt;p&gt;E com esse relatório a gente encerra uma Hacktoberfest 2025 na minha opinião super bem-sucedida. Não imaginava que tanta gente ia se interessar em contribuir pro GitFichas, e adorei revisar cada pull request que apareceu por lá.&lt;/p&gt;

&lt;p&gt;Espero que quem contribuiu também tenha curtido esse momento tanto quanto eu.&lt;/p&gt;

&lt;p&gt;Espero te ver ano que vem para Hacktoberfest 2026 🎃&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>ai</category>
      <category>git</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Field Notes: Hacktoberfest 2025, Week 4</title>
      <dc:creator>Jessica Temporal</dc:creator>
      <pubDate>Mon, 27 Oct 2025 04:00:00 +0000</pubDate>
      <link>https://dev.to/jesstemporal/field-notes-hacktoberfest-2025-week-4-4jl1</link>
      <guid>https://dev.to/jesstemporal/field-notes-hacktoberfest-2025-week-4-4jl1</guid>
      <description>&lt;p&gt;We are so close to the end of Hacktoberfest I can almost smell it. With one week left to go let’s take a look into how last week went.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitFichas
&lt;/h2&gt;

&lt;p&gt;After the “&lt;em&gt;big slow down&lt;/em&gt;”, this week continued the trend of lower amount of pull requests received with only 8 pull requests for last week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;8 PRs by the community 

&lt;ul&gt;
&lt;li&gt;7 merged&lt;/li&gt;
&lt;li&gt;1 closed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn’t make any pull requests to GitFichas as I was working on a big pull request for work I’ll love to share on next week’s report.&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%2Fs1my6sbalih7nfxb46i7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1my6sbalih7nfxb46i7.jpg" alt="GitFichas repository showing no open pull requests" width="800" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On other news, this weekend was the first time that GitFichas had no PRs open since the beginning of Hacktoberfest and to be honest this worked out great for me as I was spending most of my Saturday preparing to travel to GitHub Universe.&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%2Fui8slqqfvdz3bjcjlr21.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%2Fui8slqqfvdz3bjcjlr21.png" alt="Burnup chart showing GitFichas progress for week 4" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The gap between closed and open issues is still there, from my side, since my plan is to finish migrating the hand-drawn cards to mermaid before the end of the year, I plan to start tackling some of the open migration issues with the help of Copilot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/jtemporal/gitfichas/issues/385" rel="noopener noreferrer"&gt;Migrate all English 🇺🇸 cards to Mermaid&lt;/a&gt;: 27 issues to go&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jtemporal/gitfichas/issues/65" rel="noopener noreferrer"&gt;Migrate all Portuguese 🇧🇷 cards to mermaid&lt;/a&gt;: 36 issues to go&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contributor Badges
&lt;/h2&gt;

&lt;p&gt;Hacktoberfest is not over yet! You can still make your contributions and earn at least the 4 of the 6 badge levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;level 0 is for registering to Hacktoberfest&lt;/li&gt;
&lt;li&gt;levels 1 through 4 are for making the 4 pull requests while contributing to open source&lt;/li&gt;
&lt;li&gt;level 5 is the supercontributor badge you get while completing 6 pull requests.&lt;/li&gt;
&lt;/ul&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%2Fsdkf3n36c8bdxa7jw5ws.webp" 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%2Fsdkf3n36c8bdxa7jw5ws.webp" alt="Hacktoberfest Level 0 badge" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/hacktoberfest2025/userbadge/cmfugxuyz0001l104bsvhumlx" rel="noopener noreferrer"&gt;Level 0&lt;/a&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%2Fnyxznnkh119ijf12j2gt.webp" 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%2Fnyxznnkh119ijf12j2gt.webp" alt="Hacktoberfest Level 1 badge" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/hacktoberfest2025/userbadge/cmgjivt9x004wjv04tj9hiamp" rel="noopener noreferrer"&gt;Level 1&lt;/a&gt; |&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%2Fbdac59qkj2t6ys65pm7a.webp" 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%2Fbdac59qkj2t6ys65pm7a.webp" alt="Hacktoberfest Level 2 badge" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/hacktoberfest2025/userbadge/cmgjo5qd00047ld0487evw3y2" rel="noopener noreferrer"&gt;Level 2&lt;/a&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%2Fvzi6egpbpaff3dpxhs5t.webp" 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%2Fvzi6egpbpaff3dpxhs5t.webp" alt="Hacktoberfest Level 3 badge" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/hacktoberfest2025/userbadge/cmgjtgwh800gbjo04fzjw6zev" rel="noopener noreferrer"&gt;Level 3&lt;/a&gt; |&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%2Fkhpzqnbxn20e7npfu8wy.webp" 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%2Fkhpzqnbxn20e7npfu8wy.webp" alt="Hacktoberfest Level 4 badge" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/hacktoberfest2025/userbadge/cmgjth6e9003wjr048lfiogn8" rel="noopener noreferrer"&gt;Level 4&lt;/a&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%2Fcz51xasy7qr1nho502rj.webp" 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%2Fcz51xasy7qr1nho502rj.webp" alt="Hacktoberfest Supercontributor badge" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/hacktoberfest2025/userbadge/cmgjthekm004ol204irxkgqp6" rel="noopener noreferrer"&gt;Supercontributor&lt;/a&gt; |&lt;/p&gt;

&lt;p&gt;I also got the &lt;strong&gt;Plant a tree&lt;/strong&gt; and &lt;strong&gt;1 Badge Club&lt;/strong&gt; , the first is for the tree planted when you get the shirt for completing 6 PRs, and the second is for anyone that has gotten a Hacktoberfest badge since 2022:&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%2F1hucf3ock35p1h2ff1gs.webp" 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%2F1hucf3ock35p1h2ff1gs.webp" alt="Hacktoberfest Plant a tree badge" width="720" height="720"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/userbadge/cmgjtujb20075jr04ik32pbcn" rel="noopener noreferrer"&gt;Plant a tree&lt;/a&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%2Fuwt4dc8xncm4kdhtq6l0.webp" 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%2Fuwt4dc8xncm4kdhtq6l0.webp" alt="Hacktoberfest 1 Badge Club badge" width="720" height="720"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.holopin.io/userbadge/cmgzybiwe01pnl204bktxtxdt" rel="noopener noreferrer"&gt;1 Badge Club&lt;/a&gt; |&lt;/p&gt;




&lt;p&gt;I know this was a short one but that’s a wrap for week 4! See you soon for the final report on Hacktoberfest 2025. 🎃&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>ai</category>
      <category>git</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
