Recientemente mi equipo estaba trabajando en un punto final de la API que tenía un caso límite. El camino feliz era simplemente insertar un registro. Pero en casos raros, ese registro podría ya existir. La forma en que habíamos estructurado la lógica, el manejo de este caso era un poco difícil. Terminó representando aproximadamente la mitad de nuestra lógica e hizo que el punto final fuera más difícil de probar.
Al charlar con nuestro arquitecto sobre la mejor manera de manejar este caso límite, recibimos una respuesta sorprendente. No lo manejes. En lugar de eso, reestructura tu lógica para que no haya un caso límite.
Muchos de los casos de borde es un olor a código
Si te encuentras escribiendo un montón de código para manejar los casos de borde, podría ser un olor a código. Por supuesto, queremos crear código robusto y eso significa que tenemos que pensar en los casos de borde. A escala, los casos de borde ocurren regularmente. Así que mi argumento no es simplemente ignorarlos.
La pregunta es cómo resolvemos un caso de borde dado. En algunos casos, debemos escribir un código especial para manejar ese caso de borde, pero en otros casos, podemos simplemente reestructurar nuestra lógica de manera que el caso de borde desaparezca. Ese es el ideal. Tener mucho manejo de casos de borde en nuestro código es un olor a código porque podría indicar que no tomamos este enfoque lo suficiente.
Ejemplo: Valores nulos
Tal vez tenga lógica en muchos lugares para manejar el hecho de que un cierto campo pueda ser nulo. Para ser más específicos, digamos que tienes una cadena nula almacenada en una base de datos. Más adelante, podrías estar realizando operaciones de cadena como length o toUpperCase sobre este valor. Pero como es nulo, tienes que comprobarlo cada vez.
Ideas para refactorizar:
- ¿La cuerda debe ser anulable? Tal vez puedas ponerle una restricción no nula. Incluso si necesitas un valor vacío, ¿podría ser representado por una cadena vacía?
- También podría considerar el uso de un lenguaje que apoye los controles estrictos de nulidad para complementar esto.
Ejemplo: Insertar un registro que ya podría existir
Digamos que estás insertando un registro en una base de datos relacional que podría causar una violación de la restricción única. Después de hacer esto, realizas varias otras operaciones que deben ser manejadas de manera diferente si la inserción causó una violación. El resultado es que tienes muchas ramificaciones en tu aplicación.
En lugar de tener todas estas ramas, consideremos algunas alternativas.
- ¿Es necesario tener restricciones únicas? Tal vez debería almacenar todas las versiones de este registro y luego buscar la más reciente al acceder a él.
- ¿Podemos hacer un cortocircuito en nuestra lógica en algún lugar temprano para que sólo tengamos una rama lógica?
- ¿Tiene sentido en tu caso usar una consulta de estilo upsert en lugar de un inserto?
Ejemplo: Apoyar a los clientes obsoletos
Tal vez tengas dificultades para que una característica específica de tu aplicación funcione en una versión antigua del navegador. Esto puede ser crítico para que usted lo apoye, pero asegúrese de que ha investigado. ¿Necesitamos dar soporte a esta versión de navegador? ¿Podemos cumplir los acuerdos apoyándola en un estado ligeramente degradado?
Ejemplo: Poner una cola delante de una cola
Digamos que tenemos que publicar un mensaje a una cola. ¿Y si la publicación falla? ¿Deberíamos escribir los datos a otra fuente primero? Ahora estás poniendo una cola delante de otra cola. ¿No existirán los mismos problemas en ambas colas?
En algunos casos, esto puede tener sentido. Tal vez la primera cola tiene poca fiabilidad y no tienes control sobre ella. Pero tómate un tiempo para pensar. Tal vez termine teniendo casi los mismos escenarios de fallo y probabilidades.
O incluso si la segunda cola reduce su tasa de fallos, ¿merece la pena la complejidad? ¿Cuál es su nivel aceptable de riesgo para la aplicación?
El manual de la SRE de Google lo dice bien: «Nos esforzamos por hacer un servicio lo suficientemente fiable, pero no más de lo necesario. Es decir, cuando establecemos un objetivo de disponibilidad del 99,99%, queremos superarlo, pero no por mucho.» (Google SRE, Capítulo 3 – Aceptar el riesgo)
Tal vez sería mejor dejar que falle y reflejar ese fallo al usuario.
Resumen
Los casos límite son importantes para planificar. Deberíamos valorar las soluciones más simples para esos casos límite que sean razonables. Si la mitad o más de su código está manejando casos límite, probablemente debería repensar cómo está manejando sus casos límite. Como con cualquier olor a código, esto es sólo una heurística, no una regla.
Categorías: technicalTags: architecture, clean code