archivo

Archivo de la etiqueta: programación

En el desarrollo de software los extremos no son buenos. A veces se le da al programador un diseño tan detallado que básicamente lo que hace es traducirlo a código fuente en el contexto del framework con el que trabaja. Otras veces no existe diseño o bien las especificaciones no son completas y son los programadores solos o en colaboración con analistas los que rellenan los huecos.

Desde mi punto de vista es importante que el programador sepa lo que tiene que hacer, es decir, que las especificaciones las conozca y que también conozca los objetivos no funcionales pero sobre esa base es necesario darle libertad para que dentro de esas restricciones pueda realizar la solución que resulta más conveniente.

Es necesario tener en cuenta que si se realiza un desarrollo en base a un análisis y/o diseño que se realizó hace tiempo es muy posible que quien lo hiciera no tuviera en cuenta determinados factores que sí son conocidos en el presente y no resulta lógico no echar cuenta a una solución mejor por tener en cuenta un trabajo que se efectuó sin conocer determinados tipos de detalles.

También es importante precisar que ese margen de maniobra permite que el programador dentro de esas restricciones pueda expresar su creatividad (algo que todos llevamos dentro) y realice su trabajo más motivado, algo que todos sabemos termina produciendo resultados de mayor calidad.

Como siempre es importante evaluar el contexto. No hay verdades absolutas. Es posible que en determinadas situaciones tener un nivel de detalle importante resulta fundamental, por ejemplo, cuando se externaliza a otro equipo de trabajo las tareas propias de programación de un desarrollo (o parte de él), aunque incluso en estos casos hay que tener en cuenta que la comunicación seguirá siendo necesaria (y se debe considerar un problema si no la hay) porque siempre quedará algún detalle que no se ha especificado al 100%.

La deuda técnica condiciona el coste que tienen las tareas de evolución o mantenimiento de un software. Desde ese punto de vista todo software que sea susceptible de ser evolucionado debe tratar de mantener una deuda técnica acorde a las características del proyecto que se desarrolla y de su contexto (si los recursos para un proyecto son insuficientes la deuda técnica se verá impactada).

Ahora bien, si se trata de un desarrollo de usar y tirar, por regla general aquellas utilidades de pequeño tamaño que se hacen de manera específica para cumplir un objetivo concreto. Por ejemplo, hacer una migración o una explotación de datos no requieren un control de la deuda técnica si bien, siempre es recomendable intentar hacerlo de la mejor manera posible y si tenemos buenas prácticas de programacion, ¿por qué no aplicarlas?. ¿Qué no tendría sentido? Dedicar más tiempo del necesario para realizar ese desarrollo y por supuesto dedicar tiempo a refactorizar la solución.

Esto es extensible a todos los ámbitos de los proyectos de desarrollo de software: desde las tareas propias de programación hasta las relaciones entre las personas (dentro y fuera del equipo de desarrollo).

Realmente buena parte de la deuda técnica es provocada precisamente por pequeños problemas o aspectos “sin importancia” que se dejan, no se subsanan y que se van acumulando con todos aquellos del pasado y del futuro donde se repita la situación u no se haga nada. Y el problema va más allá de la acumulación ya que en muchos casos terminan interactuando unos con otros provocando un impacto mayor que el que tendrían por separado.

Cuando llegue el momento de ir tapando estos agujeros o simplemente de querer mejorar el código o la arquitectura nos encontraremos con que nunca habrá tiempo o que el coste será muy superior al que tendría si se hubiera aplicado el esfuerzo en el momento preciso.

¿Y qué decimos de las personas?, ¿Cuántas veces una tontería se ha convertido en un mundo entre dos personas o entre un grupo de personas?, ¿cuantas veces por no hablar en el momento adecuado se rompe el buen ambiente que debería existir en un equipo de trabajo?. Todos tenemos malos días, todos tenemos días en que estamos insoportables, todos nos equivocamos por lo que este tipo de problemas van a aparecer incluso en equipos o relaciones consolidadas, la clave es tratarlos cuando todavía no se han convertido en un problema mayor porque cuando uno está enfadado con otra persona o le guarda rencor, casi todo (por no decir todo) lo que se percibe de la otra parte es negativo aunque no tenga nada que ver contigo o no se haga con una mala intención.

Es importante programar al mayor nivel posible que nuestro conocimiento y experiencia nos permita. Esa debe ser la base a partir de ahí solo queda seguir aprendiendo y seguir adquiriendo experiencia para a partir de ese punto desarrollar software de mejor factura técnica.

Ahora bien, ni el conocimiento ni la experiencia son las que pulsan las teclas, somos nosotros y si no sumamos actitud a nuestra aptitud, el software que se desarrolla estará por debajo de nuestras posibilidades y por tanto estamos añadiendo deuda técnica extra que podía ser perfectamente evitable.

Es cierto que a veces no es tanto cuestión de aptitud sino el contexto en que el que trabajas donde lo mismo la presión y las prisas provocan que se priorice la ejecución de las tareas a cómo realmente se están ejecutando. Estas circunstancias condicionan el trabajo y provocan una bajada en nuestro listón potencial de calidad, pese a eso hay que intentar ajustar el software a ese nuevo listón y considerar todo lo que esté por debajo de él deuda técnica extra que añadimos al producto.

Después si la metodología de desarrollo, nuestra visión sobre lo que debe ser el producto software y las prioridades del proyecto lo permite habrá un tiempo para refactorizar pero como siempre digo esa actividad se debe realizar siempre sobre un software sobre el que se haya puesto el mayor empeño posible en hacerlo bien.

Al final toda la deuda técnica añadida al software son intereses sobre el desarrollo (y el mantenimiento) que tenemos que pagar, por lo que si hoy sale barato (desarrollar software sin tener en cuenta su mantenibilidad) que por cierto está por ver si realmente sale barato, mañana será caro, muy caro.

Me gusta mucho la siguiente cita de Bob Martin al respecto (traducción libre): “Hacer código de calidad no lleva tiempo. No hacer código de calidad sí que lleva tiempo”.

La visión de la refactorización no debe ser la de: “trato de cerrar cuanto antes las tareas que tengo asignadas que ya tendré oportunidad después de dedicar tiempo a poner el código un poco mejor”, principalmente por dos razones:

1) La mayoría de las veces, salvo que esté muy interiorizado por el equipo de trabajo y por el programador la necesidad de refactorizar y los beneficios que trae consigo, no se termina dedicando tiempo a realizar estas tareas porque continuamente van a surgir otras que se considerarán más prioritarias y sobre las que recaerá la atención. En este contexto tomar ese tipo de actitudes no es más que querer engañarnos a nosotros mismos: “finalmente no pude poner el código mejor o reducir el acoplamiento entre esas clases porque estoy hasta arriba de trabajo y no tengo tiempo”.

2) Se descuida la programación dejando para más adelante detalles que perfectamente podrían ser resueltos conforme se está trabajando. Todo es susceptible de mejorar de ahí lo útil que resulta refactorizar pero es importante que los esfuerzos dedicados a esa tarea sean los necesarios (y no más).

Es cierto que a veces las restricciones temporales imponen unas condicionen que ni permiten refactorizar y ni siquiera permite cuidar lo que se quisiera la codificación y la arquitectura. En estos casos el contexto del proyecto manda y probablemente para conseguir el resultado final en el tiempo y presupuesto establecidos habrá que sacrificar parte de su mantenibilidad y robustez.

¿Qué se puede hacer en estos casos? Lo que se pueda (realmente la situación de partida del proyecto es la culpable de todo lo demás) pero siempre con la intención de hacerlo de la mejor forma posible.

Cuando se escribe una historia de usuario, un catálogo de requisitos o cualquiera que sea la solución que se utilice para dejar escritas unas especificaciones resulta muy complicado por mucho que se le den muchas vueltas que todos los huecos queden cerrados.

Como siempre os digo tenemos que actuar con intención por ese motivo no vale con una sola lectura de esas especificaciones, es necesario que varias personas (que hayan intervenido en su definición) la vean y la discutan si es preciso.

Pero aún así, llegarán requisitos débiles al proceso de construcción y ahí serán los programadores y las personas que estén revisando su trabajo los que tienen que levantar la mano cuando descubran especificaciones inconsistentes, incoherentes con otras o insuficientes para poder desarrollar una funcionalidad sin que el programador se tenga que inventar parte de ella (si hay que inventar que lo haga el usuario ya que tendremos más posibilidades de acertar).

Es importante que lo programadores sepan que su misión no solo es escribir código (de la mejor manera posible) sino entre sus tareas también se encuentra detectar esas fallas en las especificaciones. Para ello es necesario que sepan qué es lo que están haciendo, que conozcan al menos la parte del negocio que compete al módulo que están desarrollando. Esto resulta también importante a la hora de realizar testing sobre las funcionalidades que desarrollan.

Para Brian Kernighan el control de la complejidad es la esencia de la programación.

Complejidad supone más esfuerzo en la construcción y en el mantenimiento, más probabilidad de que existan errores y sistemas de información más complicados de utilizar para el usuario.

La complejidad es resistencia al progreso en el desarrollo y resistencia cuando se trata de adaptarnos al cambio.

La complejidad es agotadora para los desarrolladores y una carga que lleva el producto durante todo su ciclo de vida.

Es necesario hacer un esfuerzo en todas las etapas del proyecto, desde su concepción hasta su entrega con el objetivo de encontrar la solución más simple que satisfaga las expectativas del usuario (y que permita un mantenimiento lo más simple posible ajustado a las características del sistema).

Buscar lo más simple implicará entender bien los procesos de fondo que informatiza el sistema, pensar en el usuario, pensar en el software es susceptible de ser modificado en todo su ciclo de vida, pensar en que hay que ser prácticos (tanto desarrolladores como usuarios) y no llenar el producto de funcionalidades que no se utilizan o que van a tener un uso residual.