Si sigues disparándote en el pie, arregla la pistola
- En los equipos suele haber errores recurrentes sobre el sistema, pero muchas veces no se piensa en cómo reducirlos
- En esos casos, lo importante es mejorar el sistema para disminuir los errores
- Experiencia:
- Al desarrollar en iOS con CoreData, las actualizaciones de la UI solo pueden hacerse en el hilo principal
- Los callbacks de suscripción ocurrían tanto en el hilo principal como en segundo plano, y eso causaba problemas con frecuencia
- Los miembros antiguos del equipo ya lo sabían y lo manejaban bien, pero en las revisiones de gente nueva esto salía seguido
- Cuando ocurría un error, se revisaba el reporte de crash y se agregaba
DispatchQueue.main.async
- Para resolverlo, se actualizó la capa de suscripción para que llamara a los suscriptores en el hilo principal. Tomó exactamente 10 minutos.
- Se eliminó toda una clase de crashes y un poco de carga mental
- Cualquiera que lo pensara unos minutos habría visto que era un problema evidente
- Pero como nunca hay un momento natural para resolver este tipo de cosas, duran extrañamente mucho tiempo
- Es decir, si llevas mucho tiempo en un equipo, este tipo de problemas se vuelve parte del fondo
- Hace falta un cambio de mentalidad
- De vez en cuando hay que recordarse que estos problemas pueden resolverse para hacerte la vida más fácil a ti y al equipo
Encontrar el equilibrio entre calidad y velocidad
- Siempre existe un trade-off entre la velocidad de implementación y la confianza en la exactitud
- Hay que preguntarse qué tan aceptable es lanzar un bug en la situación actual
- Si la respuesta no afecta tu forma de trabajar, entonces estás siendo demasiado rígido
- En mi primer trabajo, cuando hacía proyectos de procesamiento de datos, había un buen sistema para reprocesar datos retroactivamente
- El impacto de lanzar un bug era muy pequeño y, en ese entorno, podías apoyarte hasta cierto punto en los guardrails y moverte más rápido
- Tener 100% de cobertura de tests o un proceso extensivo de QA solo ralentizaba el desarrollo
- En mi segundo trabajo, el producto era usado por decenas de millones de personas y manejaba datos financieros de alto valor e información personal identificable, así que los bugs eran críticos
- Incluso un bug pequeño requería un análisis posterior
- Las funciones se lanzaban muy lentamente, pero creo que ese año no saqué ni un solo bug
- En la mayoría de los casos, no estás en una situación como la de esa segunda empresa
- Cuando los bugs no son críticos (por ejemplo, en el 99% de las web apps), es mejor lanzar rápido y corregir rápido
- Así puedes avanzar más que intentando lanzar una función perfecta desde el inicio
El tiempo dedicado a afilar la sierra casi siempre vale la pena
- Es importante dominar tus herramientas
- Debes poder escribir código rápido, conocer los atajos clave y manejar bien el sistema operativo y la shell
- Vas a renombrar cosas, ir a definiciones de tipos, buscar referencias, etc.
- Deberías conocer todos los atajos importantes de tu editor y poder teclear con confianza y rapidez
- También es importante usar eficazmente las herramientas de desarrollo del navegador
- Elegir bien tus herramientas y usarlas con soltura es una gran ventaja
- Una de las mayores green flags que veo en ingenieros nuevos es el interés por elegir herramientas y usarlas con maestría
Si no puedes explicar una dificultad de forma simple, probablemente sea complejidad accidental, y vale la pena resolverla
- Mi manager favorito tenía la costumbre de presionarme cada vez que yo decía que algo era difícil de implementar
- Muchas veces respondía con cosas como: "¿No es solo enviar X al hacer Y?" o "¿No es algo como Z que hicimos hace unos meses?"
- Era una objeción de muy alto nivel, no al nivel real de funciones y clases que yo estaba tratando de explicar
- La visión habitual es que este tipo de simplificación por parte de un manager solo resulta molesta
- Pero, sorprendentemente, en un porcentaje alto de los casos me di cuenta de que gran parte de la complejidad que estaba explicando era complejidad accidental
- Y que, de hecho, resolviendo eso primero, el problema sí podía volverse tan trivial como decía mi manager
- Este tipo de enfoque tiende a facilitar cambios futuros
Intenta resolver los bugs un nivel más abajo
- En vez de corregir un bug solo en la superficie, es importante encontrar y resolver la causa raíz
- Supón que hay un componente de React en el dashboard que maneja un objeto
User tomado del estado del usuario actualmente autenticado
- En Sentry aparece un reporte de bug diciendo que
user era null durante el render
- Puedes agregar rápido
if (!user) return null
- O investigar un poco más y descubrir que la función de logout hace dos actualizaciones de estado separadas
- La primera pone al usuario en
null y la segunda redirige a la página principal
- Si inviertes el orden de esas dos cosas, ningún componente volverá a sufrir ese bug
- Porque dentro del dashboard el objeto de usuario nunca debería ser
null
- Si sigues haciendo el primer tipo de arreglos, todo se volverá un desastre,
pero si sigues haciendo el segundo tipo, terminarás con un sistema limpio y un entendimiento profundo de las invariantes
No subestimes el valor de escarbar en el historial para investigar bugs
- Yo era bastante bueno depurando problemas extraños con herramientas comunes como
println y el debugger
- Por eso no solía mirar mucho git para entender la historia de un bug, pero en algunos casos eso es muy importante
- Hace poco parecía que en el servidor había una fuga constante de memoria, y lo terminaban matando y reiniciando por OOM
- Ya se habían descartado todas las causas plausibles y no podía reproducirse localmente
- Se sentía como lanzar dardos con los ojos vendados
- Al revisar el historial de commits, vi que había empezado después de agregar soporte para pagos de Play Store
- Como eran solo unas cuantas requests HTTP, jamás lo habría buscado ahí
- Resultó que había caído en un loop infinito al obtener el access token después de que expirara el primero
- Puede que cada request solo agregara alrededor de 1kB en memoria, pero si reintentas cada 10ms desde varios hilos, se acumula rápido
- Normalmente algo así habría causado un stack overflow, pero como estaba usando recursión asíncrona en Rust, no ocurrió
- Nunca se me habría ocurrido, pero al mirar un fragmento específico de código claramente relacionado con el problema, de repente se me ocurrió la teoría
- No hay una regla sobre cuándo usar este enfoque
- Se basa en intuición: un tipo distinto de "¿eh?" al leer un reporte de bug puede detonar esta clase de investigación
- Con el tiempo puedes desarrollar esa intuición, pero basta con saber que a veces esto tiene muchísimo valor
- Si el problema encaja, prueba
git bisect
- Cuando tienes un commit que sabes que está mal y otro que sabes que está bien
El mal código da retroalimentación; el código perfecto no. Equivócate del lado de escribir código malo
- Es muy fácil escribir código terrible
- Pero también es muy fácil escribir código que siga absolutamente todas las mejores prácticas
- Tendría que pasar por tests unitarios, de integración, fuzz y mutación, y una startup se quedaría sin dinero antes de terminar
- Gran parte de programar consiste en encontrar el equilibrio
- Si te equivocas del lado de escribir código rápido...
- A veces vas a sufrir por deuda técnica mala
- Vas a aprender que "hay que agregar excelentes tests al procesamiento de datos"
- Porque muchas veces después ya no se puede corregir
- También vas a aprender que "hay que pensar muy bien el diseño de las tablas"
- Porque cambiarlas sin downtime puede ser muy difícil
- Si te equivocas del lado de escribir código perfecto...
- No recibes ninguna retroalimentación
- Todo toma universalmente más tiempo
- No sabes dónde estás invirtiendo bien el tiempo ni dónde lo estás desperdiciando
- Los mecanismos de feedback son esenciales para aprender, pero así no obtienes ninguno
- Aclarando qué significa código "malo"
- No significa: "No recordaba la sintaxis para crear un hashmap, así que usé dos veces el loop interno"
- Significa cosas como:
- En lugar de reescribir la recolección de datos para hacer imposible representar cierto estado, agregas algunas assertions sobre invariantes en unos cuantos puntos clave
- Como el modelo del servidor es exactamente igual al DTO que vas a escribir, simplemente lo serializas. En vez de escribir todo el boilerplate, puedes crear el DTO después si hace falta
- Te saltas escribir tests para estos componentes porque son triviales y aunque tengan bugs no sería grave
Haz que depurar sea fácil
- Con los años he ido aprendiendo muchos pequeños trucos para hacer que el software sea más fácil de depurar
- Si no haces un esfuerzo por facilitar la depuración, conforme el software se vuelve más complejo, terminarás gastando muchísimo tiempo depurando cada issue
- Vas a empezar a temer hacer cambios, porque entender unos cuantos bugs nuevos podría tomarte una semana
- Pon atención al tiempo que se va en configuración, reproducción y limpieza posterior durante la depuración
- Si es más del 50%, deberías buscar cómo hacerlo más fácil, aunque esta vez tome un poco más
- Si todo lo demás es igual, corregir bugs debería volverse más fácil con el tiempo
Cuando trabajes en equipo, pregunta siempre
- Hay un espectro que va desde "intentar resolver todo por tu cuenta" hasta "molestar a tus colegas con preguntas triviales"
- Creo que la mayoría de quienes empiezan su carrera están demasiado inclinados hacia lo primero
- Siempre hay cerca alguien que lleva más tiempo en el codebase, o sabe mucho más de la tecnología X, o conoce mejor el producto, o simplemente tiene más experiencia como ingeniero
- Durante tus primeros 6 meses en cualquier lugar, muchas veces vas a perder más de una hora tratando de entender algo que podrías haber resuelto en unos minutos con una respuesta
- Haz preguntas. El único caso en que hacer una pregunta realmente molesta a alguien es cuando es obvio que tú mismo habrías podido encontrar la respuesta en unos minutos
El ciclo de despliegue importa muchísimo. Hay que pensar con cuidado cómo desplegar rápido y con frecuencia
- Las startups tienen runway limitado y los proyectos tienen deadlines
- Cuando dejas tu trabajo y te independizas, tus ahorros solo te van a durar unos cuantos meses
- Idealmente, la velocidad del proyecto aumenta de forma compuesta con el tiempo, hasta que terminas lanzando features más rápido de lo que imaginabas
- Para desplegar rápido hacen falta muchas cosas
- Un sistema que no sea propenso a bugs
- Tiempos de turnaround rápidos entre equipos
- La voluntad de recortar el 10% de una feature nueva (la parte que consumiría el 50% del tiempo de ingeniería) y la claridad para identificar qué partes son esas
- Patrones consistentes y reutilizables que puedas combinar para nuevas pantallas/features/endpoints
- Despliegues rápidos y sencillos
- Procesos que no ralenticen (tests inestables, CI lenta, linters fastidiosos, revisiones de PR lentas, JIRA tratado como religión, etc.)
- Y un millón de cosas más
- Desplegar lento debería requerir un análisis posterior igual que tumbar producción
- Nuestra industria no funciona así, pero eso no significa que no puedas seguir personalmente la estrella polar de desplegar rápido
6 comentarios
"dispararse en el pie" = ¿significa algo así como meterse en un problema por uno mismo?
Si algo sale mal por un código defectuoso (una pistola rota) y eso causa un problema (dispararte en el pie), la idea es que arregles la pistola.
Qué impacto, es como si hubieran sacado tal cual lo que tengo en la cabeza, wow..
¡Lo leí muy bien!!
Lo leí con gusto.
No soy desarrollador, pero hay muchas partes con las que me identifico.