52 puntos por GN⁺ 2025-02-06 | 6 comentarios | Compartir por WhatsApp
  • Desarrolladores con mucha experiencia compartieron con desarrolladores principiantes su filosofía sobre el desarrollo de software
  • Incluye consejos sobre diversos temas, como evitar una reescritura total (ground-up rewrite), gestión de tiempos, calidad de código, automatización y manejo de casos límite

Evita a toda costa las situaciones en las que una reescritura total (ground-up rewrite) parece atractiva

  • La tentación de una reescritura total aparece cuando la deuda técnica acumulada ya hizo difícil mantener el código existente
  • Hay que detectar con anticipación las señales de alerta cuando se acumula la complejidad del código, como que incluso cambios simples se vuelvan difíciles, que comentar o documentar sea complicado, o que cada vez menos personas entiendan el código central, y buscar soluciones activamente
  • Después de una fase de expansión, es indispensable pasar por una etapa de integración para reducir la complejidad y ordenar la calidad
  • Si una reescritura total se vuelve inevitable, significa que el proyecto ya entró en una fase de riesgo
  • Para reducir ese riesgo, hay que gestionar continuamente la deuda técnica y monitorear con cuidado la calidad del código

Completa el 90% del trabajo en la mitad del tiempo disponible

  • Escribir una primera versión del código y lograr que funcione representa apenas la mitad del trabajo total
  • Las etapas posteriores, como manejar casos límite, pruebas, despliegue, documentación, rendimiento y mantenibilidad, requieren mucho más tiempo del que suele pensarse
  • Hay que dejar suficiente margen para responder a problemas inesperados o al trabajo final de pulido
  • En última instancia, hay que reconocer la brecha entre “hacer que el código más o menos funcione al principio” y “convertirlo en una funcionalidad terminada”, y reflejar eso en la planificación

Automatiza las buenas prácticas

  • Si a los desarrolladores solo se les repite verbalmente o en documentos “esto debe hacerse”, es fácil que ocurran errores
  • Cuando sea posible, es más efectivo forzarlo con pruebas automatizadas o scripts, en formas como “si se viola una regla, el build falla”
  • Incluso las reglas recién introducidas, como APIs prohibidas o comentarios obligatorios, pueden automatizarse gradualmente para reducir errores u omisiones
  • Aun así, no todo puede automatizarse, y reglas demasiado estrictas pueden entorpecer el flujo de desarrollo, por lo que hace falta equilibrio

Considera también los datos extremos (pathological)

  • Evaluar el código solo con entradas normales (golden path) es riesgoso
  • Hay que asumir situaciones problemáticas, como solicitudes que se retrasan o se interrumpen, volúmenes masivos de datos (de cientos de miles a cientos de millones de filas) o cadenas extrañas (demasiado largas, con diagonales o espacios)
  • Prepararse a fondo para los casos límite determina la calidad final del código

Normalmente hay una forma más simple de escribirlo

  • Después de lograr que el código funcione al inicio, conviene volver más tarde con algo de tiempo para mejorarlo y hacerlo más simple y claro
  • Incluso si ya encontraste una “solución que se ve bien”, es importante mantener la actitud de volver a revisar si existe una mejor
  • El proceso de refactorizar código largo y complejo para volverlo conciso mejora la calidad final

Escribe el código de forma que pueda probarse

  • Si se minimizan las interfaces y los efectos secundarios, escribir pruebas automatizadas se vuelve mucho más fácil
  • Si la estructura es difícil de probar, es muy probable que la encapsulación no esté bien resuelta
  • Diseñar la estructura del código de una manera que facilite pruebas concretas ayuda a detectar bugs de forma temprana

Que el código “demuestre” que no tiene problemas no significa que eso sea suficiente

  • Incluso un código que parece estructuralmente seguro puede fallar si cambia el entorno alrededor o la forma en que se invoca
  • Si se trata de código relacionado con seguridad, aunque el punto actual de llamada sea seguro, debe diseñarse considerando la posibilidad de cambios futuros
  • El código debe escribirse para que sea “claramente seguro y que siga siéndolo aunque cambie algo”

Comentarios

  • La frase “No tuve tiempo de escribirte una carta corta, por eso te escribí una larga” se atribuye a Pascal
  • Siempre hay que tener cuidado con los errores off-by-one
  • Agregar una nueva guía al wiki
    • Si dentro de la empresa no existe un sistema bien definido de documentación y compartición de conocimiento, la información termina dispersa y genera confusión
    • Pueden surgir problemas como que la documentación oficial se vuelva obsoleta por pasar por procesos de aprobación, o que existan varios wikis al mismo tiempo y no se sepa cuál es la fuente oficial
    • Funciona bien definir un solo wiki para reunir todo el material, y si faltan partes, que los propios desarrolladores las escriban o las completen mediante ingeniería inversa (reverse-engineering)
    • Si la documentación está bien conectada con otros lugares, como el control de código fuente o los comentarios del código, es más fácil mantener la información actualizada
    • Si las pruebas automatizadas y el entorno de build son complejos o no están expuestos a los desarrolladores, puede darse la situación de que nadie haya ejecutado realmente todas las pruebas
    • Conviene mantener simples los scripts de build de Jenkins, con una forma como cd project; ./build-it, e incluir también ese script en el control de código fuente
    • Compartir con todo el equipo el mismo entorno —por ejemplo, una imagen de máquina virtual o la misma configuración de build— para ejecutar builds y pruebas puede reducir problemas antes de que aparezcan
    • Considerar los casos límite desde la etapa de desarrollo y manejar cosas como destroy_object(foo) de modo que funcione de forma segura incluso si foo es NULL, o hacer que create_object() llame internamente a destroy_object() cuando falle, resulta útil
    • En última instancia, lo importante es que todos los desarrolladores puedan acceder fácilmente a la documentación y al entorno de build/pruebas, y participar en ellos
  • Documentación y pruebas automatizadas: se menciona la importancia de los documentos o wikis para compartir conocimiento, así como la sugerencia práctica de que la configuración de entornos CI/CD como Jenkins pueda compartirse
  • No hay una herramienta de cambio de conducta más efectiva que “causar incomodidad” hasta que la gente memorice la conducta que se espera de ella
  • Así como existe una postura contraria al refrán del ajedrez “si ves una jugada que se ve buena, ejecútala de inmediato”, en programación pasa lo mismo: tiene dos caras.

6 comentarios

 
bbulbum 2025-02-07

> Normalmente hay una forma de escribirlo más simple.

Creo que pensar en una solución más concisa reduce la complejidad del código y de las políticas.

 
kipsong133 2025-02-06

> "Completar el 90% del trabajo total en la mitad del tiempo posible"

La verdad, creo que esto es totalmente cierto. En lugar de intentar implementarlo perfectamente de una sola vez, revisar dos veces rápidamente reduce los errores y también ayuda a gestionar mejor el tiempo.

 
jhj0517 2025-02-06
  1. Automatizar las buenas prácticas (código de pruebas, pipeline de CI/CD)
  2. Es mejor hacer que el código funcione al principio y luego, con algo de margen de tiempo, volver a mejorarlo para que sea más simple y claro
  3. Evita a toda costa las situaciones en las que una reescritura total (ground-up rewrite) parezca atractiva
 
winterjung 2025-02-06

> Siempre hay que tener cuidado con los errores de desfase de uno (off-by-one error)

No sabía qué era un error de desfase de uno, pero al parecer es un tipo de error relacionado con condiciones de límite como for (int i = 1; i < n; ++i) { ... }, for (int i = 0; i <= n; ++i) { ... }.

 
ethanhur 2025-02-06

> Evitar, a cualquier costo, las situaciones en las que una reescritura completa (ground-up rewrite) parece atractiva.

Parece ser una trampa que impide que avance el negocio de muchas empresas de IT, y cuando hay mucha deuda técnica acumulada, las organizaciones de ingeniería que no pueden resolverla (o no quieren hacerlo) tienden a ver una reescritura completa como algo atractivo.

Coincido profundamente con la idea de los autores de que, si no existe una razón realmente convincente, una reescritura debe evitarse tanto como sea posible.

 
GN⁺ 2025-02-06
Opiniones en Hacker News
  • El desarrollo de software es un proceso iterativo de intentar y aprender. Los desarrolladores con experiencia profundizan su comprensión escribiendo código y haciendo pruebas, y aprenden mucho, pero eso no suele verse bien en reuniones o planes. A los desarrolladores junior les cuesta más entregar código listo para producción y tienden a resistirse al trabajo que termina descartándose. Si un gerente tiene poca experiencia en desarrollo, puede agravar estos problemas

  • La cita "Perdón por escribir una carta tan larga, pero no tuve tiempo de hacerla más corta" se atribuye al matemático y filósofo francés Blaise Pascal, y transmite la idea de que escribir de forma breve es más difícil

  • La regla 90/50 enfatiza que, para mejorar la calidad del código, hay que dar importancia a las pruebas y al manejo de excepciones. Configurar pruebas automatizadas ayuda a establecer expectativas claras para la base de código

  • La educación en ciencias de la computación suele llevar a escribir código complejo, pero es importante escribir código fácil de leer. Para eso hacen falta buenos nombres para variables y funciones, formato consistente y un diseño modular

  • Un mecanismo llamado Ratchet ofrece un método perfecto para el futuro

  • Intentar generalizar la experiencia y el entendimiento del dominio puede llevar a generalizaciones erróneas. El desarrollo es el arte de gestionar la complejidad, y es importante aprender a través del fracaso

  • La cita "El primer 90% del trabajo toma el 90% del tiempo, y el 10% restante toma otro 90% del tiempo" refleja bien la realidad del desarrollo. Al considerar manejo de excepciones, errores, usabilidad y seguridad, surgen muchas tareas inesperadas

  • El texto de Joel Spolsky advierte sobre los riesgos de reescribir y destaca la sabiduría de DevOps

  • Hay que optimizar la legibilidad del código y reconocer que mientras más tiempo pasa antes de descubrir un bug, mayor es el costo. Es beneficioso preferir principios de programación funcional y usar sistemas de tipos fuertes