<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://antoniohumanes.com/blog</id>
    <title>Código &amp; Pasta Blog</title>
    <updated>2026-05-17T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://antoniohumanes.com/blog"/>
    <subtitle>Código &amp; Pasta Blog</subtitle>
    <icon>https://antoniohumanes.com/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Gamificación en una aplicación real: lo que funcionó y lo que no]]></title>
        <id>https://antoniohumanes.com/blog/2026/05/17/application gamification</id>
        <link href="https://antoniohumanes.com/blog/2026/05/17/application gamification"/>
        <updated>2026-05-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Cómo usamos gamificación en una plataforma de recursos humanos para reducir la fricción al completar perfiles y qué aprendí desde frontend, UX y producto.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Imagen de portada del artículo" src="https://antoniohumanes.com/assets/images/gamification-abc32eddd4adde9985baf4b4a6089060.webp" width="700" height="467" class="img_ev3q"></p>
<p>En uno de los proyectos en los que participé tuvimos que resolver un problema bastante común: los usuarios no completaban suficiente información en su perfil.</p>
<p>La aplicación era una plataforma de recursos humanos. Los candidatos rellenaban sus datos, experiencia, estudios y otra información relevante para poder inscribirse a ofertas de empleo.</p>
<p>El negocio era claro: conectar empresas que necesitaban talento con candidatos que buscaban trabajo.</p>
<p>El problema era que, si el usuario no completaba bien su perfil, las empresas no podían valorar correctamente a los candidatos.</p>
<p>Y si las empresas no podían valorar bien a los candidatos, los candidatos tenían menos opciones de avanzar en los procesos.</p>
<p>El problema afectaba tanto a las empresas como a los candidatos.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="por-qué-hacía-falta-cambiar">¿Por qué hacía falta cambiar?<a href="https://antoniohumanes.com/blog/2026/05/17/application%20gamification#por-qu%C3%A9-hac%C3%ADa-falta-cambiar" class="hash-link" aria-label="Enlace directo al ¿Por qué hacía falta cambiar?" title="Enlace directo al ¿Por qué hacía falta cambiar?" translate="no">​</a></h2>
<p>El problema no era solo visual ni técnico.</p>
<p>La aplicación pedía demasiada información al usuario y lo hacía en un momento delicado: cuando una persona está buscando trabajo, probablemente con poco tiempo, poca paciencia y, en algunos casos, poca soltura con herramientas digitales.</p>
<p>Rellenar un formulario enorme no era precisamente motivador.</p>
<p>Y para negocio, esa falta de información tenía un impacto directo: perfiles menos completos, candidaturas más pobres y menos valor para las empresas que buscaban talento.</p>
<p>El proyecto necesitaba un cambio a varios niveles:</p>
<ul>
<li class=""><strong>Técnico</strong>: Migramos una <code>MPA</code> a una <code>SPA</code> con Angular. Eso nos ayudó a ordenar mejor la arquitectura frontend, reutilizar componentes y construir una experiencia más fluida.</li>
<li class=""><strong>Experiencia de usuario</strong>: Los usuarios no rellenaban demasiados datos y cuando se inscribían a las ofertas de las empresas eran insuficientes para valorar candidatos.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="por-qué-la-gamificación-encajaba-en-este-caso">Por qué la gamificación encajaba en este caso<a href="https://antoniohumanes.com/blog/2026/05/17/application%20gamification#por-qu%C3%A9-la-gamificaci%C3%B3n-encajaba-en-este-caso" class="hash-link" aria-label="Enlace directo al Por qué la gamificación encajaba en este caso" title="Enlace directo al Por qué la gamificación encajaba en este caso" translate="no">​</a></h2>
<p>La gamificación funcionó bastante bien.</p>
<p>El problema real era que el usuario tenía que rellenar mucha información y no percibía claramente qué ganaba a cambio.</p>
<p>Con el nuevo modelo, cada bloque completado tenía una consecuencia visible: el usuario subía de nivel, veía progreso y desbloqueaba herramientas.</p>
<p>Por ejemplo, cuando el usuario añadía sus datos básicos, alcanzaba el nivel 1 y aparecía una animación de progreso, algo parecido a un cohete despegando.</p>
<p>Ya no eran los formularios complejos y interminables.</p>
<p>Lo importante era que el usuario entendía que estaba avanzando.</p>
<p>A medida que el usuario subía de nivel, también desbloqueaba herramientas útiles.</p>
<p>Una de ellas era un constructor de CV.</p>
<p>A partir de los datos que ya había completado, podía generar un CV en PDF usando diferentes plantillas. Ese CV podía descargarlo o compartirlo directamente desde la plataforma al inscribirse en una oferta.</p>
<p>Había una recompensa real.</p>
<p>Además utilizábamos diferentes componentes como:</p>
<ul>
<li class="">Un flujo guiado con pequeños formularios en cada paso.</li>
<li class="">Modales con animaciones de progreso.</li>
<li class="">Secciones diferenciadas con niveles, algunas desactivadas (las no desbloqueadas) y otras listas para utilizar.</li>
</ul>
<p>Fue uno de esos proyectos en los que disfrutas porque ves claramente cómo una decisión de producto cambia la forma en la que los usuarios interactúan con la aplicación.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="problemas-de-la-gamificación">Problemas de la gamificación<a href="https://antoniohumanes.com/blog/2026/05/17/application%20gamification#problemas-de-la-gamificaci%C3%B3n" class="hash-link" aria-label="Enlace directo al Problemas de la gamificación" title="Enlace directo al Problemas de la gamificación" translate="no">​</a></h2>
<p>No todo funcionó bien desde el principio.</p>
<p>Algunos usuarios no entendían el modelo de niveles. Otros no estaban acostumbrados a este tipo de experiencias o simplemente no querían “jugar” dentro de una aplicación de empleo.</p>
<p>Y esto es importante: lo que para un equipo de producto puede parecer claro, visual y motivador, para ciertos usuarios puede parecer confuso o innecesario.</p>
<p>También aparecieron problemas técnicos con algunos dispositivos y comportamientos que tuvimos que ajustar.</p>
<p>Tuvimos que iterar desde frontend y UX para que el modelo fuera más comprensible.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="dónde-sí-y-dónde-no">Dónde sí y dónde no<a href="https://antoniohumanes.com/blog/2026/05/17/application%20gamification#d%C3%B3nde-s%C3%AD-y-d%C3%B3nde-no" class="hash-link" aria-label="Enlace directo al Dónde sí y dónde no" title="Enlace directo al Dónde sí y dónde no" translate="no">​</a></h2>
<p>La gamificación puede ser un recurso muy potente, pero no funciona en cualquier contexto.</p>
<p>Funciona cuando ayuda al usuario a entender su progreso, reduce la sensación de esfuerzo y ofrece una recompensa clara.</p>
<p>En este proyecto tenía sentido porque estábamos transformando una tarea pesada, completar un perfil, en un proceso más guiado, visual y progresivo.</p>
<p>Pero si estoy en la aplicación de mi banco y voy a pedir una hipoteca o estoy construyendo mi cartera de inversión no quiero eso.</p>
<p>Necesito que me transmita seguridad, transparencia y fiabilidad.</p>
<p>En cambio, cuando la gamificación sí tiene sentido, debería aportar fluidez, sensación de progreso y un punto de diversión, sobre todo si la tarea original es aburrida.</p>
<p>Hay que tener criterio para decidir cuándo la gamificación puede ayudar y cuándo puede jugar en nuestra contra.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lo-que-aprendí">Lo que aprendí<a href="https://antoniohumanes.com/blog/2026/05/17/application%20gamification#lo-que-aprend%C3%AD" class="hash-link" aria-label="Enlace directo al Lo que aprendí" title="Enlace directo al Lo que aprendí" translate="no">​</a></h2>
<p>A nivel técnico fue un proyecto muy interesante porque nos obligó a cuidar varias cosas:</p>
<ul>
<li class=""><strong>Animaciones</strong>: tenían que aportar sensación de progreso.</li>
<li class=""><strong>Performance</strong>: cuantos más recursos visuales añades, más fácil es estropear la experiencia si no los controlas.</li>
<li class=""><strong>Formularios</strong>: dividir un formulario grande en pasos pequeños ayudaba a reducir la sensación de esfuerzo.</li>
<li class=""><strong>Arquitectura CSS</strong>: al tener una interfaz tan visual, necesitábamos una arquitectura de estilos más ordenada.</li>
<li class=""><strong>Sistema de diseño</strong>: los niveles, estados bloqueados, pasos, modales y recompensas necesitaban consistencia.</li>
<li class=""><strong>Arquitectura frontend</strong>: la migración a Angular nos permitió ordenar mejor la experiencia y construir componentes reutilizables.</li>
</ul>
<p>Pero el aprendizaje más importante para mí no fue técnico.</p>
<p>Fue entender que, como frontend, no solo implementamos pantallas.</p>
<p>También participamos en decisiones que afectan al comportamiento del usuario, al negocio y a la experiencia real del producto.</p>
<p>Porque el usuario no va a rellenar veinte campos sin nada a cambio.</p>
<p>Al menos no durante mucho tiempo.</p>]]></content>
        <author>
            <name>Antonio Humanes</name>
            <uri>https://www.linkedin.com/in/antoniohumanes/</uri>
        </author>
        <category label="Desarrollo" term="Desarrollo"/>
        <category label="UX" term="UX"/>
        <category label="Producto" term="Producto"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Integrar OpenAI en mi CI fue fácil. Conseguir reviews útiles no.]]></title>
        <id>https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai</id>
        <link href="https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai"/>
        <updated>2026-05-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Cómo integré OpenAI en mi CI para revisar PRs del blog y qué aprendí sobre prompts, ruido, automatización y criterio técnico.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Imagen de portada del artículo" src="https://antoniohumanes.com/assets/images/robot-reviewing-pr-dfccbf54de430b55ee1621cbff13f479.webp" width="700" height="467" class="img_ev3q"></p>
<p>Mientras escribía los primeros posts del blog me di cuenta de que repetía algunos errores antes de publicar.</p>
<p>No eran errores enormes, pero sí lo bastante molestos como para hacerme perder tiempo revisando PRs, imágenes, estructura y detalles de estilo.</p>
<p>Esto me ralentizaba mucho a la hora de crear, revisar y modificar.</p>
<p>Antes de entrar en la solución, pongo un poco de contexto técnico sobre el blog.</p>
<p>Para dar más contexto cada post está escrito en archivos <code>md</code> que mediante el framework son convertidos a <code>html</code>, así que cualquier cambio pasa por el flujo normal de trabajo: commit, push y Pull Request.</p>
<p>El problema aquí es que muchas veces subía un post con algunos errores:</p>
<ul>
<li class="">Añadía el thumbnail pero no la imagen de cabecera.</li>
<li class="">Algunas secciones eran demasiado largas.</li>
<li class="">Algunas explicaciones técnicas no eran lo suficientemente profundas.</li>
<li class="">En algún caso obviaba información que yo sí tenía pero el lector no.</li>
<li class="">Alguna falta de ortografía o de puntuación.</li>
</ul>
<p>Entonces pensé en una solución que pudiera ayudarme a revisar antes de mergear la PR: añadir IA a mi flujo de CI.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="el-flujo-de-ci-con-ia-que-había-implementado-al-principio">El flujo de CI con IA que había implementado al principio<a href="https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai#el-flujo-de-ci-con-ia-que-hab%C3%ADa-implementado-al-principio" class="hash-link" aria-label="Enlace directo al El flujo de CI con IA que había implementado al principio" title="Enlace directo al El flujo de CI con IA que había implementado al principio" translate="no">​</a></h2>
<p>La implementación es bastante sencilla, pero ahí no está el valor.</p>
<p>El valor está en comprobar si esta automatización ayuda de verdad o si solo añade ruido al flujo de desarrollo.</p>
<p>Estoy utilizando <code>GitHub Actions</code> y la <code>API</code> de OpenAI.</p>
<p>El flujo en resumidas cuentas consiste en lo siguiente:</p>
<ol>
<li class="">Detecta qué archivos han cambiado en la Pull Request.</li>
<li class="">Genera un diff reducido para no enviar información innecesaria a la IA.</li>
<li class="">Decide si merece la pena ejecutar la review o si puede saltarla.</li>
<li class="">OpenAI analiza posibles problemas y genera o actualiza un comentario en la PR.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="problemas-que-fui-encontrando">Problemas que fui encontrando<a href="https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai#problemas-que-fui-encontrando" class="hash-link" aria-label="Enlace directo al Problemas que fui encontrando" title="Enlace directo al Problemas que fui encontrando" translate="no">​</a></h2>
<p>Aunque era útil para mi caso, también empezó a generar bastante ruido:</p>
<ul>
<li class="">Sugerencias discutibles.</li>
<li class="">Comentarios redundantes.</li>
<li class="">Cambios de estilo que no quería.</li>
<li class="">Críticas técnicamente correctas pero irrelevantes.</li>
</ul>
<p>A veces veía problemas donde no los había y otras veces no detectaba partes que sí eran importantes.</p>
<p>Uno de los problemas más curiosos fue el tono.</p>
<p>La IA empezaba a sugerir cambios técnicamente correctos pero que hacían que el texto sonara menos mío.</p>
<p>Eso me obligó a ajustar bastante el prompt.</p>
<p>Quería una revisión que mantuviera mi estilo y eso a día de hoy sigue siendo muy complicado.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-le-pedí-exactamente-a-la-ia">Qué le pedí exactamente a la IA<a href="https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai#qu%C3%A9-le-ped%C3%AD-exactamente-a-la-ia" class="hash-link" aria-label="Enlace directo al Qué le pedí exactamente a la IA" title="Enlace directo al Qué le pedí exactamente a la IA" translate="no">​</a></h2>
<p>Una de las partes más importantes fue dejar claro qué quería revisar.</p>
<p>Aquí es donde me di cuenta de que tenía que separar dos comprobaciones y que hasta ahora lo estaba haciendo mal.</p>
<p>Antes estaba delegando toda la revisión a la IA.</p>
<p>Ahí me di cuenta de que estaba mezclando dos problemas distintos: validaciones objetivas y revisiones subjetivas.</p>
<p>Por un lado, las cosas objetivas:</p>
<ul>
<li class="">Si el post tenía imagen de portada.</li>
<li class="">Si el post tenía tags.</li>
<li class="">Si el post tenía descripción.</li>
</ul>
<p>Por otro lado, las cosas más subjetivas y que merecían debate en la PR:</p>
<ul>
<li class="">Si el título y la descripción tenían sentido.</li>
<li class="">Si alguna sección era demasiado larga.</li>
<li class="">Si estaba dando por hecho contexto que el lector no tenía.</li>
<li class="">Si el tono sonaba demasiado artificial.</li>
<li class="">Si la implementación técnica tenía sentido (en el caso de algún post más técnico).</li>
</ul>
<p>Esto cambió bastante el resultado.</p>
<p>Delegué a <code>node</code> las comprobaciones más objetivas y el resto a la IA.</p>
<p>La IA trabaja mejor cuando le damos contexto, límites claros y ejemplos concretos de lo que esperamos.</p>
<p>Cuando el prompt era demasiado abierto, la IA opinaba demasiado.
Cuando definí mejor tanto el flujo como el prompt, empezó a ser más útil.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cómo-quedó-el-nuevo-flujo">Cómo quedó el nuevo flujo<a href="https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai#c%C3%B3mo-qued%C3%B3-el-nuevo-flujo" class="hash-link" aria-label="Enlace directo al Cómo quedó el nuevo flujo" title="Enlace directo al Cómo quedó el nuevo flujo" translate="no">​</a></h2>
<div class="mermaid mermaid--loading" aria-busy="true">Cargando diagrama...</div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ejemplos-reales">Ejemplos reales<a href="https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai#ejemplos-reales" class="hash-link" aria-label="Enlace directo al Ejemplos reales" title="Enlace directo al Ejemplos reales" translate="no">​</a></h2>
<p>Para dar algunos ejemplos de cómo está funcionando actualmente el flujo tenemos tres tipos de casos.</p>
<p>A veces hace recomendaciones que no aportan demasiado, pero tampoco me molestan especialmente.</p>
<p><img decoding="async" loading="lazy" alt="Imagen de portada del artículo" src="https://antoniohumanes.com/assets/images/low-issue-found-by-ai-664af668b7151b228920f6b816f913fc.webp" width="700" height="474" class="img_ev3q"></p>
<p>Otras veces sí que aporta valor.</p>
<p>Por ejemplo, inicialmente había eliminado el diagrama del flujo porque me parecía innecesario. Después de esta sugerencia decidí volver a incluirlo porque probablemente ayuda a entender mejor cómo funciona la automatización.</p>
<p><img decoding="async" loading="lazy" alt="Imagen de portada del artículo" src="https://antoniohumanes.com/assets/images/medium-issue-found-by-ai-7f7081b1b1d96865058d546e1278b53b.webp" width="700" height="504" class="img_ev3q"></p>
<p>Y otras veces simplemente no encuentra nada.</p>
<p><img decoding="async" loading="lazy" alt="Imagen de portada del artículo" src="https://antoniohumanes.com/assets/images/empty-issue-found-by-ai-608fc6f87eaba407078fd9a90f85c301.webp" width="700" height="319" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusiones">Conclusiones<a href="https://antoniohumanes.com/blog/2026/05/14/review-pr-script-with-ai#conclusiones" class="hash-link" aria-label="Enlace directo al Conclusiones" title="Enlace directo al Conclusiones" translate="no">​</a></h2>
<p>Tengo algunas conclusiones claras:</p>
<ul>
<li class="">Meter IA en un workflow no significa automáticamente mejorar el workflow.</li>
<li class="">No todo necesita IA: Quizás algunas comprobaciones deban ser reglas simples y no una llamada a la IA.</li>
<li class="">Hay que vigilar si la automatización está generando valor o solo ruido.</li>
</ul>
<p>Por ejemplo, validar que un post tiene imagen de portada o que el frontmatter está completo probablemente no necesita OpenAI.</p>
<p>Eso puede resolverse con un script.</p>
<p>En cambio, revisar si una explicación se entiende, si el tono suena artificial o si estoy dando por hecho contexto que el lector no tiene, encaja mejor con una revisión asistida por IA.</p>
<p>Lo difícil es tener criterio suficiente para decidir:</p>
<ul>
<li class="">Qué automatizar.</li>
<li class="">Qué ignorar.</li>
<li class="">Y qué decisiones deberían seguir siendo humanas.</li>
</ul>
<p>Automatizar una review es relativamente fácil. Saber qué merece una regla fija, qué merece una opinión y qué decisiones deben seguir siendo humanas es bastante más complicado.</p>]]></content>
        <author>
            <name>Antonio Humanes</name>
            <uri>https://www.linkedin.com/in/antoniohumanes/</uri>
        </author>
        <category label="Desarrollo" term="Desarrollo"/>
        <category label="Técnico" term="Técnico"/>
        <category label="Features" term="Features"/>
        <category label="AI" term="AI"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Quería sacar 100 en PageSpeed… y casi no publico el blog]]></title>
        <id>https://antoniohumanes.com/blog/2026/05/03/optimize-to-much-stopped-me</id>
        <link href="https://antoniohumanes.com/blog/2026/05/03/optimize-to-much-stopped-me"/>
        <updated>2026-05-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Obsesionarme con PageSpeed me frenó más de lo que me ayudó. Lo que aprendí sobre optimización, producto y entregar valor.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Imagen de un semáforo que tiene varias posibilidades" src="https://antoniohumanes.com/assets/images/optimization-cb479e47dc2d8852680958078bef7453.webp" width="700" height="394" class="img_ev3q"></p>
<p>En el proceso de creación de este blog me he encontrado con varios retos, uno de ellos ha sido la performance.</p>
<p>Cada vez que pasaba la web por PageSpeed, quería ver ese 100.</p>
<p>Y si no estaba… seguía tocando cosas, revisando el informe e intentando encontrar la solución.</p>
<p>Me di cuenta de que estaba perdiendo demasiado tiempo o mejor dicho: no invirtiéndolo donde tocaba.</p>
<p>Estoy usando <code>Docusaurus</code>, que de base está bastante optimizado.</p>
<p>Pero en cuanto empecé a personalizar:</p>
<ul>
<li class="">fuentes</li>
<li class="">estilos</li>
<li class="">imágenes</li>
</ul>
<p>la puntuación empezó a bajar y ahí empezaron los problemas.</p>
<p>Esto me frenó bastante a la hora de tener el blog listo y empezar a publicar.</p>
<p>Ahí es cuando me di cuenta de que optimizar está bien pero si te empieza a frenar te puedes quedar estancado.</p>
<p>Para mí personalmente creo que fue la excusa perfecta para no publicar, para no exponerme al mundo real y quedarme en el lado cómodo para mí (el técnico).</p>
<p>Publicar implica:</p>
<ul>
<li class="">Que te lean.</li>
<li class="">Que te juzguen.</li>
<li class="">Que no guste.</li>
</ul>
<p>Estaba enfocado en:</p>
<ul>
<li class="">Optimizar imágenes.</li>
<li class="">Revisar CSS.</li>
<li class="">Peleándome con fuentes.</li>
<li class="">Buscando cómo subir unos puntos más en PageSpeed.</li>
</ul>
<p>Todo con ayuda de IA, iterando rápido… pero sin avanzar en lo importante.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="entonces-optimizar-no-importa">Entonces, ¿optimizar no importa?<a href="https://antoniohumanes.com/blog/2026/05/03/optimize-to-much-stopped-me#entonces-optimizar-no-importa" class="hash-link" aria-label="Enlace directo al Entonces, ¿optimizar no importa?" title="Enlace directo al Entonces, ¿optimizar no importa?" translate="no">​</a></h2>
<p>No digo que optimizar no sea necesario, es más, ahora hay que estar más atentos que nunca porque hay muchos casos que debemos tener en cuenta:</p>
<ul>
<li class="">Usuarios con mala conexión (metro, zonas rurales).</li>
<li class="">Accesibilidad.</li>
<li class="">SEO.</li>
</ul>
<p>Pero no todo tiene el mismo impacto.
No todo merece el mismo nivel de esfuerzo.</p>
<p>Por eso también estoy mirando otras señales, no solo PageSpeed.</p>
<p>Herramientas como <code>GTmetrix</code> o <code>Vercel Speed Insights</code> me dan otra visión del estado del blog, pero también estoy revisando métricas reales en <code>Umami</code>, como el <code>bounce rate</code> o la <code>visit duration</code>.</p>
<p>Si los usuarios pueden entrar, leer y navegar sin complicaciones, quizá el problema no es tan grave como parece en una puntuación aislada.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="la-regla-que-me-llevo">La regla que me llevo<a href="https://antoniohumanes.com/blog/2026/05/03/optimize-to-much-stopped-me#la-regla-que-me-llevo" class="hash-link" aria-label="Enlace directo al La regla que me llevo" title="Enlace directo al La regla que me llevo" translate="no">​</a></h2>
<p>Si te enfrascas en cosas con poco impacto y alto coste, deja de compensar.</p>
<p>Dicho esto, prometo optimizar la web lo máximo posible con el tiempo.</p>
<p>Si he aprendido algo es que tengo claro que hay que optimizar con criterio:</p>
<ol>
<li class="">Quiero optimizar para mis usuarios, no por sacar un 100 en PageSpeed.</li>
<li class="">Optimizar demasiado no puede frenar aportar valor (en mi caso es lo que quiero conseguir publicando).</li>
</ol>
<p>Esto también es aplicable al código.</p>
<p>Si optimizar microsegundos:</p>
<ul>
<li class="">Retrasa una feature.</li>
<li class="">O bloquea una entrega.</li>
</ul>
<p>Probablemente no es buena idea.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusión">Conclusión<a href="https://antoniohumanes.com/blog/2026/05/03/optimize-to-much-stopped-me#conclusi%C3%B3n" class="hash-link" aria-label="Enlace directo al Conclusión" title="Enlace directo al Conclusión" translate="no">​</a></h2>
<p>Primero se debe aportar, luego se debe optimizar.</p>
<p>Porque sin aportar:</p>
<ul>
<li class="">No hay usuarios.</li>
<li class="">No hay feedback.</li>
<li class="">No hay producto.</li>
</ul>
<blockquote>
<p>Me di cuenta de que no estaba construyendo.
Estaba procrastinando técnicamente.</p>
</blockquote>]]></content>
        <author>
            <name>Antonio Humanes</name>
            <uri>https://www.linkedin.com/in/antoniohumanes/</uri>
        </author>
        <category label="Desarrollo" term="Desarrollo"/>
        <category label="Optimización" term="Optimización"/>
        <category label="Producto" term="Producto"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Cómo evitamos duplicar un buscador cuando llegaron nuevos tipos de búsqueda]]></title>
        <id>https://antoniohumanes.com/blog/2026/04/24/different-searches-with-strategy</id>
        <link href="https://antoniohumanes.com/blog/2026/04/24/different-searches-with-strategy"/>
        <updated>2026-04-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Cómo resolvimos un problema real de escalabilidad además de mantener la consistencia entre distintos tipos de búsqueda sin duplicar lógica, componentes ni experiencia de usuario.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Imagen de unas piezas de lego unidas para crear una estructura" src="https://antoniohumanes.com/assets/images/strategy-335bc253adf8e8ff418866f7275fdcf8.webp" width="700" height="467" class="img_ev3q"></p>
<p>Este post va a ser un poco más técnico porque me gustaría hablar de un problema real que nos encontramos en un proyecto en el que trabajé.</p>
<p>La aplicación trataba de dar a usuarios técnicos y comerciales datos para tomar decisiones estratégicas y de negocio.</p>
<p>Al manejar muchos datos un buscador era casi imprescindible, teníamos un buscador bastante avanzado que no solo buscaba por el término introducido, sino también por términos relacionados y otros campos asociados.</p>
<p>Había un equipo que estaba preparando un LLM para la compañía y desde negocio vieron una buena oportunidad para integrarlo en nuestra aplicación, más concretamente desde el buscador.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-dudas-se-nos-vinieron-a-la-mente">Qué dudas se nos vinieron a la mente<a href="https://antoniohumanes.com/blog/2026/04/24/different-searches-with-strategy#qu%C3%A9-dudas-se-nos-vinieron-a-la-mente" class="hash-link" aria-label="Enlace directo al Qué dudas se nos vinieron a la mente" title="Enlace directo al Qué dudas se nos vinieron a la mente" translate="no">​</a></h2>
<p>Para que tengáis una visión clara, el buscador avanzado era simplemente un input con un icono de lupa, al añadir este nuevo comportamiento desde diseño se opto por añadir un switch para que el usuario pudiera elegir que tipo de búsqueda quería, "avanzado" o con "IA".</p>
<p>Algo más o menos así:</p>
<p><img decoding="async" loading="lazy" alt="Una imagen de un buscador con búsqueda normal o con IA" src="data:image/webp;base64,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" width="700" height="318" class="img_ev3q"></p>
<p>Empezamos a pensar como podríamos integrarlo en nuestro buscador, pero empezamos a ver problemas porque tanto la búsqueda y como se mostraban los resultados eran totalmente diferentes.</p>
<p>A simple vista empezamos a notar que añadir la lógica entre medias de la implementación actual no iba a ser una buena idea.</p>
<p>La alternativa habría sido añadir condicionales dentro del buscador existente, mezclando comportamientos distintos en una misma pieza y aumentando el acoplamiento.</p>
<p>Si queríamos agregar todo este nuevo comportamiento a nuestro buscador debería cumplir con los siguientes requerimientos:</p>
<ol>
<li class="">No afectar al comportamiento actual del buscador avanzado.</li>
<li class="">Que no estuviera acoplado al buscador avanzado y tuviera independencia de él.</li>
<li class="">Que las dos soluciones fueran mantenibles fácilmente.</li>
</ol>
<p>Entonces vimos que la solución pasaba por separar comportamientos. Con el tiempo entendí que esta aproximación encaja bastante bien con lo que se conoce como el patrón <code>strategy</code>.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span><em>es una versión del patrón Strategy adaptada a React.</em></div><div class="admonitionContent_BuS1"><p>No aplicamos Strategy de forma académica, con clases e interfaces como en muchos ejemplos clásicos. Lo llevamos a una versión más natural en React: una base común y varias implementaciones intercambiables para cada tipo de búsqueda.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementación-de-la-solución">Implementación de la solución<a href="https://antoniohumanes.com/blog/2026/04/24/different-searches-with-strategy#implementaci%C3%B3n-de-la-soluci%C3%B3n" class="hash-link" aria-label="Enlace directo al Implementación de la solución" title="Enlace directo al Implementación de la solución" translate="no">​</a></h2>
<p>La idea central es tener varios comportamientos intercambiables detrás de una misma interfaz, y que el contexto (la selección del usuario) elija cuál usar sin meter condicionales por todo el flujo.</p>
<p>Vamos a hacer un pequeño esquema para entender mejor como planteamos la implementación:</p>
<div class="mermaid mermaid--loading" aria-busy="true">Cargando diagrama...</div>
<p>En nuestro caso, la idea era mantener una base común para el buscador, maquetación, eventos y delegar las diferencias de comportamiento en estrategias separadas: búsqueda avanzada, búsqueda con IA, etc.</p>
<p>Tenemos los diferentes tipos de búsqueda con un <code>enum</code> y un <code>type</code>.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> SearchModes </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">ADVANCED</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ADVANCED"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">AI</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"AI"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">SearchMode</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">typeof</span><span class="token plain"> SearchModes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">keyof</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">typeof</span><span class="token plain"> SearchModes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Además un <code>type</code> para los componentes con diferentes estrategias.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">SearcherProps</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  term</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">SearchStrategy</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  Searcher</span><span class="token operator">:</span><span class="token plain"> ComponentType</span><span class="token operator">&lt;</span><span class="token plain">SearcherProps</span><span class="token operator">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Las diferentes estrategias:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> strategies</span><span class="token operator">:</span><span class="token plain"> Record</span><span class="token operator">&lt;</span><span class="token plain">SearchMode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> SearchStrategy</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">SearchModes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">ADVANCED</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Searcher</span><span class="token operator">:</span><span class="token plain"> AdvancedSearch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">SearchModes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">AI</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Searcher</span><span class="token operator">:</span><span class="token plain"> AISearch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Con esto ya tendríamos la base para ir montando componentes.</p>
<p>Los componentes que vamos a utilizar son los siguientes:</p>
<ol>
<li class=""><code>InputSearch</code>: Es el componente principal el cual está formado por un <code>input</code> y un <code>switch</code> en este caso para elegir la estrategia.</li>
<li class=""><code>AdvancedSearch</code>: que tiene la funcionalidad del buscador avanzado (llamada a una API, transformación de la data, manejar como se muestran los datos, etc.)</li>
<li class=""><code>AISearch</code>: que tiene la funcionalidad del buscador con IA (llamada a una API, transformación de la data, manejar como se muestran los datos, etc.)</li>
</ol>
<p>en <code>InputSearch</code> el usuario decide el tipo de búsqueda, y en base a esa decisión el componente selecciona la estrategia adecuada.</p>
<p>De esta forma evitamos duplicar UI y mantenemos la responsabilidad clara: el componente principal orquesta las estrategias que definen el comportamiento.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:rgb(80, 250, 123)">InputSearch</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)">// Añadimos el modo, por defecto será el modo de búsqueda avanzada.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">mode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> setMode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:rgb(80, 250, 123)">useState</span><span class="token generic-function generic class-name operator">&lt;</span><span class="token generic-function generic class-name">SearchMode</span><span class="token generic-function generic class-name operator">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">SearchModes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">ADVANCED</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)">// El término es un string vacío hasta que el usuario añada algún valor.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">term</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> setTerm</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">useState</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)">// Se obtiene el modo de búsqueda que ha elegido el usuario.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> strategy </span><span class="token operator">=</span><span class="token plain"> strategies</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">mode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)">// El componente Searcher con el comportamiento que queremos.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> Searcher </span><span class="token operator">=</span><span class="token plain"> strategy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">Searcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token operator">&lt;</span><span class="token plain">section</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token plain">div</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token operator">&lt;</span><span class="token plain">Input</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          placeholder</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"Buscar"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          value</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">term</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          onChange</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">event</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">setTerm</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">event</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">target</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token comment" style="color:rgb(98, 114, 164)">// En un caso real, el switch estaría separado. Aquí lo simplifico para centrarme en la idea.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          onSwitch</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">setMode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token operator">/</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token comment" style="color:rgb(98, 114, 164)">/* Resultados de búsqueda */</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token plain">div</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">       </span><span class="token operator">&lt;</span><span class="token plain">Searcher term</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">term</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token operator">/</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">div</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">section</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Componentes de búsqueda con IA y avanzado, los he simplificado al máximo para no añadir información innecesaria.</p>
<p>Pero la idea es que cada componente realice una llamada a un servicio, transforme datos, los muestre de una determinada manera y así su comportamiento queda encapsulado.</p>
<p>Llevado al caso real podrían mostrar diferentes tipos de resultados, podría ser que el buscador avanzado devolviera una lista de elementos y el buscador con IA devolviera otro tipo de estructura.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:rgb(80, 250, 123)">AdvancedSearch</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> term </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token operator">:</span><span class="token plain"> SearcherProps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Aquí podríamos llamar a un servicio, transformar datos, montar estructura de la respuesta en base a como muestra los resultados el buscador avanzado.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token operator">&lt;</span><span class="token plain">article className</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">styles</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token plain">h3</span><span class="token operator">&gt;</span><span class="token constant" style="color:rgb(189, 147, 249)">B</span><span class="token plain">úsqueda avanzada</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">h3</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token plain">p</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Resultados filtrados mediante el buscador avanzado para el término</span><span class="token operator">:</span><span class="token plain"> </span><span class="token operator">&lt;</span><span class="token plain">strong</span><span class="token operator">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">term</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">strong</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">p</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">article</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:rgb(80, 250, 123)">AISearch</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> term </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token operator">:</span><span class="token plain"> SearcherProps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Aquí podríamos llamar a un servicio, transformar datos, montar estructura de la respuesta en base a como muestra los resultados el buscador con IA.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token operator">&lt;</span><span class="token plain">article className</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">styles</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token plain">h3</span><span class="token operator">&gt;</span><span class="token constant" style="color:rgb(189, 147, 249)">B</span><span class="token plain">úsqueda con </span><span class="token constant" style="color:rgb(189, 147, 249)">IA</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">h3</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token plain">p</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">         Resultados filtrados mediante el buscador con </span><span class="token constant" style="color:rgb(189, 147, 249)">IA</span><span class="token plain"> para el término</span><span class="token operator">:</span><span class="token plain"> </span><span class="token operator">&lt;</span><span class="token plain">strong</span><span class="token operator">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">term</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">strong</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">p</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token plain">article</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusión">Conclusión<a href="https://antoniohumanes.com/blog/2026/04/24/different-searches-with-strategy#conclusi%C3%B3n" class="hash-link" aria-label="Enlace directo al Conclusión" title="Enlace directo al Conclusión" translate="no">​</a></h2>
<p>Cuando una nueva funcionalidad cambia mucho el comportamiento de una pieza existente, conviene parar antes de meter lógica “entre medias”. A veces la solución no es añadir más condicionales, sino separar responsabilidades.</p>
<p>En nuestro caso, añadir IA al buscador era un cambio de comportamiento. Mezclar ambas cosas en la misma implementación habría complicado el mantenimiento y aumentado el acoplamiento.</p>
<p>Separar los comportamientos nos permitió mantener una base común y establecer cada tipo de búsqueda de forma independiente.</p>
<p>Al final no se trata de aplicar patrones por aplicarlos, sino de llegar a soluciones que nos permitan:</p>
<ul>
<li class="">Mantener el código entendible cuando la aplicación crece.</li>
<li class="">Evitar mezclar responsabilidades distintas entre componentes o partes de nuestra aplicación.</li>
<li class="">Modificar o extender funcionalidades sin romper nada.</li>
</ul>]]></content>
        <author>
            <name>Antonio Humanes</name>
            <uri>https://www.linkedin.com/in/antoniohumanes/</uri>
        </author>
        <category label="Desarrollo" term="Desarrollo"/>
        <category label="Técnico" term="Técnico"/>
        <category label="Datos" term="Datos"/>
        <category label="Producto" term="Producto"/>
        <category label="Features" term="Features"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Cuando un Excel deja de ser suficiente]]></title>
        <id>https://antoniohumanes.com/blog/2025/11/18/starting-with-an-excel</id>
        <link href="https://antoniohumanes.com/blog/2025/11/18/starting-with-an-excel"/>
        <updated>2025-11-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Cómo muchas aplicaciones reales nacen de una hoja de cálculo y qué señales merece la pena observar para convertir necesidad en producto.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Imagen de una aplicación de finanzas abierta en el navegador" src="https://antoniohumanes.com/assets/images/excel-4cd49a41eee14aa9c6288fde9b275f46.webp" width="700" height="467" class="img_ev3q"></p>
<p>Hoy quiero hablar de algo que aprendes con la observación y de ciertos patrones que se dan en las empresas: <strong>la mayoría de aplicaciones nacen de un Excel</strong>.</p>
<p>Equipos de negocio, tecnología, ventas, diseño… da igual el área.<br>
<!-- -->Si necesitan guardar datos, modificarlos, analizarlos, calcularlos o simplemente extraerlos, tarde o temprano acaban creando un Excel.</p>
<p>El problema empieza cuando ese Excel <strong>funciona</strong>.</p>
<p>Empieza a crecer y, con él, empiezan los problemas. Algunos de ellos son:</p>
<ul>
<li class="">Lo tocan muchas personas y nadie tiene claro qué permisos debería tener cada uno.</li>
<li class="">Alguien borra algo que no debe o se pisan entre ellos.</li>
<li class="">Aparecen versiones duplicadas en distintos equipos para cosas que podrían ser la misma información o, al menos, compartirse.</li>
</ul>
<p>Por suerte, esto tiene solución.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="del-excel-a-la-aplicación">Del Excel a la aplicación<a href="https://antoniohumanes.com/blog/2025/11/18/starting-with-an-excel#del-excel-a-la-aplicaci%C3%B3n" class="hash-link" aria-label="Enlace directo al Del Excel a la aplicación" title="Enlace directo al Del Excel a la aplicación" translate="no">​</a></h2>
<p>En la mayoría de los casos, alguien se da cuenta de que ese Excel tiene demasiada responsabilidad, mucho desorden y otros problemas asociados.</p>
<p>Suele pasar que algún responsable pide a IT una aplicación para cubrir esa necesidad. Entonces nace un proyecto, los product managers empiezan a definir una hoja de ruta y, después, diseño y desarrollo se suman al proceso.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="en-realidad-cualquier-excel-es-candidato-a-ser-una-aplicación-incluidos-los-míos">En realidad, cualquier Excel es candidato a ser una aplicación (incluidos los míos)<a href="https://antoniohumanes.com/blog/2025/11/18/starting-with-an-excel#en-realidad-cualquier-excel-es-candidato-a-ser-una-aplicaci%C3%B3n-incluidos-los-m%C3%ADos" class="hash-link" aria-label="Enlace directo al En realidad, cualquier Excel es candidato a ser una aplicación (incluidos los míos)" title="Enlace directo al En realidad, cualquier Excel es candidato a ser una aplicación (incluidos los míos)" translate="no">​</a></h2>
<p>Como digo en el título de esta sección, cualquier Excel, hoja de cálculo o similar es candidato a ser transformado en una aplicación, siempre y cuando las necesidades lo justifiquen.</p>
<p>Te pongo dos ejemplos personales que utilizo a diario:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-mi-excel-de-cursos-libros-y-rutas-de-aprendizaje">1. Mi Excel de cursos, libros y rutas de aprendizaje<a href="https://antoniohumanes.com/blog/2025/11/18/starting-with-an-excel#1-mi-excel-de-cursos-libros-y-rutas-de-aprendizaje" class="hash-link" aria-label="Enlace directo al 1. Mi Excel de cursos, libros y rutas de aprendizaje" title="Enlace directo al 1. Mi Excel de cursos, libros y rutas de aprendizaje" translate="no">​</a></h3>
<p>Es el Excel que utilizo para seguir aprendiendo, registrando cursos, libros y artículos que tengo pendientes o que ya he realizado.</p>
<p>Lo tengo todo clasificado por temas, estado (Sin empezar / En curso / Finalizado) y con detalles de cada recurso.<br>
<!-- -->Es un Excel muy buen candidato y extrapolable a otras personas que quieran crear su ruta de aprendizaje u organizar sus cursos, documentos o artículos pendientes de leer.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-mi-excel-de-finanzas-personales">2. Mi Excel de finanzas personales<a href="https://antoniohumanes.com/blog/2025/11/18/starting-with-an-excel#2-mi-excel-de-finanzas-personales" class="hash-link" aria-label="Enlace directo al 2. Mi Excel de finanzas personales" title="Enlace directo al 2. Mi Excel de finanzas personales" translate="no">​</a></h3>
<p>El clásico.<br>
<!-- -->Una columna de “presupuesto”, otra de “gastado”, 12 columnas más para cada mes, filas con categorías y subcategorías: ocio → restaurantes, etc.</p>
<p>Me imagino que apps como Fintonic empezaron exactamente así: alguien con su Excel que decidió convertirlo en una aplicación con más funcionalidades.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-quiero-transmitir-con-todo-esto">¿Qué quiero transmitir con todo esto?<a href="https://antoniohumanes.com/blog/2025/11/18/starting-with-an-excel#qu%C3%A9-quiero-transmitir-con-todo-esto" class="hash-link" aria-label="Enlace directo al ¿Qué quiero transmitir con todo esto?" title="Enlace directo al ¿Qué quiero transmitir con todo esto?" translate="no">​</a></h2>
<p>Muy simple, y muy relacionado con estar alineados con negocio como desarrolladores:</p>
<p><strong>Empieza a observar las necesidades de los demás y a tener una mentalidad de producto.</strong></p>
<p>El Excel no es el problema. Es la señal.</p>
<p>Todas las personas, equipos y empresas tienen necesidades. Nuestro trabajo como desarrolladores no es solo crear algo que funcione, sino también acompañarles en el proceso de ideación, diseño de la solución e implementación.</p>
<p>Debemos ser capaces de acompañar al usuario desde lo que tiene en la cabeza hasta lo que realmente necesita. Y, para ello, es necesario pasar por todas las fases, incluso antes de escribir una sola línea de código.</p>
<p>Detectar la necesidad es más importante que construir la solución.</p>
<p>Además, esto no solo aplica a grandes empresas. Quizás la tienda de barrio de al lado de tu casa tenga también necesidades y, si en tu ciudad hay más tiendas así, es probable que compartan los mismos problemas.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>En conclusión:</div><div class="admonitionContent_BuS1"><p>Debemos desarrollar una mentalidad de producto para construir soluciones que no solo cumplan, sino que aporten valor real.</p></div></div>]]></content>
        <author>
            <name>Antonio Humanes</name>
            <uri>https://www.linkedin.com/in/antoniohumanes/</uri>
        </author>
        <category label="Datos" term="Datos"/>
        <category label="Producto" term="Producto"/>
        <category label="Negocio" term="Negocio"/>
        <category label="Desarrollo" term="Desarrollo"/>
        <category label="Features" term="Features"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[¿Sabes de dónde sale tu salario?]]></title>
        <id>https://antoniohumanes.com/blog/2025/11/09/business-in-the-development</id>
        <link href="https://antoniohumanes.com/blog/2025/11/09/business-in-the-development"/>
        <updated>2025-11-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Una reflexión sobre por qué entender el negocio cambia la calidad del software que construimos y el valor que aportamos.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Imagen de dinero en dólares sobre una mesa" src="https://antoniohumanes.com/assets/images/money-26f52dd2a79805b5e4920bb148838d96.webp" width="700" height="467" class="img_ev3q"></p>
<p>Cuando empecé en el desarrollo de software, me obsesioné con aprender lo máximo posible a nivel técnico. Con el tiempo entendí que eso importa, pero también estaba ignorando algo igual de clave: el negocio.</p>
<p>Empecé a darme cuenta de que el negocio es una parte esencial para cualquier developer y, en realidad, para cualquiera que trabaje en tecnología. Van de la mano.</p>
<p>Si no entiendes el negocio, es probable que te estés perdiendo algo muy importante a la hora de escribir código.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sabes-de-dónde-sale-tu-salario">¿Sabes de dónde sale tu salario?<a href="https://antoniohumanes.com/blog/2025/11/09/business-in-the-development#sabes-de-d%C3%B3nde-sale-tu-salario" class="hash-link" aria-label="Enlace directo al ¿Sabes de dónde sale tu salario?" title="Enlace directo al ¿Sabes de dónde sale tu salario?" translate="no">​</a></h2>
<p>Si no puedes responder con claridad, no pasa nada.<br>
<!-- -->Si estás empezando o trabajas en un proyecto donde la monetización es compleja, es normal.<br>
<!-- -->A mí también me pasó.</p>
<p>Te cuento una situación que me hizo darme cuenta de la importancia de entender de dónde sale el salario.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="un-proyecto-sin-entender-el-negocio">Un proyecto sin entender el negocio<a href="https://antoniohumanes.com/blog/2025/11/09/business-in-the-development#un-proyecto-sin-entender-el-negocio" class="hash-link" aria-label="Enlace directo al Un proyecto sin entender el negocio" title="Enlace directo al Un proyecto sin entender el negocio" translate="no">​</a></h2>
<p>Entré en una gran empresa española a través de una consultora y, el proyecto parecía interesante.</p>
<p>El cliente quería una aplicación web donde usuarios técnicos y comerciales pudieran añadir información operativa. Para mí, era algo extraño: una Wikipedia interna.</p>
<p>💸 ¿Esto iba a pagar el salario?</p>
<p>Sinceramente, no tenía clara la utilidad real de esa aplicación, incluso cuando empecé a picar código. No sabíamos qué problema real estábamos resolviendo.</p>
<p>El proyecto avanzó, añadimos funcionalidades y, cuando llegaron los stakeholders… quejas.<br>
<!-- -->No era lo que esperaban.</p>
<p>Hablamos con ellos, hicimos algunos ajustes y el proyecto empezó a encaminarse.</p>
<p>Aquí fue donde cambió todo.</p>
<p>Mi jefe me dijo después que solo esos cambios costaron más de lo que muchos cobran en un año.</p>
<p>No fue un problema de nadie en concreto, sino de no entender bien el contexto desde el principio.</p>
<p>Cuando todos entendimos el negocio, se cometieron menos errores, se redujeron funcionalidades innecesarias y empezamos a generar más valor.</p>
<p>Esto no es infalible, pero cambia mucho la calidad y la dirección de un proyecto.</p>
<p>En tecnología, todo suele ser un caos: cambios de prioridades, visión sesgada, MVPs, salidas rápidas con deuda técnica.<br>
<!-- -->Por eso es más importante que nunca conocer el negocio: te ahorra muchos errores y, sobre todo, mucho dinero.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cuál-era-realmente-el-problema">¿Cuál era realmente el problema?<a href="https://antoniohumanes.com/blog/2025/11/09/business-in-the-development#cu%C3%A1l-era-realmente-el-problema" class="hash-link" aria-label="Enlace directo al ¿Cuál era realmente el problema?" title="Enlace directo al ¿Cuál era realmente el problema?" translate="no">​</a></h2>
<p>El problema del cliente era simple, pero importante:<br>
<strong>la información operativa no estaba centralizada.</strong></p>
<p>Cada tienda tenía sus notas y sus propias fórmulas para cálculos.<br>
<!-- -->Esto generaba caos, errores y un montón de tiempo perdido.</p>
<p>Una llamada típica:</p>
<blockquote>
<p>— “Hola, ¿me puedes recordar cómo se calcula el importe de devolución en rebajas? He perdido la hoja.”</p>
<p>— “Sí, es el <code>(precio_final × unidades) – descuentos adicionales</code>”</p>
<p>— “Vale, gracias… espera, no me cuadra.”</p>
</blockquote>
<p>Este tipo de llamadas pasaban muchas veces.</p>
<p>Entre dos personas: media hora perdida.</p>
<p>Pongamos que ocurría 2 veces a la semana.<br>
<!-- -->52 semanas al año → más de 100 incidencias.<br>
<!-- -->Multiplica eso por 1.000 tiendas:</p>
<p>cientos de miles de horas perdidas.</p>
<p>La Wikipedia interna fue mano de santo.
El ahorro fue inmediato.
La mejora en eficiencia, brutal.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Lección aprendida:</div><div class="admonitionContent_BuS1"><ol>
<li class=""><strong>Entiende el negocio</strong> antes de tocar una línea de código.</li>
<li class=""><strong>Habla con tus usuarios</strong>, entiende sus necesidades.</li>
</ol></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusión">Conclusión<a href="https://antoniohumanes.com/blog/2025/11/09/business-in-the-development#conclusi%C3%B3n" class="hash-link" aria-label="Enlace directo al Conclusión" title="Enlace directo al Conclusión" translate="no">​</a></h2>
<p>Entender el negocio es entender por qué haces lo que haces.</p>
<p>Y cuando lo entiendes, tomas mejores decisiones, construyes mejores productos y generas más valor.</p>
<p>Porque, al final, si no entiendes el negocio, es muy difícil entender de dónde sale tu salario.</p>]]></content>
        <author>
            <name>Antonio Humanes</name>
            <uri>https://www.linkedin.com/in/antoniohumanes/</uri>
        </author>
        <category label="Producto" term="Producto"/>
        <category label="Negocio" term="Negocio"/>
        <category label="Desarrollo" term="Desarrollo"/>
        <category label="Features" term="Features"/>
    </entry>
</feed>