Archive

Archive for the ‘Ing. Software’ Category

Is your code thread safe?

The last weeks I started to considerate about concurrency testing. I wrote an utility that tries to test if your code is thread safe or not.

When you repeat an experiment many times, the result tends to be the expected

This means that if you throw a die one time, we can not know what result is going to happen, but if we do it thousands of times, we will know that each result is going to happens 1/6 of the times.

In less words, lets repeat this many times at the same time and then check if it is fine or not. The problem here is, how to do it at the same time?.

The utility has 2 classes, ConcurrenceHelper and Task. Task is an interface that you need to implement to provide ConcurrenceHelper the job to do. ConcurrenceHelper will trigger all the tasks “at the same time”.

This is the interface:

public interface Task {

	void execute();
	
}

And this class will trigger all the tasks in different threads. Onces all the threads have executed the tasks, the main thread will leave the method (it means that this method is synchronous).

import java.util.concurrent.CountDownLatch;

public class ConcurrenceHelper {

	public void triggerTasks(final int nThreadsPerTask, final Task ... tasks) throws InterruptedException{
		final CountDownLatch waitForThreadsEnd = new CountDownLatch(nThreadsPerTask*tasks.length);
		final CountDownLatch waitToStart = new CountDownLatch(nThreadsPerTask*tasks.length);
		for(final Task task : tasks){
			for(int i=0;i<nThreadsPerTask;i++){
				final String THREAD_NAME = task+"-"+i;
				new Thread(new Runnable() {
					@Override
					public void run() {
						try {
							waitToStart.countDown();
							// Threads are waiting until countDown=nThreadsPerTask*tasks.length
							waitToStart.await();
							task.execute();
						} catch (InterruptedException e) {
							throw new RuntimeException(THREAD_NAME+" can not wait any more o_O", e);
						}finally{
                        	waitForThreadsEnd.countDown();
                        }
						
					}
				}, THREAD_NAME).start();
			}
		}
		// The main thread is waiting until countDown=nThreadsPerTask*tasks.length
		waitForThreadsEnd.await();
		// At this point all threads have executed the task
	}
	
}

And now lets do some testing:


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

import java.util.List;
import java.util.Vector;

import org.junit.Test;

public class SamplesTest {

	private final ConcurrenceHelper concurrence = new ConcurrenceHelper();
	private final int CONCURRENCE_TASKS = 10000;
	
	@Test
	public void vectorIsThreadSafe() throws InterruptedException{
		final List<Long> list = new Vector<>();
		concurrence.triggerTasks(CONCURRENCE_TASKS, new Task() {
			@Override
			public void execute() {
				list.add(System.currentTimeMillis());
			}
		});
		System.out.println("Value "+list.size());
		assertEquals(CONCURRENCE_TASKS, list.size());
	}
	
	@Test
	public void notThreadSafeClass() throws InterruptedException{
		final NotThreadSafeClass obj = new NotThreadSafeClass();
		concurrence.triggerTasks(CONCURRENCE_TASKS, new Task() {	
			@Override
			public void execute() {
				obj.i++;
			}
		});
		System.out.println("Value "+obj.i);
		assertNotEquals(CONCURRENCE_TASKS, obj.i);
	}
	
	private class NotThreadSafeClass{
		protected int i;
	}
	
}

We are testing one class that we know that is thread safe (Vector) and other one that is not thread safe (NotThreadSafeClass). It is easier to test that one class is thread safe than checking that is not thread safe. The test vectorIsThreadSafe() will be successfull the 100% of the times, but notThreadSafeClass() will not be successfull the 100%. For that reason, to prevent you to have randomly testing errors, make the testing oriented to check that is thread safe (by the way, is what we really want to test).

To prevent concurrency problems, try to make the classes as much inmutables as possible.

Programación orientada a aspectos con Guice

Me gusta la programación orientada a aspectos por la cantidad de ifs que te quita. Ademas son ifs que tienes que ir repitiendo a lo largo del código.
Se me ha ocurrido hacer un pequeño ejemplo con JUnit. Vamos a hacer las tipicas comprobaciones de:

Las vamos a hacer de las dos formas, con aspectos y sin aspectos, demostrando que el resultado es el mismo, pero con aspectos queda mas limpio.

Antes de empezar con el Test, hay que configurar Shiro. Explicandolo por encima, tenemos un fichero que se llama shiro.ini, donde he definido los usuarios con su rol.

[users]
root = secret, admin
guest = guest, guest

[roles]
admin = *

También hay que configurar el modulo de Shiro para Guice, donde se le dice que se configure en base al contenido de shiro.ini:

package com.tododev.shiro;

import org.apache.shiro.config.Ini;
import org.apache.shiro.guice.ShiroModule;
import org.apache.shiro.realm.text.IniRealm;

import com.google.inject.Provides;

public class MyShiroModule extends ShiroModule{

	protected void configureShiro() {
        try {
            bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
        } catch (NoSuchMethodException e) {
            addError(e);
        }
    }

    @Provides
    Ini loadShiroIni() {
        return Ini.fromResourcePath("classpath:shiro.ini");
    }

}

Ahora vamos ya con el test. Este test contiene 4 metodos de test, que comprueban lo siguiente sobre el método de la interfaz Interface.doSomething:

  • Si no estas autenticado lanza una UnauthenticatedException.
  • Si no tienes el rol de “admin” lanza una UnauthorizedException.
  • Si le pasas el parámetro a nulo lanza una ConstraintViolationException.
  • Si estas autenticado, con el rol de “admin” y no pasas el parámetro a nulo, no lanza excepciones.

La gracia es que el test lo vamos a hacer con dos implementaciones diferentes de Interface.class:

  • Aop.class
  • NoAop.class

Es decir, se van a hacer 2 x 4 = 8 tests. El resultado tiene que se el mismo.


package com.tododev.shiro;

import static org.junit.Assert.fail;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.constraints.NotNull;

import org.apache.bval.guice.Validate;
import org.apache.bval.guice.ValidationModule;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.guice.aop.ShiroAopModule;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

/**
 * Vamos a hacer que el test se ejecute con la clase Parameterized. Esta clase
 * nos ejecutara 2 veces todos los tests, insertando en el constructor los
 * valores del metodo data().
 * 
 * @author jorge
 * 
 */
@RunWith(Parameterized.class)
public class GuiceAopTest {

	private final static String ADMIN_ROLE = "admin";
	private final UsernamePasswordToken ADMIN = new UsernamePasswordToken(
			"root", "secret");
	private final UsernamePasswordToken GUEST = new UsernamePasswordToken(
			"guest", "guest");
	private final Injector injector;

	/**
	 * Cuando se crea la instancia de GuiceAopTest.class, se le inserta uno de
	 * los valores del metodo data().
	 * 
	 * @param injector
	 */
	public GuiceAopTest(Injector injector) {
		this.injector = injector;
	}

	/**
	 * Devuelve una coleccion que contiene un inyector con AOP y otro que no. La
	 * idea es demostrar que los tests pasan satisfactoriamente de las dos
	 * formas posibles.
	 * 
	 * @return
	 */
	@Parameters
	public static Collection<Object[]> data() {
		Injector withAop = Guice.createInjector(new ShiroAopModule(),
				new MyShiroModule(), new ValidationModule(),
				new AbstractModule() {
					@Override
					protected void configure() {
						bind(Interface.class).to(Aop.class);
					}
				});
		Injector noAop = Guice.createInjector(new MyShiroModule(),
				new AbstractModule() {
					@Override
					protected void configure() {
						bind(Interface.class).to(NoAop.class);
					}
				});

		Object[][] data = { { noAop }, { withAop } };
		return Arrays.asList(data);
	}

	public static interface Interface {
		void doSomething(String value);
	}

	/**
	 * Implementacion preparada para tener aspectos.
	 * 
	 * @author jorge
	 * 
	 */
	public static class Aop implements Interface {

		@Validate
		@RequiresRoles(ADMIN_ROLE)
		@Override
		public void doSomething(@NotNull String value) {
			System.out.println(getClass() + ": " + value);
		}

	}

	/**
	 * Hace lo mismo que hace Aop, pero esta no usara aspectos.
	 * 
	 * @author jorge
	 * 
	 */
	public static class NoAop implements Interface {

		@Override
		public void doSomething(String value) {
			if (value != null) {
				Subject currentUser = SecurityUtils.getSubject();
				if (currentUser.isAuthenticated()) {
					if (currentUser.isPermitted(ADMIN_ROLE)) {
						System.out.println(getClass() + ": " + value);
					} else {
						throw new UnauthorizedException();
					}
				} else {
					throw new UnauthenticatedException();
				}
			} else {
				throw new ConstraintViolationException(
						new HashSet<ConstraintViolation<?>>());
			}
		}

	}

	/**
	 * Preparamos Shiro para ser usado.
	 */
	@Before
	public void before() {
		SecurityManager securityManager = injector
				.getInstance(SecurityManager.class);
		SecurityUtils.setSecurityManager(securityManager);
	}

	/**
	 * Deslogueamos al usuario si estuviera logueado.
	 */
	@After
	public void after() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.logout();
	}

	/**
	 * Vefirica que si no estas logueado salta una UnauthenticatedException
	 */
	@Test(expected = UnauthenticatedException.class)
	public void unauthenticated() {
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething("any value");
		fail("Unreachable code");
	}

	/**
	 * Verifica que si no tienes los permisos adecuados salta una
	 * UnauthorizedException
	 */
	@Test(expected = UnauthorizedException.class)
	public void unauthorized() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.login(GUEST);
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething("any value");
		fail("Unreachable code");
	}

	/**
	 * Verifica que si llamas al metodo con el String nulo, salta una
	 * ConstraintViolationException
	 */
	@Test(expected = ConstraintViolationException.class)
	public void constraintViolation() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.login(ADMIN);
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething(null);
		fail("Unreachable code");
	}

	/**
	 * Verifica el caso de que todo es correcto y no hay excepciones
	 */
	@Test
	public void allowed() {
		Subject currentUser = SecurityUtils.getSubject();
		currentUser.login(ADMIN);
		Interface classToTest = injector.getInstance(Interface.class);
		classToTest.doSomething("any value");
	}

}


El resultado de los test es este:


Los 8 tests han pasado bien.

Este es el resultado que se muestra en la consola:

class com.tododev.shiro.GuiceAopTest$NoAop: any value
class com.tododev.shiro.GuiceAopTest$Aop$$EnhancerByGuice$$4ee6b586: any value

Os podeis fijar que la clase Aop “ha sido mejorada” por Guice, añadiendole los aspectos de Shiro y BVal. Lo curioso es que en las clases compiladas vemos lo siguiente:


La clase que aparece señalada debería llamarse GuiceAopTest$Aop$$EnhancerByGuice$$4ee6b586, pero se llama GuiceAopTest$Aop. Esto es porque Guice no hace los aspectos en tiempo de compilación, sino en ejecución. Esto tiene algunas limitaciones, pero en muchos casos resulta ser suficiente.

En conclusión:

Se puede comprobar lo limpio que queda el código usando aspectos. Así es mas fácil ver qué es lo que hace el método doSomething(). Además, esas anotaciones las podemos aprovechar y llevar a otros métodos que requieran esas comprobaciones.

En busca de la excelencia del código

Tal y como comenta su autor, Raúl Herranz, este libro gratuito (licencia Creative Commons),  “da un repaso a la automatización de tareas, a las revisiones de código, a las pruebas y a la refactorización; como actividades que facilitan el desarrollo de código que, además de cumplir con la funcionalidad esperada, cumpla con unos estándares de calidad que permitan calificarlo como excelente“.

Es decir, es más un libro enfocado al proceso de desarrollo del software que a desarrollar código en sí, y desde el punto de vista de las metodologías ágiles. Recomendado.

http://www.scrummanager.net/files/scrum_manager_excelencia_del_codigo.pdf

La deuda técnica como medida de calidad del software ¿ es negociable la calidad ?

No hace mucho tiempo se iniciaba una discusión en un grupo de LinkedIn sobre si la calidad en un proyecto software podía ser negociable o no. Aunque se vertieron gran variedad de opiniones, la principal conclusión que saqué era que había muchas interpretaciones del concepto de calidad aplicado al software. Así que, sobre si encima dicha calidad es negociable o no, ya os podéis imaginar que no saqué nada en claro.

Y eso me ha animado a escribir este artículo para expresar mi punto de vista sobre la calidad, cómo medirla, y si es negociable o no.

¿ Qué es la calidad del software ? ¿ Cómo se puede medir ?
Existen sistemas de control de calidad que se han aplicado exitosamente en otras industrias y se han intentado aplicar al mundo de desarrollo software. Buena parte de ellas se basan en las líneas de código (LoC) como una medida del trabajo realizado por los desarrolladores, y usan métricas como cantidad de defectos introducidos por líneas de código.

Basándose en estas métricas, el sistema define qué acciones se deben tomar en cada instante y cómo llevarlas a cabo. Por ejemplo, se define si se debe realizar una revisión de código (Peer Review) básandose en la cantidad de líneas de código afectadas por dicho cambio. Y de acuerdo a dicha cantidad de LoC, se define la duración y la cantidad de líneas de código que deben revisarse en cada sesión, así como la cantidad de defectos a encontrar. No, no estoy de coña. Desgraciadamente.

He podido presenciar como, ante estas imposiciones, hay desarrolladores que recurren a prácticas como eliminar comentarios que habían añadido para facilitar la comprensión del código con el único fin de que la cantidad de líneas a añadir no llegase al mínimo necesario para convocar una revisión, que retrasararía inevitablemente la publicación del fix dos o tres días mas. O cosas como añadir bugs estúpidos y fácilmente localizables para que se identifiquen en la revisión y garantizar así que se cumplan los objetivos de defectos encontrados por líneas de código.

Otros sistemas mas modernos, ante la dificultad para evaluar objetivamente la calidad de un desarrollo software, optan por definir y monitorizar los procesos de desarrollo del software, en lugar del software en sí. Ya es un avance, puesto que reconocen que un grupo de desarrollo de software no es un cadena de producción de tornillos.

Sin embargo, no hay que perder de vista el objetivo básico del software: el software se desarrolla ante la necesidad de un cliente, ya sea un cliente real que realiza un encargo, otro departamento de la empresa o, en el caso de Scrum, el Product Owner. Por lo tanto, parece lógico que sea el cliente el que defina la calidad que debe tener el producto, ya que su percepción de calidad puede ser muy distinta de la que tenga el equipo de desarrollo.

Y es que, en efecto, el cliente debería definir cómo resolver el compromiso entre funcionalidad que desea recibir, la calidad que desea percibir, y los plazos en los que los desea tener. Dicho compromiso debería reflejarse en los requisitos o historias de usuario. Por lo tanto, no es que este aspecto de la calidad sea negociable, sino que debe ser definida por el cliente. En este caso, la única métrica de calidad es el grado de cumplimiento de los requisitos. Y no es fácilmente generalizable porque depende del cliente.

Por otra parte, es normal pensar que el equipo de desarrollo tiene un conocimiento del funcionamiento interno del sistema que no tiene por qué tener el cliente. En este caso, se podría definir la calidad interna como la habilidad del equipo de desarrollo para no introducir deuda técnica en el sistema. Es decir, el sistema goza de buena calidad si no se introduce deuda técnica. Por el contrario, la introducción de deuda técnica implica el empeoramiento de la calidad interna. Nadie mejor que el propio equipo para determinar si se está introduciendo deuda técnica o no.

La introducción de deuda técnica puede tener consecuencias tan graves como:

  • aumento progresivo del tiempo necesario para proporcionar fixes ante defectos detectados o adición de nueva
    funcionalidad.
  • aumento del riesgo de introducir nuevos defectos al proporcionar fixes para defectos detectados o adición de nueva
    funcionalidad.
  • aumento de la difcultad para detectar defectos.

Pero, sin embargo, sigue siendo el cliente quien manda. El equipo de desarrollo debe informar al cliente sobre los riesgos que implica no mantener la calidad interna, pero debe ser el cliente quien tenga la última palabra. Claro está, siempre y cuando esté dispuesto a asumir los riesgos: no son raros los clientes que desoyen los consejos del equipo y, ante una merma de la calidad percibida o el riesgo de no cumplir plazos por el progresivo incremento del tiempo necesario para realizar cambios en el sistema, piden milagros de última hora, ya sea en forma de noches en vela del equipo de desarrollo, contratación temporal masiva de “recursos” para sumar al equipo, o cualquier otra forma.

Conclusión
Desde mi punto de vista, la calidad debería ser completamente negociable. La calidad interna queda en manos del propio equipo de desarrollo, ya que son los encargados de medirla y mantenerla en niveles aceptables, aunque dejando siempre la última palabra al cliente, con la obligación de informarle de las posibles consecuenciasde no mantenerla en dichos niveles.

Por otra parte, la incorporación de la calidad percibida a las historias de usuario permite que el cliente defina su propia visión de lo que debe ser la calidad del sistema e incluso, por qué no, sacrificar parte de esa calidad para mejorar en otros aspectos. Ejemplos comunes podrían ser la disminución del Time To Market, aprovechar las ventajas de poder mostrar ciertascaracterísticas en alguna convención o feria de muestras, …

Al estar incorporadas a los requisitos o historias de usuario, este tipo de calidad no debe medirse (lo que además, sería muy complicado, al depender de cada cliente), sino cumplirse.

Y tú… mides la calidad ? Permites que se negocie con ella ?

La multitarea es mala, mala (Demostración práctica)

01/03/2012 2 comments

Que la multitarea es fatal para el rendimiento en el desarrollo de software no es nada nuevo. Todos los desarrolladores se han enfrentado a situaciones en las que tienen que abandonar lo que se está haciendo para atender otro “proyecto urgente”, por no decir apagar un fuego. A veces son tareas pequeñas que distraen de lo que se estaba haciendo durante unas pocas horas, mientras que en otras ocasiones llevan a cancelar el Sprint en curso (o la iteración en general, para no entrar en debates de metodologías) y preparar uno nuevo.

En mi experiencia, siempre resulta difícil transmitir esta idea a los “jefes”. En realidad, no sé si yo no consigo transmitir o ellos no quieren entender, probablemente los dos tengamos parte de culpa -es posible que mi jefe lea esto- ;-), pero el caso es que la comunicación parece no funcionar.

En estos casos suele ser útil contar con el respaldo de una autoridad respetable en la materia y, en este caso, viene al rescate  Henrik Kniberg, el autor de libros como Scrum and XP From the Trenches o Kanban and Scrum – making the most of both, que pretende demostrar empíricamente los efectos adversos de la multitarea con la publicación de MultiTasking Name Game. En dicho experimento se muestran los resultados que se obtienen tras realizar con distintos grupos una tarea muy sencilla, y como afecta en ese caso la introducción de la multitarea.

No os reviento el final, pero os va a gustar, seguro !!

Nota: Los chicos de ScrumManager se han currado una traducción al español a la que han llamado  El Juego del Nombre en Multitarea y, al menos uno de los libros que cito, está disponible también en español como Scrum y XP desde las trincheras.

 

Cursos online gratuitos Scrum

ScrumManager abre convocatoria de sus cursos online gratuitos, añadiendo en este caso uno nuevo: Kanban.

Estos cursos no tienen nada que ver, en cuanto a duración y esfuerzo requerido, con los de Stanford, suelen ser 8 o 16 horas de clase (online en este caso).

Es importante inscribirse, y lo antes posible, porque son plazas limitadas y se cubren muy pronto.

http://www.scrummanager.net/ok/index.php

 

Nuevos cursos gratuitos de Stanford

11/17/2011 3 comments

Parece que, ante el éxito de los cursos de Machine Learning y Artificial Intelligence (también había uno de BD pero no sé como habrá ido, supongo que bien), los de Stanford se animan a sacar nuevos cursos.

Mi opinión personal es que el de Artificial Intelligence es mejorable, tanto el curso así como los ejercicios. Sin embargo, el de Machine Learning está bastante bien. La parte fuerte de los ejercicios consiste en resolver problemas reales (usando Octave, parecido a Matlab pero OS). En cualquier caso, no me arrepiento de haberme “enrolado” en ninguno de ellos. De lo que sí me arrepiento a veces es de haberlo hecho en los dos a la vez.

%d bloggers like this: