AUTONOMATIZACIÓN CON POKA-YOKES ORIENTADOS A ASPECTOS
Rafael Luque
Madrid GUG, Feb 2014
HISTORIA DE UN BUG real
class BuggyTagLib {def applicationData = { attrs ->...out << "<div class='u'>${campaign.betaTesters.size()}</div>"...}}
LAS COLECCIONES GORM SON ESPADAS DE DOBLE FILO
- Mejoran el rendimiento cargándose perezosamente sin acceder a la base de datos.
- Pero una vez son accedidas se cargan completamente.
- N+1 queries.
SOLUCIón 1
class Campaign {
static hasMany = [ betaTesters: CampaignUser ]
int betaTestersCount
static mapping = {
betaTestersCount formula: '(select count(*) from campaign_betatesters b where b.campaign_id = id)'
}
}
SOLUCIón 2
class Campaign {
static hasMany = [ betaTesters: CampaignUser ]
int getBetaTestersCount() {
betaTesters == null ? 0 : withSession {
it.createFilter(betaTesters, 'select count(*)').uniqueResult()
}
}
}
SOLUCIÓN 3
class BootStrap {
def init = { injectMappedCollectionCountMethod() }
private void injectMappedCollectionCountMethod() {
def classesWithCollections = grailsApplication.domainClasses.findAll { it.hasProperty('hasMany') }.collect { it.clazz }
classesWithCollections.each {
it.hasMany.each { colName, colClass ->
String countMethodName = 'get' + colClass.subString(0,1).toUppercase() + colClass.subString(1) + 'Count'
it.metaClass."$countMethodName" << { ->
withSession { session ->
delegate."$colName" == null ? 0 :
session.createFilter(delegate."$colName", 'select count(*)').uniqueResult()
}
}
}
}
SOMOS LO QUE LEEMOS

¿SOLUCIONAR EL PROBLEMA ES SUFICIENTE PARA SOLUCIONAR EL PROBLEMA?
- ¿Qué ocurre con los nuevos miembros del equipo?
- ¿Y con el resto de equipos de la organización?
- ¿Y con otros miembros de la comunidad Grails?
CÓMO GESTIONAMOS LOS ERRORES EN EL DESARROLLO DE SOFTWARE
- Buscando los culpables ?
- Emails al equipo de desarrollo ?
- Wikis/Blogs internos ?
- Reuniones con el equipo ?
- Catálogos de gotchas/pitfalls ?
EL ROL ACTUAL DE LOS ERRORES DE SOFTWARE
- Es algo embarazoso de lo que no se habla mucho.
- Por lo tanto no aprendemos de ello.
- A lo sumo a nivel individual, pero rara vez como organización o como profesión.
- En el mejor de los casos se añaden algunos tests y se documenta el problema: wikis, blogs, etc.
APRENDAMOS DE LAS ingenierías más maduras
- Culturas de aprendizaje del fracaso (H. Petroski):
- Catástrofes aéreas.
- Fallos de ingeniería civil.
- Se puede aprender más de los fracasos que de los éxitos, pero hay que reflexionar sobre la causa raíz y sobre el proceso de desarrollo.
FALLO VS DEFECTO
- El fallo es humano e inevitable.
- Los defectos ocurren cuando esos fallos llegan al cliente (producción).
- Es posible mejorar el diseño de nuestros productos hasta conseguir procesos cero defectos.
- Es el deber de todo ingeniero.
- Dejemos de justificar nuestros defectos porque somos "arquitectos", "artesanos", "jardineros", etc.
-
¡¡Convirtámonos en ingenieros de software!!
DEL DRY AL DRYF
DON'T REPEAT YOUR FAILURES
responde a LAS PREGUNTAS CORRECTAS
- ¿Cómo podría haber detectado el fallo más rápido?
- ¿Cómo puede el sistema ser más resistente a este tipo de fallos? ("Resiliencia")
¿Puedo mejorar el proceso de desarrollo para que este error no vuelva a producirse?
UN POKA-YOKE NO ES UN POKEMON
POKA-YOKE
- Término japonés que significa a prueba de fallos.
- Introducido por Shigeo Shingo en los 60s como parte del TPS.
POKA-YOKE
Puede tratarse de cualquier restricción (por simple que sea) incorporada al diseño de un proceso para evitar errores o detectarlos lo antes posible.
POKA-YOKE
POKA-YOKE
POKA-YOKE

POKA-YOKE

JIDOKA
- Lean management = Muda + Jidoka.
- Muda es la eliminiación de desperdicio en el proceso de producción..
- Jidoka es la introducción de la calidad en el proceso de producción.
- Los principios ágiles han cubierto Muda:
- Refactoring
- YAGNI
- DRY
- Simplest that works.
JIDOKA EN EL DESARROLLO DE SOFTWARE
- Jidoka no es sólo detectar el fallo y corregirlo.
- Consiste en corregir el fallo, investigar la causa raíz y eliminarla para siempre del proceso.
- Las herramientas habituales no son suficientes:
- Pruebas automáticas.
- Integración continua.
AUTONOMATIZACIÓN
-
Automatización con un toque humano.
- Característica incorporada en el diseño de un máquina/proceso para aplicar el principio Jidoka.

PROPUESTA
- Herramienta para facilitar la aplicación de Jidoka en el desarrollo de las aplicaciones Grails mediante la creación de un catálogo de Poka-Yokes por parte de la comunidad.
- Cada error se convierte en una oportunidad para mejorar, tanto la aplicación actual, como las futuras.
- Tanto nuestros equipos, como toda la comunidad Grails.
ANÁLISIS DINÁMICO VS ANÁLISIS ESTÁTICO
- Herramientas similares:
- Lint
- JSLint
- PMD
- FindBugs
- Codenarc
-
Útiles para comprobaciones estilísticas y sintácticas.
- Herramientas de análisis estático limitadas al no disponer de información sobre el contexto de ejecución.
POKA-YOKES como ASPECTOS
- Cross-cutting concerns:
- SRP
- OCP
- AOP permite implementar análisis dinámico.
- Distintos modelos de weaving:
- Source-code weaving.
- Byte-code weaving.
- Load-time weaving.
AOP VS METAPROGRAMMING
"AOP was invented to be a move away from explicit metaprogramming to a direct semantics for some of the kinds of problems metaprogramming could be used for. It helps to raise abstraction level."AOP VS METAPROGRAMMING
Y Gregor Kiczales no es sospechoso ;-)
EJEMPLO 1
GormCollectionSize.aj
import org.codehaus.groovy.runtime.callsite.Callsite;import org.hibernate.collection.PersistentCollection;public aspect GormCollectionSize extends AbstractPokaYoke {public pointcut violation():sizeOnPersistentCollection(Object, Callsite);public sizeOnPersistentCollection(Object receiver, Callsite callsite):GroovyPointCuts.groovyCall(receiver, callsite) &&if ((receiver instanceof PersistentCollection) &&("size".equals(callsite.getName())));}
xpi = ASPECT PROGRAMMING INTERFACE
GroovyPointCuts.aj
public aspect GroovyPointCuts {public pointcut groovyCall(Object receiver, CallSite callsite):call(public Object CallSite.call*(..)) &&target(callsite) &&args(receiver);}
ABSTRACTPOKAYOKE
Advice implementation & Method Factory pattern:
public abstract aspect AbstractPokaYoke implements PokaYoke {public abstract pointcut violation();public abstract String getDescription();before(): violation() {warn(thisJoinPoint);}...}
EJEMPLO 2
Unintentionally bypassing the bean proxy
Be careful when calling an annotated method within a service when the annotation settings are different.
Because you're underneath the proxy it's a direct method call, and any checks that the proxy would have done will be bypassed.
@Trasactionalvoid someMethod() {anotherMethod()}@Transactional(propagation=Propagation.REQUIRES_NEW)void anotherMethod() {...}
EJEMPLO 2
ServiceTransactionalServiceBypassed.aj
public aspect ServiceTransactionalServiceBypassed extends AbstractPokaYoke {public pointcut violation():proxyBypassed(Object, Transactional, Object, Transactional);public pointcut proxyByPassed(Object caller, Transactional callerAnnotation,Object callee, Transactional calleeAnnotation):cflowbelow(annotatedPublicServiceMethod(caller, callerAnnotation)) &&annotatedPublicServiceMethod(callee, calleeAnnotation) &&if ((caller.getClass() == callee.getClass()) &&(!callerAnnotation.equals(calleeAnnotation)) &&(!isCalledThroughProxy()));...}
EJEMPLO 2
ServiceTransactionalProxyBypassed.aj (cont.)
public boolean isCalledThroughProxy() {StackTraceElement[] elements = Thread.currentThread().getStackTrace();StackTraceElement callerElement = elements[3];return ClassUtils.isCglibProxyClassName(callerElement.getClassName());}
public pointcut annotatedPublicServiceMethod(Object receiver, Transactional ann):GrailsPointCuts.publicServiceMethod() && this(receiver) && @annotation(ann);
GRAILS XPI
GrailsPointCuts.aj
public aspect GrailsPointCuts {public pointcut inService(): within(..*Service);public pointcut publicServiceMethod():inService() && execution(public * *.*(..));...}
EJEMPLO 3
Policy enforcement
GormLayeringViolation.aj
public aspect GormLayeringViolation extends AbstractPokaYoke {public pointcut violation():GrailsPointCuts.isDomain() &&GormPointCuts.gormMethod() &&!cflowbelow(GrailsPointCuts.isService());}
XPI
GrailsPointCuts.aj
GormPointCuts.aj
public pointcut gormMethod():saveMethod() || ...;public pointcut saveMethod():execution(public Object save()) ||execution(public Object save(java.util.Map)) ||execution(public Object save(boolean));
public pointcut isDomain():@within(grails.persistence.Entity);
POKA-YOKE ADVICES
- Configurable:
- Logging.
- Throw RuntimeException.
- JMX MBean -> Collectd -> Graphite dashboard.
- Sugerencias?
GRAILS POKA-YOKES PLUGIN
- Sólo es un experimento
- No-tests-no-docs mode. Usa bajo tu responsabilidad.