<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Brian's Notes]]></title><description><![CDATA[Construyo cosas. Luego escribo sobre lo que funcionó.

Soy Brian Mena. Ingeniero informático, solopreneur y padre. Trabajo con código, datos y agentes de IA para construir productos digitales rentables desde cero.

No tengo equipo. No tengo inversores.]]></description><link>https://newsletter.brianmenagomez.com</link><image><url>https://substackcdn.com/image/fetch/$s_!7nbD!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb9dcf7f-ea19-48c6-9eb8-91e45dd4b8eb_1280x1280.png</url><title>Brian&apos;s Notes</title><link>https://newsletter.brianmenagomez.com</link></image><generator>Substack</generator><lastBuildDate>Mon, 19 Oct 2026 11:27:28 GMT</lastBuildDate><atom:link href="https://newsletter.brianmenagomez.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Brian Mena Gómez]]></copyright><language><![CDATA[es]]></language><webMaster><![CDATA[contacto@brianmenagomez.com]]></webMaster><itunes:owner><itunes:email><![CDATA[contacto@brianmenagomez.com]]></itunes:email><itunes:name><![CDATA[Brian Mena Gómez]]></itunes:name></itunes:owner><itunes:author><![CDATA[Brian Mena Gómez]]></itunes:author><googleplay:owner><![CDATA[contacto@brianmenagomez.com]]></googleplay:owner><googleplay:email><![CDATA[contacto@brianmenagomez.com]]></googleplay:email><googleplay:author><![CDATA[Brian Mena Gómez]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[describe_conversor(): la función que le cuenta al agente qué sabe hacer la base]]></title><description><![CDATA[A&#241;ad&#237; una RPC que devuelve un JSONB auto-descriptivo con el inventario de funciones y la frescura de los datos, para que un agente descubra solo qu&#233; sabe hacer la base. Antes lo intent&#233; con documen...]]></description><link>https://newsletter.brianmenagomez.com/p/describe_conversor-la-funcion-que</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/describe_conversor-la-funcion-que</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 06 Oct 2026 11:02:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7nbD!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb9dcf7f-ea19-48c6-9eb8-91e45dd4b8eb_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Este ensayo se publico primero en <a href="https://brianmenagomez.com/blog/describe-conversor">brianmenagomez.com/blog/describe-conversor</a>.</p><h2>El agente que entra sin instrucciones</h2><p>El 1 de junio de 2026 dej&#233; el conector MCP vivo en claude.ai. Est&#225; abierto a usuarios gratuitos y a usuarios premium, y desde ese d&#237;a hay una pregunta a la que no he dejado de darle vueltas: &#191;qu&#233; ve un agente que entra aqu&#237; por primera vez?</p><p>Ve una lista de herramientas. Cada una con su nombre y una frase de descripci&#243;n. Y poco m&#225;s.</p><p>Esa lista le sirve para saber qu&#233; puede llamar. No le sirve para saber si merece la pena llamarlo. Son dos cosas distintas y yo las hab&#237;a tratado como una sola.</p><p>Un agente as&#237; no sabe cu&#225;ntas cosas puedo responderle, no sabe cu&#225;les se solapan y, sobre todo, no sabe de cu&#225;ndo son los datos que le voy a devolver. Ese &#250;ltimo punto es el que me preocupaba de verdad. Un agente que tiene que decidir si un dato le sirve necesita saber si es de esta semana o de hace medio a&#241;o. Si no lo sabe, o conf&#237;a de m&#225;s o avisa de menos. Las dos salidas son malas.</p><p>Un dato viejo presentado como actual es una alucinaci&#243;n con papeles. Y no la comete el modelo: la cometo yo, por no decirle de cu&#225;ndo es lo que le sirvo. Al contrario tambi&#233;n falla: un agente que no sabe la fecha tiende a ser cobarde, a dudar de un dato bueno, a responder con un &#171;depende&#187; que no ayuda a nadie.</p><h2>Lo que prob&#233; primero: escribir mejor</h2><p>Mi primer reflejo fue el de siempre: si el agente no sabe qu&#233; hay dentro, se lo cuento. Me puse a documentar. Una lista de las funciones disponibles, con una l&#237;nea por cada una explicando qu&#233; devuelve y qu&#233; necesita. Un documento ordenado, escrito por m&#237;, en el sitio donde yo escribo.</p><p>El razonamiento parec&#237;a s&#243;lido. La documentaci&#243;n es donde un humano aprende a usar un sistema; tarde o temprano un agente pasar&#225; por ah&#237;. Documento una vez y el problema queda resuelto para siempre.</p><p>Se sostiene hasta que lo piensas dos veces.</p><h2>El fallo no estaba en el texto, estaba en el sitio</h2><p>El problema de documentar era doble, y ninguno de los dos ten&#237;a que ver con la calidad de lo que escrib&#237;a.</p><p>Primero: el agente no pasa por donde yo escribo. Un agente no lee mi documentaci&#243;n, llama a herramientas. Si lo que necesita saber no est&#225; al otro lado de una llamada, para &#233;l no existe. Estaba describiendo la casa en una habitaci&#243;n a la que el interesado no tiene llave.</p><p>Segundo: un documento es una copia, y una copia se desincroniza. Cada vez que a&#241;ado una funci&#243;n o cambio lo que devuelve otra, tengo que acordarme de volver a redactar el texto. Al principio te acuerdas. Al cuarto cambio, no. Cada RPC nueva que publicaba era una l&#237;nea m&#225;s que sosten&#237;a a mano: el texto crec&#237;a en la misma proporci&#243;n que mi deuda. Estaba dibujando a mano el mapa de un territorio que se mov&#237;a solo.</p><p>Y quedaba la pregunta que ning&#250;n texto pod&#237;a contestar: &#171;&#191;de cu&#225;ndo son estos datos?&#187;. Eso no lo sabe un documento. Lo sabe la base.</p><h2>La base se describe a s&#237; misma</h2><p>La salida fue dejar de escribir sobre la base y hacer que la base hablara. A&#241;ad&#237; una RPC nueva: `describe_conversor()`.</p><p>`describe_conversor()` no devuelve ep&#237;grafes ni c&#243;digos. Devuelve un JSONB auto-descriptivo, escrito para que lo lea un agente. Dentro van dos cosas:</p><ul><li><p>el inventario de RPC a las que puedo responder, es decir, qu&#233; sabe hacer la base;</p></li><li><p>las marcas de tiempo de frescura de los datos, es decir, de cu&#225;ndo es lo que te voy a devolver.</p></li></ul><p>Juntas, esas dos piezas contestan a las dos preguntas que un agente se hace antes de fiarse de una herramienta: qu&#233; sabe hacer quien est&#225; al otro lado y si lo que le va a contar sigue siendo cierto. La primera es cobertura; la segunda es confianza. Sin la segunda, la primera sirve de poco.</p><p>Y hay algo mejor que el contenido: de d&#243;nde sale. Un agente que acaba de conectarse descubre solo qu&#233; hay aqu&#237;. No necesita integraci&#243;n previa, no necesita que yo le cuente nada y no necesita acertar por prueba y error. Pide la descripci&#243;n y recibe el mapa. Y como ese JSONB se construye leyendo el cat&#225;logo de la base, no es una copia: es una lectura. No hay nada que mantener a mano, as&#237; que no hay nada que se quede viejo.</p><p>Reforc&#233; la misma idea por otro camino, el que ten&#237;a m&#225;s a mano: los comentarios del cat&#225;logo. Con `COMMENT ON` dej&#233; descripciones pegadas a las tablas, a las columnas y a las funciones. El texto vive junto al objeto que describe, en el mismo sitio, y viaja con &#233;l cuando el esquema cambia. Y como el que describe y el descrito son el mismo esquema, el comentario no puede mentir sobre lo que la base hace: si cambia la funci&#243;n, el comentario est&#225; ah&#237; mismo, a la vista, en la misma revisi&#243;n.</p><p>Para separar audiencias, marqu&#233; esos comentarios con una etiqueta: `[agent]`. Lo que lleva esa marca est&#225; escrito para que lo lea un agente, no un humano curioseando la estructura. Las dos audiencias no necesitan el mismo texto, y mezclarlas estropea las dos. Podr&#237;a haber escrito dos documentos separados, uno para cada una, pero entonces volv&#237;a al problema de antes multiplicado por dos. La etiqueta consigue lo mismo sin duplicar: un solo comentario por objeto, marcado, y quien lo lee decide si le concierne.</p><h2>Qu&#233; aprend&#237;</h2><p>Me quedo con una regla: si quieres que un agente use tu sistema sin que nadie le explique nada, no le des documentaci&#243;n &#8212; dale una funci&#243;n que se describa a s&#237; misma.</p><p>La descripci&#243;n de un sistema tiene que vivir donde vive el sistema. Todo lo dem&#225;s es una copia que envejece. Y la frescura de los datos no es un detalle de operaci&#243;n: es informaci&#243;n de primera, tan importante como el propio dato. Un agente que sabe qu&#233; sabe la base y de cu&#225;ndo lo sabe puede decidir. Uno que no lo sabe, adivina.</p><p>Aprend&#237; la lecci&#243;n con un fallo peque&#241;o y tonto, de esos que no rompen nada y por eso tardas en verlos: hab&#237;a escrito la respuesta en el sitio equivocado. El contenido estaba bien; la ubicaci&#243;n, no.</p><p>El conector sigue vivo en claude.ai desde aquel 1 de junio, para cuentas gratuitas y de pago. Si construyes agentes y quieres ver c&#243;mo se describe la base por dentro:</p><p>https://www.conversoriaecnae.es/mcp</p>]]></content:encoded></item><item><title><![CDATA[El bug de OAuth que me enseñó que la URL de transporte no es el identificador de recurso]]></title><description><![CDATA[En mi conector MCP conviven dos rutas casi id&#233;nticas &#8212; /api/mcp/mcp y /api/mcp &#8212; y estuve a punto de armonizarlas por puro instinto. Hacerlo habr&#237;a roto el conector en vivo: la URL de transporte no...]]></description><link>https://newsletter.brianmenagomez.com/p/el-bug-de-oauth-que-me-enseno-que</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/el-bug-de-oauth-que-me-enseno-que</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 29 Sep 2026 05:33:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7nbD!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb9dcf7f-ea19-48c6-9eb8-91e45dd4b8eb_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Este ensayo se publico primero en <a href="https://brianmenagomez.com/blog/url-de-transporte-no-es-identificador-de-recurso">brianmenagomez.com/blog/url-de-transporte-no-es-identificador-de-recurso</a>.</p><h2>Dos rutas que parec&#237;an el mismo sitio</h2><p>Cuando conect&#233; mi servidor MCP a claude.ai me encontr&#233; con dos cadenas que parec&#237;an lo mismo y no lo eran.</p><p>El conector MCP es la pieza que deja a un agente en claude.ai consultar datos fiscales sin montar su propia integraci&#243;n. Para que un agente lo use hay OAuth por medio: un token, un metadato de recurso, una validaci&#243;n. Y fue montando esa parte de OAuth donde top&#233; con las dos cadenas.</p><p>La URL de transporte del conector es `/api/mcp/mcp`. Esa es la que recibe las peticiones de verdad: la ruta por la que un agente entra y consulta la base de datos fiscal. Si entras al `/api/mcp` a secas, la API contesta un 404. Le falta el segmento `[transport]`. Sin ese &#250;ltimo tramo, la ruta directamente no existe.</p><p>El identificador de recurso OAuth, en cambio, es a prop&#243;sito el `/api/mcp` a secas. Ese es el `aud` del token y el `resourceUrl` que publico en mi PRM, el Protected Resource Metadata.</p><p>Conviven, entonces, dos cadenas casi id&#233;nticas. Una termina en `/mcp/mcp`; la otra, en `/mcp`. Y lo primero que pens&#233; fue: &#171;esto es un bug&#187;.</p><h2>Por qu&#233; quise armonizarlas</h2><p>Parec&#237;a el descuido cl&#225;sico. Dos rutas que se parecen tanto que una ten&#237;a que ser un error de la otra. &#191;Qui&#233;n deja a prop&#243;sito un identificador sin el &#250;ltimo segmento?</p><p>Mi razonamiento era lineal. Si el identificador OAuth es `/api/mcp`, la URL de transporte deber&#237;a ser `/api/mcp`. O al rev&#233;s: si el transporte es `/api/mcp/mcp`, el identificador deber&#237;a ser `/api/mcp/mcp`. Cualquier opci&#243;n, con tal de no dejar dos cadenas que a simple vista no coinciden.</p><p>La tentaci&#243;n ten&#237;a nombre propio: armonizar. Un solo cambio, dos rutas consistentes, y el c&#243;digo queda m&#225;s limpio. Al fin y al cabo, &#191;no es lo mismo un recurso y el sitio donde se sirve ese recurso? Si apuntas al mismo lugar, &#191;por qu&#233; escribirlo distinto?</p><p>Estuve a punto de hacerlo. La intenci&#243;n era buena; el modelo mental, no. Lo peor es que la decisi&#243;n no ven&#237;a de leer el RFC, sino de una corazonada visual: dos cadenas que no cuadran, por definici&#243;n, ten&#237;an que ser un descuido.</p><h2>El error que no llegu&#233; a cometer</h2><p>Y aqu&#237; est&#225; el fallo, que por suerte no lleg&#243; a producci&#243;n.</p><p>El `/api/mcp` a secas devuelve 404. No es que apunte mal ni que est&#233; roto: es que no es un endpoint. Es una identidad l&#243;gica, no una ruta de red. Si lo armonizo para que coincida con la URL de transporte, estoy mezclando dos cosas que no son lo mismo, y el resultado no se vuelve m&#225;s limpio: se vuelve incorrecto.</p><p>Peor todav&#237;a: armonizar ambas habr&#237;a roto el conector en vivo. claude.ai no compara las cadenas por su parecido ni por c&#243;mo se leen de izquierda a derecha. Valida el identificador contra el PRM. Si yo cambio el identificador a `/api/mcp/mcp` para que &#171;cuadre&#187; con la URL de transporte, el PRM deja de casar, la validaci&#243;n falla y la conexi&#243;n se cae.</p><p>El bug no estaba en el c&#243;digo. Estaba en mi modelo mental. Asum&#237; que una ruta de red y un identificador de recurso deb&#237;an ser la misma cadena. No lo son. Y de haber aplicado el cambio sin pararme a entender por qu&#233; exist&#237;an dos cadenas, habr&#237;a roto algo que ya funcionaba.</p><h2>La distinci&#243;n que lo resuelve</h2><p>La salida vino de los RFC. Un identificador de recurso OAuth &#8212; el `aud` del token y el `resourceUrl` del PRM &#8212; es un identificador de recurso en el sentido de RFC 9728 y RFC 8707. Una identidad l&#243;gica. No es el endpoint de red por el que viajan los bytes.</p><p>El matiz que lo aclara todo est&#225; en c&#243;mo OAuth separa el &#171;qui&#233;n&#187; del &#171;por d&#243;nde&#187;. RFC 8707 define c&#243;mo un cliente indica a qu&#233; recurso va dirigido el token que pide. RFC 9728 define c&#243;mo el servidor de recursos publica su metadato para que el cliente lo descubra. En los dos casos, el identificador de recurso es eso: un identificador, un nombre con el que OAuth se entiende a s&#237; mismo. No una ruta de transporte.</p><p>Son dos preguntas distintas, y conviene no confundirlas.</p><p>El endpoint de transporte `/api/mcp/mcp` responde a &#171;&#191;por d&#243;nde entro?&#187;. Dice la ruta concreta, incluido el segmento `[transport]`, por la que llega la petici&#243;n. Sin ese segmento la ruta no resuelve, y por eso el 404.</p><p>El identificador `/api/mcp` responde a otra cosa: &#171;&#191;qu&#233; recurso estoy autorizando?&#187;. El `aud` del token dice para qui&#233;n se emiti&#243; ese token; el `resourceUrl` del PRM dice qu&#233; recurso es este para el mundo OAuth. Es un nombre, no una direcci&#243;n.</p><p>Dicho m&#225;s claro: la URL de transporte es una direcci&#243;n; el identificador de recurso es un nombre. Una direcci&#243;n y un nombre pueden no coincidir y aun as&#237; referirse a lo correcto. En mi conector, el recurso l&#243;gico se llama `/api/mcp` y su ruta de transporte es `/api/mcp/mcp`. Las dos conviven porque cada una responde a una pregunta diferente.</p><h2>Qu&#233; aprend&#237;</h2><p>Me qued&#233; con una regla que ahora aplico a cada integraci&#243;n OAuth que toco: la URL de transporte no es el identificador de recurso. El `aud` y el `resourceUrl` son identidad, no enrutamiento.</p><p>Cuando veo dos cadenas que &#171;deber&#237;an&#187; coincidir y no coinciden, ya no salgo a armonizarlas. Primero me pregunto si estoy comparando dos conceptos que, por dise&#241;o, no tienen por qu&#233; ser la misma cadena. Casi siempre, la respuesta est&#225; en el RFC.</p><p>Fue un bug que no llegu&#233; a meter. Y precisamente por eso es de los que m&#225;s me ha ense&#241;ado: no me corrigi&#243; el c&#243;digo, me corrigi&#243; la forma de leerlo. La pr&#243;xima vez que dos rutas no cuadren, antes de tocarlas voy a preguntarme qu&#233; est&#225; respondiendo cada una.</p><p>Si construyes agentes y quieres ver el conector que me dio esta lecci&#243;n, est&#225; aqu&#237;:</p><p>https://www.conversoriaecnae.es/mcp</p>]]></content:encoded></item><item><title><![CDATA[Memory Architecture para AI Agents 2026: El 90% son Amnésicos por Diseño, no por el LLM]]></title><description><![CDATA[El 90% de los AI agents olvidan por dise&#241;o, no por el LLM. Aprende a construir memoria en 3 capas con MemoryController, eviction por tokens y event logs persistente.]]></description><link>https://newsletter.brianmenagomez.com/p/memory-architecture-para-ai-agents-2a0</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/memory-architecture-para-ai-agents-2a0</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Wed, 09 Sep 2026 07:00:09 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f6a584bb-d207-40f3-b548-2679db2622d8_1080x666.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>El 90% de los AI Agents que Construyes Hoy est&#225;n Dise&#241;ados para Olvidar</strong></h2><p>No olvidan porque el modelo sea limitado. Olvidan porque los modelas como un chat al que le vas cortando el historial.</p><p>Miras tu c&#243;digo y ves un `messages[]` que crece. Cuando se llena, lo truncas o le pides al LLM que resuma. Repites. Eso es lo que el 90% de los agentes en producci&#243;n hacen hoy &#8212; y el resultado es amnesia estructural.</p><p><strong>*El problema no est&#225; en el LLM. Est&#225; en la arquitectura de su memoria.</strong> *</p><p>Toda la industria corre hacia ventanas de contexto m&#225;s grandes pensando que ah&#237; se resuelve. <strong>No es verdad.</strong> Agrandar la ventana no da al agente la capacidad de distinguir qu&#233; merece ser recordado ma&#241;ana. Todo pesa igual en un buffer lineal.</p><h2><strong>El Fallo no es T&#233;cnico. Es de Dise&#241;o</strong></h2><p>La mayor&#237;a de los frameworks te dan un `messages[]` plano. Crees que el modelo "no le cupo el contexto". Los comienzas a resumir. Y cuando resumes, destruyes granularidad epis&#243;dica &#8212; pierdes exactamente lo que un agente necesita para responder "&#191;qu&#233; decidimos el martes?".</p><p>Una ventana de un mill&#243;n de tokens tampoco es memoria. Es un espacio de trabajo gigante.</p><p><strong>Contexto &#8800; memoria.</strong> El contexto es un espacio de trabajo de un solo turno. La memoria es lo que persiste cuando el contexto desaparece. Confundirlos es la raz&#243;n de que se invierta en prompts cada vez m&#225;s largos en lugar de en sistemas de almacenamiento y recuperaci&#243;n.</p><p>Un buffer gigante degrada latencia, encarece cada llamada y &#8212; lo m&#225;s importante &#8212; no jerarquiza. El dato cr&#237;tico compite con el ruido al mismo peso.</p><p>&#10060; <strong>La sabidur&#237;a convencional:</strong> "El agente no recuerda porque el contexto no le cab&#237;a. Meto m&#225;s tokens."</p><p>&#9989; <strong>La realidad:</strong> "El agente no recuerda porque lo dise&#241;&#233; como un chat lineal sin pol&#237;tica de olvido ni recuperaci&#243;n."</p><h2><strong>La Memoria Humana no es un Bloc de Notas</strong></h2><p>La psicolog&#237;a distingue tres sistemas. Tus agentes necesitan los mismos tres con velocidades de olvido distintas:</p><p>1. <strong>Memoria de trabajo</strong> &#8212; vol&#225;til, se evicta por presi&#243;n de tokens.</p><p>2. <strong>Memoria sem&#225;ntica</strong> &#8212; hechos consolidados ("el usuario prefiere Python", "odia los emails largos").</p><p>3. <strong>Memoria epis&#243;dica</strong> &#8212; eventos con timestamp ("el martes desplegamos el webhook y fall&#243;").</p><p>Recuperar un episodio con un embedding sem&#225;ntico aproximado <strong>pierde lo que hace epis&#243;dico a un episodio</strong>: su secuencia exacta y sus consecuencias. Por eso esta capa necesita un log estructurado y queryable, no otra b&#250;squeda vectorial.</p><h2><strong>Dise&#241;a el Framework de 3 Capas con Controlador de Memoria</strong></h2><p>Este es el patr&#243;n que separa una demo de un producto. Un agente sin controlador vuelca todo el historial. Uno con controlador <strong>inyecta contexto curado</strong> en cada turno.</p><h3><strong>Paso 1: Audita tu Amnesia Actual</strong></h3><p>Mira tu agente. &#191;Guarda estado como un &#250;nico array que se trunca o resume?</p><p>Documenta qu&#233; informaci&#243;n concreta se pierde cuando el buffer se llena. Identidad del usuario. Decisiones tomadas. Compromisos a futuro. Preferencias declaradas.</p><p><strong>Esa lista de p&#233;rdidas es tu spec de memoria.</strong></p><h3><strong>Paso 2: Separa las Capas con un MemoryManager</strong></h3><p>Prohibe que el agente escriba directo en el historial. Define un `MemoryManager` que posea tres stores independientes:</p><p>```python</p><p>class MemoryManager:</p><p>def __init__(self):</p><p>self.working = WorkingMemory(max_tokens=4000)          # Vol&#225;til, presupuesto de tokens</p><p>self.long_term = VectorStoreBackedMemory()              # Hechos consolidados, persistente</p><p>self.episodic = EventLogBackedMemory()                  # Eventos append-only con timestamp</p><p>def write(self, entry: MemoryEntry, layer: str):</p><h1>El agente NUNCA escribe directo al historial del LLM.</h1><p>if layer == "working":</p><p>self.working.upsert(entry)</p><p>elif layer == "long_term":</p><p>self.long_term.consolidate(entry)</p><p>elif layer == "episodic":</p><p>self.episodic.log(entry)</p><p>def read_for_context(self, query: str, budget: int) -&gt; list[str]:</p><p>return self.controller.assemble(query, budget)</p><p>```</p><p>El agente escribe en la capa correcta. Nada de `list.append(role, content)` directo.</p><h3><strong>Paso 3: Working Memory con Eviction por Tokens, no por Mensajes</strong></h3><p>Un buffer circular con eviction por peso de tokens real. Usa `tiktoken` en vez de contar mensajes:</p><p>```python</p><p>import tiktoken</p><p>enc = tiktoken.encoding_for_model("gpt-4o")</p><p>class WorkingMemory:</p><p>def __init__(self, max_tokens: int = 4000):</p><p>self.max_tokens = max_tokens</p><p>self.anchor = {}          # Estado esencial: nombre, task context. NUNCA se evicta.</p><p>self.buffer = []</p><p>def add(self, message: dict) -&gt; list[dict]:</p><p>self.buffer.append(message)</p><p>return self._evict_if_needed()</p><p>def _evict_if_needed(self) -&gt; list[dict]:</p><p>token_count = sum(len(enc.encode(m["content"])) for m in self.buffer)</p><p>while token_count &gt; self.max_tokens and self.buffer:</p><p>self.buffer.pop(0)    # Evicta los m&#225;s antiguos excepto el anchor</p><p>token_count = sum(len(enc.encode(m["content"])) for m in self.buffer)</p><p>return self.anchor_slot() + self.buffer</p><p>```</p><p><strong>El anchor es el ingrediente cr&#237;tico.</strong> Un slot protegido con el estado esencial del usuario y la tarea que sobrevive a cualquier truncado. Sin &#233;l, cuando el buffer se evicta pierdes qui&#233;n es el usuario y qu&#233; estaba haciendo.</p><h3><strong>Paso 4: El Ciclo de Consolidaci&#243;n</strong></h3><p>Al final de cada sesi&#243;n (o al detectar un hito), un paso de <em>reflection</em> extrae hechos long-term y resume eventos. No esperes al "resumir por token" reactivo. <strong>Consolida proactivamente.</strong></p><p>Extrae con el LLM: "Dado este historial, &#191;qu&#233; hechos sobre el usuario debemos persistir? &#191;Qu&#233; eventos merecen registro epis&#243;dico?".</p><p>Persiste los hechos en pgvector o Chroma con metadata de recencia. Registra los episodios en un log estructurado:</p><p>```json</p><p>{</p><p>"event_id": "9f4c2a1e",</p><p>"timestamp": "2026-09-08T14:32:11Z",</p><p>"session_id": "s-7712",</p><p>"user_id": "u-8821",</p><p>"action": "deploy_webhook",</p><p>"tool_call": "vercel_deploy",</p><p>"outcome": "failure",</p><p>"error": "INVALID_ENDPOINT_URL",</p><p>"user_feedback": "vale, prueba otra vez con la URL corregida",</p><p>"next_action": "retry_with_fixed_url"</p><p>}</p><p>```</p><p>No texto libre. Campos queryables. As&#237; el agente responde "&#191;qu&#233; hicimos la semana pasada?" con un <strong>SQL o query estructurada</strong>, no con recuperaci&#243;n sem&#225;ntica difusa que confunde episodios.</p><h3><strong>Paso 5: El Controlador de Recuperaci&#243;n &#8212; el Componente que Casi Nadie Dise&#241;a</strong></h3><p>El controlador decide en cada turno si consulta long-term/episodic, con qu&#233; query y cu&#225;ntos resultados. <strong>No vuelca contexto &#8212; lo cura.</strong></p><p>```python</p><p>class MemoryController:</p><p>def __init__(self, memory: MemoryManager):</p><p>self.memory = memory</p><p>def decide(self, user_message: str, current_context: str) -&gt; list[str]:</p><p>retrieval_intent = classify(user_message)  # &#191;necesita hist&#243;rico? &#191;es continuidad o nueva tarea?</p><p>if retrieval_intent == "continuity":</p><p>return self.memory.long_term.query(user_message, top_k=5)</p><p>elif retrieval_intent == "history":</p><h1>&#191;Qu&#233; hizo el agente hace X d&#237;as, con qu&#233; resultado?</h1><p>return self.memory.episodic.query(</p><p>f"SELECT * FROM events WHERE session_id = ? AND timestamp &gt; ? ORDER BY timestamp",</p><p>params=[last_session_id, last_week]</p><p>)</p><p>else:</p><p>return []  # Nueva tarea at&#243;mica. No necesitas pasado.</p><p>```</p><p>La respuesta a RAG aqu&#237; es importante: <strong>RAG resuelve memoria sem&#225;ntica de documentos, no memoria de interacci&#243;n.</strong></p><p>Un agente que usa RAG para recordar lo que el usuario le dijo ayer est&#225; mal equipado. Los hechos conversacionales son de alta recencia y alta especificidad. No viven en un corpus indexable &#8212; viven en un flujo de eventos.</p><p>Por eso la arquitectura h&#237;brida gana: <strong>sem&#225;ntica para hechos consolidados, estructurada (SQL/JSONL) para eventos epis&#243;dicos.</strong></p><h2><strong>Mide lo que Antes Era Invisible</strong></h2><p>El controlador hace la memoria testeable. Puedes inyectar eventos conocidos y verificar recuperaci&#243;n &#8212; imposible con un buffer opaco.</p><p>Mide dos m&#233;tricas:</p><p>1. <strong>Tasa de "&#191;recuerdas&#8230;?" fallidas</strong> &#8212; cuando el usuario pregunta "&#191;qu&#233; decidimos la semana pasada?", &#191;responde el agente o improvisa?</p><p>2. <strong>Tokens de contexto por turno</strong> &#8212; si a&#241;ades memoria curada, el presupuesto de contexto deber&#237;a *bajar*, no subir.</p><p>La mejora debe verse en ambas. Si s&#243;lo baja una, tu controlador est&#225; mal calibrado.</p><h2><strong>La Objeci&#243;n del Mill&#243;n de Tokens</strong></h2><p>"&#191;Para qu&#233; complicarme con tres capas si meto todo el historial?"</p><p>El coste real: degradaci&#243;n de latencia y atenci&#243;n en prompts largos. Ruido que compite con la instrucci&#243;n actual. Y sobre todo &#8212; <strong>"todo el historial" no es memoria selectiva.</strong></p><p>El agente no distingue el dato cr&#237;tico del ruido. Sigue sin poder responder "&#191;qu&#233; decidimos el martes?" de forma fiable porque en un buffer lineal no hay jerarqu&#237;a de relevancia.</p><p><strong>La amnesia del 90% de los agentes es una decisi&#243;n de dise&#241;o, no una maldici&#243;n del modelo.</strong> Y por tanto es una ventaja competitiva disponible para quien la resuelva.</p><p>Los agentes que persistan contexto entre sesiones &#8212;con consentimiento y gobernanza de datos expl&#237;citos&#8212; podr&#225;n ofrecer continuidad real. Menos repetici&#243;n de preferencias. Menos re-explicaci&#243;n de contexto. Ah&#237; es donde los usuarios notan la diferencia entre una herramienta y un asistente.</p><h2><strong>Gobernanza y Testing: La Memoria Te Hace la Vida M&#225;s F&#225;cil, no M&#225;s Dif&#237;cil</strong></h2><p>"Persistir memoria es un riesgo de privacidad. &#191;C&#243;mo reproduzco un bug si el agente depende de estado previo?"</p><p>El dise&#241;o expl&#237;cito responde a ambas.</p><ul><li><p>Define qu&#233; se persiste, con qu&#233; consentimiento y qu&#233; pol&#237;tica de retenci&#243;n/borrado. No es un extra &#8212; es parte de la spec.</p></li><li><p>Un event log inyectable es <strong>m&#225;s f&#225;cil de depurar</strong> que un historial opaco. Reproduces un bug cargando un estado conocido y verificando la recuperaci&#243;n.</p></li><li><p>Implementa un modo "memoria desactivada" como herramienta de diagn&#243;stico. Si el agente falla con memoria pero funciona sin ella, el bug est&#225; en tu controlador de consolidaci&#243;n o recuperaci&#243;n &#8212; no en el modelo.</p></li></ul><h2><strong>Los Tres Takeaways</strong></h2><p>1. <strong>Contexto no es memoria.</strong> El contexto es temporal y caro. La memoria persiste y se cur&#243;. Deja de invertir en ventanas gigantes y empieza a dise&#241;ar almacenamiento y recuperaci&#243;n.</p><p>2. <strong>Tres capas con pol&#237;ticas de olvido distintas.</strong> Working (evicta por tokens, protege el anchor), long-term (consolida hechos), episodic (log estructurado append-only con timestamp).</p><p>3. <strong>El controlador de memoria es el producto.</strong> Decide qu&#233; escribir, qu&#233; recuperar y cu&#225;ndo. Un agente sin controlador vuelca contexto; uno con controlador act&#250;a con contexto suficiente y nada m&#225;s.</p><p>La pr&#243;xima vez que tu agente "no recuerde qui&#233;n eres", no culpes al modelo. <strong>Mira tu `messages[]` y preg&#250;ntate si lo dise&#241;aste como un chat o como un sistema de memoria.</strong> La diferencia es la que hay entre una herramienta que responde y un asistente que te conoce.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/memory-architecture-ai-agents-2026-tres-capas-20260909?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[El 90% de las "Productizaciones" No Son Producto: Son el Mismo Servicio con Alcance Recortado]]></title><description><![CDATA[Empaquetar no es productizar. Descubre por qu&#233; comprimir tu servicio te condena a la irrelevancia y c&#243;mo declarar una categor&#237;a propia es la &#250;nica v&#237;a para escalar.]]></description><link>https://newsletter.brianmenagomez.com/p/el-90-de-las-productizaciones-no-a92</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/el-90-de-las-productizaciones-no-a92</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 08 Sep 2026 07:00:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2a045c15-94a3-4bb8-8986-66f522cd0060_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>El 90% de las "Productizaciones" No Son Producto: Son el Mismo Servicio con Alcance Recortado</strong></h2><p>Crees que productizar es empaquetar. Definir alcance, estandarizar la entrega, fijar el precio y listo.</p><p><strong>*Te has equivocado de diagn&#243;stico.</strong>*</p><p>Eso no es productizar. Es comprimir. Y comprimir no crea producto: crea un servicio m&#225;s peque&#241;o con una etiqueta m&#225;s bonita. Llevo cuatro a&#241;os enviando software en Espa&#241;a y he visto docenas de operadores &#8212;agencias digitales, freelancers, consultores t&#233;cnicos&#8212; caer en la misma trampa. Cogen el servicio que ya venden, le cortan el alcance, le ponen precio fijo y lo llaman "producto". Lo que han construido es un acuerdo de no-matar-su-margen, no algo que escale.</p><p>La cifra del 90% es una hip&#243;tesis provocadora, no una estad&#237;stica con fuente. Lo digo con transparencia antes de que lo preguntes. Pero se comprueba de forma cualitativa: mirad vuestro sector y contad cu&#225;ntas "productizaciones" se describen por su entregable y su alcance &#8212;lo que las delata como servicios disfrazados&#8212; frente a cu&#225;ntas se describen por el resultado que el comprador ya reconoce como problema.</p><p>El patr&#243;n es id&#233;ntico al que desmont&#233; sobre la "opci&#243;n segura" al construir con Next.js: la decisi&#243;n prudente &#8212;empaquetar lo que ya haces&#8212; es la que garantiza la irrelevancia. La decisi&#243;n inc&#243;moda &#8212;declarar categor&#237;a&#8212; es la &#250;nica con capacidad de escalar.</p><h2><strong>El Problema: Comprimir Nunca Crea Categor&#237;a</strong></h2><p>Cuando "productizas" recortando alcance y fijando condiciones, aceptas la casilla competitiva que ya defini&#243; el incumbente. El cliente te compara con la alternativa preexistente en sus propios t&#233;rminos. Qu&#233; incluye. Qu&#233; no incluye. Cu&#225;nto cuesta frente a la otra agencia.</p><p>Tu oferta se convierte en un commodity negociable. Peleas dentro de la casilla del otro, con sus reglas, su lenguaje y sus m&#233;tricas. Y en esa pelea, el incumbente siempre gana: tiene m&#225;s casos, m&#225;s reputaci&#243;n y m&#225;s presupuesto.</p><p>&#10060; <strong>El servicio comprimido:</strong></p><p>&#8594; "Auditor&#237;a de SEO en 14 d&#237;as"</p><p>&#8594; "Paquete de branding para pymes"</p><p>&#8594; "Landing page productizada en 10 d&#237;as"</p><p>&#9989; <strong>La categor&#237;a:</strong></p><p>&#8594; "El sistema que convierte visitas en citas para cl&#237;nicas dentales"</p><p>&#8594; "La garant&#237;a de que tu web no se caiga en el Black Friday"</p><p>&#8594; "El m&#233;todo que sustituye al comercial que no contesta"</p><p>&#191;Veis la diferencia? La primera lista describe entregables. Compite con todas las auditor&#237;as, todos los paquetes de branding y todas las landing pages del mercado. La segunda lista describe resultados que el comprador ya reconoce como problema &#8212;y que no sabe c&#243;mo nombrar cuando va a comprar.</p><p>La categor&#237;a redefine el eje de comparaci&#243;n. El servicio viejo deja de ser "lo mismo pero m&#225;s caro" y pasa a ser "la forma antigua de obtener el resultado". El cliente deja de preguntar "&#191;qu&#233; incluye y cu&#225;nto cuesta?" y empieza a preguntar "&#191;puedo permitirme no tener este resultado?".</p><h2><strong>El Trabajo Fantasma Como Activo Estrat&#233;gico</strong></h2><p>La intuici&#243;n dominante dice que todo lo que entregas sin facturar ni nombrar es margen regalado o ineficiencia.</p><p>La lectura de categor&#237;a lo invierte: <strong>el trabajo fantasma es la evidencia emp&#237;rica del resultado que ya produces</strong>. Es lo &#250;nico que demuestra que tu categor&#237;a existe antes de que la nombres.</p><p>Pensad en ello. &#191;Qu&#233; hac&#233;is hoy que el cliente no ve en la propuesta? &#191;Qu&#233; arregl&#225;is que no est&#225; en el alcance? &#191;Qu&#233; garantiz&#225;is que no podr&#237;as comprar por separado en la competencia? Ese trabajo invisible es la materia prima de la categor&#237;a.</p><p>Un ejemplo real. Ten&#237;a una gestor&#237;a como cliente en Espa&#241;a que vend&#237;a "asesor&#237;a fiscal". Cuando audit&#233; su trabajo fantasma, descubr&#237; que su valor real no era presentar los impuestos &#8212;eso lo hace cualquiera&#8212; sino que el aut&#243;nomo dejaba de dormir mal en abril. Ellos absorb&#237;an la ansiedad regulatoria, las cartas de Hacienda y los sustos de &#250;ltima hora. Su productoizado no deber&#237;a llamarse "asesor&#237;a fiscal": deber&#237;a llamarse "la garant&#237;a de que Hacienda no te robe el sue&#241;o en abril".</p><p>Quien audita primero, nombra primero. Quien nombra primero, define las reglas.</p><p>El caso Graza es el ejemplo perfecto fuera de los servicios. La marca de aceite de oliva DTC no se posicion&#243; como "otra marca de aceite con suscripci&#243;n". Se posicion&#243; como la comunidad que convierte a sus clientes en nodos de adquisici&#243;n &#8212;con un programa de referidos que, seg&#250;n sus propios datos, desplom&#243; el coste de adquirir cada cliente frente al paid media. No compitieron contra las marcas de aceite. Compitieron contra la ausencia de esa comunidad. Esa es la jugada de categor&#237;a, no de empaquetado.</p><h2><strong>C&#243;mo Construir Categor&#237;a, No Eslogan</strong></h2><p>Aqu&#237; est&#225; el error que comete casi todo el mundo: intentar el camino corto. Rebautizar el servicio, subir el tono del marketing y esperar que el naming haga el trabajo.</p><p>La categor&#237;a muere en la primera entrega que no cumple el resultado prometido. Por eso el orden importa m&#225;s que el nombre.</p><h3><strong>Paso 1: Audita tu trabajo fantasma</strong></h3><p>Haz inventario de todo lo que entregas, asesoras, garantizas o arreglas que el cliente no ve en la propuesta ni podr&#237;a comprar por separado. Ese trabajo invisible es la evidencia de tu categor&#237;a antes de que exista. Pregunta a tus &#250;ltimos cinco clientes: "&#191;Qu&#233; fue lo que m&#225;s valor&#243; de trabajar conmigo?" &#8212; y busca lo que no aparece en tu factura.</p><h3><strong>Paso 2: Nombra la categor&#237;a por el resultado, no por la entrega</strong></h3><p>Si tu oferta se llama "auditor&#237;a t&#233;cnica" describes un entregable &#8212;y compites con todas las auditor&#237;as del mercado. El test: el nombre debe ser el que tu comprador usar&#237;a en una conversaci&#243;n interna con su jefe o su socio. No el que usa tu factura.</p><h3><strong>Paso 3: Declara el marco competitivo</strong></h3><p>Define qui&#233;n es la alternativa real. No es otra agencia ni otro paquete. Es el statu quo: no hacer nada, hacerlo con el servicio viejo, hacerlo in-house. Al nombrar al incumbente como alternativa, dejas de ser comparado por precio dentro de su categor&#237;a. Pasas a ser la referencia de un problema que antes no ten&#237;a nombre de compra.</p><h3><strong>Paso 4: Fija la m&#233;trica de tu categor&#237;a</strong></h3><p>Elige un indicador de resultado que solo tu categor&#237;a puede prometer y medir &#8212;y que el servicio comprimido no sabe operar porque est&#225; organizado por horas o por entregables. Esa m&#233;trica es el ancla que hace verificable la categor&#237;a. Si no puedes medir el resultado de forma observable, no tienes categor&#237;a: tienes un eslogan.</p><h3><strong>Paso 5: Cierra el bucle con mec&#225;nica de producto</strong></h3><p>El naming y el marco competitivo sin entrega repetible, puntos de entrada claros y resultados observables quedan en humo. La categor&#237;a se sostiene cuando el proceso productizado hace verdad la promesa de resultado en cada ciclo de entrega. Este es el punto donde la mayor&#237;a abandona &#8212;y donde se separan los que crean categor&#237;as de los que pintan anuncios.</p><p>Las marcas que ganaron en DTC durante 2026 no lo hicieron por tener el mejor empaque. Won't Stop, la marca de running que pas&#243; de cero a n&#250;meros estratosf&#233;ricos en 36 meses, no se posicion&#243; como "otra marca deportiva con suscripci&#243;n". Se posicion&#243; como la categor&#237;a del corredor serio que los minoristas generalistas ignoran. Su CEO lo dijo literalmente: "No construimos un negocio de suscripci&#243;n. Construimos una cultura de membres&#237;a que resulta tener un sistema de cobro pegado". Eso es declarar categor&#237;a &#8212;no vender un servicio con recurrencia mensual.</p><h2><strong>Sobre las Objeciones Que Ya Est&#225;s Pensando</strong></h2><p><strong>"Esto es branding vac&#237;o: cambiar el nombre no cambia lo que entregas."</strong></p><p>Concedido. Y por eso el framework arranca en el paso 1, con la auditor&#237;a del trabajo real, no con el naming. La etiqueta debe ser la consecuencia de un proceso que ya funciona, no su sustituto. Si cambias el nombre sin cambiar la mec&#225;nica de entrega, tienes exactamente lo que describes: humo.</p><p><strong>"Crear categor&#237;as es para grandes: eso requiere presupuesto de marca tipo Salesforce o HubSpot."</strong></p><p>Falso. La creaci&#243;n de categor&#237;a a peque&#241;a escala no exige medios masivos. Exige cambiar el marco de comparaci&#243;n &#8212;nombrar al incumbente como alternativa&#8212; y fijar la m&#233;trica del resultado. Ambos movimientos se hacen en la propuesta comercial, en la p&#225;gina de venta y en el primer discovery call. No en una campa&#241;a de marca de seis cifras. El peque&#241;o operador tiene una ventaja que Salesforce no tiene: velocidad para ejecutar el cambio de marco esta semana.</p><p><strong>"El 90% es una cifra sin fuente: la est&#225;is inventando."</strong></p><p>S&#237;, es una hip&#243;tesis provocadora, no un estudio. Pero en lugar de descartarla, haced la prueba. Examinad las productizaciones de vuestro sector &#8212;las que se anuncian, las que se venden, las que fracasan&#8212; y contad cu&#225;ntas se describen por entregable y alcance frente a cu&#225;ntas se describen por un resultado que el comprador ya reconoce como problema. El resultado de esa prueba cualitativa os dir&#225; si el 90% se queda corto o se queda largo.</p><h2><strong>El Marco de Categor&#237;a por Resultado</strong></h2><p>Este es el framework completo en cinco pasos. Llamadlo <strong>El Marco de Categor&#237;a por Resultado</strong> si os sirve para recordarlo:</p><p>1. <strong>Audita el trabajo fantasma</strong> &#8212; inventar&#237;a el outcome que ya produces de forma invisible. Es tu evidencia antes del nombre.</p><p>2. <strong>Nombra por el resultado</strong> &#8212; el nombre debe ser el que tu comprador usar&#237;a en una conversaci&#243;n interna, no el de tu factura.</p><p>3. <strong>Declara al incumbente</strong> &#8212; tu alternativa no es otra agencia, es el statu quo. N&#243;mbralo.</p><p>4. <strong>Fija la m&#233;trica de categor&#237;a</strong> &#8212; un indicador de resultado que el servicio por horas no sabe operar. Es tu ancla verificable.</p><p>5. <strong>Cierra con mec&#225;nica de producto</strong> &#8212; proceso repetible, puntos de entrada claros, resultados observables. Sin esto, es eslogan.</p><p>La idea del producto escalable no es vender el mismo servicio m&#225;s barato. Es conseguir que tu oferta <strong>sea el nombre de una categor&#237;a nueva</strong> &#8212; y pasar de competir contra otras agencias a competir contra la ausencia del resultado.</p><p>Cuando consigues eso, dejas de existir en los comparadores de la vieja categor&#237;a. Y los clientes que nadie m&#225;s ha educado empiezan a llamar a tu puerta con una pregunta que ya no es "&#191;qu&#233; incluye y cu&#225;nto cuesta?".</p><p>Es "&#191;puedo permitirme no tener esto?".</p><p>Llevo cuatro a&#241;os viendo operadores peque&#241;os hacer exactamente lo contrario de lo que dicta el instinto. Los que escalan no son los que mejor empaquetan su servicio. Son los que se atreven a declarar que su oferta resuelve un problema que una categor&#237;a entera ha ignorado. El empaquetado es c&#243;modo. La categor&#237;a da v&#233;rtigo. Y el v&#233;rtigo, en este negocio, es la se&#241;al de que vas por el buen camino.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/productizar-es-declarar-categoria-no-empaquetar-20260908?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Resend Email API Tutorial 2026: La Entregabilidad es un Commodity. El Developer Experience es el Moat]]></title><description><![CDATA[Tutorial de Resend Email API 2026 en espa&#241;ol: por qu&#233; el DX es el moat real, c&#243;mo montar el pipeline con React Email, webhooks y Edge Functions sin lock-in.]]></description><link>https://newsletter.brianmenagomez.com/p/resend-email-api-tutorial-2026-la-0ec</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/resend-email-api-tutorial-2026-la-0ec</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 08 Sep 2026 07:00:23 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3dc9d81c-76bd-4c7c-9f54-74ac182aba7d_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Tu Plantilla de Email Est&#225; Secuestrada y No lo Sabes</strong></h2><p>Abre tu dashboard de SendGrid. Mira cualquiera de tus templates activos.</p><p><strong>*No puedes mover esa plantilla a Mailgun sin reescribirla desde cero.</strong>*</p><p>No es culpa tuya. Es el modelo de negocio. SendGrid, Mailgun, SES &#8212; todos os atan a su formato de plantillas porque la retenci&#243;n es m&#225;s rentable que la calidad. Los proveedores legacy no compiten en entregabilidad &#8212; la entregabilidad es un commodity desde 2023 si configuras SPF, DKIM y DMARC bien. Compiten en lock-in.</p><p>Resend rompe ese juego. No te vende plantillas propietarias: te vende <em>c&#243;digo</em>. Componentes de React Email que viven en tu repositorio, se versionan con Git y se despliegan con tu app. Cuando tu proveedor sube el precio o te falla un rate limit, te llevas tus plantillas contigo y cambias de API en un finde.</p><p>Este es el tutorial que me hubiera gustado tener cuando mont&#233; el sistema de email de <strong>conversoriaecnae.es</strong> &#8212; y el que aplico en cada proyecto nuevo que uso Resend.</p><h2><strong>Por Qu&#233; Todos los Proveedores Entregan Igual (y C&#243;mo Lo Compruebas)</strong></h2><p>Aqu&#237; va la verdad inc&#243;moda del sector: <strong>*la entregabilidad ya no es un diferenciador entre proveedores de email transaccional.</strong> *</p><p>&#191;Por qu&#233;? Porque la entregabilidad depende de tres cosas que controlas t&#250;, no el proveedor:</p><p>1. <strong>SPF</strong> &#8212; qu&#233; servidores est&#225;n autorizados a enviar por tu dominio.</p><p>2. <strong>DKIM</strong> &#8212; la firma criptogr&#225;fica que verifica que nadie ha tocado el correo.</p><p>3. <strong>DMARC</strong> &#8212; la pol&#237;tica que dice a Gmail y Outlook qu&#233; hacer si fallan las dos anteriores.</p><p>Config&#250;ralas bien y desde cualquier proveedor serio llegas al 99% de entregabilidad en correos transaccionales. Config&#250;ralas mal y ni el mejor proveedor del mundo salva tu dominio.</p><p>Gmail y Outlook ya exigen DMARC desde 2024 para remitentes masivos. La barrera de entrada subi&#243;, y eso aplana el campo de juego: los que sobreviven tienen la infraestructura igualada.</p><p>Entonces, &#191;por qu&#233; Resend importa si SES cuesta menos?</p><p>Porque el coste real del email no es el env&#237;o. Es el desarrollo.</p><h2><strong>El Problema Real: SES Te Cobra el Env&#237;o y Te Hace Pagar el Desarrollo</strong></h2><p>Os propongo un experimento mental. Monta un email transaccional con AWS SES y uno con Resend.</p><p>Con SES necesitas:</p><ul><li><p>La SDK de AWS en tu proyecto (o firmar peticiones manualmente con SigV4).</p></li><li><p>Un sistema de template propio o te encajas con el formato de SES.</p></li><li><p>Configurar el feedback loop manualmente.</p></li><li><p>Debuggear un cuerpo de petici&#243;n que parece escrito por alguien que odia a los desarrolladores.</p></li></ul><p>&#10060; <strong>Esto es lo que pasa con SES:</strong> pasas 3 horas intentando entender el SDK, otra hora peleando con el formato de template, y acabas con un sistema que solo funciona en tu m&#225;quina.</p><p>Con Resend:</p><p>```typescript</p><p>// Tu pipeline de email completo en ~40 l&#237;neas</p><p>import { Resend } from 'resend';</p><p>const resend = new Resend(process.env.RESEND_API_KEY);</p><p>// La plantilla es un componente React. Vive en tu repo.</p><p>import { WelcomeEmail } from '@/emails/WelcomeEmail';</p><p>await resend.emails.send({</p><p>from: 'Acme &lt;onboarding@tu-dominio.com&gt;',</p><p>to: ['usuario@ejemplo.com'],</p><p>subject: 'Bienvenido a Acme',</p><p>react: &lt;WelcomeEmail username="carlos" /&gt;,</p><p>});</p><p>```</p><p>&#9989; <strong>Lo que tienes con Resend:</strong> un endpoint en TypeScript, la plantilla como componente tipado con props, y el env&#237;o en una l&#237;nea.</p><p>SES vende infraestructura. Resend vende experiencia de desarrollo. La primera te cuesta horas. La segunda te las devuelve.</p><h2><strong>El Modelo de 3 Capas para No Morir en el Intento</strong></h2><p>Me pregunt&#225;is siempre lo mismo: <em>"&#191;C&#243;mo montas el email en producci&#243;n sin volverte loco?"</em>.</p><p>La respuesta es el modelo que llamo <strong>el Modelo de 3 Capas para No Morir en el Intento</strong>. Tres capas separadas que no se mezclan:</p><h3><strong>Capa 1: React Email &#8212; Las Plantillas como C&#243;digo</strong></h3><p>Tu primer error es tratar las plantillas como artefactos del proveedor. Resend elimina ese error de ra&#237;z: usa <a href="https://react.email">React Email</a>, que compila componentes JSX a HTML compatible con los 8 clientes de correo principales.</p><p>```tsx</p><p>// emails/WelcomeEmail.tsx &#8212; una plantilla portable</p><p>import { Html, Body, Container, Heading, Text, Button } from '@react-email/components';</p><p>export function WelcomeEmail({ username }: { username: string }) {</p><p>return (</p><p>&lt;Html&gt;</p><p>&lt;Body style={{ fontFamily: 'Arial, sans-serif', padding: '20px' }}&gt;</p><p>&lt;Container&gt;</p><p>&lt;Heading&gt;&#161;Bienvenido, {username}!&lt;/Heading&gt;</p><p>&lt;Text&gt;Gracias por registrarte en tu-dominio.com.&lt;/Text&gt;</p><p>&lt;Button href="https://tu-dominio.com/dashboard" style={{ background: '#22c55e', padding: '12px 24px' }}&gt;</p><p>Ir al dashboard</p><p>&lt;/Button&gt;</p><p>&lt;/Container&gt;</p><p>&lt;/Body&gt;</p><p>&lt;/Html&gt;</p><p>);</p><p>}</p><p>```</p><p>Esa plantilla es un fichero m&#225;s de tu repo. Haces commit, code review, tests con Vitest y despliegas. Si ma&#241;ana migras de Resend a otro proveedor, los componentes se quedan &#8212; solo cambia la capa de env&#237;o.</p><p>Otra cosa que me encanta: la librer&#237;a de preview de React Email. Recibes el correo renderizado en tu navegador sin mandar un solo email real. Delegas el dise&#241;o al desarrollador, no al marketer que edita templates en un drag &amp; drop roto.</p><h3><strong>Capa 2: La API &#8212; Un Endpoint en tu Edge Function, no un Servicio Aparte</strong></h3><p>Resend se integra de forma nativa con <strong>Vercel Edge Functions</strong> y <strong>Next.js Route Handlers</strong>. Eso significa que tu l&#243;gica de email vive en el mismo runtime que tu app.</p><p>```typescript</p><p>// app/api/send-welcome/route.ts &#8212; Next.js App Router</p><p>import { Resend } from 'resend';</p><p>import { WelcomeEmail } from '@/emails/WelcomeEmail';</p><p>const resend = new Resend(process.env.RESEND_API_KEY);</p><p>export async function POST(request: Request) {</p><p>const { email, username } = await request.json();</p><p>// Validas con Zod, no con un if gigante</p><p>const { data, error } = await resend.emails.send({</p><p>from: 'Tu App &lt;hola@tu-dominio.com&gt;',</p><p>to: [email],</p><p>subject: 'Bienvenido a Tu App',</p><p>react: WelcomeEmail({ username }),</p><p>});</p><p>if (error) {</p><p>return Response.json({ error }, { status: 400 });</p><p>}</p><p>return Response.json({ id: data?.id }, { status: 201 });</p><p>}</p><p>```</p><h3><strong>Capa 3: Los Webhooks &#8212; Donde Se Gana la Batalla de la Reputaci&#243;n</strong></h3><p>Todo el mundo configura el env&#237;o. Casi nadie configura el feedback loop. Ese es el error que te hunde el dominio a los tres meses de escalar.</p><p>Resend manda <strong>webhooks</strong> por cada evento: `email.sent`, `email.delivered`, `email.opened`, `email.clicked`, y el m&#225;s importante de todos &#8212; `email.bounced` y `email.complained`.</p><p>Un correo que rebota no es un problema puntual: es un martillazo a tu reputaci&#243;n de dominio. Si no lo purgas de tu base de datos en minutos, el siguiente env&#237;o masivo perjudica a todos los dem&#225;s.</p><p>Montar el webhook es directo:</p><p>```typescript</p><p>// app/api/webhooks/resend/route.ts</p><p>import { NextResponse } from 'next/server';</p><p>export async function POST(request: Request) {</p><p>const body = await request.json();</p><p>// Eventos: email.bounced, email.complained, email.delivered</p><p>if (body.type === 'email.bounced') {</p><p>await updateUserStatus(body.data.email, 'bounced');</p><p>// L&#243;gica de purga del usuario de tu base de datos</p><p>}</p><p>if (body.type === 'email.complained') {</p><p>// Marcar como spam &#8212; bloquea TODOS los env&#237;os futuros a ese contacto</p><p>await blockContact(body.data.email);</p><p>}</p><p>return NextResponse.json({ ok: true });</p><p>}</p><p>```</p><p>Con este webhook activo, la purga de rebotes es autom&#225;tica. Tu reputaci&#243;n de dominio se mantiene intacta mientras escalas. Es la diferencia entre tener 100 contactos sucios y que Gmail te meta en spam, o purgarlos en tiempo real y mantener entregas limpias al 99%.</p><h2><strong>El Patr&#243;n que Fall&#243; en Producci&#243;n: Lo Que No Debes Hacer</strong></h2><p>Vale, os he dado el camino correcto. Ahora el atajo que os va a morder el culo.</p><p>&#10060; <strong>Patr&#243;n roto: enviar emails desde el request principal.</strong></p><p>```javascript</p><p>// &#10060; NUNCA hagas esto</p><p>app.post('/registro', async (req, res) =&gt; {</p><p>await db.createUser(req.body);</p><p>await resend.emails.send({ ... }); // Bloquea la respuesta 400ms</p><p>res.json({ ok: true });</p><p>});</p><p>```</p><p>Cuatrocientos milisegundos de bloqueo por cada registro. Con 10 registros concurrentes, tu API ya huele a humo.</p><p>&#9989; <strong>Patr&#243;n correcto: desacopla el env&#237;o.</strong></p><p>```typescript</p><p>// &#9989; La respuesta vuelve al instante. El email viaja en cola.</p><p>app.post('/registro', async (req, res) =&gt; {</p><p>await db.createUser(req.body);</p><p>await notificationsQueue.publish('welcome-email', req.body.email);</p><p>res.json({ ok: true }); // ~50ms, no 400ms</p><p>});</p><p>```</p><p>El email transaccional no es transaccional en el sentido estricto: es as&#237;ncrono. Tr&#225;talo como tal y tu API escala. Tr&#225;talo como parte del request y tu backend muere con tu primer pico de tr&#225;fico.</p><h2><strong>Resend Te Da la Salud del Pipeline, no Solo el Env&#237;o</strong></h2><p>El dashboard de Resend no es un radiador de n&#250;meros bonitos. Es diagn&#243;stico cl&#237;nico.</p><p>Cada env&#237;o te da un <strong>rate limit</strong> claro y configurable por dominio o por campa&#241;a. Te muestra <strong>health score</strong> por dominio, streaks de entregabilidad y advertencias tempranas cuando tu reputaci&#243;n empieza a degradarse.</p><p>Eso es lo que ning&#250;n proveedor legacy te da por defecto: visibilidad operativa sin tener que montar un pipeline de observabilidad paralelo. Con SES necesitas CloudWatch, presupuestos personalizados y un ingeniero a media jornada solo para entender qu&#233; est&#225; pasando.</p><p>En Resend, el feedback loop est&#225; integrado en la experiencia. Los webhooks llegan a un endpoint que montas en veinte minutos, y el dashboard te cuenta la historia antes de que se convierta en un incidente.</p><h2><strong>La Migraci&#243;n no Duele si Tu Plantillas Viven en Tu Repo</strong></h2><p>Vamos directos a la conclusi&#243;n inc&#243;moda: si uses el proveedor que uses, <strong>*tus plantillas deben vivir en tu repositorio, no en el dashboard del proveedor.</strong>*</p><p>React Email no es exclusivo de Resend. Puedes usar los componentes con cualquier proveedor que acepte HTML &#8212; que son todos. La diferencia es que Resend lo hace trivial: p&#225;sale el componente directamente como prop `react` y se encarga de compilarlo por ti.</p><p>Mi recomendaci&#243;n pr&#225;ctica:</p><p>1. <strong>Empieza con React Email</strong> en cualquier proyecto nuevo. Todas las plantillas como componentes en una carpeta `emails/`.</p><p>2. <strong>Usa Resend como proveedor por defecto</strong> &#8212; no por la entregabilidad, sino porque el DX te ahorra semanas de fricci&#243;n con SDKs y formatos propietarios.</p><p>3. <strong>Monta los webhooks el d&#237;a uno</strong>, no cuando ya has perdido la reputaci&#243;n de tu dominio.</p><p>4. <strong>Desacopla el env&#237;o del request</strong> con una cola o un job. El email es as&#237;ncrono, no importa lo que diga tu intuici&#243;n de backend.</p><p>El email no es sexy. Es la columna vertebral de cualquier producto SaaS &#8212; y la mayor&#237;a de equipos lo monta como si fuera una ocurrencia de &#250;ltima hora.</p><p>Resend te da el marco para montarlo bien desde el principio. La entregabilidad es un commodity que se configura una vez y se olvida.</p><p><strong>*El developer experience es el &#250;nico moat que importa &#8212; porque el tiempo de tu equipo es el &#250;nico coste que el proveedor no puede bajar por ti.</strong>*</p><p>Cada hora que no peleas con un SDK o un template propietario es una hora que dedicas a tu producto. Ese es el verdadero retorno de Resend. Y en 2026, con marcos como Next.js empujando todo hacia frameworks opinados, la apuesta por un proveedor que se alinea con tu stack &#8212; en vez de imponerte el suyo &#8212; es la &#250;nica que no te va a dejar atrapado en 2030.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/resend-email-api-tutorial-2026-20260908?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[El 90% de los Pivots en Servicios Productizados No Son Respuesta al Mercado — Son una Huida]]></title><description><![CDATA[&#191;Toca pivotar tu servicio productizado o es tu impaciencia hablando? Aprende a separar el 10% de pivots leg&#237;timos del 90% que son fugas.]]></description><link>https://newsletter.brianmenagomez.com/p/el-90-de-los-pivots-en-servicios</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/el-90-de-los-pivots-en-servicios</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 08 Sep 2026 07:00:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4a638711-3db2-4290-a32c-ced6d268d601_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>El 90% de los Pivots en Servicios Productizados No Son Respuesta al Mercado &#8212; Son una Huida</strong></h2><p>Crees que pivotar un servicio productizado es un acto de valent&#237;a. Que cuando los clientes se van, el equipo se aburre o el margen aprieta, la respuesta audaz es cambiar de producto, resetear expectativas y empezar de nuevo con la emoci&#243;n del reinicio.</p><p><strong>*Te has equivocado de diagn&#243;stico.</strong>*</p><p>El 90% de los pivots en servicios productizados no son una respuesta al mercado. Son un acto de <strong>cobard&#237;a estrat&#233;gica</strong> &#8212; un nombre bonito para una huida hacia adelante. Y la se&#241;al m&#225;s peligrosa no es la que te grita "cambia de producto". Es la que te susurra que <strong>el aburrimiento de ejecutar lo mismo es un diagn&#243;stico</strong>, cuando en realidad es el precio de haber ganado.</p><p>Llevo cuatro a&#241;os enviando software en Espa&#241;a. He montado gestor&#237;as conectadas, productos para servicios jur&#237;dicos, m&#243;dulos para IRPF. Y he visto docenas de operadores &#8212;agencias digitales, freelancers, consultores t&#233;cnicos&#8212; tirar a la basura ventaja acumulada de a&#241;os de entrega repetible, proceso depurado y posicionamiento ganado, solo porque confundieron su impaciencia con una se&#241;al del mercado.</p><p>El coraje real no est&#225; en cambiar de producto. Est&#225; en <strong>aguantar el aburrimiento de la ejecuci&#243;n repetible</strong> y diagnosticar si el dolor que sientes viene del mercado o de tu propia ansiedad.</p><p>---</p><h3><strong>El pivot como fuga hacia adelante: por qu&#233; es tan atractivo</strong></h3><p>Profundizar en un servicio productizado es trabajo de capitalizaci&#243;n aburrido. Significa repetir, medir, refinar, documentar, sistematizar. No da titulares. No genera dopamina. Es exactamente el tipo de trabajo que nadie celebra.</p><p>Pivotar, en cambio, es el atajo gratificante: novedad, reset de expectativas, la ilusi&#243;n de progreso. Psicol&#243;gicamente, el pivot es <strong>la opci&#243;n gratificante a corto plazo y estrat&#233;gicamente ruinosa a largo plazo</strong>.</p><p>La asimetr&#237;a es brutal. Pivotar regala la ventaja acumulada de a&#241;os de entrega repetible &#8212;el proceso depurado, el posicionamiento ganado, el conocimiento del ICP&#8212; a cambio de la emoci&#243;n de un reinicio. Es como un atleta que cambia de deporte cada temporada porque entrenar lo mismo todos los d&#237;as le aburre. Nunca llega a ser competitivo en nada.</p><p>&#10060; <strong>Lo que el 90% hace:</strong> Nota dolor &#8594; Interpreta como se&#241;al de mercado &#8594; Pivota r&#225;pido para sentirse valiente.</p><p>&#9989; <strong>Lo que el 10% leg&#237;timo hace:</strong> Nota dolor &#8594; Etiqueta el s&#237;ntoma con una taxonom&#237;a &#8594; Aplica test de amplitud &#8594; Revisa posicionamiento &#8594; Prueba profundizaci&#243;n &#8594; Solo entonces decide.</p><p>---</p><h3><strong>La taxonom&#237;a del ruido operativo: aprende a etiquetar tu s&#237;ntoma</strong></h3><p>Antes de decidir nada, necesitas saber qu&#233; est&#225;s sintiendo. El ruido operativo en servicios productizados tiene una taxonom&#237;a reconocible, y casi todo lo que los operadores llaman "se&#241;al de mercado" cae en una de estas cuatro casillas:</p><p><strong>1. Churn de un pu&#241;ado de clientes.</strong> Perder dos o tres clientes en un mes te duele. No te dice nada sobre el mercado. Los clientes se van por decenas de razones que no tienen que ver con tu producto: presupuesto interno, cambio de prioridades, un decisor nuevo. Perder dos clientes no es se&#241;al. Es estad&#237;stica.</p><p><strong>2. Fatiga de entregables.</strong> "Estoy harto de entregar lo mismo cada mes". Traducci&#243;n real: has automatizado mal, no has delegado, o no has subido de nivel a tus clientes. La fatiga no prueba que el producto haya tocado techo. Prueba que <strong>tu modelo de entrega tiene un agujero</strong> &#8212; y el agujero se tapa, no se esquiva.</p><p><strong>3. Presi&#243;n de margen por alcance mal controlado.</strong> Si tu margen aprieta porque un cliente te ha ido comiendo alcance sin pagar m&#225;s, el problema no es el producto. Es tu control de scope. Pivotar no te salva de un cliente que no sabes gestionar: el siguiente te har&#225; lo mismo.</p><p><strong>4. Aburrimiento del equipo.</strong> El argumento favorito: "si el equipo est&#225; aburrido de entregar lo mismo, la calidad se resiente". Falso en el 90% de los casos. El aburrimiento suele ser un problema de <strong>delegaci&#243;n, staffing o variedad de retos dentro del mismo producto</strong> &#8212; subir de nivel a clientes, ampliar entregables, automatizar lo repetible. Confundir aburrimiento con l&#237;mite del producto es precisamente el error que el 90% comete.</p><p>Ninguno de estos cuatro s&#237;ntomas es evidencia de que el mercado quiera otro producto. Son s&#237;ntomas del modelo de entrega: ICP equivocado, sin control de scope, delegaci&#243;n insuficiente.</p><p>---</p><h3><strong>La conexi&#243;n subversiva: tu posici&#243;n est&#225; rota, no tu producto</strong></h3><p>Aqu&#237; est&#225; el punto m&#225;s inc&#243;modo. Muchos "momentos de pivot" son en realidad el resultado de <strong>a&#241;os de posicionamiento vago o sobregeneralizado</strong>.</p><p>El s&#237;ntoma cl&#225;sico: ganas clientes que te compran una cosa pero creen que les vendes otra. El proyecto llega, las expectativas no cuadran, el cliente se frustra, t&#250; te frustras, y en tres meses est&#225;s convencido de que "este producto no funciona".</p><p><strong>*No es el producto. Es tu posici&#243;n.</strong>*</p><p>Si tu oferta es lo suficientemente difusa como para atraer a clientes que te compran por una raz&#243;n equivocada, el problema nunca fue el delivery. Fue que aceptaste el cliente equivocado porque tu posicionamiento no filtraba.</p><p>La correcci&#243;n no est&#225; en pivotar. Est&#225; en <strong>estrechar, aclarar o reposicionar la oferta</strong>. Escribir un posicionamiento m&#225;s espec&#237;fico, definido el ICP con m&#225;s precisi&#243;n, y dejar que la oferta rechace a los clientes equivocados antes de que te compren.</p><p>Es la jugada barata que nadie quiere hacer. Porque reposicionar no da titulares, no genera la emoci&#243;n de "empezar algo nuevo". Pero suele disolver la falsa se&#241;al sin necesidad de tocar el producto.</p><p>---</p><h3><strong>El Framework de las 5 Paradas: c&#243;mo decidir si toca pivotar</strong></h3><p>Aqu&#237; tienes el mecanismo. Cinco paradas obligatorias antes de permitirte pronuncar la palabra pivot. Si superas las cinco con una se&#241;al intacta, tienes derecho al 10% leg&#237;timo. Si mueres en cualquiera de ellas, tu pivot era impulsivo.</p><h4><strong>Parada 1 &#8212; Nombra el gatillo emocional antes de tocar datos.</strong></h4><p>Escribe, en un fichero, qu&#233; emoci&#243;n inici&#243; el impulso de pivotar. &#191;Aburrimiento? &#191;Frustraci&#243;n con un cliente concreto? &#191;Miedo a quedarte obsoleto? &#191;Comparaci&#243;n con un competidor?</p><p>Si el disparador es puramente emocional &#8212;y te sorprender&#225; cu&#225;ntas veces lo es&#8212;, el pivot ya parte con sospecha de ser falso. Est&#225;s tratando una emoci&#243;n con una decisi&#243;n estrat&#233;gica, y eso nunca funciona.</p><h4><strong>Parada 2 &#8212; Aplica la prueba del ruido (amplitude test).</strong></h4><p>&#191;La se&#241;al aparece en todos tus clientes, verticales y tipolog&#237;as de proyecto, o est&#225; concentrada en uno o dos casos concretos?</p><p>Regla clara: <strong>se&#241;al concentrada = ruido operativo</strong>. Es un cliente equivocado, un alcance mal gestionado, un segmento concreto que nunca debiste aceptar. <strong>Se&#241;al sist&#233;mica = candidata a estrat&#233;gica</strong>. Si el dolor se reproduce en m&#250;ltiples segmentos de tu cartera, sostenido en el tiempo, empieza a ser interesante.</p><h4><strong>Parada 3 &#8212; Relee tu posicionamiento antes de tocar el producto.</strong></h4><p>Si ganas clientes que te compran una cosa y entregas otra, o viceversa, el problema es de posicionamiento, no de producto. Reescribir el posicionamiento &#8212;con un ICP m&#225;s estrecho, una promesa m&#225;s concreta, un filtro m&#225;s agresivo&#8212; suele disolver la se&#241;al falsa sin pivotar.</p><p>No toques una l&#237;nea de tu oferta hasta que hayas reposicionado y dado tiempo a que el nuevo filtro cambie la calidad de las oportunidades entrantes.</p><h4><strong>Parada 4 &#8212; El test de profundizaci&#243;n.</strong></h4><p>Preg&#250;ntate si la soluci&#243;n al dolor es <strong>a&#241;adir profundidad a la oferta actual</strong> en lugar de crear una oferta nueva. Profundidad significa: un nuevo entregable, un nuevo segmento del mismo ICP, una nueva fase del journey del cliente, un nivel de servicio superior.</p><p>Si existe una respuesta de profundidad cre&#237;ble, el pivot no toca. Solo tienes que ejecutar la profundizaci&#243;n.</p><h4><strong>Parada 5 &#8212; Periodo de enfriamiento + hip&#243;tesis expl&#237;cita.</strong></h4><p>Aplaza la decisi&#243;n 30 d&#237;as. Y durante ese periodo, escribe la hip&#243;tesis falsable que confirmar&#237;a el pivot:</p><p>&gt; "Si en ocho semanas el churn del segmento Y supera el umbral Z sin causa externa identificable, pivotamos."</p><p>Escribe la hip&#243;tesis con m&#233;tricas concretas. Luego espera. La mayor&#237;a de los impulsos de pivot <strong>no sobreviven a su propia hip&#243;tesis escrita</strong>. Cuando el impulso era emocional, la hip&#243;tesis se revela como lo que es: una justificaci&#243;n buscando datos.</p><p>---</p><h3><strong>Respondiendo a tus objeciones &#8212; porque ya s&#233; lo que est&#225;s pensando</strong></h3><p><strong>"Mi pivot me salv&#243; el negocio &#8212; &#191;dices que todos los pivots son malos?"</strong></p><p>No. El 90% no es el 100%. El 10% leg&#237;timo existe: se&#241;al sist&#233;mica, sostenida en el tiempo, con hip&#243;tesis falsable y causa ex&#243;gena verificable. Este framework no existe para prohibir pivotar. Existe para <strong>hacerte ganar el derecho</strong> a hacerlo, demostrando que no est&#225;s huyendo.</p><p><strong>"El churn y la ca&#237;da de negocio S&#205; son se&#241;al estrat&#233;gica."</strong></p><p>V&#225;lido, pero exige umbrales. Una ca&#237;da sostenida en <strong>m&#250;ltiples segmentos</strong>, con causa ex&#243;gena verificable &#8212;cambio regulatorio, desaparici&#243;n de un canal, un competidor que entr&#243; con otra estructura de precios&#8212; es estrat&#233;gica. Perder dos clientes en un mes, o un segmento concreto mal gestionado, es ruido. La distinci&#243;n no es arbitraria: se mide en amplitud y duraci&#243;n.</p><p><strong>"Si el equipo est&#225; aburrido, eso es se&#241;al real: el aburrimiento mata la calidad."</strong></p><p>El aburrimiento mata la calidad solo cuando el operador confunde el s&#237;ntoma con la causa. Un equipo aburrido es un problema de retos, delegaci&#243;n y automatizaci&#243;n resoluble dentro del mismo producto. Que un equipo se aburra no prueba que el mercado se haya aburrido tambi&#233;n. Esa es la confusi&#243;n m&#225;s cara de todo el ecosistema.</p><p>---</p><h3><strong>La honestidad metodol&#243;gica que este art&#237;culo te debe</strong></h3><p>Un apunte necesario: el "90%" que da t&#237;tulo a este art&#237;culo no es una estad&#237;stica de un estudio publicado. Es una cifra estimativa con valor diagn&#243;stico, construida sobre la experiencia de operadores del sector. &#218;sala como lo que es: <strong>un heur&#237;stico para inclinar la presunci&#243;n en contra del pivot</strong>. No como un dato medido que puedas citar en una reuni&#243;n de inversores. La honestidad es parte del oficio &#8212; y el propio hecho de que el sector fabrique m&#233;tricas falsas para justificar pivots es precisamente el vicio que este framework combate.</p><p>---</p><h3><strong>Lo que esto significa para ti, hoy</strong></h3><p>Si has llegado hasta aqu&#237; con el impulso de pivotar activo, tu siguiente paso no es decidir. Es aplicar las cinco paradas. Nombrar la emoci&#243;n. Medir la amplitud. Releer tu posicionamiento. Buscar la opci&#243;n de profundidad. Escribir tu hip&#243;tesis falsable y esperar 30 d&#237;as.</p><p>Probablemente descubrir&#225;s lo que el 90% descubre demasiado tarde: <strong>el problema no era tu producto. Era tu negativa a profundizar en &#233;l.</strong> Y la ventaja que estabas a punto de regalar es exactamente la que el operador paciente va a capitalizar.</p><p>El pivot no es la respuesta valiente. Es la salida f&#225;cil disfrazada de coraje. El coraje real est&#225; en sentarte, aguantar el aburrimiento de capitalizar lo que ya construiste, y preguntarte la &#250;nica pregunta que importa: <strong>&#191;qu&#233; profundidad le falta a esto que ya funciona?</strong></p><p>Cuando respondas esa pregunta, no necesitar&#225;s pivotar. Necesitar&#225;s excavar.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/90-por-ciento-pivots-servicios-productizados-son-huida-20260908?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Un core, dos vistas: KYB para bancos y onboarding para SaaS sin duplicar lógica]]></title><description><![CDATA[C&#243;mo una &#250;nica resoluci&#243;n de actividad a perfil fiscal sirve dos proyecciones distintas &#8212; KYB para un banco regional y onboarding para un SaaS &#8212; sin duplicar l&#243;gica, y el build roto que me ense&#241;&#243; d...]]></description><link>https://newsletter.brianmenagomez.com/p/un-core-dos-vistas-kyb-para-bancos</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/un-core-dos-vistas-kyb-para-bancos</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 08 Sep 2026 05:34:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7nbD!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb9dcf7f-ea19-48c6-9eb8-91e45dd4b8eb_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Este ensayo se publico primero en <a href="https://brianmenagomez.com/blog/un-core-dos-vistas-kyb-y-onboarding-sin-duplicar-logica">brianmenagomez.com/blog/un-core-dos-vistas-kyb-y-onboarding-sin-duplicar-logica</a>.</p><h1>Un core, dos vistas: KYB para bancos y onboarding para SaaS sin duplicar l&#243;gica</h1><h2>El mismo dato, dos respuestas distintas</h2><p>Cuando una empresa se da de alta, hay una &#250;nica pregunta de fondo que arrastra a todas las dem&#225;s: qu&#233; hace exactamente esa actividad y qu&#233; obligaciones fiscales genera. Lo que descubr&#237; construyendo Conversor es que esa pregunta tiene al menos dos interlocutores que no se hablan entre s&#237; y que esperan respuestas con formas completamente distintas.</p><p>Por un lado, un banco regional espa&#241;ol. Cuando abre una cuenta a una empresa necesita hacer su verificaci&#243;n de negocio, lo que se llama KYB. No le interesa qu&#233; modelos tiene que presentar el aut&#243;nomo en mayo. Quiere saber en qu&#233; sector de riesgo cae la actividad, de d&#243;nde sali&#243; ese dato y si la entidad reside en la UE.</p><p>Por el otro, un SaaS fiscal. Ese s&#237; quiere exactamente lo otro: qu&#233; declaraci&#243;n de alta le toca, qu&#233; pasa con el IVA y la retenci&#243;n, qu&#233; r&#233;gimen le conviene, el calendario de obligaciones y la cuota RETA.</p><p>Son dos respuestas que casi no comparten un solo campo. Y sin embargo salen de la misma operaci&#243;n de fondo: resolver una actividad econ&#243;mica en un perfil fiscal completo.</p><h2>Mi primer impulso: dos endpoints, dos l&#243;gicas</h2><p>Lo razonable, cuando dos clientes tan distintos te piden cosas tan distintas, es construirlo dos veces. Un endpoint para el banco, otro para el SaaS, cada uno con su propio c&#243;digo. Al fin y al cabo las respuestas no se parecen en nada: qu&#233; sentido tiene unificar lo que no comparte estructura.</p><p>Me parec&#237;a la decisi&#243;n honesta. Si el banco necesita un sector de riesgo y un audit de fuentes, y el SaaS necesita la declaraci&#243;n de alta y un calendario, montar una capa com&#250;n era forzar una abstracci&#243;n antes de tiempo. Y las abstracciones prematuras son el cl&#225;sico error que uno comete por no querer escribir dos veces la misma l&#237;nea.</p><p>As&#237; que empec&#233; a escribir los dos endpoints por separado. Y fue entonces cuando apareci&#243; el error que me oblig&#243; a replantearlo todo.</p><h2>El error: helpers en route.ts y un build que tsc no vio</h2><p>Por comodidad, met&#237; los helpers de proyecci&#243;n directamente en el fichero de la ruta, `route.ts`, y los export&#233; desde ah&#237;. Quer&#237;a reutilizar una funci&#243;n de mapeo en el otro endpoint o en un test, y me parec&#237;a lo natural: est&#225;n en el mismo fichero, por qu&#233; no exportarlas.</p><p>El chequeo de tipos no dijo nada. `tsc` pasaba limpio. Todo compilaba en local. Y aun as&#237;, el build de Vercel se rompi&#243;.</p><p>La raz&#243;n es una de esas restricciones de framework que no est&#225;n en el compilador: Next.js App Router rechaza los exports que no son de Route en `route.ts`. El fichero de una ruta solo puede exportar lo que el router espera. Si exportas cualquier otra cosa, el build falla aunque TypeScript est&#233; satisfecho.</p><p>Fue un error que me cost&#243; un buen rato localizar porque la se&#241;al ven&#237;a del sitio equivocado. El compilador dec&#237;a "bien", el deploy dec&#237;a "no". Y el mensaje de error no apuntaba a mi funci&#243;n: apuntaba a una regla de convenci&#243;n que yo no conoc&#237;a.</p><h2>La soluci&#243;n: un core, dos vistas</h2><p>La correcci&#243;n me ense&#241;&#243; dos cosas a la vez, y las dos apuntaban al mismo sitio: `lib/`.</p><p>Primero, los helpers de proyecci&#243;n tienen que vivir en `lib/`, nunca exportarse desde `route.ts`. Es la regla que ahora respeto sin discutir. En `lib/api/activity-profile.ts` est&#225; el core: `resolveActivity` resuelve la actividad y `assembleDossier` monta el perfil fiscal completo.</p><p>Segundo, y esto es lo importante: una vez que el core vive fuera de las rutas, las dos vistas dejan de ser dos l&#243;gicas duplicadas y pasan a ser dos proyecciones delgadas sobre el mismo n&#250;cleo.</p><p>`/api/v1/kyb/enrich` es la vista del banco: devuelve `fiscal_profile.sector_riesgo` y lo acompa&#241;a de un audit con las fuentes, el `data_version` y la residencia UE. El banco no tiene que confiar en un n&#250;mero suelto; tiene de d&#243;nde sali&#243; y cu&#225;ndo se gener&#243;.</p><p>`/api/v1/onboarding/activity` es la vista del SaaS: devuelve el `alta_036`, el IVA y la retenci&#243;n, el r&#233;gimen recomendado, el calendario y la RETA. Es la respuesta que un producto de gesti&#243;n necesita para arrancar el alta sin montar su propia capa de datos fiscales.</p><p>Las dos respuestas siguen sin parecerse en casi nada. Pero ahora comparten un &#250;nico punto donde se resuelve la verdad fiscal. Si un d&#237;a tengo que cambiar c&#243;mo se mapea un ep&#237;grafe del IAE, lo cambio una vez, en el core, y las dos vistas se corrigen solas.</p><h2>Lo que aprend&#237;</h2><p>La lecci&#243;n no es "no dupliques c&#243;digo", que es un consejo barato. Es m&#225;s concreta. La duplicaci&#243;n aqu&#237; no era solo un coste de mantenimiento: era un coste de correcci&#243;n. Dos copias de "actividad a perfil fiscal" acaban divergiendo, y el d&#237;a que divergen, el banco y el SaaS te devuelven respuestas distintas para la misma actividad. Eso no es un refactor pendiente, es un dato fiscal que deja de ser fiable.</p><p>Y la segunda lecci&#243;n: el compilador no es el &#250;nico contrato que hay que respetar. Next.js ten&#237;a una regla de convenci&#243;n que TypeScript no pod&#237;a ver, y me la explic&#243; a base de romper el deploy. Desde entonces trato las restricciones del framework como parte del tipo: no basta con que `tsc` pase, el fichero de una ruta tiene su propio contrato de qu&#233; puede exportar.</p><p>El core resuelve una vez, las vistas proyectan y los helpers viven en `lib/`. Eso es todo, y me cost&#243; un build roto aprenderlo.</p><p>Si quieres ver los dos endpoints en detalle, los docs est&#225;n abiertos: https://www.conversoriaecnae.es/api/v1/docs</p>]]></content:encoded></item><item><title><![CDATA[Supabase vs Firebase 2026: Dejad de Llamarlo "Firebase para Postgres". Es un Motor de 35 Años que se Comió Todo su Feature Set]]></title><description><![CDATA[Supabase vs Firebase 2026: RLS, logical replication y pgvector convierten a PostgreSQL en tu capa completa de app. Sal t&#250; con un pg_dump, no con un rewrite.]]></description><link>https://newsletter.brianmenagomez.com/p/supabase-vs-firebase-2026-dejad-de</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/supabase-vs-firebase-2026-dejad-de</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Mon, 07 Sep 2026 07:00:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/42fce361-77ea-4f0c-b4df-bbfebfe2549b_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Dejad de Llamar a Supabase "Firebase para Postgres". Es al Rev&#233;s: un Motor de 35 A&#241;os que Absorbi&#243; Todo el Feature Set de Firebase</strong></h2><p>Cada vez que escucho "Supabase es el Firebase de Postgres", muero un poco.</p><p>Es la comparaci&#243;n al rev&#233;s. La direcci&#243;n real es la inversa: MySQL rein&#243; en la web de los 2000, y luego Firebase convenci&#243; a una generaci&#243;n de que los documentos NoSQL eran el futuro. Pero PostgreSQL &#8212;ese motor "aburrido" de 35 a&#241;os&#8212; ha absorbido en silencio <em>todo</em> lo que Firebase hac&#237;a especial: auth, realtime, storage, incluso b&#250;squeda vectorial para IA.</p><p>Y la prueba no est&#225; en el marketing de Supabase.</p><p>Est&#225; en que si quitas PostgreSQL de debajo de cualquier feature "estrella" de Supabase, no queda nada. Quitas Postgres y no hay auth. No hay realtime. No hay vector search. El ce&#241;o fruncido de la comparaci&#243;n se apoya entero en una columna vertebral que es SQL puro y portable.</p><p><strong>*El riesgo que cargas con Supabase no es el lock-in. Es lo contrario: que todo tu miedo va dirigido en la direcci&#243;n equivocada.</strong> *</p><p>Empecemos por lo que la mayor&#237;a entiende mal.</p><h2><strong>El Argumento Convencional es una Falsa Dicotom&#237;a</strong></h2><p>El debate t&#237;pico en 2026 se plantea as&#237;:</p><p>&#10060; <em>"&#191;Quieres velocidad para el MVP? Usa Firebase o Supabase. &#191;Quieres un backend serio para producci&#243;n? Monta algo propio."</em></p><p>&#10060; <em>"Supabase vale para hackathons. Nadie ejecuta producci&#243;n sobre ella."</em></p><p>Ambos bandos fallan igual. Los dos tratan Supabase como un atajo del que te grad&#250;as. Los dos ignoran que la premisa es exactamente la inversa.</p><p>&#9989; <em>Supabase no es la opci&#243;n f&#225;cil de la que te vas. Es el argumento de que PostgreSQL es el sitio de mayor apalancamiento para poner auth, realtime y l&#243;gica de IA.</em></p><p>Vamos a desmontarlo con evidencia, no con opiniones.</p><h2><strong>La Arquitectura Revela D&#243;nde Est&#225; el Valor Real</strong></h2><p>Mira qu&#233; pasa bajo el cap&#243; cuando usas Supabase:</p><ul><li><p><strong>Auth (GoTrue)</strong> emite JWTs. Pero la *autorizaci&#243;n<em> no vive en un middleware. Vive en pol&#237;ticas de Row-Level Security</em>* escritas en SQL directamente en la base de datos.</p></li><li><p><strong>Realtime</strong> no es un websocket con un event-bus casero por encima. Se construye sobre <strong>logical replication</strong> de Postgres. Un mensaje de chat es literalmente una fila commiteada.</p></li><li><p><strong>AI/vector search</strong> no requiere una base de datos vectorial aparte. Es una extensi&#243;n (<strong>pgvector</strong>) y una columna con una query de similitud coseno escrita en SQL plano.</p></li><li><p><strong>Storage, Edge Functions (Deno), PostgREST</strong>... todos son wrappers finos alrededor de Postgres y un stack open source bajo licencia Apache 2.0.</p></li></ul><p>Cada feature de Supabase que te parece "magia" es una primitiva de Postgres con gabardina.</p><p>Eso tiene consecuencias que casi nadie comenta.</p><h3><strong>La Seguridad la Decide la Base de Datos, No tu API</strong></h3><p>Vengo de a&#241;os escribiendo middlewares en Node/Express. Mi primer impulso fue proteger la base de datos <em>desde</em> la API. Supabase te obliga a un reset mental: <strong>*no est&#225;s protegiendo la base de datos de la API. La base de datos se protege a s&#237; misma</strong>*.</p><p>Mira una pol&#237;tica de RLS t&#237;pica:</p><p>```sql</p><p>-- Creas una tabla de perfiles cuyo due&#241;o es el usuario autenticado</p><p>CREATE TABLE profiles (</p><p>id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,</p><p>username TEXT UNIQUE NOT NULL,</p><p>avatar_url TEXT</p><p>);</p><p>-- Activas RLS ANTES de escribir tu primera l&#237;nea de app code</p><p>ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;</p><p>El `auth.uid()` lo sustituye Postgres por el ID del JWT que firm&#243; GoTrue. Esas pol&#237;ticas se ejecutan tanto si el cliente es tu web, tu app m&#243;vil <em>o una conexi&#243;n directa de psql</em> a la base. Un solo sitio decide seguridad, y se aplica uniformemente en cada superficie.</p><p>Esta es la demo de un <em>signup</em> con auth y un trigger que crea la fila de perfil autom&#225;ticamente, para que identidad y datos de usuario jam&#225;s deriven:</p><p>```sql</p><p>-- Trigger que crea el perfil al registrarse</p><p>CREATE OR REPLACE FUNCTION public.handle_new_user()</p><p>RETURNS TRIGGER AS $$</p><p>BEGIN</p><p>INSERT INTO public.profiles (id, username)</p><p>VALUES (new.id, split_part(new.email, '@', 1));</p><p>RETURN new;</p><p>END;</p><p>$$ LANGUAGE plpgsql SECURITY DEFINER;</p><p>CREATE TRIGGER on_auth_user_created</p><p>AFTER INSERT ON auth.users</p><p>FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();</p><p>```</p><p>El "modelo de seguridad" de tu app es SQL que vive en una migraci&#243;n versionada. No es middleware que puedas esquivar con una petici&#243;n directa a la base.</p><h2><strong>Realtime es la Prueba de fuego de que Entiendes la Plataforma</strong></h2><p>El realtime de Supabase te ense&#241;a m&#225;s de Postgres que cualquier tutorial.</p><p>Se construye sobre logical replication y cambios en el WAL. Cuando ves un mensaje nuevo en tu chat, no es un evento de un broker independiente: <strong>es una fila que Postgres commite&#243;</strong>. Eso tiene dos consecuencias pr&#225;cticas.</p><p>Una, buena: los eventos realtime son transaccionales. No recibes eventos de datos que no llegaron a commitearse. Es m&#225;s robusto que una capa casera de WebSocket + event-bus donde un mensaje puede perderse entre el write y el publish.</p><p>```js</p><p>// Suscripci&#243;n a cambios reales de la base, no a un mensajero artificial</p><p>const channel = supabase</p><p>.channel('todos-changes')</p><p>.on('postgres_changes', {</p><p>event: 'INSERT',</p><p>schema: 'public',</p><p>table: 'todos'</p><p>}, (payload) =&gt; {</p><p>console.log('Fila commiteada:', payload.new);</p><p>})</p><p>.subscribe();</p><p>```</p><p>Dos, la que casi nadie discute: <strong>pagas por cada write que replicas</strong>. Una suscripci&#243;n ingenua sobre una tabla caliente multiplica la carga en el WAL y en los canales de replica. El gotcha cl&#225;sico de escalado es ver que el realtime "va lento" y no entender que *tu query INSERT* es el cuello de botella. Suscr&#237;bete solo a las tablas y eventos que la interfaz necesita. Nada m&#225;s.</p><h2><strong>El SDK es un Cliente Fino sobre Sem&#225;ntica SQL &#8212; y Eso es tu Mejor Amigo (o tu Peor Pesadilla)</strong></h2><p>Aqu&#237; est&#225; la comparaci&#243;n que casi ning&#250;n an&#225;lisis superficial hace.</p><p>Firebase te obliga a adoptar su modelo de documentos NoSQL desde el d&#237;a uno. Su SDK <em>es</em> el modelo mental. Nunca escribes una query SQL. Nunca haces un JOIN. Supabase es lo contrario: su SDK es un cliente fino que envuelve una API REST (PostgREST) que a su vez traduce a SQL. Y puedes verlo:</p><p>```js</p><p>// supabase-js: operaci&#243;n CRUD</p><p>const { data, error } = await supabase</p><p>.from('todos')</p><p>.insert({ task: 'Aprender RLS', user_id: userId })</p><p>.select();</p><p>// Lo que ocurre por debajo: una petici&#243;n REST a PostgREST</p><p>// POST /rest/v1/todos</p><p>// Body: { "task": "Aprender RLS", "user_id": "..." }</p><p>// Headers: { "Content-Type": "application/json", "Authorization": "Bearer &lt;JWT&gt;" }</p><p>```</p><p>Demistifica la "magia": un `.insert().select()` es una petici&#243;n HTTP a un endpoint auto-generado que aplica tus pol&#237;ticas RLS antes de tocar una fila.</p><p>El SDK de Supabase es una capa fina sobre sem&#225;ntica SQL. Eso lo convierte en la elecci&#243;n perfecta si ya piensas en t&#233;rminos relacionales, y en la peor elecci&#243;n posible si nunca quieres escribir una cl&#225;usula WHERE. Un art&#237;culo honesto tiene que decirlo: el marco de "Firebase killer" halaga a Supabase mientras oculta sus trade-offs ergon&#243;micos reales.</p><h2><strong>pgvector Colapsa Toda una Clase de Decisiones de "Infraestructura IA"</strong></h2><p>Para productos en fase temprana, pgvector elimina la necesidad de una base de datos vectorial aparte.</p><p>A&#241;ades una extensi&#243;n, una columna, y lanzas una query de similitud con JOINs SQL normales hacia tus filas relacionales:</p><p>```sql</p><p>-- Activas la extensi&#243;n y creas la columna de embeddings</p><p>CREATE EXTENSION IF NOT EXISTS vector;</p><p>ALTER TABLE documents ADD COLUMN embedding vector(1536);</p><p>-- B&#250;squeda h&#237;brida: los documentos m&#225;s cercanos de este usuario, en este workspace</p><p>SELECT d.id, d.title,</p><p>d.embedding &lt;=&gt; $1 AS distancia</p><p>FROM documents d</p><p>JOIN workspaces w ON w.id = d.workspace_id</p><p>WHERE d.owner_id = auth.uid()</p><p>ORDER BY d.embedding &lt;=&gt; $1</p><p>LIMIT 10;</p><p>```</p><p>El gancho pr&#225;ctico: queries h&#237;bridas tipo "los documentos m&#225;s cercanos de este usuario, filtrados por su workspace" son dolorosas entre dos sistemas distintos y triviales dentro de Postgres.</p><p>Pero anota el matiz: a muy gran escala, los motores vectoriales dedicados tienen perfiles de rendimiento distintos. Esto es una <strong>simplificaci&#243;n arquitect&#243;nica</strong>, no una verdad universal. Para tu producto en fase temprana, es la decisi&#243;n correcta.</p><p>Para mover la generaci&#243;n de embeddings fuera del navegador, s&#225;cala a una Edge Function de Deno:</p><p>```ts</p><p>// Edge Function (Deno): genera y guarda el embedding sin exponer secretos</p><p>Deno.serve(async (req) =&gt; {</p><p>const { content, docId } = await req.json();</p><p>const res = await fetch("https://api.openai.com/v1/embeddings", {</p><p>method: "POST",</p><p>headers: {</p><p>"Content-Type": "application/json",</p><p>"Authorization": `Bearer ${Deno.env.get("OPENAI_API_KEY")}`,</p><p>},</p><p>body: JSON.stringify({ model: "text-embedding-3-small", input: content }),</p><p>});</p><p>const { data } = await res.json();</p><p>await supabase.from("documents")</p><p>.update({ embedding: data[0].embedding })</p><p>.eq("id", docId);</p><p>return new Response("ok", { status: 200 });</p><p>});</p><p>```</p><p>Secretos y compute pesado nunca llegan al navegador. Es Postgres puro por debajo.</p><h2><strong>El Framework que Propongo: El Patr&#243;n Schema-First con RLS como Fuente de Verdad</strong></h2><p>Aqu&#237; est&#225; el m&#233;todo que aplico en cada build que hago con Supabase. Lo llamo <strong>El Patr&#243;n Schema-First con RLS como Fuente de Verdad</strong>, y funciona as&#237;:</p><p><strong>Paso 1 &#8212; Dise&#241;a el esquema en SQL puro ANTES de tocar tu app code.</strong></p><p>Tablas, constraints, &#237;ndices, claves for&#225;neas. La carpeta de migraciones es la fuente de verdad. Cada feature de Supabase cuelga de la base de datos, as&#237; que si el esquema est&#225; mal, todo lo dem&#225;s se arrastra mal.</p><p><strong>Paso 2 &#8212; Activa ROW LEVEL SECURITY en cada tabla y escribe pol&#237;ticas expl&#237;citas con `USING` y `WITH CHECK`.</strong></p><p>Referencia `auth.uid()` directamente. Verifica con un rol que <em>no sea el owner</em> antes de construir cualquier interfaz. Si no puedes leer tu propia fila con un rol distinto al due&#241;o, no toques una sola l&#237;nea de React.</p><p><strong>Paso 3 &#8212; Conecta Auth al esquema con triggers.</strong></p><p>Crea la fila de profile en el signup (como en el snippet de arriba) para que identidad y datos de usuario jam&#225;s deriven. Nunca sincronices a mano desde el cliente.</p><p><strong>Paso 4 &#8212; Adopta realtime de forma selectiva.</strong></p><p>Suscr&#237;bete solo a las tablas y eventos que la UI necesita. Ap&#243;yate en los change data de Postgres, no en polling del cliente. Recuerda: cada replicaci&#243;n cuesta, as&#237; que no repliques tablas calientes por costumbre.</p><p><strong>Paso 5 &#8212; Para features de IA, empieza con una columna pgvector.</strong></p><p>Guarda los embeddings junto a las filas fuente. Mueve la generaci&#243;n de embeddings a una Edge Function (Deno) para que los secretos y el compute pesado jam&#225;s lleguen al navegador.</p><h2><strong>El Lock-In no Existe. Pero las Respuestas Honestas a tus Objeciones S&#237;</strong></h2><p>Vamos con las tres objeciones que s&#233; que est&#225;is pensando.</p><p><strong>"Supabase vale para un hackathon, pero nadie ejecuta producci&#243;n sobre ella."</strong></p><p>El core es PostgreSQL gestionado con tooling est&#225;ndar &#8212; pg_dump, psql, migraciones versionadas. El stack open source (Postgres, GoTrue, Realtime, Storage, PostgREST, Edge Functions) es autohospedable y se usa en producci&#243;n. Lo que s&#237; debes dimensionar honestamente son los techos operativos del servicio gestionado: l&#237;mites de conexiones, multi-regi&#243;n y fan-out de realtime muy grande. Ah&#237;, tu opci&#243;n de salida ya est&#225; definida desde el d&#237;a cero.</p><p><strong>"RLS es listo, pero mi equipo no piensa en SQL, y acoplar auth a pol&#237;ticas de base de datos es r&#237;gido."</strong></p><p>La curva de aprendizaje es real. Pero existe un workflow de depuraci&#243;n: ejecuta tus queries como el rol autenticado y ve qu&#233; devuelve. Y las pol&#237;ticas pueden saltarse deliberadamente con flujos service-role para operaciones server-side. El modelo no es tan inflexible como parece a primera vista.</p><p><strong>"Si Supabase es solo Postgres, &#191;por qu&#233; no gestiono Postgres yo mismo con un backend de Node peque&#241;o?"</strong></p><p>Concesi&#243;n total &#8212; y que sea la &#250;ltima jugada del art&#237;culo. El valor de Supabase no es un motor propietario: es un <strong>bundle mantenido de features de Postgres dif&#237;ciles de operar</strong> (realtime, storage, auth, edge compute). La respuesta honesta es que, en el momento en que tu carga operativa supere tu velocidad de features, el exit con Postgres est&#225;ndar es exactamente por qu&#233; la elecci&#243;n fue segura.</p><p><strong>*Empiezas en Postgres. Supabase solo pasa a ser el wrapper mejor mantenido que existe alrededor de &#233;l.</strong> *</p><h2><strong>El Veredicto Final</strong></h2><p>La mejor llamada de Supabase no es su tecnolog&#237;a. Es la honestidad de su apuesta: no venderte un motor propietario bonito, sino decirte que el motor m&#225;s aburrido y probado de la industria es ahora <strong>el sitio de mayor apalancamiento</strong> para tu auth, tu realtime y tu l&#243;gica de IA.</p><p>El 90% del miedo a Supabase es miedo al lock-in dirigido hacia el sitio equivocado. No est&#225;s atado a un ecosistema cerrado; est&#225;s escribiendo SQL portable sobre un motor de 35 a&#241;os con una comunidad imposible de ignorar.</p><p>Cuando encuentres el techo operativo del servicio gestionado &#8212;y lo encontrar&#225;s&#8212;, tu salida es un volcado est&#225;ndar, no un rewrite de diez semanas.</p><p>La pregunta en 2026 no es "&#191;Firebase o Supabase?".</p><p>Es "&#191;cu&#225;ndo vais a dejar de tratar Postgres como la base de datos y empezar a tratarla como la capa de aplicaci&#243;n que ya es?"</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/supabase-vs-firebase-2026-postgresql-absorbe-todo-20260907?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[La Mejor Llamada a la API es la Última, no la Primera: Por Qué "Late API" es la Twitter API Alternative 2026]]></title><description><![CDATA[Descubre por qu&#233; el patr&#243;n Late API &#8212;202 Accepted, staleness expl&#237;cita y fetch diferido&#8212; es la alternativa real a las APIs cl&#225;sicas en 2026. Con c&#243;digo.]]></description><link>https://newsletter.brianmenagomez.com/p/la-mejor-llamada-a-la-api-es-la-ultima</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/la-mejor-llamada-a-la-api-es-la-ultima</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Sun, 06 Sep 2026 07:00:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/bfa70e40-5ecf-4e8d-bccf-59cae253eb0a_1080x775.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Tu Frontend Dispara una Docena de Llamadas al Cargar que Nadie ha Pedido Ver</strong></h2><p>Y tu backend bloquea durante segundos para devolverte un 200 cuando un 202 habr&#237;a hecho el trabajo.</p><p>Llevamos una d&#233;cada optimizando APIs para que respondan temprano. El instinto contrario &#8212;tard&#237;o, perezoso, diferido&#8212; es el que de verdad escala.</p><p><strong>*La mejor llamada a la API no es la que haces primero. Es la que haces el &#250;ltimo.</strong> *</p><p>No hablo de una librer&#237;a. Hablo de un patr&#243;n de dise&#241;o que atraviesa todo tu stack: el cliente que deja de refetchar por accidente, el backend que responde 202 en vez de bloquear, y el protocolo que formaliza la lentitud como una caracter&#237;stica.</p><p>Y s&#237;, en 2026 este patr&#243;n se est&#225; convirtiendo en la alternativa real a las APIs de redes sociales como X &#8212; porque cuando el feed que consumes se genera bajo demanda en servidores que no controlas, tratar cada petici&#243;n como un fetch s&#237;ncrono y eager es la forma m&#225;s r&#225;pida de quemarte en rate limits.</p><h2><strong>El Instinto Eager es el Error por Defecto</strong></h2><p>La sabidur&#237;a convencional en ingenier&#237;a web dice: <em>fetch temprano, preload, cachea agresivamente para esconder la latencia</em>.</p><p>Una llamada hecha "tarde" se trata como un fallo de preparaci&#243;n. Como un bug de rendimiento.</p><p><strong>Pero mirad la evidencia.</strong> TanStack Query &#8212;la librer&#237;a de fetching de datos m&#225;s usada en el ecosistema React&#8212; tiene un `staleTime` por defecto de <strong>0</strong>. Eso significa que cada remount o refocus de ventana dispara un refetch completo. Tu "cach&#233;" no est&#225; cacheando: est&#225; refetcheando ligeramente menos a menudo.</p><p>Y el coste del eager es silencioso y estructural:</p><p>&#8594; Payloads descargados, parseados y cacheados que nadie ve jam&#225;s.</p><p>&#8594; Bugs de invalidaci&#243;n de cach&#233; imposibles de rastrear.</p><p>&#8594; Acoplamiento s&#237;ncrono forzado entre componentes que no comparten datos.</p><p>El fallo del eager es invisible. Un payload se descarga, se cachea, se invalida y se vuelve a descargar &#8212; y nadie se entera.</p><p><strong>El fallo del late es un spinner.</strong> Y un spinner se ve. Y un spinner se arregla.</p><p>Esa asimetr&#237;a es exactamente por qu&#233; los equipos sobre-optimizan fetches tempranos en vez de eliminarlos.</p><p>&#10060; <strong>El enfoque cl&#225;sico:</strong> fetch de todo al montar la p&#225;gina "por si acaso".</p><p>&#9989; <strong>El enfoque Late:</strong> fetch solo cuando los datos son visibles, batched, y con staleness expl&#237;cita.</p><h2><strong>Por Qu&#233; "Fecha Temprano" es un Impuesto que Pagas Sin Verlo</strong></h2><p>Mirad lo que pasa con un fetch t&#237;pico de dashboard:</p><p>```jsx</p><p>// Eager &#8212; el default: fetches al montar aunque la secci&#243;n no sea visible</p><p>const { data } = useQuery(['dashboard'], fetchDashboard)</p><p>// Late &#8212; fetch solo cuando la secci&#243;n entra en viewport</p><p>const { data } = useQuery(</p><p>['dashboard'],</p><p>fetchDashboard,</p><p>{ enabled: isSectionVisible, staleTime: 60_000 }</p><p>)</p><p>```</p><p>Un boolean (`enabled`) m&#225;s un `staleTime` convierte una llamada eager en una llamada tard&#237;a. Y el cambio de c&#243;digo es de dos l&#237;neas.</p><p>El patr&#243;n N+1 documentado en el ecosistema GraphQL nace exactamente de esto: resolver cada campo con su propia llamada eager produce N round-trips. La soluci&#243;n can&#243;nica &#8212;<strong>DataLoader</strong>, de Lee Byron y el equipo de GraphQL&#8212; existe precisamente porque aplaza cargas individuales y las fusiona en una &#250;nica query batched tard&#237;a.</p><p>```javascript</p><p>// NO hagas await fetch(user.id) dentro de cada resolver</p><p>// Deja que el batch salga tarde, en una sola query</p><p>const userLoader = new DataLoader(ids =&gt; batchFetchUsers(ids))</p><p>// En el resolver:</p><p>userLoader.load(userId) // la carga real ocurre cuando el batch est&#225; lleno</p><p>```</p><h3><strong>HTTP Ya Tiene el Status Code Para esto: 202 Accepted</strong></h3><p>El protocolo HTTP define desde hace d&#233;cadas un c&#243;digo de primera clase para resultados deliberadamente tard&#237;os.</p><p><strong>202 Accepted</strong> significa: "el servidor ha aceptado la petici&#243;n, pero el procesamiento no ha terminado". Es la se&#241;al can&#243;nica para ejecuci&#243;n diferida &#8212; frente al 200 bloqueante que ignora la sem&#225;ntica real de la operaci&#243;n.</p><p>Mirad la diferencia:</p><p>```javascript</p><p>// Express &#8212; 200 bloqueante (el cliente espera segundos)</p><p>app.post('/reports', async (req, res) =&gt; {</p><p>const result = await generateReport(req.body)   // bloquea</p><p>res.json(result)</p><p>})</p><p>// Express &#8212; 202 + endpoint de estado</p><p>app.post('/reports', async (req, res) =&gt; {</p><p>if (!validIdempotencyKey(req.headers['idempotency-key'])) {</p><p>return res.status(400).json({ error: 'Idempotency-Key requerida' })</p><p>}</p><p>const jobId = await queue.enqueue(req.body)</p><p>res.status(202).json({ jobId, statusUrl: `/reports/${jobId}` })</p><p>})</p><p>app.get('/reports/:id', async (req, res) =&gt; {</p><p>const job = await store.get(req.params.id)</p><p>return job.done</p><p>? res.json(job.result)</p><p>: res.status(202).json({ status: 'pending' })</p><p>})</p><p>```</p><p>El 202 no es solo un c&#243;digo de estado. <strong>Es un cambio de contrato</strong>: convierte al cliente de un llamador s&#237;ncrono en un suscriptor de su propio trabajo. Encolas un job, devuelves un `jobId`, expones un endpoint de estado y empujas la finalizaci&#243;n v&#237;a webhook o SSE.</p><p>El patr&#243;n funciona igual en Python con FastAPI:</p><p>```python</p><p>from fastapi import BackgroundTasks</p><p>@app.post('/reports', status_code=202)</p><p>async def create(payload: Payload, bg: BackgroundTasks):</p><p>job_id = uuid4()</p><p>bg.add_task(generate_report, job_id, payload)</p><p>return {'id': str(job_id), 'status': f'/reports/{job_id}'}</p><p>```</p><h2><strong>Los Datos Que Confirman el Giro: Late es una Alternativa Real</strong></h2><p>No estoy describiendo una teor&#237;a bonita. El giro hacia lo tard&#237;o est&#225; formaliz&#225;ndose en tres capas distintas del stack:</p><p>&#8594; <strong>Protocolo:</strong> la especificaci&#243;n GraphQL est&#225; incorporando `@defer`/`@stream` como caracter&#237;sticas nativas, no como hack. Con incremental delivery, una sola query devuelve su camino cr&#237;tico r&#225;pido inmediatamente y streamea el sub-grafo lento despu&#233;s por la misma conexi&#243;n. La dicotom&#237;a entre "todo ahora" y "todo despu&#233;s" colapsa en una decisi&#243;n por campo:</p><p>```graphql</p><p>query Home {</p><p>hero { name }</p><p>... @defer {</p><p>recommendations { items { id } }</p><p>}</p><p>}</p><p>```</p><p>El hero pinta al instante. Las recomendaciones llegan tarde. Ambas sobre la misma conexi&#243;n.</p><p>&#8594; <strong>Clientes:</strong> los equipos que suben su `staleTime` de 0 a un valor expl&#237;cito eliminan decenas de refetches accidentales sin tocar arquitectura. Es la victoria Late m&#225;s barata que existe.</p><p>&#8594; <strong>Infraestructura:</strong> SSE y gRPC server-streaming mantienen la conexi&#243;n abierta y empujan resultados a medida que los chunks completan &#8212; convirtiendo una &#250;nica respuesta tard&#237;a en un stream de resultados parciales.</p><h2><strong>El Contrapeso: Cu&#225;ndo Ser Tard&#237;o es un Error</strong></h2><p>Ser honesto: <strong>late no es siempre correcto.</strong></p><p>Para mutaciones iniciadas por el usuario, colaboraci&#243;n en tiempo real o presupuestos de latencia interactiva dura &#8212; el retraso solo mueve la espera.</p><p>Y la entrega distribuida tard&#237;a a&#241;ade peligros reales:</p><p>&#8594; Ordenaci&#243;n de mensajes.</p><p>&#8594; Retry storms.</p><p>&#8594; Hazard de idempotencia.</p><p>La madurez est&#225; en acotar d&#243;nde paga el retraso &#8212;lecturas, agregaci&#243;n, generaci&#243;n de informes&#8212; y d&#243;nde no &#8212;mutaciones con consecuencias inmediatas en la UI.</p><p>Otro matiz: diferir no es desaparecer. <strong>Un fetch tard&#237;o solo funciona si est&#225; enmascarado</strong> con skeletons, placeholders y prefetch especulativo en hover o focus. Para datos cr&#237;ticos por encima del fold, el eager es correcto. La cuesti&#243;n no es elegir uno u otro: es dejar de hacer eager por accidente.</p><h2><strong>El Marco de la &#218;ltima Responsabilidad (MLR)</strong></h2><p>Si quer&#233;is llevar esto a vuestro c&#243;digo hoy, sin teor&#237;a, aplicad este marco:</p><h3><strong>Paso 1: Auditor&#237;a de cada fetch saliente</strong></h3><p>Clasificad cada petici&#243;n en vuestra app. &#191;El dato se renderiza por encima del fold al montar? &#191;O se fetchea "por si acaso"?</p><p>Etiquetad cada llamada: `EAGER-NEEDED`, `EAGER-UNNEEDED` o `LATE-READY`.</p><p>La auditor&#237;a sola revela un conjunto de fetches que ning&#250;n path de usuario dispara jam&#225;s.</p><h3><strong>Paso 2: Pol&#237;tica de staleness expl&#237;cita ANTES de tocar arquitectura</strong></h3><p>Configurad `staleTime`, `refetchOnFocus` y `refetchOnReconnect` deliberadamente en vuestra capa de datos (TanStack Query, SWR o equivalente).</p><p><em>Es la &#250;nica forma de que el refetch temprano deje de ser el default accidental.</em></p><h3><strong>Paso 3: Cambiad el contrato cuando supere vuestro presupuesto</strong></h3><p>Para cualquier operaci&#243;n de servidor estimada por encima de vuestro presupuesto de respuesta bloqueante (unos pocos cientos de milisegundos), cambiad a: `POST &#8594; 202 { jobId }` + `GET /jobs/:id` + webhook/SSE opcional.</p><p>El GET debe ser idempotente y cacheable.</p><h3><strong>Paso 4: Arreglad el over-fetching en la fuente</strong></h3><p>Habilitad selecci&#243;n de campos dispersa (sparse fieldsets en JSON:API o selecci&#243;n de campos en GraphQL) y batched el fan-out server-side con un coalescer tipo DataLoader.</p><p><em>Una query batched tard&#237;a reemplaza a N llamadas eager.</em></p><h3><strong>Paso 5: Hardeneded la ruta async como un SLA de primera clase</strong></h3><p>Exigid claves de idempotencia en endpoints 202. Deduplicad server-side. Usad exponential-backoff para la entrega de webhooks. A&#241;adid timeout/deadline en el cliente para que una respuesta tard&#237;a genuinamente perdida no cuelgue la UI para siempre.</p><p><strong>Tratad el late como un contrato, no como un accidente.</strong></p><h2><strong>Conclusi&#243;n</strong></h2><p>Los equipos llevan una d&#233;cada pagando un impuesto invisible: fetches eager que nadie pidi&#243;, refetches accidentales que el default de `staleTime: 0` garantiza, y acoplamiento s&#237;ncrono donde un 202 habr&#237;a resuelto el problema con un endpoint de estado.</p><p>El patr&#243;n Late no es pereza. <strong>Es ingenier&#237;a de precisi&#243;n</strong>: pides el dato cuando es visible, cuando el batch est&#225; lleno, cuando el servidor ha confirmado el trabajo &#8212; y no un segundo antes.</p><p>Mientras las APIs de redes sociales sigan degradando a quien consulta de forma eager e imprevisible, la ventaja competitiva en 2026 ser&#225; de quienes traten la incertidumbre como primer principio. La tesis es sencilla: quien sabe esperar al &#250;ltimo momento responsable &#8212;ni m&#225;s tarde, ni, sobre todo, m&#225;s pronto&#8212; construye sistemas que escalan porque no piden nada que no vayan a usar.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/late-api-pattern-twitter-api-alternative-2026-20260906?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Next.js 2026: El Framework "Seguro" es la Apuesta Más Radical de React — y Nadie lo Ha Notado]]></title><description><![CDATA[Next.js lanz&#243; 4 versiones mayores en 3 a&#241;os que rompieron sus defaults: de Pages Router a App Router, de middleware a proxy.ts, de cache est&#225;tico a din&#225;mico. As&#237; evitas el impuesto anual de migraci&#243;n.]]></description><link>https://newsletter.brianmenagomez.com/p/nextjs-2026-el-framework-seguro-es</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/nextjs-2026-el-framework-seguro-es</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Sun, 06 Sep 2026 07:00:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/466d4b79-1d84-436f-8c30-db7235812eac_1080x608.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Todo tutorial de Next.js que guardaste antes de octubre de 2023 ense&#241;a ya un framework legacy</strong></h2><p>El relato dominante dice que Next.js es la apuesta segura. Mayor pool de contrataci&#243;n de React. La mejor documentaci&#243;n. El respaldo de Vercel. Elegir al l&#237;der del mercado es elegir bajo riesgo.</p><p><strong>*El problema: eso dej&#243; de ser cierto en octubre de 2022.</strong> *</p><p>Tres a&#241;os. Cuatro versiones mayores. Next.js 13, 14, 15 y 16 rompieron sus propios defaults cada ~12 meses. Renombr&#243; su fichero de middleware a `proxy.ts`. Elimin&#243; caches que ven&#237;an activadas por defecto. Movi&#243; todo el modelo de renderizado &#8212;de "server-side rendering para SEO" a React Server Components ejecut&#225;ndose en el servidor por defecto. Y el "safe choice" sigue ah&#237;: tu app compila, tus tests pasan, todo funciona.</p><p>Hasta que un tutorial de 2023 ya no cuadra con tu versi&#243;n y no sabes por qu&#233;.</p><p>El verdadero Next.js actual no es el framework conservador que elegiste en 2022. <strong>*Es la apuesta m&#225;s radical y con m&#225;s velocidad de cambio de toda la infraestructura front-end de React.</strong> *</p><h2><strong>El mito de la p&#225;gina: tu c&#243;digo de 2022 ya es legacy</strong></h2><p>Cuando Next.js se lanz&#243; en 2016 dentro de ZEIT (ahora Vercel), la arquitectura se estabiliz&#243; alrededor del <strong>Pages Router</strong>. El directorio `pages/`, con `getStaticProps` y `getServerSideProps`. Esa arquitectura fue la identidad del framework durante las versiones 7 a 12.</p><p>El modelo mental era una elecci&#243;n triple por p&#225;gina: `&lt;b&gt;est&#225;tica&lt;/b&gt;` (getStaticProps), server-rendered (getServerSideProps) o client-side. SEO. SSR. Rendimiento. Cada p&#225;gina eleg&#237;a su camino.</p><p>En <strong>Next.js 13</strong> (octubre 2022), Vercel introdujo el <strong>App Router</strong> como beta. En <strong>Next.js 14</strong> (octubre 2023), lo convirti&#243; en el default estable y recomendado. Y con &#233;l lleg&#243; React Server Components, convenciones de fichero nuevas (`layout.tsx`, `page.tsx`, `loading.tsx`, `error.tsx`) y un cambio conceptual: ya no eliges entre est&#225;tico y din&#225;mico *por p&#225;gina*.</p><p>Ahora un componente simplemente se ejecuta en el servidor <strong>a menos que</strong> optes por salir con `'use client'`.</p><p>Traducido: una base de c&#243;digo escrita en el idioma dominante de 2022 &#8212;Pages Router + getServerSideProps&#8212; es hoy t&#233;cnicamente legacy. No puede usar Server Components. Requiere una reescritura completa para el App Router. Y la comunidad, los cursos y mil tutoriales de hace dos a&#241;os ense&#241;an exactamente esa arquitectura obsoleta.</p><p>&#10060; <strong>Enfoque incorrecto:</strong> "Tenemos SSR para SEO, buscamos una migraci&#243;n incremental del Pages al App Router."</p><p>&#9989; <strong>Enfoque correcto:</strong> "El App Router no es una versi&#243;n mejorada del Pages Router. Son dos mundos distintos que comparten React debajo."</p><p>Mira la misma p&#225;gina de blog en los dos mundos. Pages Router, 2022:</p><p>```tsx</p><p>// pages/blog/[slug].js &#8212; Pages Router (2022)</p><p>export async function getStaticPaths() {</p><p>const posts = await getAllPostSlugs()</p><p>return { paths: posts.map(p =&gt; ({ params: { slug: p.slug } })), fallback: 'blocking' }</p><p>}</p><p>export async function getStaticProps({ params }) {</p><p>const post = await getPostBySlug(params.slug)</p><p>return { props: { post }, revalidate: 3600 }</p><p>}</p><p>export default function BlogPage({ post }) {</p><p>return &lt;article&gt;{post.title} {/<em> HTML plano, sin interactividad trivial </em>/}&lt;/article&gt;</p><p>}</p><p>```</p><p>App Router, hoy:</p><p>```tsx</p><p>// app/blog/[slug]/page.tsx &#8212; App Router (2025/2026)</p><p>export async function generateStaticParams() {</p><p>const posts = await getAllPostSlugs()</p><p>return posts.map(p =&gt; ({ slug: p.slug }))</p><p>}</p><p>// Este componente S&#205; est&#225; en el servidor. Ejecuta su query ah&#237; mismo.</p><p>export default async function BlogPage({ params }) {</p><p>const post = await getPostBySlug((await params).slug) // params es async en v15/v16</p><p>return &lt;article&gt;{post.title}&lt;/article&gt;</p><p>}</p><p>```</p><p>No es una refactorizaci&#243;n cosm&#233;tica. El modelo mental completo &#8212;"el servidor existe para SEO, el cliente para interactividad"&#8212; se ha colapsado. En el App Router, el servidor es el punto de partida. El cliente es la excepci&#243;n declarada con `'use client'`, que no es un toggle de hidrataci&#243;n: <strong>*es una directiva de frontera de m&#243;dulo.</strong> *</p><h2><strong>El golpe silencioso: el culebr&#243;n del cacheo entre v13/v14 y v15</strong></h2><p>Aqu&#237; es donde los tutoriales viejos son m&#225;s peligrosos.</p><p>El App Router temprano (v13/v14) era <strong>est&#225;tico-por-defecto</strong>. Las llamadas `fetch` quedaban cacheadas impl&#237;citamente. Los GET Route Handlers se cacheaban. El Router Cache del cliente guardaba resultados. C&#243;digo escrito en 2023 era instant&#225;neo &#8212; no porque fuera r&#225;pido, sino porque todo estaba en cache.</p><p><strong>Next.js 15 (octubre 2024) dio la vuelta a esos defaults.</strong> Los GET Route Handlers ya no se cachean por defecto. El Router Cache del cliente, tampoco. Y las request APIs (`cookies`, `headers`, `params`) se volvieron async.</p><p>Mira lo que eso significa con un ejemplo concreto. Un GET Route Handler escrito en el estilo v14:</p><p>```ts</p><p>// app/api/posts/route.ts &#8212; Estilo v14: cacheado impl&#237;citamente</p><p>export async function GET() {</p><p>const posts = await db.posts.findMany()</p><p>return Response.json(posts)</p><p>}</p><p>```</p><p>En v13/v14, ese handler se cacheaba autom&#225;ticamente. En v15 y v16, <strong>determinar&#225; el renderizado din&#225;mico de la ruta</strong> porque un componente que depende de esa respuesta ya no puede cachearse. Para recuperar salida est&#225;tica, tienes que optar expl&#237;citamente:</p><p>```ts</p><p>// app/api/posts/route.ts &#8212; Realidad v15/v16: opt-in expl&#237;cito</p><p>export const dynamic = 'force-static'</p><p>export async function GET() {</p><p>const posts = await db.posts.findMany()</p><p>return Response.json(posts)</p><p>}</p><p>```</p><p>P&#225;gina instant&#225;nea en 2023 porque estaba cacheada. Round trip de red en 2025 porque nadie ley&#243; las release notes. <strong>*La regresi&#243;n silenciosa de rendimiento es el coste real de "semver majors con defaults cambiados".</strong> *</p><p>Y esto afecta a otro patr&#243;n que la mayor&#237;a lleva en producci&#243;n: los Server Actions. La mutaci&#243;n de datos ya no necesita un Route Handler de API. Un formulario en un Server Component puede mutar directamente con una funci&#243;n async:</p><p>```tsx</p><p>// app/profile/form.tsx &#8212; Server Action (v15/v16)</p><p>export default function ProfileForm() {</p><p>async function updateName(formData: FormData) {</p><p>'use server'</p><p>const name = formData.get('name')</p><p>await db.users.update({ where: { id: user.id }, data: { name } })</p><p>}</p><p>return (</p><p>&lt;form action={updateName}&gt;</p><p>&lt;input name="name" /&gt;</p><p>&lt;button type="submit"&gt;Guardar&lt;/button&gt;</p><p>&lt;/form&gt;</p><p>)</p><p>}</p><p>```</p><p>Nada de esto exist&#237;a en 2022. Y reescribe c&#243;mo piensas sobre APIs y endpoints.</p><h2><strong>Next.js 16: Turbopack, proxy.ts y el cintur&#243;n que se aprieta otra vez</strong></h2><p>La cadencia no se ha detenido. <strong>Next.js 16</strong> (octubre 2025) hizo estable a Turbopack como bundler para dev y producci&#243;n, renombr&#243; `middleware.ts` a <strong>`proxy.ts`</strong> y elimin&#243; el acceso s&#237;ncrono a `params`.</p><p>Un middleware que en v13&#8211;v15 proteg&#237;a rutas con auth:</p><p>```ts</p><p>// middleware.ts &#8212; v13 a v15 (ahora obsoleto en v16)</p><p>import { NextResponse } from 'next/server'</p><p>export function middleware(request) {</p><p>const token = request.cookies.get('session')</p><p>if (!token &amp;&amp; request.nextUrl.pathname.startsWith('/dashboard')) {</p><p>return NextResponse.redirect(new URL('/login', request.url))</p><p>}</p><p>return NextResponse.next()</p><p>}</p><p>export const config = { matcher: '/dashboard/:path*' }</p><p>```</p><p>Su equivalente en v16, renombrado y con API async:</p><p>```ts</p><p>// proxy.ts &#8212; v16 (renombrado desde middleware.ts)</p><p>export default async function proxy(request: Request) {</p><p>const token = request.headers.get('cookie') // ahora es un Request est&#225;ndar</p><p>if (!token?.includes('session') &amp;&amp; new URL(request.url).pathname.startsWith('/dashboard')) {</p><p>return Response.redirect(new URL('/login', request.url))</p><p>}</p><p>return undefined // contin&#250;a el flujo</p><p>}</p><p>```</p><p>Cuatro majors en tres a&#241;os. 13&#8594;14&#8594;15&#8594;16. La app mediana de producci&#243;n est&#225; uno o dos majors por detr&#225;s. Y cada hueco se agranda: params/headers s&#237;ncronos &#8594; async, middleware &#8594; proxy, Pages &#8594; App, webpack &#8594; Turbopack.</p><p>No solo eso: Next.js absorbe los cambios ascendentes de React. La v13 ya ship-e&#243; Server Components <strong>antes</strong> de que React 19 los estabilizara oficialmente. El framework lidera la curva, y luego rompe sus propias APIs encima.</p><p>Para managers t&#233;cnicos, esto tiene una consecuencia dura: <strong>*"Senior React Developer" ya no es suficiente. Necesitas gente que lea las release notes de Next.js cada a&#241;o.</strong> *</p><h2><strong>El Patr&#243;n del Impuesto de Versi&#243;n: c&#243;mo no quedarte en la cola</strong></h2><p>Aqu&#237; va mi m&#233;todo para operar bajo esta cadencia. Lo llamo el <strong>El Patr&#243;n del Impuesto de Versi&#243;n</strong>. Cinco pasos, cada vez que toques un proyecto Next.js:</p><p><strong>Paso 1 &#8212; Fija tu versi&#243;n y presupuesta el impuesto.</strong> Trata cada release mayor (anual desde 2022) como un proyecto de migraci&#243;n, no como una actualizaci&#243;n de rutina. Antes de tocar tu c&#243;digo de app, lee el Upgrade Guide oficial y ejecuta los codemods:</p><p>```bash</p><p>npx @next/codemod@canary upgrade</p><p>```</p><p><strong>Paso 2 &#8212; Decide tu identidad de Router hoy.</strong> Si est&#225;s en Pages Router, asume una <strong>reescritura completa</strong> hacia App Router, no un retro-portado incremental. Lista qu&#233; rutas son interactivas vs de contenido para conocer tu frontera servidor/cliente.</p><p><strong>Paso 3 &#8212; Toma una decisi&#243;n de hosting expl&#237;cita en el mes uno.</strong> Vercel-first frente a self-hosting con output Node.js standalone (`output: 'standalone'`, Docker). Muchas capacidades del App Router &#8212;ISR bajo demanda, optimizaci&#243;n de imagen en el edge, soporte a PPR&#8212; tienen implementaciones de primera clase en Vercel y costes operativos distintos si te auto-alojas. Retrofitar esto despu&#233;s es de donde nacen las reescrituras.</p><p><strong>Paso 4 &#8212; Audita tus asunciones de cacheo contra la versi&#243;n actual.</strong> Ejecuta `next build` e inspecciona la tabla de rutas. Mira qu&#233; rutas son est&#225;ticas, din&#225;micas o ISR. Re-a&#241;ade `generateStaticParams`, `force-static` o la revalidaci&#243;n con sem&#225;ntica `'use cache'` donde de verdad quieras salida est&#225;tica. <strong>*No copies ejemplos v13/v14 verbatim.</strong> *</p><p><strong>Paso 5 &#8212; A&#241;ade observabilidad y una p&#225;gina de pruebas canary para RSC.</strong> La gesti&#243;n de datos ahora ocurre en el servidor, dentro de componentes. Los waterfalls y los error boundaries (`error.tsx`, `global-error.tsx`) hay que probarlos en producci&#243;n, no solo en dev.</p><h3><strong>&#191;Y el argumento del SEO?</strong></h3><p>La frase "SSR para SEO" &#8212;la raz&#243;n cl&#225;sica para elegir Next.js&#8212; es un modelo mental muerto. En 2026, SSR ya no es la feature titular. Lo que determina el rendimiento es el split compilaci&#243;n/request: salida est&#225;tica, ISR, din&#225;mico y el PPR experimental (Partial Prerendering). Para sitios dominados por contenido, una estrategia est&#225;tica/ISR gana a renderizar cada petici&#243;n en el servidor.</p><p><strong>SSR es una capacidad y un coste. No una garant&#237;a de rendimiento.</strong> El propio Next.js 15 oscil&#243; hacia render din&#225;mico precisamente porque cachear no es gratis.</p><h3><strong>D&#243;nde queda el lock-in</strong></h3><p>El lock-in no es binario. No es cierto que App Router requiera Vercel: con `output: 'standalone'` te auto-alojas en Node.js o Docker, y RSC/Server Actions son HTTP plano. El lock-in pr&#225;ctico es la <strong>completitud de features</strong> &#8212;on-demand ISR, optimizaci&#243;n de imagen en el edge, soporte tight de PPR&#8212; que est&#225;n productivizadas en Vercel y que en self-hosting tienes que ensamblar t&#250; mismo o aceptar est&#225;tico puro.</p><p>La competencia real de Next.js no son "otros frameworks React". Es la asunci&#243;n por defecto de que una app React <em>tiene que ser</em> una Next.js app. React Router (framework mode) y Astro atienden a la misma audiencia con tradeoffs distintos: React Router mantiene un modelo request/response m&#225;s cercano al SSR cl&#225;sico; Astro empuja server-first con islas y env&#237;a cero JavaScript por defecto.</p><p>Next.js aporta una gravedad de ecosistema inigualable y la madurez de Server Components m&#225;s grande del mercado. <strong>*A cambio, cobra el impuesto de migraci&#243;n anual m&#225;s alto y ejerce la atracci&#243;n m&#225;s fuerte hacia un &#250;nico vendor de hosting.</strong> *</p><h2><strong>Conclusiones</strong></h2><p>El relato de la "apuesta segura" te lo venden desde 2016. Desde 2022, la evidencia es la contraria:</p><p>&#8594; <strong>Los defaults se rompen cada ~12 meses.</strong> V13, v14, v15, v16. Caches, middleware, request APIs, bundler. Todo ha cambiado en tres a&#241;os.</p><p>&#8594; <strong>El c&#243;digo de hace dos a&#241;os es legacy.</strong> Pages Router, getServerSideProps, fetch cacheado impl&#237;citamente &#8212; toda esa arquitectura es historia.</p><p>&#8594; <strong>Migrar no es un evento. Es un ritmo.</strong> Dentro del propio App Router, v14&#8594;v15 cambi&#243; defaults de cacheo y v15&#8594;v16 renombr&#243; middleware y forz&#243; params async. Tu c&#243;digo App Router de 2023 necesita reescritura en 2026.</p><p>La pregunta no es si Next.js es "seguro". Es si est&#225;s <em>presupuestando el impuesto de versi&#243;n</em> que cobra cada octubre. Los equipos que lo hacen &#8212;api&#241;ando el upgrade, auditando cacheo contra la versi&#243;n actual, decidiendo su hosting en el mes uno&#8212; siguen operando a toda velocidad. Los que esperaban estabilidad llevan tres a&#241;os de retraso sin darse cuenta.</p><p>Next.js no es el default aburrido que eligiste por seguridad. Es la base tecnol&#243;gica m&#225;s veloz de React, y la &#250;nica forma de sobrevivirla es tratarla como lo que es: un framework que te obliga a migrar cada a&#241;o.</p><p>La pr&#243;xima vez que alguien diga "Next.js es lo seguro", preg&#250;ntale qu&#233; versi&#243;n tiene en producci&#243;n. Esa respuesta te dir&#225; m&#225;s que mil debates de arquitectura.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/nextjs-framework-seguro-apuesta-radical-react-no-has-notado-20260906?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[El Suelo de Precio de Tu Servicio Productizado No Se Calcula. Se Descubre — o Mueres en el Mes 3]]></title><description><![CDATA[El suelo de precio de tu servicio productizado se equivoca si lo calculas con costes. Descubre por qu&#233; muere en el mes 3 y c&#243;mo fijarlo bien.]]></description><link>https://newsletter.brianmenagomez.com/p/el-suelo-de-precio-de-tu-servicio</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/el-suelo-de-precio-de-tu-servicio</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Sat, 05 Sep 2026 07:00:15 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/26c0593c-0806-43a0-8fb7-b55db6e842f5_1080x721.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>El 90% de los que Productizan un Servicio Fracasan en el Primer A&#241;o &#8212; y el Suelo de Precio Que Est&#225;s Calculando Ahora No es el Problema: es el S&#237;ntoma</strong></h2><p>Crees que tu problema de precio es que no encuentras "el n&#250;mero correcto". Que si haces tres propuestas m&#225;s, miras a la competencia, o aplicas la f&#243;rmula de coste + margen, la cifra m&#225;gica aparecer&#225;.</p><p><strong>*Te has equivocado de diagn&#243;stico.</strong>*</p><p>Antes de preguntarte "&#191;cu&#225;nto cobro?" hay una pregunta anterior que ya ha condenado tu producto a morir en el mes 3. Y no tiene nada que ver con el n&#250;mero.</p><p>La mayor&#237;a de intentos de productizar un servicio fracasan en el primer a&#241;o. Y la causa declarada no es el nivel de precio. Es <strong>el esquema de cobro</strong>: forzar recurrencia donde no la hay.</p><p>El suelo de precio no es un n&#250;mero que se calcula. Es una <strong>puerta binaria</strong> que solo se abre si el patr&#243;n de consumo subyacente es recurrente. Si no lo es, cualquier suelo que fijes &#8212; alto, bajo, exacto &#8212; es una condena diferida.</p><p>---</p><h2><strong>El Debate del "Cu&#225;nto" est&#225; Secuestrando al Debate del "C&#243;mo"</strong></h2><p>La sabidur&#237;a convencional dice que el suelo de precio se descubre con f&#243;rmulas. Coste m&#225;s margen. Valor entregado. Benchmark de competencia.</p><p>El material fuente apunta a lo contrario.</p><p>Hay dos variables distintas con las que se suele jugar mezcladas: <strong>"cu&#225;nto"</strong> (el nivel de precio) y <strong>"c&#243;mo"</strong> (el modelo de cobro). El fracaso del 90% en el primer a&#241;o se explica por el segundo, no por el primero.</p><p>Esto implica algo inc&#243;modo: una obsesi&#243;n por encontrar el n&#250;mero correcto es una <strong>fuga hacia adelante</strong> cuando el modelo de cobro fuerza recurrencia sobre un consumo &#250;nico.</p><p>No hay suelo de precio que arregle un problema de inexistencia de producto recurrente. Ninguno. Puedes poner el n&#250;mero que quieras. Si tu modelo de cobro no encaja con el patr&#243;n de consumo, el suelo es decoraci&#243;n sobre una base podrida.</p><p>---</p><h2><strong>La &#218;nica Pregunta que Decide Todo: &#191;Datos Vivos o Informe Est&#225;tico?</strong></h2><p>Aqu&#237; est&#225; el punto donde casi todos se pierden. Porque la pregunta que separa un servicio que puede llevar recurrencia de uno que no la soporta es binaria.</p><p>Si tu entregable es un <strong>informe est&#225;tico</strong> &#8212; un PDF de auditor&#237;a, una migraci&#243;n completada, un an&#225;lisis puntual &#8212;, est&#225;s vendiendo un artefacto de consumo &#250;nico. Quien lo recibe lo lee una vez, lo archiva, y no vuelve a necesitarlo.</p><p>Si defender un suelo recurrente sobre eso, est&#225;s <strong>fabricando recurrencia</strong>.</p><p>Y aqu&#237; es donde entra la mec&#225;nica del fracaso. Cuando fuerzas un modelo de ingresos recurrentes sobre una necesidad de consumo &#250;nico, algo tiene que ceder. Y lo que cede es la renovaci&#243;n.</p><p>&#10060; <strong>Modelo d&#233;bil:</strong> "Vendo informes de SEO mensuales recurrente porque necesito ingresos predecibles." El cliente consume una vez y cancela en el mes 3.</p><p>&#9989; <strong>Modelo fuerte:</strong> "Vendo un panel de datos vivo que se actualiza continuamente y exige operaci&#243;n constante para seguir funcionando." El cliente renueva porque el estado muere si lo deja.</p><p>El objeto de precio cambia por completo. <strong>De documento entregado a estado mantenido.</strong></p><p>---</p><h2><strong>La Mec&#225;nica del Hito del Mes 3: Cuando la Relaci&#243;n Deja de Pagar</strong></h2><p>&#191;Por qu&#233; el mes 3? Porque no es casualidad que las ofertas "recurrente-as-a-servicio" (RaaS) sin consumo real mueran sistem&#225;ticamente alrededor del primer trimestre.</p><p>La mec&#225;nica es predecible. Durante los primeros meses, la renovaci&#243;n la sostienen tres muletas:</p><p>1. <strong>El entusiasmo del onboarding.</strong> Todo es nuevo, hay momentum, el cliente est&#225; comprometido emocionalmente.</p><p>2. <strong>La novedad.</strong> El entregable se ve, se eval&#250;a, se comenta.</p><p>3. <strong>La relaci&#243;n interpersonal.</strong> Y aqu&#237; est&#225; la trampa m&#225;s peligrosa.</p><p>Alrededor del tercer mes, esas tres muletas se retiran. Y lo &#250;nico que queda sobre la mesa es una pregunta seca: <strong>&#191;el estado de datos u operaci&#243;n que vendes realmente se consume de forma recurrente?</strong></p><p>Si la respuesta es no, el cliente cancela. No porque el precio fuese caro. No porque el trabajo fuese malo. Sino porque <strong>el consumo no se repiti&#243;</strong>, y pagar por algo que no consumes es absurdo para cualquier comprador racional.</p><p>Por eso la mortalidad se concentra en ese punto: es el momento exacto en que la relaci&#243;n deja de pagar y el producto empieza a facturar &#8212; o no.</p><p>---</p><h2><strong>El Primer Cliente Feliz es una Trampa Estad&#237;stica Doble</strong></h2><p>Y aqu&#237; entra el error de validaci&#243;n m&#225;s caro que cometer&#225;s en todo el proceso.</p><p>Tu primer cliente satisfecho <strong>no valida un producto. Valida una relaci&#243;n interpersonal</strong> cuyo pago de repetici&#243;n es una inc&#243;gnita.</p><p>&#191;Qu&#233; significa eso para tu suelo de precio?</p><p>Que su disposici&#243;n a pagar no mide el valor de tu producto. Mide <strong>c&#243;mo se siente contigo</strong>. Mide la afinidad, la confianza, el favor que te debe porque le resolviste algo en persona.</p><p>Extrapolar un suelo de precio desde ese &#250;nico punto de datos comete el mismo error de validaci&#243;n que ya cometi&#243; quien confundi&#243; un favor con un producto. El primer cliente no es una muestra representativa del mercado: es una muestra representativa de <strong>una relaci&#243;n</strong>.</p><p>La consecuencia operativa es directa. <strong>La &#250;nica se&#241;al de precio que vale es el pago de repetici&#243;n de alguien que ya no te debe un favor.</strong> Si ese dato no existe, tu suelo de precio sigue siendo una especulaci&#243;n disfrazada de decisi&#243;n.</p><p>---</p><h2><strong>El Modelo del Suelo Binario: 5 Pasos para Fijar el Suelo Como Funci&#243;n del Consumo, No del Coste</strong></h2><p>Este es el framework <strong>El Modelo del Suelo Binario</strong> &#8212; puerta de consumo, no f&#243;rmula de coste. Cinco pasos, en orden estricto. No los saltes.</p><h3><strong>Paso 1 &#8212; Clasifica Antes de Numerar</strong></h3><p>Responde la pregunta de diagn&#243;stico sobre tu entregable <strong>real</strong>, no sobre el que te gustar&#237;a vender: &#191;entrega datos vivos o un informe est&#225;tico?</p><p>Si la respuesta honesta es "informe est&#225;tico", tu problema de suelo est&#225; mal planteado desde el origen. Puedes productizarlo &#8212; claro que s&#237; &#8212; pero el suelo y el modelo de cobro deben corresponder al consumo &#250;nico o al estado recurrente adyacente. No a una suscripci&#243;n forzada.</p><p>La alternativa es aceptar abiertamente el modelo de proyecto, sin disfrazarlo de recurrente. Duele menos que fingir recurrencia y morir en el mes 3.</p><h3><strong>Paso 2 &#8212; Define el Estado Recurrente Productizable</strong></h3><p>Si tu entregable es un informe anual de seguridad laboral, pregunta: &#191;qu&#233; estado queda vivo entre una entrega y la siguiente? &#191;Los riesgos cambian? &#191;Hay normativa nueva? &#191;Hay datos que se actualizan?</p><p>Ese estado de datos u operaci&#243;n que permanece vivo <strong>entre</strong> entregas y que dispara tu trabajo de forma peri&#243;dica es el objeto sobre el que tiene sentido hablar de suelo. No el artefacto. El artefacto es una foto. El estado es la c&#225;mara funcionando para siempre.</p><h3><strong>Paso 3 &#8212; Valida la Hip&#243;tesis Contra el Mes 3</strong></h3><p>Antes de comprometer un suelo y un modelo de cobro, dise&#241;a una prueba que mida si el consumo se repite pasado el trimestre.</p><p>El mes 3 es el punto ciego de toda la teor&#237;a del suelo de precio. El entusiasmo inicial y la relaci&#243;n interpersonal dejan de sostener la renovaci&#243;n. Solo queda el consumo real.</p><p>Dise&#241;a la prueba. M&#237;dela. No lances sin ese dato.</p><h3><strong>Paso 4 &#8212; Modela el Pago Seg&#250;n el Patr&#243;n Real</strong></h3><p>Solo ahora, cuando sabes si tu consumo es recurrente, por consumo puntual repetible o por proyecto, tiene sentido derivar el suelo.</p><p>El suelo se modela como <strong>funci&#243;n de la unidad de valor recurrente que mantienes viva</strong> &#8212; no del coste de producci&#243;n del artefacto. Si lo que mantienes vivo requiere operaci&#243;n continua para seguir valiendo, esa operaci&#243;n es tu unidad de valor. Eso, y no el coste de escribir un informe, es lo que ancla el suelo.</p><h3><strong>Paso 5 &#8212; Recalibra con la Renovaci&#243;n del Primer Cliente</strong></h3><p>La &#250;nica se&#241;al leg&#237;tima de precio es el pago de repetici&#243;n de alguien que ya no te debe un favor.</p><p>Si el primer cliente renueva pasado el mes 3, <strong>y</strong> puedes verificar que renueva porque el estado de datos sigue siendo valioso para &#233;l &#8212; no porque le caes bien &#8212;, tienes la primera muestra real de tu suelo.</p><p>Si no tienes ese dato, tu suelo sigue siendo una especulaci&#243;n. Y reconocerlo es m&#225;s barato que fingir que sabes.</p><p>---</p><h2><strong>Las Objeciones Que Vas a Tener (y sus Respuestas)</strong></h2><h3><strong>"Mi servicio es genuinamente de consumo &#250;nico. &#191;No puedo productizarlo?"</strong></h3><p>S&#237; puedes. Pero el suelo y el modelo de cobro deben corresponder al consumo &#250;nico o al estado recurrente adyacente. Si vendes migraciones de datos, no fuerces una suscripci&#243;n de "mantenimiento de migraci&#243;n". O acepta el modelo de proyecto sin maquillarlo de recurrente, o encuentra el estado vivo que queda despu&#233;s de la migraci&#243;n &#8212; el sistema operativo, los accesos, los backups vivos.</p><h3><strong>"Mis clientes renuevan m&#225;s all&#225; del mes 3."</strong></h3><p>Entonces la hip&#243;tesis de recurrencia se confirma. Pero verifica la causa: &#191;renuevan porque el estado de datos sigue vivo y trabajando, o porque siguen pagando por la relaci&#243;n interpersonal con quien les vendi&#243;?</p><p>Renovar por relaci&#243;n es <strong>la misma trampa del primer cliente, diferida unos meses</strong>. Y se nota en un dato: si solo renuevan los clientes que te conocen en persona y no los que llegan por inbound publicitado, est&#225;s cobrando confianza, no producto.</p><h3><strong>"&#191;No necesito un suelo bajo para ganar a los primeros clientes?"</strong></h3><p>Confundes suelo con descuento comercial. El suelo es la puerta del modelo, derivada del patr&#243;n de consumo. El descuento es una t&#225;ctica de adquisici&#243;n.</p><p>Regalar descuentos por debajo del suelo no valida tu modelo: <strong>lo contamina</strong>. Convierte la muestra del primer cliente en todav&#237;a menos representativa de lo que ya era. Un cliente que te compra a mitad de precio por afinidad es la peor evidencia posible para fijar el suelo definitivo.</p><p>---</p><h2><strong>Una Nota de Honestidad Metodol&#243;gica</strong></h2><p>La cifra del 90% de fracaso en el primer a&#241;o y la evidencia de los RaaS que mueren en el mes 3 provienen del propio material de partida, no de una estad&#237;stica externa auditada.</p><p>&#218;salas como lo que son: <strong>hip&#243;tesis de trabajo</strong>, no datos verificados.</p><p>Y esa misma cautela refuerza el argumento central. Si ni siquiera las cifras del sector est&#225;n auditadas, cualquier suelo de precio calculado en abstracto vale todav&#237;a menos. La &#250;nica fuente de validaci&#243;n que existe son las renovaciones reales de clientes que ya no te deben nada.</p><p>---</p><h2><strong>La Idea Que Te Llevas</strong></h2><p>El suelo de precio no es un n&#250;mero que se calcula antes de lanzar. Es una <strong>consecuencia que solo existe si el patr&#243;n de consumo es recurrente</strong>.</p><p>Deja de calcular. Empieza a clasificar.</p><p>Pregunta primero si tu entregable es datos vivos o informe est&#225;tico. Encuentra el estado recurrente que queda vivo entre entregas. Val&#237;dalo contra el mes 3. Modela el pago seg&#250;n el consumo real. Y recalibra con la &#250;nica se&#241;al que vale: el pago de repetici&#243;n de alguien que ya no te debe un favor.</p><p>La mayor&#237;a de los que creen tener un problema de precio tienen en realidad un <strong>problema de inexistencia de producto recurrente</strong>. Y ning&#250;n suelo, por bajo o por alto que sea, lo arregla.</p><p>Hazlo bien y el n&#250;mero dejar&#225; de ser una conjetura. Ser&#225; una consecuencia.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/suelo-de-precio-servicio-productizado-modelo-20260905?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Claude Agent SDK Tutorial 2026: El Loop Ya no se Construye. Se Configura — y Casi Todos lo Hacéis Mal]]></title><description><![CDATA[Tutorial pr&#225;ctico del Claude Agent SDK 2026 en espa&#241;ol: instala el SDK, configura permisos, a&#241;ade MCP servers y domina el loop de agente que Anthropic ya ha automatizado.]]></description><link>https://newsletter.brianmenagomez.com/p/claude-agent-sdk-tutorial-2026-el-f2f</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/claude-agent-sdk-tutorial-2026-el-f2f</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Sat, 05 Sep 2026 07:00:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/28deb2b1-5703-469f-b0a0-c8507006f6a3_1080x719.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Cada Equipo que Conozco Pas&#243; Meses Cableando un Prompt a una Funci&#243;n y Llam&#225;ndolo "Agente". Anthropic Acaba de Enviar el Agente Mismo &#8212; con su Loop de Producci&#243;n Completo &#8212; como Dependencia de TypeScript</strong></h2><p><strong>*El mundo no est&#225; construyendo agentes. Est&#225; reimplementando un loop que ya est&#225; resuelto.</strong>*</p><p>La mayor&#237;a de vosotros hab&#233;is construido "agentes" as&#237;: llamada al modelo, prompt gigante, un bucle manual que parsea tool calls, maneja errores, reintenta, gestiona contexto... y reza por que en producci&#243;n no se rompa.</p><p>Eso era razonable en 2024.</p><p>En 2026, Anthropic ha distribuido como paquete npm (`@anthropic-ai/claude-agent-sdk`) el mismo loop de agente que potencia Claude Code. El agente planifica, usa herramientas y itera de forma aut&#243;noma. No es una API de chat con esteroides. Es el agente entero, empaquetado, versionado y mantenido.</p><p>Esto ya no es un problema de ingenier&#237;a. Es un problema de configuraci&#243;n.</p><p>Y casi todo el mundo lo est&#225; haciendo mal.</p><p>&lt;br&gt;</p><h2><strong>El Problema: Segu&#237;s Pagando el Peaje de una Abstracci&#243;n que ya no Necesit&#225;is</strong></h2><p>El argumento convencional dice que construir un agente significa elegir un framework de orquestaci&#243;n &#8212; LangChain, CrewAI, AutoGen, LangGraph &#8212; y montar t&#250; mismo los prompts, las llamadas al modelo y las herramientas.</p><p>El SDK invierte esa suposici&#243;n por completo.</p><p>&#10060; <strong>El enfoque heredado:</strong> Ciento cincuenta l&#237;neas de orquestaci&#243;n casera. Cada equipo reimplementa el plan-act&#250;a-observa-itera a mano, lo depura durante semanas y lo rompe en producci&#243;n.</p><p>&#9989; <strong>El enfoque del Claude Agent SDK:</strong> Importas el SDK, defines qu&#233; herramientas expones, qu&#233; permisos concedes y cu&#225;ntas iteraciones permites. El loop &#8212; la parte m&#225;s dif&#237;cil, fr&#225;gil y propensa a errores de cualquier agente &#8212; ya est&#225; escrito, endurecido y desplegado dentro de Claude Code.</p><p>El valor marginal de escribir tu propia orquestaci&#243;n colapsa.</p><p>Esto no es especulaci&#243;n. Es la diferencia entre reimplementar React a mano y usar React porque el DOM ya lo resuelve alguien que lleva a&#241;os depur&#225;ndolo en producci&#243;n.</p><p>&lt;br&gt;</p><h2><strong>La Evidencia: El Loop es el Producto, no un Detalle de Implementaci&#243;n</strong></h2><p>Mirad lo que expone el SDK en su superficie p&#250;blica. No es un constructor de prompts. Es la infraestructura del agente como API:</p><p><strong>Dos modos de entrada que mapean a necesidades de control distintas:</strong></p><ul><li><p>`query()` &#8212; para un resultado simple y streameado</p></li><li><p>`streamQuery()` &#8212; para control granular del ciclo de vida con soporte de `AbortSignal`</p></li></ul><p><strong>Sistema de herramientas embebido:</strong> El SDK trae el toolset completo de Claude Code &#8212; `Bash`, `Read`, `Write`, `Edit` &#8212; sin que tengas que implementar ni uno. Las capacidades web y de b&#250;squeda llegan v&#237;a conexiones MCP servers.</p><p><strong>Gobernanza como par&#225;metro de primera clase:</strong> `allowedTools`, `permissionMode`, y l&#237;mites de ejecuci&#243;n como `maxTurns` y `maxTokens` son opciones de configuraci&#243;n, no ideas de &#250;ltimo momento.</p><p><strong>Persistencia de sesi&#243;n:</strong> Hooks y checkpoints con `resume`. Una sesi&#243;n se puede pausar, inspeccionar y continuar. No es fire-and-forget.</p><p>Aqu&#237; est&#225; el c&#243;digo m&#237;nimo de un agente funcional:</p><p>```typescript</p><p>import { query } from '@anthropic-ai/claude-agent-sdk';</p><p>const result = await query({</p><p>prompt: 'Revisa el fichero src/main.ts, encuentra las vulnerabilidades y apl&#237;calas.',</p><p>options: {</p><p>allowedTools: ['Bash', 'Read', 'Edit'],</p><p>permissionMode: 'acceptEdits',</p><p>maxTurns: 25,</p><p>},</p><p>});</p><p>for await (const message of result) {</p><p>if (message.type === 'assistant') {</p><p>console.log(message.message.content);</p><p>}</p><p>}</p><p>```</p><p>Diez l&#237;neas. Un agente aut&#243;nomo que lee, edita y ejecuta. Sin framework. Sin bucle manual. Sin orchestrator.</p><p>Si alguien te dice que necesitas LangChain para esto, te est&#225; vendiendo una abstracci&#243;n que ya no necesitas.</p><p>&lt;br&gt;</p><h2><strong>El An&#225;lisis: Qu&#233; Significa que el Fabricante del Modelo Posea el Loop</strong></h2><p>Esto no es solo una mejora de developer experience. Es un cambio estructural del mercado.</p><p>Cuando el vendor del modelo posee el loop <strong>y opcionalmente el runtime</strong> &#8212; v&#237;a la gestionada Claude Agent API &#8212; el diferenciador para los desarrolladores deja de ser el c&#243;digo de orquestaci&#243;n.</p><p><strong>*El diferenciador es la configuraci&#243;n.</strong>*</p><p>Qu&#233; herramientas expones. Qu&#233; permission modes activas. C&#243;mo modelas los checkpoints para revisi&#243;n humana. Esas son las superficies donde un agente real triunfa o fracasa.</p><p>Y hay una segunda implicaci&#243;n m&#225;s profunda: <strong>las salvaguardas han dejado de ser un truco de prompt engineering.</strong></p><p>Antes, la seguridad de un agente depend&#237;a de escribir "por favor, no ejecutes comandos destructivos" en el prompt. Ahora es un contrato enforceable y auditable v&#237;a `permissionMode` y `allowedTools`.</p><p>Eso es precisamente lo que hace que los agentes aut&#243;nomos sean aprobables dentro de organizaciones reguladas. La seguridad ya no es una s&#250;plica al modelo. Es una propiedad de la API.</p><p>&lt;br&gt;</p><h2><strong>El M&#233;todo del Permiso por Dise&#241;o: C&#243;mo Llevar un Agente del Demo al Producto</strong></h2><p>Todos los tutoriales te ense&#241;an a hacer el agente funcionar. Casi ninguno te ense&#241;a a hacerlo <strong>seguro, gobernado y recuperable</strong>. Ese es el salto del demo al producto. Yo lo llamo <strong>El M&#233;todo del Permiso por Dise&#241;o</strong>, y tiene cinco pasos:</p><p><strong>Paso 1 &#8212; Instala y verifica antes de escribir c&#243;digo de features.</strong></p><p>Crea un proyecto Node/TypeScript, a&#241;ade `@anthropic-ai/claude-agent-sdk`, configura credenciales y ejecuta un hello-world. Confirma el requisito de servidor Node &#8212; esto <strong>no</strong> se ejecuta en el navegador, comunica v&#237;a Server-Sent Events &#8212; y examina los entry points del SDK antes de tocar nada m&#225;s.</p><p><strong>Paso 2 &#8212; Elige tu modo de ejecuci&#243;n con intenci&#243;n.</strong></p><p>Empieza en modo local con `query()`/`streamQuery()` contra la API del modelo. Solo m&#225;s tarde eval&#250;a la gestionada Claude Agent API si quieres que Anthropic opere el runtime, los reintentos y el escalado por ti.</p><p><strong>Paso 3 &#8212; Limita el poder del agente antes de darle herramientas.</strong></p><p>Esto es el coraz&#243;n del m&#233;todo:</p><p>```typescript</p><p>import { streamQuery } from '@anthropic-ai/claude-agent-sdk';</p><p>const controller = new AbortController();</p><p>const result = await streamQuery({</p><p>prompt: 'Refactoriza el m&#243;dulo de facturaci&#243;n manteniendo la API p&#250;blica.',</p><p>options: {</p><p>allowedTools: ['Bash', 'Write', 'Edit'], // Solo lectura + edici&#243;n acotada</p><p>permissionMode: 'default',               // Pide confirmaci&#243;n para lo destructivo</p><p>maxTurns: 40,                            // Presupuesto de ejecuci&#243;n duro</p><p>maxTokens: 100_000,                      // Presupuesto de tokens duro</p><p>},</p><p>signal: controller.signal,</p><p>});</p><p>for await (const event of result) {</p><p>switch (event.type) {</p><p>case 'init': console.log('Sesi&#243;n iniciada:', event.sessionId); break;</p><p>case 'agent_message': console.log('Agente:', event.message.content); break;</p><p>case 'tool_use': console.log('Tool:', event.name, JSON.stringify(event.input)); break;</p><p>case 'result': console.log('Tarea completada'); break;</p><p>}</p><p>}</p><p>// Para cancelar un agente desbocado a mitad de ejecuci&#243;n:</p><p>controller.abort();</p><p>```</p><p>Tratad los permisos como parte de vuestro dise&#241;o de API, no como una idea de seguridad de &#250;ltimo momento. El `AbortSignal` aqu&#237; no es un extra. Es la diferencia entre un agente desbocado que cuesta tokens y uno que se puede detener.</p><p><strong>Paso 4 &#8212; A&#241;ade capacidades reales v&#237;a tools y MCP, no engordando prompts.</strong></p><p>Nada de prompt-hacking. Extender el agente es declarativo. Un custom tool se define con schema:</p><p>```typescript</p><p>// registrar.ts</p><p>import { query } from '@anthropic-ai/claude-agent-sdk';</p><p>// Con un MCP server, el agente descubre y llama las tools del protocolo est&#225;ndar</p><p>const result = await query({</p><p>prompt: 'Consulta la base de datos de clientes y resume los impagos del &#250;ltimo trimestre.',</p><p>options: {</p><p>mcpServers: {</p><p>postgres: {</p><p>command: 'npx',</p><p>args: ['-y', '@modelcontextprotocol/server-postgres'],</p><p>env: { POSTGRES_CONNECTION_STRING: process.env.DATABASE_URL! },</p><p>},</p><p>},</p><p>permissionMode: 'default',</p><p>},</p><p>});</p><p>```</p><p>Aqu&#237; est&#225; el giro estrat&#233;gico: <strong>gran parte de la "ingenier&#237;a de agentes" se convierte en curaci&#243;n de MCP servers.</strong> Igual que la gesti&#243;n de dependencias define la mayor parte del trabajo backend tradicional, declarar MCP servers define la mayor parte del trabajo con agentes. Reutilizas el ecosistema MCP existente en lugar de escribir c&#243;digo de conexi&#243;n a medida para cada fuente de datos.</p><p>Y MCP es lo que evita el lock-in aburrido. No escribes connectors bespoke para cada API. Declaras servers y el agente consume lo que el ecosistema ya ofrece.</p><p><strong>Paso 5 &#8212; Productiviza el ciclo de vida de la sesi&#243;n.</strong></p><p>Este es el paso que separa el demo del producto. Necesit&#225;is:</p><p>1. <strong>Hooks para logging y aprobaci&#243;n humana</strong> &#8212; wire up un hook para auditar cada tool call</p><p>2. <strong>Persistencia de checkpoints</strong> &#8212; que una tarea larga se pueda pausar, inspeccionar y continuar</p><p>3. <strong>Resume para trabajos largos</strong> &#8212; sesiones recuperables, no fire-and-forget</p><p>```typescript</p><p>import { query } from '@anthropic-ai/claude-agent-sdk';</p><p>// Simula el control de sesi&#243;n: hooks que pausan la ejecuci&#243;n</p><p>// y checkpoints que permiten inspeccionarla y resumirla</p><p>const result = await query({</p><p>prompt: 'Migra los datos del CSV antiguo a la nueva tabla normalizada.',</p><p>options: {</p><p>allowedTools: ['Bash', 'Read', 'Write', 'Edit'],</p><p>permissionMode: 'default',</p><p>hooks: {</p><p>PreToolUse: (hookInput) =&gt; {</p><p>console.log('[AUDIT] Tool a punto de ejecutarse:', hookInput.toolName);</p><p>// Devuelve approval para controlar ejecuciones sensibles</p><p>return { behavior: hookInput.toolName === 'Bash' ? 'deny' : 'allow' };</p><p>},</p><p>Stop: async () =&gt; {</p><p>console.log('[CHECKPOINT] Guardando estado de sesi&#243;n...');</p><p>// Persiste aqu&#237; el checkpoint para un futuro resume</p><p>},</p><p>},</p><p>maxTurns: 60,</p><p>},</p><p>});</p><p>for await (const message of result) {</p><p>if (message.type === 'result') {</p><p>console.log('Resultado final:', message.result);</p><p>}</p><p>}</p><p>```</p><p>Aqu&#237; es donde el SDK demuestra que no es solo "el CLI de Claude Code con gabardina". Un wrapper de subprocess o una llamada chat cruda <strong>no exponen</strong> la superficie de embedding que s&#237; expone el SDK: checkpoints, resume, hooks, permission modes y `AbortSignal`.</p><p>&lt;br&gt;</p><h2><strong>Objeci&#243;n 1: "&#191;Y el lock-in? &#191;Mis tool calls y mi estado pasan por infraestructura de Anthropic?"</strong></h2><p>Distinguid los dos modos, porque son muy distintos:</p><p><strong>Modo local:</strong> El SDK ejecuta el loop en tu servidor Node. Las llamadas al modelo van a un endpoint compatible con Anthropic &#8212; incluyendo Bedrock o Vertex si ya est&#225;is ah&#237;. Tus tool calls y tu estado no pasan por la infraestructura gestionada de Anthropic.</p><p><strong>Gestionada Claude Agent API:</strong> Anthropic opera el runtime, los reintentos y el escalado. Aqu&#237; s&#237;, tus outputs de herramientas y estado intermedio viven en un runtime del vendor. Conveniente, pero cambia la forma de tus decisiones de portabilidad multi-modelo y residencia de datos.</p><p>La respuesta honesta no es fingir que no hay trade-off. Es ser expl&#237;cito sobre qu&#233; deja tu control en cada modo. Si la residencia de datos es dura en tu sector, modo local con Bedrock/Vertex. Si quieres escalar sin operar infraestructura, gestionada.</p><p>&lt;br&gt;</p><h2><strong>Objeci&#243;n 2: "&#191;Por qu&#233; no OpenAI Agents SDK, LangGraph o un framework abierto multi-modelo?"</strong></h2><p>Este es un trade-off, no una victoria. El Claude Agent SDK es TypeScript-first y est&#225; lockeado al modelo de Anthropic. A cambio compras comportamiento de agente turnkey y un runtime opinado.</p><p>&#10060; <strong>Eleg&#237;s por moda:</strong> Framework que soporta 47 modelos porque "da flexibilidad". Nunca us&#225;is 46 de ellos. Pag&#225;is el coste de abstracci&#243;n por un caso que no vais a tener.</p><p>&#9989; <strong>Eleg&#237;s por criterio:</strong> &#191;Est&#225;s comprometido con un solo vendor y quieres el loop m&#225;s endurecido disponible? Claude Agent SDK. &#191;Necesitas portabilidad multi-modelo como requisito arquitect&#243;nico real &#8212; no hipot&#233;tico? Eval&#250;a LangGraph o semejantes.</p><p>La pregunta no es "cu&#225;l es mejor". Es "&#191;cu&#225;nto vale un loop endurecido para ti y qu&#233; est&#225;s dispuesto a ceder en portabilidad?".</p><p>&lt;br&gt;</p><h2><strong>Objeci&#243;n 3: "Ya puedo hacer shell out al CLI de `claude` o llamar a la API con mi propio loop. &#191;Qu&#233; a&#241;ade un SDK?"</strong></h2><p>El CLI te da un agente, pero no te da una superficie de integraci&#243;n. No puedes exponer program&#225;ticamente checkpoints, hooks, `permissionMode` ni `AbortSignal` desde un subprocess wrapper.</p><p>Un raw chat call te da un modelo, pero te obliga a ti a reimplementar el loop entero &#8212; planificaci&#243;n, iteraci&#243;n, gesti&#243;n de contexto, manejo de errores.</p><p><strong>*El SDK no es el agente en otro formato. Es la superficie de embedding que ni el CLI ni una chat call te dan.</strong>* Y es un agente mantenido y versionado, no un prompt loop que forkaste en 2024 y que arrastras desde entonces.</p><p>&lt;br&gt;</p><h2><strong>Conclusi&#243;n: La Ingenier&#237;a de Agentes se Ha Convertido en Configuraci&#243;n</strong></h2><p>Lo que os llev&#243; meses construiros &#8212; el loop de plan-act&#250;a-observa-itera &#8212; ahora es una dependencia que actualiz&#225;is con `npm install`.</p><p>El trabajo que queda es m&#225;s interesante, no menos. Dise&#241;ar qu&#233; herramientas exponer. Definir los l&#237;mites de permisos como parte del dise&#241;o de API. Modelar checkpoints para revisi&#243;n humana. Construir harnesses de evaluaci&#243;n que midan a vuestro agente contra casos reales.</p><p>Las claves para llevarte hoy:</p><ul><li><p><strong>El loop es el producto.</strong> Dejad de reimplementarlo.</p></li><li><p><strong>Los permisos no son una idea de &#250;ltimo momento.</strong> Son tu dise&#241;o de API.</p></li><li><p><strong>MCP es vuestra capa de integraci&#243;n.</strong> Curaci&#243;n de servers, no c&#243;digo bespoke.</p></li><li><p><strong>Los checkpoints y hooks separan el demo del producto.</strong></p></li><li><p><strong>Sed expl&#237;citos sobre qu&#233; modo de ejecuci&#243;n us&#225;is</strong> &#8212; y qu&#233; sale de vuestro control en cada uno.</p></li></ul><p>En 2024, construir un agente era un problema de ingenier&#237;a que casi nadie resolv&#237;a bien. En 2026, es un problema de configuraci&#243;n que casi cualquiera puede ejecutar &#8212; si entiende qu&#233; configurar y por qu&#233;.</p><p>Los que sigan cableando prompts y funciones a mano dentro de dos a&#241;os no estar&#225;n "construyendo agentes". Estar&#225;n pagando el peaje de una abstracci&#243;n que ya nadie necesita.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/claude-agent-sdk-tutorial-2026-20260905?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Apify Web Scraping Tutorial 2026: Deja de Escribir Scrapers. Forkea, Despliega y Cobra por Operar]]></title><description><![CDATA[Tutorial completo de Apify 2026: por qu&#233; el scraping es orquestaci&#243;n, no selectores. Del Apify Store al Actor serverless con Crawlee, en 5 pasos.]]></description><link>https://newsletter.brianmenagomez.com/p/apify-web-scraping-tutorial-2026-bfb</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/apify-web-scraping-tutorial-2026-bfb</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Fri, 04 Sep 2026 07:00:15 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0f82833f-eee1-496f-a141-74988dba6f95_1080x639.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Deja de Escribir Scrapers. Nadie ha Fallado Nunca por sus Selectores CSS</strong></h2><p>Que te bloqueen en la ejecuci&#243;n 47 a las 3 de la ma&#241;ana es lo que mata tu pipeline. No un selector roto. No una p&#225;gina que cambi&#243; de markup.</p><p>Nadie fracasa en web scraping porque su `document.querySelector` estuviera mal escrito. Fracasan porque el sitio objetivo detecta tu fingerprint TLS en el request 43, te sirve un 403 y tu script en bucle reintenta hasta que el VPS se queda sin memoria.</p><p><strong>*Apify no es una empresa de scraping. Es una empresa de anti-fragilidad.</strong>* Esa distinci&#243;n es toda la lecci&#243;n.</p><p>Y aqu&#237; est&#225; la parte que ning&#250;n tutorial te cuenta: el c&#243;digo de scraping &#8212; lo que todo el mundo comenta &#8212; te lo regalan gratis. Es open source. Lo que pagas es la fontaner&#237;a: rotaci&#243;n de proxies, reintentos, escalado serverless, almacenamiento y el hecho de que tu scraper siga vivo ma&#241;ana.</p><p>Vamos a construir algo real. Pero primero, derribemos la suposici&#243;n equivocada.</p><p>---</p><h2><strong>El Problema: Crees que el Scraping es un Problema de C&#243;digo</strong></h2><p>Hay dos mentiras circulando por ah&#237; sobre web scraping en 2026.</p><p>La primera dice que scraping est&#225; muriendo porque la IA "ya lo sabe todo". Falso. La adopci&#243;n de LLMs ha <strong>*aumentado</strong>* la demanda de datos frescos y estructurados para RAG pipelines y conjuntos de evaluaci&#243;n. Un modelo fundacional no puede saber el precio actual de un producto ni el estado de una oferta. Eso solo lo da un scraper que corre hoy.</p><p>La segunda mentira dice que scraping es trivial: un bucle `for` sobre URLs con una librer&#237;a de selectores.</p><p>Eso funciona exactamente hasta la primera p&#225;gina que te bloquea.</p><p><strong>El cuello de botella se desplaz&#243;.</strong> Ya no es "conseguir el HTML". Es mantener el pipeline vivo a escala contra defensas anti-bot que fingerprintean tu TLS, throttlean tu concurrencia y te sirven honeypots que parecen datos reales.</p><p>El c&#243;digo nunca fue el problema. <strong>*La resiliencia es el problema.</strong>* Y esa es exactamente el &#225;rea que Apify construy&#243; alrededor de un core open source llamado Crawlee.</p><p>---</p><h2><strong>La Evidencia: el Scraper que Gana Nunca se Ejecuta en tu Port&#225;til</strong></h2><p>Miremos el boilerplate. Un scraper ingenuo con `axios` + `cheerio`:</p><p>```javascript</p><p>// scraper_naive.js &#8212; muere en el primer 403</p><p>const axios = require('axios');</p><p>const cheerio = require('cheerio');</p><p>const urls = ['https://ejemplo.com/a', 'https://ejemplo.com/b'];</p><p>for (const url of urls) {</p><p>const { data } = await axios.get(url); // &#8594; 403 en producci&#243;n, sin reintento</p><p>const $ = cheerio.load(data);</p><p>const titulo = $('h1').text();</p><p>console.log(titulo);</p><p>}</p><p>```</p><p>Cinco l&#237;neas. Funciona en local. Y se rompe en producci&#243;n cuando el sitio detecta que no hay sesi&#243;n, que no hay headers reales de navegador, que el ritmo de requests es de bot.</p><p>Ahora el mismo objetivo con <strong>Crawlee</strong> en Node:</p><p>```javascript</p><p>// crawlee_scraper.js &#8212; sobrevive a producci&#243;n</p><p>import { CheerioCrawler } from 'crawlee';</p><p>const crawler = new CheerioCrawler({</p><p>maxRequestsPerCrawl: 100,</p><p>maxConcurrency: 10,</p><p>// Session pool: rotaci&#243;n de identidad + reintentos autom&#225;ticos</p><p>useSessionPool: true,</p><p>sessionPoolOptions: {</p><p>maxPoolSize: 50,</p><p>blockedStatusCodes: [403, 429], // detecta bloqueo &#8594; gira sesi&#243;n</p><p>},</p><p>requestHandler: async ({ request, $ }) =&gt; {</p><p>await crawler.pushData({</p><p>url: request.url,</p><p>titulo: $('h1').text().trim(),</p><p>fecha: $('time').attr('datetime'),</p><p>});</p><p>},</p><p>});</p><p>await crawler.run(['https://ejemplo.com/listado']);</p><p>```</p><p>La diferencia no son los selectores. Son ~15 l&#237;neas de configuraci&#243;n que gestionan reintentos, detecci&#243;n de bloqueo y salida estructurada a dataset.</p><p><strong>*Eso es el valor de Crawlee: no es lo que raspa. Es lo que ya no tienes que escribir t&#250;.</strong>*</p><p>El mismo patr&#243;n existe en Python. Para objetivos con mucho JavaScript, usas el `PlaywrightCrawler`, que levanta un navegador headless con un session pool consolidado. Intentar eso a mano &#8212; con Playwright crudo y l&#243;gica manual de waits &#8212; es semanas de debugging que nadie te agradece.</p><p>Y aqu&#237; est&#225; la decisi&#243;n estrat&#233;gica que casi nadie analiza: <strong>Apify renombr&#243; su SDK a Crawlee en 2022 y lo liber&#243; como open source con un nombre neutral.</strong> No es caridad. Es que el foso no est&#225; en el c&#243;digo &#8212; que cualquiera puede forkear &#8212; sino en la capa de operaci&#243;n: inventario de proxies, uptime, scheduling, almacenamiento y tooling de compliance.</p><p>Regalaron el cebo. Venden la fontaner&#237;a.</p><p>---</p><h2><strong>El An&#225;lisis: el Modelo de Actors es Serverless con Entrada y Salida Tipadas</strong></h2><p>Aqu&#237; es donde el tutorial se separa del chiste. Porque un "Actor" en Apify no es "c&#243;digo subido a la nube". Es un cambio de mentalidad:</p><p><strong>*Un scraper deja de ser un script y se convierte en un microservicio serverless con contrato de entrada, contrato de salida y ciclo de vida.</strong>*</p><p>Envolver tu crawler con el patr&#243;n Actor:</p><p>```javascript</p><p>// main.js &#8212; el mismo c&#243;digo corre en local y en la nube</p><p>import { Actor } from 'apify';</p><p>import { CheerioCrawler } from 'crawlee';</p><p>await Actor.main(async () =&gt; {</p><p>const input = await Actor.getInput(); // viene del INPUT_SCHEMA</p><p>const { urls, maxItems } = input;</p><p>const crawler = new CheerioCrawler({</p><p>maxRequestsPerCrawl: maxItems,</p><p>requestHandler: async ({ request, $ }) =&gt; {</p><p>await Actor.pushData({</p><p>url: request.url,</p><p>titulo: $('h1').text().trim(),</p><p>});</p><p>},</p><p>});</p><p>await crawler.run(urls);</p><p>console.log('Actor completado.');</p><p>});</p><p>```</p><p>Con un `INPUT_SCHEMA.json` que declara qu&#233; acepta tu Actor:</p><p>```json</p><p>{</p><p>"title": "Raspador de ejemplo",</p><p>"type": "object",</p><p>"properties": {</p><p>"urls": { "type": "array", "items": { "type": "string" } },</p><p>"maxItems": { "type": "integer", "minimum": 1, "maximum": 1000 }</p><p>},</p><p>"required": ["urls"]</p><p>}</p><p>```</p><p>Ejecutas eso con `apify run` en local. Cuando funciona, haces `apify push` y el mismo c&#243;digo corre en un runtime serverless de Apify &#8212; escalando, con retries y con los resultados escritos en un dataset acoplado.</p><p>Ya no es un script. <strong>*Es una pieza componible en un sistema m&#225;s grande.</strong>*</p><p>---</p><h2><strong>El An&#225;lisis (II): la Ola LLM le Dio la Vuelta a la Narrativa</strong></h2><p>Apify pas&#243; de "raspamos webs" a "alimentamos modelos y agentes de IA". Ese giro no es cosm&#233;tico. Es real, y cambia c&#243;mo deber&#237;as arquitecturar tus outputs.</p><p>Tres razones concretas:</p><p>1. <strong>Los sistemas RAG necesitan datos actuales</strong> que ning&#250;n periodo de entrenamiento de un modelo fundacional puede tener.</p><p>2. <strong>Los conjuntos de evaluaci&#243;n necesitan ground truth capturado hoy</strong> &#8212; no datos de hace seis meses.</p><p>3. <strong>Los frameworks de agentes permiten que un LLM invoque un scraper como tool a mitad de tarea.</strong></p><p>La consecuencia pr&#225;ctica: en 2026, raspas para que un agente o un pipeline de datos consuma tu output. Eso significa <strong>JSON limpio y con esquema definido gana a HTML bonito</strong>, siempre.</p><p>Los tutoriales cl&#225;sicos de scraping te ense&#241;an a guardar el HTML crudo. El futuro es un Actor que devuelve schema-shaped JSON que un LLM puede parsear sin instrucciones adicionales.</p><p>---</p><h2><strong>El Framework de los 5 Movimientos del Actor</strong></h2><p>Vale, vamos a cortar el rollo te&#243;rico. Este es el <strong>Framework de los 5 Movimientos del Actor</strong> &#8212; el m&#233;todo &#250;nico de operador que uso para cualquier recolector nuevo.</p><h3><strong>Paso 1: Forkea antes de escribir c&#243;digo</strong></h3><p>Busca en el Apify Store un Actor que se aproxime a tu objetivo. No reedites la rueda.</p><p>Los source code de los Actors del Store son open source. Cl&#243;nalos en un proyecto local de Crawlee, inspecciona su `INPUT_SCHEMA` y entiende qu&#233; hacen antes de escribir una sola l&#237;nea.</p><p>La ruta t&#237;pica de 2026 es: <strong>reusar &#8594; forkear &#8594; customizar &#8594; desplegar.</strong> No empezar de cero.</p><h3><strong>Paso 2: Construye y estresa en local con Crawlee</strong></h3><p>Elige tu tipo de crawler deliberadamente:</p><ul><li><p><strong>CheerioCrawler</strong> para p&#225;ginas est&#225;ticas r&#225;pidas</p></li><li><p><strong>Playwright/PuppeteerCrawler</strong> solo donde el renderizado sea imprescindible</p></li></ul><p>Activa siempre el session pool y configura la l&#243;gica de reintentos. <strong>Asume que el objetivo te va a bloquear.</strong> No lo asumas cuando ocurra.</p><h3><strong>Paso 3: Convierte el script en Actor</strong></h3><p>Envu&#233;lvelo en `Actor.main()`, declara el `INPUT_SCHEMA` (URLs, selectores, l&#237;mites) y el output (dataset + key-value store). Prueba end-to-end con `apify run` en local.</p><p>Configura la compliance desde el d&#237;a uno &#8212; respeta `robots.txt` y limita con `maxRequestsPerCrawl`:</p><p>```javascript</p><p>// Defiende tu reputaci&#243;n de IP desde la primera ejecuci&#243;n</p><p>const crawler = new CheerioCrawler({</p><p>maxRequestsPerCrawl: 500,</p><p>maxConcurrency: 5,</p><p>// Pol&#237;tica cort&#233;s: no impactes al objetivo</p><p>});</p><p>```</p><h3><strong>Paso 4: Despliega y operacionaliza</strong></h3><p>`apify push` a la nube. Conecta el scheduling y monitoriza la tasa de requests bloqueados. Si ese n&#250;mero sube, quieres saberlo t&#250; &#8212; antes de que lo note tu consumidor downstream.</p><h3><strong>Paso 5: Dise&#241;a para el pipeline de IA</strong></h3><p>Exponer el Actor terminado como API o tool invocable &#8212; mediante la API de Apify, una integraci&#243;n con LangChain o una definici&#243;n de tool para un agente.</p><p>```javascript</p><p>// Un agente LLM llama a otro Actor como tool</p><p>// Esto convierte el scraping en una tool-call dentro del loop del agente</p><p>const resultado = await Actor.call('tu-usuario/raspador-ejemplo', {</p><p>urls: ['https://ejemplo.com/productos'],</p><p>maxItems: 20,</p><p>});</p><p>// resultado.dataset &#8594; JSON esquematizado, listo para el LLM</p><p>```</p><p><strong>Trata el scraper como un microservicio de datos, no como un script de un solo uso.</strong> Esa es la mentalidad m&#225;s a prueba de futuro que puedes adoptar.</p><p>---</p><h2><strong>Las Objeciones que te Est&#225;s Planteando Ahora</strong></h2><p><strong>"&#191;Y por qu&#233; no ejecuto Scrapy en mi propio VPS?"</strong></p><p>Para trabajos peque&#241;os de un solo uso, self-hosting est&#225; genial. Dicho sin rodeos. Pero hay un impuesto operativo oculto: rotaci&#243;n de proxies, reputaci&#243;n de IP, backoff de reintentos, scheduling, durabilidad de almacenamiento y monitoring &#8212; todo se convierte en tu problema.</p><p>El c&#225;lculo cambia cuando necesitas recolecci&#243;n continua y resiliente. Ah&#237; es donde un VPS barato te cuesta m&#225;s en horas de operaci&#243;n de lo que ahorras en infraestructura.</p><p><strong>"Si hay cientos de Actors listos en el Store, &#191;para qu&#233; escribir c&#243;digo?"</strong></p><p>Los Actors listos son un punto de partida, no una l&#237;nea de meta. Los objetivos cambian su markup y su postura anti-bot en semanas. Necesitar&#225;s selectores custom, manejo de paginaci&#243;n, flujos de autenticaci&#243;n o reshaping de output.</p><p>El c&#243;digo open source es el producto real: fork&#233;alo, ad&#225;ptalo y solo paga por la operaci&#243;n.</p><p><strong>"&#191;No es scraping una violaci&#243;n de t&#233;rminos de servicio o GDPR?"</strong></p><p>Abordemos esto sin ambig&#252;edad:</p><ul><li><p>`robots.txt` y T&#233;rminos de Servicio <strong>no son lo mismo que la ley</strong></p></li><li><p>Los datos personales activan obligaciones de GDPR independientemente de la herramienta</p></li><li><p>Apify incluye utilidades de cumplimiento con `robots.txt` y gu&#237;as</p></li></ul><p>Trata el scraping como una pr&#225;ctica de ingenier&#237;a defendible con guardarra&#237;les. No como un hack de sombrero gris. Respeta `robots.txt`, limita tu tasa, no recolectes datos personales sin base legal &#8212; y estar&#225;s construyendo algo que sobrevive tanto a auditores como a GitHub Issues.</p><p>---</p><h2><strong>El Resumen que te Llevas</strong></h2><p>El scraping no es un problema de selectores CSS. Es un problema de orquestaci&#243;n anti-fr&#225;gil.</p><p>Crawlee es el cerebro open source que gestiona sesiones, reintentos y detecci&#243;n de bloqueo. Apify es la capa de operaci&#243;n que ejecuta ese c&#243;digo como un Actor serverless &#8212; con scheduling, almacenamiento y escalado que nunca se ejecuta en tu port&#225;til.</p><p><strong>Los cinco movimientos:</strong> forkear el Store &#8594; estresar en local &#8594; envolver como Actor &#8594; desplegar con monitoring &#8594; exponerlo como data microservice para tu pipeline de IA.</p><p>La empresa que tiene scraping como <em>operaci&#243;n</em>, no como <em>c&#243;digo</em>, es la que gana cuando el objetivo cambia su markup a las 2 de la madrugada.</p><p>Construye el scraper que sobreviva a la ejecuci&#243;n 47. Porque esa ejecuci&#243;n va a llegar &#8212; la &#250;nica pregunta es si tu pipeline la atraviesa en pie.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/apify-web-scraping-tutorial-2026-20260904?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Tu Primer Cliente Empaquetó el Artefacto Equivocado: Extrae el Estado Recurrente, No el Entregable]]></title><description><![CDATA[El 90% de las primeras productizaciones fracasan: empaquetan el informe est&#225;tico en vez del dato vivo. Checklist de 5 puntos antes de construir producto.]]></description><link>https://newsletter.brianmenagomez.com/p/tu-primer-cliente-empaqueto-el-artefacto</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/tu-primer-cliente-empaqueto-el-artefacto</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Fri, 04 Sep 2026 07:00:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ba37f46d-7463-446b-894b-bad6c2907848_1080x810.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>El 90% de las Primeras Productizaciones Fracasan &#8212; y No Es por el Producto. Es por Confundir a un Cliente Feliz con un Mercado Validado.</strong></h2><p>Crees que tu primer cliente fue tu validaci&#243;n. Que entregaste un trabajo brillante, que pag&#243; a gusto, que te dijo "esto lo comprar&#237;a cualquier empresa".</p><p><strong>*Te has equivocado de diagn&#243;stico.</strong>*</p><p>El 90% de las primeras productizaciones fracasan exactamente en ese punto. Y no por mala ejecuci&#243;n, ni por falta de calidad, ni por un producto feo.</p><p>Fracasan porque <strong>confundes una relaci&#243;n interpersonal con un segmento de mercado validado</strong>.</p><p>Tu primer cliente no compr&#243; tu producto. Compr&#243; tu capacidad de improvisar bajo ambig&#252;edad. Compr&#243; que le cogieras el tel&#233;fono, que entendieras su contexto sin que te lo explicara del todo, que iteraras quince veces sobre un entregable mal definido.</p><p><strong>*Ninguna de esas se&#241;ales se transfiere a un desconocido.</strong>*</p><p>El error gemelo es definir el "producto" por los entregables que produjiste para un solo cliente. Empaquetas el informe. La plantilla. El dashboard. Y llamas a eso producto.</p><p>Es historia. No producto.</p><p>Llevo a&#241;os enviando software en Espa&#241;a. He visto docenas de agencias y freelancers morir en esta bifurcaci&#243;n. La se&#241;al no es que un cliente pagara una vez con entusiasmo. La se&#241;al es que varios desconocidos verbalicen el mismo dolor &#8212; sin tu ayuda y sin haberte visto nunca.</p><p>En este art&#237;culo te doy el checklist que separa el oro de la paja antes de que inviertas un solo d&#237;a construyendo.</p><h2><strong>La Trampa del Sesgo de Supervivencia Inverso</strong></h2><p>Aqu&#237; est&#225; lo que nadie te dice cuando miras agencias que "productizaron tras su primer cliente".</p><p>Casi todas mintieron (o al menos, comprimieron la historia).</p><p>Cuando una agencia te ense&#241;a su servicio productizado en la web, suele haber tenido <strong>entre 5 y 10 clientes parecidos antes de empaquetar</strong>. Pero venden la narrativa como si el producto hubiera nacido del encargo n&#250;mero uno.</p><p>Ese es el sesgo de supervivencia inverso: solo ves a los que sobrevivieron al jinete del caballo blanco de un buen primer cliente. No ves a los cientos que empaquetaron el artefacto de un informe one-off y murieron en el intento porque ning&#250;n desconocido lo compr&#243;.</p><p>La cifra del 90% es un marco argumentativo, no un estudio externo. Pero el mecanismo es real y destruye m&#225;s operaciones que cualquier error t&#233;cnico.</p><p>Mec&#225;nica del fallo:</p><p>&#8594; Un fundador entrega un proyecto bespoke excelente a un cliente.</p><p>&#8594; El cliente, encantado, dice que "comprar&#237;a esto como producto".</p><p>&#8594; El fundador construye durante meses alrededor del entregable espec&#237;fico que produjo para ese cliente.</p><p>&#8594; Saca el producto. Los desconocidos no lo compran.</p><p>&#8594; El fundador concluye que "no ten&#237;a demanda". En realidad nunca la testeo.</p><p>El primer cliente <strong>no demuestra que haya demanda</strong>. Demuestra que t&#250; sabes construir rapport y entregar bajo ambig&#252;edad. Exactamente las dos competencias que menos se transfieren a extra&#241;os.</p><p>Un case study valida calidad de entrega y relaci&#243;n. No valida tama&#241;o de mercado. Un segmento necesita repetibilidad y compradores que no te conocen. No an&#233;cdotas.</p><h2><strong>La Bifurcaci&#243;n Real: Datos Vivos vs. Informe Est&#225;tico</strong></h2><p>Aqu&#237; est&#225; la decisi&#243;n que separa al producto del servicio renombrado.</p><p>Todo proyecto que entregas produce uno de dos tipos de output:</p><p>&#10060; <strong>Informe est&#225;tico</strong> &#8212; Un entregable one-off cuyo valor se deprecia a cero en el momento de la entrega. Una auditor&#237;a. Un informe de mercado. Un estudio de viabilidad. Un an&#225;lisis de seguridad puntual. Se factura una vez, se entrega, se acab&#243;. Si lo empaquetas, no has creado un producto: has creado <strong>un PDF con alcance</strong>. Eso es un servicio renombrado.</p><p>&#9989; <strong>Estado de datos vivos</strong> &#8212; Una necesidad que cambia por s&#237; sola y exige actualizaci&#243;n recurrente. El estado de cumplimiento normativo de una empresa. El inventario en tiempo real de un sector. El estado de las convocatorias de ayudas que afectan a un cliente. La operaci&#243;n de monitorizaci&#243;n de algo que muta cada semana.</p><p>La distinci&#243;n es brutal: <strong>el producto no es el entregable que produjiste. Es el umbral recurrente que dispara tu trabajo.</strong></p><p>Pongo un ejemplo m&#237;o. Gestiono gestoriascercademi.com, un directorio de gestor&#237;as en Espa&#241;a. Eso no naci&#243; de un informe est&#225;tico para un cliente. Naci&#243; de un dato vivo: la geolocalizaci&#243;n cambiante del tejido de asesor&#237;as y la demanda de b&#250;squeda local que muta cada trimestre. El dato vivo justifica la actualizaci&#243;n. La actualizaci&#243;n justifica la recurrencia.</p><p>Un informe no. Un informe es una foto. Nadie paga una suscripci&#243;n mensual por ver la misma foto.</p><p>Esto conecta directamente con el aprendizaje RaaS que ya document&#233;: <strong>el 90% de los nichos de suscripci&#243;n mueren antes de lanzar porque el fundador elige datos que cambian demasiado despacio</strong> para justificar una suscripci&#243;n mensual.</p><p>Si el dato del cliente cambia una vez al a&#241;o, no hay raz&#243;n para que nadie pague mensualmente. El nicho muere igual que muere el producto extra&#237;do del informe one-off. Misma ra&#237;z: falta de cadencia de cambio.</p><p>Diagn&#243;stico obligatorio antes de construir nada:</p><p>&#8594; &#191;El valor que entregaste muere al entregarlo, o renace solo porque el mundo sigue girando?</p><p>&#8594; &#191;El estado del problema del cliente cambia sin que t&#250; lo toques?</p><p>&#8594; &#191;Podr&#237;as escribir un cron job que detectara cu&#225;ndo hay que regenerar ese valor?</p><p>Si respondes no a las tres, <strong>no tienes un producto. Tienes un servicio con buena presentaci&#243;n.</strong></p><h2><strong>La Prueba de Recompra Sin Preguntar</strong></h2><p>El segundo error que mata la primera productizaci&#243;n es testear la demanda preguntando.</p><p>"&#191;Comprar&#237;as esto otra vez, [nombre del cliente]?"</p><p><strong>*Un "s&#237;, me encant&#243;" no es validaci&#243;n. Es cortes&#237;a post-entrega.</strong>*</p><p>La intenci&#243;n declarada post-entrega est&#225; contaminada por la relaci&#243;n. Ese cliente te debe favores, le caes bien, le resolviste un problema real. Por supuesto que dice que s&#237;. Decir no ser&#237;a un acto social desagradable.</p><p>El test real tiene dos capas:</p><p><strong>Capa 1 &#8212; La necesidad viva existe sin ti.</strong> &#191;El dato del cliente seguir&#237;a cambiando aunque desaparecieras? Si la respuesta es "no, el proyecto acab&#243; y ya no hay nada que actualizar", no hay recurrencia. Da igual lo que diga el cliente.</p><p><strong>Capa 2 &#8212; Desconocidos lo verbalizan solos.</strong> Encuentra al menos 3 desconocidos (no referidos por el cliente #1) que describan el mismo problema <strong>con sus propias palabras</strong> y el mismo umbral de dolor. Si nadie lo verbaliza sin que t&#250; lo nombres primero, el segmento no existe.</p><p>Aqu&#237; la trampa es sutil: un primer cliente conseguido por recomendaci&#243;n c&#225;lida es <strong>una muestra contaminada</strong>. Su compra arrastra el capital social del referidor. No es una se&#241;al limpia de demanda; es una se&#241;al de que alguien influyente apost&#243; por ti.</p><p>La se&#241;al limpia es la compra de un extra&#241;o que no te conoce, no ha visto tu trabajo y aun as&#237; paga por un output definido.</p><h2><strong>El Checklist de 5 Decisiones Antes de Construir Tu Producto</strong></h2><p>No hace falta esperar a tener 3 clientes parecidos para empezar. Tampoco hace falta rechazar al cliente #1. Lo que hay que hacer es <strong>tratarlo como fuente de datos y de relaci&#243;n, no como validaci&#243;n.</strong></p><p>Corre este checklist antes de escribir una l&#237;nea de c&#243;digo o dise&#241;ar una landing page. La validaci&#243;n y el arranque son actividades paralelas, no secuenciales.</p><h3><strong>Paso 1 &#8212; Diagnostica el Tipo de Necesidad</strong></h3><p>&#191;Datos vivos o informe est&#225;tico? Si el entregable fue puntual, no es producto. Es historia. Si existe un estado de datos u operaci&#243;n que cambia y exige actualizaci&#243;n, hay candidato a suscripci&#243;n.</p><p>&#10060; "Mi primer cliente me pidi&#243; un estudio de mercado. Lo empaqueto como producto."</p><p>&#9989; "Mi primer cliente necesita saber cada mes qu&#233; ayudas p&#250;blicas le afectan. El estado del mercado regulatorio cambia solo. Es dato vivo."</p><h3><strong>Paso 2 &#8212; Separa la Capa de Relaci&#243;n del Entregable</strong></h3><p>Haz dos listas:</p><p>&#8594; Lo que el cliente #1 compr&#243; por confianza y rapport: iteraciones infinitas, ambig&#252;edad, llamadas a cualquier hora, contexto que entendiste sin que te lo explicaran.</p><p>&#8594; Lo que se puede estandarizar en un entregable definido que un desconocido aceptar&#237;a <strong>sin conocerte ni haberte visto jam&#225;s</strong>.</p><p>La primera lista es tu servicio consultivo. No muere: se vende por horas o por proyecto. La segunda lista es tu candidato a producto.</p><p>El error t&#237;pico es empaquetar la primera lista y llamarla producto. Eso explica por qu&#233; tantos "productos" extra&#237;dos de un cliente son en realidad servicios renombrados con un PDF de alcance.</p><h3><strong>Paso 3 &#8212; Testea la Recompra Antes de Construir</strong></h3><p>No preguntes "&#191;te gust&#243;?". Pregunta: <strong>"Si pudiera entregarte este resultado cada [mes/trimestre] sin que tengas que record&#225;rmelo, &#191;lo contratar&#237;as &#8212; y a qu&#233; cadencia?"</strong></p><p>Y aunque responda s&#237;, no lo tomes como validaci&#243;n final. Un solo cliente feliz no es un mercado.</p><p>Un "s&#237;, me encant&#243;" no es validaci&#243;n. Es cortes&#237;a post-entrega.</p><h3><strong>Paso 4 &#8212; Encuentra 3 Desconocidos Que Verbalicen el Mismo Dolor</strong></h3><p>Sal a por compradores que no te conozcan. LinkedIn, comunidades sectoriales, b&#250;squeda de problemas. La pregunta no es "&#191;te interesa mi soluci&#243;n?". La pregunta es <strong>"&#191;cu&#225;l es el mayor dolor que tienes con [el tema]?"</strong></p><p>Si tres desconocidos describen el mismo problema con sus propias palabras y el mismo umbral de dolor &#8212; sin que t&#250; introduzcas el concepto &#8212; hay segmento. Si nadie lo verbaliza solo, no existe.</p><h3><strong>Paso 5 &#8212; Define Precio y Alcance Alrededor del Resultado Recurrente</strong></h3><p>El precio se ancla al estado de los datos vivos, no a las horas empleadas ni al artefacto hist&#243;rico que entregaste al cliente #1.</p><p>Tu producto cobra por mantener un problema del cliente en un estado controlado. No cobra por el informe que ya redactaste una vez.</p><p>La m&#233;trica de &#233;xito no es "cu&#225;ntos entregables produzco". Es <strong>"cu&#225;nto tiempo el problema del cliente permanece resuelto sin que yo intervenga &#8212; y qu&#233; pasa cuando deja de estarlo"</strong>.</p><p>Ese segundo t&#233;rmino es la oportunidad de recurrencia.</p><h2><strong>La Se&#241;al Que Buscas No Es un Cliente Feliz. Es un Extra&#241;o Que Paga.</strong></h2><p>Vuelvo al principio para rematar.</p><p>Un cliente feliz valida tu capacidad de construir relaci&#243;n y entregar bajo ambig&#252;edad. Son competencias valiosas &#8212; se venden caras por proyecto. Pero no son un producto.</p><p>El producto nace cuando a&#237;slas el dato vivo, lo separas del artefacto est&#225;tico, y encuentras desconocidos que verbalizan el mismo dolor sin que se lo nombres.</p><p>El entregable del cliente #1 es historia. El umbral recurrente que activa tu servicio es el producto. <strong>La bifuraci&#243;n del dato vivo es el filtro que separa al operador que productiza del que renombra su servicio.</strong></p><p>No hace falta esperar a tener tres clientes para empezar. Hace falta dejar de tratar al primero como validaci&#243;n y empezar a tratarlo como fuente de datos. Corre el checklist antes de construir. La validaci&#243;n y el arranque son actividades paralelas, no secuenciales.</p><p>El mercado no premia al que entrega bien un proyecto. Premia al que encuentra el dato que nunca deja de cambiar y construye alrededor de su ritmo.</p><p>Ah&#237; est&#225; tu producto. B&#250;scalo ah&#237;, no en el informe que ya entregaste.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/primer-cliente-productizar-estado-recurrente-checklist-20260904?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[El 90% de los Nichos RaaS Están Muertos Antes de Empezar — y No es por Falta de Demanda]]></title><description><![CDATA[El 90% de los nichos RaaS mueren antes de existir. Aprende el filtro de 3 se&#241;ales para elegir nicho con suscripci&#243;n viable: frecuencia, pago real y competencia.]]></description><link>https://newsletter.brianmenagomez.com/p/el-90-de-los-nichos-raas-estan-muertos-267</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/el-90-de-los-nichos-raas-estan-muertos-267</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Thu, 03 Sep 2026 07:00:17 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7a5e3cc1-d081-40a8-8424-742661680344_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>El 90% de los Nichos RaaS Est&#225;n Muertos Antes de Empezar &#8212; y el Autopsy No Dice "No Hay Demanda"</strong></h2><p>Crees que un nicho RaaS fracasa porque no tiene suficiente demanda. O porque el mercado est&#225; demasiado saturado.</p><p><strong>*Te has equivocado de diagn&#243;stico.</strong>*</p><p>El 90% de los nichos RaaS mueren antes de lanzar por una raz&#243;n m&#225;s simple y m&#225;s estructural: <strong>el fundador eligi&#243; datos que cambian demasiado despacio para justificar que alguien te pague dos veces.</strong></p><p>Lo he visto en los productos que he enviado desde Espa&#241;a &#8212; conversoriaecnae.es, gestoriascercademi.com, Juridica Integral &#8212; y lo he visto en el an&#225;lisis de nichos ajenos. El patr&#243;n se repite con precisi&#243;n quir&#250;rgica.</p><p>No es un problema de demanda. Es un problema de temporalidad.</p><p>El cliente racional no pagar&#225; una suscripci&#243;n mensual por datos que solo cambian trimestralmente. Comprar&#225; una vez. Y en la segunda renovaci&#243;n te preguntar&#225; por qu&#233; no lo produce &#233;l mismo en casa.</p><p>Vamos a desmontar el mito y construir lo &#250;nico que importa: <strong>el filtro que separa un nicho con suscripci&#243;n viable de un informe de una sola venta.</strong></p><p>---</p><h2><strong>El Espejismo de los Datos de Mercado</strong></h2><p>Empecemos por la trampa m&#225;s seductora de todas: los datos de mercado.</p><p>El consejo convencional de selecci&#243;n de nicho dice que elijas donde la demanda sea m&#225;s visible. Y no hay demanda m&#225;s visible que los datos de mercado. Todo el mundo habla de la econom&#237;a, de los tipos de inter&#233;s, de la inflaci&#243;n de los precios.</p><p><strong>*Pero el inter&#233;s visible no es pago real.</strong>*</p><p>Todos tienen inter&#233;s en la econom&#237;a. Casi nadie tiene una partida presupuestaria recurrente dedicada a un feed econ&#243;mico gen&#233;rico. El espejismo de los datos de mercado funciona as&#237;:</p><ul><li><p>La demanda "parece" alta porque todos hablan del tema.</p></li><li><p>Los incumbentes y las fuentes gratuitas ya responden la pregunta.</p></li><li><p>El cliente tiene alternativas en un feed RSS, en un informe trimestral gratuito, en un analista al que ya paga.</p></li></ul><p>El resultado: <strong>un nicho que se siente vivo y que monetiza p&#233;simamente.</strong></p><p>El inter&#233;s visible es la m&#233;trica equivocada. Lo que separa un nicho real de una charla de sal&#243;n es una pregunta mucho m&#225;s inc&#243;moda:</p><p>&gt; &#10060; "&#191;Le parece &#250;til al cliente esta informaci&#243;n?"</p><p>&gt; &#9989; "&#191;Qu&#233; est&#225; haciendo ESTE cliente HOY para resolver este problema?"</p><p>La disposici&#243;n a pagar no se declara. Se demuestra con comportamiento existente.</p><p>---</p><h2><strong>Se&#241;al #1: Disposici&#243;n a Pagar por Comportamiento, No por Inter&#233;s</strong></h2><p>La primera de las tres se&#241;ales que realmente importan es la disposici&#243;n a pagar. Y el discriminador limpio es el comportamiento actual.</p><p>Un cliente que <strong>ya paga a un analista, una suscripci&#243;n a una herramienta, o horas internas de un empleado</strong> para responder esta pregunta es un nicho real.</p><p>Un cliente que encontrar&#237;a tus datos "&#250;tiles" es una conferencia, no un mercado.</p><p>El truco est&#225; en la evidencia concreta. Si tu cliente objetivo ya est&#225; gastando recursos en el problema, tienes presupuesto que desviar. Si no est&#225; gastando nada, est&#225;s pidiendo a alguien que cree un presupuesto nuevo &#8212; y esa es la venta m&#225;s cara del mundo.</p><p>Esto explica por qu&#233; el nicho de datos de mercado se siente vivo pero monetiza mal: todo el mundo est&#225; interesado en la econom&#237;a, casi nadie mantiene un gasto recurrente dedicado a un feed econ&#243;mico gen&#233;rico.</p><p><strong>La pregunta correcta no es "&#191;le interesa?" Es "&#191;en qu&#233; gasta hoy para responder esta pregunta?"</strong></p><p>Busca uno de estos tres comportamientos:</p><ul><li><p><strong>Paga una suscripci&#243;n o herramienta</strong> que responde el problema de forma parcial.</p></li><li><p><strong>Paga horas a un analista o consultor</strong> que lo resuelve con fricci&#243;n.</p></li><li><p><strong>Paga salario interno</strong> de un empleado junior que lo produce a mano cada semana.</p></li></ul><p>Cualquiera de los tres significa que hay dinero circulando. El punto dos y el tres son especialmente interesantes &#8212; representan fricci&#243;n que tu RaaS elimina.</p><p>---</p><h2><strong>Se&#241;al #2: La Cadencia de Cambio que Nadie Audita</strong></h2><p>La segunda se&#241;al es la que la mayor&#237;a de fundadores nunca inspecciona: <strong>&#191;con qu&#233; frecuencia cambian los datos subyacentes?</strong></p><p>Este es el motor oculto del churn. Y nadie lo audita antes de construir.</p><p>Piensa en la l&#243;gica del cliente. Si los datos cambian trimestralmente, su movimiento racional es comprar una vez y redecidir cada trimestre. &#191;Por qu&#233; mantener una suscripci&#243;n mensual para un informe que solo cambia cada tres meses?</p><p>Y aqu&#237; est&#225; el peligro real: en la segunda renovaci&#243;n, el cliente no solo redecidir&#225;. <strong>Cuestionar&#225; si merece la pena producir los datos en casa.</strong></p><p>Los datos con cadencia mensual o superior, o los datos dirigidos por eventos, fuerzan dependencia continua. Convierten el modelo de entrega en algo natural: no est&#225;s reportando, est&#225;s <strong>monitorizando</strong>.</p><h3><strong>La auditor&#237;a de cadencia</strong></h3><p>Antes de escribir una sola l&#237;nea de c&#243;digo, haz esto:</p><p><strong>Paso 1:</strong> Escribe el nicho candidato y su fuente de datos subyacente.</p><p><strong>Paso 2:</strong> Define la cadencia natural de cambio: diaria, semanal, mensual, trimestral, dirigida por eventos.</p><p><strong>Paso 3:</strong> S&#233; honesto. Si la respuesta es trimestral o m&#225;s lenta, asume que est&#225;s construyendo un informe de una sola venta, no una suscripci&#243;n &#8212; hasta que se demuestre lo contrario.</p><p>La regla es brutal pero simple: <strong>los datos que cambian despacio destruyen el modelo de suscripci&#243;n por dise&#241;o.</strong></p><p>Esto no significa un veto duro. Un nicho con datos trimestrales puede funcionar si el flujo de trabajo del cliente est&#225; construido alrededor de esa cadencia y la producci&#243;n interna es costosa. Pero es la excepci&#243;n, no la regla. Y el riesgo es estructural: en alg&#250;n momento el cliente construir&#225; la capacidad interna.</p><h3><strong>El bonus doble: datos frecuentes y desordenados</strong></h3><p>Aqu&#237; hay una capa que pocos analizan. Los datos que cambian a menudo <strong>pero en formas desordenadas y no estandarizadas</strong> son doblemente atractivos.</p><p>&#191;Por qu&#233;?</p><p>Porque la capacidad de reproducir de forma consistente un entregable &#8212; lo que he llamado en art&#237;culos anteriores <strong>el contrato de datos</strong> &#8212; es genuinamente dif&#237;cil de copiar cuando la fuente de datos es ca&#243;tica.</p><p>Los datos limpios que cambian a menudo pueden ser replicados por cualquiera con un cron job y un buen parser. Los datos desordenados que cambian a menudo requieren una arquitectura que tolere la suciedad, normalice los inputs y garantice consistencia. Eso es dif&#237;cil. Y lo dif&#237;cil es donde vive la ventaja competitiva real.</p><p>---</p><h2><strong>Se&#241;al #3: Limitadas Alternativas Competitivas &#8212; y las Dos Causas del "No Hay Competencia"</strong></h2><p>La tercera se&#241;al es la m&#225;s malinterpretada del framework.</p><p>Ves un nicho donde no hay competencia y asumes luz verde. <strong>*Mal.</strong>*</p><p>La baja competencia tiene dos causas muy diferentes:</p><p><strong>Causa A &#8212; Descuido:</strong> Los incumbentes ignoran el nicho porque les queda peque&#241;o o porque est&#225; fuera de su foco. Aqu&#237;, luz verde: puedes ocupar la posici&#243;n de especialista.</p><p><strong>Causa B &#8212; Ausencia total de dinero:</strong> Nadie paga por ello porque no hay valor real. Aqu&#237;, luz roja: no hay competencia porque no hay mercado.</p><p>La prueba discriminadora es esta: <strong>&#191;responde el incumbente a la pregunta adyacente bien y simplemente se salta esta?</strong> Si s&#237;, es descuido &#8212; oportunidad. Si falla por completo, es ausencia de valor &#8212; trampa.</p><h3><strong>El mapa de sustitutos</strong></h3><p>Haz el mapa honesto de todas las alternativas que tiene el cliente hoy:</p><ul><li><p><strong>Agregadores gratuitos</strong> que responden la pregunta con un feed RSS o un dashboard p&#250;blico.</p></li><li><p><strong>Vendors incumbentes</strong> que la resuelven como feature secundario de un producto mayor.</p></li><li><p><strong>La hoja de c&#225;lculo del cliente</strong> donde un junior la produce cada lunes con dolor.</p></li></ul><p>El cliente ya resuelve el problema hoy. Tu preguntas es: <strong>&#191;lo paga, lo sufre, y lo produce con fricci&#243;n suficiente para que prefiera suscribirse a construirlo?</strong></p><p>El nicho RaaS defendible no est&#225; donde no hay alternativa. Est&#225; donde <strong>la alternativa es tan dolorosa o costosa que el cliente prefiere pagar recurrencia a producirla &#233;l mismo.</strong></p><p>---</p><h2><strong>El Filtro de las 3 Se&#241;ales: La Intersecci&#243;n Que Buscas</strong></h2><p>Has visto las tres se&#241;ales por separado. Ahora la jugada maestra: <strong>el nicho ganador existe solo en la intersecci&#243;n de las tres.</strong></p><p>Propongo un framework que puedes ejecutar en un d&#237;a &#8212; <strong>el Filtro de 3 Se&#241;ales para Nichos RaaS.</strong></p><h3><strong>Se&#241;al 1 &#8212; Disposici&#243;n a pagar</strong></h3><p><strong>Test de comportamiento existente.</strong> &#191;El cliente gasta hoy dinero, herramientas o tiempo interno en este problema? Interesado no paga. Comportamiento actual paga.</p><h3><strong>Se&#241;al 2 &#8212; Frecuencia de cambio</strong></h3><p><strong>Auditor&#237;a de cadencia.</strong> &#191;Los datos cambian mensualmente o m&#225;s a menudo? &#191;Est&#225;n dirigidos por eventos? &#191;Y son lo bastante desordenados para que la producci&#243;n consistente sea genuinamente dif&#237;cil?</p><h3><strong>Se&#241;al 3 &#8212; Alternativas limitadas</strong></h3><p><strong>Mapa de sustitutos.</strong> &#191;Responde hoy alguien a esta pregunta? &#191;La responde bien pero se salta tu nicho (descuido = oportunidad) o falla por completo (ausencia de valor = trampa)?</p><p>---</p><h3><strong>C&#243;mo puntuar tu nicho hoy</strong></h3><p><strong>Paso 1:</strong> Escribe tu nicho candidato en una hoja.</p><p><strong>Paso 2:</strong> Audita la cadencia. Si es trimestral o m&#225;s lenta, salvo excepciones demostrables, asume que no es una suscripci&#243;n.</p><p><strong>Paso 3:</strong> Busca evidencia de gasto existente. No intereses. Gasto. Un cliente que paga a un analista hoy es un mercado. Un cliente que encuentra tus datos &#250;tiles es una audiencia.</p><p><strong>Paso 4:</strong> Mapea los sustitutos. Trata el "no hay competencia" como luz verde solo si el incumbente ignora el nicho por descuido, no porque no haya dinero.</p><p><strong>Paso 5:</strong> Pregunta si el entregable soporta un contrato de datos. &#191;Puedes definir un esquema estable y una salida de monitorizaci&#243;n reproducible? Si los datos son demasiado inestables para contratar sobre ellos, lo que tienes es consultor&#237;a, no RaaS.</p><p>Solo el nicho que pasa las tres se&#241;ales con luz verde merece tu arquitectura. <strong>El 90% de los nichos muere en la se&#241;al 2.</strong> Antes de escribir c&#243;digo, antes de onboarding un primer cliente &#8212; ejecuta el filtro.</p><p>---</p><h2><strong>Sobre la Cifra del 90% &#8212; S&#233; Honesto Contigo Mismo</strong></h2><p>Vale. Hablemos claro sobre la cifra.</p><p>El 90% es una estimaci&#243;n interna, un patr&#243;n observado a trav&#233;s de intentos de nicho RaaS que he analizado y construido. No hay un dataset auditado detr&#225;s. La fuente del mecanismo es el contenido original de este thread, no una estad&#237;stica publicada.</p><p><strong>*&#191;Y qu&#233;?</strong></p><p>El valor intelectual no est&#225; en el n&#250;mero. Est&#225; en el mecanismo. Un 70%, un 80%, un 90% &#8212; cualquiera de esas cifras apunta a la misma conclusi&#243;n: <strong>la mayor&#237;a de los nichos mueren por una decisi&#243;n estructural evitable.</strong></p><p>Pod&#233;is falsificar el filtro con vuestro propio nicho en un d&#237;a. Poned el n&#250;mero por delante, sometedlo a prueba contra vuestras tres se&#241;ales, y dejad que el resultado hable &#8212; no la cifra.</p><p>Esto es lo que separa un filtro de una opini&#243;n: los filtros se pueden falsificar.</p><p>---</p><h2><strong>El Patr&#243;n de Entrega Correcto Nace del Nicho Correcto</strong></h2><p>Un apunte final sobre el orden de las operaciones.</p><p>Los art&#237;culos anteriores de este thread establecieron dos verdades de arquitectura:</p><p>1. El producto de un RaaS no es el informe. Es <strong>la entrega fiable y habitual de monitorizaci&#243;n.</strong></p><p>2. La ventaja competitiva es <strong>la reproducibilidad y consistencia del entregable</strong>, resuelta con un contrato de datos.</p><p>La selecci&#243;n de nicho es el prequel de ambas. No puedes dise&#241;ar una arquitectura de entrega hasta que sabes si el nicho produce la frecuencia y la suciedad de datos que la justifican.</p><p>Los datos frecuentes y desordenados fuerzan la arquitectura de monitorizaci&#243;n que ya describ&#237;. Los datos limpios y lentos no la justifican. <strong>El nicho que elijas determina qu&#233; arquitectura es viable.</strong></p><p>No construyas la capa de entrega antes de auditar la cadencia. Es construir infraestructura para un problema que todav&#237;a no has confirmado que existe.</p><p>---</p><h2><strong>Resumen: Las Tres Se&#241;ales y lo Que Viene Despu&#233;s</strong></h2><p>El 90% de los nichos RaaS mueren antes de existir &#8212; y el autopsy no dice falta de demanda.</p><p>Dice datos que cambian demasiado lento para que alguien te pague dos veces.</p><p>El nicho ganador es la intersecci&#243;n de tres se&#241;ales:</p><p>1. <strong>Disposici&#243;n a pagar demostrada por comportamiento actual</strong>, no por inter&#233;s declarado.</p><p>2. <strong>Datos con cadencia mensual o superior</strong>, y lo bastante desordenados para que la producci&#243;n consistente sea dif&#237;cil de copiar.</p><p>3. <strong>Alternativas limitadas por descuido del incumbente</strong>, no por ausencia de valor.</p><p>Ejecuta el <strong>Filtro de las 3 Se&#241;ales</strong> sobre tu nicho antes de invertir un solo euro en arquitectura o en onboarding de clientes.</p><p>El 90% de los nichos que auditas van a fallar el filtro. Y ese es el mejor resultado posible: <strong>fallar antes de construir es el &#250;nico fallo que te ahorra dinero.</strong></p><p>Los nichos RaaS que sobreviven no son los que tienen m&#225;s demanda visible. Son los que alimentan una decisi&#243;n recurrente con datos que nadie m&#225;s puede entregar de forma fiable &#8212; y que el cliente ya paga por resolver hoy.</p><p>La pr&#243;xima vez que te enamores de un nicho por su demanda visible, preg&#250;ntate una sola cosa:</p><p><strong>*&#191;Qu&#233; est&#225; haciendo el cliente HOY para resolver esto, y cu&#225;nto le duele?</strong>*</p><p>Esa respuesta te dir&#225; m&#225;s que cualquier dashboard de keywords del mundo.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/nicho-rasa-filtro-3-senales-seleccion-20260903?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Claude Code no es un Autocompletado Mejorado. Es un Operario que Posee el Loop Completo — y Eso Cambia Qué Determina la Calidad]]></title><description><![CDATA[Claude Code no es un copiloto de la IDE: es un agente que edita, ejecuta tests y corrige sus propios errores desde la terminal. Aprende a dominar CLAUDE.md, hooks y modos de permiso para que la autonom&#237;a sea segura.]]></description><link>https://newsletter.brianmenagomez.com/p/claude-code-no-es-un-autocompletado</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/claude-code-no-es-un-autocompletado</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Thu, 03 Sep 2026 07:00:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/71157f7c-c673-4232-ba04-476cd1a957c8_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>La Herramienta de C&#243;digo M&#225;s Disruptiva de este Ciclo no Tiene Chat Lateral, Panel de Extensiones ni Overlay de Resaltado &#8212; Es una Terminal Pelada donde Escribes `claude` y Dejas de Escribir</strong></h2><p>Porque empieza a escribir los tests. Los ejecuta. Lee el stack trace. Corrige su propio fallo. Y hace commit.</p><p><strong>*Ese cambio en qui&#233;n posee el loop vale m&#225;s que cualquier upgrade de modelo.</strong> *</p><p>Durante a&#241;os la industria asumi&#243; que el siguiente paso en IA para c&#243;digo era un autocompletado mejor o un chat inline en tu editor. El marco de Cursor y Copilot: el humano escribe, la IA sugiere.</p><p>El supuesto equivocado es que el modelo es el producto y la IDE es su casa.</p><p>Claude Code relocaliza la IA de "motor de sugerencias en el margen" a "trabajador aut&#243;nomo que posee el loop completo desde una terminal". Y la consecuencia contraria es la que casi nadie ve:</p><p><strong>*Lo que determina la calidad del output deja de ser la creatividad de tu prompt o el IQ del modelo. Se convierte en dos cosas: la higiene del contexto y el dise&#241;o de la gobernanza.</strong> *</p><p>Equipos que "solo apuntan al agente al repo" obtienen resultados mediocres y culpan al modelo. Equipos que tratan CLAUDE.md como una constituci&#243;n y los tests como una red de seguridad obtienen un resultado distinto con el mismo modelo.</p><p>El cuello de botella se ha movido. Ya no est&#225; en el modelo. Est&#225; en el repositorio y en el proceso que lo rodea.</p><h2><strong>Por Qu&#233; la Terminal, y no la IDE, es Donde los Agentes Cobraron Vida</strong></h2><p>En una IDE, la IA es un motor de sugerencias. Propone un diff y espera a que un humano lo aplique.</p><p><strong>No puede observar las consecuencias de sus propias acciones.</strong></p><p>En una terminal, Claude Code puede editar un fichero, ejecutar tu suite de tests, leer el stack trace, volver a editar y re-ejecutar. Es un bucle de feedback cerrado donde el agente <em>experimenta los resultados de lo que ha hecho</em>.</p><p>Ese bucle de autocorrecci&#243;n es la diferencia arquitect&#243;nica entre un copiloto y un agente.</p><p>&#10060; <strong>Copiiloto</strong>: propone c&#243;digo &#8594; t&#250; lo aplicas &#8594; t&#250; ejecutas &#8594; t&#250; corriges</p><p>&#9989; <strong>Agente</strong>: planifica &#8594; edita &#8594; ejecuta tests &#8594; lee el fallo &#8594; corrige &#8594; re-ejecuta &#8594; commitea</p><p>Es exactamente por eso que los vendors que corren a pegar "ag&#233;ntico" al chat lateral de la IDE est&#225;n peleando contra la forma equivocada. La fortaleza de la IDE &#8212; la proximidad a tu cursor &#8212; es precisamente lo que la mantiene como motor de sugerencias en lugar de agente.</p><p>La terminal no es un retroceso. Es el &#250;nico lugar donde el loop cierra.</p><h3><strong>La Bestia del Loop Ag&#233;ntico: Latencia y Tokens</strong></h3><p>Este dise&#241;o no es gratis. Un agente que de verdad <em>posee</em> el trabajo ejecuta decenas de llamadas de tool secuenciales por tarea: leer un fichero, editar este, ejecutar tests, leer el output, volver a editar...</p><p>Cada paso consume latencia y tokens.</p><p>Por eso Anthropic ha posicionado una clase de modelo m&#225;s r&#225;pida y ligera como el caballo de batalla ag&#233;ntico por defecto, reservando los modelos m&#225;s grandes para planificaci&#243;n compleja y ediciones de alto riesgo.</p><p><strong>*El throughput ag&#233;ntico es un objetivo de optimizaci&#243;n distinto a "mejor respuesta &#250;nica".</strong> *</p><p>Si eliges el modelo solo por su mejor single shot, est&#225;s optimizando para el problema equivocado. Aqu&#237; el trabajo no es una respuesta. Son decenas de pasos secuenciales donde cada milisegundo y cada token cuentan.</p><h2><strong>El Contexto es el Nuevo Prompt Engineering &#8212; y CLAUDE.md es la Palanca</strong></h2><p>Llevamos a&#241;os obsesionados con el system prompt. En los agentes, el texto de mayor palanca no es ese.</p><p><strong>Es el fichero de memoria del repositorio, cargado en cada sesi&#243;n.</strong></p><p>Ejecutas `/init` en Claude Code y la herramienta escanea tu repo, analiza estructura y convenciones, y genera un CLAUDE.md base.</p><p>Ah&#237; empieza el trabajo real. Porque el CLAUDE.md que genera la herramienta es un borrador. Tu trabajo es convertirlo en una constituci&#243;n.</p><h3><strong>El Patr&#243;n de las Tres Categor&#237;as Cr&#237;ticas</strong></h3><p>Hay tres categor&#237;as de contenido en tu CLAUDE.md que determinan la calidad del agente m&#225;s que cualquier otra cosa:</p><p>1. <strong>Comandos prohibidos</strong>: qu&#233; NO debe ejecutar jam&#225;s. Migraciones de base de datos destructivas, `git push --force`, `rm -rf` fuera de rutas de build.</p><p>2. <strong>Convenciones de estilo</strong>: c&#243;mo se escribe el c&#243;digo en *este* repo. No gen&#233;rico. Espec&#237;fico. Naming, estructura de ficheros, manejo de errores.</p><p>3. <strong>Mapa de m&#243;dulos</strong>: qu&#233; fichero pertenece a qu&#233; capa. Fronteras de responsabilidad. Qu&#233; tocar y qu&#233; no.</p><p>Un repo con arquitectura documentada con claridad, comandos prohibidos expl&#237;citos y convenciones impuestas produce resultados aut&#243;nomos dram&#225;ticamente mejores.</p><p>Una sesi&#243;n apuntada a un monorepo sin documentar se degrada a medida que el ruido llena la ventana de contexto.</p><p>Ah&#237; es donde `/compact` (que resume la conversaci&#243;n para mantener el contexto dentro de los l&#237;mites) y los subagentes existen para luchar contra esa degradaci&#243;n: podando y particionando contexto.</p><h2><strong>La Autonom&#237;a Convierte la Gobernanza en un Problema de Dise&#241;o, no de Confianza</strong></h2><p>El argumento est&#225;ndar contra los agentes es simple: <em>"no voy a dejar que una m&#225;quina ejecute comandos arbitrarios en mi repo. Una edici&#243;n mala y lo corrompo."</em></p><p>Es la objeci&#243;n m&#225;s fuerte. Y merece una respuesta dedicada, con configuraci&#243;n real.</p><p>Claude Code gestiona la autonom&#237;a con capas: modos de permiso, hooks de ciclo de vida y checkpoints.</p><h3><strong>Los Tres Modos que Todo Equipo Debe Dominar</strong></h3><ul><li><p><strong>`plan`</strong>: el agente propone cambios y espera tu aprobaci&#243;n antes de tocar un fichero.</p></li><li><p><strong>`default`</strong>: ejecuta comandos y ediciones tras pedirte confirmaci&#243;n en cada paso.</p></li><li><p><strong>`acceptEdits`</strong>: ediciones sin preguntar, pero a&#250;n controlado por los hooks.</p></li></ul><p>Empezar en modo `plan` no es cobard&#237;a. Es la fase de aprendizaje donde calibras qu&#233; sabe hacer el agente antes de soltarle las riendas.</p><h3><strong>Los Hooks como Capa de Cumplimiento Normativo</strong></h3><p>La autonom&#237;a se vuelve segura cuando la pol&#237;tica es c&#243;digo. Los hooks de ciclo de vida (`PreToolUse`, `PostToolUse`, `Notification`, `Stop`) se declaran en ficheros JSON de settings.</p><p>Este es el snippet que bloquea lo destructivo y te avisa cuando el agente se para:</p><p>```json</p><p>{</p><p>"hooks": {</p><p>"PreToolUse": [</p><p>{</p><p>"matcher": "Bash",</p><p>"hooks": [</p><p>{</p><p>"type": "command",</p><p>"command": "if echo \"$TOOL_INPUT\" | grep -qE 'rm -rf|git push --force|drop table'; then echo '{\"decision\":\"block\",\"reason\":\"Comando destructivo bloqueado por policy\"}'; exit 0; fi"</p><p>}</p><p>]</p><p>}</p><p>],</p><p>"Stop": [</p><p>{</p><p>"matcher": "",</p><p>"hooks": [</p><p>{</p><p>"type": "command",</p><p>"command": "curl -s -X POST https://tu-endpoint.webhook/agente-parado -d '{\"msg\":\"Claude Code ha terminado o fallado\"}'"</p><p>}</p><p>]</p><p>}</p><p>]</p><p>}</p><p>}</p><p>```</p><p>No le preguntas al agente <em>"&#191;puedo confiar en ti?"</em>. Le defines <em>qu&#233; le est&#225; permitido hacer</em> y <em>qu&#233; pasa cuando se para</em>.</p><p><strong>*Trata al agente como a un ingeniero nuevo con credenciales limitadas, no como a un or&#225;culo omnisciente.</strong> *</p><p>Los equipos que triunfan con agentes tratan los hooks como una capa de cumplimiento. Bloquean comandos destructivos. Exigen aprobaci&#243;n antes de push. Notifican en Stop.</p><p>Esto reencuadra la cr&#237;tica est&#225;ndar &#8212; "la IA no es de fiar con mi c&#243;digo" &#8212; como un problema de ingenier&#237;a resoluble de scopes y pol&#237;ticas. No como un juicio moral sobre la IA.</p><h2><strong>Marco de Implementaci&#243;n: La Constituci&#243;n del Repositorio</strong></h2><p>No es teor&#237;a. Es un orden de operaciones. Cinco pasos que convierten un agente desatado en un trabajador aut&#243;nomo con guardarra&#237;les.</p><h3><strong>Paso 1: Empieza Peque&#241;o y Planifica Primero</strong></h3><p>Elige un repo peque&#241;o y bien testeado para tus primeras sesiones. Lanza Claude Code en modo `plan` y deja que proponga cambios.</p><p>Revisa su plan antes de que toque un solo fichero.</p><h3><strong>Paso 2: Ejecuta `/init` y Cura el CLAUDE.md con Agresividad</strong></h3><p>El fichero generado es el esqueleto. Tu trabajo es codificar las tres categor&#237;as cr&#237;ticas: comandos prohibidos, convenciones y mapa de m&#243;dulos.</p><p>As&#237; se ve un fragmento bien curado:</p><p>```markdown</p><h1>CLAUDE.md &#8212; Constituci&#243;n del Repositorio</h1><h2>Reglas no negociables</h2><ul><li><p>NO ejecutar migraciones DDL directamente en producci&#243;n.</p></li></ul><p>Usa siempre `prisma migrate deploy` via CI.</p><ul><li><p>NO usar `git push --force` en ramas compartidas.</p></li><li><p>NO borrar ficheros fuera de `src/` sin aprobaci&#243;n humana.</p></li></ul><h2>Estilo</h2><ul><li><p>TypeScript estricto. Sin `any` impl&#237;cito.</p></li><li><p>Funciones puras fuera de componentes. Hooks en `hooks/`.</p></li><li><p>Errores se lanzan con causas nombradas, nunca gen&#233;ricos.</p></li></ul><h2>Mapa de m&#243;dulos</h2><ul><li><p>`src/api/` &#8594; handlers de API. Sin l&#243;gica de negocio.</p></li><li><p>`src/domain/` &#8594; reglas de negocio. Prohibido importar HTTP aqu&#237;.</p></li><li><p>`src/repos/` &#8594; acceso a datos. Solo se habla con la base de datos.</p></li></ul><p>```</p><p><strong>Cada sesi&#243;n carga este fichero.</strong> Cada promesa que le haces aqu&#237; es contexto durable que le ahorras al agente tener que adivinar.</p><h3><strong>Paso 3: Institucionaliza el Loop de TDD</strong></h3><p>Instruye al agente para que escriba primero un test que falle. Implemente hasta que la suite est&#233; en verde. Y revises cada cambio con `git diff` antes de aceptarlo.</p><p>La suite de tests se convierte en tu red de seguridad para la autonom&#237;a. No el humano. El test.</p><h3><strong>Paso 4: A&#241;ade Pol&#237;tica Encima de la Autonom&#237;a Antes de Escalar</strong></h3><p>Los hooks de `settings.json` que bloquean invocaciones de tools peligrosas y notifican en Stop. Capa de cumplimiento antes de soltar m&#225;s autonom&#237;a.</p><h3><strong>Paso 5: Escala Solo Tras Probar la Confianza Local</strong></h3><p>Descomp&#243;n el trabajo repetido en subagentes (ficheros Markdown bajo `.claude/agents/`) y comandos slash (`/changelog`, por ejemplo) bajo `.claude/commands/`. Las tools externas se conectan v&#237;a MCP servers.</p><p>Y cuando el agente demuestre fiabilidad local, grad&#250;alo a ejecuci&#243;n headless (`.claude -p`), Agent SDK o GitHub Actions. Siempre con un gate de revisi&#243;n humana obligatoria sobre el diff producido.</p><h3><strong>El Salto al Pipeline</strong></h3><p>El mismo loop que arregla un test local fallido puede programarse para hacer triage de pull requests o parchear issues autom&#225;ticamente en cada push.</p><p>La consecuencia es un cambio de rol del ingeniero. Dedicas menos tiempo a escribir cada l&#237;nea y m&#225;s a definir pol&#237;tica y revisar diffs producidos por el agente.</p><p><strong>El gate de revisi&#243;n se mueve m&#225;s temprano. Y la suite de tests, no el humano, se convierte en la primera l&#237;nea de defensa.</strong></p><h2><strong>La Objeci&#243;n del Repo Legacy</strong></h2><p>&#191;"Esto solo funciona en proyectos greenfield limpios; mi monorepo legacy es un caos y el agente se va a perder"?</p><p>Justo. Y parcialmente cierto.</p><p>Por eso el argumento honesto es el de la higiene del contexto: la curaci&#243;n de CLAUDE.md, `/compact`, los subagentes y la descomposici&#243;n de tareas con scope limitado son <em>precisamente</em> las herramientas para codebases grandes y desordenados.</p><p>Hay que conceder la correlaci&#243;n entre calidad de documentaci&#243;n del repo, cobertura de tests y &#233;xito ag&#233;ntico. No sobrevender la herramienta.</p><p>Pero &#8212; y esto es clave &#8212; ese trabajo de documentaci&#243;n y tests es exactamente el que har&#237;as para que cualquier ingeniero nuevo fuera productivo. Un agente solo hace que ese coste tenga retorno m&#225;s r&#225;pido.</p><h2><strong>La Verdad Inc&#243;moda</strong></h2><p>Claude Code no es un autocompletado mejorado con extra steps.</p><p>Un copiloto sugiere c&#243;digo mientras el humano posee la tarea. Claude Code posee la tarea de principio a fin desde la terminal, donde puede ejecutar comandos y observar resultados.</p><p>La diferencia de unidad de trabajo no es cosm&#233;tica. Es arquitect&#243;nica.</p><p>Y la consecuencia es que el pato de los agentes no se decide por el hardware del modelo. Se decide por cu&#225;nta disciplina tienes en el repo: qu&#233; permites, qu&#233; documentas, qu&#233; bloqueas y qu&#233; testes.</p><p><strong>El modelo ya no es el l&#237;mite. Tu repositorio lo es.</strong></p><p>Por eso el mismo agente que rinde mediocre en un monorepo sin documentar se convierte en un operario fiable en un repo con una constituci&#243;n clara y una suite de tests que muerde.</p><p>Los que aprendan a tratar el CLAUDE.md como una constituci&#243;n y los hooks como una capa de cumplimiento van a entregar software a una velocidad que los que pelean contra la forma de la herramienta no van a poder igualar.</p><p><strong>*El pr&#243;ximo salto no va a venir de un modelo m&#225;s listo. Va a venir de un repositorio mejor gobernado.</strong> *</p><p>Y la terminal pelada donde escribes `claude` y dejas de escribir &#8212; ese es el lugar donde se va a decidir todo.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/claude-code-agente-terminal-loop-seguro-20260903?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[Tu Cliente Casi Nunca Visitará el Portal Que Estás Construyendo — y Si lo Diseñas Bien, Ese Es el Punto]]></title><description><![CDATA[Dise&#241;a portales cliente RaaS de presencia cero: el valor se entrega con push, contratos de datos y cadencia fija. El producto no es el informe, es la certeza.]]></description><link>https://newsletter.brianmenagomez.com/p/tu-cliente-casi-nunca-visitara-el</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/tu-cliente-casi-nunca-visitara-el</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Wed, 02 Sep 2026 07:00:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3c02fe49-9e5b-4367-b493-b5f323613f75_1080x810.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Tu Cliente Casi Nunca Visitar&#225; el Portal Que Est&#225;s Construyendo &#8212; y Si lo Dise&#241;as Bien, Ese Es el Punto</h2><p>Crees que el factor cr&#237;tico de tu Research-as-a-Service es impresionar. Un dashboard brillante. Gr&#225;ficos que se actualizan en vivo. M&#225;s fuentes de datos que tu competencia.</p><p><strong>*Te has equivocado de diagn&#243;stico.</strong>*</p><p>El producto que tu cliente compra no es el informe en pantalla. Es la certeza tranquila, llegando a tiempo, de que alguien sigue vigilando. Y esa certeza se entrega con <em>push</em> &#8212; no con un portal que el cliente deba recordar visitar.</p><p>Tu cliente casi nunca entrar&#225; en el portal. Si lo dise&#241;as bien, eso no es un fallo. Es el objetivo.</p><h3>El Error de Tratar tu Entrega Como un SaaS</h3><p>Todo el pensamiento de producto mainstream optimiza para engagement. DAU. Time-on-page. Profundidad de sesi&#243;n. Cuantas m&#225;s visitas, mejor.</p><p><strong>*Para un sistema de entrega, una visita es una se&#241;al de fallo.</strong>*</p><p>Significa que el cliente tuvo que gastar esfuerzo para obtener el valor que ya pag&#243;. Significa que la monitorizaci&#243;n no lleg&#243; sola. Significa que tu sistema le pidi&#243; trabajo.</p><p>La investigaci&#243;n no es un evento que se encarga. Es un h&#225;bito. Un sistema de entrega perpetua. Y el h&#225;bito se construye con repetici&#243;n predecible, no con un portal brillante que nadie abre.</p><p>&#10060; <strong>Enfoque d&#233;bil:</strong> Dashboard impresionante dise&#241;ado para que el cliente entre cada d&#237;a. M&#233;tricas de engagement como KPI de &#233;xito. M&#225;s features, m&#225;s datos, m&#225;s gr&#225;ficos.</p><p>&#9989; <strong>Enfoque correcto:</strong> Portal de presencia cero. El cliente nunca necesita entrar. Las alertas, el timeline y los informes llegan solos. Una visita se investiga como una anomal&#237;a.</p><p>El valor que tu cliente renueva no lo demuestra el tr&#225;fico del portal. Lo demuestra la continuidad sentida de la monitorizaci&#243;n.</p><h3>Por Qu&#233; la Tranquilidad Supera a la Informaci&#243;n</h3><p>Un insight obliga al cliente a interpretarlo. Ese es trabajo cognitivo. Y el trabajo cognitivo es ansiedad.</p><p>La certeza viene pre-digerida. Elimina una preocupaci&#243;n de la carga mental del cliente.</p><p>Esto reencuadra cada decisi&#243;n de entrega: <strong>el objetivo del informe, la alerta y el timeline no es ser brillante. Es estar presente y ser predecible.</strong></p><p>Un informe mediocre que llega puntual cada martes entrega m&#225;s valor percibido que un informe brillante que llega tarde o de forma espor&#225;dica.</p><p>Pi&#233;nsalo: cuando el cliente abre su email y ve que la monitorizaci&#243;n de esta semana ya est&#225;, una peque&#241;a alarma mental se apaga. <em>Todav&#237;a me est&#225;n vigilando.</em> Esa alarma que se apaga es el producto.</p><p>La informaci&#243;n es la prueba del trabajo que gana la confianza. Pero la suscripci&#243;n recurrente se compra por la fiabilidad.</p><h3>El Contrato de Datos No es Burocracia. Es tu Foso.</h3><p>Todo el mundo asume que la ventaja competitiva de un RaaS es el volumen de captura de datos. M&#225;s scraping. M&#225;s fuentes. M&#225;s cobertura.</p><p><strong>*El foso no es eso. El foso es aburrido.</strong>*</p><p>La ventaja competitiva se define por <strong>lo reproducible y consistente que es el entregable</strong> &#8212; no por tener m&#225;s datos que el vecino.</p><p>El problema de la consistencia se resuelve estructuralmente, con un contrato de datos y una arquitectura de tres capas. No a&#241;adiendo fuentes.</p><p>Sin un contrato definido por escrito, cada ciclo de entrega depende del humor de la persona que lo ejecuta, de la herramienta que est&#233; de moda, de la fuente que decidi&#243; cambiar su formato.</p><p>Con un contrato, el entregable es auditable y predecible. El cliente puede planificar decisiones de negocio alrededor de un informe que siempre llega con la misma estructura. <strong>Esa predictibilidad convierte a un proveedor en infraestructura. Y la infraestructura justifica un contrato recurrente, no una compra puntual.</strong></p><p>La arquitectura de tres capas existe precisamente para que esa consistencia sobreviva al caos de las fuentes de datos reales.</p><h3>Tu Ventaja No es el Scraping. Es No Hacer Scraping de M&#225;s.</h3><p>Veamos la arquitectura que lo sostiene. La pipeline se divide en tres capas con interfaces estables entre ellas:</p><p><strong>Capa 1 &#8212; Captura:</strong> Recogida de datos cruda. Aqu&#237; es donde la gente se pierde a&#241;adiendo fuentes infinitas.</p><p><strong>Capa 2 &#8212; An&#225;lisis:</strong> Procesamiento y transformaci&#243;n. Convierte datos en lo que el contrato exige.</p><p><strong>Capa 3 &#8212; Entrega:</strong> Generaci&#243;n del entregable final. Informe, alerta, entrada de timeline.</p><p>El contrato es la interfaz. Las capas son la implementaci&#243;n.</p><p>Esto importa porque <strong>la consistencia del entregable debe sobrevivir al cambio de personal, al cambio de herramientas y al cambio de fuentes de datos.</strong></p><p>Si el analista se va, la capa de an&#225;lisis cambia. Pero si el contrato est&#225; bien definido, el entregable que recibe el cliente es indistinguible. Si una fuente de datos muere, la capa de captura se adapta. Pero el informe sigue llegando el martes con la misma estructura.</p><p>&lt;img src="https://placehold.co/800x400" alt="Pipeline de tres capas con contrato de datos como interfaz" /&gt;</p><p>Eso es lo que convierte tu servicio en infraestructura. Y la infraestructura es lo que se renueva.</p><h3>El Timeline Completa el Bucle del H&#225;bito</h3><p>La investigaci&#243;n deja de ser un evento que el cliente encarga y <strong>se convierte en un ritmo en el que puede confiar</strong>.</p><p>El timeline es la capa de cadencia: el mismo d&#237;a, la misma estructura, cada ciclo.</p><p>El cliente abre su email un martes cualquiera y ve la entrada del timeline. Sin buscarla. Sin entrar en el portal. La monitorizaci&#243;n es visible, continua y reduce la ansiedad sin exigir esfuerzo.</p><p>Esas entradas del timeline son la prueba tangible de que el trabajo sigue pasando. Son la evidencia que justifica la renovaci&#243;n. Un cliente que ve actividad cada ciclo no es un cliente que se pregunta qu&#233; est&#225; pagando.</p><p>La tranquilidad del cliente viene de <strong>poder predecir la monitorizaci&#243;n, no de que le sorprendan con ella</strong>.</p><h3>Presencia Cero No es Comunicaci&#243;n Cero</h3><p>Aqu&#237; est&#225; la objeci&#243;n m&#225;s com&#250;n: "Si el cliente nunca entra, &#191;no parece que no estoy haciendo nada?"</p><p>Hay una distinci&#243;n cr&#237;tica entre <strong>presencia cero y comunicaci&#243;n cero</strong>.</p><p>La ausencia se refiere al esfuerzo del cliente. No a tu silencio como proveedor.</p><p>El sistema debe empujar regularmente y de forma visible. El timeline garantiza que el cliente vea actividad cada ciclo sin tener que buscarla. Las alertas llegan cuando cambia algo relevante. Los informes aparecen en su inbox con la cadencia pactada.</p><p>&#10060; <strong>Zero-communication:</strong> Construyes el portal, lo env&#237;as, y esperas a que el cliente entre. Si no entra, no ve nada. Y el mes que viene no renueva porque "no pasa nada".</p><p>&#9989; <strong>Zero-presence:</strong> El valor viaja en push. El timeline empuja entradas. Las alertas empujan notificaciones. Los informes empujan documentos. El portal existe como red de seguridad y registro de auditor&#237;a &#8212; no como destino.</p><p>El momento en que un cliente debe entrar al portal para obtener valor, tu sistema ya ha fallado.</p><h3>La Regla del "No Construir" Como Estrategia de Entrega</h3><p>Cada feature que a&#241;ades al portal es coste de infraestructura recurrente. Y en un sistema de entrega, <strong>el scope es una responsabilidad</strong>.</p><p>Un sistema m&#237;nimo que hace una sola cosa &#8212; entregar monitorizaci&#243;n predecible &#8212; supera a un portal rico en features que ocasionalmente se rompe o se desv&#237;a del contrato.</p><p>Pasa cada idea de feature propuesta por este filtro:</p><p>1. <strong>&#191;Aumenta la sensaci&#243;n del cliente de ser monitorizado?</strong> O solo a&#241;ade superficie, coste y modos de fallo.</p><p>2. <strong>&#191;Reduce el esfuerzo del cliente?</strong> O solo existe para impresionar a otro proveedor de servicios.</p><p>3. <strong>&#191;Est&#225; en el contrato?</strong> Si no est&#225;, no se construye.</p><p>La respuesta por defecto es no.</p><h3>El Marco de los 5 Pasos para Dise&#241;ar tu Portal de Presencia Cero</h3><p>Aqu&#237; tienes el orden exacto para implementarlo. Se llama <strong>el Modelo de Presencia Cero</strong> &#8212; una arquitectura de entrega donde el valor viaja en push y el portal es una red de seguridad, no un destino.</p><p><strong>Paso 1 &#8212; Codifica el contrato de datos primero.</strong></p><p>Escribe exactamente qu&#233; recibe el cliente cada ciclo: campos, formato, desencadenantes de cambio y cadencia. Cualquier desviaci&#243;n de ese contrato se trata como <strong>un incidente de producci&#243;n</strong>.</p><p>El contrato existe antes que cualquier trabajo de UI. Sin &#233;l, todo lo dem&#225;s es an&#233;cdota.</p><p><strong>Paso 2 &#8212; Construye la pipeline de tres capas.</strong></p><p>Captura &#8594; An&#225;lisis &#8594; Entrega. Interfaces estables entre capas. El contrato es la interfaz; las capas son la implementaci&#243;n.</p><p>As&#237; la consistencia del entregable sobrevive al cambio de personal, herramientas y fuentes.</p><p><strong>Paso 3 &#8212; Dise&#241;a el portal como presencia cero.</strong></p><p>El canal de entrega por defecto es push: entradas de timeline, alertas y digest programados que llegan sin acci&#243;n del cliente.</p><p>Define una visita como anomal&#237;a a investigar. No como un KPI a crecer.</p><p><strong>Paso 4 &#8212; Instituye el timeline como ritual fijo.</strong></p><p>El mismo d&#237;a. La misma estructura. Cada ciclo.</p><p>La cadencia convierte la investigaci&#243;n de evento en h&#225;bito. Y el h&#225;bito es lo que el cliente renueva.</p><p><strong>Paso 5 &#8212; Pasa cada feature por el filtro de "no construir".</strong></p><p>&#191;Aumenta la sensaci&#243;n de ser monitorizado, o solo a&#241;ade superficie y modos de fallo?</p><p>El tiempo es la &#250;nica moneda irrecuperable. Cada feature que no construyes es tiempo que recuperas.</p><h3>La Ventaja de la Quietud</h3><p>En un mundo donde los proveedores presumen de dashboards y de vol&#250;menes de datos, la jugada ganadora es la contraria.</p><p>Un sistema m&#225;s peque&#241;o. M&#225;s callado. M&#225;s fiable.</p><p>Que hace que el cliente se sienta <strong>vigilado, no impresionado</strong>.</p><p>La consistencia y la reproducibilidad superan al volumen de datos porque hacen que el entregable sea auditable y predecible. Y esa predictibilidad es lo que convierte a tu servicio en infraestructura &#8212; el &#250;nico tipo de proveedor que justifica un contrato recurrente en vez de una compra puntual.</p><p>Tu producto no es el informe que aparece en pantalla. Es la alarma mental del cliente que se apaga cada martes cuando ve que la monitorizaci&#243;n ya est&#225; hecha.</p><p>Dise&#241;a para esa alarma. Y acepta que el portal &#8212; si lo haces bien &#8212; quedar&#225; casi vac&#237;o.</p><p>Esa quietud es tu se&#241;al de que el sistema est&#225; funcionando. Un sistema de entrega que no exige visitas es el m&#225;s cercano al verdadero <strong>productized services business model</strong>: uno donde el producto no es el contenido, sino la certeza de que nunca dejar&#225; de llegar.</p><p>---</p><p><strong>Conclusiones clave:</strong></p><p>&#8594; El valor RaaS se entrega con push (timeline, alertas, digest), no con pull (un portal que el cliente debe recordar visitar).</p><p>&#8594; Una visita al portal es una se&#241;al de fallo, no una m&#233;trica de &#233;xito.</p><p>&#8594; El contrato de datos es tu verdadero foso: hace el entregable auditable, predecible y renovable.</p><p>&#8594; La pipeline de tres capas mantiene la consistencia cuando cambian personas, herramientas y fuentes.</p><p>&#8594; El filtro de "no construir" es una estrategia de entrega, no solo de eficiencia.</p><p>El futuro no pertenece a quien construye el dashboard m&#225;s impresionante. Pertenece a quien construye la certeza m&#225;s silenciosa.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/portal-presencia-cero-client-delivery-raas-20260902?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[El 80% de tu Esfuerzo en RaaS Está en el Sitio Equivocado: Automatiza la Entrega, No el Scraping]]></title><description><![CDATA[Tu RaaS no pierde clientes por falta de datos, sino por entregables inconsistentes. Aprende a automatizar la capa de entrega con un contrato de datos y tres capas separadas.]]></description><link>https://newsletter.brianmenagomez.com/p/el-80-de-tu-esfuerzo-en-raas-esta</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/el-80-de-tu-esfuerzo-en-raas-esta</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 01 Sep 2026 07:00:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7729da9a-d62f-44cb-92e7-4bdfc2750ecc_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>El 80% de tu Esfuerzo en RaaS Est&#225; en el Sitio Equivocado: Automatiza la Entrega, No el Scraping</strong></h2><p>Crees que el problema de tu Research-as-a-Service es que no capturas suficientes datos. Que necesitas m&#225;s scrapers, m&#225;s fuentes, m&#225;s volumen para que el servicio parezca serio.</p><p><strong>*Te has equivocado de diagn&#243;stico.</strong>*</p><p>Tu problema no es que te falten datos: es que <strong>tus clientes se dan de baja porque cada informe que reciben llega con una estructura distinta</strong>. Mientras el 80% de tu esfuerzo est&#225; en scrapear, la batalla que de verdad retiene clientes se libra en la capa de entrega &#8212; y la est&#225;s perdiendo.</p><p>El 80/20 est&#225; invertido. Y la mayor&#237;a de las agencias digitales que montan servicios de investigaci&#243;n nunca lo notan porque est&#225;n demasiado ocupadas optimizando lo que pueden medir.</p><p>---</p><h3><strong>Por Qu&#233; Todos Creen Que el Problema es el Volumen de Datos</strong></h3><p>Hay una raz&#243;n estructural por la que los equipos RaaS invierten el 80% de su esfuerzo en la capa de recolecci&#243;n: <strong>el scraping es medible</strong>. URLs visitadas, p&#225;ginas descargadas, documentos procesados. Son m&#233;tricas que puedes poner en un dashboard y mostrar a tu equipo.</p><p>La calidad de la entrega, en cambio, es subjetiva. Dif&#237;cil de cuantificar. Requiere que alguien mire cada informe y juzgue si la estructura es coherente.</p><p>Esto es un cl&#225;sico <strong>efecto Goodhart</strong>: los equipos optimizan lo que pueden medir, aunque sea lo que menos impacta en la retenci&#243;n. La recolecci&#243;n se ha commoditizado &#8212; Scrapy, Playwright, APIs p&#250;blicas y LLMs para resumir hacen que traer datos sea barato y casi infinito &#8212; mientras que el churn se concentra en la &#250;ltima milla: estructura inconsistente, formato variable, citaci&#243;n descuidada, cadencia irregular.</p><p>&#10060; <strong>Lo que hace la mayor&#237;a:</strong> a&#241;ade m&#225;s fuentes al pipeline y mejora los scrapers</p><p>&#9989; <strong>Lo que retiene clientes:</strong> define un contrato de entrega, separa el pipeline en capas y automatiza el QA del entregable final</p><p>---</p><h3><strong>El 80/20 Invertido, en la Pr&#225;ctica</strong></h3><p>Piensa en tu propio pipeline. &#191;D&#243;nde se va el tiempo real de tu equipo?</p><ul><li><p><strong>Collection:</strong> escribir y mantener scrapers, lidiar con cambios de HTML, rotar proxies</p></li><li><p><strong>Processing:</strong> normalizar datos, limpiar, verificar citas, estructurar hallazgos</p></li><li><p><strong>Delivery:</strong> dar formato final, revisar que todo cuadre, enviar al cliente</p></li></ul><p>Si eres como el 80% de los operadores de RaaS, casi todo tu esfuerzo est&#225; en la primera capa. Y la primera capa es exactamente la que <strong>ya no produce diferenciaci&#243;n</strong>.</p><p>Aqu&#237; est&#225; la verdad inc&#243;moda: traer datos ya no es el trabajo caro. Es la normalizaci&#243;n de estructura, la verificaci&#243;n de citas y el formateo lo que decide si tu cliente renueva. Los equipos que invierten en la &#250;ltima milla <strong>reducen retrabajo y aumentan retenci&#243;n sin a&#241;adir ni una sola fuente nueva a su pipeline</strong>.</p><p>Yo lo veo con mis propios productos. En <strong>conversoriaecnae.es</strong> o <strong>gestoriascercademi.com</strong>, el valor no est&#225; en cu&#225;ntos registros capturo. Est&#225; en que cada entrega llega con la misma estructura, la misma cadencia y la misma fiabilidad &#8212; semana tras semana. Eso es lo que hace que un servicio de retenci&#243;n se sostenga.</p><p>---</p><h3><strong>El Content Lake Aplicado a Investigaci&#243;n</strong></h3><p>La soluci&#243;n estructural no es m&#225;s scraping. Es una <strong>arquitectura de tres capas</strong> que separa la recolecci&#243;n del procesamiento y de la entrega, en lugar de un &#250;nico monolito donde todo se mezcla.</p><p>Y hay una analog&#237;a perfecta que ya hemos cubierto en este hilo: <strong>Sanity.io no es un CMS. Es una base de datos de contenido (el content lake) con un editor open source encima</strong>. Los equipos fallan cuando buscan un dashboard en vez de escribir un esquema de contenido.</p><p>Traslada esa lecci&#243;n a tu RaaS y obtienes esto: en vez de producir un PDF por cliente, produces <strong>objetos de investigaci&#243;n estructurados</strong> &#8212; hallazgos, fuentes con URL y fecha de acceso, metadatos, nivel de confianza. Objetos que se pueden rebanar por cliente, actualizar incrementalmente y versionar.</p><p>El entregable final es una <strong>vista sobre los datos</strong>, no el almacenamiento. Quien entiende esto puede servir a diez clientes con la misma base de datos. Quien no, rehace cada informe desde cero.</p><p>Aqu&#237; est&#225; el contrato de datos que define cada entregable:</p><p>```json</p><p>{</p><p>"$schema": "https://example.com/contracts/research-deliverable/v1",</p><p>"type": "object",</p><p>"required": ["client_id", "topic", "generated_at", "findings"],</p><p>"properties": {</p><p>"client_id": { "type": "string", "pattern": "^cli_" },</p><p>"topic": { "type": "string" },</p><p>"generated_at": { "type": "string", "format": "date-time" },</p><p>"findings": {</p><p>"type": "array",</p><p>"minItems": 1,</p><p>"items": {</p><p>"type": "object",</p><p>"required": ["claim", "sources", "confidence"],</p><p>"properties": {</p><p>"claim": { "type": "string" },</p><p>"confidence": { "enum": ["high", "medium", "low"] },</p><p>"sources": {</p><p>"type": "array",</p><p>"minItems": 1,</p><p>"items": {</p><p>"type": "object",</p><p>"required": ["url", "accessed_at", "title"],</p><p>"properties": {</p><p>"url": { "type": "string", "format": "uri" },</p><p>"accessed_at": { "type": "string", "format": "date" },</p><p>"title": { "type": "string" }</p><p>}</p><p>}</p><p>}</p><p>}</p><p>}</p><p>}</p><p>}</p><p>}</p><p>```</p><p>Cada cliente es una vista sobre este mismo esquema. Cambias el `client_id`, el formato de salida, el template &#8212; y el contrato sigue siendo el mismo.</p><p>---</p><h3><strong>Tres Capas, No Un Monolito</strong></h3><p>Aqu&#237; est&#225; el <strong>fragmento clave</strong> del framework que te propongo. Se llama <strong>El Patr&#243;n del Contrato de Datos</strong> y tiene tres capas, cada una con su propio artefacto de entrada y salida:</p><p><strong>Capa 1 &#8212; Collection (scrapea amplio y barato):</strong></p><p>```python</p><h1>scrape_wide.py &#8212; Capa 1: solo recolecta crudo, sin pulir</h1><p>from scrapy import Spider</p><p>from datetime import datetime</p><p>class WideCollector(Spider):</p><p>name = "wide_collector"</p><h1>Output: fichero crudo, sin normalizar</h1><p>def parse(self, response):</p><p>yield {</p><p>"raw_html": response.text[:5000],</p><p>"url": response.url,</p><p>"crawled_at": datetime.utcnow().isoformat(),</p><h1>SIN estructura de negocio. Eso es trabajo de la Capa 2.</h1><p>}</p><p>```</p><p><strong>Capa 2 &#8212; Processing (extracci&#243;n y normalizaci&#243;n con IA):</strong></p><p>```python</p><h1>normalize.py &#8212; Capa 2: objetos estructurados, listos para el contrato</h1><p>from pydantic import BaseModel</p><p>class Finding(BaseModel):</p><p>claim: str</p><p>confidence: str  # high | medium | low</p><p>sources: list[Source]  # validados contra el contrato</p><p>def process(raw_records: list[dict]) -&gt; list[Finding]:</p><h1>LLM resume y estructura. La salida CUMPLE el contrato de datos.</h1><p>return [llm_extract(record) for record in raw_records]</p><p>```</p><p><strong>Capa 3 &#8212; Delivery (templates + QA + formato final):</strong></p><p>```python</p><h1>deliver.py &#8212; Capa 3: QA autom&#225;tico + formato por cliente</h1><p>def validate_deliverable(payload: dict) -&gt; bool:</p><p>errors = []</p><p>if "findings" not in payload or len(payload["findings"]) == 0:</p><p>errors.append("Sin hallazgos")</p><p>for f in payload["findings"]:</p><p>if not all(s["url"].startswith("http") for s in f["sources"]):</p><p>errors.append(f"Fuente inv&#225;lida en: {f['claim'][:50]}")</p><p>if f["confidence"] not in {"high", "medium", "low"}:</p><p>errors.append(f"Confianza inv&#225;lida: {f['confidence']}")</p><h1>Un entregable que no pasa QA NUNCA llega al cliente</h1><p>return len(errors) == 0, errors</p><p>```</p><p>Ninguna capa depende del formato de salida de otra. Puedes cambiar el scraper sin tocar el template. Puedes cambiar el template sin rehacer el pipeline de datos.</p><p>---</p><h3><strong>El Stretch Goal: Servir a Escala con una Base de Datos</strong></h3><p>Cuando separas las capas, algo m&#225;gico ocurre: <strong>puedes servir a m&#225;s clientes sin m&#225;s horas de trabajo</strong>.</p><p>Tu equipo ya no rehace el informe desde cero. Recompila datos, ejecuta la normalizaci&#243;n, valida contra el contrato, y genera el formato del cliente. La IA puede recompilar, actualizar y re-formatear por cliente <strong>sin rehacer el trabajo</strong>.</p><p>Y aqu&#237; entra la lecci&#243;n bootstrapped que hemos cubierto antes en este hilo: <strong>no construyas una plataforma de entrega a medida "por si acaso"</strong>. El tiempo es la &#250;nica moneda irrecuperable, y la infraestructura especulativa se paga en velocidad presente. En un RaaS bootstrapped, esa velocidad es literalmente la capacidad de servir m&#225;s clientes.</p><p>Usa herramientas existentes. Un content lake como Sanity, la API de Notion, o incluso un generador de documentaci&#243;n. <strong>El contrato de datos es tuyo; la infraestructura no tiene por qu&#233; serlo.</strong></p><p>---</p><h3><strong>El Marco de 5 Pasos para Implementar el Patr&#243;n del Contrato de Datos</strong></h3><p><strong>Paso 1 &#8212; Define primero el contrato de entrega.</strong> Un esquema &#250;nico y versionado de cada entregable, ANTES de escribir un solo scraper. El cliente no compra datos: compra un formato, una estructura y una cadencia. Escribe el JSON Schema primero.</p><p><strong>Paso 2 &#8212; Separa el pipeline en tres capas con SLAs propios.</strong> Collection scrapea amplio y barato. Processing normaliza con IA. Delivery aplica templates y QA. Que ninguna capa dependa del formato de salida de otra.</p><p><strong>Paso 3 &#8212; Audita d&#243;nde se va tu esfuerzo real.</strong> Mide horas por capa durante un sprint. Si m&#225;s del 80% est&#225; en Collection, rebalancea hacia Processing y Delivery. Ese es el 80/20 invertido que hay que corregir.</p><p><strong>Paso 4 &#8212; Mide el churn por causa, no por volumen.</strong> Clasifica cada baja de cliente &#8212; estructura inconsistente vs. datos incompletos vs. precio. Deja que los datos de retenci&#243;n dirijan la hoja de ruta t&#233;cnica, no las m&#233;tricas de scraping.</p><p><strong>Paso 5 &#8212; Trata cada entregable como dato estructurado, no como documento.</strong> Si el output vive en un content lake, puedes servir a diez clientes con la misma base.</p><p>---</p><h3><strong>Las Objeciones Que Vas a Querer Plantear (y Sus Respuestas)</strong></h3><p><strong>"Los clientes se van porque la investigaci&#243;n est&#225; incompleta &#8212; necesito M&#193;S datos."</strong></p><p>Ambos factores conviven, pero con pesos distintos. No lo asumas: haz entrevistas de churn y audita los entregables rechazados para clasificar el motivo real de cada baja antes de decidir d&#243;nde invertir. Ver&#225;s que la estructura inconsistente duele m&#225;s que el dato faltante.</p><p><strong>"Estandarizar mata el valor a medida &#8212; el cliente paga por an&#225;lisis personalizado."</strong></p><p>El contrato aplica al <strong>contenedor</strong> &#8212; formato, estructura, citaci&#243;n, cadencia &#8212; no al <strong>contenido anal&#237;tico</strong>. La personalizaci&#243;n vive en la capa de contenido y la consistencia en la capa de formato. Son capas separadas. Exactamente el argumento de las tres capas.</p><p><strong>"Esto suena a que nos convertimos en una empresa de software."</strong></p><p>No. El content lake reutiliza herramientas existentes. Est&#225;s definiendo un contrato de datos, no construyendo una plataforma. El objetivo no es la tecnolog&#237;a: <strong>es la retenci&#243;n</strong>.</p><p>---</p><h3><strong>La Consistencia Es la Confianza</strong></h3><p>Durante a&#241;os el marketing digital vendi&#243; el productoizado como un problema de adquisici&#243;n. Consigue m&#225;s clientes, haz m&#225;s lead gen, escala el outreach. Pero la retenci&#243;n es el motor silencioso del <strong>productized services business model</strong> &#8212; y en un RaaS, la retenci&#243;n no se juega en las fuentes que scrapeas. Se juega en el formato que entregas.</p><p>La entrega de investigaci&#243;n es un problema de <strong>UX, no de ingenier&#237;a</strong>. La experiencia del cliente ES la estructura, el formato y la cadencia del entregable &#8212; no los datos que hay detr&#225;s. En un servicio donde el output es el producto, la consistencia es la confianza, y la confianza es la retenci&#243;n.</p><p>Deja de optimizar el scraping. Define el contrato, separa las capas y automatiza el QA del entregable. El 80% de tu esfuerzo est&#225; donde ya no importa.</p><p><strong>Lo que viene despu&#233;s es un escenario inc&#243;modo para quienes siguen compitiendo por volumen: la ventaja ya no se construye con m&#225;s datos, sino con la arquitectura que los convierte en confianza. La recolecci&#243;n se ha vuelto infinitamente barata. La consistencia, infinitamente valiosa.</strong></p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/raas-automatiza-la-entrega-no-el-scraping-20260901?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item><item><title><![CDATA[El flag que rompe el build: por qué compilo con webpack y nunca con turbopack]]></title><description><![CDATA[Sanity crashea cada vez que activo --turbopack, as&#237; que el build de producci&#243;n de Conversor compila siempre con webpack. La historia de c&#243;mo bloque&#233; el flag en dos capas y por qu&#233; un tsc en verde n...]]></description><link>https://newsletter.brianmenagomez.com/p/el-flag-que-rompe-el-build-por-que</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/el-flag-que-rompe-el-build-por-que</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Tue, 01 Sep 2026 05:37:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7nbD!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffb9dcf7f-ea19-48c6-9eb8-91e45dd4b8eb_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Este ensayo se publico primero en <a href="https://brianmenagomez.com/blog/el-flag-que-rompe-el-build">brianmenagomez.com/blog/el-flag-que-rompe-el-build</a>.</p><h2>El flag que rompe el build</h2><p>Activar `--turbopack` deber&#237;a haber sido un cambio de una l&#237;nea. Es lo que le&#237;a en la documentaci&#243;n de Next.js: el bundler nuevo, escrito en Rust, m&#225;s r&#225;pido que webpack, empujado como su reemplazo natural. En mi cabeza la cuenta era sencilla. Pongo el flag, el build tarda menos, y me olvido. Lo puse. El build cay&#243;.</p><p>No cay&#243; una vez. Cay&#243; cada vez, en el mismo punto, con el mismo error. Quitaba el flag y compilaba sin problemas. Lo volv&#237;a a poner y crasheaba otra vez, id&#233;ntico. Repet&#237; el ciclo unas cuantas veces por pura incredulidad: el resultado no cambi&#243; en ninguna. Un fallo que se repite de forma determinista deja de ser un misterio: se convierte en un dato. Y el dato era inc&#243;modo: Sanity, el CMS con el que gestiono el contenido de Conversor, no soporta Turbopack.</p><p>Lo que estaba en juego no era un detalle cosm&#233;tico. El build es lo que empaqueta el sitio que sirve los datos fiscales y la API. Un build roto significa una p&#225;gina ca&#237;da y unos docs inaccesibles. Por eso me lo tom&#233; como un problema de verdad y no como una curiosidad de bundler.</p><h3>Lo que no pude arreglar</h3><p>Aqu&#237; est&#225; la parte que m&#225;s me cost&#243; aceptar. No es un bug que pueda arreglar desde mi c&#243;digo. Yo no escribo el plugin de Sanity; solo lo consumo. La incompatibilidad vive en una dependencia de terceros, y ah&#237; no pinto nada. Ten&#237;a dos caminos. Perseguir el fallo durante d&#237;as &#8212; probar versiones, abrir issues, leer c&#243;digo ajeno &#8212; o aceptar el dato y decidir.</p><p>Decid&#237; lo corto. El build de producci&#243;n usa siempre webpack: `npm run build`, sin flag. No es la opci&#243;n m&#225;s r&#225;pida, pero es la que no se rompe. Cuando despliegas cada semana, un build un poco m&#225;s lento es un precio razonable por no levantarte con un deploy ca&#237;do por culpa de una dependencia que no controlas.</p><p>Pero una decisi&#243;n tomada una vez no basta, porque yo no soy el &#250;nico que toca este repositorio.</p><h3>La defensa en profundidad</h3><p>Tengo un operador nocturno: un agente aut&#243;nomo que corre tareas de mantenimiento mientras duermo. Ese agente podr&#237;a reintroducir `--turbopack` de buena fe. Porque lo ley&#243; en la documentaci&#243;n, porque es lo que se recomienda, porque en las instrucciones que le dej&#233; nadie le cont&#243; lo del crash. No me f&#237;o de mi memoria para impedirlo, y no me f&#237;o de la suya.</p><p>As&#237; que lo bloque&#233; de forma determinista, en dos capas.</p><p>La primera es `permissions.deny`, la lista de permisos que el agente no puede usar. Ah&#237;, ese flag no existe: est&#225; denegado antes de que nada se ejecute. La segunda es `guard.mjs`, un hook que corre en cada paso y frena el flag antes de que llegue a tocar nada. Son dos mecanismos distintos, en dos puntos distintos del flujo: uno a nivel de permiso, otro a nivel de ejecuci&#243;n. Si una capa falla, queda la otra. Es defensa en profundidad, y el motivo es concreto: quiero que el flag no pueda volver a entrar por descuido, pase lo que pase. La redundancia no es paranoia; es lo que convierte un "no deber&#237;a pasar" en un "no puede pasar".</p><p>Esto es la diferencia entre esperar que no ocurra y hacer que no pueda ocurrir. Yo ya no espero.</p><h3>El build local no sirve como gate</h3><p>La segunda lecci&#243;n tir&#243; abajo un h&#225;bito que daba por bueno. Durante un tiempo verifiqu&#233; todo en mi m&#225;quina antes de subir nada: `npm run build` local, y si estaba en verde, adelante. El problema es que en Windows ese build local est&#225; roto para los c&#243;digos con asterisco. Los c&#243;digos IAE y CNAE con los que trabajo llevan `*`, y el build se atraganta con ellos en mi entorno. El resultado era un gate que me daba falsa seguridad o falsas alarmas, dependiendo del d&#237;a, y que adem&#225;s no reflejaba lo que iba a pasar en producci&#243;n.</p><p>Dej&#233; de hacer gate en el build local. La correcci&#243;n del build de producci&#243;n se verifica solo en la preview de Vercel. Si la preview compila, el build est&#225; bien. Si no, no lo est&#225;. No hay grises. La preview pas&#243; a ser la fuente de verdad, y el build local qued&#243; como una comprobaci&#243;n temprana que no siempre est&#225; disponible y en la que ya no baso ninguna decisi&#243;n.</p><h3>tsc en verde no es next build en verde</h3><p>La tercera lecci&#243;n es la que m&#225;s me resist&#237; a aceptar: un `tsc` en verde no prueba que un `next build` est&#233; en verde. Son dos comprobaciones distintas, y yo las trataba como una sola.</p><p>El chequeo de tipos de TypeScript valida que los tipos cuadran: que una funci&#243;n devuelve lo que declara, que un objeto tiene los campos que debe. El build valida que el proyecto entero empaqueta: el bundler, los plugins, el CMS, todo junto y en orden. Puedo tener tipos perfectos y un build que crashea. Lo s&#233; porque me pas&#243;: `tsc` pasaba sin un solo error, y Sanity segu&#237;a cayendo con el flag. Eran dos preguntas distintas y yo solo miraba la primera creyendo que respond&#237;a por las dos.</p><h3>Qu&#233; aprend&#237;</h3><p>Lo que me llevo es m&#225;s simple de lo que parece. Cuando un fallo es determinista y no es m&#237;o, no lo persigo: lo bloqueo, y lo bloqueo dos veces. Y ya no me f&#237;o de una comprobaci&#243;n como prueba de otra. El chequeo de tipos responde a una pregunta, el build responde a otra, la preview responde a una tercera. Cada una necesita su verificaci&#243;n, y ninguna sustituye a las dem&#225;s.</p><p>Lo dem&#225;s &#8212; webpack en vez de Turbopack &#8212; es solo una l&#237;nea en la configuraci&#243;n y dos capas de guardarra&#237;l que me dejan dormir tranquilo.</p><p>Lo que publica ese build, si quieres verlo: https://www.conversoriaecnae.es/api/v1/docs</p>]]></content:encoded></item><item><title><![CDATA[Sanity.io no es un CMS. Es una Base de Datos de Contenido con un Editor que te Tocará Reconstruir]]></title><description><![CDATA[Sanity.io no es un CMS: es un content lake con GROQ y un editor open source. Aprende a modelarlo como esquema-as-code en TypeScript en este tutorial.]]></description><link>https://newsletter.brianmenagomez.com/p/sanityio-no-es-un-cms-es-una-base-eb1</link><guid isPermaLink="false">https://newsletter.brianmenagomez.com/p/sanityio-no-es-un-cms-es-una-base-eb1</guid><dc:creator><![CDATA[Brian Mena Gómez]]></dc:creator><pubDate>Mon, 31 Aug 2026 07:00:15 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/aee7f02c-74db-4ab4-bbc1-3babf572c3cf_1080x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Sanity.io fue fundada en Oslo en 2014, y en 2025 acab&#243; absorbida por Contentful. Pero jam&#225;s envi&#243; un CMS. Envi&#243; una base de datos JSON, un lenguaje de consultas que nadie fuera de su ecosistema conoce, y un editor React open source que esperan que reconstruyas</strong></h2><p>Los equipos que triunfan con Sanity son los que dejan de preguntar "&#191;qu&#233; pinta el dashboard?" y empiezan a preguntar "&#191;cu&#225;l es mi esquema de contenido?".</p><p>El resto fracasa. No por culpa de Sanity. Por culpa de su propio marco mental.</p><p>La sabidur&#237;a convencional dice: "un CMS es donde los editores escriben blogs, y eliges uno comparando dashboards, plugins y editores WYSIWYG". Eso es err&#243;neo a tres niveles cuando hablamos de Sanity.</p><p>Primero: Sanity no tiene p&#225;ginas. El contenido es JSON definido por esquema. Comparar dashboards es comparar la decoraci&#243;n cuando lo que eliges es la capa de datos.</p><p>Segundo: todo el mundo asume que headless = "sin edici&#243;n visual". Falso. La colaboraci&#243;n en tiempo real y Visual Editing dan a los editores previews en vivo.</p><p>Tercero: la mayor&#237;a asume que las consultas necesitan GraphQL o REST. Sanity responde con GROQ, un lenguaje completo que supera a GraphQL t&#237;pico para contenido relacional. Elegir Sanity significa comprometerte con un lenguaje que la mayor&#237;a de desarrolladores jam&#225;s ha o&#237;do nombrar.</p><p><strong>*El take m&#225;s contracorriente: la mayor debilidad de Sanity es tambi&#233;n su mayor fortaleza. No hay producto hasta que escribes c&#243;digo.</strong>*</p><p>---</p><h2><strong>El Problema: Est&#225;s Comparando CMS Cuando Deber&#237;as Elegir una Capa de Datos</strong></h2><p>WordPress, Drupal, incluso Strapi: organizan el contenido alrededor de p&#225;ginas o documentos con campos fijos.</p><p>Sanity lo organiza alrededor de un esquema que escribes en TypeScript. Arrays, objetos, referencias y bloques de portable text. La misma entidad puede renderizarse como p&#225;gina web, tarjeta m&#243;vil, layout de impresi&#243;n o respuesta de API sin duplicaci&#243;n.</p><p>Pero eso significa que los stakeholders no t&#233;cnicos necesitan que les expliques el esquema <strong>antes de siquiera ver un editor</strong>.</p><p>Ah&#237; est&#225; el primer choque. Tu cliente quiere escribir un art&#237;culo. T&#250; le ense&#241;as un fichero de TypeScript.</p><p>Y el segundo choque ocurre un mes despu&#233;s, cuando intentas la integraci&#243;n con herramientas esperando un endpoint REST y descubres GROQ.</p><p>&#10060; <strong>El enfoque equivocado:</strong> Buscas el mejor "CMS headless" comparando dashboards, WYSIWYG y plugins. Esperas un giro que te d&#233; el panel bonito.</p><p>&#9989; <strong>El enfoque correcto:</strong> Asumes que est&#225;s eligiendo una capa de datos. El esquema es un contrato de programaci&#243;n, no una pantalla de ajustes. Y ese contrato vive en git.</p><p>El tercer choque es el m&#225;s silencioso: la adquisici&#243;n por parte de Contentful en 2025. Dos de los nombres m&#225;s grandes del headless content se fusionaron por l&#243;gica de infraestructura y consolidaci&#243;n, no por listas de features.</p><p>Para ti, como desarrollador, es una se&#241;al clara: <strong>elige tu stack por modelado de datos y dise&#241;o de API, no por el roadmap del vendor actual</strong>. Porque el mapa de vendors se mueve debajo de tus pies.</p><p>---</p><h2><strong>La Evidencia: Lo que Sanity Realmente Despliega</strong></h2><p>Desmontemos la arquitectura. Sanity es, arquitect&#243;nicamente, una base de datos de contenido hospedada &#8212; el famoso content lake. El contenido se almacena como documentos JSON, no como p&#225;ginas renderizadas. No existe una entidad "p&#225;gina" en el modelo de datos.</p><p>Esa pieza cambia todo.</p><p>El Studio es open source bajo licencia MIT, y es una aplicaci&#243;n React configurable. <strong>La interfaz de edici&#243;n es c&#243;digo que tus equipos pueden extender o reemplazar.</strong> Se configura v&#237;a `sanity.config.ts`, no v&#237;a un panel de administraci&#243;n.</p><p>Y GROQ &#8212; Graph-Relational Object Queries &#8212; es el motor relacional. Proyecta, filtra, atraviesa arrays y desreferencia referencias con el operador `-&gt;`. Sin GraphQL. Sin fragamentos. Sin round-trips REST.</p><p>```typescript</p><p>// sanity.config.ts &#8212; el "dashboard" que eliges</p><p>import { defineConfig } from 'sanity'</p><p>import { structureTool } from 'sanity/structure'</p><p>import { schemaTypes } from './schema'</p><p>export default defineConfig({</p><p>name: 'default',</p><p>title: 'Mi Studio de Contenido',</p><p>projectId: 'abc123',</p><p>dataset: 'production',</p><p>plugins: [structureTool()],</p><p>schema: { types: schemaTypes },</p><p>})</p><p>```</p><p>Se acab&#243; el comparar paneles. Esto es lo que eliges: <strong>una configuraci&#243;n de TypeScript que define c&#243;mo se modelo y edita tu contenido</strong>.</p><p>La evidencia est&#225; en el modelo de datos, no en el marketing.</p><p>---</p><h2><strong>El An&#225;lisis: Por Qu&#233; esto Importa Ahora, en 2026</strong></h2><p>La consolidaci&#243;n del mercado confirma el papel de Sanity. La adquisici&#243;n por Contentful en 2025 reframe por completo la categor&#237;a. Dos gigantes del headless content se fusionaron y la l&#243;gica fue de infraestructura y consolidaci&#243;n: no listas de features.</p><p>Para ti, desarrollador, hay una lecci&#243;n pr&#225;ctica y urgente: <strong>tu contenido deja de ser documentos de un vendor y se convierte en datos versionados en git.</strong> El esquema vive en tu repositorio, se revisa en pull requests, se migra con commits, no con clics en un panel.</p><p>Eso es lo que ning&#250;n CMS administrado te ha podido dar jam&#225;s.</p><p>Y la edici&#243;n visual disuelve la vieja l&#237;nea entre headless y legacy. El Presentation tool y la colaboraci&#243;n en tiempo real dan a los editores previews en vivo sobre el frontend real. Lo que los cr&#237;ticos del headless siempre dijeron imposible &#8212; editar con contexto visual &#8212; es ahora el default.</p><p>El resultado pr&#225;ctico: "headless" ya no significa "a ciegas". El factor decisivo entre Sanity y un CMS legacy ya no es el preview. <strong>Es si quieres tu modelo de contenido versionado en git o atrapado en una base de datos opaca.</strong></p><p>---</p><h2><strong>El Framework: El Patr&#243;n del Esquema Como Contrato</strong></h2><h3><strong>Paso 1: Scaffold y Acepta el Studio por Defecto</strong></h3><p>```bash</p><p>npm create sanity@latest</p><h1>O, para un proyecto existente:</h1><p>npm install sanity @sanity/client</p><p>```</p><p>Acepta el setup por defecto. Crear&#225;s una app Next.js o Remix con el Studio integrado. No te preocupes por personalizar a&#250;n. La personalizaci&#243;n viene despu&#233;s, cuando el esquema est&#233; estable.</p><h3><strong>Paso 2: Modela Contenido Como C&#243;digo Primero</strong></h3><p>Resiste la tentaci&#243;n de empezar por la UI. Define cada tipo &#8212; post, autor, producto &#8212; como un objeto TypeScript con campos, validaci&#243;n y referencias.</p><p>```typescript</p><p>// schema/post.ts &#8212; el modelo de contenido ES TypeScript</p><p>import { defineField, defineType } from 'sanity'</p><p>export default defineType({</p><p>name: 'post',</p><p>title: 'Post',</p><p>type: 'document',</p><p>fields: [</p><p>defineField({ name: 'title', type: 'string', validation: r =&gt; r.required() }),</p><p>defineField({ name: 'slug', type: 'slug', options: { source: 'title' } }),</p><p>defineField({ name: 'author', type: 'reference', to: [{ type: 'author' }] }),</p><p>defineField({ name: 'body', type: 'blockContent' }),</p><p>defineField({ name: 'tags', type: 'array', of: [{ type: 'reference', to: [{ type: 'tag' }] }] }),</p><p>],</p><p>})</p><p>```</p><p>Este fichero es tu contrato. Vive en git. Se revisa en PR. Se versiona.</p><h3><strong>Paso 3: Consulta con GROQ a trav&#233;s de @sanity/client</strong></h3><p>Versiona tus queries en un &#250;nico fichero. Usa el plugin Vision en Studio para iterar contra datos reales.</p><p>```javascript</p><p>// lib/queries.js &#8212; todas las queries versionadas en un sitio</p><p>export const postQuery = `*[_type == "post" &amp;&amp; publishedAt &lt;= now()]</p><p>| order(publishedAt desc) {</p><p>title,</p><p>slug,</p><p>"author": author-&gt;name,</p><p>"tags": tags[]-&gt;{label}</p><p>}`</p><p>```</p><p><strong>*El operador `-&gt;` desreferencia referencias inline. Lo que en GraphQL ser&#237;an m&#250;ltiples fragments, en GROQ es una l&#237;nea.</strong>*</p><h3><strong>Paso 4: Conecta a tu Frontend</strong></h3><p>```javascript</p><p>// app/page.js &#8212; fetch desde una app Next.js</p><p>import { client } from './lib/sanity'</p><p>import { postQuery } from './lib/queries'</p><p>export default async function HomePage() {</p><p>const posts = await client.fetch(postQuery, {}, { next: { revalidate: 60 } })</p><p>return &lt;PostList posts={posts} /&gt;</p><p>}</p><p>```</p><p>Sanity sirve desde CDN por defecto. El caching es parte de la capa de datos, no un plugin.</p><h3><strong>Paso 5: Real-time en vez de Rebuilds Est&#225;ticos</strong></h3><p>Activa el draft mode o el preview. Usa `client.listen()` o el Presentation tool para actualizaciones de contenido instant&#225;neas, en vez de rebuilds on-publish.</p><p>```javascript</p><p>// Escucha cambios en tiempo real &#8212; sin rebuild on publish</p><p>const subscription = client.listen(`*[_type == "post"]`)</p><p>subscription.subscribe(update =&gt; {</p><p>console.log('Contenido actualizado:', update.result)</p><p>// Invalidaci&#243;n selectiva de cach&#233;, no rebuild completo</p><p>})</p><p>```</p><h3><strong>Paso 6: Extiende el Studio para tus Workflows Reales</strong></h3><p>El editor es una app React que posees. A&#241;ade inputs custom, validaci&#243;n espec&#237;fica y plugins. Despu&#233;s itera sobre el esquema sabiendo que las migraciones son aditivas, no destructivas.</p><p>```tsx</p><p>// Custom input: valida slugs &#250;nicos v&#237;a API</p><p>import { useClient } from 'sanity'</p><p>import { StringInputProps } from 'sanity'</p><p>export function UniqueSlugInput(props: StringInputProps) {</p><p>const client = useClient({ apiVersion: '2026-01-01' })</p><p>// Verifica unicidad contra el content lake en tiempo real</p><p>return &lt;input {...props} placeholder="Escribe un slug &#250;nico..." /&gt;</p><p>}</p><p>```</p><p>El nombre de este patr&#243;n: <strong>El Patr&#243;n del Esquema Como Contrato</strong>. El esquema no es configuraci&#243;n. Es el contrato firmado entre editores, frontend y API. Todo lo dem&#225;s &#8212; Studio, queries, previews &#8212; desciende de &#233;l.</p><p>---</p><h2><strong>Las Objeciones que Me Har&#237;as (y sus Respuestas)</strong></h2><p><strong>"&#191;Por qu&#233; a&#241;adir GROQ al stack cuando ya conozco GraphQL? &#191;No eleva la curva de aprendizaje y me encierra en Sanity?"</strong></p><p>GROQ se aprende en una tarde. Viene con el client oficial. Y el contrato esquema-as-code reduce el riesgo de lock-in comparado con esquemas opacos de base de datos. La concesi&#243;n honesta: el ecosistema de tooling es menor que el de GraphQL.</p><p><strong>"El contenido JSON estructurado significa que no hay integridad relacional real &#8212; las referencias son strings que pueden quedar hu&#233;rfanas."</strong></p><p>El tipo `reference` de Sanity incluye reglas de validaci&#243;n. El trade-off: flexibilidad y evoluci&#243;n sin migraciones destructivas contra garant&#237;as de foreign keys estrictas. Si tu equipo necesita constraints ACID duros, reconsidera si un CMS es la herramienta correcta en absoluto.</p><p><strong>"Es SaaS hospedado &#8212; &#191;y si el servicio o el vendor fusionado cambia de rumbo tras la adquisici&#243;n?"</strong></p><p>El Studio es open source MIT. La API y la especificaci&#243;n GROQ son p&#250;blicas. Puedes exportar y hacer backup del contenido v&#237;a la API. Mitigaci&#243;n real. Pero s&#233; honesto: el content lake es un servicio administrado. Si necesitas self-hosting extremo, valora eso antes de comprometerte.</p><p>---</p><h2><strong>Resumen y Hacia Delante</strong></h2><p>El error colectivo es tratar Sanity como un WordPress sin interfaz. Lo que es: una base de datos de contenido donde el esquema es un contrato TypeScript versionado en git, consultada con GROQ, administrada por un editor React open source.</p><p><strong>Los take-aways:</strong></p><ul><li><p>El esquema es el producto. Todo lo dem&#225;s desciende.</p></li><li><p>GROQ reemplaza lo que a GraphQL le costar&#237;a fragments y round-trips.</p></li><li><p>El editor es c&#243;digo que posees, no un dashboard alquilado.</p></li><li><p>El contenido versionado en git es la raz&#243;n real de elegir Sanity.</p></li></ul><p>La frase que me repito cada vez que un cliente me ense&#241;a el "CMS": elige tu stack por el modelo de datos, no por la lista de features del vendor actual. El mapa de vendors se mover&#225;. Tu esquema, versionado en git, permanece.</p><div><hr></div><p>Lee el art&#237;culo completo en <a href="https://brianmenagomez.com/blog/sanity-io-headless-cms-no-es-un-cms-sino-base-de-datos-de-contenido-20260831?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>M&#225;s sobre mis servicios en <a href="https://www.brianmenagomez.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=crosslink">brianmenagomez.com</a></p><p>Herramientas: <a href="https://conversoriaecnae.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Conversor IAE CNAE</a> &#183; <a href="https://gestoriascercademi.com?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Gestorias cerca de ti</a> &#183; <a href="https://modulosirpf.es?utm_source=brianmenagomez&amp;utm_medium=substack&amp;utm_campaign=ecosystem">Calculadora IRPF</a></p>]]></content:encoded></item></channel></rss>