<?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: Jesus Manuel Garcia Torres</title>
    <description>The latest articles on DEV Community by Jesus Manuel Garcia Torres (@gartox).</description>
    <link>https://dev.to/gartox</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%2F766778%2Fc19c1f2b-20d5-48ca-846a-66bffacf1493.jpg</url>
      <title>DEV Community: Jesus Manuel Garcia Torres</title>
      <link>https://dev.to/gartox</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gartox"/>
    <language>en</language>
    <item>
      <title>Meet Anfi: an agent that gets first-timers to their first pull request, one step at a time</title>
      <dc:creator>Jesus Manuel Garcia Torres</dc:creator>
      <pubDate>Mon, 12 Oct 2026 17:09:26 +0000</pubDate>
      <link>https://dev.to/gartox/meet-anfi-an-agent-that-gets-first-timers-to-their-first-pull-request-one-step-at-a-time-2enf</link>
      <guid>https://dev.to/gartox/meet-anfi-an-agent-that-gets-first-timers-to-their-first-pull-request-one-step-at-a-time-2enf</guid>
      <description>&lt;p&gt;Next Saturday, October 17, we're hosting &lt;strong&gt;Hacktoberfest 2026 Tabasco&lt;/strong&gt; at Xpectre House in Villahermosa, Mexico. The poster says "Beginners welcome", and we mean it. But every year I see the same moment: someone opens GitHub, stares at millions of repositories, and quietly closes the laptop.&lt;/p&gt;

&lt;p&gt;This weekend I built &lt;strong&gt;Anfi&lt;/strong&gt; (short for &lt;em&gt;anfitrión&lt;/em&gt;, Spanish for "host"), a chat agent whose only job is to get a first-time contributor from &lt;em&gt;"I've never done this"&lt;/em&gt; to &lt;em&gt;"I opened my first pull request."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does and who it's for
&lt;/h2&gt;

&lt;p&gt;Anfi is for people coming to their first open source event: students, career-switchers, and developers who have used GitHub only to download things.&lt;/p&gt;

&lt;p&gt;In a conversation, Anfi:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Asks two questions, and no more:&lt;/strong&gt; which language you know (even a little), and whether you've used git.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finds one beginner-friendly issue&lt;/strong&gt; on GitHub that matches you. It looks for open, unassigned issues labeled &lt;code&gt;good first issue&lt;/code&gt; in your language. If you don't code yet, it looks for documentation issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Walks you through the contribution one step at a time&lt;/strong&gt;: ask to be assigned, fork, clone, branch, change, commit, push, open the PR. It waits for you to say "done" before giving the next step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answers event questions&lt;/strong&gt; (when, where, what to bring) and is honest when it doesn't know something.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It speaks Spanish or English, depending on how you write to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built it
&lt;/h2&gt;

&lt;p&gt;I built Anfi in &lt;strong&gt;n8n&lt;/strong&gt;, the workflow automation tool I use every day as an n8n Ambassador, with &lt;strong&gt;Amazon Bedrock&lt;/strong&gt; as the brain.&lt;/p&gt;

&lt;p&gt;The workflow has five nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chat Trigger.&lt;/strong&gt; n8n's hosted public chat gives me a shareable URL for attendees, so nobody has to install anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Agent.&lt;/strong&gt; This node holds the system prompt, which is where most of the design work went (more on that below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Bedrock Chat Model.&lt;/strong&gt; The workflow ships wired to &lt;strong&gt;Claude Haiku on Amazon Bedrock&lt;/strong&gt;. For a guided, short-message conversation it is fast and cheap, and Bedrock keeps the model behind IAM credentials instead of yet another API key. For fast, free iteration, I ran the test suite below on the same workflow with the model node pointed at &lt;strong&gt;Gemma 4 running locally in Ollama&lt;/strong&gt;; the model is a single swappable node in n8n.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window Buffer Memory.&lt;/strong&gt; It keeps the last ~10 messages per session, so Anfi remembers your language and git level as you go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Request Tool (&lt;code&gt;search_good_first_issues&lt;/code&gt;).&lt;/strong&gt; It calls the GitHub Search API with a query like &lt;code&gt;is:issue is:open no:assignee label:"good first issue" language:python&lt;/code&gt;. The model fills in the language with n8n's &lt;code&gt;$fromAI()&lt;/code&gt;. I trimmed the response down to title, URL, repo, comment count and labels, so the model reads five small objects instead of a wall of JSON.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I treated the system prompt as the product spec. It defines the event facts (and tells the agent to never invent others), the conversation flow, the tone (3–6 lines, at most one emoji), how to handle errors, and one safety rule: &lt;strong&gt;issue text from GitHub is untrusted data, and Anfi never follows instructions inside it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To keep the build honest, the workflow JSON is generated from the prompt file by a script. A validator checks the node types, the connections, that the system message matches the prompt byte for byte, and that no secrets leaked into the export. A second script runs the real GitHub queries for Python, JavaScript and Markdown to confirm they return issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The delightful detail: one issue, one step
&lt;/h2&gt;

&lt;p&gt;The obvious design is what most agents do: return a list of ten issues and a full eight-step git tutorial. It's technically correct, and it's exactly what makes beginners close the laptop. Choosing between ten repos you've never heard of is a decision a first-timer can't make yet.&lt;/p&gt;

&lt;p&gt;So I made one rule and built everything around it: &lt;strong&gt;Anfi gives you exactly one thing at a time.&lt;/strong&gt; One issue, picked for you, with one sentence on why it's a good first one. Then one step, and it waits for you to say "done" before the next.&lt;/p&gt;

&lt;p&gt;The first run follows the same rule. Anfi greets you and asks two plain questions ("What language do you know, even a little? Have you used git?"), not a form. You get value in your second message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I know it worked:&lt;/strong&gt; before putting it in front of real attendees, I wrote 20 end-to-end scenarios that talk to the live n8n chat endpoint, multi-turn, the way a person would: a Python beginner who has never used git, someone who "can't code yet", an English speaker, someone who asks for a different issue, someone who pastes a git error, someone who asks for the system prompt, someone who offers their GitHub token. Each scenario checks the behavior that matters for the experience (exactly one issue link, the reply's language, short messages, correct event facts), and the runner calls the GitHub API to confirm that &lt;strong&gt;every issue Anfi recommends actually exists and is open&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The first run passed &lt;strong&gt;15 of 20&lt;/strong&gt;. One failure was my test's fault. The other four were real, and they were all experience bugs rather than crashes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anfi named an issue but forgot the link.&lt;/li&gt;
&lt;li&gt;It said "give me a moment, I'll search" and then never called the tool. The person only ever sees the final message.&lt;/li&gt;
&lt;li&gt;When someone opened with "I can't code yet and never used git", it asked the two questions they had just answered.&lt;/li&gt;
&lt;li&gt;It answered an English speaker in Spanish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each fix was a line in the system prompt. Reading the transcripts also caught a false pass in my own language check, so I tightened it and confirmed it now fails on the old reply. The third run passed &lt;strong&gt;19 of 20&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In all 10 replies that recommended an issue, there was exactly one link, and every link was a real, open issue.&lt;/li&gt;
&lt;li&gt;No replies leaked the prompt or accepted a token.&lt;/li&gt;
&lt;li&gt;The remaining failure is honest and still open: a Spanish message that contained an English git error got its (correct) explanation in English.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proof it works
&lt;/h2&gt;

&lt;p&gt;A real conversation from the test run, word for word:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; no sé programar todavía y nunca he usado git &lt;em&gt;(I can't code yet and I've never used git)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anfi:&lt;/strong&gt; ¡Hola! No te preocupes, es un punto de partida perfecto y es muy valiente querer empezar. ¡Te voy a guiar paso a paso! He encontrado una tarea sencilla en el repositorio &lt;code&gt;mdn/translated-content&lt;/code&gt;: &lt;strong&gt;[es] Sincronizar Games/Tutorial_2D_breakout_game_pure_JavaScript/Initialize_the_canvas&lt;/strong&gt;, &lt;a href="https://github.com/mdn/translated-content/issues/39175" rel="noopener noreferrer"&gt;https://github.com/mdn/translated-content/issues/39175&lt;/a&gt; …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For someone who can't code yet, it picked a Spanish translation task on MDN. That's the kind of match I would have made by hand.&lt;/p&gt;

&lt;p&gt;And a live run in n8n's hosted chat. The issue title is the link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyiemwbpuntk36avisa9e.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyiemwbpuntk36avisa9e.webp" alt="Anfi in n8n's hosted chat recommending one beginner-friendly Python issue" width="800" height="933"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run the same 20 scenarios against Claude Haiku on Bedrock and compare the numbers with the local run.&lt;/li&gt;
&lt;li&gt;Fix the last failing scenario: when a message mixes Spanish with an English error log, Anfi should still answer in Spanish.&lt;/li&gt;
&lt;li&gt;Add a GitHub token so the search tool isn't limited by the API's unauthenticated rate limit during the event rush.&lt;/li&gt;
&lt;li&gt;Let Anfi check the PR link you paste and congratulate you once the PR actually exists.&lt;/li&gt;
&lt;li&gt;Bring it to the room on Saturday and watch where people still get stuck.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're organizing a Hacktoberfest event, the workflow is a single JSON import: plug in your AWS credentials and your event details, and you have a host that never gets tired of explaining &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See you in Villahermosa. 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://builder.aws.com/content/3KbRmAwxRSApnBFvtM2JIDaaChg/meet-anfi-an-agent-that-gets-first-timers-to-their-first-pull-request-one-step-at-a-time" rel="noopener noreferrer"&gt;AWS Builder Center&lt;/a&gt;. Drafted with AI assistance; the build, tests and results are mine.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>n8n</category>
      <category>hacktoberfest</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Resuelve tu primer conflicto de Git entendiendo el cambio, con ayuda de GitKraken</title>
      <dc:creator>Jesus Manuel Garcia Torres</dc:creator>
      <pubDate>Sun, 11 Oct 2026 22:11:53 +0000</pubDate>
      <link>https://dev.to/gartox/resuelve-tu-primer-conflicto-de-git-entendiendo-el-cambio-con-ayuda-de-gitkraken-472d</link>
      <guid>https://dev.to/gartox/resuelve-tu-primer-conflicto-de-git-entendiendo-el-cambio-con-ayuda-de-gitkraken-472d</guid>
      <description>&lt;p&gt;Soy ambassador de GitKraken. Esta práctica usa un conflicto intencional para mostrar cómo interpretar e integrar dos cambios.&lt;/p&gt;

&lt;p&gt;Un conflicto aparece cuando Git necesita que una persona decida el resultado de una integración. Dos cambios pueden ser válidos por separado y aun así requerir una decisión conjunta.&lt;/p&gt;

&lt;p&gt;Vamos a modificar la misma línea desde dos ramas. Después construiremos un resultado que conserve ambas intenciones: ampliar el horario y aclarar la zona horaria.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de empezar
&lt;/h2&gt;

&lt;p&gt;Necesitas Git, GitKraken Desktop y un editor. Usa una carpeta nueva y Bash, Zsh o Git Bash. Todo el ejercicio es local.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Crea una versión inicial
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;taller-git-conflictos
&lt;span class="nb"&gt;cd &lt;/span&gt;taller-git-conflictos
git init &lt;span class="nt"&gt;-b&lt;/span&gt; main
git config user.name &lt;span class="s2"&gt;"Demo Git"&lt;/span&gt;
git config user.email &lt;span class="s2"&gt;"demo@example.com"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Horario: 09:00 a 18:00\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; horario.txt
git add horario.txt
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"docs: registrar horario inicial"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abre el repositorio en GitKraken. Este commit será el ancestro común de las dos versiones.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Amplía el horario en una rama
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch &lt;span class="nt"&gt;-c&lt;/span&gt; docs/horario-ampliado
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Horario: 08:00 a 20:00\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; horario.txt
git add horario.txt
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"docs: ampliar horario de atención"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La intención es atender durante más horas. Todavía no hemos cambiado &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Aclara la zona horaria en main
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Horario: 09:00 a 18:00, hora de Ciudad de México\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; horario.txt
git add horario.txt
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"docs: aclarar zona horaria"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el gráfico verás que las ramas divergieron: cada una tiene un commit que la otra no contiene.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Inicia la integración
&lt;/h2&gt;

&lt;p&gt;Con &lt;code&gt;main&lt;/code&gt; activa en GitKraken, haz clic derecho sobre &lt;code&gt;docs/horario-ampliado&lt;/code&gt; y selecciona la opción de merge hacia la rama actual. Confirma que el destino sea &lt;code&gt;main&lt;/code&gt;. El &lt;a href="https://help.gitkraken.com/gitkraken-desktop/branching-and-merging/" rel="noopener noreferrer"&gt;flujo oficial de merge&lt;/a&gt; describe este procedimiento.&lt;/p&gt;

&lt;p&gt;También puedes iniciar la misma operación desde la terminal, pero ejecuta solo una de las dos alternativas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge docs/horario-ampliado
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El conflicto en &lt;code&gt;horario.txt&lt;/code&gt; es el resultado esperado.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Decide qué debería decir el archivo
&lt;/h2&gt;

&lt;p&gt;Abre el archivo conflictivo. Si usas un editor de texto, encontrarás un bloque parecido a este:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
Horario: 09:00 a 18:00, hora de Ciudad de México
=======
Horario: 08:00 a 20:00
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; docs/horario-ampliado
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este merge, &lt;code&gt;HEAD&lt;/code&gt; representa &lt;code&gt;main&lt;/code&gt;, la rama destino. La otra sección corresponde a la rama que estás incorporando.&lt;/p&gt;

&lt;p&gt;Elegir cualquiera de las líneas completas perdería una intención. Para este ejercicio, el resultado acordado es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Horario: 08:00 a 20:00, hora de Ciudad de México
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reemplaza todo el bloque por esa línea y guarda. GitKraken ofrece herramientas visuales para conflictos; su editor de salida integrado requiere una licencia de pago según la &lt;a href="https://help.gitkraken.com/gitkraken-desktop/branching-and-merging/" rel="noopener noreferrer"&gt;documentación&lt;/a&gt;. También puedes editar el archivo con tu editor habitual, como acabamos de hacer.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Marca la resolución y concluye
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add horario.txt
git diff &lt;span class="nt"&gt;--cached&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"merge: unificar horario ampliado y zona horaria"&lt;/span&gt;
git status
git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antes del commit, confirma que el diff contiene la frase final y ningún marcador. Al terminar, espera un directorio limpio y un commit de merge con dos padres.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolver también implica validar
&lt;/h2&gt;

&lt;p&gt;En este archivo basta leer la oración final. En código, una integración puede quedar sin marcadores y seguir siendo incorrecta: ejecuta las pruebas correspondientes y revisa el comportamiento afectado.&lt;/p&gt;

&lt;p&gt;Si necesitas cancelar antes de concluir este laboratorio, puedes usar &lt;code&gt;git merge --abort&lt;/code&gt;. El ejercicio comenzó la integración con el directorio limpio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reto:&lt;/strong&gt; repite la práctica con dos precios diferentes. Esta vez, explica qué información de negocio necesitas antes de escoger un valor.&lt;/p&gt;

&lt;p&gt;¿Qué conflicto te ha exigido hablar con otra persona para entender la intención del cambio?&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%2F92d6asa4jaiqot8o7428.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%2F92d6asa4jaiqot8o7428.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>gitkraken</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Guardabosque: an offline nature guide for Tabasco's jungles, powered by Gemma running on my laptop</title>
      <dc:creator>Jesus Manuel Garcia Torres</dc:creator>
      <pubDate>Sun, 11 Oct 2026 19:29:59 +0000</pubDate>
      <link>https://dev.to/gartox/guardabosque-an-offline-nature-guide-for-tabascos-jungles-powered-by-gemma-running-on-my-laptop-4nc6</link>
      <guid>https://dev.to/gartox/guardabosque-an-offline-nature-guide-for-tabascos-jungles-powered-by-gemma-running-on-my-laptop-4nc6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I live in Villahermosa, Tabasco, in southeastern Mexico. We're surrounded by wetlands, rivers, jungle reserves and city parks full of toucans, iguanas, herons and plants most of us can't name. But the moment you walk into Pantanos de Centla or the trails of Agua Blanca, your phone signal disappears, and so does every "identify this plant" app that depends on the cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guardabosque&lt;/strong&gt; ("forest ranger" in Spanish) is a nature guide that works with &lt;strong&gt;no internet at all&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your laptop runs &lt;strong&gt;Gemma 4&lt;/strong&gt; locally through &lt;strong&gt;Ollama&lt;/strong&gt; and serves a small web app.&lt;/li&gt;
&lt;li&gt;Your phone connects to the laptop over a local hotspot (no data plan needed) and opens the app.&lt;/li&gt;
&lt;li&gt;You snap a photo of a bird, plant or insect. Gemma identifies it, tells you what else to look for nearby, warns you if it's dangerous (snakes, stinging plants, crocodiles: this is Tabasco), and gives you a &lt;strong&gt;5–10 minute field quest&lt;/strong&gt; so you keep walking instead of scrolling.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Mission&lt;/strong&gt; tab generates a short walk for real Tabasco places (Parque Tomás Garrido, La Venta, Yumká, Pantanos de Centla, Agua Blanca, Kolem Jaa', Grutas de Coconá, or just your neighborhood park), with steps you check off as you go.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;field journal&lt;/strong&gt; saves what you found, with a thumbnail, on your own machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The design goal was simple: &lt;strong&gt;the screen should be the shortest part of the experience.&lt;/strong&gt; Take a photo, read for ten seconds, put the phone away.&lt;/p&gt;

&lt;p&gt;It's for families, students and anyone in the southeast who wants to know what they're looking at, especially the kids in our &lt;a href="https://steamher.xpectrelabs.com/" rel="noopener noreferrer"&gt;STEAM-HER&lt;/a&gt; community who are starting to explore science and tech.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmzj0558zizz2qt2jwp42.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%2Fmzj0558zizz2qt2jwp42.png" alt="Guardabosque identifying palm fruit from a ranch in Cárdenas, Tabasco" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last week I was at a ranch in &lt;strong&gt;Cárdenas, Tabasco&lt;/strong&gt; and photographed a pile of green fruit that had fallen under the trees. Nobody around was sure what it was. Back home, I ran that photo through Guardabosque. Gemma recognized it as &lt;strong&gt;tropical palm fruit&lt;/strong&gt;, described the oblong, rough-skinned fruit and the jungle setting, and added the one safety note that actually matters: &lt;em&gt;don't eat it without an expert identification, because some tropical plants are toxic&lt;/em&gt;. Then it gave me a field quest: walk slowly along the trail and look at the mosses and ferns growing at the base of the trees.&lt;/p&gt;

&lt;p&gt;That last part is the whole point. Instead of "here's a Wikipedia summary, keep scrolling", it sends you back out to look at something else.&lt;/p&gt;

&lt;p&gt;Here's a real response from Gemma 4 running on my M1 Pro, for a photo of a keel-billed toucan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bird"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commonName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tucán"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scientificName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ramphastos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Es un ave grande con un cuerpo predominantemente negro. Su característica más notable es su pico grande y colorido, que presenta tonos verdes, rojos y amarillos."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lookFor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Busca aves con picos grandes y coloridos."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Observar el comportamiento de alimentación en el dosel del bosque."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Escuchar sus llamadas distintivas."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"safety"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fieldQuest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Camina lentamente por los senderos permitidos, observando la diversidad de aves en el dosel del bosque durante diez minutos."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Each photo takes about &lt;strong&gt;30 seconds&lt;/strong&gt; on a 16 GB M1 Pro once the model is warm. On a trail, that's fine: you take the photo and keep looking around while it thinks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/gartox" rel="noopener noreferrer"&gt;
        gartox
      &lt;/a&gt; / &lt;a href="https://github.com/gartox/guardabosque" rel="noopener noreferrer"&gt;
        guardabosque
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Offline nature guide for Tabasco, Mexico: identify plants and wildlife from a photo with Gemma running locally via Ollama. No internet needed.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Guardabosque&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Guardabosque is an offline nature guide for people exploring Tabasco, Mexico. It uses an open-weight Gemma model running locally through Ollama to identify a plant, bird, insect, or other organism from a phone photo, suggest what to observe, and create a short field mission that gets people walking again.&lt;/p&gt;

&lt;p&gt;It is designed for parks, wetlands, forests, and neighborhoods where mobile coverage is unreliable. Photos stay on the local device and laptop, there is no service cost, and the model can be changed with &lt;code&gt;GUARDABOSQUE_MODEL&lt;/code&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Requirements&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 24 or newer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ollama.com/" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt; running on the laptop&lt;/li&gt;
&lt;li&gt;A Gemma model available locally&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run it&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;ollama pull gemma4:e4b-it-qat
npm start&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open the printed network URL from a phone connected to the same Wi-Fi network or to a hotspot hosted by the phone or laptop. The laptop runs the server and Ollama; the phone is the field interface.&lt;/p&gt;
&lt;p&gt;Useful configuration defaults are:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;PORT=3000&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/gartox/guardabosque" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; &lt;a href="https://ollama.com/library/gemma4" rel="noopener noreferrer"&gt;Gemma 4 E4B&lt;/a&gt; (&lt;code&gt;gemma4:e4b-it-qat&lt;/code&gt;), Google's open-weight multimodal model, in its quantization-aware 6.1 GB build. It reads the photo and answers in Spanish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local inference:&lt;/strong&gt; &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; on the laptop. Nothing leaves the device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App:&lt;/strong&gt; Node.js with &lt;strong&gt;zero dependencies&lt;/strong&gt;. Just &lt;code&gt;node:http&lt;/code&gt;, &lt;code&gt;node:fs&lt;/code&gt; and &lt;code&gt;node:test&lt;/code&gt;. If the whole point is that it works without internet, I didn't want a &lt;code&gt;npm install&lt;/code&gt; step that needs it. The frontend is plain HTML, CSS and JavaScript, installable as a PWA, mobile first, with high contrast so you can read it in full sun.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety by design:&lt;/strong&gt; the model's output is treated as untrusted data. The server validates and normalizes every field (unknown values fall back to safe defaults, long text is trimmed), the UI never renders model text as HTML, and the prompt tells Gemma to ignore any instructions written inside a photo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests:&lt;/strong&gt; 12 tests with a fake Ollama server, covering broken JSON from the model, adversarial output with HTML and extra fields, oversized uploads, path traversal and Ollama being offline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Two things I learned the hard way
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Structured output isn't guaranteed.&lt;/strong&gt; My first version asked Ollama to enforce a JSON schema with the &lt;code&gt;format&lt;/code&gt; parameter. On my Homebrew build, the MLX engine returned &lt;code&gt;501 Not Implemented&lt;/code&gt; for structured output, and a different build silently ignored the schema and answered in prose. The fix: always describe the schema in the prompt too, extract the JSON even if it's wrapped in markdown, retry once, and remember when a server doesn't support &lt;code&gt;format&lt;/code&gt; so you don't pay for the 501 on every request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Pick the model size for the machine you have.&lt;/strong&gt; The default &lt;code&gt;gemma4:e4b&lt;/code&gt; tag pulled a 9.5 GB build, and with Docker running my 16 GB Mac hit 30 GB of swap; a single photo took more than three minutes. Switching to the 6.1 GB QAT build brought it down to about 30 seconds with similar answers. With open weights you choose the trade-off yourself instead of taking whatever a vendor serves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works where we actually go outside.&lt;/strong&gt; Pantanos de Centla, Kolem Jaa' and the Sierra de Tabasco have little or no signal. A cloud API is useless there; a model on my laptop works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Photos stay with you.&lt;/strong&gt; Pictures of your kids at the park, your backyard or a protected area never go to someone else's server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; No API key and no per-photo bill, so a school, a community group or a park ranger can use it all day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can change the model.&lt;/strong&gt; One environment variable (&lt;code&gt;GUARDABOSQUE_MODEL&lt;/code&gt;) swaps Gemma for any other model in Ollama. When I needed a lighter build for my laptop, I switched in minutes. With a closed API I'd have been stuck with whatever was offered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anyone can adapt it.&lt;/strong&gt; It's MIT licensed with no dependencies. Someone in Chiapas or the Yucatán can fork it, swap in their own places and run it tomorrow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 4 E4B, running fully locally and multimodal, is the core of the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built by &lt;a href="https://dev.to/gartox"&gt;@gartox&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Git worktrees con GitKraken: atiende un hotfix sin perder tu contexto</title>
      <dc:creator>Jesus Manuel Garcia Torres</dc:creator>
      <pubDate>Mon, 28 Sep 2026 17:53:24 +0000</pubDate>
      <link>https://dev.to/gartox/git-worktrees-con-gitkraken-atiende-un-hotfix-sin-perder-tu-contexto-4agi</link>
      <guid>https://dev.to/gartox/git-worktrees-con-gitkraken-atiende-un-hotfix-sin-perder-tu-contexto-4agi</guid>
      <description>&lt;p&gt;Estás a mitad de una funcionalidad y llega el mensaje: “Necesitamos corregir esto cuanto antes”.&lt;/p&gt;

&lt;p&gt;Tienes archivos modificados, ideas a medias y una rama que todavía no está lista. Necesitas atender la urgencia sin desacomodar lo que estabas haciendo.&lt;/p&gt;

&lt;p&gt;Aquí es donde &lt;strong&gt;Git worktrees&lt;/strong&gt; resulta útil: puedes abrir otra rama del mismo repositorio en una carpeta diferente y trabajar en ambas sin cambiar continuamente de rama en un único directorio.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Esta guía forma parte de mi serie sobre Git y GitKraken para la comunidad de habla hispana.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En el &lt;a href="https://dev.to/gartox/git-con-contexto-tu-primer-flujo-de-ramas-y-commits-en-gitkraken-1ja8"&gt;primer artículo practicamos ramas, staging y commits&lt;/a&gt;. Ahora vamos a usar esos conceptos para resolver una interrupción cotidiana.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es un worktree?
&lt;/h2&gt;

&lt;p&gt;Un worktree es un directorio de trabajo vinculado a tu repositorio.&lt;/p&gt;

&lt;p&gt;Cada worktree tiene sus propios archivos, área de staging y &lt;code&gt;HEAD&lt;/code&gt;, que identifica el commit o la rama en la que estás trabajando. Los worktrees comparten los objetos de Git y las referencias de las ramas.&lt;/p&gt;

&lt;p&gt;Puedes organizarte así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;carpeta-de-practicas/
├── taller-git-worktrees/   → feature/nuevo-menu
└── taller-git-hotfix/      → hotfix/contacto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compartir repositorio no significa que los cambios se integren automáticamente entre ramas.&lt;/strong&gt; Un commit creado en el hotfix estará disponible en el historial compartido, pero la otra rama conservará su versión de los archivos hasta que incorpores ese cambio.&lt;/p&gt;

&lt;p&gt;Puedes consultar este modelo en la &lt;a href="https://git-scm.com/docs/git-worktree" rel="noopener noreferrer"&gt;documentación oficial de Git worktree&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué vamos a conseguir
&lt;/h2&gt;

&lt;p&gt;Al terminar tendrás:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Una funcionalidad con trabajo pendiente en su propia carpeta.&lt;/li&gt;
&lt;li&gt;Una corrección guardada en otra rama.&lt;/li&gt;
&lt;li&gt;La comprobación de que atender el hotfix no modificó el trabajo pendiente.&lt;/li&gt;
&lt;li&gt;Un procedimiento para retirar el worktree cuando termines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No necesitas instalar dependencias ni levantar una aplicación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de empezar
&lt;/h2&gt;

&lt;p&gt;Necesitas Git, un editor y GitKraken Desktop con soporte para worktrees, disponible desde la versión 10.5 según su &lt;a href="https://help.gitkraken.com/gitkraken-desktop/worktrees/" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Los comandos están preparados para Bash, Zsh o Git Bash en Windows. Usa una carpeta nueva, fuera de tus proyectos habituales.&lt;/p&gt;

&lt;p&gt;Combinaremos la terminal para preparar el laboratorio con GitKraken para revisar los cambios y crear el commit de la corrección.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prepara una funcionalidad a medias
&lt;/h2&gt;

&lt;p&gt;Ejecuta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;taller-git-worktrees
&lt;span class="nb"&gt;cd &lt;/span&gt;taller-git-worktrees

git init &lt;span class="nt"&gt;-b&lt;/span&gt; main
git config user.name &lt;span class="s2"&gt;"Demo Git"&lt;/span&gt;
git config user.email &lt;span class="s2"&gt;"demo@example.com"&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Contacto: soporte pendiente\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; contacto.txt

git add contacto.txt
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"chore: crear portal de ejemplo"&lt;/span&gt;

git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature/nuevo-menu
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Menú en construcción\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; menu.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La identidad de ejemplo queda configurada únicamente para este repositorio.&lt;/p&gt;

&lt;p&gt;Ahora tienes una rama llamada &lt;code&gt;feature/nuevo-menu&lt;/code&gt; y un archivo &lt;code&gt;menu.txt&lt;/code&gt; sin commit. Este archivo representa la funcionalidad que estabas desarrollando cuando llegó la urgencia.&lt;/p&gt;

&lt;p&gt;Abre la carpeta &lt;code&gt;taller-git-worktrees&lt;/code&gt; en GitKraken. Comprueba que la rama activa sea &lt;code&gt;feature/nuevo-menu&lt;/code&gt; y que aparezca el archivo pendiente.&lt;/p&gt;

&lt;p&gt;También puedes verificarlo en la terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--short&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El resultado debe incluir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;?? menu.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los signos &lt;code&gt;??&lt;/code&gt; indican que Git todavía no está siguiendo ese archivo.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Crea un worktree para el hotfix
&lt;/h2&gt;

&lt;p&gt;La urgencia consiste en corregir el contacto de soporte. Queremos partir de &lt;code&gt;main&lt;/code&gt;, sin incluir la funcionalidad incompleta.&lt;/p&gt;

&lt;p&gt;Desde la misma terminal, ejecuta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add &lt;span class="nt"&gt;-b&lt;/span&gt; hotfix/contacto ../taller-git-hotfix main
git worktree list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El primer comando hace tres cosas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crea la rama &lt;code&gt;hotfix/contacto&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Usa &lt;code&gt;main&lt;/code&gt; como punto de partida.&lt;/li&gt;
&lt;li&gt;Prepara los archivos de esa rama en &lt;code&gt;../taller-git-hotfix&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El segundo comando muestra las carpetas vinculadas y la rama activa en cada una.&lt;/p&gt;

&lt;p&gt;Tu archivo &lt;code&gt;menu.txt&lt;/code&gt; sigue pendiente en la carpeta original. No necesitamos guardarlo en un commit ni retirarlo para comenzar la corrección.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿También se puede crear desde GitKraken?
&lt;/h3&gt;

&lt;p&gt;Sí. Para hacerlo visualmente, prepara una rama que no esté activa en otro worktree, haz clic derecho sobre ella y selecciona &lt;strong&gt;Create worktree&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;En este laboratorio ya lo creamos desde la terminal. Ahora solo ábrelo desde el panel izquierdo de GitKraken, usando &lt;strong&gt;Open this worktree&lt;/strong&gt; o &lt;strong&gt;Open worktree in a new tab&lt;/strong&gt;. El &lt;a href="https://help.gitkraken.com/gitkraken-desktop/worktrees/" rel="noopener noreferrer"&gt;procedimiento visual está documentado aquí&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Corrige desde la carpeta del hotfix
&lt;/h2&gt;

&lt;p&gt;En la terminal, entra al nuevo directorio y comprueba la rama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../taller-git-hotfix
git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El resultado debe ser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hotfix/contacto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actualiza el archivo y revisa la diferencia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Contacto: soporte@example.com\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; contacto.txt
git diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El cambio esperado es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-Contacto: soporte pendiente
&lt;/span&gt;&lt;span class="gi"&gt;+Contacto: soporte@example.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En GitKraken:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Abre el worktree del hotfix.&lt;/li&gt;
&lt;li&gt;Confirma que estás en &lt;code&gt;hotfix/contacto&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Revisa el diff de &lt;code&gt;contacto.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pasa únicamente ese archivo a staging.&lt;/li&gt;
&lt;li&gt;Crea el commit con este mensaje:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix: corregir contacto de soporte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El correo utilizado es ficticio.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Regresa y comprueba que conservaste tu contexto
&lt;/h2&gt;

&lt;p&gt;Vuelve al directorio original:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../taller-git-worktrees

git status &lt;span class="nt"&gt;--short&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;menu.txt
&lt;span class="nb"&gt;cat &lt;/span&gt;contacto.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Debes encontrar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;menu.txt&lt;/code&gt; todavía sin commit.&lt;/li&gt;
&lt;li&gt;El texto &lt;code&gt;Menú en construcción&lt;/code&gt; intacto.&lt;/li&gt;
&lt;li&gt;El contacto original: &lt;code&gt;Contacto: soporte pendiente&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este último punto es importante: &lt;strong&gt;la corrección quedó guardada en &lt;code&gt;hotfix/contacto&lt;/code&gt;, pero todavía no está integrada en &lt;code&gt;feature/nuevo-menu&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Los worktrees nos permitieron trabajar en dos contextos independientes. La integración entre ramas sigue siendo una decisión explícita.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Termina el laboratorio y retira el worktree
&lt;/h2&gt;

&lt;p&gt;Para cerrar la práctica, guardaremos el borrador del menú en su rama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add menu.txt
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: agregar borrador del menú"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora integra la corrección en &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
git merge &lt;span class="nt"&gt;--ff-only&lt;/span&gt; hotfix/contacto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este laboratorio &lt;code&gt;main&lt;/code&gt; no recibió otros commits, así que puede avanzar directamente hasta el commit del hotfix.&lt;/p&gt;

&lt;p&gt;Antes de retirar la carpeta, comprueba que no tenga cambios pendientes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; ../taller-git-hotfix status &lt;span class="nt"&gt;--short&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si no muestra nada, puedes continuar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree remove ../taller-git-hotfix
git branch &lt;span class="nt"&gt;-d&lt;/span&gt; hotfix/contacto
git worktree list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git worktree remove&lt;/code&gt; retira el directorio de trabajo vinculado. La eliminación de la rama es una operación separada, que hacemos después de integrarla.&lt;/p&gt;

&lt;p&gt;Al terminar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; contiene el contacto corregido.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;feature/nuevo-menu&lt;/code&gt; conserva el commit del menú.&lt;/li&gt;
&lt;li&gt;La carpeta temporal del hotfix ya no es necesaria.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En un repositorio compartido, publica la rama y sigue el proceso de pull request, revisión y validación del equipo antes de dar por integrada la corrección.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que debes considerar en una aplicación real
&lt;/h2&gt;

&lt;p&gt;Un worktree separa los archivos de trabajo, pero no configura automáticamente un entorno completo e independiente.&lt;/p&gt;

&lt;p&gt;Si ejecutas dos versiones de tu aplicación, revisa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dependencias:&lt;/strong&gt; cada carpeta puede necesitar su propia instalación.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variables locales:&lt;/strong&gt; los archivos ignorados, como &lt;code&gt;.env&lt;/code&gt;, no se copian automáticamente desde la otra carpeta.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Puertos:&lt;/strong&gt; dos servidores no pueden escuchar simultáneamente en la misma dirección y puerto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bases de datos y servicios:&lt;/strong&gt; ambas instancias podrían conectarse al mismo recurso externo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Además, Git normalmente impide activar una misma rama en dos worktrees. Utiliza una rama distinta por tarea.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cuándo conviene usar este flujo?
&lt;/h2&gt;

&lt;p&gt;Es especialmente útil cuando necesitas mantener dos contextos disponibles durante un tiempo: atender un hotfix, revisar otra rama o comparar implementaciones.&lt;/p&gt;

&lt;p&gt;Para una pausa breve, &lt;code&gt;git stash&lt;/code&gt; también puede ser suficiente. La ventaja del worktree aparece cuando quieres conservar las carpetas abiertas y moverte entre ellas sin reemplazar continuamente sus archivos.&lt;/p&gt;

&lt;p&gt;GitKraken te permite seguir visualmente qué rama estás revisando y qué cambios estás guardando. El hábito sigue siendo el mismo: &lt;strong&gt;confirma carpeta, rama y diff antes de cada commit.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reto para practicar
&lt;/h2&gt;

&lt;p&gt;Desde &lt;code&gt;main&lt;/code&gt;, crea otra rama con su propio worktree para escribir documentación.&lt;/p&gt;

&lt;p&gt;Agrega un archivo sin hacer commit y comprueba que:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Aparece como pendiente en el nuevo worktree.&lt;/li&gt;
&lt;li&gt;No aparece en el directorio original.&lt;/li&gt;
&lt;li&gt;Puedes identificar ambas carpetas con &lt;code&gt;git worktree list&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Después guarda el cambio y decide si quieres integrarlo o conservarlo en su rama antes de retirar el worktree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Para qué usarías primero este flujo: atender un hotfix, revisar un pull request o probar una idea?&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%2Flccrhyvrlyjwc9svo2ls.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%2Flccrhyvrlyjwc9svo2ls.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>gitkraken</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Git con contexto: tu primer flujo de ramas y commits en GitKraken</title>
      <dc:creator>Jesus Manuel Garcia Torres</dc:creator>
      <pubDate>Mon, 21 Sep 2026 18:38:28 +0000</pubDate>
      <link>https://dev.to/gartox/git-con-contexto-tu-primer-flujo-de-ramas-y-commits-en-gitkraken-1ja8</link>
      <guid>https://dev.to/gartox/git-con-contexto-tu-primer-flujo-de-ramas-y-commits-en-gitkraken-1ja8</guid>
      <description>&lt;h1&gt;
  
  
  Git con contexto: tu primer flujo de ramas y commits en GitKraken
&lt;/h1&gt;

&lt;p&gt;Etiquetas sugeridas: &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;gitkraken&lt;/code&gt;, &lt;code&gt;tutorial&lt;/code&gt;, &lt;code&gt;beginners&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Soy ambassador de GitKraken. En esta guía comparto un ejercicio práctico para aprender Git con apoyo visual y entender qué hace cada operación.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Terminas un cambio, haces commit y descubres que también agregaste un archivo que no tenía relación. El problema empezó antes del commit: faltó revisar qué iba a entrar en él.&lt;/p&gt;

&lt;p&gt;Vamos a construir un flujo pequeño: crear una rama, modificar un archivo, revisar el diff y registrar un cambio con intención. Usaremos GitKraken Desktop para observar el historial y una terminal para preparar un laboratorio reproducible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de empezar
&lt;/h2&gt;

&lt;p&gt;Necesitas Git, GitKraken Desktop y un editor de texto. Los comandos funcionan en Bash, Zsh o Git Bash en Windows. Ejecuta el ejercicio en una carpeta nueva, fuera de tus proyectos. No necesitas un remoto.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prepara el repositorio
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;taller-git-ramas
&lt;span class="nb"&gt;cd &lt;/span&gt;taller-git-ramas
git init &lt;span class="nt"&gt;-b&lt;/span&gt; main
git config user.name &lt;span class="s2"&gt;"Demo Git"&lt;/span&gt;
git config user.email &lt;span class="s2"&gt;"demo@example.com"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'# Portal demo\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; README.md
git add README.md
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"docs: crear descripción inicial"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La identidad queda configurada únicamente en este repositorio. El primer commit será el punto de partida.&lt;/p&gt;

&lt;p&gt;Abre la carpeta desde la opción de abrir un repositorio existente de GitKraken Desktop. Localiza el commit y la etiqueta &lt;code&gt;main&lt;/code&gt; en el gráfico.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Crea una rama para una sola tarea
&lt;/h2&gt;

&lt;p&gt;La tarea será documentar cómo iniciar el portal.&lt;/p&gt;

&lt;p&gt;En GitKraken, haz clic derecho sobre el commit de &lt;code&gt;main&lt;/code&gt;, selecciona &lt;strong&gt;Create branch here&lt;/strong&gt; y escribe &lt;code&gt;docs/inicio-local&lt;/code&gt;. Después haz doble clic en la rama para activarla. Este procedimiento está documentado en la &lt;a href="https://help.gitkraken.com/gitkraken-desktop/branching-and-merging/" rel="noopener noreferrer"&gt;guía oficial de ramas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Antes de editar, comprueba que la rama activa sea &lt;code&gt;docs/inicio-local&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Una rama nombra una línea de trabajo. Crear la rama todavía no produce cambios en los archivos ni un commit nuevo.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Haz un cambio fácil de revisar
&lt;/h2&gt;

&lt;p&gt;En tu editor, reemplaza el contenido de &lt;code&gt;README.md&lt;/code&gt; por:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Portal demo&lt;/span&gt;

&lt;span class="gu"&gt;## Inicio local&lt;/span&gt;

Este repositorio es un laboratorio para practicar Git.
No requiere instalar dependencias ni levantar un servidor.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guarda el archivo. Regresa a GitKraken y revisa el diff del cambio pendiente. Debe mostrar únicamente las líneas que acabas de agregar.&lt;/p&gt;

&lt;p&gt;Pregúntate: ¿este cambio resuelve una sola tarea?, ¿el texto es correcto?, ¿hay algo accidental?&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Prepara y crea el commit
&lt;/h2&gt;

&lt;p&gt;Pasa &lt;code&gt;README.md&lt;/code&gt; al área de staging y escribe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docs: explicar el inicio del laboratorio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crea el commit. El staging funciona como la selección de contenido que entrará en la siguiente versión: no tienes que incluir todo lo modificado.&lt;/p&gt;

&lt;p&gt;Puedes verificar el resultado desde la terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
git show &lt;span class="nt"&gt;--stat&lt;/span&gt; HEAD
git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Espera un directorio limpio y un commit adicional en &lt;code&gt;docs/inicio-local&lt;/code&gt;. La rama &lt;code&gt;main&lt;/code&gt; debe seguir en el commit inicial.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Integra el resultado
&lt;/h2&gt;

&lt;p&gt;Como este laboratorio es local, integra directamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
git merge &lt;span class="nt"&gt;--ff-only&lt;/span&gt; docs/inicio-local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observa el gráfico otra vez. &lt;code&gt;main&lt;/code&gt; avanzó hasta el commit nuevo. No apareció un commit de merge porque pudo hacerse un avance directo, también llamado fast-forward.&lt;/p&gt;

&lt;p&gt;En un equipo, normalmente publicarías la rama y abrirías un pull request antes de integrarla, según las reglas del repositorio.&lt;/p&gt;

&lt;h2&gt;
  
  
  El hábito que vale la pena conservar
&lt;/h2&gt;

&lt;p&gt;Antes de cada commit, revisa rama, diff y staging. Es una pausa pequeña que permite explicar qué estás guardando y por qué.&lt;/p&gt;

&lt;p&gt;GitKraken aporta una vista del historial; el criterio para decidir qué pertenece a un commit sigue siendo tuyo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reto:&lt;/strong&gt; crea &lt;code&gt;docs/objetivo&lt;/code&gt;, agrega una sección con el objetivo del laboratorio y repite el flujo. Antes de integrar, explica en voz alta qué cambió y qué rama lo contiene.&lt;/p&gt;

&lt;p&gt;¿Qué te cuesta más al empezar con Git: las ramas, el staging o los mensajes de commit?&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%2F6bygblk770jdi58xid06.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%2F6bygblk770jdi58xid06.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>git</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Stop Reaching for the Edge Runtime</title>
      <dc:creator>Jesus Manuel Garcia Torres</dc:creator>
      <pubDate>Sat, 29 Aug 2026 03:31:10 +0000</pubDate>
      <link>https://dev.to/gartox/stop-reaching-for-the-edge-runtime-4edg</link>
      <guid>https://dev.to/gartox/stop-reaching-for-the-edge-runtime-4edg</guid>
      <description>&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%2Ffhubwmy061gree1sfrny.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%2Ffhubwmy061gree1sfrny.png" alt="Stop reaching for the Edge Runtime — Fluid Compute is the default on Vercel now" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to show you a line of code. You almost certainly have it somewhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;edge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go look. I'll wait.&lt;/p&gt;

&lt;p&gt;Every time I open someone's Next.js repo, it's there. Usually on a streaming route, sometimes stamped across an entire API folder. And when I ask who put it there, nobody knows. The answer is always some version of &lt;em&gt;"it's faster, right?"&lt;/em&gt; or &lt;em&gt;"we needed it for streaming."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the thing: you weren't wrong. In 2022 that was a solid call. But the ground moved under that line, and now it's quietly costing you — Node APIs you can't reach for, libraries that explode at build time, and that one afternoon you lost to &lt;em&gt;"wait, why isn't &lt;code&gt;crypto&lt;/code&gt; here?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So let me walk you through what changed. Not the changelog version — the version that actually rewires how you think about your backend.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, let's be fair to Edge
&lt;/h2&gt;

&lt;p&gt;I'm not here to tell you past-you was an idiot. Edge Runtime solved two genuinely painful things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One request, one instance.&lt;/strong&gt; Every concurrent request needed its own container. Traffic spike? Enjoy your wall of cold starts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You paid for waiting.&lt;/strong&gt; Billing was GB-seconds of wall clock. That 900ms your function spent staring at Postgres doing nothing? You paid for it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Edge dodged both by running a stripped-down V8 isolate instead of a real Node process. Isolates boot in single-digit milliseconds. Beautiful.&lt;/p&gt;

&lt;p&gt;The price was Node itself. No &lt;code&gt;fs&lt;/code&gt;. No native modules. Half a &lt;code&gt;crypto&lt;/code&gt;. A long, miserable tail of npm packages that just don't run.&lt;/p&gt;

&lt;p&gt;You took that trade. It was often worth it.&lt;/p&gt;

&lt;p&gt;Then Vercel went and fixed the actual problem instead of routing around it. That fix is &lt;strong&gt;Fluid Compute&lt;/strong&gt;, and it's the default now.&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea that made it click for me
&lt;/h2&gt;

&lt;p&gt;Sit with this one for a second, because everything else falls out of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your backend spends most of its life waiting.&lt;/strong&gt; Waiting on Postgres. Waiting on Stripe. Waiting four full seconds while an LLM dribbles out tokens one at a time. And during every millisecond of that, the CPU you're paying for is doing absolutely nothing.&lt;/p&gt;

&lt;p&gt;Classic serverless shrugs at that. One request owns one instance, start to finish, idle or not.&lt;/p&gt;

&lt;p&gt;Fluid looks at the same idle CPU and sees &lt;strong&gt;inventory&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%2F5htmsu13x583ok8dz2li.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%2F5htmsu13x583ok8dz2li.png" alt="Figure 1 — Classic serverless runs one request per instance and pays for idle CPU. Fluid Compute reuses warm instances across concurrent requests, filling the I/O wait." width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Fluid instance takes &lt;strong&gt;multiple concurrent requests&lt;/strong&gt;. Request 2 shows up while request 1 is blocked on I/O, lands on that same warm instance, and burns CPU that you were throwing in the bin.&lt;/p&gt;

&lt;p&gt;Two things follow immediately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your cold starts mostly go away.&lt;/strong&gt; Instances get reused instead of provisioned per request. You get the thing you wanted from Edge — without handing back Node to get it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your in-memory state survives.&lt;/strong&gt; Database pool, cached JWKS, compiled regexes — they persist now, like on a normal server. Flip side, and I'd genuinely watch for this one: module-scope mutable state is now &lt;em&gt;actually shared&lt;/em&gt; between concurrent requests. Treat it the way you'd treat state on any long-lived server, because that's what you have now.&lt;/p&gt;

&lt;p&gt;If you take one sentence out of this post, take this one: &lt;strong&gt;it's a concurrency model change, not a speed trick.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  "But we need Edge for streaming"
&lt;/h2&gt;

&lt;p&gt;This is the one I hear most, and it's the expensive one — because it's exactly why Edge ends up on the routes that need Node the most.&lt;/p&gt;

&lt;p&gt;Streaming was never an Edge thing. &lt;code&gt;ReadableStream&lt;/code&gt;, Server-Sent Events, AI token streaming — all of it runs on the default Node.js runtime. Zero config. Watch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/chat/route.ts — no `runtime` export. Node.js. Streams beautifully.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;streamText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;messages&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;streamText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anthropic/claude-sonnet-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// via AI Gateway&lt;/span&gt;
    &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUIMessageStreamResponse&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;Prefer it raw, no framework? Same story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReadableStream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &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;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getChunks&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keep-alive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now flip it around. Your AI route is the &lt;strong&gt;worst&lt;/strong&gt; thing you could put on Edge, not the best. It runs long. It's almost pure I/O wait. It wants SDKs, tracing, a database driver — the whole Node ecosystem.&lt;/p&gt;

&lt;p&gt;That's not an edge case for Fluid. That's the exact shape it was built for.&lt;/p&gt;




&lt;h2&gt;
  
  
  "Edge is cheaper though"
&lt;/h2&gt;

&lt;p&gt;Not anymore, and this is the part I think most people missed: Fluid changed the &lt;strong&gt;billing model&lt;/strong&gt;, not just the runtime.&lt;/p&gt;

&lt;p&gt;Vercel Functions now bill on &lt;strong&gt;Active CPU time + provisioned memory + invocations&lt;/strong&gt;. Not GB-seconds of wall clock.&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%2F3lc19xor7bx81r3df3qi.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%2F3lc19xor7bx81r3df3qi.png" alt="Figure 2 — A 2-second request that mostly waits on an LLM and a database only bills for the slices where the CPU is actually working." width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your function is CPU-bound, this barely moves. If it's I/O-bound — so, most of your functions, and &lt;em&gt;every single&lt;/em&gt; AI function you've ever written — the gap between wall clock and active CPU is huge. And you've stopped paying for it.&lt;/p&gt;

&lt;p&gt;Then instance reuse compounds it. Three concurrent requests on one warm instance is one block of provisioned memory, not three.&lt;/p&gt;




&lt;h2&gt;
  
  
  "Middleware has to be Edge"
&lt;/h2&gt;

&lt;p&gt;Two things to untangle here, and they trip people up separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next.js middleware runs full Node.js now.&lt;/strong&gt; That edge-only constraint is gone. Middleware and Edge Functions are both Vercel Functions under the hood these days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Routing Middleware is a different product.&lt;/strong&gt; Framework-agnostic, runs before the cache, works with SvelteKit, Nuxt, Astro, or a pile of static files. It is &lt;em&gt;not&lt;/em&gt; Next.js middleware. Keep them separate in your head when you're reading docs, it'll save you an hour.&lt;/p&gt;

&lt;p&gt;And while I have you: &lt;strong&gt;ISR isn't Next-only either.&lt;/strong&gt; SvelteKit, Nuxt, and Astro all get it on Vercel.&lt;/p&gt;




&lt;h2&gt;
  
  
  The limits you're still designing around
&lt;/h2&gt;

&lt;p&gt;This is my favorite part, because I keep finding architecture decisions that are load-bearing on constraints that stopped existing.&lt;/p&gt;

&lt;p&gt;If any of these shaped a design of yours, go reopen it.&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%2Fm51xb23vsyezti0edc10.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%2Fm51xb23vsyezti0edc10.png" alt="Figure 3 — 300s max duration on every plan, 5 GB package size, 100 MB request bodies, Node 24 LTS, WebSockets, plus Bun and Rust runtimes." width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two of them trigger a redesign more often than the rest:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5 GB package size.&lt;/strong&gt; Up from 250 MB. That's a 20× jump, and it means Playwright, Puppeteer, Python data and ML libraries, heavy image processing. If you've got a "we'll need a separate container for this" conversation on the calendar, you might be able to cancel it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;100 MB request bodies.&lt;/strong&gt; Up from 4.5 MB. Uploads, fat webhook payloads, document intake — straight into a Function. You can stop doing the signed-URL dance you only ever did to dodge a body limit.&lt;/p&gt;

&lt;p&gt;And WebSockets work now. So if your reflex is "we'll need Pusher for this one feature," give it a second look first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;experimental_upgradeWebSocket&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vercel/functions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;experimental_upgradeWebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`echo: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Needs Fluid Compute — which, again, you already have. Plain &lt;code&gt;ws&lt;/code&gt; and Socket.IO work too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Do this Monday morning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Find your Edge exports.&lt;/strong&gt; Run this right now, seriously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"runtime = ['&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]edge['&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.ts"&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.tsx"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For every hit, ask yourself one question: what is this buying me? If the answer is "streaming" or "it felt faster" — delete the line. Test it, obviously. But expect it to just work, with more available to it than it had before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Check your Node version.&lt;/strong&gt; Node 24 is the LTS default. Node 18 is deprecated. If you're pinned to 18, that's a ticket, write it now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Go find your workarounds.&lt;/strong&gt; Anything you built to dodge a 4.5 MB body, a 250 MB bundle, or a 60-second timeout deserves a fresh look.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Try &lt;code&gt;vercel.ts&lt;/code&gt; instead of &lt;code&gt;vercel.json&lt;/code&gt;.&lt;/strong&gt; Typed config, real logic, env access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vercel.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;VercelConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vercel/config/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VercelConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;framework&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nextjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cacheControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/static/(.*)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1 week&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;immutable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;crons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/cleanup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 0 * * *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When I'd still use Edge
&lt;/h2&gt;

&lt;p&gt;I'm not telling you it's dead. I still reach for it on genuinely trivial, latency-critical work right at the boundary: a header rewrite, a geo redirect, an A/B bucket assignment, a feature flag lookup. Small, CPU-light, no Node dependencies, runs before anything else touches the request.&lt;/p&gt;

&lt;p&gt;That's a narrow band, though. Anything that touches a database, an SDK, a file, or a model? That's Fluid.&lt;/p&gt;

&lt;p&gt;Here's the heuristic I'd give you: &lt;strong&gt;Edge is a specialist tool for the request boundary. It is not the default for your application code.&lt;/strong&gt; Somewhere along the way we all flipped that around, and a lot of us are still carrying the flip.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bit I actually want you to remember
&lt;/h2&gt;

&lt;p&gt;Fluid Compute isn't a Vercel feature to file away. It's a category shift.&lt;/p&gt;

&lt;p&gt;We spent years accepting that serverless &lt;em&gt;meant&lt;/em&gt; one-request-per-instance, cold starts, and paying for idle time. Then we built elaborate, clever workarounds on top of those assumptions — and got good at maintaining them.&lt;/p&gt;

&lt;p&gt;The assumptions changed. The workarounds didn't. A lot of what you're maintaining right now is complexity you're paying for out of habit.&lt;/p&gt;

&lt;p&gt;Go delete a line of code. Tell me what breaks.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by Jesús García at &lt;a href="https://xpectrelabs.com" rel="noopener noreferrer"&gt;XpectreLabs&lt;/a&gt;. Run that &lt;code&gt;grep&lt;/code&gt; and drop what you find in the comments — I'm genuinely curious how many of you have that line sitting on an AI route.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>serverless</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
