javisantana.com

Programador

Cuando vas al notario, además de sentirte ajeno a todo aquel ritual, te preguntan tu nombre, DNI, estado civil y, por razones que ChatGPT me ha aclarado, tu profesión. Siempre es un momento complicado y responder “programador” me cuesta. Y lo tengo que reconocer, es así porque creo que van a pensar que soy “un cualquiera”. Hay que ser gilipollas, aunque no voy mal encaminado, hay un tufillo clasista en ese tema (dejando a un lado la parte práctica y legal)

Empecé mi carrera profesional delante de un teclado, pero no como programador, empecé como “digitalizador”, es decir, alguien que copiaba lo que ponía en un papel a una base de datos, en una empresa de toda la vida, una empresa del metal.

Aquello era horrible, tanto que me busqué la vida para automatizar el proceso y no tener que hacerlo a mano. Tenía 22 años y mi primer ordenata lo compré justo un año antes (Pentium III, 256mb de ram) así que no tenía mucha idea y tampoco había mucha información, Google estaba arrancando y aún sacabas la mayor parte de información del IRC (para GenZ, una especial de slack/discord pero bien hecho). Te tocaba trabajar y entender, no había ChatGPT.

Más tarde me convertiría en programador, programé videojuegos, software para embebido, backend, frontend, seguramente todas las posiciones por las que un buen caraboli pueda pasar… pero la realidad es que más de la mitad de mi vida adulta no he programado activamente de forma profesional (pero sí personal, nunca he dejado de programar)

He hecho sobre todo roles de liderazgo técnico, he trabajado en entornos de alto rendimiento, startup o en retos relativamente importantes, he montado y liderado empresas, productos, desastres… pero no lo he hecho porque realmente quisiese hacerlo, pasaba por allí y me tocó.

La realidad que me cuesta aceptar, y la razón para escribir este post, es que nunca más voy a volver a ser un programador, primero porque hay gente mucho mejor que yo, pero lo más importante, porque realmente lo que sé hacer bien es lo otro, por mucho que me empeñe. Es duro pensar que lo que haces bien no es lo que realmente quieres hacer bien.

Aunque bueno, más duro es ver como una máquina es mejor que tú en lo que se suponía eras bueno. Los LLMs aún no están al nivel del un humano pero lo van a estar en meses, así que aunque quisiera ser programador, más me vale que lo haga en mis ratos libres y no para pagar las facturas.

Imagino que hay gente que se ha dado cuenta mucho antes que yo o que simplemente tenían la vocación de ser “líderes”, gestores, emprendedores o lo que sea, a mi me cuesta ponerme ese título, me parece que me estoy traicionando a mi mismo cuando asumo un rol por rol, sin que sea un medio para un fin. No quiero ser emprendedor por ser emprendedor, quiero ser emprendedor porque no me quede otro remedio para conseguir lo que quiero.

Siempre me he dicho a mi mismo que aunque tenga un rol, lo haré como realmente crea que hay que hacerlo, no siguiendo las recetas mágicas de nadie. Así que simplemente tengo que asumir que nunca fui programador, fui lo que tuve que ser en ese momento para conseguir lo que quería (ayudar agricultores, pintar mapas, analizar datos…)

Siento este post amigos, sé que os importa muy poco, pero me lo necesitaba escribir.

Recortes 2025

Como es tradición, además de asustarte con el título del email, por la razón que sea la gente -especialmente los empleados de mi empresa- piensa que voy a recortar algo (que efectivamente este año he recortado bastantes cosas), voy a hacer mi tradicional recopilación de cosas que me han gustado o, mejor dicho, dejado huella de alguna forma. Las de 2023 y 2024 por si tienes interés.

Como el pasado, este año me ha dejado menos momentos estelares. Nuevamente seguro que es cosa de la edad y mi estado vital. Además este año ha sido de extremos, bastante radical en lo personal y profesional, pero tranquilos, que no os voy a dar la brasa con mi problemas.

Aquí va la lista de cosas personales/profesionales del año:

El CEO de Snowflake siempre da en el clave el tío. Este año pasé a ser CEO de Tinybird y podría firmar con sangre estas palabras, especialmente la parte de energía. Empujar todos los días es carísimo a nivel mental.

Image

Remote work is individual convenience at the expense of group effectiveness

Guillermo Rauch, CEO de Vercel, siempre aparece en estas recopilaciones, esta vez con un tuit controvertido con el que no puedo estar más de acuerdo. Creo que el trabajo en remoto está bien y funciona, pero elimina algunas partes de la interacción humana, que aún siendo “prescindibles” las considero parte fundamental de la vida de una persona (que tenga un mínimo interés por realizarse en su trabajo). Posiblemente sea algo añejo, mi hija por ejemplo vivo en un entorno “remoto” constante (habla con sus amigas por videoconferencia) y seguramente se ria de esto cuando lo lea en el futuro.

Esta me duele especialmente. La tendencia a hacer lo que me gusta constantemente, aunque muchas veces de forma inconsciente, es horrible.

Working hard for a long time without asking “what are the better things to be working on” is a hidden form of laziness.

Jason Fried posteaba esto. Real como la vida misma. Se ve que estos años son de encontrar mi camino, por eso estas cosas

Esta se me ha quedado grabada en el hipotálamo amigos:

YOU CANNOT COMPETE WITH SOMEONE WHO IS HAVING FUN

No hay nada como alguien que está disfrutando de lo que hace. Lo veo en personas - especialmente deportistas- que cierta edad que aún se mantienen competitivos (Fernando Alonso, Carlos Sainz -me duele matizar esto, pero el padre-, John Carmack, Tim Sweeney, Jonathan Blow, DHH…) porque viven lo que puto hacen.

This is a goodbye letter I am sending friends to tell them that I am on my way to Switzerland, where my life will end on March 27.

Este es el comienzo del email que Daniel Kahneman envió a su familia y amigos. No he leído thinking fast and slow pero conocía al autor. Este artículo explica desde un punto de vista muy personal la decisión. Un quote más.

I am still active, enjoying many things in life (except the daily news) and will die a happy man. But my kidneys are on their last legs, the frequency of mental lapses is increasing, and I am ninety years old. It is time to go.

Pagaría un buen dinero por leer ese email pero algo me dice que es mejor no leerlo (o incluso escribir el tuyo propio, pero sin irte a Suiza, claro)

Whisky Tango Romeo, es un relato de Javier Cañada. Javier está escribiendo novela corta y relatos relacionados con el tiempo y me están gustando a pesar de años de no tolerar la ficción. Le sigo desde hace años y me gusta mucho la gente que es capaz de cambiar de registro siguiendo su línea y personalidad (cosa que valoro especialmente en esta era post chatGPT). Os recomiendo una de sus novelas, Santa Olalla, see lee en una tarde y si estás en tus 40s seguramente te guste aún más.

Este año hay más parte moñas de lo esperado, estáis avisados, ahí voy:

Alguien que contraté hace unos años me escribió esto. He contratado a cientos de personas, muchas de esas personas ahora tienen carreras espectaculares, no es que quiera atribuirme todo el éxito pero un poco sí. Nunca me habían escrito nada así. Gracias amigo, you made my year.

Esta foto con Diego Mariño junto con su tuit. La foto no tiene nada de especial, salvo que me estoy riendo que no suele ser habitual, pero hay algo que no se ve que sí es especial: alguien (al que le cuesta especialmente expresarse en estos términos) haciendo un esfuerzo por animarte. No se ve todos los días.

Si sientes ese dolor, mantenlo

Cuanto más te mueves con el dolor, mejor

Ahí es donde está el éxito

Todos sienten el dolor y pocos de ellos saben realmente como manejar el dolor.

Ellos serán exitosos

Debes entender que el dolor es parte del éxito

Combate el dolor con algo positivo.

De Kipchoge en este video. Este año me he acordado de esta mierda unas cuantas veces en mi cabeza mientras hacia series corriendo para hacer una 10k por debajo de 50 minutos (al final hice algo menos de 48 consiguiendo uno de mis TODO vitales a los 43 años)

Me voy a hacer una self-quote aquí. Este año Aitor García Rey se fue inesperadamente. Es “curioso” (mi léxico no me da para más) porque justo hablaba de lo poco que sabíamos de nuestros padres (en el 26 aniversario de la muerte del mio) y Aitor respondía. Mantengo un diario para mi hija, para que sepa exactamente qué coño pasaba por mi cabeza, a veces las decisiones se ven mejor con cierto contexto (y este año da para novela). Aún no sé como hacerselo llegar de forma fiable en el futuro.

Escribía esto sobre Aitor en el slack de la empresa

Hey, kind of sad news today, the CTO of Devengo, one of our customers, passed away. It’s not just a customer, he is a pretty well know guy in the tech community in Spain, some people at this company had a good relationship with him (me included). It’s super sad to see someone that young leaving. So if you have some time, I’d try to read some of the things he wrote in his blog or listen to some of his talks –> https://x.com/_aitor/status/1968657411826393187 https://aitor.is/writing/ Unexpected inhabitants: algorithms, ethics, and emergency |

El año pasado compartía un artículo de reflexiones de un ex empleado de Palantir. Este año toca compartir las reflexiones de una ex-empleada de Tinybird. Esta salida ha dolido de verdad. Pero bueno, siempre digo que “nunca vayas a un pueblo peor que el tuyo” que aplicado a las empresas es algo así como “vete siempre a una empresa mejor” y creo que en este caso se ha cumplido a la perfección, así que aunque las salidas de empleados no sean lo más agradable del mundo, siempre ayuda ver que progresan.

Y para dejar un poco de buen sabor de boca, este documental sobre un taller artesano japonés (Naito engineering) es una auténtica delicia. Si no te gustan los coches seguramente no lo disfrutes tanto pero en el fondo habla de una forma de ver la vida y el trabajo. Antes de morir profesionalmente seguramente monte “Santana engineering” o algo similar (no me gusta mucho mi apellido, la verdad), con la misma filosofía pero en software/datos.

Y aquí lo voy a dejar. Ha habido podcasts, películas y otras cosas que me han gustado, pero nada me ha marcado demasiado.

Comentarios del director

Ryanair tiene dos cosas maravillosas: a pesar de humillar a la gente en forma de reglas aleatorias, todavía aplauden al aterrizar, y la menos impresionante, te permite viajar a aeropuertos -que de otra forma estarían muertos- por lo que te cuesta una cena sobre mantel (para los mileniales: los manteles son como unas movidas para poner encima de las mesas, que solían ser de tela. También se usaban cubiertos para comer hasta que la optimización mató al sentido común)

El caso es que cogí uno de esos vuelos a Treviso, imagino que es la forma barata de ir a Venecia pero en este caso fui a Padua (que es una maravilla de ciudad aún no colonizada por el turista de Ryanair) que está a tiro de piedra de Verona, donde pasó toda la trama de novela adolescente-renacentista que hemos idealizado y creado algo histórico (Romeo y Julieta, para milenials). Es bastante increíble lo bien que venden los Italianos lo que tienen, empezando por la comida.

Paseando por Verona dimos con la catedral. Yo nací y pasé una tristísima infancia en un pueblo con dos iglesias tremendas, no son catedrales pero harían sombra, literal, a muchas de las catedrales construidas en la misma época. Cuando ves algo todos los días pierde el valor por mucho que tenga, así que, aunque se que las catedrales son obras increíbles en muchos sentidos, me cuesta apreciarlo. En cualquier caso entré por inercia y poco de presión social, como buen discípulo de Ryanair.

Ojo, por un momento pensé que me salvaría de entrar, cerraban ese día antes porque había un concierto más tarde, pero ya había pagado, así que aunque fuesen 5 minutos tenía que rentabilizar la inversión. Justo al pasar el NFC se empezó a escuchar música clásica, la acustica era espectacular y ya solo esos 5 segundos merecieron la pena. Estaban ensayando lo que después sería el concierto y me pude acercar a los músicos que casi podía tocar los platillos.

El ensayo no era una reproducción del concierto pero sin vestir de gala, el director paraba cada poco tiempo para dar indicaciones, repetir una parte, ajustar el volumen (me perdonen por no usar la jerga adecuada) e iba haciendo pruebas de algunas partes de la obra, incluido el coro.

En resumen, disfruté más es esa media hora que de las obras del mismo tipo, todas ellas ejecutadas a la perfección, que he visto en mi vida.

Seguramente disfruté tanto no solo porque fue inesperado si no porque me aprecio mucho más los detalles cuando ves cómo está hecho, cuando se cometen errores, se aprecia mejor el esfuerzo y lo que cuesta mejorar ese último 10-20% (que nadie más va a notar, solo Dios lo ve). Me gusta más ver una repetición de un combate de Topuria comentado por Topuria que el propio combate, una repetición de la Indy500 con las decisiones tomadas por Palou que la carrera o decisiones de diseño que alguien tomó en una aplicación. Disfruté mucho más del podcast de la serie Chernobyl donde Mazin cuenta las decisiones que tomaron (por ejemplo, el idioma del rodaje) que de la ya de por si buenísima serie.

Hubiese pagado el doble de la entrada para ver el ensayo completo.

Que viene el lobo

Cada semana se anuncian unas cientos de empresas que, dicen, van a cambiar el panorama de usando IA. En esa misma semana empresas levantan millonadas argumentando que lo hacen para jugar al juego de la IA. Y las empresas de toda la vida, los Google, los Microsoft y demás ballenas se convierten en delfines anunciando modelos e integraciones con productos actuales.

Por otro lado tienes a unas cuantas otras empresas, nacidas al calor de la IA, haciéndose los gallitos con el crecimiento de ARR. Me pregunto cual es el objetivo de anunciar a los 4 vientos cuanto ingresas, imagino que es una manera de decir “aquí el que voy a ganar soy yo”, pero no lo tengo claro.

Es bastante obvio que hay un cambio. Bitcoin tardó en llegar a la boca del pueblo bastante más de un lustro. Esto está ya en la cabeza de todo dios en menos de 3 años. Si tienes interés, haz una búsqueda en google trends (bitcoin vs chatgpt):

Y hay un dilema tremendo si eres emprendedor/empresario es, a donde pego el tiro? las opciones que veo en mi cabeza:

Puedes elegir pasar y esperar (hacer un Apple lo llaman) pero sinceramente me parece una estrategia arriesgada. Te van a pasar por la derecha (salvo que seas Apple y puede que también, espera que otros se pongan a hacer hardware). No tengo pruebas, tampoco dudas.

Puedes elegir integrar IA en tu servicio/producto/empresa. Parece la más sensata, usar la IA con cierta cabeza para tener esas mejoras e ir integrandote en la ola poco a poco, sin asustar a tus clientes actuales, haciendo una transición ordenada y con relativo poco riesgo.

Pero claro, si eres una empresa de VC y estás con un crecimiento que no está mal, pero no es el “hockey stick” que esperas (que es el 95% de las startups que no están muriendo) , es muy tentador ir a coger la ola más grande y hacer que tu posicionamiento sea IA first. Ves como las empresas que usan cualquier cosa que sea IA como canal de distribución, algunas dando más pena que gloria, pero consiguiendo cierta tracción, así que tú, con tu WhatEverFy que es el nuevo Uber de la venta de coliflor con colores que puede elegir, te montas en la ola y ahora eres WhatEverFy.ai y las coliflores son las mismas pero creadas con un modelo propio de IA.

Lo que en ningún caso puedes hacer es estarte quieto (en mi opinión). Nadie sabe donde va a caer el gordo pero tienes que empezar a poner a tu equipo en guardia, empezar a meter a todos en la cabeza que hay que estar un poco al día. Cuando todo empiece a asentar un poco es mejor que te pille con los deberes hechos. Muchas empresas van tarde, incluso en Tinybird que es una empresa donde todo el mundo es bastante espabilado, hay gente que no estaba poniéndose manos a la obra.

En Tinybird dedicimos hace 8 meses que o hacíamos un cambio o estábamos mal. Somos una herramienta para desarrolladores y los desarrolladores de hace 1 año no son los mismos que los de ahora. Pensarás que estoy exagerando y que tardará en llegar a sectores más tradicionales. Como buen heterobásico que soy generalizo anécdotas pero esto es demasiado goloso como para que hasta el más tonto no se de cuenta: los sistemas automáticos de código son mejores que muchos desarrolladores y hasta las cárnicas se han dado cuenta.

Hagas lo que hagas con tu producto, tienes la otra parte de la estrategía y es qué coño vas a hacer a nivel de procesos internos de compañóa. Es un cambio fuerte, la inercia es altísima y hacer cambiar a gente de entre 30 y 40 años su forma de trabajar con una máquina no es trivial, incluso aunque sea gente con mucha experiencia con la informática. Cada cosa que hago me doy cuenta de las oportunidades perdidas para haberlas hecho mejor y más rápido, pero no lo hago así porque hay fricción. Fricción en mi cerebro, no en las herramientas.

Y esto pasa en muchos trabajos. Por suerte los humanos nos podremos dedicar a hacer otras cosas mientras la IA hace lo que realmente hace bien, pero hay que adaptarse. Eso o afiliarse a un sindicato que luche para que los robots no reemplacen puestos de trabajo tratando de retrasar lo inevitable y sacar unas cuantas “prejubilaciones” y “recolocaciones”.

When the chef cooks at home

Chefs usually manage “large” kitchens. They have a few cooks and have to deal with a constant flow of dishes they need to dispatch on time. It’s not just cooking, it’s the whole process: from designing the recipe, getting the raw food, and cooking, to serving it on time, hiring, firing, company financials…. I can’t imagine how hard it must be to handle all of that.

In software companies, things are pretty similar. It’s not just the product; it’s designing, developing, keeping everything up, serving customers, and so on.

Chefs also cook at home, small dishes maybe, just for their families, or something they want to test. They do what (and this is a guess) they love and what pushed them to become chefs in the first place: creating good food.

On weekends, I wake up at 6:00 AM so I have a few hours to work/learn on whatever I want before everyone else wakes up. It’s my “cook for my family” time, but in this case, I don’t do anything for them; I code without the constraints of the production environment. I do it for myself, to test new things or just enjoy it.

I’m working on a project using an ESP32, a small microcontroller with super limited resources (if you compare it with a modern laptop), but it has everything: connectivity, storage, a little bit of RAM, a pretty decent ARM processor, and good toolchain and library support.

So, I wake up, make myself a large coffee, and start coding my own data compressor to store GPS positions in the most efficient way so I can review the laps I do with my “racecar”. No AI, just Vim and g++.

Como uso los LLM

Lo que pueden hacer los LLMs es básicamente un sueño hecho realidad para mí. Soy un “builder”, me gusta construir y me parece magia todo lo que está pasando, es como revivir la época cuando aprendí a programar. Peeero como siempre que llega algo nuevo, está lo que idealizamos y luego la vida real, cuando la tecnología toca suelo, hay que hacerla funcionar y adaptarla al caso real. Aquí voy a ir al caso práctico, para hablarte de cómo la IA va a cambiar el futuro futuro, puedes ir a LinkedIn y leer a todos los chamanes, que no dejan de ser los mismos middle managers que no saben ni por dónde les viene, que han encontrado la nueva vía de hacerse notar.

Voy a dejar por escrito cómo estoy usando estos sistemas a día de hoy, como digo, a nivel práctico, enfocado en la construcción de productos (seguramente haga algún update de vez en cuando), dejando a un lado las preguntas de todos los días que antes hacías a Google y ahora las hago a Grok (pago suscripción a Twitter y con ella tengo acceso a deepsearch que es bastante bueno). y ChatGPT. Esto ya lo saben hacer hasta los niños de 6 años, incluso dan cursos en las empresas de como usar ChatGPT, así que no voy a entrar en detalle.

Una nota sobre las herramientas; uso exclusivamente dos herramientas e intento hacerlas al nivel más básico posible. Con los años te das cuenta que profundizar en detalles te hace perder foco y el tiempo en cosas que aportan bastante poco, es mejor centrarte en la funcionalidad core y listo.

La primera es Cursor. Si no sabes que es, seguramente este post no sea para ti, si realmente estás interesado, buscarás que es y lo probarás.

La segunda es llm, una herramienta de línea de comandos que tiene lo justo y necesario para trabajar con LLMs a alto nivel, sin tener que usar el API de OpenAI y permite automatizar fácilmente. Algunos ejemplos básicos que cubren el 99% de lo que uso y explican la herramienta.

``$ llm "this is my prompt"
$ llm -m gemini-2.0 "this is the prompt"
$ llm --system "you are an expert on this" "the prompt"
``

Usos prácticos

“Vibe codeo” bastante, concretamente hay dos herramientas que he “programado” que uso en mi día a día para hacer mi trabajo:

El editor de texto que estoy usando para escribir esto es una aplicación, toda contenida en un “index.html” que me permite escribir y además usar LLMs para ayudarme con cosas como corregir el inglés, contarme si hay frases que a un nativo le suenen mal, reescribir el documento como si fuese “Pérez Reverte”, hacerlo más corto, etc.

Image

Para ello uso Cursor, abro un nuevo proyecto, creo un index.html y le pido lo que quiero dejando claro que quiero una aplicación html/css/vanilla javascript.

La herramienta que uso a nivel de producto para entender el “journey” de los usuarios de Tinybird, desde que llegan a la web, cuando se registran y como usan el producto. Uso el mismo sistema que antes, Cursor + app básica escrita en html/javascript, integrando con las API de Tinybird y PostHog para recoger datos y mostrarlos de una forma lógica.

Son aplicaciones muy muy sencillas, muy ad-hoc a como me gusta hacer las cosas. Son un puto traje a hecho por ti con un implante cerebral del mejor sastre del mundo vs uno de Zara de 119.93€. Es absurdo que en unos meses las aplicaciones, no solo las escriba quien las necesita, sino que las aplicaciones no sean fijas y no cambien todo el rato (sí, ahora estoy haciendo de chaman)

A nivel de gestión de producto & marketing:

Dejando a un lado que en Tinybird ya no se hace una feature sin pensar como aplicar esto, de hecho acabamos de rehacer una parte del producto donde el foco está en mejorar el trabajo con LLMs. No tienes que convertir tu producto en un chat, pero usarlo para tareas que son un auténtico peñazo es un buen comienzo.

En Tinybird genero una newsletter cada dos semanas donde enviamos los artículos que el equipo ingeniería de Tinybird lee. Me gusta saber qué lee la gente del equipo y esto es una excusa perfecta. Además, mantiene a la gente que sigue Tinybird un poco más pegada a la empresa. El sistema es simple: hay un pequeño bot que lee un canal del Slack interno de la compañía y en base a un prompt genera el correo para enviar a la lista de correo.

Para que nadie se sienta estafado por leer algo generado automáticamente le he dado personalidad propia al bot. Esto es una parte del prompt:

`` Get the comments people made for each link and generate a brief comment about it. It should be funny, like making fun of the people in the company, but in the possitive [positive] way. We love to troll each others [each other] in this company. You act as an editor, you should have your own voice and speak in first person. This was the last one@newsletter.md
- Try to guess the company position based on their comments
- Do not mention names just leave what you think is the position
also introduce you at the beggining [beginning], your name is lebrelbot you work at tinybird, an AI bot and don't care about what other people say about
``

En las primeras iteraciones le dije que fuese un poco “pasivo-agresivo” y tuve que quitarlo porque los LLMs son especialmente buenos dando donde duele.

Para estas automatizaciones uso llm, por ejemplo, el comando para generar la lista de correo es:

``uv run llm  -m gemini-2.0-flash --system "$(cat prompt) today is $(date)" "$(cat 2025-03-28/chat) `curl -s https://www.tinybird.co/blog-posts/rss.xml`"
``

Le paso el prompt que comentaba antes, la fecha para que sepa donde estamos, el chat según viene de Slack (sin formatear, para qué perder el tiempo) y el RSS con los posts del blog Tinybird (que incluye al final de la lista). Te puedes suscribir aquí si tienes curiosidad. Este es la última edición.

Al hilo de lo anterior, pensé: puede un LLM emular el estilo de escritura de una persona leyendo lo que escribe en los canales públicos de Slack. Hice un pequeño test de usar mi estilo de escritura (pero como si fuese americano nativo) y meter el bot en un canal de slack a que respondiese cuando alguien le pregunte. Era bastante cabroncete por ser honestos y tuve que pararlo. Los hijos y los LLM reflejan los peores actos de cada uno.

Como anecdota, cuando le pedí que hiciera un perfil de la persona se me olvidó “de escritura” y me sacó un perfil de todas las personas del equipo. No sé si es efecto horóscopo (escribe lo que quieres escuchar) pero es muy bueno calando a la gente.

Me parece un tema bastante interesante, de hecho hay más gente haciendo esto para simular audiencias.

También lo uso para generar informes del comportamiento de los usuarios de Tinybird a nivel de producto, es decir, qué hace un usuario, donde se atasca, qué hace bien. En Tinybird guardamos todos los eventos (nuestro producto sirve para guardar datos, así que parece obvio) de lo que hacen los usuarios. Es una tabla muy con user_id, timestamp, event_data. En vez de usar una herramienta a medida, símplemente cojo los eventos y hago pipe al llm. Es algo más complejo que esto porque elimino bastante información para reducir los tokens que necesito enviar al LLM (aunque gemini tiene un contexto muy aceptable) y eliminar detalles de errores y otras cosas que no sean puro metadato.

``curl https://api.tinybird.com/v0/pipes/user_events?user_id=lebrel | llm -m gemini-2.0-flash --system "You are an expert in analyzing user behavior on Tinybird, a web platform to work with analytical data. The first thing you'll recieve [receive] is a JSON with all the events from an user using Tinybird (a web platform to work with analytical data), generate a summary of what the user did.
    You should include the following sections with a title for each one:
        1) relevant dates
        2) a timeline of events per day/week/month
        3) what errors the user hit
"
``

El prompt es mucho más extenso, pero te haces una idea. Y si no, preguntame y te lo paso completo.

Otros temas

Sin un orden particular.

Optimización de SQL y tablas, ejemplo

Generar títulos y draft de posts sobre clusters de temas de los que quiero escribir:

Busco una lista de correo de la que me guste el estilo. Copio la lista completa en un fichero (sí, todo el contenido junto)

Voy a Grok research y le pregunto que en base a Reddit y otros sites me genere 50 títulos con, por ejemplo, “top problems and use casesdata engineers when dealing with real time data”. Copio la lista en otro fichero.

Pongo cursor en modo agente (que va haciendo cosas por ti de forma semi desatendida) y le digo: “generate a blogpost for each title following the style of this mailing list. Add examples on how to do it with Tinybird”. Además le paso el prompt super curado que tenemos para Tinybird (tú ya tienes el prompt para tu producto, verdad?) Hice un test de pasarle a mis compañeros el post para que me lo revisaran (como si lo hubiese escrito yo) y solo una persona se percató de que era 100% automático. Un ejemplo de lo que genera, “how to build a recommendation engine”

Uso bastante lo de generar visualizaciones ad-hoc para cosas, por ejemplo, para hacer debugging de ciertas cosas. Un ejemplo con una traza GPS.

En general, cualquier cosa que me llega, abro un terminal, copio el contenido en un fichero y tiro un cat file | llm --system "whatever I want to do"

Hago bastante el “critica esto que he escrito de forma dura, se un poco pasivo agresivo manteniendo el humor, esto me ayuda mucho a mejorar”. Te advierto que es duro leer las críticas y si tienes la piel fina, no lo hagas. Ningún humano que no tenga problema social va a ser tan sincero. Aún no he probado a pasarle una foto y decirle lo mismo, pero si no ha pasado, va a pasar.

Estoy en una fase ahora donde estoy empezando a automatizar de forma desatendida, por ejemplo para hacer matching de problemas reportados en Slack con tickets en gitlab, pero aún no he profundizado casi nada, así que lo dejo para la siguiente edición.

We need a new git

A few years ago continuous integration systems were not a thing. You were lucky if you worked for a company with automation, so imagine having something that runs for every commit. We were using CVS, subversion and other similar non-distributed version control systems.

Those systems were like git, you didn’t have a local repo, you needed to push into a remote server, but that’s what we are doing most of the time these days as collaboration happens remotely. Git fundamentally only moved things locally and allowed better merges and collaboration. That’s great but we paid a high price: git is hard to use.

With git, continuous integration system started to be popular, it was not entirely “thanks to” git, but it happened at the same time. Modern guys were doing nodejs projects, started using React and testing was mandatory, so did CI.

It was like magic, each push an automatic system would run and give you feedback: OK/NOK.

I’m changing topics for a second. Big data was slow because 20 years ago, hardware was not as fast as today. People got used to slow batch job processes, and that’s what they have been doing since then. A few years ago, someone realized that processing a lot of data didn’t have to be that slow and created a different kind of databases.

15 years have passed and we are still changing things locally and still need to commit manually and wait for a long feedback loop to know if we are doing great. We got used to it, we didn’t even know we gave up on speed, we assume CI cycles are slow, we have to wait minutes from the time we type the last character to the OK.

This is becoming super important as LLMs are starting to be “in the loop”. And as humans, they make a lot of mistakes, but if you give them feedback, they usually manage to fix them.

Speed is a feature and I think there is room (and hardware resources) to have a system that gives us feedback every time we type a new character.

Iceberg and S3Tables

Iceberg is the latest trend in the data space, and for good reason (it allows different products to share data in a standard way) Everyone is thinking, “I’ll store all my data in Iceberg to avoid vendor lock-in.”. But I’m not so sure that’s actually true.

In theory, Iceberg hasn’t won yet. There are other data lake format implementations out there. However, Databricks and Snowflake (the two largest cloud companies) put the format in the spotlight when they battled to acquire Tabular, the company behind the Iceberg spec. Databricks won, but in theory, everyone benefits from the increased adoption.

AWS recently announced an extension to S3 to manage Iceberg tables (S3 Tables). This move surprised me, S3 is AWS’s flagship product, so adding native support for Iceberg suggests they’re seeing something we aren’t (probably S3 usage stats).

Iceberg itself isn’t a complex format, understanding the spec is a matter of spending a few hours reading the spec and then a few questions to your favourite GPT. But managing it? That’s where things can quickly become a headache. That’s exactly what S3 Tables aim to solve: no need to worry about compaction, cleanup, or spinning up EMR clusters to let Spark handle it (since Spark is, afaik, the only battle-tested Iceberg implementation).

Moving data into S3 Tables is convenient, you get all the power and cost benefits of S3 without the management overhead. Of course, data compaction at scale is expensive, especially if you’re dealing with massive datasets. I made the math and some of our customers would spend $60k a day (for a real time workload)

But here’s the catch: if you want to process that data, you’ll likely need to do it within AWS. Egress costs make running outside of AWS nearly impossible. So in the end, AWS wins.

So you’ll not have vendor lock-in in your database engine but you’ll have a different kind of vendor lock-in.

España y startups

El otro día teníamos esta conversación en twitter (para algunas cosas sigo pensando en pesetas, imaginate cambiar el nombre a twitter) donde Jaime Novoa la clava:

Es muy difícil hacer una lista mejor que esa, así que no la voy a tocar pero lo que sí voy a hacer es dar el punto de vista desde dentro, el de fundador de una startup.

Por dar contexto, en 2006 monté mi primer empresa de producto que vendí 11 años después (todos ellos siendo muy rentable), trabajé en CARTO como CTO (más de +$30M de financiación cuando yo me fui, ahora estarán cerca de los 100) y llevo 5 años como fundador de Tinybird, una empresa deep tech que ha levantado unos 70 kilos con inversores Americanos y Europeos. En ambas compañías hemos conseguido llegar a varios millones de ARR. Además he trabajado en otras startup que se la pegaron, en el gran corporate, como freelance y en industrias tradicionales. Sé lo que es sufrir

Sobre “Risk takers”

No sé a quien se refería, si a los emprendedores o al resto de jugadores (inversores, clientes) pero es cierto, pero el punto de vista del fundador es muy claro: te la juegas con todo.

Si eres un chaval y arrancas, seguramente no tengas ni un puto duro y o bien tienes padres que van en Porsche Panamera Turbo o te va a tocar ir muy lean unos años. En mi caso vengo de una familia muy pobre, no de pedir por la calle, pero de pasarlas putas sí. Me he pegado mis 20s trabajando todos los fines de semana para ir a vender a los agricultores mi producto por los pueblos, por dar una referencia.

Si tienes cierta edad, puede que tengas unos ahorros, pero ahora juegas con tu familia. Se puede emprender de 9 a 5 seguramente, yo aún no lo he visto.

No solo es un tema de dinero, puede que te sobre, es un tema de mentalidad. Si has sido pobre llegar y fundirte 25 millones de dolares (ups, perdón, de euros) en un año, 300 mil pavos en un dominio, $400k al año en un perfil concreto es muy duro y difícil de asimilar. Pero lo tienes que hacer. Mucho de ese dinero lo vas a tirar, pero para conseguir cosas, hay que fallar.

Cuando voy a SF y hablo con algún emprendor de ahí, además de que se venden muy bien, les importa tres narices la mayoría de cosas que aquí nos asustan. Que hay que pivotar porque aparecen los LLMs, se pivota, que hay que tirar clientes, se tiran. No miran atrás. Esto hay que mamarlo y en España, donde tu entorno está más cerca del funcionariado que de todo su jugarse el capital en 3 años, no es fácil.

Personalmente a mi me está costando y dudo que con 43 años vaya a cambiar de forma radical. El día que voy a tomar una decisión jodida al acostarme siempre veo la foto de mis abuelos que tengo en la mesita de noche y no puedo evitar pensar que me echarían una peta monumental. Ya no están aquí, pero lo que ves en casa durante años es difícil darle la vuelta.

El VC en España tampoco es que sea muy aventurado, espero que esto cambie.

Cultura pro-empresa

No voy a entrar mucho aquí porque tocamos tema político, sindical y creencias estúpidas como que seguimos en una economía donde poner fundas de asientos en una cadena de producción es lo que levanta un país, pero solo os diré una cosa: yo no digo a nadie de fuera del entorno startup en lo que trabajo.

No digo que tengo una empresa, evito hablar del tema porque no me apetece dar explicaciones de nada. Y cuando lo cuento, me arrepiento, muchas veces hay preguntas perniciosas o incredulas. No les culpo, te imaginas estar hablando con alguien con problemas más que patentes de comunicacion con otros seres humanos diciendote que ha levantado 70M, trabaja haciendo no sé que de bases de datos y quiere reventar el mercado. Necesitamos que la persona que monta una empresa, especialmente del tipo “todo o nada” no sea un bicho raro. O aceptar a los que hay bichos raros.

Más capital

Por suerte nosotros vamos bien servidos. Pero vamos bien porque tenemos un track record y nos ha permitido levantar capital “facilmente”. Esto no viene símplemente por estar ahí: nos hemos labrado a pulso, trabajando 16 horas al día para sacar proyectos increíbles, peleando cada partido, contando como lo hacíamos, yendo a jugar con los mejores (o intentandolo), etc, etc.

Puedo entender que alguien empezando lo tenga crudo y seguramente lo de “risk takers” vaya también por la gente de VC (y otras entidades que meten gallina) se atrevan con gente que parece lista, pero que no pinta tan bien en el excel. Lo peor es que perdemos todas las oportunidades cuando empiezan, me autorreferencio un tuit de hace dos años (no voy a dejar pasa esta y aprovecho a darme la razón)

Esto que digo no existe, pero se puede generar con dinero y la gente adecuada (más sobre esto después)

Luego tenemos las ayudas estatales y europeas, pero muchas de ellas terminan en proyectos, y perdonad la expresión, de mierda o creados ad-hoc para trincar la subvención. La industria de la ayuda europea.

**Más referentes **

Tenemos references, el mismo Joaquín Cuenta que empeza el hilo en twitter es uno de ellos y hay otros muchos en el panorama español. El problema es que muchas veces desaparecen porque pasan de estar todo el puto día con gente apuntando a lo que hacen o han hecho mal. Para qué se van a tener que estar exponiendo, poniendo su opinión o inviertiendo su energía? Por qué esa gente no está contando en prime time lo que han hecho? por qué no todo el mundo sabe su historia?

Yo estoy lejos de ser un referente pero aún así ya hay gente que te veta (y no me refiero a que te trate de cancelar, que ese es el menor de los problemas) y te toca los huevos por lo que dices y haces, por tener una opinión [empresarial] que compartes públicamente.

Esto tiene mucho que ver con la cultura “pro-empresa”. Mientras el empresario sea el enemigo, estamos jodidos.

Seguramente alguien esté pensando que estoy exagerando pero, dime, cuantos programadores/técnicos españoles conoces detrás de una empresa? empresarios con “salsa” conoces de casualidad al amigo Jose Elías porque se ha puesto a poner videos en youtube y da gracias, así que imaginate a un programador o alguien en la escena técnica levantando una empresa.

No solo los fundadores son referentes. Hay mucha gente que trinca bastante pasta como empleado, pero… cuantos conoces? ninguno porque no dicen ni pio, lo sabes por corrillos, los intuyes porque de un día para otro hacen pequeñas inversiones como “angel”. Pero eso solo lo ves si estás dentro, poca gente de “fuera” ve que puedes hacer dinero y divertirte (en vez de estar haciendo reuniones con mid managers sobre gilipolleces en el corporate medio)

…Y clientes

Hablando de risk takers. La diferencia cuando vas a una reunión con un empresote americano a una empresa española es brutal. La gente de España normalmente te quiere usar, pero el hecho de ser una empresa jóven te pone unas barreras tremendas. Empezando por el proceso de pasar por múltiples reuniones que aportan poco, luego el proceso de compras, seguridad, compliance y todo el tinglado, una startup no tiene el personal para estar liado 6 meses.

Que algunas empresas grandes confién en ti es la noche y el día en los primeros días (en una empresa B2B) . Para nosotros el hecho de que una empresa grande nos apoyase al principio fue clave, eternamente agradecido por ello y por todo lo que he aprendido de ellos. Bueno, y ellos de nosotros, seguramente se hayan ahorrado bastantes millones de euros gracias a lo que han aprendido.

En general, digamos que España es un pais que prefiere pagar por software en hombre-horas. No entendemos que innovar no se hace dando a la manivela sobre lo mismo con mucha gente.

Y no voy a comentar más de este tema, no soy tan risk taker.

Varias décadas de ventaja construyendo tech

El tiempo no podemos acelerarlo, de momento. Aquí lo único que podemos hacer es hacer lo que ha hecho China: remangarse, apretar los dientes y sufrir como perros. Nos va a tocar vivir un poco peor me temo. O es que nos hemos pensado que las cosas salen símplemente por tener una legislación más favorable?

Traer gente que lo haya hecho es también importante, pero digamos que no es trivial importar gente. Quien coño quiere venir a España si no es para vivir mejor. Y vivir mejor significa no dejarte los cuernos sacando algo adelante (perdón por la crudeza)

Repito, hay que sufrir para sacar algo adelante y darte muchísimas hostias antes de llegar a algo que funciona. Mucha gente viene y comenta (por seguir con Freepik, empresa de Cuenca) “pero mira los de Magnific, en 2 años lo han petado y vendido a Freepik”. Bueno, sí, pero no miras los otros +20 años que Javi (y Emilio, claro) ha estado haciendo proyectos de tecnología, creando tutoriales de como hacer juegos, etc.

Y lo creerás o no pero en otros países el trabajo duro está bien visto :)

Chorradas del gobierno

Las leyes laborales no están pensadas para startups, están pensadas para gente que va a una cadena de producción, para la gran empresa. Cuando una startup se tiene que adaptar a la ley laboral significa que el riesgo es mayor para el empleado y para la empresa (y no hablo de casos como Glovo). En un mercado de empleados de alta cualificación las reglas del juego actuales son absurdas.

Quién va a a arriesgarse a algo con muy poca probabilidad de éxito si te puedes ir a un banco, cobrar un sueldo decentísimo, hipoteca al 0%, bonus del 150% anual, guardería, gimnasio y jubilación a los 55? Si no hay un incentivo claro la gente no es gilipollas y va por el camino fácil (pero aburrido, no lo hagas)

Ojo, no todo el mundo hace las cosas por dinero, la mayoría por suerte no, pero la gente tiene hijos, casas y otros temas y llega un punto, precisamente el punto donde tienes mucha experiencia, que no te apetece correr según que riesgos.

El tema calentito de las stock options es absurdo, no tiene sentido que tengas que tributar por algo que no existe y seguramente nunca lo haga. Igual que antes, para que me voy a arriesgar si no voy a sacar nada en claro.

Por otro lado, y perdonen mis amigos abogados: necesitamos sacar un poquito a los abogados de las instituciones (un 50% en toda Europa según Grok deepsearch) y poner más gente que no sea tan conservadora (que es el trabajo de un abogado)

Para terminar, el otro tema es el exit tax. Si yo tengo una startup con una valoración alta, estoy jodido si quiero irme a otro pais, por ejemplo, para vender a muerte en USA o irme allí a contrar a gente que no hay en España.

No hablemos de la cultura tecnológica. Ahí tenemos al amigo Jaime Gómez-Obregón sacando las vergüenzas.

Tecnología

Esta no la ha puesto Novoa, pero la pongo yo. A nivel técnico estamos en pañales y nos da exactamente igual. A veces veo a gente cualificada hacer cosas manualmente que, con unas semanas de estudio, podrían automatizar o por lo menos saber que se puede automatizar. Por amor de dios, esa gente luego se monta unos videos en tiktok espectaculares, hay que invertir horas para saber hacer esa mierda.

Este ejemplo parece muy tonto, pero el hecho de que la gente no vea alcanzable la tecnología como herramienta. Voy un poco más allá. Intenta encontrar gente en España que trabaje en lenguages de bajo nivel, que sepa o esté interesado en software de infra/sistemas (algunos no sabéis ni de lo que estoy hablando, lo cual me da más la razón).

Intenta encontrar a alguien en España interesado en entrenar modelos base LLM. Hay 4 gatos. Ahora, busca frontend react developer, te salen hasta debajo de las piedras. La razón: nadie se atreve porque no lo han visto. Yo siempre pregunto en las entrevistas técnicas “has entrado a ver como funciona X proyecto a bajo nivel” y raro es cuando alguien ha echado un ojo. No lo miran porque nadie les ha dicho que se puede hacer, no han visto a nadie hacerlo.

Si no hay densidad de personas así, no hay deep tech y por tanto te dejas fuera las grandes oportunidades. No todo el monte son ecommerces.

Sobre “la pasta”

En todo este post he estado hablando de pasta, puedes traducirlo directamente a valor, a veces no es directo, pero es una buena forma de entenderlo.

Como decía, la mayoría de la gente que hacemos esto no estamos aquí por el dinero, de hecho verás mucho emprendedor con exits montando más cosas. No lo hacen por la pasta, lo hacen por que es su motivación, su forma de vivir. Yo no entiendo mi vida sin esta mierda.

Soluciones

El rant es importante y seguramente estarás diciendo, vale, y qué podemos hacer? pues creo que lo único que podemos es remar y ser valientes. Obviamente no hay nada que una persona individual pueda hacer a nivel global, más bien es la contribución de muchas, durante tiempo.

A nivel individual siempre intento escribir sobre mi experiencia, dar charlas cada vez que me invitan, dar consejo a emprendedores cuando me lo piden. Siempre que veo a alguien joven en la empresa, de los que ves que tienen madera, intento animarle, “cuando montes tu empresa, no hagas esto”, etc. Algunos de ellos se han ido a trabajar a empresotes, con salarios super altos y han decidido no emprender :/

Es suficiente? ni idea, siempre pienso que podría contar mucho más de cosas de “go to market” de tecnología, company building, cultura, open source, licencias y demás mierdas variadas por las que todo el mundo pasa, especialmente en la fase de 0-10M ARR que es donde creo que puedo aportar más. Sin embargo, para llegar ahí alguien ha tenido que lanzarse a montar algo.

Donde seguramente pueda aportar muy poco es en intentar convencer/influenciar a algún político. Al menos un diputado me sigue en twitter, pero están más preocupados por otros temas por desgracia.

Sinceramente, siendo yo poco amigo de la patria en general, últimamente me está doliendo un poco (por no ser más explícito) el tema de US vs EU (especialmente el puto meme del tapón de la botella) y digo yo que algo habrá que hacer. Incluso desde España, que como dice un buen amigo, hemos nacido para salir a ser segundos.

Terminado y cerrado

Me gustan los coches, si por mi fuese tendría un parking con una casa pegada. No estoy ahí -aun- pero tengo la suerte de tener algún que otro coche de fin de semana. Uno de ellos, un Lotus Elise, es un coche peculiar, es un coche diseñado a mediados de los 90 con una premisa clarísima: ser lo mas ligero posible. Es una auténtica obra de ingeniería, aunque la intra historia tiene bastante más de cinta aislante que de glamour. Bueno, como todas las historias que salen bien. Hay un documental por aquí.

Para ilustrar, os dejo una foto del susodicho en el mejor circuito del mundo.

Image

Lo llevaron al extremo, y cuando llevas las cosas al extremo o bien sale algo muy complejo o muy sencillo, Puedes ver la imagen del interior del coche debajo de estas lineas para saber lo que fue en este caso. No quiere decir que no tenga innovaciones súper interesantes, ya sabes que cuando pones límites siempre salen soluciones diferentes, te obliga a pensar, y pensando, echando horas, es como salen las cosas. Si quieres leer más, échale un ojo a la biografía de Colin Chapman o escucha alguna entrevista de los diseñadores del Lotus Elise, Julian Thomson y Richard Rackham, se aprende mucho de diseño de producto. Te dejo esta maravilla de quote de Dave Minter, que de nuevo, tiene mucho que ver con hacer producto.

Lotus never used to take cars back in transporting, always drove them. And quite rightly, you can learn so much about the car just driving it. It was always said you have got to pretend you are a customer… we were really trying to develop a car

Image


En España solo hay un concesionario oficial Lotus, así que depende de donde vivas te toca hacerlo todo a ti mismo, no puedes transportar el coche 800km para hacer un simple cambio de aceite. Por suerte con unos vídeos de YouTube, una comunidad en WhatsApp, un poco de maña o tiempo, y tienes el 99% resuelto.

La cosa no queda ahí, uso este coche para ir al circuito y hay ciertas cosas que hay que hacer para evitar matarte: cambiar aceite regularmente, comprobar pastillas, verificar que el circuito de frenos está bien, comprobar que los tornillos están apretados como tocan, revisar si hay algo doblado, agrietado, y ver si hay alguna pérdida de aceite que pueda terminar en un incendio. No te quieres jugar el tipo en una frenada.

El hecho de que puedas modificar y “reparar” tu mismo el coche te hace apreciarlo más. Sabes de qué pie cojea cuando empieza a hacer un ruido, te sientes orgulloso cuando algo funciona como debe. Esto ya de por si hace que automáticamente sea tu coche favorito, por muy buenos que sean los otros.

Pero eso no es lo mejor, viniendo del mundo del software, donde nada está acabado, es reconfortante hacer algo y terminarlo. Una vez aprietas el último tornillo ya está, todo listo, está terminado, el producto está completo. En mundo del software es un fractal, siempre puedes seguir cambiando y añadiendo de forma sencilla, no existen las limitaciones de las leyes de la física.

Es muy reconfortante ver un producto terminado. Podría ser mejor, podría mejorarse el diseño, pero alguien en un momento determinado lo cerró y es lo que salió. Nadie más va a cambiarlo, es lo que hay. El mundo del software nunca nada está terminado y no deja de ser una fuente de frustración constante. Imagino que los diseñadores de cosas ligadas a leyes de la física sueñan con tener la elasticidad del software, pero como dije antes, las restricciones a veces son buenas.

A veces que el mundo del software no se respeta a si mismo, y a su trabajo, lo suficiente. Todo es de usar y tirar.

Building a simple SQL Agent from Scratch

More people are talking about AI Agents than actual agents exist. And likely the ratio of builders to people talking about building is 1:10000 and still here I am talking about agents, so looks like I’m contributing with more shit. I hope not but you know, I’m just a guy with a blog.

(BTW, if you want to read the code with a better formatting, just read the post here)

I wanted to create a really simple SQL Agent to teach myself how to do it, no libraries to simplify the process, just a bash script using the llm cli tool. I don’t actually like bash so much but it’s somehow limited so that allows me to focus on the actual problem.

The basic algorithm to generate a working SQL based on a user question would work like (python-ish):

``chat = LLM(system="You are an expert SQL query generator, write the SQL given the prompt.")
answer = chat(prompt)
while not is_correct(answer):
    answer = chat(answer + ".Please fix the SQL query.")
print(answer)
``

So, in theory, I’d just need to write a function (is_correct) that tells the model if the SQL is rigth, and the initial prompt. In theory.

Ok, so let’s build a super simple agent that write SQL given a prompt. For simplicity the is_correct function will return “OK” if the SQL runs. It’s is not a good criterion but it’s a good starting point to understand the dynamics. I’m using DuckDB (a embedded analytics database I’m testing lately) as runtime (so I don’t need to run a fully fledged database server)

1. Generate a SQL query for a given prompt

``LLM="llm -m gemini-2.0-flash-exp"
prompt="You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. Generate a SQL query for: $1"
flags=""

while true; do
    # Generate SQL using LLM
    SQL=$($LLM $flags "$prompt")
    # Remove any ``` markers from the SQL
    SQL=$(echo "$SQL" | sed 's/^```sql$//g' | sed 's/```$//g' | sed 's/^```//g')
    
    # Try executing the SQL with DuckDB and capture the error output
    ERROR=$(echo "$SQL" | duckdb 2>&1)
    if [ $? -ne 1 ]; then
        echo "*** DONE!"
        echo "$SQL"
        break
    else
        echo "*** Invalid SQL generated, retrying..."
        echo "*** Error: $ERROR"
        # Include the error in the next LLM prompt
        prompt="Previous attempt failed with error: $ERROR Please fix the SQL query."
        flags="-c" # to tell LLM to continue the conversation
        sleep 1
    fi
done

``

It kind of works. If you test with prompts that don’t require any table, it gives you pretty decent SQL queries.

``./sqlagent_1.sh "generate a the fibonacci series"
./sqlagent_1.sh "generate the game of life"
``

For the game of life needs a few iterations but that’s ok, it’s a recursive query. I coded that query myself a few years ago and it took me an afternoon.

But if we ask for stuff like

``./sqlagent_1.sh "generate a histogram with table http_requests"
``

It fails. It does because we are asking to do something that can’t do as the http_requests table doesn’t exist. Humans are constanty asking for stuff that is not possible, so we need a way to stop the agent to try. Is there a way so the LLM can say “enough is enough” and stop trying?

To be honest, sometimes it finds the way and generates things like:

``SELECT
    CASE 
        WHEN request_time < 1 THEN 1
        WHEN request_time < 2 THEN 2
        WHEN request_time < 3 THEN 3
        WHEN request_time < 4 THEN 4
        WHEN request_time < 5 THEN 5
        WHEN request_time < 6 THEN 6
        WHEN request_time < 7 THEN 7
        WHEN request_time < 8 THEN 8
        WHEN request_time < 9 THEN 9
        ELSE 10
    END AS bucket,
    count(*) AS count
FROM (SELECT random()*10 as request_time FROM range(100))
GROUP BY bucket
ORDER BY bucket;
``

Step 2: trying to stop the agent

So I tried adding a “If you unable to fix it, just return select 'STOP'” to the prompt but it does not work :)

``prompt="Previous attempt failed with error: $ERROR Please fix the SQL query. If you are not able to fix it, just return `select 'STOP'`."
``

But being more explicit and setting the prompt to:

``prompt="Previous attempt failed with error: $ERROR Please fix the SQL query. If you can't find the tables just return select 'STOP'."
``

it works (added the iteration number as debugging info)

``-- [ITERATION 0] --------------------------------------
*** Invalid SQL generated, retrying...
*** Error: Catalog Error: Table with name http_requests does not exist!
Did you mean "pg_sequences"?
LINE 5:     http_requests
            ^
-- [ITERATION 1] --------------------------------------
*** DONE!

SELECT 'STOP'
``

Step 3: using some the data inside the database

Generating a query that do not read any data is not really useful, so let’s add a step where we pass the duckdb database to the agent so it can use the data inside it.

There are just two changes to the agent: 1) we pass to the prompt a list of columns for each table with the type (using information_schema.columns table) 2) we pass the database to the agent so it can use the data inside the database.

In this case I’m using a duckdb database I generated with all the views I track on this website. It looks like this

``✗ duckdb test.db -c "describe web_requests" 
┌─────────────┬─────────────┬─────────┬─────────┬─────────┬─────────┐
│ column_name │ column_type │  null   │   key   │ default │  extra  │
│   varchar   │   varchar   │ varchar │ varchar │ varchar │ varchar │
├─────────────┼─────────────┼─────────┼─────────┼─────────┼─────────┤
│ timestamp   │ UINTEGER    │ YES     │         │         │         │
│ session_id  │ VARCHAR     │ YES     │         │         │         │
│ action      │ VARCHAR     │ YES     │         │         │         │
│ version     │ VARCHAR     │ YES     │         │         │         │
│ payload     │ VARCHAR     │ YES     │         │         │         │
└─────────────┴─────────────┴─────────┴─────────┴─────────┴─────────┘
``

The code:

``#!/bin/bash

LLM="llm -m gemini-2.0-flash-exp"

DB=$1

# Get the list of columns for each table
tables=$(duckdb $DB -c "select table_name, column_name, data_type, is_nullable  from information_schema.columns")

prompt="You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. The list of columns for each table is:\n $tables. Generate a SQL query for: $2"
flags=""

ITERATION=0
while true; do
    # Generate SQL using LLM
    echo "-- [ITERATION $ITERATION] --------------------------------------"
    SQL=$($LLM $flags "$prompt")
    # Remove any ``` markers from the SQL
    SQL=$(echo "$SQL" | sed 's/^```sql$//g' | sed 's/```$//g' | sed 's/^```//g')
    
    # Try executing the SQL with DuckDB and capture the error output
    ERROR=$(echo "$SQL" | duckdb $DB 2>&1)
    if [ $? -ne 1 ]; then
        echo "*** DONE!"
        echo "$SQL"
        res=$(duckdb $DB -c "$SQL")
        SQL=$($LLM $flags "the query ran successfully, check the results and return 'select 'STOP' if you think is that what the initial prompt asked for. Otherwise, return a new the SQL query.")
        echo "$res"
        SQL=$(echo "$SQL" | sed 's/^```sql$//g' | sed 's/```$//g' | sed 's/^```//g')
        if [[ "$SQL" == *"STOP"* ]]; then
            break
        fi
    else
        echo "*** Invalid SQL generated, retrying..."
        echo "*** Error: $ERROR"
        # Include the error in the next LLM prompt
        #prompt="Previous attempt failed with error: $ERROR Please fix the SQL query. If you are not able to fix it, just return `select 'STOP'`."
        prompt="Previous attempt failed with error: $ERROR\n Please fix the SQL query. If you don't find the tables just return select 'STOP'."
        flags="-c" # to tell LLM to continue the conversation
        sleep 1
    fi
    ITERATION=$((ITERATION + 1))
done

duckdb $DB -c "$SQL"
``

When running it, it can generate queries but the results are not good.

``✗ ./sqlagent_3.sh test.db "visitors per day"
-- [ITERATION 0] --------------------------------------
*** Invalid SQL generated, retrying...
*** Error: Catalog Error: Scalar Function with name date does not exist!
Did you mean "datesub"?
...
                    ^
-- [ITERATION 8] --------------------------------------
*** DONE!

SELECT CAST(TO_TIMESTAMP(timestamp/1000) AS DATE) AS day, COUNT(DISTINCT session_id) AS visitors
FROM web_requests
GROUP BY day
ORDER BY day;
┌────────────┬──────────┐
│    day     │ visitors │
│    date    │  int64   │
├────────────┼──────────┤
│ 1970-01-21 │     7502 │
└────────────┴──────────┘
``

I see many ways to fix this:

Run the query and ask the LLM if it thinks it correct based on the initial prompt.

Give more info about the data (like a sample) so in the case of the example, it can actually see how the timestamp look like

Put humans in the loop so it can give more context, save the context for future runs and so on. Actually that’s what people are doing, check this example from Uber on how they do it: QueryGPT.

I tested 1 (as you see) and it does not work well, and tested 3 that, of course, works.

And this could go on and on, but I think I now understand how to build an super simple SQL Agent and I hope you did too.

Building a simple SQL Agent from Scratch

There are more people talking about AI Agents than actual agents. And likely the ratio of builders to people talking about building is 1:10000 and still here I am talking about agents, so looks like I’m contributing with more shit. I hope not but you know, I’m just a guy with a blog.

I wanted to create a really simple SQL Agent to teach myself how to do it, no libraries to simplify the process, just a bash script using the llm cli tool. I don’t actually like bash so much but it’s somehow limited so that allows me to focus on the actual problem.

The basic algorithm to generate a working SQL based on a user question would work like (python-ish):

chat = LLM(system="You are an expert SQL query generator, write the SQL given the prompt.")
answer = chat(prompt)
while not is_correct(answer):
    answer = chat(answer + ".Please fix the SQL query.")
print(answer)

So, in theory, I’d just need to write a function (is_correct) that tells the model if the SQL is rigth, and the initial prompt. In theory.

Ok, so let’s build a super simple agent that write SQL given a prompt. For simplicity the is_correct function will return “OK” if the SQL runs. It’s is not a good criterion but it’s a good starting point to understand the dynamics. I’m using DuckDB (a embedded analytics database I’m testing lately) as runtime (so I don’t need to run a fully fledged database server)

1. Generate a SQL query for a given prompt

LLM="llm -m gemini-2.0-flash-exp"
prompt="You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. Generate a SQL query for: $1"
flags=""

while true; do
    # Generate SQL using LLM
    SQL=$($LLM $flags "$prompt")
    # Remove any ``` markers from the SQL
    SQL=$(echo "$SQL" | sed 's/^```sql$//g' | sed 's/```$//g' | sed 's/^```//g')
    
    # Try executing the SQL with DuckDB and capture the error output
    ERROR=$(echo "$SQL" | duckdb 2>&1)
    if [ $? -ne 1 ]; then
        echo "*** DONE!"
        echo "$SQL"
        break
    else
        echo "*** Invalid SQL generated, retrying..."
        echo "*** Error: $ERROR"
        # Include the error in the next LLM prompt
        prompt="Previous attempt failed with error: $ERROR Please fix the SQL query."
        flags="-c" # to tell LLM to continue the conversation
        sleep 1
    fi
done

It kind of works. If you test with prompts that don’t require any table, it gives you pretty decent SQL queries.

./sqlagent_1.sh "generate a the fibonacci series"
./sqlagent_1.sh "generate the game of life"

For the game of life needs a few iterations but that’s ok, it’s a recursive query. I coded that query myself a few years ago and it took me an afternoon.

But if we ask for stuff like

./sqlagent_1.sh "generate a histogram with table http_requests"

It fails. It does because we are asking to do something that can’t do as the http_requests table doesn’t exist. Humans are constanty asking for stuff that is not possible, so we need a way to stop the agent to try. Is there a way so the LLM can say “enough is enough” and stop trying?

To be honest, sometimes it finds the way and generates things like:

SELECT
    CASE 
        WHEN request_time < 1 THEN 1
        WHEN request_time < 2 THEN 2
        WHEN request_time < 3 THEN 3
        WHEN request_time < 4 THEN 4
        WHEN request_time < 5 THEN 5
        WHEN request_time < 6 THEN 6
        WHEN request_time < 7 THEN 7
        WHEN request_time < 8 THEN 8
        WHEN request_time < 9 THEN 9
        ELSE 10
    END AS bucket,
    count(*) AS count
FROM (SELECT random()*10 as request_time FROM range(100))
GROUP BY bucket
ORDER BY bucket;

Step 2: trying to stop the agent

So I tried adding a “If you unable to fix it, just return select 'STOP'” to the prompt but it does not work :)

prompt="Previous attempt failed with error: $ERROR Please fix the SQL query. If you are not able to fix it, just return `select 'STOP'`."

But being more explicit and setting the prompt to:

prompt="Previous attempt failed with error: $ERROR Please fix the SQL query. If you can't find the tables just return select 'STOP'."

it works (added the iteration number as debugging info)

-- [ITERATION 0] --------------------------------------
*** Invalid SQL generated, retrying...
*** Error: Catalog Error: Table with name http_requests does not exist!
Did you mean "pg_sequences"?
LINE 5:     http_requests
            ^
-- [ITERATION 1] --------------------------------------
*** DONE!

SELECT 'STOP'

Step 3: using some the data inside the database

Generating a query that do not read any data is not really useful, so let’s add a step where we pass the duckdb database to the agent so it can use the data inside it.

There are just two changes to the agent: 1) we pass to the prompt a list of columns for each table with the type (using information_schema.columns table) 2) we pass the database to the agent so it can use the data inside the database.

In this case I’m using a duckdb database I generated with all the views I track on this website. It looks like this

✗ duckdb test.db -c "describe web_requests" 
┌─────────────┬─────────────┬─────────┬─────────┬─────────┬─────────┐
│ column_name │ column_type │  null   │   key   │ default │  extra  │
│   varchar   │   varchar   │ varchar │ varchar │ varchar │ varchar │
├─────────────┼─────────────┼─────────┼─────────┼─────────┼─────────┤
│ timestamp   │ UINTEGER    │ YES     │         │         │         │
│ session_id  │ VARCHAR     │ YES     │         │         │         │
│ action      │ VARCHAR     │ YES     │         │         │         │
│ version     │ VARCHAR     │ YES     │         │         │         │
│ payload     │ VARCHAR     │ YES     │         │         │         │
└─────────────┴─────────────┴─────────┴─────────┴─────────┴─────────┘

The code:

#!/bin/bash

LLM="llm -m gemini-2.0-flash-exp"

DB=$1

# Get the list of columns for each table
tables=$(duckdb $DB -c "select table_name, column_name, data_type, is_nullable  from information_schema.columns")

prompt="You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. The list of columns for each table is:\n $tables. Generate a SQL query for: $2"
flags=""

ITERATION=0
while true; do
    # Generate SQL using LLM
    echo "-- [ITERATION $ITERATION] --------------------------------------"
    SQL=$($LLM $flags "$prompt")
    # Remove any ``` markers from the SQL
    SQL=$(echo "$SQL" | sed 's/^```sql$//g' | sed 's/```$//g' | sed 's/^```//g')
    
    # Try executing the SQL with DuckDB and capture the error output
    ERROR=$(echo "$SQL" | duckdb $DB 2>&1)
    if [ $? -ne 1 ]; then
        echo "*** DONE!"
        echo "$SQL"
        res=$(duckdb $DB -c "$SQL")
        SQL=$($LLM $flags "the query ran successfully, check the results and return 'select 'STOP' if you think is that what the initial prompt asked for. Otherwise, return a new the SQL query.")
        echo "$res"
        SQL=$(echo "$SQL" | sed 's/^```sql$//g' | sed 's/```$//g' | sed 's/^```//g')
        if [[ "$SQL" == *"STOP"* ]]; then
            break
        fi
    else
        echo "*** Invalid SQL generated, retrying..."
        echo "*** Error: $ERROR"
        # Include the error in the next LLM prompt
        #prompt="Previous attempt failed with error: $ERROR Please fix the SQL query. If you are not able to fix it, just return `select 'STOP'`."
        prompt="Previous attempt failed with error: $ERROR\n Please fix the SQL query. If you don't find the tables just return select 'STOP'."
        flags="-c" # to tell LLM to continue the conversation
        sleep 1
    fi
    ITERATION=$((ITERATION + 1))
done

duckdb $DB -c "$SQL"

When running it, it can generate queries but the results are not good.

✗ ./sqlagent_3.sh test.db "visitors per day"
-- [ITERATION 0] --------------------------------------
*** Invalid SQL generated, retrying...
*** Error: Catalog Error: Scalar Function with name date does not exist!
Did you mean "datesub"?
...
                    ^
-- [ITERATION 8] --------------------------------------
*** DONE!

SELECT CAST(TO_TIMESTAMP(timestamp/1000) AS DATE) AS day, COUNT(DISTINCT session_id) AS visitors
FROM web_requests
GROUP BY day
ORDER BY day;
┌────────────┬──────────┐
│    day     │ visitors │
│    date    │  int64   │
├────────────┼──────────┤
│ 1970-01-21 │     7502 │
└────────────┴──────────┘

I see many ways to fix this:

  1. Run the query and ask the LLM if it thinks it correct based on the initial prompt.
  2. Give more info about the data (like a sample) so in the case of the example, it can actually see how the timestamp look like
  3. Put human in the loop so it can give more context, save the context for future runs and so on. Actually that’s what people are doing, check this example from Uber on how they do it: QueryGPT.

I tested 1 (as you see) and it does not work well, and tested 3 that, of course, works.

And this could go on and on, but I think I now understand how to build an super simple SQL Agent and I hope you did too.

I love SQL

I used to dislike SQL. I learned to deal with databases using ORMs and when I needed to open psql to understand what was going on I felt like I was losing my time dealing with SQL. Why would someone want to deal with SQL when having a simple API in the language you are working with?

After some time I moved to the database infra space (as CTO of a company dealing with hundreds of postgres databases) and I started to work with SQL every day. It was not that bad and I learned to appreciate it little by little. And I finally loved it, I have done all sorts of crazy stuff with it, like rendering my company logo, recreating a game of life simulation and starting a company (that raised +50M) where the only interface is SQL.

Don’t get me wrong, I don’t think ORMs are useless, quite the opposite, I think they are good and developers should use them (they help you to avoid making mistakes) but that shouldn’t stop someone from learning SQL. You are going to find it somewhere in your professional career, there are many systems that only talk SQL (actually all the database systems end up talking SQL, hello MongoDB). And being honest, any developer can learn the basics of SQL in a few hours. That plus a few LLM prompts and you are good to go for 90% of the use cases. Learning a little bit of database internals is also good, that’ll put you in the top 1% of developers worldwide.

But there is one thing about SQL that amazes me: the runtime. It’s so simple, a simple text language, you send it over the wire and boom, you have results. No npm install, just a simple language, focused on a simple (but powerful) task, that lived many iterations of databases and systems over 50 years (!!!). That’s hard to beat, I don’t know so many designs that survived that long.

You have local runtimes (sqlite, duckdb), you can install postgres, mysql… you can go to neon and get a postgres database in a few milliseconds. You don’t even need data locally, you can query data in S3 and other places, you can even query data coming from other processes, it’s just amazing how simple it is. There is no other programming language with this simplicity.

I know SQL is not perfect when you need to work with complex logic. You can always twist it but you end up with crazy 2000 line SQL queries. In 20 years working with it I only found a few cases where it gets actually unmanageable to run data operations but many times it’s easy to use any other procedural language. You need to like functional programming as well, but you get used to thinking in that way after a while.

I’ll probably end my career writing SQL or talking to an LLM that writes SQL :)

Sin mirar atrás

Sabéis ese tipo de pique entre personas, no diría sano pero no del todo dañino, que te permite llevarte bien aunque ambos sabéis que nada te alegraría más que ganarle al otro. Es un poco secreto para nadie, igual que cuando hay tensión sexual no resuelta, pero mal.

Esto pasa entre empresas también. Hace unos cuantos años competíamos en el mercado de los mapas Carto y Mapbox. Las dos con un origen similar, haciendo algo muy parecido y peleando por los mismos clientes. Lógicamente siempre decíamos que “no competíamos” pero la realidad es que el pique era evidente y cuando una empresa te dice que no compite con otra es que en realidad compite y además está perdiendo.

El pique entre empresas pasa porque la gente dentro está picada, es más que obvio. Además suele ser un tema de testosterona que no beneficia a nadie. Y allí estábamos unos cuantos españolitos picados con unos americanos claramente más listos y más valientes que nosotros. Intentábamos emularles, nunca nos salía tan bien, jugar al juego de otro nunca sale.

Al caso, que me lio. Años después, una vez me di cuenta lo estupido que fui, escribí a uno de aquellos americanos, ya los dos fuera de las respectivas empresas, con el que estaba enganchadísimo para preguntarle como hacían para ser tan putos amos, como cuando el segundo felicita al ganador y de paso deja caer un “cómo lo haces?”

Fui directo y le dije “os teníamos envidia, como lo hacíais ” y aunque esta última parte no la dije, añade el necesario resquicio de envidia que aún siento. No se si sorprendentemente o no, el tío me contestó con una transparencia asombrosa, como si nada hubiese pasado, se ve que ellos no estaban tan picados. Primera lección: hacían lo que les daba la gana sin mirar a los lados, por eso ganaban.

Me comentó algunas cosas que en realidad nos habían pasado a nosotros también, errores (que nosotros vimos como súper aciertos), etc pero la parte más importante es que no les importó tirar lo que no funcionaba. No miraban atrás.

Me voy a parar un momento aquí a definir que es algo que no funciona. A veces, especialmente cuando tienes mucho dinero, te da la ventolera y piensas que algo va a funcionar. Te pones, metes 400 mil euros en personal+AWS y lo pones en producción. Todo el mundo a tope, the next big thing, esto va a ser la polla, imparables. Yo cada vez que lanzo algo escribo al concesionario de Porsche para decirles que vayan calentando. Al cabo de 3 meses tienes 12 clientes pagando lo suficiente poco para que no rente pero no llega como para distinguir la señal del ruido.

Te agarras a un clavo ardiendo porque has metido en un embolado a media empresa y al cabo de unos meses muere por sus propios medios, dejando eso si, un legacy que tienes que mantener por esa docena de clientes. Y tu como un gilipollas mirando (y peleando por) esos $200k de ARR que no te van a solucionar nada (en el contexto de una empresa con inversión de VC, claro)

Y ahí es donde ellos fueron valientes y tiraron abajo, haciendo un pelín de daño inevitable a algunos clientes, lo que no había funcionado.

Probaron, no funcionó, a tomar por culo.

Sabían que o encontraban la forma de hacer que la empresa funcionase o daba igual. Y si vas a tomar una decisión, las medias tintas nunca funcionan. Que fácil parece hacer esto, qué difícil es.

Del mismo modo que a veces tus clientes cambian de proveedor, a veces a ti te toca cambiar de clientes. De hecho, este año he aprendido mucho de lo de tomar decisiones firmes de alguno de nuestros clientes que han decidido no mirar atrás.

SQL is good

I used to dislike SQL. I learned to deal with databases using ORMs and when I needed to open psql to understand what was going on I felt like I was losing my time having to deal with SQL. Why would someone want to deal with SQL when having a simple API in the language you are working with?

After some time I moved to the database infra space (as CTO of a company dealing with hundreds of postgres databases) and I started to work with SQL every day. It was not that bad and I learned to appreciate it little by little. And I finally loved it, I have done all sorts of crazy stuff with it, like rendering my company logo, recreating a game of life simulation and starting a company (that raised +50M) where the only interface is SQL.

Don’t get me wrong, I don’t think ORMs are useless, quite the opposite, I think they are good and developers should use them (they help you to avoid making mistakes) but that shouldn’t stop someone from learning SQL. You are going to find it somewhere in your professional career, there are many systems that only talk SQL (actually all the database systems end up talking SQL, hello MongoDB). And being honest, any developer can learn the basics of SQL in a few hours. That plus a few LLM prompts and you are good to go for 90% of the use cases. Learning a little bit of database internals is also good, that’ll put you in the top 1% of developers worldwide.

But there is one thing about SQL that amazes me: the runtime. It’s so simple, a simple text language, you send it over the wire and boom, you have results. No npm install, just a simple language, focused on a simple (but powerful) task that have lived many iterations of databases and systems. That’s hard to beat.

You have local runtimes (sqlite, duckdb), you can install postgres, mysql… you can go to neon and get a postgres database in a few milliseconds. You don’t even need data locally, you can query data in S3 and other places, you can even query data coming from other processes, it’s just amazing how simple it is. There is no other programming language with this simplicity.

I know SQL is not perfect when you need to work with complex logic. You can always twist it but you end up with crazy 2000 line SQL queries. In 20 years working with it I only found a few cases where it gets actually unmanageable to run data operations but many times it’s easy to use any other procedural language. You need to like functional programming as well, but you get used to thinking in that way after a while.

I’ll probably end my career writing SQL or talking to an LLM that writes SQL :)

LLMs to transform data

I transform data every day and I usually do 2 kinds of transformations, changing the data format so I can use it in a tool (CSV to parquet) or the shape, like running an aggregation so I can understand it. I’m using LLMs more and more for this because it saves me a lot of time (and it’s cool)

I see two approaches for using LLMs to transform data:

Asking to transform the data and output it

`cat file | llm —system “transform the data into <format>”`

Asking to generate code to transform the data

`cat file | llm —system “generate a python code to transform this data into <format> ” > trasform.py && python trasform.py file > transformed`

If you don’t know what the llm command is, please, go to check the fantastic llm cli tool from .

The second one has many benefits:

The code will run way faster, LLMs are still slow compared to regular CPUs

The transformation can be audited and fixed

Let’s test it. I have a file with NMEA records from a GPS. NMEA according to Wikipedia “is a combined electrical and data specification for communication between marine electronics such as echo sounder, sonars, anemometer, gyrocompass, autopilot, GPS receivers and many other types of instruments”. I NMEA was invested today would have been NDJSON but at that time machines were sending data through a 9600 bauds per second comm line so they needed to optimize. Parsing is also super easy (probably they couldn’t afford to spend a lot of code for the parsing) but let’s get back to the transformation thing.

I have some data I got from my car’s GPS (which still sends the info using NMEA these days) in a file, I grep GPRMC sentences (the ones that have the coordinates) and pipe into the llm command (using gemini-2.0 code execution). This would be the command (I shortened it for clarity)

` ✗ head -n 40 cheste_fixed.nmea | grep RMC | llm  -m gemini-2.0-flash-exp -o code_execution 1 --system "write and EXECUTE a program to parse that data and generate ndjson with lat, lon, time and speed attributes. No explanations, just write and execute"
```python
import json
import re
from datetime import datetime, timedelta

data = """
$GPRMC,172633.00,A,3929.0184,N,00037.8573,W,80.7,54.1,160923,,*22
$GPRMC,172634.00,A,3929.0331,N,00037.8280,W,84.0,55.5,160923,,*24
$GPRMC,172635.00,A,3929.0495,N,00037.7985,W,93.5,54.8,160923,,*22
...
"""

def parse_nmea(line):
    match = re.match(r'\$GPRMC,([\d.]+),A,([\d.]+),([NS]),([\d.]+),([EW]),([\d.]+),([\d.]+),(\d+),,', line)
    if match:
      time_str, lat_str, lat_dir, lon_str, lon_dir, speed_str, _, date_str = match.groups()

      time_obj = datetime.strptime(date_str+ time_str.split(".")[0], "%d%m%y%H%M%S")

      lat = float(lat_str[:2]) + float(lat_str[2:]) / 60.0
      if lat_dir == 'S':
          lat = -lat
      lon = float(lon_str[:3]) + float(lon_str[3:]) / 60.0
      if lon_dir == 'W':
         lon = -lon

      speed = float(speed_str) * 1.852

      return {
          "lat": lat,
          "lon": lon,
          "time": time_obj.isoformat(),
          "speed": speed
      }
    return None

parsed_data = []
for line in data.strip().splitlines():
  parsed_line = parse_nmea(line)
  if parsed_line:
    parsed_data.append(parsed_line)

for item in parsed_data:
  print(json.dumps(item))
{"lat": 39.48364, "lon": -0.630955, "time": "2023-09-16T17:26:33", "speed": 149.4564}
{"lat": 39.483885, "lon": -0.6304666666666667, "time": "2023-09-16T17:26:34", "speed": 155.568}
{"lat": 39.48415833333333, "lon": -0.629975, "time": "2023-09-16T17:26:35", "speed": 173.162}
{"lat": 39.48441833333333, "lon": -0.6294983333333334, "time": "2023-09-16T17:26:36", "speed": 177.9772}
{"lat": 39.48466666666667, "lon": -0.6290183333333333, "time": "2023-09-16T17:26:37", "speed": 177.9772}
{"lat": 39.48491666666666, "lon": -0.628515, "time": "2023-09-16T17:26:38", "speed": 179.27360000000002}

`

It sounds like it did the right transformation (indeed, checking the data, it’s accurate). Just in case you are checking the data carefully, speed attribute feels like too high but it’s a car in a race track, so it’s expected.

But how could we make sure it’s doing it right? I’d not trust the transformed data right away but I can use what we have been using in software development for years: tests. So let’s ask the LLM to generate not just the transform, but also the test with the backwards transformation:

`$ head -n 10  ~/Downloads/lap_timer/cheste_fixed.nmea | grep RMC | llm  -m gemini-2.0-flash-exp -o code_execution 1 --system "write and EXECUTE a program to parse that data and generate ndjson with lat, lon, time and speed attributes. Also write the code that transforms the output back to original format and check the transformation back. No explanations, just write and execute"

```python
import json
import pynmea2
from datetime import datetime, timedelta

nmea_data = """$GPRMC,172633.00,A,3929.0184,N,00037.8573,W,80.7,54.1,160923,,*22
$GPRMC,172634.00,A,3929.0331,N,00037.8280,W,84.0,55.5,160923,,*24
$GPRMC,172635.00,A,3929.0495,N,00037.7985,W,93.5,54.8,160923,,*22
$GPRMC,172636.00,A,3929.0651,N,00037.7699,W,96.1,54.8,160923,,*28
$GPRMC,172637.00,A,3929.0800,N,00037.7411,W,96.1,55.5,160923,,*2D"""

def parse_nmea_to_ndjson(nmea_str):
  ndjson_output = []
  for line in nmea_str.strip().splitlines():
    try:
      msg = pynmea2.parse(line)
      if isinstance(msg, pynmea2.RMC):
          time_obj = datetime.combine(msg.datestamp, msg.timestamp)
          ndjson_output.append(json.dumps({
              "lat": msg.latitude,
              "lon": msg.longitude,
              "time": time_obj.isoformat(),
              "speed": msg.spd_over_grnd
          }))
    except pynmea2.ParseError:
      print(f"Could not parse: {line}")
      continue
  return "\n".join(ndjson_output)

def transform_ndjson_to_nmea(ndjson_str):
    nmea_output = []
    for line in ndjson_str.strip().splitlines():
        try:
            data = json.loads(line)
            time_obj = datetime.fromisoformat(data['time'])
            nmea_time = time_obj.strftime("%H%M%S.%f")[:-3]
            nmea_date = time_obj.strftime("%d%m%y")

            lat_deg = int(abs(data['lat']))
            lat_min = (abs(data['lat']) - lat_deg) * 60
            lat_dir = "N" if data['lat'] >= 0 else "S"

            lon_deg = int(abs(data['lon']))
            lon_min = (abs(data['lon']) - lon_deg) * 60
            lon_dir = "E" if data['lon'] >= 0 else "W"
            
            nmea_speed = data['speed']
            
            nmea_str = f"$GPRMC,{nmea_time},A,{lat_deg}{lat_min:.4f},{lat_dir},{lon_deg}{lon_min:.4f},{lon_dir},{nmea_speed:.1f},{nmea_speed:.1f},{nmea_date},,"
            
            checksum = 0
            for char in nmea_str[1:]:
                checksum ^= ord(char)
            nmea_str += f"*{hex(checksum)[2:].upper()}"
            nmea_output.append(nmea_str)
            
        except json.JSONDecodeError:
            print(f"Could not decode json: {line}")
            continue
    return "\n".join(nmea_output)

ndjson_data = parse_nmea_to_ndjson(nmea_data)
print("NDJSON output:")
print(ndjson_data)

nmea_transformed = transform_ndjson_to_nmea(ndjson_data)
print("\nTransformed back to NMEA:")
print(nmea_transformed)

print("\nOriginal NMEA:")
print(nmea_data)

def check_transformation(original, transformed):
    original_lines = original.strip().splitlines()
    transformed_lines = transformed.strip().splitlines()

    if len(original_lines) != len(transformed_lines):
        print("Number of lines do not match.")
        return False
    
    for orig_line, trans_line in zip(original_lines, transformed_lines):
        try:
            orig_msg = pynmea2.parse(orig_line)
            trans_msg = pynmea2.parse(trans_line)
            if not isinstance(orig_msg, pynmea2.RMC) or not isinstance(trans_msg, pynmea2.RMC):
                print("Message type is incorrect.")
                return False

            if not (abs(orig_msg.latitude - trans_msg.latitude) < 0.00001 and
                   abs(orig_msg.longitude - trans_msg.longitude) < 0.00001 and
                   abs(orig_msg.spd_over_grnd - trans_msg.spd_over_grnd) < 0.0001 and
                   orig_msg.timestamp == trans_msg.timestamp and
                   orig_msg.datestamp == trans_msg.datestamp
                   ):
                print(f"Original: {orig_msg}")
                print(f"Transformed:{trans_msg}")
                return False
        except pynmea2.ParseError:
            print(f"Could not parse: {orig_line} or {trans_line}")
            return False
    print("Transformation check passed!")
    return True

check_transformation(nmea_data, nmea_transformed)
ExecuteCode failed: MODULE_NOT_FOUND.
msg_err: Traceback (most recent call last):
  File "<string>", line 3, in <module>
ModuleNotFoundError: No module named 'pynmea2'

```

It fails to run because of the pynmea2 dependency but if you run it locally it manages to do it. So running that self-test gives me some confidence about the transformation function and I’d trust it to be in a pull request.

BTW, using the first approach also work, at least for gemini 2.0 it manages to transform NMEA to JSON, but it takes way more time than generating the script (as expected)

LLMs to transform data

I transform data every day and I usually do 2 kinds of transformations, changing the data format so I can use it in a tool (CSV to parquet) or the shape of it, like running an aggregation so I can understand it. I’m using LLMs more and more for this because it saves me a lot of time (and it’s cool)

I see two approaches for using LLMs to transform data:

Asking to transform the data and output it

cat file | llm —system "transform the data into <format>"

Asking to generate code to transform the data

cat file | llm —system "generate a python code to transform this data into <format>" > trasform.py && \
python trasform.py file > transformed

If you don’t know what the llm command is, please, go to check the fantastic llm cli tool from

The second one has many benefits:

The code will run way faster, LLMs are still slow compared to regular CPUs

The transformation can be audited and fixed

Let’s test it. I have a file with NMEA records. NMEA according to Wikipedia “is a combined electrical and data specification for communication between marine electronics such as echo sounder, sonars, anemometer, gyrocompass, autopilot, GPS receivers and many other types of instruments”. if NMEA was invented today, it would have been NDJSON but at that time machines were sending data through a 9600 bauds per second comm line.

Anyway, I have some data I got from my car’s GPS (which still send the info using NMEA these days), filter RMC sentences (the ones that have the coordinates) and pipe into the llm command (using gemini-2.0 code execution). This would be the command (I shortened it for clarity)

head -n 40 cheste_fixed.nmea | grep RMC | llm  -m gemini-2.0-flash-exp -o code_execution 1 --system "write and EXECUTE a program to parse that data and generate ndjson with lat, lon, time and speed attributes. No explanations, just write and execute"

import json
import re
from datetime import datetime, timedelta

data = """
$GPRMC,172633.00,A,3929.0184,N,00037.8573,W,80.7,54.1,160923,,*22
$GPRMC,172634.00,A,3929.0331,N,00037.8280,W,84.0,55.5,160923,,*24
$GPRMC,172635.00,A,3929.0495,N,00037.7985,W,93.5,54.8,160923,,*22
...
"""

def parse_nmea(line):
    match = re.match(r'\$GPRMC,([\d.]+),A,([\d.]+),([NS]),([\d.]+),([EW]),([\d.]+),([\d.]+),(\d+),,', line)
    if match:
      time_str, lat_str, lat_dir, lon_str, lon_dir, speed_str, _, date_str = match.groups()

      time_obj = datetime.strptime(date_str+ time_str.split(".")[0], "%d%m%y%H%M%S")


      lat = float(lat_str[:2]) + float(lat_str[2:]) / 60.0
      if lat_dir == 'S':
          lat = -lat
      lon = float(lon_str[:3]) + float(lon_str[3:]) / 60.0
      if lon_dir == 'W':
         lon = -lon

      speed = float(speed_str) * 1.852

      return {
          "lat": lat,
          "lon": lon,
          "time": time_obj.isoformat(),
          "speed": speed
      }
    return None


parsed_data = []
for line in data.strip().splitlines():
  parsed_line = parse_nmea(line)
  if parsed_line:
    parsed_data.append(parsed_line)

for item in parsed_data:
  print(json.dumps(item))

{"lat": 39.48364, "lon": -0.630955, "time": "2023-09-16T17:26:33", "speed": 149.4564}
{"lat": 39.483885, "lon": -0.6304666666666667, "time": "2023-09-16T17:26:34", "speed": 155.568}
{"lat": 39.48415833333333, "lon": -0.629975, "time": "2023-09-16T17:26:35", "speed": 173.162}
{"lat": 39.48441833333333, "lon": -0.6294983333333334, "time": "2023-09-16T17:26:36", "speed": 177.9772}
{"lat": 39.48466666666667, "lon": -0.6290183333333333, "time": "2023-09-16T17:26:37", "speed": 177.9772}
{"lat": 39.48491666666666, "lon": -0.628515, "time": "2023-09-16T17:26:38", "speed": 179.27360000000002}

It feels like it did the right transformation (indeed, checking the data, it’s accurate). Just in case you are checking the data carefully, speed attribute feels like to high but it’s a car in a race track, so it’s expected.

But how could we make sure it’s doing it right? let’s use what we have been using in software development for years: tests. So let’s ask the LLM to generate not just the transform, but also the test.

$ head -n 10  ~/Downloads/lap_timer/cheste_fixed.nmea | grep RMC | llm  -m gemini-2.0-flash-exp -o code_execution 1 --system "write and EXECUTE a program to parse that data and generate ndjson with lat, lon, time and speed attributes. Also write the code that transforms the output back to original format and check the transformation back. No explanations, just write and execute"

import json
import pynmea2
from datetime import datetime, timedelta

nmea_data = """$GPRMC,172633.00,A,3929.0184,N,00037.8573,W,80.7,54.1,160923,,*22
$GPRMC,172634.00,A,3929.0331,N,00037.8280,W,84.0,55.5,160923,,*24
$GPRMC,172635.00,A,3929.0495,N,00037.7985,W,93.5,54.8,160923,,*22
$GPRMC,172636.00,A,3929.0651,N,00037.7699,W,96.1,54.8,160923,,*28
$GPRMC,172637.00,A,3929.0800,N,00037.7411,W,96.1,55.5,160923,,*2D"""

def parse_nmea_to_ndjson(nmea_str):
  ndjson_output = []
  for line in nmea_str.strip().splitlines():
    try:
      msg = pynmea2.parse(line)
      if isinstance(msg, pynmea2.RMC):
          time_obj = datetime.combine(msg.datestamp, msg.timestamp)
          ndjson_output.append(json.dumps({
              "lat": msg.latitude,
              "lon": msg.longitude,
              "time": time_obj.isoformat(),
              "speed": msg.spd_over_grnd
          }))
    except pynmea2.ParseError:
      print(f"Could not parse: {line}")
      continue
  return "\n".join(ndjson_output)

def transform_ndjson_to_nmea(ndjson_str):
    nmea_output = []
    for line in ndjson_str.strip().splitlines():
        try:
            data = json.loads(line)
            time_obj = datetime.fromisoformat(data['time'])
            nmea_time = time_obj.strftime("%H%M%S.%f")[:-3]
            nmea_date = time_obj.strftime("%d%m%y")

            lat_deg = int(abs(data['lat']))
            lat_min = (abs(data['lat']) - lat_deg) * 60
            lat_dir = "N" if data['lat'] >= 0 else "S"

            lon_deg = int(abs(data['lon']))
            lon_min = (abs(data['lon']) - lon_deg) * 60
            lon_dir = "E" if data['lon'] >= 0 else "W"
            
            nmea_speed = data['speed']
            
            nmea_str = f"$GPRMC,{nmea_time},A,{lat_deg}{lat_min:.4f},{lat_dir},{lon_deg}{lon_min:.4f},{lon_dir},{nmea_speed:.1f},{nmea_speed:.1f},{nmea_date},,"
            
            checksum = 0
            for char in nmea_str[1:]:
                checksum ^= ord(char)
            nmea_str += f"*{hex(checksum)[2:].upper()}"
            nmea_output.append(nmea_str)
            
        except json.JSONDecodeError:
            print(f"Could not decode json: {line}")
            continue
    return "\n".join(nmea_output)

ndjson_data = parse_nmea_to_ndjson(nmea_data)
print("NDJSON output:")
print(ndjson_data)

nmea_transformed = transform_ndjson_to_nmea(ndjson_data)
print("\nTransformed back to NMEA:")
print(nmea_transformed)

print("\nOriginal NMEA:")
print(nmea_data)

def check_transformation(original, transformed):
    original_lines = original.strip().splitlines()
    transformed_lines = transformed.strip().splitlines()

    if len(original_lines) != len(transformed_lines):
        print("Number of lines do not match.")
        return False
    
    for orig_line, trans_line in zip(original_lines, transformed_lines):
        try:
            orig_msg = pynmea2.parse(orig_line)
            trans_msg = pynmea2.parse(trans_line)
            if not isinstance(orig_msg, pynmea2.RMC) or not isinstance(trans_msg, pynmea2.RMC):
                print("Message type is incorrect.")
                return False

            if not (abs(orig_msg.latitude - trans_msg.latitude) < 0.00001 and
                   abs(orig_msg.longitude - trans_msg.longitude) < 0.00001 and
                   abs(orig_msg.spd_over_grnd - trans_msg.spd_over_grnd) < 0.0001 and
                   orig_msg.timestamp == trans_msg.timestamp and
                   orig_msg.datestamp == trans_msg.datestamp
                   ):
                print(f"Original: {orig_msg}")
                print(f"Transformed:{trans_msg}")
                return False
        except pynmea2.ParseError:
            print(f"Could not parse: {orig_line} or {trans_line}")
            return False
    print("Transformation check passed!")
    return True

check_transformation(nmea_data, nmea_transformed)

ExecuteCode failed: MODULE_NOT_FOUND.
msg_err: Traceback (most recent call last):
  File "<string>", line 3, in <module>
ModuleNotFoundError: No module named 'pynmea2'

It fails to run because of the pynmea2 but if you run it locally it manages to do it. So running that self-test gives us some confidence about the transformation function and I’d trust it to be in a pull requests.

BTW, using the first approach also work, at least for gemini 2.0 it manages to transform NMEA to JSON right, but it take way more time than generating the script (as expected)

The data engineer

I’ve been running a data engineering department for 4 years in my company, and to be honest, I never looked at the actual role of the people I was hiring for that team. They passed a “simple” technical test and that’s it. BTW, any LLM nowadays is capable of solving the test in 3 seconds.

When working on the team, I never assumed they were different from regular developers and all the projects they were involved in had development tasks. For sure they had to understand the data and part of the business rules, but, as I said, nothing different than a regular developer.

You can argue the code (normally SQL + glue python code) and the data size is specific for a data engineer but if you have been working enough time as a developer you probably changed 5-6 times of the kind of tech you use, so again, nothing new.

The only difference I found is how well the good practices we learned over the last 30 years in software development are used. Most of the time version control, testing, continuous delivery, observability, monitoring, and others are just nice to have. BTW, I have this feeling with companies doing work based on LLMs.

And that’s worrying.

Coming soon

This is 🔥 FAILing with data.

Subscribe now

Recortes 2024

El año pasado hice una recopilación, a la que por cierto vuelvo de vez en cuando, de cosas que me han dejado marcado de una u otra forma, así que este año voy a hacer lo mismo. Es curioso como cambia la percepción de algunas cosas con el tiempo.

Para ser honesto, este año no ha habido tantas cosas que se me han fijado, mejor dicho, ha habido muchas cosas, pero no me han dejado tanta huella. No ha sido un buen año en general, muy duro en todos los sentidos, mi crisis de los 40 se está alargando.

Al lio:

The obstacle is the way

Sé que no os gusta nada DHH, pero sigue siendo un referente para mi. No es fácil tomar decisiones y ejecutar como él lo hace. Tampoco lo es cambiar de forma de pensar y no quedar como un gilipollas. Y ahí le tienes, corriendo LeMans24, haciendo Linux más usable para desarrolladores, migrando fuera del cloud o creando su propio kubernetes.

The best SaaS companies just move faster. It gets done this quarter. The feature gets rolled out now, not late in the year. The hires get made this quarter. All of them. This compounds at an epic rate

10 years ago I made the decision to never correct anyone who’s wrong unless I’m paid to do that. Never been happier.

No sé si es que hay un tunero dentro de mi (bueno, sí lo sé) pero no puedo dejar de pensar en este F40 de Liberty walk y puede que este año me haya pasado horas viendo como RWB pega aletas con silicona o soñando con tunear mi lotus.

Como Hideo Kojima diseñaba los niveles de Metal Gear. Siempre fan de sentir como se siente antes de construir.

Our product philosophy is “everything should be instant fun”

Teenage Engineering. Vaya pedazo de frase, me encanta esta compañía: hacen lo que les da la gana, con un estilo brutal. Seguramente esté romantizándoles, pero mira, me da exactamente igual. Creo que la mayoría de cosas que llegan a alguna parte empiezan como un juego, así que no les falta razón. Algo que he aprendido este año es que todo lo de la consistencia y demás, es verdad, pero los humanos nos movemos por gustos (“la cabra tira al monte” decían antes) y no podemos mirar hacia otro lado. Al hilo, del libro “masters of doom”, de la historia de id-software, creadores de doom, quake…

"a society without games is one sunk in the zombie trance of the automaton. ... Games are popular art, col-lective, social reactions to the main drive or actions of any culture...

Hablando de videojuegos, este año escribí al desarrollador de Tomb Raider para darle las gracias. Esto es lo que escribí:

Hey man, just want to say a very late thank you (well, just 28 years late). I have been following you for a while but today I thought I never say thanks, so here I am.

I guess in the ’90s you didn’t have a way to talk your favourite games developers but yesterday I was showing a video of Tomb Raider to my daughter and though “maybe is a good a idea to write Paul Douglas”

I was a huge fan of Tomb Raider, I played it 100 times, I became developer thanks to it, it went to games industry and worked there for several years. Have a good day, keep posting about Tomb Raider.

También hablé más de 1 hora con unos de los desarrolladores de Commandos, el mejor juego español de la historia y uno de mis preferidos de siempre. Me hizo bastante ilusión, fue como cerrar un capítulo vital.

Este artículo del creador de ninja. Seguramente no tengas ni idea de que es ninja, basta con que sepas que es una herramienta clave en el andamiaje que se usa para construir el navegador que estás usando ahora mismo para leer esto. La parte importante es como platea la filosofía del proyecto. He dicho ya que me encantan los proyectos con una filosofía clara, un poco contraituitivos y con algo de alma?

Queriendo respetar las individualidades , acabamos uniformizando las formas de entrenamiento (…) si metemos a todos en el mismo envase, se uniformiza tanto que luego echamos en falta la diferencia. En el fútbol y en procesos educativos pasa lo mismo: queremos personas que cumplan órdenes, que realicen procesos pero luego decimos ‘no tiene iniciativa’.

If you are tired, learn to rest not to quit

Visto en un documental de Kilian Jornet, hacian unas tomas en su colegio (con una filosofía bastante interesante por cierto) y tenían un cartel con ese mensaje, que parece ser una frase de Bansky. Me lo aplico cada día. La parte de “aprender” es importante, he descubierto que yo descanso mucho más “soltando” ciertas cosas que parando de hacer. Hacer me da la vida, no tanto el estar sin hacer nada que parece ser el estado ideal de mucha gente. Estoy pensando en ponerlo en casa para mi hija, aunque para cansarte, antes tiene que coger el hábito de trabajar, así que tal vez tenga que esperar.

La paz no se encuentra en ningún sitio salvo dentro de uno mismo. Uno puede estar en paz en plena guerra, ese es el gran secreto. La gente que cree que hay un sitio que te da paz está equivocada, la paz nace del corazón

Inspiration is for amateurs

How do you change your mindset? Do something.

Go to the gym, take a walk, or do an activity you love.

Connect with others who share similar values or encourage you to become better.

Volunteer your time, start a new project, or try a new hobby.

Reject negative stereotypes that suggest you’re too old or it’s too late.

Changing your mindset isn’t easy, but it is free. And that’s a great place to star improving today so you can live longer tomorrow

Arnold Schwarzenegger. Tiene una fantástica lista de correo donde te da unos consejos de salud que pintan bien, que por supuesto no hago ni puto caso, y además te vende sus cosas. Me encanta porque me llegan correos con su nombre y siempre que abro el correo y veo uno digo “coño, me ha escrito Schwarzenegger”.

Esta entrevista a Patty McCoord sobre recursos humanos es de los mejores podcasts que he escuchado. Es bastante bruta, tan bruta que hasta me lo ha parecido a mi, que mira que soy radical. Este podcast lo escuché mientras corría y casi pierdo la vida al meterme en una pista que daba a un barranco y no me di cuenta. Puede ser que la adrenalina del momento me haya dejado marcado el podcast y en realidad no sea tan bueno.

Pero siendo sincero, el podcast que más me ha gustado no ha sido técnico, ha sido esta entrevista a Mauricio Wiesenthal. No he leído ninguno de sus libros, dudo que lo haga, y su forma de hablar es un tanto particular, pero engancha.

La historiaza del año ha sido el backdoor de XZ. Seguramente no te acuerdes pero alguien estuvo contribuyendo a un proyecto open source, usado a su vez por otros usados por millones de personas, para ganarse la confianza de los creadores, hacerse con el control y meter un backdoor. La parte no confirmada, pero que le da un aire de “Netflix series”, es que parece que todo apunta a que hay un gobierto detrás de todo este tema.

Pensaba que la casa perfecta no existía. Hasta este año.

Health is wealth. Put your fitness and nutrition first. The infrastructure on top of which you build your relationships and businesses.

Un compañero, de esos jóvenes, con pelo largo, con un flow que flipas, de los que para ellos siempre ha habido AVE y la expo de Sevilla es algo que pregunta a chatGPT, me hizo saber sobre Recurse center. Me encanta la idea de irse unas semanas a hackear lo que te de la real gana en un entorno que lo favorece. Mataría por volver a tener las responsabilidades de hace 20 años y meterme ahí. De hecho, cuando el board me largue de Tinybird me voy a montar uno de esos en España, lo sabe Dios.

Barefoot Navigator introduces us to a unique take on navigation - using the skills of the ancients and technology-free techniques, we learn how to navigate using the sun, sea, wind and stars, and even the flight patterns of ocean birds.

The Barefoot Navigator “Wayfinding with the Skills of the Ancients”. Este año, por mera casualidad, estuve unos 45 minutos en un pequeño bote navegando 100% a vela. Quizá era el cansancio que tenía después de nadar todo el día, pero me dejó marcada la paz de las olas golpeando el bote. El fulano que conducía el barco era un free rider de mucho cuidado y me encantó que tuviese este libro en el barco. Perdonad este momento #clubprozac

The designer republic, diseñadores de Wipeout, un juego de carreras futurista que lo petó en los noventa, se marcan esta troleada explicando el porqué no estuvieron implicados en el diseño de la siguiente versión del juego. Tener principios es lo que tiene. Si eres un fan de la saga, este libro es una delicia.

Este documental sobre la selección de hockey sovietica. Es una mezcla entre política y cultura de equipo. A mi el hockey y la política me importan relativamente poco, pero lo que sí me encanta es ver como diferentes formas, que parten del culturas diferentes, de aproximar el mismo deporte puede hacer que cambie tanto el resultado. Lo veo a diario en el mundo del software.

Al hilo de hacer las cosas de forma diferente. Otra compañera, sí, de esas jóvenes y con flow, y que además hace unas recopilaciones parecidas a esta de vez en cuando, me pasó esta, para mi, una maravilla de artículo donde un ex empleado de Palantir cuenta como funciona. Una aproximación diferente a la mayoría de empresas, un poco radical y no sin polémica, pero ahí está. Me encantan estas romantizaciones empresariales aún sabiendo la mierda que pasa por detrás.

HOW TO SUCCEED IN MRBEAST PRODUCTION es una auténtica obra maestra de “libro” escrito por MrBeast, al parecer el Youtuber más exitoso de todos los tiempos. No tengo datos, pero tampoco tengo dudas de que ha sido “cancelado” por lo que dice y por como lo dice. El texto está empujado al barranco, escrito con rabia, claramente le sale de dentro, no ha perdido tiempo ni en cambiar la fuente por defecto. Es de los mejores manuales que he leído para hacer que un negocio vaya bien. Es duro, pero es que así son los negocios, el resto son los mundos de Yupi que nos han vendido. Recomiendo este análisis de Simon Willison.

I finalmente

Esto es lo que más me ha marcado este años: los primeros emails de mi hija que ha aprendido a escribir este año.

Learnings after 4 years working with +50 companies on data engineering projects

During the first 4 years of Tinybird (the company I founded) I’ve been helping our customers on the technical side (pre and post sales). I’ve probably talked to more than 100 companies and actively helped +50, ranging from those with just a few employees and Gigabytes of data to top companies in the world with Terabytes.

Tinybird (bear with me, this is not a post about the product) helps solve specific projects, and most of the use cases we had to deal with were not just about building a data platform but also refactoring what the company already had to meet real-time requirements. I think helping these companies change their mindset has saved them millions of dollars a year.

Some clarifications before I start: when I talk about real-time, people think about Kafka, Spark, Flink, etc. But reality means “what you were doing before, but actually fast” or “what you were doing before, but without having to go take a coffee when you run the pipeline”. I like to call it “high performance data engineering”. It usually means:

  1. Lots of data (more than dozens of millions of rows a day with a few years of history, usually Terabytes)
  2. Low latency end to end (so no or lightweight ETL)
  3. Sub second queries, usually <100ms
  4. Reasonable costs (similar to or lower than traditional ETL)

Some practical learnings, in no particular order:

I learned a lot more things about people itself, but that’s another story.

El prisas

No puedo asegurarlo pero tampoco tengo muchas dudas que la gente que trabaja en mi equipo piensa que soy gilipollas por “apretar” todo el rato con las fechas de entrega. Cada nueva cosa que arrancamos siempre digo lo mismo: “si podemos tener algo visible mañana mejor que pasado, pero lo ideal serían un par de horas”, “si dudas, mejor avanza” y otra serie de cosas que intento transmitir, habitualmente con poco éxito, aunque no será pos ser el brasas.

Cualquiera que trabaje en el mundo del software sabe por mucho que aprietes, da igual, tarde o temprano terminas inviertiendo el tiempo que se necesitaba. Como todo lo bueno, y no es diferente en esta industria, necesita mucho tiempo. Entonces, por qué soy el gilipollas que aprieta con el plazo, que negocia cada paso del proyecto, que pide buscar compromisos en la “calidad” todo el rato ? es muy fácil: en un producto como en nuestro la mayor parte del tiempo no sabes bien lo que estás resolviendo, la falacia de que sabes dónde tienes que ir es la peste de la industria del software, especialmente en el mundo startup.

Nosotros no somos una empresa que haga “commodity”, estamos haciendo algo que no es lo que hace la industria pero un poco diferente, tratamos de simplificar algo que lleva 40 años haciéndose de la misma forma. Las bases de datos han llegado a un punto donde ya no hay mucha mejora relativa a la mejora del hardware y nuestro plan es hacer que las cosas sean más fáciles. Así que si crees que sabes lo que realmente estás haciendo en un entorno así es que o confías mucho en ti o eres demasiado “junior” (odio este concepto pero lo pongo por hiriente)

“Apretar“ no es más que una forma de poner lo que estás haciendo delante del usuario (bueno, y delante tuyo también, que a veces es importante ver la mierda que has hecho) lo antes posible. Esto no es nuevo, es lo que llaman feedback rápido; en este caso trato de que sea fugaz, visceral, como cuando un músico saca la guitarra y toca, sin importar los fallos, un poco desafinado, lo que luego será una obra bien tocada, arreglada y perfectamente producida. La esencia es la misma, la canción la vas a reconocer, y si te gusta en crudo, te va a gustar “a lo grande”. El mejor ejemplo actual de esto es Tesla, el Cybertruck a nivel de standard de calidad de fabricación es absurdo, pero seguramente se dieron cuenta pronto que esa parte no es la importante (recomiendo este video donde explican el drama en detalle)

Cuesta, no es trivial plantear la mínima expresión de producto, desarrollar sabiendo que dejas cadáveres por el camino, mucha gente ni sabe ni quiere hacerlo pero es fundamental: si tardas 3 meses en poner en algo producción, has consumido el 15-20% de lo que dura una ronda de financiación de una startup, solo tienes 6 balas y te aseguro que 6 intentos son demasiado pocos. Y el desarrollo es lo de menos, es toda la cadena de marketing, ventas, etc que viene detrás tuyo. Del mismo modo, este tipo de “features” no se pueden desarrollar usando un proceso habitual, necesita que haya poca gente tomando decisiones, que al resto del equipo le “moleste” un poco, necesita que la política y las opiniones no estén en el camino (y ahora entiendes el porqué de la innovación en las grandes empresas)

La literatura no sirve aquí, el proceso tampoco, lo único que sirve es llegar cuanto antes para ver lo que hay y volver rápido a contar lo que has encontrado. Bueno, en realidad la literatura del software nos lleva diciendo años que tenemos que iterar rápido, pero nos empeñamos en montar castillos en el aire para intentar solucionar un problema demasiado complejo para arreglarlo proceso.

Por último, no entiendo a la gente que no disfruta del subidón de tener prisa por poner algo en producción :)

Sobre raíles

Ayudo a hacer los deberes a mi hija de 6 años, esas cosas que la modernidad nos ha traído. Mis padres nunca me ayudaron y así creo que debería ser, no va a haber nadie para sacarles las castañas en 10 años. Es de las cosas más aburridas que alguien puede hacer, ver como alguien las pasa putas para hacer una suma trivial o leer una frase con 5 palabras de 6 letras cada una es horroroso y si tienes poca paciencia te llevan los mismos demonios. Sé que son 6 años, pero aún así duele.

No todo es malo (no soy tan mal padre) tiene cosas interesantes. Ver como un humano aprende cuando viene de la nada es absolutamente maravilloso. No tener ni la menor idea de las reglas establecidas, unido a que un niño de 6 años es como un adulto pero borracho todo el día, hace que salgan por peteneras continuamente, enlacen temas totalmente descorrelados, entienden diferente. Los problemas suelen ser cerrados. Marca la respuesta correcta. Dos más tres. Une las relacionadas. Nada se deja al azar. Tiene sentido, si la cantidad de atención que un niño es reducida, imaginate si además no le pones puertas al campo.

El problema de esto es que durante toda la etapa educativa se mantiene así. Vas al examen, te ponen un problema y te quejas si “este contenido no lo hemos dado en clase” en el mejor de los casos. Solo hay una cosa peor que esto, son los exámenes tipo test, que parece más un sudoku que algo que demuestre que sabes de lo que estoy hablando. Seguramente tenga sentido, ahorra tiempo de corrección y uniformiza la forma de medir a todos los alumnos. Nadie puede destacar en los suyo, todos en la media, tema resuelto. Desconozco si el cambio del sistema universitario hace unos lustros mejoró esto, pero no me da la impresión.

Y el tema es que el mundo real no es así. Para luchar contra esto le digo a mi hija que cuando no sepa algo o dude ponga una nota al margen con lo que ella piensa. Son notas muy ingenuas pero por lo menos deja una pequeña puerta abierta a que razone, a que no haya una sola forma de hacer las cosas y abre una puerta a la conversación con el “otro lado”. No diré que no cuesta hacerle escribirlas, pero es seguramente lo único que saque de beneficio.

Llegamos a la edad adulta y hemos vivido en un mundo donde todo son preguntas cerradas y respuestas que no se pueden salir del guion, en el momento que hay folio en blanco o el problema que queremos resolver es muy abierto, tan abierto que ni siquiera la propia definición del problema puedes darla por completa, estamos absolutamente vendidos.

Y toda esta explicación para llegar a los procesos de selección en las empresas. En el proceso de contratación que sigo, hay siempre una prueba técnica. Esta prueba (por aquí un ejemplo) es una frase vaga con un problema muy mal definido y cuales van a ser los criterios de valoración. El principal es sobre el proceso de decisión, no tanto el resultado.

La mayoría de la gente va sobre railes. Resuelve el problema sin hacer ni una sola pregunta, sin hacer “challenge”, como si no se pudiese cambiar nada, como si no hubiese margen para negociar y entender las razones. No preguntan o preguntan para responder y no para entender. No se dan cuenta que resolver un problema es un proceso de iterar con otra gente, de acotar el problema hasta que acorrale a la solución. A veces no hay solución y hay que llegar a un compromiso. Nada de esto se enseña en el proceso educativo donde todo tiene su respuesta y lo tenemos metido hasta el mismo bulbo raquídeo, al lado de la función de respirar.

Intentando simplificar la vida de mucha gente en la etapa de aprendizaje, en realidad les estamos complicando la vida. Prefiero que mi hija suspenda en un acto de “rebeldía” a que se acostumbre a no tener una visión crítica o pensar, como dicen ahora, “fuera de la caja”.

La última bala

Mi padre a mi actual edad ya estaba enfermo. En realidad lo estuvo desde que tomó su primera gota de alcohol, pero a sus 42 ya tenía todo firmado y listo para zarpar. Aún así el cáncer tardo otros 3 largos años en pasar a recogerle, es increíble lo que aguanta un cuerpo humano.

Durante los últimos meses él era consciente de que se moría, no parecía importarle, es como si ya estuviese preparado para eso. En ese tiempo tuve la única conversación verdaderamente profunda de nuestra vida: se despidió y me pidió perdón por ser un mal padre. Realmente lo fue, no era falsa modestia, pero no deja de honrarle, podría haberse ido sin decir nada.

Por esto y otros antecedentes similares en la familia no pasa un mes sin que piense en el momento de que algún médico, después de una prueba sin mucha importancia me diga que ha visto algo raro y no hace falta que os cuente lo que pasa después. Se puede decir que llevo unos 25 años preparándome para ese día.

Aún no estoy enfermo -aparentemente- así que es probable que viva más que mi padre, pero no puedo dejar de pensar en que puede que no me queden muchas balas que disparar. Ya lo sabía de hace tiempo, pero llegar a los 40 te debe activar meter un x10 en la sensibilidad de estas cosas.

Me encanta lo que hago y tengo una sensación entre rabia y pena al pensar que me quedan muy posiblemente pocos tiros que pegar. Incluso aunque vivas hasta jubilarte, el tipo de cosa que hago ahora (empresas de tecnología) no suelen ser para señoros de 55 años. No digo que no lo puedas hacer, seguro que sí, pero si estás leyendo esto y tienes 30 años, la energía se acaba y a los 40 ya no te sale de las narices hacer según que cosas. Y te lo dice alguien que come mierda a paladas a diario. Por otro lado la cantidad de tiempo que tienes que invertir en seguir bien crece mucho, salvo que Dios te haya tocado con la varita mágica, y eso reduce aún más el tiempo efectivo.

Todo esto hace que me pregunte casi a diario si lo que hago es lo que debería estar haciendo, si estar moviendo datos de un lado para otro va a mover alguna aguja o si no estaré cambiando mi tiempo por un puñado de dólares sin propósito alguno. Imagino que a todo el mundo le llega un punto en la vida en el que se hace esta pregunta, aquí lo dejo yo por si a ti te ha pasado, que sepas que no eres el único.

Lógicamente a nivel personal hay otras miles de cosas que me matan pero no has venido aquí a que te cuente mi vida.

16 milisegundos

Tenía unos 20 años y frecuentaba canales de IRC en el irc-hispano -el discord de antes, de cuando internet no estaba optimizado- de programación de videojuegos y 3D. Uno de los chavales allí me mandó una demo, era un patito de juguete modelado en 3D girando con iluminación “por pixel”. A día de hoy eso te lo programas en 10 minutos con cualquier motor 3D y chatGPT, pero en aquel momento era bastante impresionante, teniendo en cuenta que por aquella época no tenía tarjeta gráfica. Agradezco enormemente que Unai aguantase mis gilipolleces de novato y tuviese la paciencia para hacerme algo de caso.

Algo en mi interior me empujó a ponerme a trabajar para aprender a hacer tal maravilla y me metí en la programación de videojuegos y 3D de lleno. Desde pequeño me encantaron los videojuegos, al nivel de que mi primera gran “travesura” fue gastarme 13 mil pesetas (unos 300€ hoy) en una Master System sin decir nada a mis padres. No fue sencillo, en mi pueblo no podías comprarla, así que me tocó engañar a una persona para que me la comprase en “la gran ciudad”. Nunca más he vuelto a ser tan bueno engañando a alguien, ni siendo comercial como ahora. Los incentivos, bien diseñados, son maravillosos.

Perdonad, no me lio más. Cuando programas un videojuego la pantalla se tiene que actualizar unas 60 veces cada segundo para que parezca fluido, es decir, cada 16.6 milisegundos tienes que hacer los cálculos para que todo lo que está en la pantalla se mueva y se dibuje.

En términos absolutos, para una CPU actual, 16 milisegundos es la enternidad, pero la realidad es que a día de hoy la mayoría de desarrolladores no son capaces de devolver una respuesta a una llamada sencilla de un navegador web en 10 veces ese tiempo, cosa que es un órden de magnitud más sencillo (y estoy siento generoso para que los desarrolladores web no se sientan heridos)

El hecho de tener esa restricción pone el listón muy alto desde el principio. No puedes pensar en solo resolver algo además, por defecto, tiene que ser rápido o no va a funcionar. Eso hace que aprender a programar sea especialmente difícil, es el campo de entrenamiento más duro posible. Seguramente la gente que haga High Frequency Trading esté en una situación similar, pero dudo que nadie se ponga de niño a programar HFT por gusto.

Terminé haciendo 3D y videojuegos durante los primeros años de mi carrera. No me pagaban mucho, pero me daba igual, era mi sueño y cada uno pone a sus sueños el precio que quiere. Bueno, no solo eso, además hacía videojuegos y demos (algunas de ellos, además, con una restricción de 4kb de tamaño total) en mi tiempo libre, que seguramente debería haber invertido en hacer deporte y conocer gente (*)

Al cabo de los años, cuando la vida te lleva a otras industrias y ves que la gente no se ha formado con esa restricción, que nunca han tenido que hacer alto con limitaciones de tiempo, no te encaja su forma de plantear las soluciones, todo es un desperdicio. Cuesta entender el porqué esa gente hace las cosas sin pensar en que tiene que estar dentro de esos 16ms. En realidad ves como la mayoría de gente ha vivido la mayor parte de su vida laboral en vacas gordas que es de las peores cosas que te pueden pasar a nivel profesional. La industria del software está un par de órdenes de magnitud en eficiencia de donde debería estar, incluso después de 20 años de locura.

Lógicamente al cabo de los años uno se acostumbra a la buena vida y te olvidas de ser eficiente alegremente. La parte buena es que has vivido en el infierno, sabes como llegar y que puedes quedarte allí a vivir cómodamente. Así que si tienes la oportunidad, búscate tus “16 milisegundos” particular.

Mis libros favoritos

Muchos libros pasan por una vida pero muy pocos se quedan. No tenía una lista y para ser un personaje notable hay que tener una, así que ahí va, estos son los que me han marcado de una u otra forma:

Los cómics de “Asterix y Obelix” Uderzo & Goscinny . Me encantaba la historia de unos señores muy raros ganando a los grandes con su fórmula mágica, sin mucha organización, sin teatro, sin ser muy sofisticados. Me quedaba siempre impresionado como con dos trazos eran capaces de definir un movimiento y una expresión. Durante una época quise ser dibujante de comics, gané un premio pero luego me di cuenta que no era lo mio.

“El universo” de Isaac Asimov: lo leí cuando tenía 16 años o algo así. Era un libro de bolsillo que tenía las páginas ya amarillas en los bordes. Lo empecé a leer buscando respuestas, tuve una crisis importante al darme cuenta de que no podía explicar en qué lugar estaban los humanos, no podía entender (y sigo sin hacerlo) que el universo sea “infinito”. Me quedé alucinado con las explicaciones de cómo hace ya unos miles de años eran capaces de medir distancias interplanetarias con palos, matemáticas básicas y mucha inteligencia (los fundamentos siempre son un buen fondo de armario) No encontré la respuesta a lo que buscaba.

“Historia del tiempo” de Hawking: lo leí después del de Asimov me reventó mi adolescencia con el tema de la demostración que hace de que Dios no puede existir. Tampoco encontré la respuesta.

“The pragmatic programmer” David Thomas & Andrew Hunt: es de los mejores libros de programación nunca escritos, atemporal, buenos consejos, al grano. Cuando lo lei consiguió dar sentido a muchas de las cosas que instintivamente creía.

“The ghost in the wires” de Kevin mitnik: el libro no dejan de ser anécdotas pero me marcó el hecho de ver que ilusos y controlables somos los humanos.

“Grushenka· (Anónimo): un libro erótico que vaya usted a saber cómo cayó en mis manos pero era, desde luego, menor de edad. No se si lo leí demasiado pronto o demasiado tarde. No recuerdo tampoco si era un buen libro o no, pero me dejó especialmente marcado. Debe ser como un chaval de 13 años que encuentra PornHub (aunque seguramente el libro es un poco más real) por casualidad.

“How to build a car”, Adrian Newey: me encanta la tecnología llevada al extremo y en cierto modo me recuerda a mi carrera profesional (salvando las distancias, tristemente). No deja de ser una biografía que si no te gusta la F1 seguramente pierda el 90% del encanto, pero puede ser interesante aunque no la sigas.

“Visual display of quantitative information”, Tufte: si escribo un libro sobre datos (y no lo descarto) me gustaría que fuese así. Es un libro de teoría y fundamentos, bien escrito, buenos ejemplos y encima en con edición física brutal. Bueno, quizá mi libro sería una mezcla entre este y “The laws of simplicity” the Maeda.

“Getting real”, DHH & Jason Fried: me lo leí cuando estaba en pleno apogeo con Agroguia (una empresa que fundé hace 18 años) y que resonaba cada párrafo que leía. Un poco desactualizado pero el mejor libro si estás haciendo el 0 a 1 de una empresa. Aviso: soy muy fan de 37signals y eso puede que este libro tenga una evaluación un tanto inflada

“Creativity inc” Amy Wallace and Edwin Catmull: es el mejor libro de gestión empresarial nunca escrito y de paso te cuenta una historia increíble. Me ha marcado especialmente porque Toy Story tuvo un impacto grande, pensar en cómo era posible hacer algo en 3d con un PC era magia (aún no sabía nada de programación en aquella época). Leer la historia lo hace más épico si cabe. La frase del libro:

Getting the team right is the necessary precursor to getting the ideas right. It is easy to say you want talented people, and you do, but the way those people interact with one another is the real key. Even the smartest people can form an ineffective team if they are mismatched.

“The baby owners manual”, Louis Borgenicht M.D, Joe Borgenicht : ayudó a calmar la ansiedad de tener un ser a tu cargo los primeros días de su vida y hay pocas que ayuden más en ese momento. La mejor parte es que te enseñan cómo hacer un burrito con tu bebé, mano de santo.

Seguramente haya más, algunos libros del instituto los recuerdo con especial cariño, algunos que te han regalado, otros que nunca terminaste porque te daban vergüenza ajena (como 4-hour-work-week), los que te compraste pero que nunca vas a leer o los que te gustaría que te gustasen (como la novela o los clásicos en mi caso)

Recortes del 2023

Algunos recortes del año, dicho de otra forma, las cosas que he leído que tras 12 meses siguen resonando en mi cabeza, sin orden alguno, con una mezcla entre lo personal y lo profesional, con sus respectivos comentarios.

Image

· fuente. Serán los últimos coletazos de la crisis de los 40, será que estoy cambiando, pero este tipo de contenido me resuenta más. Será momento de retomar a Coelho?

· High variance management - define claramente que hay dos tipos de situaciones que gestionar, las de velocidad de crucero o en las que tienes que ir a la selva, rescatar a los rehenes y salir de allí sin conocer el terreno. Creo que define bien mi estilo de gestión (por decir algo), de muy alta varianza, o mejor dicho, en la línea del desastre.

· Jean-Michel Lemieux (ex CTO shopify y otras muchas cosas interesantes) escribe

hey managers, going into the weeds is not the problem staying in the weeds is the problem never going into the weeds is fatal

Exactamente eso, mucha gente se empeña en llamar micromanagement a la forma de defender cierta incompetencia. No conozco a nadie competente que no te mande a la mierda si te metes donde no te llamen o que no esté dispuesto a hacer lo que le salga de las narices si cree que es lo correcto.

· Understanding DeepMind’s Sorting Algorithm - Justine Tunney es de las figuras, creo, más relevantes en aproximar el software de una forma diferente. Tuvo que venir a explicar como lo que DeepMind no hizo.

· Esta delicia de artículo donde Signal (la app de comunicación segura) hace un análisis de costes a nivel de ingeniería que debería ser obligatorio en todas las empresas

· Esta historia sobre el niño que mandó una carta a Francisco Ibañez pidiéndole consejo para ser dibujante de tebeos. Qué maravilla, cada párrafo, aunque explicando cosas muy concretas, es una lección [de vida]. Pego la imagen por si se pierde el tuit. Seguramente me llega al corazón especialmente porque pasé mi infancia entre Asterix y Mortadelo, solo pueda dar las gracias a la fantástica biblioteca pública que tenía mi pueblo.

Image

· El CEO de snowflake es un lebrel de cuidado, por ejemplo con esto argumetando que los CEOs (y creo que cualquier puesto de gestión) no puede no estar empujando. Este podcast tambien es MUY bueno.

· Startup Restructuring 101. Es un artículo que como fundador te pone en tu sitio. Este año, cuando Silicon Valley Bank se fue a tomar por culo, nosotros teníamos “solo” un par de kilos allí, aunque gran parte del resto estaba en otro banco que iba a ser uno de los siguientes en caer si nadie arreglaba aquello. Recuerdo el sábado por la tarde como abrimos una excel y empezamos a ver como podríamos hacer sobrevivir la empresa con solo el dinero que sabíamos “seguro” no íbamos a ver esfumar. Un ejercicio brutal. Por otro lado, me encanta el término “mokita”, lo uso a menudo.

The truth we all know but agree not to talk about - Mokita, in New Guinean

· Una de las mejores formas de negociar es “decir la verdad” (fuente) Especialmente personas no especialmente espabiladas (como un servidor) a veces ir de frente es la mejor estrategia.

· Hacer cosas inútiles no es tan inútil como parece. Soy un gran aficionado a buscar “aire” para hacer cosas a priori poco útiles.

· Esta entrevista al CEO de teenage.engineering me ha dejado pensando durante meses, seguramente muy influenciado desde mi posición de fundador. Esta parte especialmente

Do you maintain that same attitude towards risk and creativity nowadays?

— Right after school I started my own business in Gothenburg where I designed record slips. Following that, I moved to Stockholm to work in the advertising world until I was 25. I then started my first company, Acne. At that point, I had been so involved in everything I’ve ever done that I felt almost broken. At Teenage Engineering now we are about 60 and I understand that if you are involved in every little part of the business, it destroys you. So a few years ago I started to rebuild the organisation to essentially shoot me out of daily operations so now I can work on my own projects, and try to stay focused on the things I want to do. 

How has that rebuilding process been?

— I try not to share a vision but work on what may inspire people and teach them values to live a life they are happy with. If you’re not happy working with a specific subject, experiment, do something else

· Esta entrevista a Guenter Steiner. El menda tiene una aproximación a la gestión muy particular y un “me la sopla todo, yo voy a hacer lo que creo que está bien” que me encanta. No puedo dejar de criticar la forma de entrevistas en este podcast, siempre buscando la “lección rápida”, la frase “inteligente”, el topicazo de libro de autoayuda a las “3 cosas que blah blah” , qué forma de no sacar partido a sus entrevistados, qué cascarrabias me estoy haciendo.

· Otra entrevista más, en este caso a Amparo Llanos. Me da paz mental escuchar a esta mujer, irradia inteligencia. Dover para mi ha sido una banda brutal, no solo por su música, todo el producto es bueno: dos mujeres al frente, rompiendo cualquier tipo de esterotipo de cantante, cambiando de estilo musical cuando les dio la real gana y dejando el listón altísimo. De las cosas que no he hecho en la vida y me arrepentiré para siempre es no verlas tocar en directo.

· Dejo, por último, las dos canciones que más he escuchado este año, Ciaccona, interpretada por Alana Youssefian** **y L’Amour toujours tocado en un órgano de iglesia (en lo que parece ser una boda bastante mítica) a 22 dedos.

Y hasta aquí las cosas que se han fijado en mi cerebro, hay otras cientos de cosas, pero estas son las que han quedado.

Mis principios de ingeniería de datos

A menudo pensamos que hay una sola forma buena de hacer las cosas. La realidad nos dice una y otra vez que no es tan importante el camino si no la experiencia que tengas en hacer las cosas de la forma que hayas elegido.

Puedes ir por tu propia vía, tirar por una existente, una mezcla, da igual, la parte interesante no es lo que te da seguir una filosofía, es lo que no te quita, que es el tiempo en estar pensando que tu opción no es la adecuada. Posiblemente no lo sea pero nunca lo sabrás si no profundizas. Y si, insistir en tu propia forma de hacer las cosas ayuda.

Hay gente que se dedica a ir probando cosas sin profundizar y terminan haciendo cosas sin saber muy bien porqué. No se trata de cambiar nunca, se trata de explorar lo suficientemente lejos.

Mi carrera en los últimos años ha estado enfocada en al trabajo con datos, siempre en lo que me gusta llamar “alto rendimiento” y nunca había escrito los principios que sigo. Nunca está de más invertir un rato en explicárselos a uno mismo, por aquí los dejo.

Los datos tienen que servir un propósito.

Cuando generas datos deben tener un fin, y solo se deben generar los datos que van a ser útiles para eso.

No me creo que almacenar datos por almacenarlos sirva para algo, empresas almacenando datos sin valor. Y si el propósito último es almacenarlos, deberían cumplirlo a la perfección, es decir, ocupar poco, no consumir mucho, ser seguros, y sobre todo, ser capaz de eliminarlos cuando ya no sirvan.

Por eso no me creo mucho el “datawarehouse”, la de millones de € que se van en procesar y almacenar datos que nunca se usan es impresionante. Recuerda, la primera regla de cualquier ingeniero de datos es “el mejor dato es el que nunca se escribe, o mejor, nunca se genera”

Siempre que empiezo a diseñar un proyecto de datos lo hago por el mismo sitio: qué problema se quiere resolver, es decir, empezar por el final. Todos los proyectos que he visto donde se empieza por “pongamos tal tecnología”, “vamos a pensar en el modelo de datos y luego ya vamos viendo”, “hagamos un sistema genérico”, “guardamos en JSON y luego ya se verá” terminan mal y/o son carísimos.

Destilar, condensar y explicar los datos es un arte, aunque la mayoría de veces se usa una herramienta de BI.

Cualquier análisis sobre los datos debe ser rápido.

Los humanos funcionamos con el oído, la vista y otros sistemas que son prácticamente instantaneos, así debería ser cualquier cosa en la que una máquina que es mucho más rápida que un humano.

Programé videojuegos y 3D durante una temporada, en un juego tienes que hacer todos los cálculos en 16ms que es lo que dura un frame de una animación para que parezca fluida. De hecho empecé a programar gracias al código fuente del Quake (fue publicado en el 1999) que, aunque accede a una cantidad de información bastante grande, mantenía la velocidad incluso en aquellas máquinas. El código del Quake debería estudiarse en la universidad y dejarnos de tanta “programacion orientada a objetos”

El feedback debe ser instantáneo.

No solo se trata de que los datos sean rápidos, es que no debería pasar mucho tiempo desde que se generan hasta que se usan.

Un examen se puede estudiar todos los días o el último, adivina qué técnica es la mejor para aprender (que no tiene nada que ver con aprobar un examen)

Los datos sobre los datos son tan importante como los propios datos

Cuando las cosas pasan deprisa, tienes que saber qué está pasando, anticiparte, detectar cuando hay un problema, entender o símplemente quedarme mirando una gráfica que se mueve durante minutos por el simple hecho rendir tributo a la cantidad de cosas que tienen que pasar, de forma orquestada, para ver ese numerito en la pantalla.

Sí, es esto lo llamamos metadatos, y al ser meta, todas las reglas anteriores aplican.

Cómo preparo una charla

Siempre pensé que la gente que daba charlas eran expertos en la materia, nadie les podría toser, por eso estaban en un escenario hablando de algo y el resto, abajo, plebeyos, escuchando atentamente.

Si tienes una edad sabes que esto no es verdad, de hecho a veces los que más saben están abajo. Bueno, los que más saben están trabajando.

En cualquier caso, por suerte o desgracia he dado unas cuantas charlas de todo tipo. Me da una vergüenza absurda verme dando una charla, de hecho no vuelvo a ver mis charlas (ni podcast, ni nada donde haga me tenga que ver a mismo) nunca más, lo hecho, hecho está. Aún me asombra que me sigan llamando para hablar.

Dicho esto, dejo por aquí algunas cosas que he aprendido y sigo a la hora de preparar una charla. Tengo que decir que nunca repito una charla, reuso cosas, pero es raro que de una charla dos veces. Dar una charla tiene que ser divertido, tiene que tener un componente de emoción de la primera vez, de preparar la historia, de improvisar sobre lo que planteas.

Algunas reglas

La primera regla que sigo es: si me está llevando demasiado tiempo quiere decir que no sé suficiente de lo que voy a hablar y tiro la charla. Hay demasiadas charlas de mierda con gente que no tiene puta idea de lo que habla. Vale más una charla sencilla de algo que controlas a algo que no tienes ni puta idea, se nota, no te vas a ganar el respeto de nadie

En general si la charla es técnica lleva entre 3-5 veces más prepararla a si es inspiracional (sí, las que sueltas tu movida filosófica quitando cualquier detalle doloroso para dartelas de listo). Si tienes experiencia puedes preparar una charla en 1 hora o menos.

La segunda: lo que interesa es tu punto de vista del tema que hablas. No estás dando una clase de física teórica, estás hablando de tu experiencia y me interesa tu historia, como ves las cosas.

La tercera: para mi una charla tiene que sonar como si no fuese una charla, tiene que sonar a una conversación entre alguien y tú. Tienen que sonar a ti hablando normal, no como si estuvieses dando el tiempo. Esto lo aprendí cuando estaba dando una charla interna dentro de CARTO y un comercial me dijo “por qué ponéis ese tono de gilipollas al dar una charla, hablad normal”

La cuarta y más importante: las historias siempre ganan, tiene que haber un hilo narrativo con sus personajes (yo me escojo a mi familia y a mí mismo) su intro-nudo-desenlace. Personalmente me gusta hacer algo que aprendí en el fabuloso podcast sobre la serie Chernobyl , el director comentaba que para qué hacer esperar, era mejor arrancar con la explosión de la central, así que muchas veces empiezo por el final. BOOM, aquí lo tienes.

Otras cosas que tengo en cuenta:

Cuanto más corta la charla mejor. No hagas ni puto caso a los organizadores, si te dicen 30 minutos, haz 15 o los que creas necesario para contar tu historia, luego ya se verá. Si tardas más, qué va a pasar, van a llamar a la policía para sacarte del escenario? Eso sí, lo bueno si breve…

Añadir toques de humor siempre funciona. Bueno, no siempre, hay a gente que no le gusta, pero pueden irse a otra charla si quieren. Especialmente si intercalas para que la gente no muera. Además te sirve para medir cuanto de atenta está la gente, si se rien, es que va bien.

No seguir las tendencias es mejor. Cambia cosas.

Provocar a la audiencia es otro modo de mantener la tensión.

Yo cojo 3 ó 4 personas de referencia del público y les hablo a ellos. Una vez elegí a uno que se durmió (y luego preguntó durante el turno de preguntas el jeta)

Por ser claro, de lo que preparo luego uso la mitad porque luego improviso, quiero pensar que la improvisación funciona gracias a la preparación.

La parte más importante de todas es, como siempre, a nivel personal

Dar charlas es una forma de conocer gente, una excusa para hablar de algo que os une. Se hacen amigos, se aprende como otros hacen cosas, y das a conocer lo que haces.

Hay gente que te empieza a odiar también, no vayamos a idealizar. Una vez, por razones que no vienen a cuento, pude escuchar comentarios de una charla que di en Barcelona y os aseguro que duelen, especialmente porque los suelen ser personales y no sobre el contenido. Pero cuando subes al escenario, te expones a esto, es la vida. Yo también “odio”visceralmente a gente, sin razón aparente, luego conoces a la persona detrás del personaje y se te pasa.

Estar ahí arriba hace que tengas una oportunidad para poder empezar una conversación casi con casi cualquiera, aprovéchalo.

La respuesta al concurso del Lego de la TGR23

En esta TRG en Tinybird hicimos un concurso en el que tenías que adivinar el tiempo de una query SQL sencilla sobre 1 billón de registros. Si acertabas ganarías un LEGO que seguramente sea más difícil de montar que aprender SQL. El objetivo era no sólamente hacer un concurso estúpido para recoger “leads”, hacer algo que retase a la gente y a su vez sirviese para explicar qué hace nuestra empresa tenía más sentido. Además, si no sabes mucho de datos, te permite aprender.

Antes de escribir nada: es imposible saber el tiempo de ejecución de una query en un sistema donde desconoces todos los componentes. Bueno, aunque los conozcas, es en la práctica imposible. Así que era un juego de un mucho de estimación y otro mucho de suerte

El enunciado era tal que así:

“”” Responde con el tiempo que crees que tarda Tinybird en ejecutar esta query SQL

SELECT 
    toDate(timestamp) AS date, 
    avg(speed) AS avg_speed
FROM rocket_telemetry 
GROUP BY date

La tabla se le añaden 1000 registros por segundo y en el momento del comienzo de la TRG23 tenía 1 billón americano de registros.

HINT: es más rápido de lo que crees. “””

Como ves, sin conocer datos tan básicos como cual es el sistema que corre, como almacena los datos, el esquema de la tabla etc etc es absolutamente imposible. Aún así, vamos a hacer un análisis de como se podría estimar sin ponernos muy técnicos, en términos simples.

Empecemos por los básicos: el tiempo de una query tan sencilla depende del tiempo que tardemos en leer los datos. Esto es así para la gran mayoría de queries que son ejecutadas en nuestras queridas bases de datos.

Así que en el caso que nos atañe tenemos dos columnas, vamos a asumir que son de 4 bytes cada una (un campo Date y otro Float), a la hora de ejecutar la query había unos 1.1b de registros, tenemos un total de ~8.2Gb (=1000000000 * 4 * 2). Mi portátil, macbook pro M1, es capaz de leer aproximadamente unos 3.8gb/s de memoria cuando todos los registros están perfectamente colocados, no tengo ni pajolera de lo que tardará un servidor de google -donde estaba alojada mi cuenta de tinybird- pero entiendo que es algo similar, así que la query debería tardar algo más de 2 segundos.

No tan rápido, en realidad nosotros somos más listos que eso, en la tabla había un timestamp, y en enunciado dice que son unos 1000 registros por segundo. Debería haber usado un timestamp de 64 bits, pero no lo hice (mal), pero en este caso, sabemos que hay 1000 valores seguidos iguales si los vamos colocando en la base de datos según nos los envían.

Y si en vez almacenar el valor, almacenamos la diferencia con el valor anterior? Pues vamos a tener 1 uno, seguido de 999 ceros. No sé a ti, pero a mi me da que eso se puede guardar de una forma eficiente. Alguien ya se dio cuenta hace años he inventó algo llamado RLE compression, que permite reducir la cantidad de información cuando se repiten mucho los valores. En este caso, usé un algoritmo algo más moderno, LZ4. La historia de LZ4 es bastante increíble, os recomiendo este podcast.

Echando cuentas, si guardamos 1 uno y 999 ceros, repetido 60 segundos * 60 minutos * 24 horas son básicamente 1kb que es tamaño que ocupa 1000 valores y otro más para decirle las veces que lo tiene que repetir. En la base de datos que usamos, Clickhouse, ocupa unos 22mb. Lógicamente hay ineficiencias porque el sistema de almacenamiento no está pensado para este caso de uso, ni LZ4 funciona exactamente como he dicho. Aún así, de 4gb a 22Mb podemos decir que hemos hecho un buen trabajo.

Entonces ahora tenemos dos columnas, una de ellas, timestamp, que ocupa 21mb y otra, speed que ocupa 4gb. Dirás, por qué no se comprime esa columna? Pues porque usé un rand() que por definición apenas comprime. La realidad es que usando ZSTD, otro algoritmo de compresión, se queda en aproximadamente 3.8gb. Es importante saber donde es fuerte cada algoritmos de compresión, no tienes que saber los detalles, pero sí tener intuición.

Para terminar, echando cuentas, la query debería tardan aproximadamente 1 segundo, y efectivamente, así era. De las 224 personas que participaron, menos de un 5% dieron una respuesta entre 0.5 y 1.5 segundos, menos de 10% si muevo el rango de 500ms a 3s.

Pero aquí no termina todo, a nada que sepas SQL podrás intuir que para ejecutar esa query no necesitas leer todos los datos todas las veces que ejecutes la query, puedes ir agregando a medida que vienen los datos. SI estás pensando ahora mismo en “pongo un scheduler que se ejecute cada minuto” o “uso spark whatever” o “snowflake blabla” siento decirte que la tecnología ha avanzado en estas últimas 3 décadas y ahora las cosas se pueden en tiempo real. Bueno, procesar 1000 registros cada segundo se puedía hacer hace unos 30 años en cualquier PC, va a hacer 30 años de la release de DOOM, amigos (para los milenials, DOOM era nuestro Fortnite)

Así que si agregas según vienen los datos, necesitas almacenar unos 12 días (aprox 100M de eventos al día son generados a 1000 ev/s) con lo cual necesitas una tabla de 12 filas con 3 columnas: fecha, suma total, count. Eso son unos 144 bytes si usas 4 bytes por cada tipo de dato.

Con ese dato puedes intuir que la query está por debajo de 1 milisegundo casi con total seguridad, lo que se tarde en parsear la SQL y poco más. Si tu sistema tarda en parsear la query y coger 120bytes (~8kb en Clickhouse) más de 1ms, me jode ser yo el que te lo diga, pero hay algo que hay que repensar.

Solo 8 personas respondieron 1ms o menos. De hecho la query total costaba unos 0.8ms, ganó Claudia que puso 0.99 :)

Si has llegado hasta aquí es porque el tema te interesa. Hice un curso de unas 3 horas explicando en detalle todo esto, lo puedes ver por aquí.

Gracias por pasar por el stand de Tinybird, fue un placer hablar con vosotros.