Feeds:
Entradas
Comentarios

Archive for the ‘Filosofada’ Category


(Aviso a navegantes: ¡Este post contiene mucho código escrito a altas horas de la noche!)

Hola de nuevo, en este post, el último sobre “extends”, voy a intentar mostrar alternativas a la herencia de implementación. Se trata de la técnica genérica llamada “composición y delegación”, técnica en la que se basan varios patrones de diseño, como el famoso “Command”, el “Strategy”, o el “Decorator” entre otros. Antes de nada voy a resumir lo contado hasta ahora. En el primer post vimos cómo usar la herencia de implementación de forma “ingenua” nos podía meter en un buen lío, ya que sin darnos cuenta podemos romper la encapsulación. En el segundo post intentamos arreglar los problemas usando la palabra reservada “final” y el patrón “Template Method”. Si bien al principio parecía que funcionaba, más tarde al ir añadiendo funcionalidades, nos dimos cuenta de que violábamos el principio DRY y se producía una explosión combinatoria de clases, además de dificultarnos el testing y tener que planear de antemano los “ejes” de extensibilidad de nuestra clase.

El problema más importante es la explosión combinatoria de clases y la violación del DRY. La base de este problema es que la lógica de filtrado, y de postprocesamiento de la inserción de elementos, no se encuentran debidamente encapsuladas y abstraídas, sino que están incrustadas dentro de las clases hijas de “ListaSencilla”.

Empezando por la lógica de filtrado, ¿no sería mejor si pudiéramos tener una clase donde realmente pudiéramos poner dicha lógica? De esa forma podríamos eliminar ésta de las clases “ListaAuditableConElementosSinEspacios”, “ListaAuditableConElementosLongitudPar”, “ListaConElementosSinEspacios” y “ListaConElementosLongitudPar”. Está claro que estas clases violan el principio de única responsabilidad, ya que implementan el contrato de “Lista” y además tienen lógica de filtrado. Si sacamos esa lógica de filtrado aparte, vamos a quitar responsabilidad de dichas clases y a simplificarlas.

Lo primero de todo sería definir una interfaz “EstrategiaDeFiltrado” que represente el contrato entre una implementación de “Lista” y la lógica de filtrado.

public interface EstrategiaDeFiltrado {
  boolean esInsertable(String elemento);
}

Sencillo, ¿verdad? Ahora sólo tengo que tener una implementación por estragia de filtrado en mi sistema. Primero para los elementos pares:

public final class FiltrarElementosLongitudImpar implements EstrategiaDeFiltrado {
  public FiltrarElementosLongitudImpar() {
    super();
  }
  @Override
  public boolean esInsertable(String elemento) {
    return elemento.length() % 2 == 0;
  }
}

Y ahora para los elementos con espacios:

public final class FiltrarElementosConEspacios implements EstrategiaDeFiltrado {
  public FiltrarElementosConEspacios() {
    super();
  }
  @Override
  public boolean esInsertable(String elemento) {
    return !elemento.contains(" ");
  }
}

Nótese ese “final” para cada clase. Ahora nadie podrá usar herencia de implementación de dichas clases y provocar el caos. Es también interesante notar que con este diseño puedo hacer unos tests muy sencillos de cada implementación de “EstrategiaDeFiltrado”. Esto pinta bien, pero aun no hemos arreglado nada. Vamos a ver como reutilizar estas implementaciones de “EstrategiaDeFiltrado” en nuestras listas. Primero un cambio pequeño en “ListaAuditableConElementosLongitudPar”:

public class ListaAuditableConElementosLongitudPar extends ListaAuditable {
  private EstrategiaDeFiltrado filtro = new FiltrarElementosLongitudImpar();
  public ListaAuditableConElementosLongitudPar(Auditor auditor) {
    super(auditor);
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return filtro.esInsertable(elemento);
  }
}

y lo mismo en “ListaAuditableConElementosSinEspacios”:

public class ListaAuditableConElementosSinEspacios extends ListaAuditable {
  private EstrategiaDeFiltrado filtro = new FiltrarElementosConEspacios();
  public ListaAuditableConElementosSinEspacios(Auditor auditor) {
    super(auditor);
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return filtro.esInsertable(elemento);
  }
}

Mmmm, si os fijais “ListaAuditableConElementosLongitudPar” y “ListaAuditableConElementosSinEspacios” tienen exactamente el mismo código, salvo que cada una usa una implementación concreta diferente de “EstragiaDeFiltrado”. Una violación del DRY muy clara. Además ese “new” es muy, pero que muy feo ¿No sería mejor pasar el colaborador, es decir, la implementación concreta de “EstrategiaDeFiltrado”, por el constructor? Esto nos elimina la duplicación y de paso podemos borrar una clase. ¡ Me encanta cuando puedo borrar código ! ¿Y a vosotros? Ahora ambas clases desaparecen y son sustituidas por “ListaAuditableConFiltro”:

public class ListaAuditableConFiltro extends ListaAuditable {
  private EstrategiaDeFiltrado filtro;
  public ListaAuditableConFiltro(Auditor auditor, EstrategiaDeFiltrado filtro) {
    super(auditor);
    this.filtro = filtro;
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return filtro.esInsertable(elemento);
  }
}

De forma análoga, “ListaConElementosLongitudPar” y “ListaConElementosSinEspacios” acaban siendo eliminadas y sustituidas por “ListaConFiltro”. Esto no me termina de convencer. Seguimos violando DRY. Por un lado “ListaConFiltro” y “ListaAuditableConFiltro” tienen lógica claramente duplicada. Por otro lado, el método protegido “esInsertable” es exactamente igual que la interfaz “EstrategiaDeFiltrado”. El código pide a gritos ser simplificado, así que, ¿qué tal si nos llevamos todo ese código común a la superclase “ListaSencilla”? El único problema es que de “ListaSencilla” heredan también listas que no tienen filtro, ¿qué hacemos? Podemos aplicar el patrón “null object” y crear una “EstrategiaDeFiltrado” que no haga nada. Veamos ese código:

public final class NuncaFiltrar implements EstrategiaDeFiltrado {
  public NuncaFiltrar() {
    super();
  }
  @Override
  public boolean esInsertable(String elemento) {
    return true;
  }
}

Por otro lado no queremos tener que configurar el colaborador filtro con la clase “NuncaFiltrar” cada vez que queramos una lista sin filtro. Por lo tanto necesitamos que el filtro sea una dependencia opcional dentro de “ListaSencilla”. El código quedaría:

public class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  private EstrategiaDeFiltrado filtro = new NuncaFiltrar();
  public ListaSencilla() {
    super();
  }
  @Override
  final public void insertar(String elemento) {
    if (!filtro.esInsertable(elemento))
      throw new ElementoRechazadoError(elemento);
    datos.add(elemento);
    procesamientoAdicionalTrasInsertarElemento(elemento);
  }
  @Override
  final public void insertarVarios(Collection elementos) {
    for (String elemento : elementos) {
      if (!filtro.esInsertable(elemento))
        throw new ElementoRechazadoError(elemento);
    }
    for (String elemento : elementos)
      insertar(elemento);
  }
  public final void configurarFiltro(EstrategiaDeFiltrado nuevoFiltro) {
    this.filtro = nuevoFiltro;
  }
  /**
   * Este método es invocado cada vez que un nuevo elemento
   * es insertado en la Lista
   *
   * @param elemento
   *        El elemento que acaba de ser insertado
   */
  protected void procesamientoAdicionalTrasInsertarElemento(String elemento)
  {
    // Intencionadamente en blanco
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

Obsérvese como se recibe el colaborador mediante un “setter” llamado “configurarFiltro”, y no por el constructor. También como se usa por defecto una instancia de “NuncaFiltrar” en caso de que no se configure nada. También hemos eliminado el método protegido “esInsertable”.

Finalmente hemos conseguido nuestro objetivo, hemos eliminado todas las subclases de “ListaSencilla” que creamos con el único fin de añadir lógica de filtrado. El patrón que estamos usando es el “Strategy” tal y como apareción en GoF. Podemos volver a usar el patrón “Strategy” para eliminar “ListaAuditable” y el método protegido “procesamientoAdicionalTrasInsertarElemento”. Para ello creamos una interfaz “PostProcesador”:

public interface PostProcesador {
  void postProcesar(String elemento);
}

y una implementación “Auditar”:

public class Auditar implements PostProcesador {
  private Auditor auditor;
  public Auditar(Auditor auditor) {
    super();
    this.auditor = auditor;
  }
  @Override
  public void postProcesar(String elemento) {
    auditor.elementoInsertado(elemento);
  }
}

Obsérvese que “Auditar” no es más que un patrón “Adapter”, entre la interfaz “PostProcesador” y “Auditor”. Además sospechosamente “Auditar” se parece mucho a “ListaAuditable”. Finalmente “ListaSencilla” queda:

public final class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  private EstrategiaDeFiltrado filtro = new NuncaFiltrar();
  private PostProcesador postProcesador = new NoProcesar();
  public ListaSencilla() {
    super();
  }
  @Override
  public void insertar(String elemento) {
    if (!filtro.esInsertable(elemento))
      throw new ElementoRechazadoError(elemento);
    datos.add(elemento);
    postProcesador.postProcesar(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    for (String elemento : elementos) {
      if (!filtro.esInsertable(elemento))
        throw new ElementoRechazadoError(elemento);
    }
    for (String elemento : elementos)
      insertar(elemento);
  }
  public void configurarFiltro(EstrategiaDeFiltrado nuevoFiltro) {
    this.filtro = nuevoFiltro;
  }
  public void configurarPostProcesador(PostProcesador nuevoPostProcesador) {
    this.postProcesador = nuevoPostProcesador;
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

Ahora “ListaSencilla” es “final”, sin ningún método protegido y hemos eliminado “ListaAuditable”. Hemos conseguido cubrir todos nuestros requisitos sin necesitar la “potencia” de la herencia de implementación. Además hemos simplificado los tests. Es muy sencillo hacer tests de las implementaciones de “EstrategiaDeFiltrado” y “PostProcesador”. También los tests de “ListaSencilla” pueden ser simplificados usando dobles de prueba.

Sólo nos queda una cosa, el famoso patrón “Factory”:

public final class NuevaLista {
  public NuevaLista() {
    super();
  }
  private ListaSencilla configurarAuditorEstricto(ListaSencilla lista) {
    lista.configurarPostProcesador(new Auditar(new AuditorEstricto()));
    return lista;
  }
  private ListaSencilla configurarFiltro(ListaSencilla lista,
      EstrategiaDeFiltrado filtro) {
    lista.configurarFiltro(filtro);
    return lista;
  }
  public Lista sencilla() {
    return new ListaSencilla();
  }
  public Lista auditable() {
    return configurarAuditorEstricto(new ListaSencilla());
  }
  public Lista sinElementosConEspacios() {
    return configurarFiltro(new ListaSencilla(),
        new FiltrarElementosConEspacios());
  }
  public Lista sinElementosLongitudImpar() {
    return configurarFiltro(new ListaSencilla(),
        new FiltrarElementosLongitudImpar());
  }
  public Lista auditableSinElementosConEspacios() {
    return configurarAuditorEstricto(configurarFiltro(new ListaSencilla(),
        new FiltrarElementosConEspacios()));
  }
  public Lista auditableSinElementosLongitudImpar() {
    return configurarAuditorEstricto(configurarFiltro(new ListaSencilla(),
        new FiltrarElementosLongitudImpar()));
  }
}

O si son ustedes partidarios de usar frameworks, pueden usar el framework “Spring” que da mucho juego para esto, y programarse exactamente la misma lógica que tenemos en “NuevaLista” pero en XML en vez de en JAVA (es que el XML queda más profesional). Con este patrón “Factory” podemos tener todas las combinaciones de “Lista”, “EstrategiaDeFiltrado” y “PostProcesador” que queramos, sin necesidad de volver a tocar una linea de código en ninguna de estas.

El patrón “Strategy” nos permite hacer una cosa que no podemos con “extends”, definir la combinación que queramos de “Lista”, “EstrategiaDeFiltrado” y “PostProcesador” en tiempo de ejecución.

Podríamos dejarlo aquí, pero he de comentar que “Strategy” comparte un problema con “Template Method”. Con ambos necesitamos planear de antemano que “ejes” de extensibilidad o composición vamos a tener. Esto hace que la clase “ListaSencilla” no sea tan sencilla. Estamos en cierto modo violando el principio de única responsabilidad, ya que está claro que la responsabilidad de “ListaSencilla” es simplemente gestionar la lógica de “Lista”. Además, estamos pagando una pequeña sobrecarga, al añadir lógica de llamada a filtrado y postprocesamiento en aquellos casos que realmente no la necesitamos. Cierto, es una sobrecarga muy pequeña, pero no deja de ser una mala señal. Yo lo que quiero es que “ListaSencilla” sea eso, “sencilla”. Quiero este código:

public final class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  public ListaSencilla() {
    super();
  }
  @Override
  public void insertar(String elemento) {
    datos.add(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    for (String elemento : elementos)
      insertar(elemento);
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

¡ Ah, que gusto volver a los viejos tiempos donde las listas eran listas y nada más ! Pero necesitamos filtrado y postprocesamiento, ¿qué hacemos? Recurrir a otro patrón del “GoF”, el “Decorator”. Podemos hacer una clase que admita una “EstrategiaDeFiltrado”, pero que no implemente nada de la lógica de la lista, sino que la delegue a un colaborador. O sea, la responsabilidad de esta nueva clase es simplemente orquestar a una “Lista” y a una “EstrategiaDeFiltrado”, sin que ninguna de las dos sepa nada. Creemos, para tal efecto, la clase “ListaConFiltrado”:

public final class ListaConFiltrado implements Lista {
  private Lista lista;
  private EstrategiaDeFiltrado filtro;
  public ListaConFiltrado(Lista lista, EstrategiaDeFiltrado filtro) {
    super();
    this.lista = lista;
    this.filtro = filtro;
  }
  @Override
  public void insertar(String elemento) {
    if (!filtro.esInsertable(elemento))
      throw new ElementoRechazadoError(elemento);
    lista.insertar(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    for (String elemento : elementos) {
      if (!filtro.esInsertable(elemento))
        throw new ElementoRechazadoError(elemento);
    }
    lista.insertarVarios(elementos);
  }
  @Override
  public String describirContenido() {
    return lista.describirContenido();
  }
}

También podemos hacer lo mismo y crear una “ListaConPostProcesamiento”:

public final class ListaConPostProcesamiento implements Lista {
  private Lista lista;
  private PostProcesador postProcesador;
  public ListaConPostProcesamiento(Lista lista, PostProcesador postProcesador) {
    super();
    this.lista = lista;
    this.postProcesador = postProcesador;
  }
  @Override
  public void insertar(String elemento) {
    lista.insertar(elemento);
    postProcesador.postProcesar(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    lista.insertarVarios(elementos);
    for (String elemento : elementos)
      postProcesador.postProcesar(elemento);
  }
  @Override
  public String describirContenido() {
    return lista.describirContenido();
  }
}

¿Necesitaremos una clase “ListaConFiltradoYPostProcesamiento”? No, tal objeto se puede crear componiendo una “ListaConFiltrado” con una “ListaConPostProcesamiento”, pasando la última como argumento al constructor de la primera. De hecho ahora podemos eliminar “NuncaFiltrar” y “NoProcesar” ya que no las usamos. Además volvemos a simplificar los tests de “ListaSencilla”, ¡ ya no necesitamos dobles de prueba ! Por otro lado los tests de “ListaConFiltrado” y “ListaConPostProcesamiento” son muy sencillos, basta usar unos dobles de prueba para comprobar que las llamadas se delegan en el orden oportuno, y que no se llama ni a “insertar” ni a “insertarVarios” si el filtro nos devuelve “false”.

Si os fijais estoy usando varios patrones que no usan herencia de implementación, sino una filosofía de diseño general llamada “composición y delegación“. Esta filosofía consiste en tener objetos sencillos, y bien encapsulados, que puedan ser combinados mediante composición, para obtener nueva funcionalidad. Tanto “Strategy”, como “Decorator” usan esta técnica.

Siguiendo con el ejemplo, todos estos cambios afectan a nuestra factoría “NuevaLista”, que ahora queda:

public final class NuevaLista {
  public NuevaLista() {
    super();
  }
  public Lista sencilla() {
    return new ListaSencilla();
  }
  public Lista auditable() {
    return new ListaConPostProcesamiento(sencilla(), new Auditar(
        new AuditorEstricto()));
  }
  public Lista sinElementosConEspacios() {
    return new ListaConFiltrado(sencilla(), new FiltrarElementosConEspacios());
  }
  public Lista sinElementosLongitudImpar() {
    return new ListaConFiltrado(sencilla(), new FiltrarElementosLongitudImpar());
  }
  public Lista auditableSinElementosConEspacios() {
    return new ListaConFiltrado(auditable(), new FiltrarElementosConEspacios());
  }
  public Lista auditableSinElementosLongitudImpar() {
    return new ListaConFiltrado(auditable(),
        new FiltrarElementosLongitudImpar());
  }
}

Ahora ese “Factory” es más sencillo y elegante que antes al haber eliminado los “setter”. La clase “NuevaLista” simplemente compone o combina las distintas implementaciones de lista. Desgraciadamente cada vez que necesitemos una nueva combinación tenemos que tocar “NuevaLista” (o el XML de “Spring”). Para solventar ese problema podríamos aprovechar la potencia de “componer y delegar” y hacernos un mini DSL interno. Veamos cómo:

public final class FabricaDeListas {
  private List<EstrategiaDeFiltrado> filtros = new ArrayList();
  private List<PostProcesador> postProcesadores = new ArrayList();
  public FabricaDeListas() {
    super();
  }
  public FabricaDeListas(List filtros,
      List postProcesadores) {
    this();
    this.filtros.addAll(filtros);
    this.postProcesadores.addAll(postProcesadores);
  }
  public Lista fabricar() {
    Lista resultado = new ListaSencilla();
    for (PostProcesador procesador : postProcesadores)
      resultado = new ListaConPostProcesamiento(resultado, procesador);
    for (EstrategiaDeFiltrado filtro : filtros)
      resultado = new ListaConFiltrado(resultado, filtro);
    return resultado;
  }
  public FabricaDeListas conPostProcesador(PostProcesador procesador) {
    FabricaDeListas fabrica = new FabricaDeListas(this.filtros,
        this.postProcesadores);
    fabrica.postProcesadores.add(procesador);
    return fabrica;
  }
  public FabricaDeListas conAuditor(Auditor auditor) {
    return conPostProcesador(new Auditar(auditor));
  }
  public FabricaDeListas conAuditoriaEstricta() {
    return conAuditor(new AuditorEstricto());
  }
  public FabricaDeListas conFiltro(EstrategiaDeFiltrado filtro) {
    FabricaDeListas fabrica = new FabricaDeListas(this.filtros,
        this.postProcesadores);
    fabrica.filtros.add(filtro);
    return fabrica;
  }
  public FabricaDeListas sinElementosLongitudImpar() {
    return conFiltro(new FiltrarElementosLongitudImpar());
  }
  public FabricaDeListas sinElementosConEspacios() {
    return conFiltro(new FiltrarElementosConEspacios());
  }
}

Simplemente usamos un patrón “Value Object” mezclado con un “Builder”. La ventaja de este enfoque es que sea cual sea la combinación que quiera la puedo obtener fácilmente con “FabricaDeListas”, sin tener que tocar una sola linea de código dentro de “FabricaDeListas”. Puedo hacer cosas como:

Lista auditadaSinElementosConEspacios = fabrica
                                           .sinElementosConEspacios()
                                           .conAuditoriaEstricta()
                                           .fabricar();

Mucho más legible, ¿no?

Tras este largo camino, ¿alguien considera que el código resultante es ilegible, poco mantenible o poco potente? ¿Para que nos sirvió el “extends” si no fue sólo para darnos dolores de cabeza? Hemos aprendido una valiosa lección, cualquier diseño basado en “extends” y “Template Method” puede ser refactorizado en un “Strategy”, y éste a su vez en un “Decorator”. Por lo tanto, ¿para que perder el tiempo usando “extends” si puedo hacer un “Strategy” o un “Decorator”? Al fin y al cabo el diseño basado en “composición y delegación” es más seguro y más potente.

Sin embargo aquí entra en juego las herramientas que te da el lenguaje. Una pista nos la puede dar la clase “Auditar”, donde estamos creando una clase para simplemente adaptar la interfaz “PostProcesador” a “Auditor”, pero realmente no hay ninguna lógica ahí. A mi me parece que esa clase es un buen desperdicio de código, pero la verdad es que no se puede hacer mucho más en JAVA. Otro ejemplo es el método “describirContenido” en “ListaConFiltrado” y “ListaConPostProcesamiento”. Ese método no hace nada, sólo delega la llamada sin añadir lógica, ¡ qué desperdicio de líneas de código ! Tampoco podemos hacer mucho más en leguaje JAVA. Afortunadamente tenemos IDEs como “Eclipse”, “NetBeans” o “IntelliJ” que nos proporcionan el poderoso wizard “Generate Delegate Methods…”, y no las tenemos que escribir.

Cuando veo este tipo de detalles es cuando pienso que JAVA no está envejeciendo muy bien. Gracias señores estandarizadores de JAVA, perdieron ustedes una gran oportunidad de mejorar el lenguaje cuando lanzaron JAVA 5. En vez de parir ese aborto de “Map<? extends Enumerable, List<? extends X>>”, debían haber mejorado el lenguaje para añadir una forma sencilla de aplicar la técnica de “composición y delegación”, ¿tal vez una palabra clave “delegate”? ¿O soporte para funciones como primer ciudadano? No, ¿para qué? Los genéricos son mucho más “molones” y para reutilizar código ya tenemos el “extends”. Todo esto es mucho más simple de hacer en JavaScript, Groovy o Ruby. A ver si en JAVA 7 o JAVA 8 se ponen las pilas.

¡ Gracias por aguantar hasta el final de este post tan largo !

Read Full Post »


Hola a todos, este post me lo venía pidiendo el cuerpo desde hace tiempo. Es común oírme decir que la herencia de implementación (“extends” para los javeros) es una mierda, y cada vez que lo digo veo caras de estupefacción e incluso recibo alguna que otra crítica. La verdad es que no lo entiendo bien ya que esto se sabe desde hace bastante tiempo, es más hasta tiene un nombre: “problema de la clase base frágil”, aunque yo prefiero decir que “extends es una mierda”. Incluso, algunos dicen que James Goslin, el padre de JAVA, reconoció que incluir “extends” en el lenguaje fue un gran error. Yo considero que “extends” no tiene nada que ver con la OO (al menos no con el concepto original), sino que se incluyó porque para algo tenían que servir las clases, y se pensó que se podrían usar para reutilizar código mediante la herencia de implementación.

Vamos al lío, os voy a contar una historia. Suponed la siguiente interfaz:

public interface Lista {
	void insertar(String elemento);
	void insertarVarios(Collection elementos);
	String describirContenido();
}

Se trata de una interfaz sencilla que representa una lista, con dos métodos para añadir contenido. Veamos ahora una implementación de tal interfaz:

public class ListaSencilla implements Lista {
	private List datos = new ArrayList();

	public ListaSencilla() {
		super();
	}

	@Override
	public void insertar(String elemento) {
		datos.add(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		for (String elemento : elementos)
			this.insertar(elemento);
	}

	@Override
	public String describirContenido() {
		StringBuilder descripcion = new StringBuilder("Contenidos: ");
		for (String elemento : datos) {
			descripcion.append("'");
			descripcion.append(elemento);
			descripcion.append("' ");
		}
		return descripcion.toString();
	}
}

¿Sencillo verdad? Es una implementación muy directa y no tiene mayor misterio. Ahora supongamos que a otro miembro del equipo, que no escribió la clase “ListaSencilla”, le encargan desarrollar una implementación de “Lista” que cada vez que se añada un elemento a ésta, informe a un objeto auditor de tal suceso. Como nuestro desarrollador está enfocado en el objetivo de esta nueva clase, que es informar al auditor, y no le interesa volver a reimplementar y duplicar la lógica de inserción de datos, decide reutilizar código. ¿Qué mejor manera de utilizar el venerable “extends”? Al fin y al cabo, la herencia de implementación es la verdadera ventaja de la OO y su mecanismo más potente para reutilizar código. Mirando la especificación de “Lista” y conociendo que ya hay una implementación “ListaSencilla” disponible, le sale esto:

public class ListaAuditable extends ListaSencilla {
	private Auditor auditor;

	public ListaAuditable(Auditor auditor) {
		super();
		this.auditor = auditor;
	}

	@Override
	public void insertar(String elemento) {
		super.insertar(elemento);
		this.auditor.elementoInsertado(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		super.insertarVarios(elementos);
		for (String elemento : elementos)
			this.auditor.elementoInsertado(elemento);
	}
}

El desarrollador ni se molesta en probarlo, obviamente va a funcionar, él ha respetado el contrato de la interfaz y está usando a los amiguetes “extends” y “super”. Más tarde le llega un bug, ¡resulta que el auditor es notificado por cada elemento por duplicado! Desconcertado nuestro programador recurre a esa poderosa arma de la ingeniería del software, el depurador, y consigue aclarar el misterio. La implementación del método “insertarVarios” en la superclase llama internamente a “insertar”, que como está sobreescrito en la clase hija también notifica al auditor, pero claro, “insertarVarios” de la clase hija también notifica al auditor por su cuenta, y de ahí la doble notificación al auditor. Mas claro agua, y si no lo habéis entendido, ¡depurad!

El desarrollador aprende la valiosa lección de que no sólo debe saber el contrato del código que quiere reutilizar, sino que debe analizar con detalle como está diseñada internamente la clase a reutilizar. Ya sabía él que todo esto del diseño por contrato y las interfaces no eran más que paparruchas para vender libros y cursos. Tras pensar detenidamente cambia el código y le queda:

public class ListaAuditable extends ListaSencilla {
	private Auditor auditor;

	public ListaAuditable(Auditor auditor) {
		super();
		this.auditor = auditor;
	}

	@Override
	public void insertar(String elemento) {
		super.insertar(elemento);
		this.auditor.elementoInsertado(elemento);
	}
}

Genial, bug resuelto de la mejor manera posible: borrando código. Más contento que unas castañuelas, el desarrollador se va a casa con la satisfacción del buen trabajo cumplido.

Pasa el tiempo, y un desarrollador senior, revisando la clase “ListaSencilla”, e ignorante de que esta clase tiene más descendientes que Gengis Khan, decide optimizar el método “insertarVarios”. Decide ahorrarse el bucle, y que leches, reutilizar código de la clase “ArrayList” que para eso viene en la JDK, estos desarrolladores junior sufren del síndrome NIH… La nueva versión:

public class ListaSencilla implements Lista {
	private List datos = new ArrayList();

	public ListaSencilla() {
		super();
	}

	@Override
	public void insertar(String elemento) {
		datos.add(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		datos.addAll(elementos);
	}

	@Override
	public String describirContenido() {
		StringBuilder descripcion = new StringBuilder("Contenidos: ");
		for (String elemento : datos) {
			descripcion.append("'");
			descripcion.append(elemento);
			descripcion.append("' ");
		}
		return descripcion.toString();
	}
}

¡ Qué sencillez ! ¡ Qué elegancia ! No por nada es un “lead developer” que se conoce la JDK al dedillo. Otro que se va para casa inconsciente de la que acaba de armar. ¿Por qué debería preocuparse? Al fin y al cabo el contrato de la interfaz “Lista” se cumple, y para demostrarlo tiene un test automatizado de hermoso color verde. Mientras tanto, la “ListaAuditable” y posiblemente muchas más subclases de “ListaSencilla” comienzan a fallar sin previo aviso.

Al día siguiente, el “lead developer” decide arreglar el problema, mira a ver el código de “ListaAuditable” y lo solventa en menos que canta un gallo. El código queda así:

public class ListaAuditable extends ListaSencilla {
    private Auditor auditor;

    public ListaAuditable(Auditor auditor) {
        super();
        this.auditor = auditor;
    }

    @Override
    public void insertar(String elemento) {
        super.insertar(elemento);
        this.auditor.elementoInsertado(elemento);
    }

    @Override
    public void insertarVarios(Collection elementos) {
        super.insertarVarios(elementos);
        for (String elemento : elementos)
            this.auditor.elementoInsertado(elemento);
    }
}

¿Os resulta familiar ;-) ?

“Tonto desarrollador junior”, piensa el lead developer, “se nota que en vez de fijarse en la interfaz y los tests, se ha mirado la implementación de la superclase, y por ahorrarse un método la que liado…”. En fin, muy triste :-(

Algunos de vosotros pensará que he hecho trampas, que no he usado “bien” la herencia de implementación y que soy un petardo de programador. En el siguiente post explicaré como hacerlo correctamente. Pero ésto no es lo importante, lo que quiero mostrar es que tenemos un mecanismo de reutilización de código, la herencia de implementación, que es más peligrosa que una caja de bombas. Yo prefiero no usar un mecanismo con el que tengo que andarme con pies de plomo, aunque me haya leído y entendido el contrato del código que quiera reutilizar. La herencia de implementación no es segura porque rompe la encapsulación, y necesito saber cómo está implementada la clase padre para poder heredar de ella de forma segura. Todo esto se produce porque mezcla el estado e implementación privada de la superclase con la de la clase hija en una única instancia, en vez de mantenerlas separadas. ¿No sería mejor tener un mecanismo de reutilización de código que siempre fuera seguro? Sí, claro que existe, y lo veremos más adelante.

Read Full Post »


Hola a todos, pensaba hacer una reseña al uso sobre el SpringIO 2011 Madrid, pero las distintas sesiones a las que asistí me han dejado una idea bastante persistente en la cabeza: VMWare se está posicionando como clara competencia de Oracle en el mundo de las aplicaciones empresariales JAVA (plataforma JVM mejor dicho). Como la mayoría de vosotros sabeis VMWare compró SpringSource, así que ahora todas las decisiones estratégicas sobre el desarrollo de Spring se toman con la participación y el consentimiento de VMWare. Pensar otra cosa sería pecar de excesiva inocencia.

En la conferencia me percaté de que varios temas se repetían una y otra vez en las distintas charlas: Grails, Cloud Computing, noSQL y “Portable Service Abstractions”. Comentaré primero lo que se habló sobre cada uno de estos temas y después explicaré porqué todos ellos me hacen pensar que VMWare se postula como seria competencia de Oracle.

Sin saber mucho de Grails, me da la impresión que es un framework “orientado a la base de datos”. En los ejemplos que he visto siempre se habla del MVC, pero todos los modelos Grails que he visto no son más que JavaBeans (a.k.a. registros COBOL recauchutados), eso sí, codificados en Groovy, que es mas “cool”. Después tienes las vistas, que no son más que JSPs (perdonad, GSPs) y un controlador escrito en Groovy que en realidad es un controlador de Spring MVC (que es casi igual a un backing bean de JSF). Vamos, más modelo anémico. Ciertamente es mucho mejor que un modelo anémico que podamos hacer en JAVA, y sí, no hay que picar apenas código (se agradece), con lo cual considero a Grails la plataforma perfecta para este tipo de aplicaciones. Si estoy metiendo la pata perdonadme, pero ya sabéis que me hierve la sangre con este tema ¡ Si alguien sabe como hacer buena OO con Grails que me lo diga !

Dejando de lado esta diatriba, la charla a la que asistí de Grails fue muy interesante. Peter Ledbrook explicó de forma bastante clara como integrar Grails con el resto de la empresa. ¿Qué hacemos si queremos hacer un build con ANT? ¿Y con MAVEN? ¿Y si ya existe un modelo hecho con JPA? ¿Y si resulta que el administrador de base de datos nos obliga a una determinada nomenclatura? La verdad es que resolvió bastante bien todos estos escenarios, aunque la parte de integración con MAVEN me dio la impresión de estar un pelín verde. Quizás el mayor problema es cuando ya existe un esquema de base de datos que tenemos que respetar. Aquí es donde la solución que dio no me terminó de parecer clara, y me pareció que proponía que se hiciera un modelo JPA y se integrara éste modelo con Grails. Está claro que en SpringSource se han dado cuenta de que uno de los obstáculos principales para que Grails triunfe es poder integrarse con los elementos más usuales en un sistema empresarial.

Otro punto que se habló de Grails fue integrar GORM (motor de mapeo Objeto/Relacional de Grails) con bases de datos noSQL. Se mencionó este tema en la segunda key note e @ialcazar me comentó un poco sobre una sesión que se dio a este respecto. No termino de ver como se puede integrar un motor de mapeo objeto/relacional con una base de datos no relacional ¡ Precisamente una de las ventajas de una base de datos noSQL es que no necesitamos un mapeo Objeto/Relacional ! No termino de verle el sentido técnico, pero sí el de marketing. En cualquier caso creo que es una iniciativa muy interesante de la comunidad de Grails, esperemos que lo consigan sin cargarse la escalabilidad que nos da un noSQL. También noté preocupación por el rendimiento de Grails, y se ve que están trabajando muy duro para mejorarlo.

Otro tema que estuvo bastante presente fue el Cloud Computing. Durante la primera key note y alguna que otra charla se habló de que el objetivo a medio plazo es preparar a Spring para permitir desarrollar aplicaciones en la nube. Realmente no termino de ver que problema hay con las APIs actuales de Spring y la nube, así que todo esto me suena a simple marketing. En este sentido me hubiera gustado que hablaran de tecnologías como vFabric o GemFire, pero claro, aunque son parte de VMWare, técnicamente no forman parte de Spring, con lo que hubiera estado fuera de lugar nombrarlas. Tal vez cuando se hablaba de adaptar Spring a la nube se refirieran a conseguir mayor rendimiento, y sobre todo escalabilidad y elasticidad, cosa fundamental en este tipo de entornos.

Quizás en este sentido se entienda el esfuerzo por integrarse con sistemas de persistencia noSQL, que proporcionan mayor escalabilidad a las aplicaciones. Una de las charlas más interesantes fue la de Spring Data de Costin Landau. Realmente me gustó la propuesta de Spring de montar un API “estándar” para noSQL, sobre todo teniendo en cuenta que JEE no proporciona nada comparable. En concreto las APIs de bajo nivel y el concepto de “repositorio” me resultó interesante. También la idea de dar cobertura a bases de datos documentales y orientadas a grafos me resultó bastante atractiva. Sin embargo a otras propuestas, como incorporar un sistema de persistencia mixto que mantuviera parte del modelo en BBDD relacional y parte en noSQL no acabo de verles el sentido. Tal vez sea un compromiso de “marketing” para que a la gente no le de miedo adoptar noSQL, ya que seguirían teniendo el respaldo de bases de datos tradicionales. A mi simplemente me parece una postura originada por el miedo de la gente a noSQL. Ya sabéis que pienso que si el sistema tiene las consultas bien definidas, el enfoque noSQL es totalmente superior al tradicional, y que sólo usaría un sistema SQL si realmente el conjunto de consultas las va a decidir el usuario en runtime, como son el caso de bussiness inteligence y data mining por ejemplo.

En la línea de noSQL la charla sobre Redis de Alberto Gimeno fue curiosa. Personalmente pienso que Redis está un poco verde todavía, aunque a alguno le puede interesar el concepto de clave/valor avanzado. Al contrario que un clave/valor simple, el valor almacenado puede ser no sólo un array de bytes, sino estructuras de datos como contadores, listas, mapas o sets. Para cada tipo de datos REDIS proporciona un conjunto de operaciones específicas, que se pueden ejecutar de forma atómica, y extienden el paradigma clásico del clave/valor. No termino de ver por qué esta complejidad extra es necesaria, pero sí que es verdad que facilita a un recién llegado al noSQL la transición desde el mundo de las BBDD relacionales. En todo caso me alegró ver que la gente empieza a usar sistemas noSQL, ya sabéis de que pié cojeo.

Otro tema que fue hilo conductor de esta conferencia fue el concepto de “Portable Service Abstraction”. Este nombre es una forma “cool” de decir que si desarrollamos contra interfaces “estándar”, nuestro código será portable a otros entornos. Es una idea muy vieja, y es lo que intentó JEE. Como ejemplos de estas “Portable Service Abstraction” podemos tener Spring Data, Spring REST, Spring AMQP, Spring Social, Spring Mobile y Spring Android. En general me gusta el enfoque de Spring de hacer APIs sencillas, y de reaccionar de forma rápida y ágil a las necesidades de los desarrolladores. Sin embargo no es oro todo lo que reluce. No entiendo la necesidad de Spring REST, desde mi punto de vista JAX-RS era un estándar más que suficiente. De hecho ambos estándares son tan similares, que es difícil distinguir dos aplicaciones hechas sobre cada una de ellas. Por otro lado Spring Mobile y Spring Android me decepcionaron, ya que apenan aportan valor. El primero básicamente sólo te detecta el tipo de dispositivo móvil y te redirige a una página de presentación u otra en función de éste. Vamos, nada que no exista ya en las múltiples arquitecturas multicanal “clásicas”. Spring Android es sólo un cliente para hacer llamadas al servidor. Los que sí me gustaron bastante fueron Spring Social, Spring Data y Spring AMQP. En concreto este último, ya que me pareció que el RabbitMQ era genial.

Toda esta información que recibí en la SpringIO me hizo ver que VMWare está intentando atacar a Oracle por varios frentes:

  • Spring como substituto de JEE. Aunque Spring es compatible con JEE, en el fondo lo que pretende es que programemos contra sus APIs en vez de las de JEE, aduciendo motivos de portabilidad. Esto es gracioso, ya que JEE nos propone exactamente lo mismo: una aplicación programada contra JEE es portable al ser éste el estándar de iure para el desarrollo de aplicaciones empresariales. Así que, ¿cuál API es más estándar, JEE o Spring? Esta claro que Spring ha sabido evolucionar sus APIs de forma más ágil y práctica que JEE, con todos sus comités. Todo esto es una amenaza clara contra Oracle, que es la actual “dueña” de JEE.
  • Tomcat y Spring tc Server como substitutos de WebLogic (y de paso de WebSphere). Si vamos a programar contra Spring en vez de contra JEE no tiene sentido comprar un servidor JEE completo como WebLogic o WebSphere, nos basta un contenedor de Servlets como Tomcat. Para los que no lo sepáis WebLogic es de Oracle.
  • AMQP y RabbitMQ contra JMS. Aunque RabbitMQ se integra con JMS, en realidad es una alternativa viable a JMS, y a cosas como MQSeries.
  • Grails como substituto de JSF. Como ya sabréis el modelo de programación web JEE es JSF. De hecho Oracle ha invertido en su implementación de JSF, llamada Oracle ADF, y en un IDE especial para ADF basado en Eclipse. Oracle ADF y su IDE nos permite hacer aplicaciones orientadas a base de datos muy rápidamente (o eso me han contado). Está claro que SpringToolSuite junto con Grails es competencia directa de esta tecnología.
  • noSQL contra las bases de datos relacionales. Como sabéis Oracle es dueña no sólo de la base de datos relacional más famosa del mundo, sino que con la compra de Sun, puede hacerse con el control de MySQL. Obviamente apostar por noSQL es un golpe directo a la linea de flotación de Oracle. Curiosamente el único sistema noSQL del que se habló con detalle en la conferencia fue de REDIS, que está “esponsorizado” por VMWare.
  • Cloud Computing contra hosting tradicional. El Cloud Computing es un escenario que creo beneficia más a VMWare que a Oracle. El primero, ha hecho una apuesta fuerte en este sentido con vFabric y vmForce. Si tienes una instalación propia, puede venir un comercial de Oracle a venderte WebLogic y la base de datos de turno. En un cloud no. Si convences a una empresa para irse al cloud, es más que probable que sus bases de datos Oracle queden en desuso.

En definitiva, Spring pretende suplantar a JEE como plataforma estandarizada para el desarrollo de aplicaciones empresariales con JAVA. Bueno, tal vez yo sea un poco suspicaz, pero esto es lo que realmente leo entre lineas de la conferencia SpringIO. Por cierto fue un éxito de público, y en general el nivel de las sesiones y talleres fue bastante alto. Enhorabuena a la organización, ya que no creo que con 20 euros se pudiera haber hecho mejor. ¿A alguien le pareció caro? A mi no.

Read Full Post »


Hola de nuevo a todos, el tema que voy a tratar en este post en su momento me ocasionó un shock acompañado de su posterior revelación (es que soy un tío simple e impresionable).

En el anterior post de esta serie comenté que había que valorar el coste de complicar nuestro diseño contra el tiempo disponible y los beneficios a largo plazo que nos iba a reportar. En el caso de un proyecto gestionado mediante el paradigma ágil, el equipo de desarrollo debe ser capaz de aceptar muchos cambios a un ritmo alto. Estos cambios provienen al menos de dos fuentes diferentes: cambios o malentendidos a nivel funcional, y otros ocasionados por tener que entregar funcionalidad de forma continua, lo que nos deja en la posición de tener que cambiar y extender nuestro diseño de forma interminable. Como conclusión tenemos que nuestro diseño debe ser barato y rápido de cambiar y que toda técnica orientada a este fin es valiosa.

¿Cómo podemos abaratar y agilizar los cambios en el software? ¿DRY, SOLID, OO, Modelo de dominio? Bueno, no vayamos tan rápido, centrémonos primero en lo esencial. Pensad un poco en la siguiente pregunta, ¿qué es lo que hacemos siempre (siempre) cada vez que tenemos que introducir un cambio en nuestro sistema? …. Pues es obvio, siempre hay que leer el código ya existente y entenderlo para empezar a saber como cambiarlo. ¿Cuántas veces os ha pasado que no entendéis el código que escribisteis hace dos meses? ¿Y el de la semana pasada? A mi me ha ocurrido de no entender código del día anterior, ¡ menuda bazofia ! Si no podéis entender vuestro código, imaginaros la comprensión que tendrán de éste vuestros compañeros cuando les toque mantenerlo o añadir un cambio.

Para hacer que vuestro código se pueda modificar de forma rápida y barata, es indispensable que sea legible y comprensible (dios, que shock, ¡ no voy a poder dormir !). Este principio es muy básico y para mi es el principio de “calidad mundana” más importante que existe. Un nombre popular para este principio es el de “código expresivo”. Es un principio tan esencial que aunque no seáis ágiles lo vais a necesitar. Tal vez no sea tan crucial para proyectos no ágiles, pero sí es muy importante en la fase de mantenimiento, ¿alguno ha mantenido aplicaciones ilegibles alguna vez? Seguro que sí. Quizás el ejemplo clásico de “no ágil” y necesidad de “código expresivo” sea el caso del mantenimiento de aplicaciones legacy. Cuando hay que mantener una base de código que es difícil de leer, a veces es más barato reescribirla desde cero.

Un escenario más confuso se produce con aplicaciones de “campaña”, de usar y tirar. La aplicación debe estar lista para una fecha (time to market crítico), se usará durante dos o tres meses, y después se desechará. En estas aplicaciones no va a haber mantenimiento evolutivo. En un entorno ágil vamos a seguir necesitando que el código sea expresivo, ya que los cambios llegarán seguro durante la fase de construcción. En un entorno no ágil no habrá cambios durante la fase de construcción (¿seguro?), gracias a un fabuloso contrato cerrado, con lo que alguno podrá pensar que no es necesario que el código sea legible, y que la necesidad de facilidad de cambio (calidad mundana) tiende a cero. A esas mentes preclaras les deseo que entreguen el software con cero defectos, porque como empiecen a llegar bugs en la fase de explotación, que para colmo dura unas pocas semanas o meses, no sé como los vas a resolver a un ritmo suficientemente rápido como para que los de negocio queden satisfechos. Por lo tanto la legibilidad del código no sólo es importante para aceptar el cambio (ser ágil), sino también para arreglar bugs. Como todo el software tiene bugs (escondidos tal vez), todo el software, ágil o no ágil, de “campaña” o “estratégico”, debe ser legible.

¿Qué técnicas nos pueden ayudar a conseguir este fin? Pues hay muchas:

  • Pair programming (programación por parejas). Sí, señor, no hay nada como un compañero leyendo por encima de tu hombro lo que escribes, tratando de entenderlo y buscándole defectos, como para que tu código de repente se vuelva legible (para los demás). Afortunadamente cuando cambie el turno te podrás vengar, je, je }:-)
  • Peer review (revisión de código). El pair programming no es más que revisión de código continuo, ¿por qué necesitamos otra etapa de revisión de código? Pues muy sencillo, a veces las parejas desarrollan un sentido de la identidad compartida, y un sentido de la legibilidad un poco particular. Para evitar esto es conveniente rotar las parejas, y también que otros compañeros, y deseablemente alguien un poco externo al proyecto (normalmente alguien respetado), revise el código. Por supuesto en la medida de lo posible y las restricciones de tiempo y dinero.
  • Domain Model (modelos de dominio). Consiste en modelar de forma explícita conceptos usados en el dominio del problema. De esta forma cuando veamos el código reconoceremos conceptos del problema directamente en el código, lo que nos ayudará a entender éste. Este enfoque es muy popular y tuvo como repercusión la aparición del paradigma OO, en el que aparece el concepto de objeto que nos permite modelar los conceptos de nuestro problema de forma directa, algo muy costoso de hacer con un lenguaje procedural.
  • Fluent API (API fluida o API sexy) o Internal DSL y también External DSL. Como ya comenté en un post anterior, los DSL nos permiten una mayor legibilidad, al modelar directamente el lenguaje de dominio de nuestro problema. Este problema puede ser la funcionalidad de negocio, pero también puede ser un problema técnico (procesamiento de XML, de BBDD, seguridad, etc). Notad que en los DSLs el énfasis está en el lenguaje y la dinámica del problema, mientras que en el modelo de dominio nos centramos en la estructura estática. Esto hace ambas que ambas técnicas sean complementarias unas de otras.

Como veis la mayoría de las técnicas de diseño ágil tienen como una de sus ventajas, si es que no su principal ventaja, el hacer el código más legible. Algunas son más potentes que otras, y también más costosas que otras. Como siempre la decisión debéis tomarla teniendo en cuenta el coste. Por ejemplo, construir un DSL, ya sea interno o externo, puede tener costes altos, sobre todo en algunas plataformas tecnológicas. Otras como TDD, Pair Programming o modelado de dominio puede tener algún beneficio adicional.

Por otro lado, para que lo penséis, ¿se os ocurre alguna técnica no ágil que facilite la legibilidad del código? ¿Wizards y generadores de código? ¿Copy&Paste? ¿DFD? ¿Alguna idea? Al fin y al cabo el paradigma no ágil también necesita que su código sea legible.

No lo he puesto en la lista anterior, pero algunos refactors pueden ayudarnos con la legibilidad de nuestro código. El problema es que esos mismos refactors en diferentes circunstancias nos hacen perderla. En general yo uso la cantidad de legibilidad que gana mi código como guía para saber si hacer el refactor merece la pena o no.

Finalmente, si usáis TDD, tenéis que tener en cuenta que la legibilidad del código no sólo se aplica al código de producción, sino también al código de test. Para mi, es incluso más importante que el código de test sea legible a que lo sea el de producción, ¿por qué? Simplemente porque el código de test define la API de vuestro sistema (o componente), son los ejemplos de uso. Si estos ejemplos (los tests) son legibles, ¡ habréis conseguido que vuestro código de test sea a la vez la documentación ! Nada de irse al javadoc, un buen vistazo a los escenarios de vuestros tests y un programador ya sabrá como manejar vuestro sistema (o componente). Por otra parte, una buena suite de pruebas, bien legible, y refactorizada adecuadamente, os proporciona además, un “fluent API” o “internal DSL”. O sea, que simplemente por hacer que vuestro TDD sea legible, obtenéis un DSL y una guía del programador (con how to’s), gratis de serie con vuestro sistema con pruebas automáticas. A mi esta idea me seduce bastante.

Por supuesto si no practicáis TDD todo esto os dará igual :-P —> ¡ A qué esperais para empezar con vuestro TDD ! ¡ Es que acaso pensáis que el TDD sirve para “probar” !

Un punto importante a tener en cuenta es la plataforma tecnológica en la que nos movemos. Por ejemplo, algunos lenguajes nos permiten hacer nuestro código más expresivo que otros. Comparad en este sentido JAVA o COBOL con Ruby, Scala, Groovy, etc. La mayoría de los lenguajes modernos van en la dirección de facilitar cada vez más que nuestro código sea legible. De hecho la evolución de los lenguajes de programación, desde el código máquina hasta el Groovy, han ido en esta dirección. Los desarrollos más recientes (o no tanto) que hacen que JAVA quede obsoleto en este sentido son:

  • Intentar eliminar al máximo el su ruido sintáctico. Declaraciones de tipos, palabras reservadas, tokens, etc. oscurecen la sintaxis de nuestro código, haciéndolo menos legible. Algunas técnicas usadas por los lenguajes para mejorar esta:
    1. Para los lenguajes de tipado estático, la inferencia de tipos, como demuestra Scala, nos evita tener que escribir un montón de código. El compilador es inteligente y en los casos en los que el tipo de una expresión sea computable en tiempo de compilación, nos permite omitir la declaración de tipo en las variables, parámetros y valores de retornos de las funciones. Esto es una de las razonas por las que el compilador de Scala es más lento que el de JAVA.
    2. Los lenguajes de tipado dinámico no necesitan estas declaraciones, con lo que son de forma natural más sucintos y expresivos. Esto, junto con el auge del TDD, hace que este tipo de lenguajes se haga cada vez más popular.
    3. Sintaxis flexible. Los lenguajes modernos permiten una sintaxis flexible. Podemos ahorrarnos llaves, comas, puntos, puntos y comas y paréntesis en algunas circunstancias. El resultado es un código con menos ruido sintáctico y más parecido al lenguaje natural, lo que ayuda al desarrollo de DSLs internos. Groovy, Ruby o Scala son ejemplos de lenguajes que usan esta técnica.
  • Paradigma mixto. La mezcla de paradigmas OO y Funcional nos permite usar el paradigma más expresivo según para que tarea. El uso de closures o bloques de código nos permite simplificar mucho nuestro código haciéndolo más legible.
  • Metaprogramación. La metaprogramación nos permite construir DSLs internos de forma más sencilla. En este sentido Groovy y Ruby son bastante potentes.

Si veis que vuestro lenguaje de programación os impide que vuestro código sea lo suficientemente expresivo como para cambiarlo con suficiente rapidez, empezad a pensar en adoptar otro lenguaje.

Como os dije soy un tío simple e impresionable. ¡ A pasarlo bien !

Read Full Post »


Hola a todos, como veo que hay mucha inquietud por todas esas prácticas de desarrollo ágil, como el TDD, el refactor y el pair programming, y como me huelo que por ahí que se ha puesto de moda eso del SOLID, he decidido hacer unos cuantos de posts sobre el diseño de software ágil. Espero que me salga algo coherente…

En posts anteriores he hablado mucho de gestión de proyectos, sobre todo de Scrum. Me centré en que lo bueno de las metodologías ágiles es que proponen que se debe entregar valor de forma continua y cuanto antes mejor. Ésto es mucho más sencillo de decir que de hacer. Scrum, por ejemplo, no nos orienta sobre cómo conseguir implementar en cada sprint rodajas de funcionalidad potencialmente entregables al usuario. De hecho esto es bastante complicado de hacer, ya que implica que con cada nueva funcionalidad que queramos implementar, debemos modificar o extender los componentes de software que hayamos realizado hasta el momento. Además es probable que debamos introducir nuevos componentes, no previstos hasta el momento, en la arquitectura del sistema y que estos encajen con los ya existentes. Todo un desafío. Las prácticas de ingeniería ágiles no son más que unas herramientas para conseguir estos objetivos. Muchas de dichas prácticas existen desde hace mucho tiempo (pre agilismo) dentro del paradigma OO, aunque hayan sidos ignoradas por nosotros, programadores de JABOL.

Antes de meterme en materia técnica, y abrir el arsenal de armas de destrucción masiva, voy a explicar en este post lo que quiero decir cuando en una consultoría digo: “pues depende…”. Es un tema que me preocupa porque empiezo a oír hablar de SOLID, código expresivo y demás, y todo ello justificado en nombre de una entidad abstracta llamada “calidad”. Es una corriente que me está dando algo de miedo, ya que en nombre de la “calidad”, me puedo tirar refactorizando mi código y chapándolo en oro por los siglos de los siglos sin entregar nunca a tiempo nada de valor a mi cliente. Me da la impresión de que algunos hablan de la “calidad” de la misma manera en la que un sacerdote podría hablar del “bien supremo” o un artista de la “belleza” de su obra. Las técnicas de diseño ágil son armas poderosas, y con ese poder viene la responsabilidad de no hacernos pajas mentales con ellas :-P

Mi postura es que la calidad del software, como entidad platónica y absoluta no existe. Es algo que no es medible, ¿alguien me puede proponer una fabulosa métrica que la mida y que sea factible de implementar? Si algo no es medible no debemos tomarlo como guía para tomar decisiones de diseño de nuestro código, por lo tanto, yo no uso la calidad para estos fines. Veamos pues como bajar al mundo real para tomar decisiones de diseño. En el mundo real del desarrollo de software profesional el objetivo es entregar software al cliente y que éste esté satisfecho con él. Por satisfecho quiero decir que se deben cumplir las dos siguientes condiciones:

  • El funcionamiento del software cumple las expectativas del cliente de forma razonable.
  • El software se entrega en un plazo de tiempo y con un coste razonable para el cliente. Si es más importante el coste o el tiempo lo debe decidir el cliente al principio del proyecto, de forma que podamos balancear estos dos factores adecuadamente.

O sea, que no descubro la pólvora, el cliente estará satisfecho si el software funciona, está a tiempo y su coste no se ha ido de madre. La parte quizás más problemática es la definición de “razonable”. Para mi el significado de razonable es algo que se debe acordar con el cliente mediante negociación, aunque eso sí, es una negociación asimétrica donde el cliente tiene la última palabra.

Por lo tanto el objetivo del diseño de software ágil es hacer software que funcione, respetando unas restricciones de tiempo y de dinero, y se entregue dicha funcionalidad de forma continua e incremental, aceptando posibles cambios. Las técnicas y principios de desarrollo ágil como TDD, Pair Programming, SOLID, KISS, DRY, etc. no son más que meras herramientas para conseguir ésto, no las confundamos con el objetivo. Antes de aplicar un patrón de diseño, o una refactorización, calculad el coste que os supondrá hacerlo en tiempo y dinero y comparadlo con el ahorro de coste y dinero que os supondrá a largo plazo. Si no hay un beneficio obvio, no lo hagáis. Así que cuando alguien me pregunta: “¿es bueno seguir los principios SOLID?”; y yo respondo: “pues depende..”; simplemente estoy diciendo que en función del contexto de tu proyecto tendrás que analizar si te va a dar beneficios o no.

En este sentido, la primera técnica que podemos aplicar es usar un timebox. Un timebox es un intervalo de tiempo predefinido, y no alterable, que restringe la implementación de una tarea. Ejemplos de timeboxes son: pomodoro, jornada de trabajo, sprint, deadline… La idea es que nos concentremos en cumplir con nuestras tareas y sintamos la presión de que se no acaba el tiempo. Esta presión nos ayudará a evitar que nos “entretengamos” en cosas que no sean conseguir terminar nuestra tarea. Usar timebox es una técnica común en el mundo ágil, ya que nos permite controlar nuestro ímpetu por demostrar como de bien sabemos programar, y centrarnos en simplemente implementar la funcionalidad, y si da tiempo se mejora su “calidad”. Usar timebox es una forma efectiva de conseguir KISS y YAGNI, si tienes claro que tienes un límite de tiempo vas a lo práctico, y haces lo más simple que puede funcionar y te olvidas de cosas que no vas a necesitar.

Enganchando con la primera parte de este post, el desafío técnico del agilismo, consiste por un lado en entregar incrementos de funcionalidad, y por otro aceptar cambios en la funcionalidad y el contexto del proyecto. Por lo tanto en un proyecto gestionado de forma ágil, vamos a estar continuamente cambiando nuestra arquitectura, nuestro diseño y los componentes ya existentes. Esto es así, aunque no cambien los requisitos funcionales, al tener que modificar el software ya existente para poder acomodar al menos la siguiente funcionalidad a entregar. Si hay cambios funcionales, el ritmo de cambio a absorber por nuestra arquitectura es mayor aun. Por lo tanto nuestro diseño y nuestros componentes deben ser maleables y flexibles. Cualquier técnica o principio de diseño, que recorte los gastos o el tiempo de implementar un cambio, rendirá beneficio en un proyecto ágil.

Por lo tanto ya tenemos una regla para guiarnos a la hora de tomar rápidamente decisiones de diseño. Podremos aplicar una técnica y/o refactorización, siempre y cuando:

  • Podamos aplicarla si no nos vamos a saltar el timebox de la tarea a implementar. Esto asegura que no tiene un coste alto, y no nos desvía de nuestro objetivo de hacer software que funcione.
  • Tenemos claro que consigue hacer que nuestro diseño sea más sencillo de cambiar en un futuro.

La regla anterior tiene una debilidad, la posibilidad de que el timebox sea irreal y demasiado corto para realizar la tarea. En este caso nunca tienes tiempo para refactorizar ya que estas un ritmo de trabajo insostenible, y tu diseño puede hacerse frágil ante el cambio. Por lo tanto tengo otra regla que tiene más precedencia que la anterior: si te cuesta cada vez más encajar una nueva funcionalidad en el diseño, refactoriza. Esta regla actúa como una alarma que me indica que debo “complicar” el diseño, sí o sí, ya que éste no es lo suficientemente flexible para aceptar cambios.

Resumiendo mi postura,la calidad del software no existe en si misma. Lo que realmente nos importa es terminar en tiempo y dinero con software que funcione. En un contexto ágil esto va a implicar que tu software debe ser maleable y aceptar cambios de forma rápida y barata. Olvídate de métricas, documentación y estándares. Céntrate en que tu software funcione y acepte cambios fácilmente. Me gusta llamar a esto calidad mundana del software, en contraste con la idea de que la calidad es un ideal a alcanzar como objetivo. Para obtener suficiente “calidad mundana”, pero nada más, recopilando lo dicho anteriormente, yo suelo usar las siguientes reglas:

  • Usar timebox para centrarme en implementar funcionalidad y no salirte de costes y tiempos. Puedo usar distintos timebox (pomodoros, jornadas y sprints) para distintos ámbitos (tareas, escenarios y Product Backlog Items). Esto promueve KISS.
  • Si sobra algo del timebox, cambia tu diseño para que admita cambios de forma más sencilla. En función del nivel del timebox puedes afrontar cambios más locales o más globales en diseño. Por ejemplo, en un pomodoro puedes intentar un refactor local, en un sprint, proponer a tu equipo alguna tarea de review de código o de rediseño de un subsistema.
  • Si detectas, de forma empírica (nada de corazonadas o instinto), que cada vez es más difícil aceptar cambios, entonces tu arquitectura es frágil ante el cambio. Da la alarma y prepárate para refactorizar.
  • Si no tienes problemas céntrate en producir funcionalidad. Olvídate de posibles futuros problemas que pueden no ocurrir nunca (YAGNI). Al fin y al cabo la calidad cuesta tiempo y dinero, si no necesitas más, ¿para que comprarla?

Bueno, este es mi enfoque personal. Como veis es muy cauto e intento no complicar el diseño hasta que no es realmente necesario. Llegué a esta forma de trabajar tras darme cuenta de que tenía tendencia a “chapar en oro” mi software, y hacer programación barroca. Me di cuenta que esto es un defecto y hay que balancear muy bien el esfuerzo que se dedica a mejorar el código contra el que dedicas hacer funcionalidad. Hay que llegar a un sano equilibrio.

Para finalizar, si tu proyecto no es ágil, olvídate de todo esto. En los proyectos no ágiles, el cambio es algo malo, que hay que penalizar, no aceptar. Por lo tanto no tiene sentido diseñar para aceptar el cambio. Como mucho tendrá sentido diseñar para cumplir el plan. Ahora empiezo a entender, porque algunos principios como YAGNI, KISS, SOLID o DRY no se han aplicado como debían en la industria, porque simplemente en un proyecto no ágil no tienen mucho sentido.

¡ Salud a todos y felices fiestas !

Read Full Post »


Hola a todos, lamento no haber podido escribir antes, pero tenía bastante trabajo acumulado y el deber me llamaba. Aprovecho que me iba a ir de fiesta esta noche y que al final no, para hacer algo útil con mi tiempo y escribir este post. Espero daros una crónica de lo que fue mi participación en la AOS2010, espero que un poco distinta y un pelín más crítica ;-)

Asistí junto con @ialcazar, @antoniodfr y @mcberros. Llegamos tarde, con la sesión de votación a mitad y un poco desubicados la verdad. Yo pensaba proponer una sesión llamada “los frameworks crean zombies” pero un tal Xavi Gost(@Xav1uzz) se me adelantó. Menos mal, porque con eso de llegar tarde, y ser mi primer open, estaba un poco cortado de verdad (a pesar del pequeño intento de @jmbeas de que saltara a la palestra). Mi compañero @antoniodfr no tenía muy claro si el formato open space iba a ser bueno o si por el contrario iba a degenerar en el caos. Al final se rindió al concepto de autoorganización y salió enamorado del concepto open.

Después vino una noche de parranda por los bares de Barcelona. Conté con la colaboración inestimable de Ramón Clariso y el inefable Aleix Cerecedo. Los bares al ver a una horda de agilistas por la noche de Barna iban cerrando a cal y canto, para que no entráramos en ellos ¡ y es que somos peligrosos ! Al final acabamos en un asador cerca del barrio de Graçia (¿está escrito bien?) donde tuvimos una animada charla con Roberto Canales(@rcanalesmora). Después el pobre Aleix tuvo la difícil tarea de buscar un bar en dicho barrio donde cupieran 40 personas. El pobre no dejaba de mirar atrás acojonado y diciéndome: “porque eres tu Enrique, sino salgo corriendo :-S “. Al final su plan “A” fue acertado y el sitio donde queríamos ir estaba vacío, pero en cuanto entramos quedo lo petamos. Desgraciadamente no servían mojitos, (mea culpa @kinisoftware) y lo peor es que no quedaba Moritz. Nos conformamos con gin-tonics y tercios de cerveza. A la llegada al hotel puse el despertador, este me informo amablemente que sonaría en sólo 3 horas, y cuando me quise dar cuenta ya estaba desayunando en el campus La Salle. Enrique, al lío.

Vayamos a las sesiones a las que asistí:

  • Coaching. Una sesión excelentemente moderada. Hubo un intercambio sano de ideas y experiencias, bastante refrescante. Allí me llamó la atención la intervención de Emma (¡dios, no se su twitter, que alguien me ayude, que quiero seguir a esta chica), según entendí es ScrumMaster quemada de muchas guerrillas en su empresa.
  • Software Craftsmanship. Propuesto por Xavi Gost y @ecomba. Bueno, nos contaron los principios del manifiesto y un poco como funcionaban, todo lo de los maestros y aprendices y demás. Me he leído el manifiesto y puedo decir que suscribo todo lo expuesto en él, ¿quién no podría estar de acuerdo? Es como los diez mandamiento, que aunque no seas judeocristiano, es probable que los pudieras firmar sin cargos de conciencia. Por ejemplo, el tema de educar mediante un sistema de maestro/aprendiz es obviamente la mejor manera de transmitir conocimientos. Por otro lado me sigue sin gustar el nombre o la analogía, ya que en el desarrollo de software no se construye nada. Los artesanos construyen pero en el software no se construye, se diseña. El que construye es el compilador. Por eso me sigue gustando más hablar de nosotros como ingenieros, y no como artesanos, porque nosotros diseñamos y el compilador construye. No veo por que el término ingeniero debe ser “evil”. En fin, es un tema de nomenclatura, porque a nivel de valores, que es lo que importa, me encuentro muy cercano a ésta forma de pensar. Un punto negativo en esta sesión fue que la moderación no fue muy efectiva, hablaron sobre todo Xavi y @ecomba, y la charla fue muy unidireccional (como me señaló alguien que no quiero nombrar).
  • La batalla de los frameworks. De nuevo con Xavi Gost, @ecomba y con la colaboración inestimable de @rcanalesmora en el papel del malo maloso de la película. Yo me lo pasé pipa y participé bastante, sobre todo para meterle caña a @rcanalesmora. Yo en esta estoy del lado del frente artesano y creo que los frameworks son armas de destrucción masiva a manejar con cuidado y mucha responsabilidad. El bando opuesto defendía que los frameworks te permitían contratar “personal poco cualificado” (a.k.a. “monos”) en masa para hacer los proyectos y ganar mucho dinerito calentito. El señor @rcanalesmora hizo una interpretación torticera de unas palabras que le dije la noche anterior, entre vinitos, y yo le respondí dándole cañita brava (pero con cariño).
  • Manipula. La señorita @rlaina nos dio una charla sobre el tema seguido de un debate. Tanto Emma como @jmbeas contaron sus problemas, que se parecen sospechosamente a los que tenemos todos. El problema es que cuando el debate se ponía interesante se nos acabó el tiempo y ¡ rompimos el timebox ! Cometimos el feo pecado de extender la charla a pesar de los heroicos esfuerzos de @amaliahern de salvar nuestras almas. La verdad es que el debate estuvo interesante, pero nos saltamos media comida.
  • Como tratar con locos paranoicos. Propuesta y dirigida por Xavi Albadalejo fue una de las charlas más interesantes. Usando la técnica de identificación de causas raíces analizamos porqué muchas personas recurren al Command & Control (a.k.a. “aquí se hace lo que yo digo”). Llegamos a la conclusión de que existían dos tipos de jefes autoritarios: los paranoicos y los desconfiados. Si te toca el primer tipo es mejor plantearte si dejar la empresa, ya que es una señal de que la cultura empresarial fomenta este tipo de personas como jefes. Si te toca el segundo tipo entonces hay esperanza. Lo curioso es que las técnicas que fuimos sacando para desactivar el ordeno y mando de un jefe desconfiado es simplemente… usar Scrum Efectivamente las técnicas propuestas por Scrum tienen como resultado aumentar la transparencia y esto acaba por aumentar la confianza. Muy enriquecedora esta sesión.
  • Diseño ágil. En esta sesión estuve bastante activo. Hablamos de como conseguir un buen diseño OO sin tener que hacer un diseño detallado al principio del desarrollo, práctica que va en contra de los principios ágiles. Yo conté mi experiencia, de que usando sólo TDD junto con un refactor continuo y agresivo, llegas a un diseño bueno. Sólo necesitas hacer al principio un pequeño diagrama de “cajitas” con la arquitectura de alto nivel. El detalle de cada “cajita” te la da el TDD+Refactor. También hice notar que esto por otro lado no te resolvía aspectos como el rendimiento y la seguridad, que tal vez sí hay que planearlos más de antemano. Algunos compañeros comentaron que ellos ponían pruebas de rendimiento en el build nocturno y que pedían a un experto en seguridad, que atacara el entorno de demo cuando el quisiera. Una aportación muy interesante. También se habló de las revisiones de código, y hubo unanimidad en que eran necesarias. Yo sostuve que la programación por parejas era equivalente a una “revisión de código continua”. Un asistente comentó que además ellos tenían un “moderador de proyecto”, que era externo al proyecto y neutral al equipo, que hacía revisiones de código. A mi me pareció una excelente idea. Ellos usaban un plugin de JIRA llamado Crucible, para hacer dichas revisiones. Lo tengo que probar. Todos estuvimos de acuerdo en que el UML era bueno para hacer bocetos de ideas y arquitectura de alto nivel. También consensuamos que había que hacer toda la documentación técnica necesaria para hacer el mantenimiento del sistema, pero por lo general, cuando el código está bien diseñado, éste es bastante legible, y necesitas menos documentación de la necesitarías normalmente.

Por supuesto hubo otras charlas, que supongo cada uno habrá comentado en su blog. Finalmente vino la retrospectiva, dinamizada, de forma genial, por el señor Ariel Ber (no se si tiene twitter). Se habló de las “AgileGirls”, de hacer un “Scrumbar” y de hacer el siguiente Open más grande. En concreto yo creo que una mejora para el AOS2011 debería ser que la gente participara más, me explico. En muchas sesiones la charla estuvo dominada por los “pesos pesados” y los “gurus” y vi a la gente un poco cohibida, lo que creo que impidió que se generara más debate. Yo mismo soy culpable de ese pecado y en muchas sesiones me hubiera gustado participar más, pero no me atreví. Tal vez en el siguiente open solucionemos eso.

Por supuesto hubo una segunda noche de fiesta, comimos en el “divinus”  y terminamos trasegando mucha cerveza y sangría en el “Ovella negra” (¿?) mientras @ydarias y compañía hacían lobby para que la siguiente AOS se celebre en Canarias (¡yo voto por vosotros!)

Quiero hacer mención especial de @amaliahern, que estuvo de fotógrafa e iba de sesión en sesión procurando que se cumpliera el horario. También creo que Ariel Ber estuvo excelente durante la retrospectiva, como comenté anteriormente.

Por último quiero poner mi opinión sobre un debate “silencioso” que se produjo de forma soterrada durante muchas sesiones: certificaciones sí, certificaciones no. De hecho hubo una sesión explícita sobre las certificaciones. Yo personalmente me he certificado CSM con Alan Cyment y Ariel Ber. Os recomiendo su curso, aprendí más sobre el espíritu de Scrum que en cualquier libro. Entiendo perfectamente a los que les da miedo que las certificaciones se perviertan y terminen siendo un modo fácil para que las grandes empresas se pongan el sellito de ágil y vendan por ahí lo que no son. También se corre el riesgo de que el agilismo quede preso de grandes consorcios certificadores. Sin embargo queda claro en todos sitios lo que significa ser CSM: que simplemente has dado un curso y que has comprendido lo que es Scrum, nada más. De comprender lo que es Scrum a practicarlo de verdad hay un mundo. De hecho la certificación que dice que usas Scrum con éxito no es la CSM, sino la CSP, en la que debes acreditar experiencia y pasar un “peer review”. De todas formas, sería de incautos juzgar la experiencia de alguien sólo por su certificación. Creo que los certificados son necesarios, pero no indispensables, y que tienen sus peligros, pero no por ello se debe generar un sentimiento de animosidad entre ambos bandos. Pensad, ¿se necesita ser universitario para ser un buen desarrollador de software? Por supuesto que no ¿Debemos abolir los títulos universitarios de ingeniería de informática por ello? Rotundamente no, aunque algunos políticos quieran. Pues lo mismo con las certificaciones.

En fin señor@s, yo vengo muy contento del AOS2010, y espero un AOS2011 aun mejor, tal vez en Canarias ;-)

P.S. Las fotos que tengo mejor no las cuelgo, curiosamente parecemos todos unos borrachos por la noche…

P.S.S. Señor @jmbeas péguele un tiro en la cabeza a su zombi, es para que no sufra el pobre :-D

Read Full Post »


… y lo sabéis en el fondo de vuestro corazón, ¿o no? Bueno, a lo mejor sí que existen, pero a mi me parece que no. En este post os voy a contar mi punto de vista sobre esto de los proyectos cerrados ¡ Bien, hoy no hay TDD, menos mal ! Creo que es importante tratar este tema de los proyectos cerrados, sobre todo en relación a las metodologías ágiles, ya que está muy extendida la idea de que dichas metodologías no pueden aplicarse en proyectos cerrados. Mi idea es que eso de los proyectos cerrados no existe, y que cuando se dice que un proyecto es cerrado en realidad estamos soltando una buena mentira.

¿Os acordais del teorema CAP? Bueno, pues lo que voy a contar a continuación se parece mucho. A alto nivel y desde el punto de vista de planificación y preventa de un proyecto hay tres variables: el alcance, el tiempo de entrega y el coste unitario. Veamos que son estas tres variables:

  • Alcance. El alcance es la cantidad de funcionalidad o de historias de usuario que va a tener el proyecto. También se puede interpretar como el tamaño del proyecto. En los proyectos de alcance pequeño sólo hay que implementar unas pocas funcionalidades, en los de alcance grande tenemos una lista de requisitos grande (o unos pocos requisitos enormes). Algunos factores no funcionales, como la complejidad de las tecnologías implicadas en el proyecto, o la complejidad a nivel de interlocución, también pueden afectar al alcance.
  • Tiempo de entrega. Cuanto tiempo tenemos para terminar el proyecto.
  • Coste unitario. El coste de mantener un equipo de trabajo y toda su infraestructura por unidad de tiempo. Cuanta más gente en el equipo, mayor coste unitario. Cuantas más máquinas y más caras, más coste unitario. Cuantas más licencias de pago tengamos, más coste unitario. No confundir con el coste total, que lo da la suma del coste unitario a lo largo de todo el proyecto.

Bien, todo sencillo. En este contexto podemos definir un proyecto cerrado como aquel que tiene un coste unitario, tiempo de entrega y alcance fijo. Sin embargo esto en la práctica no existe. Un proyecto cerrado sólo podría existir en un mundo estático, donde el entorno no cambia y las cosas son totalmente predecibles y repetibles, y el mundo real no es así. En la realidad las necesidades de negocio cambian durante el desarrollo de los proyectos, los clientes y usuarios se equivocan y cambian de opinión, la productividad de un equipo de desarrollo cambia con el tiempo. Sólo un novato puede pensar que un cliente no va a cambiar de opinión o que un acta de reunión se va a respetar (en Alemania tal vez, en España no). He visto demasiadas actas de reunión y contratos y análisis funcionales haciendo compañía al papel higiénico (hay que reciclar). Todo esto lo sabe la gran mayoría de los profesionales y por ello en la práctica tenemos mecanismos de defensa como:

  • Las notas de cambio de alcance y la gestión del cambio. Es el mecanismo de defensa típico del proveedor. ¿Que el cliente cambia el alcance? Pues pagas dinero. La opción de quitar alguna funcionalidad a cambio no es tan popular.
  • Las cláusulas de penalización si un proyecto se retrasa. Con esto el cliente se defiende tanto de un proveedor en el que no confia como de posibles retrasos debido a imponderables.
  • Precio total del proyecto cerrado. Como el anterior permite al cliente defenderse de proveedores malvados o ineptos o simplemente ante cualquier tipo de desgracia acaecida durante el proyecto.

Fijaros en la contradicción, todo el mundo habla de que los proyectos de verdad son cerrados, pero todo el mundo asume que habrá retrasos, malentendidos, incompetencia, imponderables y cambios de necesidades de negocio. Todo el mundo hace proyectos cerrados, pero a la vez usan tácticas de defensa. El problema real es que la gente tiene en cuenta la realidad, pero las metodologías tradicionales no. Las metodologías tradicionales asumen que una vez pasada la fase de análisis de requisitos (o preventa como se llama en España), se puede hacer un proyecto cerrado, ya que presuponen que el mundo es estático e inamovible. Como la realidad es caótica y cambiante, pero todos se empeña en usar metodologías pesadas, se producen estas contradicciones. Usar metodologías basadas en concepciones irreales es peligroso para tu negocio, no me extraña que la mayoría de los proyectos no sean exitosos. En vez de usar metodologías que no se ajustan a la realidad y que tienen una visión inocente respecto al nivel de caos de un proyecto real, es más eficiente usar metodologías diseñadas con la cruda realidad en mente, y que se basan en aceptar el cambio y gestionarlo. ¿Cúales pueden ser estas metodologías? Mmmm, ah, sí, eso raro del agile.

Visto que el caos de la realidad nos impide tener proyectos cerrados, ¿qué hacemos? Tal vez no podamos cerrar las tres variables, pero si algunas de ellas y jugar con las demás. Veamos que posibilidades tenemos:

  • Alcance abierto, coste unitario y tiempo de entrega cerrado. Este tipo de proyectos es el clásico ejemplo en las metodologías ágiles. Se tiene un tiempo de entrega cerrado, ya que en estos casos el time to market suele ser vital en el negocio. Dada una estimación del alcance, se propone un equipo cerrado (coste unitario cerrado). Se cierra el equipo ya que normalmente añadir o quitar miembros a un equipo suele ser malo para la productividad (costes de formación, ponerse al día con el proyecto, etc). Durante el proyecto se acepta cualquier cambio de alcance, pero al acabar el tiempo pactado, se entrega. Esto permite ofertar a coste total cerrado. El proyecto se considera exitoso si el alcance conseguido es similar al estimado. Más vale estimar bien, sino el cliente quedará descontento.
  • Alcance cerrado, coste unitario cerrado y tiempo de entrega abierto. Como ya he comentado con anterioridad este caso no se produce en el mundo real, excepto tal vez, en proyectos pequeños, debido a que el alcance siempre cambia, ya sea por cambios en necesidades de negocio o por malentendidos. Aun suponiendo que el alcance es cerrado, el que el tiempo de entrega sea abierto, implica que la eficiencia o productividad del equipo varía o se ha estimado mal la capacidad del equipo. Otra posibilidad es que el time to market no afecte al valor del proyecto, pero esto es poco realista. En este caso el coste total del proyecto es abierto, ya que no sabemos por cuanto tiempo vamos a tener al equipo trabajando.
  • Alcance cerrado, coste unitario abierto y tiempo de entrega cerrado. De nuevo como el anterior este caso no es muy real, tal vez se puede dar en aplicaciones pequeñas pero con un time to market muy estricto. Para conseguir llegar a la entrega se invertirán los recursos que hagan falta. De nuevo el coste total del proyecto es abierto.
  • Alcance y coste unitario abierto, pero tiempo de entrega cerrado. Se corresponde con un proyecto crítico, no sabemos exactamente lo que se va a implementar, pero tiene que estar listo en una fecha concreta. Para conseguirlo se pondrán todos los recursos que sean necesarios.
  • Alcance y tiempo de entrega abierto, pero coste unitario cerrado. Esto es un proyecto sin prioridad ni ningún tipo de urgencia. Puede ser un proyecto personal o entre amigos.

El tipo de proyecto que os podéis encontrar más frecuentemente en la realidad es el primero, el que deja abierto el alcance y cerrado lo demás. Qué casualidad que es el tipo de proyecto en el que se centran las metodologías ágiles. Este tipo de proyectos tiene unas cualidades muy interesantes: el coste total es cerrado y por otro lado se ajusta mejor a la realidad. Esto es bueno para el proveedor, ya que evita entrar en perdidas y puede asegurarse un beneficio económico. También es bueno para el cliente, pero sólo si el alcance conseguido es suficientemente bueno.

Esta circunstancia pone al cliente en una posición de debilidad con respecto al proveedor. Muchas consultoras sin escrúpulos se aprovecharon de esto en el pasado. Por eso los clientes ahora demandan “proyectos cerrados”. Lo que en realidad hacen es protegerse contractualmente. Es muy raro que un cliente admita “alcance abierto” debido a esa debilidad. Sin embargo si la relación cliente/proveedor es de una gran confianza y la experiencia pasada sea buena, si puede admitir este modelo de proyecto con alcance abierto, al fin y al cabo lo que le suele interesar al cliente es protegerse de proyectos que no se acaban nunca y que consumen dinero sin fin. Si el fracaso del proyecto no supone un gran perjuicio para el cliente, aumenta la posibilidad de que éste se preste a un contrato abierto. Otro caso es el de los proyectos internos, donde es absurdo penalizarse a uno mismo, y es mejor aceptar la realidad. En este caso el poder hacer “lo máximo posible” en un tiempo y coste acotado es bastante atractivo.

En el resto de los casos el cliente querrá un modelo de penalización o bien de precio cerrado. Esto no significa que no tengamos un proyecto con alcance abierto, sino que tenemos un contrato cerrado. Sólo debemos aceptar contratos cerrados si estamos muy seguros de poder cumplirlos o si estamos muy desesperados. Para tener esa seguridad nuestra organización debe ser lo suficientemente madura como para poder hacer estimaciones certeras. En este caso se trata de estimar el alcance, y en función de esta estimación saber que coste unitario hay que invertir para cumplir los plazos. Esto no es fácil, sobre todo si tenemos en cuenta que el coste unitario no afecta de forma muy predecible a la productividad del equipo. Podemos gastar mucho pero mal, por ejemplo comprando caras licencias de productos que no son adecuados o poniendo un pelotón de becarios en vez de unos pocos desarrolladores expertos.

En cualquier caso debemos estar preparados para lo peor. Sabemos que aunque el proyecto sea abierto el cliente normalmente se protegerá con un contrato cerrado, asi que ¿qué puede ocurrir si fracasa el proyecto? Es decir, ¿que pasa si el alcance logrado no da el valor suficiente al cliente? En estos casos el cliente tiene diferentes mecanismos para protegerse:

  • Si lo más importante para el negocio es alcanzar un nivel de funcionalidad aceptable, se da un tiempo extra al proyecto, a cambio de una penalización económica debido a los “daños” de no tener el sistema a tiempo.
  • Si lo que realmente importa es el time to market, se pone en producción pero se penaliza al proveedor en función de la cantidad de funcionalidad que falte, para compensar los inconvenientes de que el sistema no tenga suficiente funcionalidad.
  • Precio cerrado. El proyecto es precio cerrado, y el cliente no te pagará ni un céntimo más del pactado. Si el time to market es lo importante tendrás que aumentar el coste unitario para reforzar el equipo. Si lo importante es la funcionalidad, tu equipo estará más tiempo del planificado. Combinaciones de ambos escenarios son bastante típicas. Todo esto puede lleva a proyectos con poca rentabilidad o pérdidas si no consigues estimar bien.
  • Te demandan y/o devuelves el dinero más el perjuicio recibido por el cliente debido a no conseguir tener la aplicación.
  • Si aplicas una metodología que lo permita, se puede abortar el proyecto en cuanto se vea que se va a fallar. Las metodologías ágiles son especialmente adecuadas en este sentido. Si se falla al principio, el cliente aun está a tiempo de contratar a otro, con lo que tiene menos perjuicio. Desgraciadamente en España es raro ver proyectos abortados aunque claramente se encaminen al fracaso.

¿Cómo afrontamos los contratos cerrados? ¿Nos rendimos y hacemos lo “mismo de siempre”? Muy al contrario, lo ideal en estos casos es usar metodologías ágiles, aunque la idea parezca de poco sentido común. Desde el punto de vista de los contratos cerrados, las metodologías ágiles son muy eficaces, ya que nos permiten gestionar el riesgo mejor y por otro lado nos permiten tener estimaciones precisas. Uno de los pilares de dichas metodologías es el uso de ciclos de feedback frecuentes, lo que nos permite:

  • Enterarnos rápidamente de los cambios de alcance. Mediante entregas frecuentes y desarrollo incremental el cliente tiene acceso rápidamente a la funcionalidad con lo que nos puede advertir de los cambios de requisitos. Este es un mecanismo muy útil para mitigar uno de los riesgos más típicos: construir funcionalidad que el cliente no quiere.
  • Aceptar cambios e implementar la funcionalidad más importante primero. Cuando se llegue a la fecha de entrega, al menos se habrá cubierto la funcionalidad más importante, con lo que la posibilidad de fracasar disminuye.
  • Mediante las reuniones diarias nos enteramos de los posibles impedimentos al proyecto, lo que nos permite tomar acciones correctivas, mitigando riesgos tecnológicos y organizativos.
  • Análisis de viabilidad frecuente. Por ejemplo, en Scrum, podemos decidir al final de cada iteración o sprint, cancelar el proyecto, de acuerdo al feedback del usuario y de los equipos de desarrollo.
  • Las estimaciones mejoran más rápidamente, ya que al tener un feedback más rápido, tenemos más datos y por lo tanto podemos ajustar mejor las estimaciones. Con una metodología pesada el feedback es al final del proyecto, y las estimaciones sólo se pueden ajustar al final del proyecto. En Scrum ajustas las estimaciones al menos una vez por iteración (sprint). En kanban, una vez cada vez que se termina una historia de usuario.

También hay que tener en cuenta que cada sprint o iteración se puede considerar como un proyecto en si mismo, pero de alcance muy pequeño y duración corta. Si el alcance es pequeño y la duración corta,  pensar que el alcance está cerrado, sí es una buena aproximación a la realidad. Al contrario que en un proyecto real, durante un sprint, hay muy poco tiempo, y por lo tanto poca probabilidad de que el alcance del sprint cambie. Podemos considerar pues, que cada sprint es un verdadero proyecto cerrado en el sentido estricto del término. De esta forma gestionar un sprint sí es sencillo, comparado con gestionar un proyecto. De todas formas, en proyectos muy cambiantes (o altamente caóticos), el alcance cambia incluso durante el sprint. En estos caso Scrum puede que no sea la mejor opción, y habría que utilizar enfoques más ágiles todavía, como kanban o el agilismo minimalista.

Como vemos, los proyectos cerrados no existen, pero los contratos cerrados sí, lo que hace los proyectos sean arriesgados para el proveedor. Sin embargo el mito de que una metodología ágil no es útil con contratos cerrados se cae bajo su propio peso. Al contrario de lo que muchos piensan, las metodologías ágiles permiten una mejor estimación y gestión del riesgo de un proyecto con contrato cerrado, además de incorporar de forma natural la gestión del cambio. Tal vez, cuando tu cliente vea que no lo engañas y que cumples tus contratos, te deje hacer un “contrato abierto”.

Read Full Post »

Older Posts »

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 42 seguidores