Acerca de Dani

Soy un ingeniero de software actualmente trabajando como desarrollador freelance. Estoy especializado en desarrollo backend aunque me estoy poniendo al día en cosas en las que antes era bueno y que dejé de lado. Me interesan las buenas prácticas en el desarrollo de aplicaciones, el código bien testado, la usabilidad y la accesibilidad del software. En internet, te enseño a programar a través de mi canal de YouTube, makigas.

Prepending `bundle exec` to your command may solve this

Minientrada

¿Sabes cuando usas la doctrina «si funciona, no toques», pero aun así las cosas se rompen por arte de magia de un día para otro?

Bundle tiene este problema a veces, que no he podido depurar pero que no es la primera vez que me ocurre: debido a que pueden coexistir múltiples versiones de una gema en simultáneo, a veces utilizar un shim (o sea, invocar rspec o rails en la consola sin más, sin especificar bundle exec ni usar el binstub) falla, porque no está usando la versión concreta de alguna dependencia.

Después de actualizar unas dependencias en nuestro Gemfile, me empezó a salir este mensaje de error:

/Users/danirod/.rbenv/versions/2.6.7/lib/ruby/gems/2.6.0/gems/bundler-2.2.17/lib/bundler/runtime.rb:302:in `check_for_activated_spec!': You have already activated rspec-support 3.10.3, but your Gemfile requires rspec-support 3.10.2. Prepending `bundle exec` to your command may solve this. (Gem::LoadError)

Se soluciona usando bundle exec rspec en vez de rspec. No es que tenga fobia a escribir bundle exec antes de mis comandos, pero mi sentido común me dice que si día tras día, escribir rspec funciona, si de repente deja de funcionar de un día para otro, el problema no lo tengo yo. Por no ir, no me iba ni el binstub. O sea, bin/rspec también me da el mismo mensaje de error.

A veces cuando me pasan estas cosas hago un pristine, o un cleanup. No piques: es una ruleta rusa. A veces se tira 15 minutos limpiando y reinstalando paquetes para luego seguir fallando al ejecutar. En esta ocasión, he optado por tirar una solución más rápida:

gem uninstall rspec-support
bundle install

Si primero elimino todas las versiones de esta gema que hay en mi ordenador, y luego vuelvo a hacer bundle install, me deberá dejar una única versión instalada: la que pida el Gemfile. Me parece una solución aceptable. Esta es mi workstation, y no uso rbenv con software esencial (si tuviese software instalado desde Homebrew que esté escrito en Ruby, probablemente usaría el Ruby de Homebrew, no rbenv). A lo que quiero llegar es a que no pasa nada si elimino todas las versiones de un paquete y luego reinstalo.

Código sin tests

Cita

El otro día respondí lo siguiente por nuestro Discord en un hilo [1] en el que se debatía si era 100% necesario escribir tests a la hora de escribir un código. Traigo el contenido aquí para no perderlo en el historial y tenerlo a mano.

Yo me voy a tirar a la piscina y voy a decir que sí es posible escribir el código de un proyecto sin tests si lo pruebas a mano según lo estás desarrollando.

Lo que no es posible hacer sin tests es mantenerlo. Porque cuando se hagan cambios en el software ya no va a ser el mismo software que el que has probado a mano cuando lo estabas desarrollando. Tendrías que probar a mano de nuevo todo el programa, y eso puede ser una pérdida de tiempo. ¿Cuándo entra un código en mantenimiento? En cuanto dejas de tener visible ese código después de escribirlo.

Creo que la principal finalidad de los tests desde el punto de vista pragmático, más allá del «virtuosismo» de «soy guay, tengo tests, tengo buen coverage (JA)», es poder automatizar el poder comprobar que el software sigue funcionando bien conforme va pasando el tiempo de una forma automática y no intrusiva. Si hago un cambio de mantenimiento a un módulo de software quiero poder tener mi checklist ya implementada que me cubra todos los casos de uso de ese módulo de software para poder hacer la validación automáticamente y comprobar que no se introducen regresiones, o sea, que no he roto nada inesperado al hacer el cambio. (El software a veces se conecta de formas poco esperadas; cohesión siempre acaba habiendo y las interfaces de un módulo nunca son tan buenas como cabría esperar)

[1] Para que este deeplink funcione hay que haberse unido antes al servidor.

¿Por qué FreeBSD y no (x)BSD?

Minientrada

Por eliminación.

Elegí FreeBSD porque NVIDIA publica drivers para la ABI de FreeBSD. Lo que significa que puedo tener aceleración gráfica y tener mis pantallas con la resolución correcta en vez de usar el driver genérico xorg-vesa. No sirve de nada más porque NVENC no está disponible fuera de Windows y porque no uso FreeBSD para jugar.

Eso es todo. No soy un fanboy de FreeBSD y si mis casos de uso hubiesen sido otros tal vez hubiese ganado otro sistema operativo. Pero ganó este. Los tres sistemas son muy similares entre sí de todos modos.


Otros candidatos:

  • NetBSD: me gusta por su extremada simpleza, hecha para darle una perfecta portabilidad. El lema de NetBSD es “por supuesto que corre NetBSD”, haciendo referencia al hecho de que es posible instalarlo en muchísimas arquitecturas (incluso en Macs de principios de los años 90 o en Play Stations).
  • OpenBSD: lo admiro por su extrema seguridad, pero mis casos de uso con mi ordenador me llevan a admirarlo de lejos. OpenBSD es tan seguro que hace unos años eliminaron el soporte para Bluetooth porque es un protocolo inseguro y su driver no estaba bien mantenido.

SQL: Suma acumulativa

Minientrada

Tengo una tabla como la siguiente:

datetime               points
2021-07-05 10:00:00    1
2021-07-05 10:05:00    1
2021-07-05 10:08:00    -1
2021-07-05 10:10:00    1
2021-07-05 10:11:00    -1

Y quiero sacar las sumas acumulativas. Es decir, no quiero sacar simplemente un SUM(points) y que me devuelva 1, sino que quiero ir viendo, para cada fila, la suma parcial de todas las filas que hay hasta llegar a esa desde el principio.

Por lo que veo en Stack Overflow, una forma universal de hacerlo aunque poco eficiente con tablas muy grandes, es utilizar INNER JOINs aplicados sobre la misma tabla que parcialmente vayan agrupando los resultados por fecha. Me hice algo como lo siguiente en mi caso usando un WITH para filtrar sólo aquellos resultados que realmente quiero sumar y así tratar de hacerlo más eficiente.

WITH points AS (
  SELECT datetime, points
    FROM data
   WHERE target_id = 12345
)
SELECT a.datetime, a.points, SUM(b.points)
  FROM points a
       INNER JOIN points b
               ON b.datetime <= a.datetime;

GitLab vs Gitea

Minientrada

No entiendo por qué GitLab se ha convertido en la implementación de referencia para alojar repositorios con funciones extras como tareas, peticiones de integración o cuentas de usuario. Es lento, dificil de utilizar, dificil de instalar… y además, es un software que se sigue llamando «libre» porque tiene una versión comunitaria, pero que vista la capa de marketing que tiene como producto, no se siente tan libre.

Y sin embargo es lo que plataformas como GNOME, Xfce o Free Desktop han instalado en los últimos años como alternativa a los clientes más duros pero simples como cgit. Una alternativa mucho más ligera que dispone de funciones web es Gitea. Una de las cosas buenas de las aplicaciones web hechas en Go es que igualmente se distribuyen como un binario que se puede arrancar de la forma que haga falta. Configurar un GitLab es absurdamente complicado.

App Nap en Mojave vs Big Sur

Minientrada

El otro día enchufé mi ordenador portátil después de tenerlo dos días desconectado de la corriente pero encendido y en modo suspensión. La batería estaba al 95% de carga. Es curioso porque antes del downgrade, durante los meses en los que usé Big Sur, la batería se me descargaba más rápido en este tipo de casos. Hablamos de una pérdida del 30% o del 40% de la batería en ese mismo intervalo de tiempo.

Me pregunto si habría algún tipo de cambio en App Nap o en algún componente del sistema operativo responsable de esto, que haga que consuma más batería en Big Sur. O si fue algún tipo de bug temporal que ha quedado resuelto en sucesivos parches del sistema operativo. (Sigo sin plantearme actualizar, de todos modos.)

El cacao de los requisitos de Windows 11

Minientrada

No solo mintieron con lo de que Windows 10 iba a ser la última versión de Windows, sino que Windows 11 tiene por el momento una importante subida de requisitos mínimos que ha enfadado a bastante gente. Quizá tenga su explicación lógica: los requisitos mínimos de Windows 10 en 2015 fueron tan bajos que a cualquier máquina compatible con Windows 7 se le ofreció actualizar a la versión 10, y en algún momento habrá que empezar a dejar de lado máquinas de 11 o 12 años de vida. Sin embargo, tal vez el timing no sea el más adecuado para pedirle a gente que se deshaga de hardware perfectamente funcional solo porque no es de la generación de procesadores que a Microsoft le parece bien.

Antes de que Microsoft decidiese tumbar la PC and Health Check bajo la promesa de mejorarla, la descargué y la instalé en mi gaming rig para ver la compatibilidad. Llevaba días escuchando que muchos ordenadores no tan viejos estaban reportando ser incompatibles, pero la herramienta hacía una pésima labor en decir por qué. El primer intento de ejecutarla sobre mi ordenador, comprado hace 7 meses, no me sorprendió del todo.

Interesante.

Hasta que Microsoft arregle su PC and Health Check y haga que muestre un mensaje de error un poco más claro, una forma de ver por qué no es compatible un ordenador con Windows 11 es utilizar WhyNotWin11. Este programa analiza las specs y muestra en detalle todas las specs mínimas para Windows 11 y las comprueba y reporta por separado de forma gráfica.

Foto del GitHub, no es mi ordenador.

Después de una pequeña confusión entre «Windows 11 requerirá activar Secure Boot» y «Windows 11 requerirá ser compatible con Secure Boot» (FreeBSD no utiliza todavía Secure Boot y no me gustaría complicarme el acceso a esa partición, bien gracias), el único problema está en TPM 2.0. Mi Ryzen tiene TPM, pero al ser un ordenador montado, se conoce que no viene activo por defecto en la configuración del UEFI.

Para poder activar TPM tuve que consultar varios vídeos en YouTube que me explicasen cómo utilizar el menú UEFI de mi ordenador, entender por cuál de las docenas de submenús me tengo que meter, y ser capaz de localizar la entrada correcta del menú, que tiene el sensual nombre de AMD CPU fTPM (a veces parece que los nombres de las cosas se eligen dejando caminar un gato sobre el teclado…)

Todo lo que podía pensar mientras hacía esto es cómo un usuario con menos conocimientos (y eso que yo no entiendo de hardware, por eso alguien montó mi ordenador por mí y por eso paralelamente mis otros dispositivos son Apple) iba a ser capaz de sobrevivir a este paso en el supuesto de que sea esto todo lo que tiene que hacer para hacer una máquina compatible con Windows 11.

Después de hacer este paso, la PC and Health reportó que mi máquina ya podría recibir la actualización a Windows 11 (¡gracias!).

Gracias, hombre.

Listar paquetes instalados no automáticamente con pkg

Minientrada

pkg es el gestor de paquetes de FreeBSD. Es una alternativa a ports que permite instalar paquetes adicionales en el sistema que no vienen con el sistema base.

Es posible listar los paquetes instalados utilizando pkg-info(8). Sin embargo, mi principal problema con casi todos los gestores de paquetes es lo mal que diferencian entre paquetes instalados por mi y paquetes instalados como dependencia.

Me interesa sacar una lista de los paquetes verdaderamente instalados por mi. Esos paquetes que puse explícitamente en mi terminal al escribir algún comando pkg install. Por supuesto es importante saber la lista de dependencias que hay instaladas en mi sistema, pero si estoy fabricando una copia de seguridad con el listado de paquetes que he instalado en mi máquina, me parece importante ocultar las dependencias que no haya pedido, porque puede ocurrir que en el futuro esa dependencia también cambie con respecto a mis programas y deje de ser necesaria (o aparezcan otras nuevas).

pkg-query(8) es el front-end para la base de datos en la que pkg guarda su configuración y estado de los distintos paquetes. Lo bonito es que cuando se instala un paquete, sí recuerda si se ha hecho a consecuencia de ser una dependencia, o porque se haya pedido explícitamente. Así que se le puede pedir un filtro que deje fuera los paquetes que se han instalado automáticamente para listar sólo los que sí se han instalado intencionalmente con el siguiente comando: pkg query -e '%a = 0' '%n'.

danirod@beastie:~ % pkg query -e '%a = 0' '%n' | head
ImageMagick7
alacritty
base64
claws-mail
compton
doas
dsbmixer
en-freebsd-doc
feh
firefox

jq: filtrar índice inverso por valor de un subcampo

Minientrada

Amo jq, pero a la vez daría lo que fuera por poder aprender del todo su lenguaje de consulta porque a veces se me atraganta.

Sería muy fácil con jq hacer una búsqueda en un array de objetos en función de lo que vale uno de los campos de cada objeto, ¿verdad? Hablo de un array como este:

[
  {
    "nombre": "Carmen",
    "departamento": "Ventas",
    "superior": "Ana"
  }
]

Pero ¿qué pasa si en vez de un array tengo un objeto a modo de índice inverso? Me refiero a la clásica de fabricar con este array un objeto donde como valores tengo cada uno de los valores del array, pero como clave tengo algún identificador concreto de cada objeto. Al fin y al cabo, buscar items en una lista tiene complejidad O(n), pero hacerlo en un mapa tiene complejidad O(1). Es más rápido hacer lista["Ana"] que hacer lista.find(i => i.name == "Ana").

{
  "Carmen": {
    "nombre": "Carmen",
    "departamento": "Ventas",
    "superior": "Ana"
  }
}

El secreto está en usar to_entries y from_entries para convertir entre objeto y array de entries. jq 'to_entries | from_entries' < input.json se anula entre sí. Entre ambas sentencias podemos plantar un select para hacer un filtro de aquellos elementos del nuevo array generado por to_entries que filtre por una condición concreta para .[].value.whatever.

jq 'to_entries | select(.[].value.superior == "Ana") | from_entries' < empleados.json

La salida de to_entries es un array, por lo que cuando se hace el select para filtrar elementos que cumplan un criterio, hay que empezar la query con un .[], para poder hacer introspección sobre cada elemento del array. Realmente no es muy complejo, pero me lo apunto porque me ha costado un poco de ensayo y error dar con el orden correcto en el que tengo que poner los puntos y los corchetes.

Abrir en una ventana nueva, Slack Edition

Minientrada

Hablemos de Slack. Slack tiene un menú de acceso rápido que permite saltar rápidamente a otra conversación pulsando ⌘K. Se trata de otro acto de respeto hacia los power users que consideran que levantar las manos del teclado para agarrar el teclado, desplazarlo a la barra lateral, localizar la conversación y hacer clic en ella es una pérdida de tiempo pudiendo teclear ⌘-K-D-A-N-I-Enter. Muy estilo Vim. Es otra forma diferente de mostrar respeto.

Pero sigue sin ser abrir en ventana nueva. No puedo trabajar con dos conversaciones a la vez. A lo sumo con una conversación abierta en la pantalla principal y un hilo abierto en la barra lateral. No puedo consultar información o un código que me hayan enviado por una conversación mientras la discuto o hablo sobre ella por otra conversación. Todo lo que puedo hacer es utilizar su versión web desde mi navegador web y abrir múltiples instancias de la misma aplicación web, y aun así estoy condenado a tener en todas las instancias la misma barra lateral y el mismo encabezado en la parte superior.

Ojo, no estoy negando que haya complejidad. Me leí el post que publicaron hace un par de años en su blog de ingeniería. Entiendo que todo es una aplicación React gigante con estados de Redux globales. Aquí sólo me estoy lamentando en voz alta de que hayamos aceptado esta nueva normalidad plagada de aplicaciones no nativas sin considerer este tipo de casos de uso más avanzados.

Vamos, Slack, aquí te dejo una idea de cómo se podría ver. Haría también el concepto de cómo sería ese pop-up: una ventana nueva donde sólo se vea la barra de título del sistema operativo y luego el área principal con la conversación (sin barras laterales ni superiores), pero se acaba mi descanso de la hora de comer y no tengo tiempo de diseñar esa pantalla.

Concepto. Este menú no existe. (Ojalá.)