Cheguei ontem em Porto Alegre para participar da oitava edição do FISL, o Fórum Internacional Software Livre. O IME organizou uma caravana com cerca de 50 pessoas, mas eu preferi não viajar 20 horas de ônibus (incluindo 2 paradas policiais para conferir o nome e RG de todo mundo) e p(e|a)gar um vôo de 1h para chegar aqui (Ok, o vôo atrasou 1 hora para sair de SP…)



Chegando ao FIERGS, encontramos uma fila gigante para o cadastramento. Depois de passar um tempo parados na fila, descobrimos que podíamos entrar sem o crachá até o 12:00, em outras palavras, estávamos perdendo tempo. Entramos direto para a palestra do “Maddog” sobre VoIP e Asterisk, onde ele apresentou um pouco sobre a evolução e a influência da tecnologia nos sistemas de telefonia, desde os tempos onde precisávamos pedir para a telefonista preparar uma linha para uma ligação de longa distância (não, eu não sou dessa época), passando pela época onde precisamos de diversas traduções entre sinais analógico-digital e vice-versa (PABX), até hoje onde conseguimos tratar voz como dados, que são roteados normalmente na internet (VoIP).

No entanto, não estou assistindo muitas palestras pois existem muitas trilhas concorrentes e os assuntos são muito diversificados. Porém, a convivência com as pessoas do IME e o pessoal que estou conhecendo aqui no FISL são muito mais valiosas que o conteúdo das palestras em si. A troca de experiências com as diversas comunidades é muito enriquecedora. Vou gastar mais algumas linhas com alguns dos highlights do primeiro dia do FISL:

  • Lilypond: Infelizmente peguei só os últimos 10 minutos da palestra com o criador do Lilypond (que agora trabalha no Google, em Belo Horizonte), mas foram os 10 minutos que eu mais gostei até agora! Ele mostrou uma forma diferente de encarar os testes automatizados no Lilypond, através da comparação de imagens e análise de mensagens de log, além de falar sobre a forma como criaram um Grand Unified Build para compilar distribuições para diferentes plataformas de forma unificada e automatizada.
  • Google: O Google está muito presente aqui no evento, recrutando, entrevistando, distribuindo brindes e apresentando palestras. Na parte da tarde assisti uma palestra sobre alguns dos papers já publicados por lá, falando sobre: MapReduce, Google File System (GFS) e Big-Table. Vale a pena conhecer, quem não conhece!
  • Churrasco: Não poderíamos passar por Porto Alegre sem comer um belo churrasco, o pessoal do IME se organizou para jantar numa das melhores churrascarias daqui (segundo a propaganda). Se é realmente a melhor eu não posso dizer, mas que a comida estava muito boa, isso não posso negar.

Hoje já aconteceram mais coisas legais, mas vou deixar para escrever o relato do Dia 2 amanhã, afinal meu ritmo de produção é diretamente proporcional ao número de intervalos livres que tenho durante o FISL. :-)

Post to Twitter

March 14th, 2007XP 2007 aí vou eu!

Boas novas! Meu artigo foi aceito e agora estou planejando minha viagem para Como na Itália para participar do XP 2007. Se você também está pensando em ir para a conferência, entre em contato ou deixe um comentário para combinarmos de nos encontrar por lá para dividirmos experiências e, quem sabe até, despesas de hotel. :-)



Conforme o Vinicius anunciou, esse ano promete para as conferências ágeis no mundo e, quem sabe, no Brasil também. As coisas estão esquentando por aqui ultimamente. Tivemos o nosso Curso de Verão da AgilCoop e já estamos planejando ministrá-lo novamente em horários melhores. Além disso, a Teamware está organizando novos cursos de Certified Scrum Master em São Paulo, Rio de Janeiro, Florianópolis, Brasília e Buenos Aires, e Certified Scrum Leader em São Paulo. A Heptagon está organizando um workshop sobre FDD. A ImproveIt está com um curso super interessante de Imersão Ágil.

Estamos semeando novos conceitos e paradigmas na indústria brasileira de software e tenho certeza que em breve teremos muitas empresas interessadas em adotar métodos ágeis. Não vamos deixar a bola cair!

Post to Twitter

Para quem ainda não sabe, eu toco piano/teclado desde pequeno. Acontece que fui chamado para cobrir um tecladista de última hora e vou me apresentar hoje tocando Rush cover no Led Slay. O bar fica na Zona Leste, Av. Celso Garcia, 5765 e tem um mapa para os perdidos (assim como eu). O preço é R$1,00 até as 22:00hs e R$5,00 depois das 22:00hs. Devemos tocar por volta das 00:00hs. Se conseguir fotos/vídeos eu publico aqui depois.

Post to Twitter

March 1st, 2007De Volta à Ativa

Apesar das coisas parecerem paradas por aqui, estive bastante ocupado nos últimos meses. Nada melhor que aproveitar o pós-carnaval para voltar a escrever (um post curto) :-)



É claro que nada serve como desculpa para não escrever no blog, mas deixo aqui uma lista com algumas das coisas que me mantiveram ocupado desde o último post:

  • Mestrado: O tema do meu mestrado é tracking de projetos ágeis (mais especificamente XP) e minha qualificação foi no dia 12 de Dezembro. O feedback recebido da banca de avaliação foi bastante positivo e enriquecedor. O texto da qualificação não está disponível pois está em fase de “refatoração+implementação” para a defesa, que deve ocorrer nos próximos meses.
  • Artigos: Meu primeiro artigo científico foi aceito numa edição especial de uma das principais publicações da SBC, o Journal of Brazilian Computer Science. O artigo foi escrito em inglês, com o título “Experiences Tracking Agile Projects: an Empirical Study”. A versão impressa já deve sair em breve mas a versão online já está disponível aqui e no site da AgilCoop. Além disso, em Janeiro submeti outro artigo para a conferência XP 2007 que ainda não sei se vai ser aceito.
  • Curso de Verão: A AgilCoop ministrou em Fevereiro dois cursos de uma semana (um teórico e outro prático) sobre Métodos Ágeis. Foi muito divertido preparar e apresentar esses cursos e o feedback que recebemos foi muito positivo. Devo falar um pouco mais sobre o curso em outro post, mas para quem estiver curioso, os slides estão disponíveis (com Copyleft) no site da AgilCoop.
  • Coaching XP: Desde Dezembro, estou prestando consultoria em XP/Métodos Ágeis numa empresa start-up que tem potencial para ganhar o seu espaço na web brasileira: o Ikwa. A premissa básica do projeto é criar uma comunidade ao redor do tema “orientação profissional”, onde estudantes do ensino médio ao universitário possam explorar o seu futuro e traçar suas carreiras através de ferramentas que os auxiliam na busca pelo auto-conhecimento, desde a escolha do curso até a vida profissional. O trabalho está muito legal! Nós passamos por uma fase de entrevistas e testes para montar a equipe de desenvolvimento que acaba de ser completada. Devo falar mais sobre as experiências por lá em outros posts. Por enquanto os curiosos podem acessar o Ikwa Blog.

Por enquanto é só. Até breve! E, dessa vez, espero que seja breve mesmo :-)

Post to Twitter

October 20th, 2006Novo Vício

Ultimamente me dei conta que estou cada vez mais dependente dos serviços do Google para realizar minhas tarefas mais básicas no trabalho, na faculdade e em casa. Meu novo vício é ler notícias no novo leitor RSS, o Google Reader



Esses dias percebi que tem um link “all my services »” no GMail que mostra a lista de serviços do Google associados à minha conta. Resolvi clicar para ver e descobri que eles sabem muito sobre a minha vida: e-mail, agenda, acessos ao meu site, orkut, listas de discussão, GTalk e não acaba mais… Dá até medo pensar o que eles podem fazer com tudo isso de informação.

Acontece que, mesmo assim, eu continuo me rendendo aos novos serviços.
Por um lado, ainda não acho tão viável fazer documentos e planilhas na web. A idéia é ótima e dá até gosto ver essas aplicações rodando no browser, mas o Firefox ainda não é um bom gerenciador de janelas. Ficar colocando aplicações em tabs não é muito organizado. Ainda mais quando eu já costumo deixar um monte de links abertos em tabs para ler quando chegar em casa (já ia me esquecendo, também uso o serviço de sincronização de browser). Por outro lado, é tão bom ter esses serviços de graça e com tanta qualidade que fica difícil de resistir.

Há algumas semanas atrás, meu amigo Daniel, vulgo DC, me mostrou o novo leitor de RSS do Google, o Google Reader. Achei fantástico! Nos últimos tempos, havia tentado utilizar alguns produtos stand-alone e até extensões do Firefox para essa tarefa, mas eu sempre esquecia de ler e acabava acumulando um monte de coisas. Dava até desânimo perceber que tinha deixado passar tanta coisa interessante. Quando vi o Google Reader funcionando na web (na mesma tab que já está sincronizada e vive aberta em qualquer navegador que eu abro), e ainda por cima com gadgets para colocar na minha página incial do Google, foi amor a primeira vista.

Em primeiro lugar, exportar o OPML do antigo leitor para o novo foi tão fácil que não deu nem graça. Em segundos já tinha todos os meus sites de notícia funcionando no Google Reader. E as teclas de atalho ajudam muito! O que antes era clicar em vários ícones para abrir um outro programa e ficar clicando em cada RSS para ler, agora é só mudar de tab e apertar “j”.

Como se não bastasse tudo isso de legal, agora eu também posso compartilhar minhas notícias preferidas:

Add to Google

CUIDADO! Vocês também podem acabar se viciando como eu. Agora eu não consigo mais dormir sem deixar a “caixa de notícias” vazia.

Atualização (15/Dez): Eu e alguns amigos tivemos problemas com o Google Reader devido à forma como compartilhei o meu feed. Estava utilizando o Google Fusion com o ícone acima e ele estava com um bug, o que fazia com que minhas notícias no Google Reader ficassem marcadas como não-lidas e, o pior, ninguém conseguia remover meu RSS. Desculpem o inconveniente. Ontem (14/Dez) foi lançada uma nova release do Google Reader que permite a remoção da minha feed. Além disso, o Google Fusion não permite mais adicionar feeds ao Google Reader. Espero que eles consertem isso logo. Quem estiver interessado nas minhas notícias, ainda podem adicioná-la no próprio Google Reader, através da URL: http://www.google.com/reader/public/atom/user/06330148482348338208/state/com.google/broadcast

Post to Twitter

September 30th, 2006TDD em Combate – Parte 3

Apesar da demora, o desenvolvimento do jogo continuou. Neste post falaremos um pouco sobre a implementação das regras de movimentação, mostrando como novos conceitos surgem com a evolução natural do design e com a constante preocupação com a clareza e a simplicidade.

Refatorando um pouco antes de seguir em frente

Desde o último post, muitas coisas aconteceram (dentre elas minha ida ao Agile 2006) e aprendi muitas coisas. Uma das mais importantes descobertas foi a relevância dos nomes dos testes. Após ficar um tempo sem mexer no código do jogo, olhei para os nomes dos métodos de teste e não entendi o que eles faziam. Precisei olhar para o código para entender. É muito importante que o nome dos métodos de teste evidenciem não apenas o cenário do teste, mas também as expectativas sobre o comportamento que está sendo testado. Quando um teste falhar no futuro, se o nome do teste for bom, você não precisará olhar o código para entender o problema. Existe inclusive uma ferramenta Java muito simples porém muito poderosa, o Agiledox, que transforma os nomes dos métodos de uma classe de teste JUnit numa frase, criando uma documentação simples a partir do código.

Além disso, pensando na legibilidade do código de teste, resolvemos criar uma subclasse especial da peça, com métodos auxiliares que verificam o comportamento esperado numa vitória, num empate e numa derrota. Por fim, descobrimos um pequeno erro nos testes com exceções esperadas: era preciso chamar self.fail() ao invés de fail() para utilizar o método da superclasse unittest.TestCase (desculpem minha falta de conhecimento de Python, ainda estou aprendendo). O código de teste refatorado ficou assim:

# StrategoTest.py
 
import unittest
import unittestgui
import Stratego
from Stratego import InvalidPiece, InvalidAttack
 
class Piece(Stratego.Piece):    def winsAgainst(self, defendingPiece):        return self.attack(defendingPiece) > 0    def losesAgainst(self, defendingPiece):        return self.attack(defendingPiece) < 0    def tiesWith(self, defendingPiece):        return self.attack(defendingPiece) == 0 
class CreatePieceTest(unittest.TestCase):
    def testCreateSoldierWithRank2(self):
        assert Piece("soldier").rank == 2
    def testCreateMinerWithRank3(self):
        assert Piece("miner").rank == 3
    def testCreateSergeantWithRank4(self):
        assert Piece("sergeant").rank == 4
    def testCreateLieutenantWithRank5(self):
        assert Piece("lieutenant").rank == 5
    def testCreateCaptainWithRank6(self):
        assert Piece("captain").rank == 6
    def testCreateMajorWithRank7(self):
        assert Piece("major").rank == 7
    def testCreateColonelWithRank8(self):
        assert Piece("colonel").rank == 8
    def testCreateGeneralWithRank9(self):
        assert Piece("general").rank == 9
    def testCreateMarshalWithRank10(self):
        assert Piece("marshal").rank == 10
    def testCreateSpyWithRank1(self):
        assert Piece("spy").rank == 1
    def testCreateBombWithRank11(self):
        assert Piece("bomb").rank == 11
    def testCreateFlagWithRank0(self):
        assert Piece("flag").rank == 0
    def testCreateInvalidPieceRaises(self):
        try:
            Piece("invalid")
        except InvalidPiece:
            pass
        else:
            self.fail("expected InvalidPiece exception") 
class AttackTest(unittest.TestCase):
    def testHigherRankWins(self):
        assert Piece("sergeant").winsAgainst(Piece("soldier"))    def testLowerRankLoses(self):
        assert Piece("miner").losesAgainst(Piece("colonel"))    def testEqualRankTies(self):
        assert Piece("major").tiesWith(Piece("major"))    def testSpyWinsAgainstMarshal(self):
        assert Piece("spy").winsAgainst(Piece("marshal"))    def testMarshalWinsAgainstSpy(self):
        assert Piece("marshal").winsAgainst(Piece("spy"))    def testMinerWinsAgainstBomb(self):
        assert Piece("miner").winsAgainst(Piece("bomb"))    def testBombCannotAtack(self):
        try:
            Piece("bomb").attack(Piece("miner"))
        except InvalidAttack:
            pass
        else:
            self.fail("expected InvalidAttack exception")    def testFlagCannotAtack(self):
        try:
            Piece("flag").attack(Piece("lieutenant"))
        except InvalidAttack:
            pass
        else:
            self.fail("expected InvalidAttack exception") 
# (...) Main Method

 
Como uma lição final aprendida com o erro acima, devemos sempre olhar o motivo de um teste estar vermelho. Nesse caso, ele ficava vermelho devido ao erro de sintaxe, pois o interpretador Python não encontrava o método fail() quando a exceção não era lançada. Ao fazer o teste passar lançando a exceção esperada, fazemos com que o teste nunca caia na chamada ao método fail(). Por ser uma linguagem dinâmica, o interpretador de Python não reclama pois nem precisa executar a linha errada.

Evoluindo o design para implementar a movimentação

Conforme discutimos no primeiro post, as regras de movimentação são simples: todas as peças podem dar 1 passo em qualquer direção vertical ou horizontal, exceto o Soldado (que pode andar mais que 1 passo), a Bomba e a Bandeira (que não podem andar).

É hora de pensarmos um pouco sobre o design para implementar a movimentação. Uma primeira idéia era fazer a peça guardar sua posição atual no tabuleiro e representar um movimento com uma chamada a um método que receberia a posição final da peça. O problema com essa abordagem é que a peça ficaria com muito mais responsabilidade que ela deveria. Uma peça precisa saber sobre coordenadas do tabuleiro? Como o tabuleiro saberia qual peça está em qual posição na hora de verificar movimentos inválidos ou situações de ataque? Pensando nas dependências entre os objetos, nos parece muito mais sensato o tabuleiro conhecer as peças. Porém, conforme já verificamos, algumas peças possuem regras especiais de movimentação. Então, apesar da peça não precisar saber sobre coordenadas, precisaremos de alguma forma perguntar para a peça se o “tamanho” do movimento que estamos tentando realizar é válido.

Foi nessa hora que tivemos um daqueles momentos “Eureka”: “Podemos representar o movimento como um vetor geométrico aplicado à um ponto num plano cartesiano”. Com isso, podemos calcular a magnitude do vetor e perguntar para a peça se aquele movimento é de “tamanho” válido. Infelizmente, TDD não ensina como ter esses momentos de insight, mas todos nós sabemos que programar é uma atividade que requer criatividade e treino. Na minha opinião, duas práticas de XP são o segredo para facilitar o surgimento desses momentos: Trabalho Energizado (antigamente chamado de Ritmo Sustentável ou Semana de 40 Horas) e Programação Pareada. Quantas vezes você já não ficou trabalhando até tarde em cima do mesmo problema e, ao chegar na manhã seguinte, descobre a solução trivial? Além disso, é incrível a quantidade de vezes que me surpreendi em discussões sobre um problema específico. Muitas vezes a discordância entre os pares termina com o surgimento de uma nova solução, muito melhor do que todas as outras pensadas anteriormente (e que originaram a discussão).

Uma pequena revisão da aula de geometria e vetores: um vetor num espaço euclidiano de n dimensões pode ser representado como uma combinação linear de n vetores unitários e ortonormais (também conhecidos como base). Num espaço bi-dimensional, essa base é formada pelos vetores geradores dos eixos x e y. Sendo i e j os vetores unitários paralelos aos eixos x e y respectivamente, podemos representar qualquer outro vetor a nesse espaço 2D como uma tupla (a1, a2) onde a = a1i + a2j.

Como os movimentos no jogo são apenas na vertical ou na horizontal, criamos o que chamamos de vetores ortogonais. Vetores ortogonais devem ter pelo menos um componente nulo, seja na vertical ou na horizontal. Com isso já podemos continuar com os próximos testes.

Vetores num tabuleiro 2D

Primeiramente vamos fazer um teste para verificar a criação de um vetor horizontal. Para isso, criamos uma nova classe de teste e a incluímos na suite de testes:

# StrategoTest.py
 
class OrthogonalVectorTest(unittest.TestCase):
    def testCreateHorizontalVector(self):
        assert OrthogonalVector(42, 0).y == 0
 
suite = unittest.TestSuite((unittest.makeSuite(CreatePieceTest),
                            unittest.makeSuite(AttackTest),
                            unittest.makeSuite(OrthogonalVectorTest)                          ))
# (...) Main Method

Para fazer esse teste passar, basta criar uma nova classe OrthogonalVector e importá-la no módulo de teste:

# Stratego.py
 
class OrthogonalVector:
    def __init__(self, x, y):
        self.y = 0

A implementação para os próximos dois testes segue o mesmo raciocínio, então vou pular os passos intermediários e mostrar os novos testes no verde:

# StrategoTest.py
 
class OrthogonalVectorTest(unittest.TestCase):
    def testCreateHorizontalVector(self):
        assert OrthogonalVector(42, 0).y == 0
    def testCreateVerticalVector(self):        assert OrthogonalVector(0, 42).x == 0    def testNonOrthogonalVectorRaises(self):        try:            OrthogonalVector(1, 2)        except InvalidVector:            pass        else:            self.fail("expected InvalidVector exception") 
# Stratego.py - (...)
 
class OrthogonalVector:
    def __init__(self, x, y):
        if 0 not in [x, y]:            raise InvalidVector        self.x = 0        self.y = 0
# (...)
 
class InvalidVector(Exception):
    pass

Nosso vetor ainda não armazena os valores esperados no construtor. Lembrem-se que a implementação para deixar o teste verde deve ser rápida. O próximo teste que falha verifica o tamanho de um vetor com componentes positivas:

# StrategoTest.py - (...) class OrthogonalVectorTest
 
    def testCalculatePositiveMagnitude(self):
        assert OrthogonalVector(3, 0).magnitude() == 3
# (...)

A implementação ainda não pede pelo armazenamento dos componentes do vetor, uma vez que podemos devolver uma constante no novo método:

# Stratego.py - (...) class OrthogonalVector
 
    def magnitude(self):
        return 3
# (...)

O próximo teste para calcular o tamanho de um vetor com componentes negativos finalmente pede pela implementação que armazena os valores dos componentes no construtor:

# StrategoTest.py - (...) class OrthogonalVectorTest
 
    def testCalculateNegativeMagnitude(self):        assert OrthogonalVector(0, -42).magnitude() == 42 
# Stratego.py - (...) class OrthogonalVector
 
    def __init__(self, x, y):
        if 0 not in [x, y]:
            raise InvalidVector
        self.x = x        self.y = y    def magnitude(self):
        return abs(self.x + self.y)# (...)

Essa técnica de implementação que acabamos de utilizar é chamada de triangulação. Para aplicá-la, você deve escrever mais de um teste com exemplos diferentes a respeito do mesmo comportamento. Por exemplo, num método que soma 2 + 2, para evitar que o resultado seja sempre 4, você pode escrever outro teste que verifica se soma(3, 4) é igual a 7. Reparem também que nossa implementação não é completamente correta. Aqueles que estudaram geometria devem estar pensando: “Por que estamos calculando a norma do vetor como a soma de seus componentes?”, quando na verdade deveria ser a raíz da soma dos quadrados dos componentes. Lembrem-se da importância do design simples e evolutivo. Não tentem incluir complexidade além da necessária. No nosso caso particular, como um dos componentes é sempre nulo por construção, podemos considerar que a norma do vetor é a soma dos componentes.
 

As peças aprendem o quanto podem se movimentar

Agora que já temos uma implementação simples de vetores, podemos começar a pensar nas regras que verificam o quanto cada peça pode se mover. Para isso, escrevemos o nosso próximo teste que verifica a regra geral de movimentação, implementando o mínimo para fazê-lo passar. Por padrão, todas as peças podem dar 1 passo em qualquer direção:

# StrategoTest.py
 
class MovementTest(unittest.TestCase):    def testSergeantCanMoveOneStep(self):        assert Piece("sergeant").canMove(1) 
suite = unittest.TestSuite((unittest.makeSuite(CreatePieceTest),
                            unittest.makeSuite(AttackTest),
                            unittest.makeSuite(OrthogonalVectorTest),
                            unittest.makeSuite(MovementTest)                           ))
# (...) Main Method
 
# Stratego.py - (...) class Piece
 
    def canMove(self, steps):        return True 
# (...) OrthogonalVector/Exceptions

Os próximos dois testes garantem que a bomba e a bandeira não podem andar. Mais uma vez vou mostrar os testes e a implementação, agora que vocês já entenderam o ritmo de TDD. Primeiro vemos o teste falhar, depois implementamos o mínimo para fazê-lo passar para depois refatorar e melhorar o design:

# StrategoTest.py - (...) class MovementTest
 
   def testBombCannotMove(self):        assert not Piece("bomb").canMove(1)    def testFlagCannotMove(self):        assert not Piece("flag").canMove(1)# (...)
 
# Stratego.py - (...) class Piece
 
    def canMove(self, steps):
        if self.rank in [11, 0]:            return False        return True
# (...) OrthogonalVector/Exceptions

Por enquanto a implementação ainda não está completa. Precisamos de um teste para garantir que uma peça normal não pode dar mais que 1 passo:

# StrategoTest.py - (...) class MovementTest
 
    def testMajorCannotMoveMoreThanOneStep(self):        assert not Piece("major").canMove(2)# (...)

A implementação para fazer o teste passar pode ser feia a princípio…

# Stratego.py - (...) class Piece
 
    def canMove(self, steps):
        if self.rank in [11, 0] or (self.rank == 7 and steps > 1):            return False
        return True
# (...)

… contanto que a refatoração seja feita depois que a barra de testes está verde. Nesse caso, resolvemos criar uma classe auxiliar para armazenar as propriedades de uma peça (por enquanto o rank e o número máximo de passos que ela pode dar), modificando o dicionário de inicialização, o construtor e o método canMove():

# Stratego.py
 
class PieceProperties:    def __init__(self, rank, maxSteps=1):        self.rank = rank        self.maxSteps = maxSteps 
class Piece:
    __pieces = {"flag": PieceProperties(0, 0),                "spy": PieceProperties(1),                "soldier": PieceProperties(2),                "miner": PieceProperties(3),                "sergeant": PieceProperties(4),                "lieutenant": PieceProperties(5),                "captain": PieceProperties(6),                "major": PieceProperties(7),                "colonel": PieceProperties(8),                "general": PieceProperties(9),                "marshal": PieceProperties(10),                "bomb": PieceProperties(11, 0)}    __winsAttacking = [(1,10), (3,11)]
    __cannotAttack = [0,11]
 
    def __init__(self, name):
        try:
            self.rank = self.__pieces[name].rank            self.maxSteps = self.__pieces[name].maxSteps        except KeyError:
            raise InvalidPiece()
 
    # (...) attack() Method
 
    def canMove(self, steps):        return steps <= self.maxSteps 
# (...) OrthogonalVector/Exceptions

O próximo teste verifica a última exceção: o soldado pode dar quantos passos quiser, numa mesma direção. Para a implementação, resolvemos utilizar um valor negativo, que significa “não há número máximo de passos para essa peça”:

# StrategoTest.py - (...) class MovementTest
 
    def testSoldierCanMoveMoreThanOneStep(self):        assert Piece("soldier").canMove(42)# Stratego.py - (...) class Piece
 
    __pieces = {"flag": PieceProperties(0, 0),
                "spy": PieceProperties(1),
                "soldier": PieceProperties(2,-1),                "miner": PieceProperties(3), # (...)
 
    def canMove(self, steps):
        return steps <= self.maxSteps or self.maxSteps == -1 
# (...) OrthogonalVector/Exceptions

O último teste verifica o cenário de erro numa tentativa de andar passos negativos. A implementação é simples: criamos uma nova exceção e verificamos o erro no método canMove():

# StrategoTest.py - (...) class MovementTest
 
    def testMoveNegativeStepsRaises(self):        try:            Piece("lieutenant").canMove(-1)        except IllegalStep:            pass        else:            self.fail("expected IllegalStep exception") 
# Stratego.py - (...) class Piece
 
    def canMove(self, steps):
        if steps <= 0:            raise IllegalStep        return steps <= self.maxSteps or self.maxSteps == -1
 
# (...) OrthogonalVector/Exceptions
 
class IllegalStep(Exception):    pass

Conclusões e próximos passos

Terminamos o terceiro post com 123 linhas de código de teste e 63 linhas de código de produção. Uma verificação de cobertura de código nos mostra que estamos 100% cobertos. Esse é um dos “efeitos colaterais” de TDD: além de ganhar confiança no código que está escrevendo, você ainda termina com uma bateria de testes que cobre 100% do seu código. No próximo post iremos evoluir nossos vetores para implementar as regras de movimentação no tabuleiro. A seguir, um resumo do que aprendemos nesse post e o código final:
 

  • Conhecemos a importância dos nomes que damos aos testes
  • Aprendemos a verificar o que está errado quando estamos no vermelho
  • Conversamos um pouco sobre dependências e divisão de responsabilidades entre objetos
  • Discutimos outras práticas de XP, como Trabalho Energizado e Programação Pareada
  • Implementamos uma classe auxiliar para representar vetores e facilitar nossa implementação das regras de movimentação

Atualização 03-Out-06: Conforme sugestões, estou disponibilizando para download o código fonte final dos testes e das classes de produção para os interessados não precisarem copiar/colar/formatar tudo novamente.

# StrategoTest.py
 
import unittest
import unittestgui
import Stratego
from Stratego import InvalidPiece, InvalidAttack, InvalidVector, IllegalStep
from Stratego import OrthogonalVector
 
class Piece(Stratego.Piece):
    def winsAgainst(self, defendingPiece):
        return self.attack(defendingPiece) > 0
    def losesAgainst(self, defendingPiece):
        return self.attack(defendingPiece) < 0
    def tiesWith(self, defendingPiece):
        return self.attack(defendingPiece) == 0
 
class CreatePieceTest(unittest.TestCase):
    def testCreateSoldierWithRank2(self):
        assert Piece("soldier").rank == 2
    def testCreateMinerWithRank3(self):
        assert Piece("miner").rank == 3
    def testCreateSergeantWithRank4(self):
        assert Piece("sergeant").rank == 4
    def testCreateLieutenantWithRank5(self):
        assert Piece("lieutenant").rank == 5
    def testCreateCaptainWithRank6(self):
        assert Piece("captain").rank == 6
    def testCreateMajorWithRank7(self):
        assert Piece("major").rank == 7
    def testCreateColonelWithRank8(self):
        assert Piece("colonel").rank == 8
    def testCreateGeneralWithRank9(self):
        assert Piece("general").rank == 9
    def testCreateMarshalWithRank10(self):
        assert Piece("marshal").rank == 10
    def testCreateSpyWithRank1(self):
        assert Piece("spy").rank == 1
    def testCreateBombWithRank11(self):
        assert Piece("bomb").rank == 11
    def testCreateFlagWithRank0(self):
        assert Piece("flag").rank == 0
    def testCreateInvalidPieceRaises(self):
        try:
            Piece("invalid")
        except InvalidPiece:
            pass
        else:
            self.fail("expected InvalidPiece exception")
 
class AttackTest(unittest.TestCase):
    def testHigherRankWins(self):
        assert Piece("sergeant").winsAgainst(Piece("soldier"))
    def testLowerRankLoses(self):
        assert Piece("miner").losesAgainst(Piece("colonel"))
    def testEqualRankTies(self):
        assert Piece("major").tiesWith(Piece("major"))
    def testSpyWinsAgainstMarshal(self):
        assert Piece("spy").winsAgainst(Piece("marshal"))
    def testMarshalWinsAgainstSpy(self):
        assert Piece("marshal").winsAgainst(Piece("spy"))
    def testMinerWinsAgainstBomb(self):
        assert Piece("miner").winsAgainst(Piece("bomb"))
    def testBombCannotAtack(self):
        try:
            Piece("bomb").attack(Piece("miner"))
        except InvalidAttack:
            pass
        else:
            self.fail("expected InvalidAttack exception")
    def testFlagCannotAtack(self):
        try:
            Piece("flag").attack(Piece("lieutenant"))
        except InvalidAttack:
            pass
        else:
            self.fail("expected InvalidAttack exception")
 
class OrthogonalVectorTest(unittest.TestCase):
    def testCreateHorizontalVector(self):
        assert OrthogonalVector(42, 0).y == 0
    def testCreateVerticalVector(self):
        assert OrthogonalVector(0, 42).x == 0
    def testNonOrthogonalVectorRaises(self):
        try:
            OrthogonalVector(1, 2)
        except InvalidVector:
            pass
        else:
            self.fail("expected InvalidVector exception")
    def testCalculatePositiveMagnitude(self):
        assert OrthogonalVector(3, 0).magnitude() == 3
    def testCalculateNegativeMagnitude(self):
        assert OrthogonalVector(0, -42).magnitude() == 42
 
class MovementTest(unittest.TestCase):
    def testSergeantCanMoveOneStep(self):
        assert Piece("sergeant").canMove(1)
    def testBombCannotMove(self):
        assert not Piece("bomb").canMove(1)
    def testFlagCannotMove(self):
        assert not Piece("flag").canMove(1)
    def testMajorCannotMoveMoreThanOneStep(self):
        assert not Piece("major").canMove(2)
    def testSoldierCanMoveMoreThanOneStep(self):
        assert Piece("soldier").canMove(42)
    def testMoveNegativeStepsRaises(self):
        try:
            Piece("lieutenant").canMove(-1)
        except IllegalStep:
            pass
        else:
            self.fail("expected IllegalStep exception")
 
suite = unittest.TestSuite((unittest.makeSuite(CreatePieceTest),
                            unittest.makeSuite(AttackTest),
                            unittest.makeSuite(OrthogonalVectorTest),
                            unittest.makeSuite(MovementTest)
                           ))
 
if __name__ == "__main__":
    unittestgui.main("StrategoTest.suite")
 
# Stratego.py
 
class PieceProperties:
    def __init__(self, rank, maxSteps=1):
        self.rank = rank
        self.maxSteps = maxSteps
 
class Piece:
    __pieces = {"flag": PieceProperties(0, 0),
                "spy": PieceProperties(1),
                "soldier": PieceProperties(2,-1),
                "miner": PieceProperties(3),
                "sergeant": PieceProperties(4),
                "lieutenant": PieceProperties(5),
                "captain": PieceProperties(6),
                "major": PieceProperties(7),
                "colonel": PieceProperties(8),
                "general": PieceProperties(9),
                "marshal": PieceProperties(10),
                "bomb": PieceProperties(11, 0)}
    __winsAttacking = [(1,10), (3,11)]
    __cannotAttack = [0,11]
 
    def __init__(self, name):
        try:
            self.rank = self.__pieces[name].rank
            self.maxSteps = self.__pieces[name].maxSteps
        except KeyError:
            raise InvalidPiece()
 
    def attack(self, defender):
        if self.rank in self.__cannotAttack:
            raise InvalidAttack()
        if (self.rank,defender.rank) in self.__winsAttacking:
            return 1
        else:
            return self.rank - defender.rank
 
    def canMove(self, steps):
        if steps <= 0:
            raise IllegalStep
        return steps <= self.maxSteps or self.maxSteps == -1
 
class OrthogonalVector:
    def __init__(self, x, y):
        if 0 not in [x, y]:
            raise InvalidVector
        self.x = x
        self.y = y
    def magnitude(self):
        return abs(self.x + self.y)
 
class InvalidPiece(Exception):
    pass
 
class InvalidAttack(Exception):
    pass
 
class InvalidVector(Exception):
    pass
 
class IllegalStep(Exception):
    pass

Post to Twitter

September 21st, 2006Palestra sobre TDD

Um pouco em cima da hora, mas apresentarei um workshop sobre Desenvolvimento Dirigido por Testes no próximo sábado.



Enquanto os novos posts da série “TDD em Combate” ainda não saem (eu sei, já faz tempo que estou devendo, mas ele ainda não morreu. Já temos mais material para o próximo post, só falta escrever :-)), apresentarei junto com o RBP um workshop sobre Desenvolvimento Dirigido por Testes. O evento acontecerá no SENAC Santo Amaro e será uma demonstração ao vivo dessa técnica no desenvolvimento de um pequeno jogo.

Mais informações no portal da AgilCoop e no site do evento.

Post to Twitter

July 27th, 2006Agile 2006 – Dia 5

No último dia da conferência também estava cheio de trabalho agendado na parte da manhã e na parte da tarde.
As duas últimas sessões que participei foram também muito interessantes: refatoração de banco de dados e técnicas
eficientes para trabalhar com código legado. Para finalizar, uma última palestra no banquete servido a noite.

Refactoring Databases: Evolutionary Database Design – Scott Ambler e Pramod Sadalage

Essa palestra foi muito interessante. Ambos os apresentadores acabaram de lançar um
livro
sobre o assunto e falaram sobre como as pessoas do mundo dos bancos de dados estão atrasadas
em relação ao mundo “ágil”. Poucas ferramentas
existem para teste em banco de dados e, conforme os autores
comentaram, os bancos de dados relacionais já estão no mercado há muito tempo. Eles lançaram uma pergunta desafiadora:
“Você acha possível fazer uma mudança trivial no seu banco de dados atual, como mudar o nome de uma coluna, e implantar
essa mudança em produção no dia seguinte?”. Muitos dos presentes achavam muito difícil fazer essa simples mudança nas
empresas onde trabalham. Scott Ambler está
lançando uma série de pesquisas no site do Dr. Dobb's e uma delas é sobre
banco de dados ágeis. Os resultados serão publicados em Novembro e os dados estarão disponíveis no
site dele, porém ele disse que, apesar da maioria das pessoas afirmarem que
as informações armazenadas no banco de dados são consideradas ativos da empresa, poucos têm uma estratégia
para recuperação e manutenção dos dados em produção.

Durante o tutorial, Pramod mostrou exemplos de como trazer a evolução do
banco de dados para seu sistema de controle de versão (CVS, SVN, …) e como executar algumas das
refatorações propostas, integrando tudo com Ant e
Cruise Control.

Devemos dar a devida atenção para a integração entre as 2 comunidades (banco de dados e a comunidade ágil),
pois essa será uma área que receberá grande atenção nos próximos anos. Acredito que através da colaboração conjunta,
existe um grande potencial para o surgimento de novas ferramentas e processos que devem mudar a perspectiva
das empresas em relação ao assunto. E isso não deve acontecer num futuro tão distante. Para finalizar, uma foto
com os apresentadores:

Scott Ambler and Pramod Sadalage

Working Effectively with Legacy Code – Michael Feathers

Na parte da tarde eu fui auxiliar a sessão do Michael Feathers sobre o livro homônimo
“Working Effectively with Legacy Code”.
O tutorial começou com a sua definição de código legado: “Todo código que não tem teste”. Muito boa essa definição.
O resto da palestra foi sobre técnicas para conseguir colocar pedaços de código existentes numa suite de testes. Grande
parte do desafio nessa tarefa é conseguir instanciar objetos numa suite de teste, evitando operações
demoradas ou efeitos colaterais indesejados num teste de unidade. Alguns dos critérios que Michael propôs para saber quando
um teste de unidade não é eficiente são:

  • O teste fala com o Banco de Dados
  • O teste faz algum tipo de comunicação na rede
  • O teste fala com o Sistema de Arquivos
  • O teste não pode ser executado ao mesmo tempo que outros testes (dependência entre testes)
  • Você precisa fazer alterações especiais no ambiente para poder executá-los

De modo geral, ele não quis dizer que você não deve testar coisas como acesso ao Banco de Dados ou Sistema de Arquivos,
mas sim que essas coisas fazem seu teste de unidade demorar para rodar. Testes de unidade devem rodar rápido. Ou melhor:
você considera o Banco de Dados parte da “unidade” que está testando?



A grande fonte de dificuldade quando você precisa lidar com código legado é que ele está muito acoplado ou dependente
de outras partes do código, tornando difícil instanciá-lo independentemente do resto no seu teste.
Michael mostrou
diversas técnicas para quebrar tais dependências. Refatorar o código para melhorar o design e diminuir as
dependências é algo inseguro, por isso a refatoração depende tanto da suite de testes: ela é sua rede de segurança
para evitar que as coisas quebrem. Porém, ao lidar com código legado, é difícil saber se você quebrou alguma outra
coisa, pois os testes não estão lá para te ajudar. Por isso as técnicas mostradas no tutorial são muito cuidadosas,
compostas de pequenos passos seguros onde as chances de quebrar são muito pequenas. Pode ser que seu código fique
mais feio para conseguir escrever o teste. Porém, uma vez que seu código está “cercado” por testes, você tem
segurança para começar a mudá-lo e melhorá-lo, levando-o em direção a um design mais robusto.

Engineering Outside the Box – Greg Harbaugh

O dia e a conferência terminaram com um banquete na parte da noite, com muita comida, bebida,
cowbells e colares brilhantes:

Student Volunteers at the Banquet Table

Durante o banquete, Greg Harbaugh, um ex-astronauta, deu uma palestra inspiradora sobre a forma com a qual
encaramos nossos problemas. Após a palestra, foram apresentados os ganhadores do prêmio
Gordon Pask desse ano, um prêmio entregue
para lembrar e recompensar as pessoas que contribuíram para o movimento ágil durante o último ano.

Essa foi a primeira vez que fui a uma conferência e foi uma experiência incrível. Durante esses 6 dias eu aprendi
muita coisa, conheci muita gente (não é todo dia que você almoça do lado do Martin Fowler)
e fiquei realmente inspirado com as idéias e valores do movimento ágil. Mais do que assistir palestras dos
autores que admiro, eu consegui apreciar por completo o valor da existência de uma comunidade que suporta e
promove os mesmos valores. São pessoas que realmente gostam e acreditam naquilo que fazem e trabalham para
elevar o estado da arte da nossa profissão. Termino com uma frase dita por um dos ganhadores do prêmio desse ano,
Laurent Bossavit:

“Devemos continuar trabalhando para fazer da nossa profissão algo tão nobre quanto a profissão do bombeiro ou do astronauta”

Post to Twitter

July 26th, 2006Agile 2006 – Dia 4

Penúltimo dia de conferência. Minha agenda de voluntário está cheia nesses dois últimos dias e,
para minha sorte, consegui alocar o trabalho nas sessões mais interessantes. Hoje assisti (em ambos os
sentidos) dois autores famosos: Mary Poppendieck e Kent Beck.

From Concept to Cash: Managing the Pipeline – Mary Poppendieck

Mary é uma pessoa incrível e uma ótima apresentadora. Ela está trabalhando num novo livro que será continuação
do famoso “Lean Software Development”,
chamado “Implementing Lean Software Development: From Concept to Cash”
e que será lançado em Setembro desse ano. Na palestra ela falou sobre alguns dos princípios do pensamento
“Lean” aplicados no contexto do desenvolvimento de software. A idéia tem suas origens no Sistema de Produção da
Toyota para manufatura de carros “just-in-time”. Mais tarde essas idéias foram reutilizadas no contexto do desenvolvimento
de produtos (e onde ela tem muita experiência trabalhando para a 3M).

Sabemos das limitações da antiga metáfora que
compara o desenvolvimento de software à manufatura ou à engenharia civil. Se existe algo de parecido entre esses
dois mundos, devemos comparar o desenvolvimento de software com o desenvolvimento de produtos: uma área onde o sucesso
depende de um ambiente criativo e adaptativo.

A palestra foi muito informativa e esclarecedora. O timing também foi excelente. Dentre os principais tópicos que
ela cobriu, cito alguns:

  • Princípios “Lean”: aplicados no contexto do desenvolvimento de software.
  • Liderança: a importância de enxergar o todo, evitar a micro-otimização e o papel da liderança nas soluções mais bem sucedidas.
  • Desperdício: eliminar desperdícios é o principal foco do pensamento “Lean”. Ela falou sobre as principais fontes de desperdício em projetos de software.
  • Indivíduos e Times: a importância do trabalho em equipe.
  • Conhecimento: o momento certo de tomar uma decisão. Atrase decisões irreversíveis para o último momento.

Aprendi muita coisa nessa sessão e estou ansioso para terminar de ler seu primeiro livro, que comecei a ler na semana
passada no aeroporto. Aqui vai uma foto que tirei ao final da sessão (depois de recolher os milhares de formulários
de avaliação da palestra):

Mary and Tom Poppendieck

XP Geography: A Guide to Mapping Your First Steps – Kent Beck e Cynthia Andres

Na parte da tarde foi a vez da palestra do Kent Beck e sua esposa Cynthia Andres! A palestra durava o dia inteiro,
então o Sergei cobriu a parte da manhã e eu fiquei com a parte da tarde. Como peguei
as coisas no meio do caminho, acabei perdendo uma parte da palestra. Porém, pelo material que estava espalhado na
sala e pelos desenhos no flip chart, eu pude perceber que ele havia falado sobre os valores, princípios e
práticas de XP, explorando algumas das práticas primárias através de Mind Maps.

Na parte da tarde, eles falaram sobre uma técnica chamada “Appreciative Inquiry”,
onde um time lembra de fatos positivos sobre o passado e tenta aplicá-los em situações no presente. Os participantes
trabalharam em pares: enquanto um contava sua história o outro capturava os pontos positivos. Depois de um tempo os
papéis se invertiam. Ao final, cada mesa trabalhava em conjunto para compartilhar as experiências e lições aprendidas.

No final da palestra ele falou sobre a importância do comprometimento e da responsabilidade para uma mudança
sustentável. Apesar do lado um pouco filosófico, eu achei muito interessante a forma com que o tutorial foi
conduzido, pois fez os participantes realmente pensarem sobre os valores por trás das práticas de XP e, mais ainda,
na forma em que esses valores podem ser aplicados durante a implantação sustentável de XP na sua organização.

Kent Beck and Cynthia Andres

Após as palestras eu estava realmente cansado e decidi não participar da
festa promovida pela Google e voltei para
o albergue para descansar. Amanhã será mais um dia de agenda cheia, então quero estar com as energias recarregadas
para aproveitar o último dia da conferência.

Post to Twitter

July 25th, 2006Agile 2006 – Dia 3

Resumo do terceiro dia de conferência: uma sessão pela manhã, quatro na parte da tarde e eu descobri que estou ficando míope :-)

Storytelling with FIT – Steve Freeman e Mike Hill

Depois da sessão de ontem
com o Ward Cunningham sobre a importância dos testes para a evolução de um sistema, decidi assistir uma palestra
sobre o framework que ele inventou para testes de aceitação: FIT. O ponto principal
da palestra foi mostrar a importância dos testes de aceitação como meio de comunicar requisitos. Eu já ouvi muitas
conversas por aqui sobre a utilização da palavra “teste”, pois muitos podem achar que o único objetivo de um teste
é encontrar erros (e sabemos que, apesar de eficientes, testes não provam a ausência de bugs). Ao invés de “teste”,
os apresentadores tentaram usar o termo “documento”, pois os testes de aceitação no FIT são páginas HTML contendo
texto e tabelas com exemplos que especificam o comportamento esperado do sistema. O mais interessante é que o
documento é algo executável. Com isso você tem como especificar os critérios de aceitação de uma história antes de
ter a implementação pronta (a idéia de escrever testes antes do código aplicada num contexto mais amplo).
Ao implementar a funcionalidade esperada, você utiliza ferramentas como os testes de unidade e
TDD para garantir
a qualidade do código. Porém o desenvolvedor é também responsável por fazer o teste de aceitação passar. Para isso,
você precisa escrever um Fixture, que na verdade é uma classe
que será chamada pelo framework e te dará acesso aos dados escritos no documento FIT. A partir daí, você faz as
chamadas necessárias ao código da sua aplicação e avalia os resultados de acordo com as expectativas descritas no
documento FIT. Abaixo são duas tabelas que fizemos para especificar uma parte de um sistema de venda online de
serviços de internet:

Storytelling with FIT

Agile Quality: A Canary in a Coal Mine – Ken Schwaber

A sessão que escolhi participar depois do almoço foi do outro inventor do Scrum:
Ken Schwaber. Ele falou sobre a importância da qualidade como
atributo essencial num sistema de software: devemos nos preocupar com a qualidade desde o início. O custo para
colocar a qualidade depois é muito alto, podendo até levar um projeto ao fracasso no longo prazo devido à perda
de vantagem competitiva. Quando mais tempo você gasta lidando com defeitos no seu sistema, menos tempo sobre para
pensar no valor que ele está trazendo para o negócio. E o principal objetivo de qualquer sistema de software
deve ser agregar valor ao negócio. Na sua palestra, Ken tentou mostrar a importância da qualidade para um projeto de software e defendeu a idéia de que cortar qualidade deve ser uma decisão de negócio.

Agile Stories – Research Papers

Na segunda metade da tarde, resolvi assistir à apresentação de 3 papers, numa trilha chamada “Agile Stories”.
Falarei um pouco sobre cada um dos papers nos tópicos abaixo:

The Deployment Production Line – Jez Humble, Chris Read e Dan North

Esse artigo foi bem interessante: eles mostraram uma forma efetiva de estruturar e automatizar a implantação da sua
aplicação em diferentes ambientes (teste, integração, aceitação, …). O artigo explica de forma mais detalhada a idéia
da linha de produção que eles desenvolveram num projeto da ThoughtWorks. O interessante
é que eles automatizaram o processo de tal forma que qualquer pessoa podia fazer o deploy de qualquer versão do sistema
em qualquer ambiente com poucos cliques. É interessante ressaltar também o modo em que eles quebraram as dependências
para acelerar a execução dos testes nos diferentes ambientes, paralelizando em várias instâncias separadas do
Cruise Control a execução dos testes de unidade, testes de aceitação nos
cenários de sucesso (smoke tests, que rodam mais rápido), testes de aceitação nos cenários mais complicados (demoram
mais), testes de performance, etc. Com isso, um determinado build vai ganhando “medalhas” conforme passa em
cada etapa de teste, ficando fácil de saber quando ele pode ou não ser implantado em ambiente de produção.

The Cost of Code Quality – Yuri Kharmov

Essa apresentação foi, por enquanto, a pior da conferência. Foi o oposto da palestra do Ken Schwaber
que eu tinha acabado de assistir. O autor defendeu a idéia de que nem sempre precisamos
nos preocupar com a qualidade quando escrevemos código. O principal argumento dele é: toda preocupação com
qualidade tem um custo associado e você deve considerá-lo na hora de usar técnicas como TDD ou Integração Contínua,
em outras palavras, não é um grande problema ter um sistema cheio de bugs se eles não te atrapalham.
Duas coisas me incomodaram: em primeiro lugar ele transpareceu não entender a verdadeira essência de
TDD como uma
ferramenta que possibilita o Design Incremental.
Segundo, quando perguntaram para ele ao final da palestra quantas vezes ele discutia sobre qualidade de código com
o cliente, ele respondeu “Quase nunca”. Como ele pode julgar pelo cliente o valor da ausência ou não de bugs se ele
nunca discute sobre isso?

Appropriate Agile Measurement: Using Metrics and Diagnostics to Deliver Business Value – Deborah Hartmann e Robin Dymond

A última palestra do dia foi sobre métricas ágeis. Eu achei bem interessante a distinção entre “Diagnósticos” e “Métricas”
proposta pelos autores:

  • “Diagnósticos”: são números que te ajudam a entender o andamento das coisas em relação ao processo. Por exemplo: medir a velocidade do time; interpretar o gráfico de “Burn-Down” para descobrir se o time irá terminar no prazo; ou saber a quantidade de defeitos. “Diagnósticos” dependem do contexto do time e devem ser temporários, ou seja, descarte-os assim que o processo estiver funcionando.
  • “Métricas”: são números que medem o valor de negócio que seu sistema está produzindo. Por exemplo: retorno de investimento; lucro; ou presença de mercado. “Métricas” são mais difíceis de medir, por isso é bom escolher apenas uma.

É preciso tomar cuidado com as métricas que você escolhe para avaliar o desempenho de uma equipe ágil, afinal
“You get what you measure”. Outra coisa que eles mostraram foi um template para ser utilizado na hora de
criar uma métrica ágil, com pontos que te fazem pensar sobre o verdadeiro valor que aquela métrica irá representar
quando estiver medindo o sucesso do seu software.

Post to Twitter


© 2007-2009 Danilo Sato | Powered by Wordpress

Page optimized by WP Minify WordPress Plugin