Escribir código que sea fácil de borrar
- Cada línea de código implica un costo de mantenimiento. Reutilizar código hace que los cambios sean más difíciles.
- Cuantos más consumidores tenga una API, más código habrá que reescribir al cambiarla.
- Gestionar las dependencias del código es un problema importante en sistemas a gran escala.
Paso 0: no escribir código
- La cantidad de líneas de código no aporta mucha información por sí sola.
- El código que no se escribió es el código más fácil de borrar.
Paso 1: copiar y pegar código
- El código reutilizable puede escribirse más fácilmente más adelante a partir de ejemplos.
- Copiar y pegar código es una forma de evitar dependencias y ganar flexibilidad.
Paso 2: dejar de copiar y pegar código
- Si el código ya se ha copiado y pegado lo suficiente, es momento de extraer una función.
- Conviene crear un directorio
util para guardar varias utilidades en otros archivos.
Paso 3: escribir más boilerplate
- El boilerplate es similar a copiar y pegar código, pero cambia el código en distintos lugares.
- El boilerplate reduce dependencias y aporta flexibilidad.
Paso 4: no escribir boilerplate
- Si hay demasiado boilerplate, conviene envolverlo con una librería que tenga opiniones sobre políticas, flujos de trabajo y estado.
- La relación entre
requests y urllib3 es un buen ejemplo.
Paso 5: escribir grandes bloques de código
- La lógica de negocio se caracteriza por casos excepcionales interminables y hacks rápidos y sucios.
- Es más fácil borrar un gran error que borrar varios errores pequeños.
Paso 6: dividir el código en partes
- Los grandes bloques de código tienen un alto costo de mantenimiento.
- Hay que separar las responsabilidades del código y diseñar módulos considerando qué tan probable es que cambien.
Paso 7: seguir escribiendo código
- Debería ser posible escribir código nuevo de forma independiente al código existente para poder experimentar con ideas nuevas.
- Los feature flags son una forma de poder cambiar de opinión más adelante.
Resumen de GN⁺
- Este artículo explica cómo crear código que sea fácil de borrar al escribir software.
- La clave es reducir las dependencias del código, aumentar la flexibilidad y disminuir el costo de mantenimiento.
- Un proyecto con funciones similares es la relación entre
requests y urllib3.
- Este artículo recuerda a los desarrolladores de software la importancia de la gestión y el mantenimiento del código.
1 comentarios
Opiniones de Hacker News
Me gusta la frase "Simple is robust". Significa que mientras menos complejidad tenga un sistema, más fácil es cambiarlo. La planificación para el futuro debería basarse en código intuitivo más que en código escalable. Por ejemplo, abstraer solo cuando la situación lo exija, favorecer la duplicación simple, usar un monolito al inicio y priorizar el escalado vertical sobre el horizontal. Al construir varios sistemas 0 a 1, he encontrado este patrón en común.
Me sorprende que no se mencione nada sobre pruebas u observabilidad. Las pruebas tienen un costo de mantenimiento, pero reducen el riesgo de que algo falle al eliminar código. Cuando expones un servicio a consumidores externos, necesitas una forma sólida de marcar algunas llamadas como deprecadas y observar si todavía se siguen usando. Hace poco eliminé resolvers de GraphQL de forma semiautomatizada, y con métricas de frecuencia de uso identifiqué cuáles no podían borrarse. GraphQL tiene anotaciones de deprecación, pero en el servicio no se manejaban de forma especial. Si agregas observabilidad para activar una bandera cuando se llama una función deprecada y lo ejecutas en producción durante un periodo suficientemente largo, puedes borrar con seguridad código expuesto externamente.
He terminado promoviendo el "diseño para borrar". Antes pensaba que podía planearlo todo y crear una obra que cubriera todas las necesidades, pero es difícil predecir las necesidades futuras. Algún día, lo que construí no le servirá a alguien, y estará justificado que lo desmantelen. Por eso vale la pena esforzarse en hacerlo fácil de quitar. Eso a menudo termina reduciendo el acoplamiento, pero no es lo mismo que la idea juvenil de separar todo en frameworks meta-configurables. A veces, un acoplamiento estrecho es mejor si resulta más fácil de entender lógicamente.
Para escribir código fácil de borrar, hay que repetir para evitar dependencias, y no repetir para administrarlo. Hay que dividir el código en capas y construir APIs simples sobre partes que pueden ser fáciles de implementar pero incómodas de usar. Hay que separar el código y aislar tanto las partes difíciles de escribir como las que probablemente cambien del resto del código y entre sí. No debes hardcodear todas las decisiones; conviene permitir cambiar algunas cosas en tiempo de ejecución. En mi experiencia, el código fácil de borrar está estructurado en capas y modularizado, así que también es fácil de extender.
Siempre les he dicho a los estudiantes de física computacional que el mejor cálculo es el que no tienes que atender.
Personalmente, separo el código entre lógica de negocio e implementación real. La lógica de negocio, por su naturaleza, puede duplicarse, pero no deberían duplicarse demasiados detalles técnicos. Mientras mantengas la lógica de negocio independiente de la aplicación y no la mezcles directamente con esos detalles, puede ser tan desordenada como quieras. Si algo falla y no funciona bien, siempre existe la opción de borrar toda la implementación, corregirla y no verte obligado a buscar la especificación real dentro de la implementación.
El error evidente del primer párrafo: el problema de la reutilización de código es que estorba cuando después cambias de idea. Eso, en general, es una afirmación equivocada. Si cambias de idea y el código fue copiado y pegado en diez lugares, tienes que modificar diez lugares. En cambio, si el código está en una función, solo lo cambias una vez. Si una de las diez llamadas no debe cambiar, todavía puedes copiar y pegar, o hacer la función más general. Igual que cruzar la calle sin mirar, copiar y pegar casi siempre es una mala idea.
Hay una gran correlación: el código malo permanece porque es difícil de quitar.
Me pregunto si esto significa que hay que usar el software lo más posible en su estado base y evitar una personalización profunda.