<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es"><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://codecantor.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://codecantor.com/" rel="alternate" type="text/html" hreflang="es" /><updated>2026-06-28T13:21:49+02:00</updated><id>https://codecantor.com/feed.xml</id><title type="html">Codecantor</title><subtitle>Laboratorio de software que diseña y construye soluciones.</subtitle><entry><title type="html">Sarebus: Un motor de búsqueda de rutas de autobús regional</title><link href="https://codecantor.com/blog/sarebus/" rel="alternate" type="text/html" title="Sarebus: Un motor de búsqueda de rutas de autobús regional" /><published>2026-06-21T00:00:00+02:00</published><updated>2026-06-21T00:00:00+02:00</updated><id>https://codecantor.com/blog/sarebus</id><content type="html" xml:base="https://codecantor.com/blog/sarebus/"><![CDATA[<p>Un día para viajar de Donosti a Vitoria no había personal en la estación de autobuses para vendernos billetes en taquilla, y la terminal de venta automática estaba rota (había gente en la cola hablando de comprarlo online pero no encontraban dónde).</p>

<p>Yo conocía la página web porque me sonaba que era “Pesa”, pero lo más natural era buscarlo en internet, y esto es lo que sucedió:</p>

<ul>
  <li><strong>Si buscabas en Google</strong> “autobuses Donosti Vitoria” o “Autobuses San Sebastián Vitoria” el resultado correcto era el séptimo, te salen resultados en PDF donde no puedes comprar el billete online y la web que los vende no lo parece.</li>
  <li>Usando <strong>Google Maps</strong> o comparadores de autobuses como Omnio te muestran ALSA o RENFE y no disponen más que de 2 o 3 autobuses al día (mientras que en Pesa había 1 cada hora o menos).</li>
  <li><strong>ChatGPT</strong> también te informaba de que había varios comparadores que lo vendían como Movalia y Omnio pero llegabas igual que Google Maps, con resultados como ALSA o RENFE y la existencia de otro autobús llamado 0B2332 que debes encontrar (y si buscabas eso llegabas de nuevo al PDF).</li>
</ul>

<p>De hecho la mayoría de la gente que conozco suele ir con tiempo a la estación (o incluso ir unos días antes a comprarlo a la terminal de venta o a taquilla) porque al no reservarlo online muchos de los autobuses se llenan y te tienes que esperar al siguiente que sale una hora más tarde.</p>

<figure class="post-figure">
  <img loading="lazy" src="/assets/images/posts/pesa-venta-online.jpg" alt="Pantalla de venta de billetes online de Pesa y Avanza en online.pesa.net, con campos de origen, destino y fecha" width="1024" height="691" />
  
    <figcaption class="post-caption">La web de PESA / Avanza / Lurraldebus para comprar billetes online</figcaption>
  
</figure>

<h2 id="definir-el-problema-el-primero-de-muchos">Definir el problema (el primero de muchos)</h2>

<p>Este problema se repite una y otra vez con rutas muy populares y usando otros proveedores distintos a Pesa o Lurraldebus, como para ir de Logroño a Bilbao o de Vitoria al Aeropuerto de Loiu (BIO), que es una ruta nueva de otra compañía que usan miles de personas.</p>

<p>Una vez analizado el problema: la idea es cruzar los datos de los distintos proveedores y ofrecer la mejor ruta indicándole la página web donde puede hacer la compra, ese al menos es el MVP<sup class="note-ref"><a href="#fn-1">1</a></sup> que diseñé para Enrique.</p>

<p>Así es como nació la primera versión de <a target="_blank" rel="noopener noreferrer" href="https://sarebus.com/">Sarebus</a>.</p>

<figure class="post-figure">
  <img loading="lazy" src="/assets/images/posts/sarebus-mockups.webp" alt="Dos mockups de iPhone con la app Sarebus: resultados de búsqueda de autobuses desde Vitoria-Gasteiz y detalle de ruta con paradas intermedias" width="1024" height="768" />
  
    <figcaption class="post-caption">MVP de sarebus</figcaption>
  
</figure>

<h2 id="gtfs-y-cómo-funcionan-los-datos">GTFS y cómo funcionan los datos</h2>

<p>Enrique recogió todos los datos GTFS del <a target="_blank" rel="noopener noreferrer" href="https://nap.transportes.gob.es/faqs">NAP</a> o <strong>Punto de Acceso Nacional de datos del transporte</strong> de España (National Access Point), que centraliza datos de transporte público bajo el Ministerio de Transporte (autobús, tren, metro, marítimo, aéreo, etc.), es la única fuente centralizada, accesible y estándar que comparten todas las compañías de transporte público.</p>

<p><strong>Los GTFS (General Transit Feed Specification)</strong> son un estándar abierto para describir servicios de transporte público con datos interesantes como las paradas, rutas, horarios etc. Fue impulsado originalmente por Google y hoy es el formato más utilizado para intercambiar datos de transporte entre operadores y aplicaciones.</p>

<p>La información ya la teníamos, y la prueba de concepto resultó que funcionaba, pero con otros problemas que ya sabíamos que íbamos a tener:</p>

<ol>
  <li>Los nombres son distintos y se duplican, mientras que una compañía llama a la estación de Autobuses de Bilbao “Bilbo Bilbao Intermodal” otra la llama “Estación de autobuses Bilbao Intermodal”.</li>
  <li>Hay paradas que están juntas pero que están geolocalizadas en latitudes y longitudes distintas, dos paradas (el bus de ida y de vuelta que pasa por el mismo sitio) pero que localizan como si fueran dos paradas distintas.</li>
  <li>Los datos no están agrupados ni por provincia, ni tampoco en sus propios municipios y ciudades, no podía seleccionar “Bilbao” (todas las paradas), sino “Bilbao Bolueta” o “Bilbao Intermodal” (adjunto imagen justo debajo).</li>
  <li>Los nombres de los lugares estaban en mayúsculas, minúsculas, en euskera/castellano o directamente la parada la llaman distinta, no siguen ninguna nomenclatura específica, mientras que un proveedor usa todo en mayúsculas “BILBO BILBAO (DEUSTO)” otro lo llama “Deusto (Bilbo / Bilbao)”, y si buscas paradas aleatorias mientras que uno usa la calle completa “Apalategi Kalea 3 Parean, Apalategi” otro de los proveedores usa “Apalategi Eroski” o “Apalategi Gasolinera” que entendemos que es algo más distintivo del lugar.</li>
</ol>

<figure class="post-figure">
  <img loading="lazy" src="/assets/images/posts/launion-autobues-bilbao.jpg" alt="Formulario de compra de billetes de La Unión Autobuses con origen Bilbao y destinos en Bizkaia" width="1024" height="691" />
  
    <figcaption class="post-caption">La web de LA UNION AUTOBUSES es la web para viajar por Bizkaia</figcaption>
  
</figure>

<h2 id="trabajando-con-los-datos">Trabajando con los datos</h2>

<p>Creamos un panel de administración que nos sirviera para trabajar los datos, y Enrique preparó el sistema de tal forma que permitiese no solo buscar las paradas y renombrarlas, sino agruparlas (forman parte de un mismo grupo), unificarlas (son la misma parada), geolocalizarlas visualmente en un mapa para deducir si merece la pena agruparlas o unificarlas, e incluir más información adicional que la que ya teníamos en la base de datos.</p>

<p>Entre otros muchos cambios ahora estamos inmersos en tres muy concretos:</p>

<ol>
  <li>Geolocalizamos las paradas mediante coordenadas cercanas y las unificamos, aunque haya una parada claramente de “estación de autobuses”, los datos nos devuelven 5 o 6 paradas distintas, cuando la parada es la misma: estación de autobuses, la herramienta nos permite conectar unas paradas con otras para que entienda que son la misma parada. Gracias a un mapa que hemos generado identificamos rápidamente dónde están.</li>
  <li>Agrupamos en lo que llamamos <em>City Hubs</em> grandes ciudades y los municipios más importantes, por ejemplo, hay pueblos donde localizamos 6 o 7 paradas distintas, una en la gasolinera, otra en la plaza del pueblo y otra en la estación de autobuses (multiplicado por las 6 o 7 paradas duplicadas) creamos una herramienta que nos permite unificarlas y renombrarlas de forma fácil para que tengan una nomenclatura legible, para poder ir a Eibar seleccionando “Todas las paradas” o si lo deseas localizar la más específica (Gasolinera, Estación, etc.).</li>
  <li>Hicimos que algunos de estos puntos fueran definibles, aunque llamemos a la parada correctamente “Bilbo / Bilbao (Intermodal)”, debajo en pequeño declaramos que esta parada es una Estación de autobuses (<em>autobus geltokia</em> o <em>bus station</em>). Hacemos eso también con los aeropuertos, universidades etc.</li>
</ol>

<h2 id="próximos-pasos">Próximos pasos</h2>

<p>Todavía hay muchos frentes que resolver, como la recomendación de rutas con transbordo, o si recomendarle una alternativa (ir andando, en metro, etc.) para cuando seleccione rutas que se queden cortas y no alcancen el destino deseado.</p>

<p>Hay mejoras a muchos niveles sin siquiera meternos en el asunto de la venta de billetes, que no es nuestro principal objetivo, todavía estamos creando una serie de contenidos para posicionar cientos de rutas en Google alimentándola con nuestro propio contenido, queremos que más usuarios lleguen a nuestra web localizando dónde y cómo pueden ir desde el punto A al punto B.</p>

<p>Ya tenemos una base muy importante, si quieres entrar en más detalles sobre los avances del proyecto y las soluciones también <a target="_blank" rel="noopener noreferrer" href="https://sarebus.com/blog">hemos abierto un blog en sarebus</a> donde vamos contando novedades del proyecto.</p>

<div class="post-notes">

  <h2 id="notas">Notas</h2>

  <ol>
  <li id="fn-1"><strong>MVP (Minimum Viable Product).</strong> Producto mínimo viable: la versión más reducida de un producto que sirve para validar una idea con usuarios reales, medir si resuelve el problema y aprender qué merece la pena construir después.</li>
</ol>

</div>]]></content><author><name>Jano M. Gorgal</name><email>alex@codecantor.com</email></author><summary type="html"><![CDATA[Cómo convertimos cientos de miles de datos GTFS en un buscador]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://codecantor.com/assets/images/og/default.jpg" /><media:content medium="image" url="https://codecantor.com/assets/images/og/default.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Sobre la IA y el futuro de la industria del desarrollo de software</title><link href="https://codecantor.com/blog/sobre-la-ia-y-el-futuro-del-desarrollo/" rel="alternate" type="text/html" title="Sobre la IA y el futuro de la industria del desarrollo de software" /><published>2026-06-20T00:00:00+02:00</published><updated>2026-06-20T00:00:00+02:00</updated><id>https://codecantor.com/blog/sobre-la-ia-y-el-futuro-del-desarrollo</id><content type="html" xml:base="https://codecantor.com/blog/sobre-la-ia-y-el-futuro-del-desarrollo/"><![CDATA[<p><em>Próximamente el post..</em></p>]]></content><author><name>Enrique &amp; Jano</name><email>hola@codecantor.com</email></author><summary type="html"><![CDATA[Manifiesto de codecantor sobre el futuro del software]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://codecantor.com/assets/images/og/default.jpg" /><media:content medium="image" url="https://codecantor.com/assets/images/og/default.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Cómo hacer un blog con IA</title><link href="https://codecantor.com/blog/como-hacer-blog-ia/" rel="alternate" type="text/html" title="Cómo hacer un blog con IA" /><published>2026-06-19T00:00:00+02:00</published><updated>2026-06-19T00:00:00+02:00</updated><id>https://codecantor.com/blog/construyendo-un-blog-con-ia</id><content type="html" xml:base="https://codecantor.com/blog/como-hacer-blog-ia/"><![CDATA[<p>La IA aunque lleva años dando que hablar, todavía hoy se siente novedosa, aunque es accesible para todo el que disponga de conexión a internet y cualquiera puede usar la IA generativa para crear un cartel o hacer un resumen, para diseñar ilustraciones, reconstruír imágenes, editar vídeos o escribir código con cierta complejidad, sigue siendo bastante impredecible.</p>

<p>Según el modelo de IA que utilices y qué instrucciones le indicas, la IA puede devolver unos resultados u otros, según las entradas de información que haya obtenido o que tú mismo le hayas proporcionado. Es decir, no son deterministas, si tu le das la misma instrucción dos veces a un modelo de IA u otro, puede devolverte resultados diferentes (a veces incluso al mismo modelo si éste ha sido alimentado por ti con otro contexto y necesidades diferentes).</p>

<p><a target="_blank" rel="noopener noreferrer" href="https://curiosity.ventures/posts/how-to-build-a-blog-with-ai">Hace poco leía</a> que podría resultar interesante que, a medida que la IA se vuelve omnipresente, recordemos cómo construimos los primeros proyectos como si se tratasen de las notas de un laboratorio.</p>

<p>Al final del artículo dejo apuntadas aclaraciones por si algún término te parece marciano o por si quieres profundizar.</p>

<h2 id="las-instrucciones">Las instrucciones</h2>

<p>Monta un blog llamado codecantor en el que vamos a escribir todos los contenidos a mano:</p>

<ol>
  <li>Seguir los guidelines del diseño en Figma<sup class="note-ref"><a href="#fn-1">1</a></sup>.</li>
  <li>Usa un generador de páginas estáticas como Jekyll<sup class="note-ref"><a href="#fn-2">2</a></sup>: HTML en build, sin framework de cliente; quiero escribir los posts con Markdown y HTML.</li>
  <li>Estilos con Tailwind CSS<sup class="note-ref"><a href="#fn-3">3</a></sup>, tokens en <code class="language-plaintext highlighter-rouge">tailwind.config.js</code> y cumplimiento de estándares WCAG 2.2 AA<sup class="note-ref"><a href="#fn-4">4</a></sup>.</li>
  <li>Tres vistas: home, índice de artículos (<code class="language-plaintext highlighter-rouge">/blog/</code>) y post individual (<code class="language-plaintext highlighter-rouge">/blog/title-post</code>) .</li>
  <li>Todo el contenido en español.</li>
  <li>Estructura predecible: layouts en <code class="language-plaintext highlighter-rouge">_layouts/</code>, fragmentos en <code class="language-plaintext highlighter-rouge">_includes/</code>, autores en <code class="language-plaintext highlighter-rouge">_data/authors.yml</code>.</li>
  <li>Deploy en GitHub Pages<sup class="note-ref"><a href="#fn-5">5</a></sup> al mergear a <code class="language-plaintext highlighter-rouge">main</code> vía GitHub Actions.</li>
  <li>SEO por página: títulos y descripciones únicos, JSON-LD<sup class="note-ref"><a href="#fn-6">6</a></sup> en posts, RSS y Open Graph<sup class="note-ref"><a href="#fn-7">7</a></sup> con imágenes generadas en build (Satori + Resvg)<sup class="note-ref"><a href="#fn-8">8</a></sup>.</li>
  <li>Crea un fichero <code class="language-plaintext highlighter-rouge">README.md</code><sup class="note-ref"><a href="#fn-9">9</a></sup> con la documentación necesaria para arrancar, editar y publicar el proyecto.</li>
  <li>Crea un fichero <code class="language-plaintext highlighter-rouge">front-end-prompt.md</code><sup class="note-ref"><a href="#fn-10">10</a></sup> con reglas básicas para cambios de UI: fidelidad al diseño, accesibilidad y convenciones del código, cuando acabes añadiremos más directrices del front.</li>
  <li>Escribe 3 posts de ejemplo para trabajar en los detalles del diseño.</li>
</ol>

<p>A medida que iba hablando con la IA me sugería si le podía dar información de los autores etc, haciendo muy fácil construir la base. En poco más de 15 minutos tenía una base sobre lo que trabajar, y tuve que darle 30 directrices de diseño (micro-interacciones, detalles del diseño muy dificiles de apreciar etc), así como incluir el contenido que quería que fuese 100% humano.</p>

<p>Quería un sitio sencillo en el que poder escribir y publicar, y quitando alguna corrección de código que hizo mal (como el contador de minutos de los artículos que lo metió manual y yo quiero que lo haga automático), el resultado para mi fué el deseado.</p>

<p>Es increíble además que me sugería ciertas dudas que yo sabía que tenía que resolver pero que no supe detallar a la hora de darle las primeras instrucciones, pero son detalles que iré resolviendo sobre la marcha.</p>

<p>Ahora sí, podemos centrarnos en construir, documentarlo y en aprender de todo lo que vendrá próximamente.</p>

<div class="post-notes">

  <h2 id="notas">Notas</h2>

  <ol>
  <li id="fn-1"><strong>Figma.</strong> Es la herramienta más popular de diseño UI. Diseñé usando Figma dos pantallas (la home y un post) con el grid que se ve en desktop, la intro y todos los elementos del logo etc, usé 4 colores y 4 tamaños de letra, suficiente para generar un sistema de diseño que luego reutilizo en el resto de la web.</li>
  <li id="fn-2"><strong>Jekyll.</strong> Generador de sitios estáticos en Ruby. Toma Markdown y plantillas Liquid y produce HTML listo para servir; no hay servidor de aplicación en producción.</li>
  <li id="fn-3"><strong>Tailwind CSS.</strong> Framework de utilidades CSS: clases como <code>text-subtitle</code> o <code>px-gutter</code> en el HTML, compiladas a un CSS minificado. Los tokens del diseño viven en <code>tailwind.config.js</code>.</li>
  <li id="fn-4"><strong>WCAG 2.2 AA.</strong> Pautas de accesibilidad web (contraste, teclado, HTML semántico). En este proyecto significa, por ejemplo, un solo <code>h1</code> por página y foco visible en enlaces.</li>
  <li id="fn-5"><strong>GitHub Pages.</strong> Hosting estático gratuito de GitHub. Un push a <code>main</code> dispara el workflow que construye el sitio y lo publica.</li>
  <li id="fn-6"><strong>JSON-LD.</strong> Datos estructurados en JSON embebidos en el HTML (<code>schema.org/BlogPosting</code>) para que buscadores entiendan título, autor y fecha del artículo.</li>
  <li id="fn-7"><strong>Open Graph.</strong> Metaetiquetas (<code>og:image</code>, <code>og:title</code>…) que controlan la vista previa al compartir un enlace en redes o mensajería.</li>
  <li id="fn-8"><strong>Satori + Resvg.</strong> Satori convierte HTML/SVG en una imagen vectorial; Resvg la rasteriza a PNG. Las usamos en CI para generar automáticamente la imagen OG de cada post con el logo y el título.</li>
  <li id="fn-9"><strong>README.md.</strong> Es un fichero (que suele existir por defecto en los proyectos de código), ahí se incluyen las instrucciones e información relacionada con el proyecto.</li>
  <li id="fn-10"><strong>front-end-prompt.md.</strong> Es un fichero con todas las reglas de front-end que queramos incluir y tener en cuenta siempre que hagamos cambios de código (cumplir accesibilidad, usar este framework o este js, estos colores…) para mejorar la precisión de diseño y desarrollo que usa la IA.</li>
</ol>

</div>]]></content><author><name>Jano M. Gorgal</name><email>alex@codecantor.com</email></author><summary type="html"><![CDATA[Créditos del software]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://codecantor.com/assets/images/og/default.jpg" /><media:content medium="image" url="https://codecantor.com/assets/images/og/default.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>