segunda-feira, 21 de junho de 2010

Scrum e XP das trincheiras

INTRODUÇÃO

É grande o número de empresas e instituições de TI que tentaram implantar SCRUM em em seus times de desenvolvimento e falharam. A principal causa, a meu ver, é a ausência de uma normatização a respeitos das práticas que constituem SCRUM. De fato, considero esta a maior força e a maior fraqueza do SCRUM: Ele pode e deve ser adaptado ao ambiente e ao contexto da instituição onde será implantado. Esta questão rende debates acalorados e já foi tema de um post deste blog (aqui ó).

Scrum and XP from the trenches” (“Scrum e XP das trincheiras” em uma tradução livre) Descreve em detalhes o conjunto de práticas que funcionou em uma empresa sueca sob a liderança de Henrik Kniberg. Longe de ser uma “implementação de referência”, trata-se de uma caso de sucesso que acredito valer a pena ser lido e estudado.

O livro aborda temas comuns como:
  • Como conduzir cada cerimônia SCRUM
  • Como organizar o backlog do produto e do sprint
  • Interação e responsabilidades de cada papel (PO, SCRUM master, time)
  • Como lidar com múltiplos times e times geograficamente distribuídos
  • Tamanho dos Sprints
E também temas menos comuns, tais:
  • Arranjo físico do time
  • Pausas entre sprints
  • Comunicação do andamento do sprint para o restante da instituição. 
Seguem informações comentadas sobre alguns trechos do livro que julguei mais relevantes:

BACKLOG DO PRODUTO

O Backlog do produto é basicamente uma lista priorizada de requisitos, estórias, funcionalidades (escolha um nome, por aqui vamos ficar com estória). São coisas que o cliente quer descritas na linguagem do cliente.

É mantido em uma planilha do excel que fica em um drive compartilhado para todo o time. Cada item é composto pelos seguintes campos:

  • ID – Identificador numérico único. Serve principalmente para evitar confusão caso uma estória mude de nome.
  • Nome – Um nome que seja curto e mais descritivo possível. Normalmente entre 2 e 10 palavras.
  • Importância – O grau de importância da história para o cliente (informado pelo PO). Quanto maior a importância maior a prioridade da estória.
  • Estimativa Inicial – A estimativa do time com relação a esta estória.
  • Como demonstrar – Uma descrição de auto nível de como a história será demonstrada na sprint review.
  • Notas – Qualquer outra informação ou observação que o PO julgar necessária.
É importante manter o backlog do produto em um nível bastante negocial. Isso pode ser um problema se o PO possui background técnico, já que ele pode querer adicionar estórias como: “Incluir índice na base de dados”. Nestes casos o ideal é substituir o nome da estória por aquilo que seria na verdade o objetivo da tarefa técnica (neste caso, algo como “acelerar a resposta do sistema nas pesquisas”). O time é o papel mais adequado para decidir como isso será alcançado (embora a ideia original do PO pode e deve ser levada em consideração).

PLANEJAMENTO DO SPRINT

Esta é a cerimônia mais importante do SCRUM. Seu objetivo é levantar informação suficiente para que o time possa trabalhar tranquilamente por todo um sprint. Ao fim da reunião, é importante ter:

  • O objetivo do sprint.
  • Lista de membros do time.
  • Sprint Backlog.
  • Data da review.
  • Local e hora para o daily meeting.

A presença do PO é fundamental não apenas para explicações e remoção de dúvidas, mas para negociar escopo e importância das estórias de forma a influenciar o sprint backlog de acordo com os interesses do cliente.

É comum que sprints plannings demorem mais do que esperado. Nestes casos o melhor a fazer é interromper a reunião no momento planejado e deixar o sprint sofrer. Da próxima vez a tendência é que todos se esforcem para concluir tudo no tempo pre estabelecido.

BACKLOG DO SPRINT

Para decidir os itens do backlog do produto que irão compor o backlog do sprint, o time pode usar 3 abordagens:
  • Aproximação pelo sentimento
  • Cálculo de velocidade através da média dos sprints passados.
  • Cálculo de velocidade através cálculo de recursos.
Na aproximação pelo sentimento o time simplesmente inclui as estórias que acredita ser capaz de entregar ao fim do sprint.

No cálculo de velocidade através da média dos sprints passados o time inclui um total de pontos de estória equivalente a média dos pontos de estória entregues nos sprints passados.

No cálculo de velocidade usando cálculo de recursos é feita uma equivalência entre os pontos de estória e a quantidade de trabalho que um membro do time realiza em um dia. São incluídos um total de pontos de estória equivalentes a capacidade do time (que é o total de membros vezes o total de dias do sprint) multiplicado por um fator redutor que representa imprevistos e momentos não produtivos do time (o chamado fator de foco).

O ideal é usar essas abordagens em conjunto. Primeiro calcula-se a velocidade através do cálclo de recursos (aplicando o fator de foco adequado). Compara-se a velocidade obtida com a velocidade dos sprints passados para eventuais ajustes. Após incluir estórias de acordo com esta velocidade, faz-se uma análise de aproximação pelo sentimento possivelmente incluindo ou removendo estórias.

Quebrar uma estória em tarefas é útil para obter estimativas mais precisas. Além disso, melhora a eficiência dos daily meetings.

A melhor forma de manter um backlog do sprint é um quadro de tarefas como este aqui:



Notar:
  • Área para estórias (e tarefas) não iniciadas (mas planejadas), em execução e prontas.
  • Ordem de importância de cima para baixo.
  • Gráfico burndown
  • Área específica para tarefas não planejadas
  • Área específica para tarefas não incluídas no backlog, caso sobre tempo.

ESTÓRIAS NÃO NEGOCIAIS

Em todo Sprint existem tarefas que não constituem entregáveis, nem estão ligadas a nenhuma estória específica, enfim, não adicionam valor diretamente ao produto. São coisas como Configurar e manutenir o servidor de integração contínua, ou refatorar determinada camada da aplicação. Encarar estas tarefas normalmente como itens do backlog do produto não é bom porque naturalmente o PO vai negligenciá-las na hora de priorizar. Eis algumas possíveis soluções:

  • Evitar estórias não negociais. Tentar ao máximo adicionar valor negocial a este tipo de estória e deixar que o PO priorizar normalmente.
  • Verificar se a tarefa pode ser realizada como parte de outra estória negocial.
  • Manter um backlog separado de estórias não negociais e incluir algumas em cada sprint, tentando conscientizar o PO sobre sua importância (é possível usar o cálculo da velocidade para barganhar!)

REUNIÃO DIÁRIA

A reunião diária é basicamente “by the book”. O time se reúne diante do quadro de tarefas do backlog do sprint, responde às três perguntas básicas e cada membro atualiza o quadro enquanto fala.

REVIEW

Esta reunião tende a ser subestimada, mas é importante principalmente porque:
  • O time obtém crédito pelo seu trabalho.
  • Outras pessoas ficam sabendo no que o time está trabalhando.
  • Atrai feedbacks importantes dos clientes.
  • Força o time a terminar os sprints com estórias realmente prontas.
Em uma review, não esquecer:
  • Apresentar claramente o objetivo do sprint
  • Foco em mostrar o produto funcionando.
  • Manter sempre em nível negocial
  • Se possível, deixe a audiência usar o produto.

RETROSPECTIVA

Essa é a segunda reunião mais importante do SCRUM. Como devem ser organizadas:
  • Aloque de 1 a 3 horas, dependendo de quanto discussão é prevista.
  • O time, o Scrum Master e o PO devem participar.
  • A reunião deve ocorrer em uma sala isolada.
  • Alguém é designado secretário
  • O scrum master exibe o sprint backlog e com ajuda do time faz um resumo do sprint, incluindo acontecimentos importantes e decisões relevantes.
  • Cada pessoa, na sua vez, fala, sem ser interrompida, o que achou bom, o que poderia ter sido melhor e o que gostaria de fazer diferente no próximo sprint.
  • Compara-se a velocidade estimada e a real. Havendo grande discrepância, discute-se as possíveis causas.
  • No final o scrum master resume as sugestões concretas sobre o que pode ser melhorado para o próximo sprint.
  • Faz-se uma votação para decidir quais destas sugestões devem ser levadas a cabo no sprint que vai iniciar.

CONCLUSÃO

Obviamente há muito mais no livro do que foi enumerado aqui. Em especial, a parte de múltiplos times e times distribuídos, deixada de fora aqui por motivos de espaço, são bastante interessantes. Além disso, existem diversas dicas de como lidar com situações comuns que encontramos no dia a dia de um time que usa SCRUM. Enfim, trata-se de uma excelente leitura!

sábado, 12 de junho de 2010

Apache Commons: Lang

Continuando a série sobre as bibliotecas apache commons, Este post irá se aprofundar nos componentes da biblioteca Lang, cujo principal objetivo é estender as funcionalidades das classes do pacote java.lang. Além disso, a commons Lang inclui componentes de uso comum a outras bibliotecas.

Existem vários componentes do tipo “XXXUtils” na biblioteca commons Lang. Seus métodos, estáticos e autossuficientes, funcionam como funções globais presentes em outras tecnologias.

Devido a diversidade dos componentes desta biblioteca, examinaremo-os por categoria de acordo com as funcionalidades que julguei mais interessantes:

Manipulação de Strings

Suporte a manipulação de String é, sem dúvida, o ponto forte desta biblioteca. Existe uma série de componentes com este propósito, sendo os principais:
  • StringUtils - Encapsula uma série de funcionalidades null-safe de propósito geral que atuam sobre Strings. Minhas favoritas:
    • isEmpty/isBlank - Verifica se uma string contém texto. Uma string empty (vazia) seria o equivalente a “” e uma string blank (branca) cotém apenas caracteres brancos “    ”.
    • upperCase/lowerCase/swapCase/capitalize/uncaptalize - Mudam a capitalização de uma String. Atenção para o método captalize que coloca em caixa alta apenas o primeiro caractere. Nice hum?
    • isAlpha/isNumeric/isWhitespace/isAsciiPrintable - Faz verificações sobre uma string. IsNumeric é bem legal, mas seria mais útil se pudéssemos aferir sobre o tipo de numérico, algo como isInteger ou isShort.
    • abbreviate - Faz abreviação de uma string usando reticências.
    • difference - Compara duas Strings, retornando suas diferenças.
  • RandomStringUtils - Ajuda na criação de string randômicos, úteis na geração de senhas, por exemplo.
  • WordUtils  - Contém métodos que manipulam palavras de um texto, seja obtendo as iniciais (initials) ou colocando-as em caixa alta (captalize)
  • StrSubstitutor – Substitui variáveis em uma string por valores pré-definidos.

Exemplos StringUtils

String nullString = null;
String emptyString = "";
String blankString = "  ";
String regularString = "apache commons lang";

StringUtils.isEmpty(nullString); // true
StringUtils.isEmpty(emptyString);// true
StringUtils.isEmpty(blankString);// false
StringUtils.isEmpty(regularString);// false

StringUtils.isBlank(nullString); // true
StringUtils.isBlank(emptyString); // true
StringUtils.isBlank(blankString); // true
StringUtils.isBlank(regularString); // false


String lowcase = "lang";
  String uppercase = "LANG";
  String nullString = null;

  StringUtils.upperCase(lowcase); // LANG
  StringUtils.upperCase(uppercase); // LANG
  StringUtils.upperCase(nullString); // null

  StringUtils.lowerCase(lowcase); // lang
  StringUtils.lowerCase(uppercase);// lang
  StringUtils.lowerCase(nullString);// null

  StringUtils.swapCase(lowcase); // LANG
  StringUtils.swapCase(uppercase);// lang
  StringUtils.swapCase(nullString);// null

  StringUtils.capitalize(lowcase); // Lang
  StringUtils.capitalize(uppercase);// LANG
  StringUtils.capitalize(nullString);// null

  StringUtils.uncapitalize(lowcase); // lang
  StringUtils.uncapitalize(uppercase);// lANG
  StringUtils.uncapitalize(nullString);// null


String onlyAlphaString = "abcd";
  String onlyNumericString = "123";
  String mixString = "a1b 2c3";
  String blackString = "  ";

  StringUtils.isAlpha(onlyAlphaString); // true
  StringUtils.isAlpha(onlyNumericString); // false
  StringUtils.isAlpha(mixString); // false
  StringUtils.isAlpha(blackString); // false

  StringUtils.isNumeric(onlyAlphaString); // false
  StringUtils.isNumeric(onlyNumericString); // true
  StringUtils.isNumeric(mixString); // false
  StringUtils.isNumeric(blackString); // false

  StringUtils.isWhitespace(onlyAlphaString); // false
  StringUtils.isWhitespace(onlyNumericString); // false
  StringUtils.isWhitespace(mixString); // false
  StringUtils.isWhitespace(blackString); // true




String bigString = "this string is too big to fit you application's grid";

  String smallerString = StringUtils.abbreviate(bigString, 30);

  System.out.println(smallerString); // this string is too big to f...






String stringOne = "Luke, I'm your father!";
String stringTwo = "Luke, I'm your uncle!"; 
StringUtils.difference(stringOne, stringTwo); //uncle!
Exemplo WordUtils

String name = "carla gabriele batista dos santos";
  
  WordUtils.initials(name);//cgbds
  WordUtils.capitalize(name);//Carla Gabriele Batista Dos Santos



Exemplo StrSubstitutor

Map variablesMap = new HashMap();
   variablesMap.put("var1", "first variable");
   variablesMap.put("var2", "second variable");
   String templateString = "Let's print ${var1} and ${var2}";
   StrSubstitutor sub = new StrSubstitutor(variablesMap);
   String resolvedString = sub.replace(templateString);
   
System.out.println(resolvedString); //Let's print first variable and second variable



ObjectUtils e ClassUtils

Estes componentes merecem uma menção honrosa a parte. ObjectUtils pode ser usado para efetuar operações corriqueiras como comparações e obtenções de hashCode de forma null-safe. ClassUtils contém método para obtenção de uma série de informações a respeito de uma ou mais classes, como as interfaces que ela implementa e superclasses que estende, o pacote no qual se localiza, verificar se é uma classe interna, se pode ser assinalada para uma referência de um outro tipo, entre outros, e tudo isso de forma null-safe.

Integer intVariable = null;
  Integer anotherIntVariable = 5;
  
  ObjectUtils.hashCode(intVariable); //0
  
  ObjectUtils.toString(intVariable); //""
  
  ObjectUtils.equals(intVariable, anotherIntVariable); //false
  
 ObjectUtils.max(intVariable, anotherIntVariable); // 5 (podia ser genérico né?)



System.out.println(ClassUtils.getAllInterfaces(Integer.class)); // [interface java.lang.Comparable, interface java.io.Serializable]
  
  ClassUtils.getAllSuperclasses(Integer.class); //[class java.lang.Number, class java.lang.Object]
  
  ClassUtils.getPackageName(Integer.class); //java.lang
  
  ClassUtils.isInnerClass(Integer.class); //false
  
  ClassUtils.isInnerClass(Integer.class); //false

  ClassUtils.isAssignable(Integer.class, Number.class); //true





Builders

Esta categoria inclui 4 componentes que auxiliam na implementação de 4 métodos básicos, em especial para classes de domínio, de qualquer sistema: EqualsBuilder, HashCodeBuilder, CompareToBuilder e toStringBuilder. Através delas é possível obter uma implementação correta e eficiente dos métodos equals, hashCode, compareTo e toString, respectivamente, apenas informando as propriedades envolvidas.

Além disso, todos os componentes possuem métodos alternativos que permitem a execução de forma reflexiva, incluindo todas as propriedades. Nestes casos, é possível especificar propriedades a serem ignoradas, se for o caso.

public class Person implements Comparable {

 private String name;

 private Integer age;

 private Float weight;

 private Float height;

 private Person mother;

 /**
  * São consideradas iguais duas pessoas com mesmo nome, idade e filhas da
  * mesma mãe.
  */
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Person other = (Person) obj;

  return new EqualsBuilder().append(age, other.age).append(mother,
    other.mother).append(name, other.name).isEquals();

  // Implementação alternativa que usa reflexão. É preciso informar os
  // campos a serem ignorados na compração para manter equivalência com a
  // implementação a cima.
  //
  // return EqualsBuilder.reflectionEquals(this, obj, Arrays.asList(
  // "weight", "height"));

 }

 /**
  * cálculo do hash code consistente com equals.
  */
 @Override
 public int hashCode() {

  return new HashCodeBuilder(17, 37).append(name).append(age).append(
    mother).toHashCode();

  // Implementação alternativa que usa reflexão. É preciso informar os
  // campos a serem ignorados. para manter equivalência com a
  // implementação a cima.
  //
  // return HashCodeBuilder.reflectionHashCode(this,
  // Arrays.asList("weight",
  // "height"));

 }

 /**
  * Define a representação em forma de String de um objeto da classe Person
  */
 @Override
 public String toString() {

  return new ToStringBuilder(this).append("name", name)
    .append("age", age).toString();

  // Implementação alternativa que usa reflexão. Não é possível informar
  // atributos a serem ignorados, portanto esta implementação não é
  // equivalente àquela a cima.
  //
  // return ToStringBuilder.reflectionToString(this);
 }

 /**
  * Determina forma de comparar objetos do tipo Person.
  */
 @Override
 public int compareTo(Person o) {
  return new CompareToBuilder().append(this.name, o.name).append(
    this.age, o.age).toComparison();

  // Implementação alternativa que usa reflexão. É preciso informar os
  // campos a serem ignorados. para manter equivalência com a
  // implementação a cima.
  //
  // return CompareToBuilder.reflectionCompare(this, o,
  // Arrays.asList("weight", "height","mother"));
 }

//getter e setter omitidos
} 

Math

A biblioteca commons lang inclui alguns componentes com funcionalidades matemáticas básicas. (as mais avançadas ficam na bilbioteca Math) Destaque para as classes que representam intervalos numéricos (IntRange, LongRange, FloatRange e DoubleRange), para a classe Fraction, que representa um número fracionário e para NumberUtils, que disponibiliza métodos utilitários interessantes.

IntRange firstRange = new IntRange(0, 10);
  IntRange secondRange = new IntRange(6,13);
  IntRange thirdRange = new IntRange(11,15);
  
  
  firstRange.containsInteger(5); //true
  firstRange.containsInteger(10); //true. ambos liites são inclusivos.
  firstRange.containsInteger(15); // false
  
  secondRange.getMaximumInteger(); //13
  secondRange.getMinimumInteger(); // 6
  
  firstRange.overlapsRange(secondRange); // true
  firstRange.overlapsRange(thirdRange); // false



Outros

Além dos componentes já mencinados, existem alguns outros que vale apena conferir, como aqueles relacioandos a tempo (destque para StopWatch) e para criação de tipe-safes enums que, devido a sua flexibilidade, continuam sendo muito utilizadas apesar das enumerações já terem sido padonizadas na versão 5 da linguagem.