sábado, 29 de noviembre de 2014

Herramientas Imprescindibles: Control de versiones

Durante la semana un codificador me estaba exponiendo una excelente idea para poder resolver un problema, en mitad de la conversación que pregunta, "¿Crees que va a funcionar?" y le conteste: "No, nunca funciona a la primera" (Regla N°2 de la Ingeniera de Software: "Va a fallar").

Cualquier cosa o idea que tengamos nunca va a funcionar a la primera, y si funciona es que porque algo hemos hecho mal o estamos obviando algo. Las buenas ideas necesitan fallos para poder triunfar, montarle piezas y quitárselas hasta que se obtengan la forma ideal, y generalmente se necesitan más de una persona para hacer grandes proyectos realidad.

Ante tal panorama es importante el control, algo que nos responda rápidamente a las preguntas "¿Qué hice y donde lo hice?" y "¿Qué hicieron otros y donde?", a la vez que permitirnos regresarnos rápidamente sobre nuestro pasos, para poder emprender otra dirección más exitosa.

Este control nos lo permite las herramientas de "Control de versiones", que son medios automatizados para gestionar el código fuente, a través de su evolución. No solo eso, es como una máquina del tiempo (o mejor como una Tardis), que nos permite ver que ocurrió con nuestro código fuente (Porque todo cambia, Regla N°1 de la Ingeniera de Software: "Va a cambiar") , en una fecha determinada, quien hizo los cambios, y comprender el porqué. (por que todo tiene un motivo, Regla N° 4: Todo sistema tiene un propósito).

Pero, exactamente ¿Que debe permitir una herramienta de Control de versiones?

  • Gestionar los cambios que se hagan con nuestro código fuente, de una forma sencilla.
  • Integrar los cambios que hagan otros codificadores de forma automática.
  • permitirnos revisar dichos cambios a través del tiempo.
  • Permitirnos reversar cambios específicos de nuestro código ( es decir no se trata de reverso "todo o nada").
  • Debe funcionar de forma remota. Los programadores trabajan en sus maquinas (Cada uno en sus maquinas), y mediante el software de control de versiones, se integra todo en un repositorio que está en un servidor, de forma automáticamente. las cambios de los otros codificadores se integran a nuestro código (en nuestra maquina) de forma automática de igual modo.
  • Realizar ramificaciones de nuestro código.


El tema de las ramificaciones (branch) en un tema muy importante, que se explica con el siguiente ejemplo: Imaginemos que tenemos un determinado proyecto, del cual vamos a sacar la versión 2.0, con lo que usamos el control de versiones para gestionar nuestros cambios desde la versión 1.0 a la versión 2.0. La construcción dura unos 6 meses en cuestión de tiempo, pero a su vez tenemos la versión 1.0 la cual sigue siendo usada por nuestro clientes (porque seis meses es mucho tiempo), Esta versión tiene errores que necesitan ser corregidos o mejoras que no pueden esperar a la versiones 2.0. Aquí tenemos varias opciones:

  • No hacer nada para la versión 1.0, con lo que estaremos perdiendo seis meses de oportunidades de negocio.
  • Hacer los cambios en la versión 1.0 y cuando salga la versiones 2.0 incorporarlos manualmente, con la extremadamente posibilidad de perder código, o funcionalidad al hacer una mala "recodificación" de los cambios de la versión 1.0 a la 2.0.


La tercera opciones es hacer una rama de software con nuestra herramienta de control de versiones, esto seria así: Originalmente existe un solo proyecto llamado "Proyecto 1.0", que se ramificaría en dos. uno llamado "Proyecto 2.0" y otro "Proyecto 1.1 " (por poner un nombre), cada una con una serie de codificaciones independientes (y puede que un equipo de trabajo también). Llegado el momento la herramienta de control de versiones nos permitirá replicar (de forma automática), todos los cambios realizados desde la versión "Proyecto 1.0" hasta la versión "Proyecto 1.1", pero dentro del código fuente del "Proyecto 2.0". La integración del código será automática, y en el caso que existiera conflictos, se nos indicara para que los corrijamos manualmente.

Como comentamos, cada codificador trabaja de forma independiente en su máquina, y preferiblemente, hace una entrega de código (llamada frecuentemente commit) y una integración (automática) del código fuente de otros codificadores (llamada también frecuentemente update), todo esto pasando por un servidor remoto que contiene toda la historia del código fuente.

Pero existe otra forma de trabajar, mediante repositorios distribuidos, esto es un escenario en que cada codificador tiene una copia del repositorio en su máquina, y puede trabajar libremente con el sin aceptar a otros codificadores (puede hacer commit, revertir, crear rama e integrarlas, etc.). cuando el codificador lo considere oportuno puede integrar el código al repositorio común, para que este a disponibilidad de otros codificadores, al mismo tiempo que descarga el código de estos.

Aquí una pequeña guía para saber cuándo usar control de versiones en nuestros proyectos

  • Regla numero 1:¿Cuándo debo usar control de versiones?
Siempre.

  • Regla numero 2:Aun así, no sé si por el tamaño de mi proyecto debo usar control de versiones.
Ok, a veces es confuso, por favor en caso de duda revise la regla numero uno.

  • Regla numero 3:Pero no estoy compartiendo código fuente con otros codificadores, ¿Realmente es necesario usar control de versiones?
En este caso igualmente podemos aplicar la regla numero uno.


Por último Algunas herramientas de control de versiones:

  • Subversion:frecuentemente llamado SVN, uno de los más populares y más fácil de aprender, puede consultarse un excelente manual aquí, y descargase de aquí.
  • TorotiseSVN:cliente de SVN integrado con el explorador de Windows, una maravilla, puede descargase de aquí.
  • AnkhSVN:cliente SVN integrado con Visual Studio, se descarga de aquí.
  • VisualSVN Server:Un servidor de SVN, extremadamente sencillo de configurar, vale mucho la pena, incluso su licencia gratuita, de descargar de aquí.
  • Git:Control de versiones distribuido muy útil, quizás el mejor, algo difícil al principio, pero sin duda vale la pena el esfuerzo, se puede consultar el siguiente manual aquí, y descargarse el Gitde aquí.
  • TortoiseGIT:El homologo de TortoiseSVN para GIT, se descarga de aquí.


Aunque SVN es una excelente herramienta, mi recomendación es comenzar con GIT, aunque sea un poco más difícil, porque es sistema posterior que se creó con la intención de mejorar la debilidades de SVN. No obstante los creadores de Git, sabiendo de la gran difusión de SVN, crearon una capa de compatibilidad entre ambos sistemas, pudiendo trabajar con un cliente GIT en nuestra maquina (con toda sus ventajas) y un servidor SVN del lado remoto.

Por último una serie de reglas a aplicar en el uso de control de versiones:

  • Nunca subas al control de versión algo que no compila, solo te causaras el desprecio del resto de codificadores
  • Revisa línea por liana lo que modificaste antes de subirlo, si no reconoces un cambio reviértelo, debes reconoce todo lo que estas subiendo. Para realizar esta tarea el control de versiones tiene herramientas que te muestran que se modifico exactamente, de forma rápida y sencilla.Nunca te saltes este paso.
  • Haz update y commit tantas veces como sea posible (por lo menos una vez al día), si no tu versión del código de alejara de la versión de otros codificadores, y cuando quieres integrarla, te encontraras con un monto de conflictos.

domingo, 2 de noviembre de 2014

Estándares de programación: Preferiblemente usa bucles "foreach"

En cualquier lenguaje de programación hay una serie de estructuras básicas para realizar un bucle, en esta publicación analizaremos el bucle for y el bucle foreach, y la conveniencia de usar el segundo sobre el primero, el código mostrado sera en C# (aunque debiera ser semejante en cualquier otro lenguaje).

Comencemos viendo los tipos de bucle mas comunes:
  • Esta el bucle while:
1
2
3
4
while (SeCumplaCondicion())
{
}
while (SeCumplaCondicion())
{

}
Es el más básico, y se ejecutaría hasta que algo hiciera que se cumpla la condición.
  • bucle while tipo dos:

1
2
3
do {
} while (SeCumplaCondicion())
do {

} while (SeCumplaCondicion())
Garantiza que por lo menos se realiza una iteración del bucle antes de comprobar si se cumple la sentencia, por lo demás es igual que el anterior
  • El bucle for:


1
2
3
4
for (int i = 0; i < 10; i++)
{
}
for (int i = 0; i < 10; i++)
{

}
Este bucle se usa para recorrer algún tipo de array, o una colección, teniendo esta estructura

1
2
3
4
5
6
List<string> nombres = new List<string>() { "JUAN", "PEDRO", "ANA", "INES" };
for (int i = 0; i < nombres.Count; i++)
{
    Console.WriteLine(" Nombre :" + nombres[i]);
}
List<string> nombres = new List<string>() { "JUAN", "PEDRO", "ANA", "INES" };

for (int i = 0; i < nombres.Count; i++)
{
    Console.WriteLine(" Nombre :" + nombres[i]);
}
El resultado será algo parecido a:


  • Nombre :JUAN
  • Nombre :PEDRO
  • Nombre :ANA
  • Nombre :INES

Este bucle tiene varios problemas asociados:

lo primero es que usamos un numero para acceder a la colección, si nos fijamos nuestra intención no es trabajar con la posición de los nombres dentro del objeto, es decir nos da igual si es la primera o la ultima, nosotros queremos recórrelos todos en secuencia uno a uno. Su posición para nosotros es irrelevante. Esto es importante, por que se pierde el verdadero objetivo del código, hacer algo con todos los elementos del array, al margen de su posición. Requiera además que ya se sepan todos los nombres (los elementos de la colección) antes de procesar el bucle, en algunos casos no requeríamos conocer todo el escenario antes de querer procesarlo, por ejemplo, si estamos buscando un archivo en todo el disco duro, no sería recomendable que primero listemos todos los archivos y luego lo recorriéramos buscando el archivo idóneo.

  • El último bucle, es el bucle foreach, tiene la siguiente estructura


List<string> nombres = GenerarListaNombres();

1
2
3
4
5
6
List<string> nombres = GenerarListaNombres();
foreach (string nombre in nombres)
{
    Console.WriteLine(" Nombre :" + nombre);
}
List<string> nombres = GenerarListaNombres();

foreach (string nombre in nombres)
{
    Console.WriteLine(" Nombre :" + nombre);
}
En esta forma de bucle, se elimina el uso de los índices (int i), lo que simplifica su código y nos permite centrarnos en el objetivo real, que es recorrer los elementos de la lista.

El hecho de que el código sea más sencillo, lo hace a su vez mas mantenibles y escalable en un futuro.

Por otro lado el único requisito es que el elemento a recorrer implemente la interfaz IEnumerable, lo cual nos permite realizar ajustes a como se procesan los elementos, en este ejemplo se recorren los nombres hasta que se encuentra la palabra "PEDRO"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void Ejemplo2()
{
    foreach (string nombre in GenerarListaNombresEnumerable())
    {
        Console.WriteLine(" Nombre :" + nombre);
        if (nombre == "PEDRO") break;
    }
}
public static IEnumerable<string> GenerarListaNombresEnumerable()
{
    yield return "JUAN";
    yield return "PEDRO";
    yield return "ANA";
    yield return "INES";
}
public static void Ejemplo2()
{
    foreach (string nombre in GenerarListaNombresEnumerable())
    {
        Console.WriteLine(" Nombre :" + nombre);
        if (nombre == "PEDRO") break;
    }
}

public static IEnumerable<string> GenerarListaNombresEnumerable()
{
    yield return "JUAN";
    yield return "PEDRO";
    yield return "ANA";
    yield return "INES";
}
Esta función especial que devuelve los campos con la palabra yield, nos permite genera la lista de nombres al mismo tiempo que se está procesando, la ventaja es que no se generan nombres que no se van a usar (por ejemplo no se genera "ANA", ni "INES", en caso que poníamos anteriormente, si estuviéramos buscando un archivo por todo el disco duro, podríamos detener la búsqueda cuando encontráramos el archivos requerido, y no tendríamos primero que localizar todo los archivos y ver si cumplen las características que deseamos uno a uno.

Adicionalmente el listado de nombre no se puede modificar dentro de un bucle foreach, con lo que garantiza seguridad de no alteración del listado (los objetos dentro del listado si se puede mejorar).

Resumiendo las ventajas del bucle foreach, quedarían las siguiente:

  • Código más limpio y sencillo, que expresa mejor las intenciones del codificador.
  • No es necesario generar completamente los elementos a recorrer, antes de comenzar a recorrerlos.
  • No es posible modificar el listado original.


¿Cuándo usar un bucle for o una foreach?, en principio la mejor opción es siempre el bucle foreach, por los motivos establecidos, (sobre todo el de claridad en el código), salvo en el caso que realmente sea relevante la posición del elemento en la lista.

jueves, 16 de octubre de 2014

Regla N° 6: Lo importante es el "Que hace" y no el "Como lo hace"

Tradicionalmente cuando uno estudia programación por primera vez, en la carrera de Informática o afines, por algún motivo desconocido para mi, se comienza estudiando programación estructurada, a veces en pseudocódigo, en Pascal, en C, o quizás en Java, pero orientado de alguna forma estructura. Se ven bonitos diagramas de flujo (Flowchart), donde todo tiene un orden y una secuencia determina. Nos enseñan que un algoritmo es un proceso, pasó a paso, que con una misma entrada, produce una misma salida. Datos entran y datos salen.

Y después cuando ya está adaptado nuestro celebro para hacer las cosas de una determinada manera, ¡Bang!, nos cambian el paradigma a una la programación orientada a objetos, que curiosamente es el paradigma de programación que ofrece mayores virtudes en el desarrollo de software, es mas casi todos los lenguajes modernos de programación vienen con la coletilla "Todo es un objeto", frase que aunque ciertamente relativa, nos hace ver la importancia de este paradigma.

Pero ¡Ay!, todo se ve con el prisma de programación estructurada, incluso se aprende la programación orientada a objetos desde este enfoque, haciendo comparaciones, diferencia y similitudes. Todo esto hace que el aprendizaje este condicionado y limitado por un enfoque previo.

Hagamos un paréntesis en este punto para contar una pequeña anécdota. Hace años, tuve un maestro de historia, que nos comento el gran avance industrial que supuso la creación telar mecánico, que simplifica y automatizaba la creación de tejidos de distintas formas y colores, con lo que se vio una mejora en los tiempos en la fabricación, y una rebaja en su costo. Era como el principio de la industrialización, en la que mejores procesos, y avances se iba incorporando en la vida diaria de la producción de nuevos elementos, desde las mencionadas telas, a la fabricación de coches, hasta la llegada de microprocesadores y computadoras. También nos señalo que seria ridículo, que si exigiera un país en vías de industrialización, comenzara por los telares y continuara implantado poco a poco todos los avances tecnológicos de otros países, como queriendo recorrer su mismo camino, porque este jamás alcanzaría un mismo estatus industrializado (llego tarde a la carrera). La única solución posible seria que comenzara en el mismo punto tecnológico que corresponde a la actualidad, para que ambos pudieran progresar a la par.

Lo mismo pasaría con lo programación, que se quiere enseñar aparentemente en el mismo orden en que evoluciono esta disciplina, (Curiosamente cuando estudie sistemas, nos enseñaron, en este orden, los siguientes lenguajes de programación: Pascal, Fortran, C, Prolog, ADA, C++, Visual Basic 6 y Java, que básicamente fue el orden en que fueron creados, salvo los dos primero).

El problema del enfoque de la programación estructurada es que se enfoque demasiado en la forma de trabaja que tiene las computadores, esto es "Lo importante es como lo hace", ejecutando una por una las líneas de programación de un sistema, y realizando saltos en el flujo según determinadas instrucciones.

Los "humanos", no resolvernos problemas de esta manera, o por lo menos no generalmente, nosotros descomponemos un problema en elementos mas pequeños, a los que podemos catalogar, y les atribuimos características y propiedades, olvidando además aquellas datos y circunstancia que no su útiles para el problema, después generalizamos dichos elementos para que nos sirva en solucionar otra problemas parecidos. Este forma de pensamiento es donde prima "Lo importa es lo que hace", y es uno de los pilares de la programación orientada a Objetos.

En la programación orientada a objetos, se diseñan elementos, donde prima la representación de "Que es lo que hace" y "Que son", principalmente, a través de sus atributos y sus métodos. también se establece como se relacionan con otros tipos de elementos (o clases), ya sea mediante técnicas como la composición, la herencia, o el envió y recepción de mensajes.

De esta forma centrándonos en "Que hace" obtenemos una comprensión mas clara del sistema, y de sus requisitos, además construiremos un sistemas más escalable (debido a que no nos estamos centrando en "Como lo hace"). Inclusive tendremos muchas más posibilidades de conectarnos a otros sistemas, puesto que queda mas claramente establecidos los limites y conectores de estos.

Cuando nos centramos al contrario en "Como lo hace", obtenemos, entre otros, los siguientes problemas:

  • Un código lleno de sentencias if, y totalmente ausente de herencia y polimorfismo (la herencia se sustituye con bloques if, que se repiten en cada función).
  • Igualmente total falta de Interfaces, que son la máxima expresión del pensamiento "Lo importante es lo que Hace".
  • Un código lleno de clases estáticas, con simples métodos expuestos.
  • Funciones que en lugar de recibir objetos y trabajar con ellos, recibe una lista indeterminada de parámetros, con tendencia a crecer, uno detrás del otro y con ausencia de sobrecargas.
  • Separación de los datos y el negocio del sistema (en la programación orientada a objetos los datos (atributos) y el negocio son indivisibles).
  • Clases muy grandes cuyos métodos no tiene ninguna relación.
  • Clases muy pequeñas, porque no se tiene claro dónde colocar los métodos, y se crean nuevas clases para albergar pocos métodos.
  • Métodos desproporcionados (tanto por grandes, como por pequeños), con excesiva dependencia entre ellos.


Y lo más importante dificultad para modificar y comprender el código.

viernes, 10 de octubre de 2014

Claves del la Calidad: El equilibrio

Una de las claves de la calidad de un sistema, es el equilibrio entre tres factores principales: Tiempo, Recursos y Alcance. Juntos forman una especie de taburete de tres patas, sobre el cual esta nuestro desarrollo y donde si una de ellas esta desproporcionada, puede llevarnos al fracaso en la construcción de nuestro software.

El tiempo

El tiempo posiblemente sea el factor más difícil de manejar, es más, el tiempo en el desarrollo de software se curva en contra de toda ley de la física, y así dos meses se pueden convertir en seis y seis meses de trabajo deben caben en dos.

Es más posible que te toque la lotería, el día de tu cumpleaños, un 29 de febrero, sin comprar boleto, a estimar correctamente un proyecto.

Este factor hace referencia al espacio de tiempo que tenemos disponible para sacar un producto al mercado en el momento oportuno para que nos genere beneficios. Por ejemplo, el tema de la facturación electrónica en México, hace años era un bien escaso y la gente que le entro pronto a este mercado tuvo bastante éxito, pero en estos momento el mercado está saturado de proveedores de factura electrónica, y con una dura competencia, con lo que sería bastante difícil entrarle de cero a vender un nuevo producto de facturación.

Debido a que lo importante, como decimos es llegar en el momento justo, generalmente tiempo es el factor que más escasea.


  • Cuando tenemos poco tiempo

Se da generalmente un uso excesivo de los recursos, esto es, los involucrados en el sistema se ven obligados a hacer horas extras (con lo que se fabrica tiempo, se convertirán jornadas de 8 horas en jornadas de 10, 12 o más horas, aquí es donde caben seis meses de trabajo en dos), lo que principalmente provoca desanimo, cansancio en el equipo y software de baja calidad con multitud de errores.


  • Cuando tenemos demasiado tiempo

Se pierde el objetivo del sistema, y el interés en el mismo, igualmente el equipo se acaba sintiendo alejado del mismo proyecto (incluyendo al cliente), al no ver una producción real de su trabajo, y la meta queda diluida. Se baja la calidad del sistema al no tener un resultado real y tangible. Igualmente puede ser que el producto pierda su significado al no haber salido en el tiempo adecuado


  • ¿Cuál es la medida correcta?

Va ligado con el alcance, hay que tener tiempo reducidos en los que se desarrolle una iteración (entrega) que le haga sentido al cliente (o usuario), pero que sea los suficiente amplio para no caer en las horas extras innecesarias.

La primera entrega siempre debe ser la más larga, porque si bien no incluye toda la funcionalidad, es necesario que ya está sostenida sobre una arquitectura firme y concisa para todo el proyecto (sobre todo por se va a aplicar la regla "Va a cambiar", y si no se tiene en cuanta nos afectara a posterior).

Los recursos

En los recursos, se hace referencia a elementos tales como maquinas de cómputo, licencia de software, equipos especiales, dinero, y también a las personas "recursos humamos".


  • Cuando tenemos pocos recursos

Si tenemos pocos recursos "físicos", como equipos hardware (o elementos de software que se deben compartir como bases de datos), puede ser que los participantes del proyecto compitan por ellos, con lo que más que un elemento de cooperación se convierte en uno de contienda, generando roces y malestar en general.

Si se tiene menos personas de las necesarias, se produce una sobrecarga de trabajo de dichas personas, es decir, hacen horas extras. Al final por el cansancio es normal tener descuidos y fallos, y estos a su vez provocan retrabajos, (y más horas extras).

Si no se aplicaran horas extras, no queda de otra que reducir el alcance (si un es una "pata" es otra).


  • Cuando tenemos demasiados recursos

Que al final tenemos recursos que no hacen nada, y por lo tanto tenemos perdidas de dinero (o lo gastamos innecesariamente)

¿Puede llegar un punto donde haya demasiados recursos? Si, generalmente hay personas claves en los proyectos (como los lideres), que no pueden gestionar apropiadamente un número determinado de recursos o personas si este es excesivo, con lo que se provoca un descontrol en la gestión del proyecto , o se crean estructuras jerárquicas (en forma de árbol), en las que se depende de otras persona, que delegan trabajo a su vez en otras, al final se pierde facilidades de comunicación, y se generan mecanismos demasiado burocráticos para controlar el proyecto (lo que influye principalmente en el tiempo y en conocer realmente las necesidades del cliente).


  • ¿Cuál es la medida exacta?

Lo recursos para un proyecto deben llegar en el momento adecuado, si llegan demasiado pronto, es posible que estén en desuso y si llegan demasiado tarde, es posible que lleguen a ser inútiles (o que el proyecto ya se haya cancelado por falta de estos). Los recursos deben llegar en el momento que puedan ser usados, y liberarse cuando ya no son necesarios.

El alcance

El alcance representa la cantidad de funcionalidad que se va a liberar.


  • Cuando nuestro alcance es demasiado pequeño

Generalmente el sistema no sirve para aquello que el cliente quiera que sirva. Este nunca estará satisfecho con el sistema y le generada una sensación de descontento (Por no hacer el sistema lo que él quiere que haga).


  • Cuando nuestro alcance es demasiado grande

Cuando esto ocurre generalmente se crea una proyecto inacabable, en el que ni el cliente, ni el equipo de desarrollo ven un producto tangible, y es muy probablemente que se abandone el proyecto.


  • ¿Cuál es la medida exacta?

Entregas continúas de corto alcance, en el que se libere una funcionalidad nueva en cada iteración, de forma que los retos y las metas sean alcanzables a la vez que interesantes para todos los implicados en el desarrollo del sistema.

sábado, 4 de octubre de 2014

¿Qué es la calidad?

La palabra "calidad" tiene muchas definiciones y criterios según el ámbito o la persona que este proporcionando dicha definiciones.

Según la rae calidad es "Propiedad o conjunto de propiedades inherentes a algo, que permiten juzgar su valor", otras definiciones son por ejemplo "Es la aptitud de un producto o servicio para satisfacer las necesidades del usuario.".

A lo largo de mi carrera he recibido diferente opiniones sobre que es la calidad, con mayor o menor atino, me quede con la que recibí cuando desempeñaba labores de tester, al inicio de mi vida profesional.

Estaba en mi primer trabajo, en el que mi labor era realizar pruebas de diversos tipos sobre el portal Web de la empresa (era un servicio de búsqueda), hacíamos pruebas de stress, de volumen, o de carga entre otros tipos de pruebas. en mis primeras semanas de capacitación, el líder del equipo de pruebas me pregunto, "¿Cuántas peticiones web debiera poder soportar el sistema?", "Todas" conteste yo, a lo que él me volvió a preguntar, "Es decir si tenemos 100 peticiones en un minuto, debemos responderlas todas, y si tenemos 2000 en un minuto también ¿no?, y ¿si tenemos un millón en un minuto, también verdad?", a lo que yo contestaba que si, a todo, cada vez menos convencido de mi mismo, "Ok", prosiguió, "y si solo recibimos de media 100 peticiones al minuto y si la infraestructura para contestar ese millón de peticiones sea demasiado costosa para nosotros y no se podría amortizar, ¿debiéramos seguir respondiendo ese millón de peticiones?", y allí se plateo la primera duda sobre la calidad, el responder unas 100 peticiones al minuto, parecía suficiente, debido al tráfico que tenia la pagina actualmente, pero si se llegara a un irreal caso de un millón de peticiones al minuto, el sistema no podría responderlo. entonces ¿Era buena o no la infraestructura sobre la cual estaba el sistema?. Aquí descubrí una nueva definición de calidad relacionada con el ámbito real de trabajo.

La calidad es una medida de aceptación, por la cual se contrapone los fallos de los sistemas, con respecto a su aciertos, y en base a dicha relación se decide sobre la viabilidad de un producto. Por ejemplo nuestro sistema permitía contestar apropiadamente 500 peticiones en un minuto (mas que las 100 que recibíamos realmente), a partir de las cuales comenzaba a tardase en responder al cliente. Esto nos daba un posible punto de control y un enfoque sobre cómo y por qué tenemos que tomar medidas de recuperación y prevención.

Una vez identificada la calidad del sistema, (por ejemplo un marguen o limite de respuesta de 100% en menos de 500 peticiones en minuto, y descendiendo a partir de allí), hemos establecido los márgenes reales sobre los que puede operar este y en base a esa "medida" de calidad, podemos establecer si aceptamos el sistema o no (es decir si el sistema satisface nuestras necesidades)

Me gusta este enfoque particularmente por qué parte de la regla "Va a fallar" y en base a esa establece en que términos fallaría, y como lo haría, lo cual no da un posible escenario de trabajo y estar listos en cuando un comportamiento anómalo del sistema, a la vez que desarrollar un plan de contingencia adecuado. Dicho de otra manera enfoca la calidad en descubrir sus deficiencias, y no en ensalzar sus virtudes.

domingo, 28 de septiembre de 2014

Regla N°5 de la Ingeniería de Software: No te repitas

"He redactado esta carta más extensa de lo usual porque carezco de tiempo para escribirla más breve.", Pascal

Aunque pareciera que la frase anterior, perteneciente a Pascal (el matemático, no el lenguaje de programación), no tuviera que ver con el tema "No te repitas", quise mencionarla porque igual que dicho tema, representa la idea de que hacer las cosas bien lleva tiempo, y que un aparente volumen superior de "líneas" no representan más trabajo, ni más esfuerzo, sino un conjunto de datos innecesarios (o repetidos) que restan valor al resultado de nuestro trabajo. Por ejemplo la pregunta correcta no sería ¿que pesa más un kilo de paja o un kilo de plomo?, sino ¿que es mas manejable un kilo de paja o un kilo de plomo?

La regla "No te repitas", hace referencia a que no se deben repetir porciones de código, con funcionalidades iguales, "casi iguales", o parecidas a los largo del código. Aunque técnicamente funcione y de el resultado correcto, aumenta el grado de "degeneración del software" (el tiempo en que se echa a perder un software, hasta que ya no es útil su funcionalidad).

Recuerdo una vez en que me toco auditar un sistema auxiliar en que se tenía que hacer una serie de sencillas copias desde una fuente de información a otra, aplicándole una serie de transformaciones. Las fuentes eran diversas y los destinos también, en este caso se cayó en el ejemplo de "casi se parece", mas sin embargo, si se elijo hacer un proceso completamente independiente para cada fuente de información, pensando en la sencillez de hacerlo así, por la rapidez y por resultar así más cómodo. Inevitablemente se aplico la regla "Va a cambiar" y la regla "Va a fallar". A lo largo del proyecto cambiaron en multitud de ocasiones el formato y las características de las fuentes de datos de los orígenes y de los destinos, teniendo que modificar una y otra vez todo el código fuente y replicando ese código a cada uno de los escenarios "que casi se parecían". Evidentemente todo esto se pudiera haber evitado con una correcta programación orientada a objetos, donde el código "casi igual" se hubiera abstraído para convertirse en la clase base, y las particularidades de cada fuente en clases heredadas.

En el caso anterior solo es un ejemplo sobre como querer hacer algo rápido, acaba convirtiéndose en retrabajos innecesarios e inevitables horas extras.

Un sistema lleno de código repetido, oculta un diseño pobre del sistema, que evidentemente tenderán a "salir a flote" en el peor momento posible.

Algunos motivos de un código repetido son:

  • Como mencionaba, un pobre diseño de la solución a resolver (precedido posiblemente por un pobre análisis).
  • Un equipo poco integrado y con poca comunicación. Me ha tocado ver que cada integrante de un equipo de construcción, creara exactamente una misma funcionalidad, pero cada uno implementándola de una manera diferente según sus necesidades y criterios.
  • Una arquitectura deficiente, que obliga una y otra vez a repetir lo mismo.
  • Programadores que quieren tomar atajos, o lo que es lo mismo "pan para hoy y hambre para mañana" (y generalmente "mañana", es el día siguiente, literalmente).
  • Falta de comprensión de la programación orientada a objetos: Esta es más frecuente de lo que se pudiera pensar, si bien muchos codificadores tiene claras las bases de la programación orientada a objetos, les difícil situar dichas bases en un contexto practico y real.
  • Miedo a tomar el código existente por si llegara a dejar de funcionar: unos tres programadores "miedosos" encadenados y el código será imposible de comprender...

Para evitar tener código repetido, algunas recomendaciones serian las siguientes:

  • Tener el principio de abstracción siempre en la mente, buscar siempre lo abstracto y común de cada problema, y paramétrizar (o heredar más bien) las particularidades de cada solución
  • Conocer los patrones de software, y buscar soluciones de que nos permitan reutilizar código (o más bien objetos) en ellos.
  • Equipos integrados con buena comunicación, que compartan una serie estándares.
  • Refactoring para eliminar las partes de código repetidas.

domingo, 21 de septiembre de 2014

Regla N°4 de la Ingeniería de Software: Todo sistema tiene un propósito.

Olvidar el propósito de un sistema, es condenarlo al fracaso.

Hace bastantes años unos de mis primeros acercamientos al mundo de sus sistemas fue cuando estaba probando un sistema de PC que permitía dos personas compartir información (casi siempre texto), a través de un módem de 3200 baudios conectado a un puerto COM y a la línea telefónica convencional. No era a través de Internet (que no era ni barata ni común en aquel entonces), sino directamente con un módem llamando a otro módem, a través de un numero de teléfono convencional. Una vez, establecida la conexión, ambos usuarios se les permita intercambia frases y pequeños datos a través de esta con una velocidad extremadamente lenta. No era gran cosa, y sus posibilidades eran limitadas (tan limitadas que hubiera sido mejor una llamada telefónica normal), pero quede fascinado con el mecanismo que permitía a dos computadoras estaban unidas, a kilómetros de distancia, intercambiando bytes entre ellas. Cuando intente compartir con entusiasmo mi entusiasmo sobre la posibilidad en la que dos ordenadores podían intercambiar datos entre ellos, una persona me señalo que lo realmente impresionante, es que dos personas podían intercambiar datos entre ellas. Jamás se me ocurrió verlo así, para mí solo era un intercambio de bytes, cuando realmente representaba una comunicación e intercambio de información entre personas, objetivo real del software.

Esa fue una de las primeras lecciones de ingeniera de software que recibí. No podemos tener una visión puramente técnica de los sistemas y olvidar las necesidades operativas o de negocio para los cuales son realmente creados.

Es común que olvidemos que lo que estamos creando no es un conjunto de variables, y líneas de código, sino que tiene que un significado y relevancia especial fuera del lenguaje de programación en el que los estamos creando. Por ejemplo, una trasferencia bancaria de un millón de dólares errónea no es una posición de memoria mal inicializada, sino que puede ser una pérdida real, o un sistema médico de monitores mal construido puede poner en juego vidas humanas. Hay que pensar en nuestros sistemas por lo que van a poner aportar en un negocio o los problemas que van a poder resolver y no como simples entes tecnológicos.

En metodologías clásicas, en las que esta separado el programador del cliente, y se llega a construir el software por una serie de fases encadenadas claramente diferenciadas, en normal que el programador se centre en los aspectos tecnológicos de la solución olvidando el motivo de la solución misma. Esto crea sistemas técnicamente correctos, pero que sirven de poco, al no satisfacer las necesidades del cliente. En la nuevas metodologías agiles, se intenta que el cliente sea parte del equipo, con lo que es mas fácil comprender su punto de vista y aprovechar su conocimiento para crear un sistema funcional.

Es importante recordar que nuestro sistema es un ente que después va a ser usado por alguien, si nuestra sistema no se pueda usar más que en un ambiento de desarrollo y por nosotros mismos, no resuelve nada. Igualmente Hay que poner el propósito (la necesidad, el por qué se hacen las cosas) en primer lugar y usarlo como nuestro camino y nuestra guía para encontrar la solución.