Patterns de Adoção Ágil

August 3rd, 2008

Uma pergunta frequente é: Como eu introduzo Agilidade na minha empresa?

Recentemente eu fiz uma revisão do Agile Adoption Patterns. Este livro é fantástico para você conhecer diversas técnicas e seus impactos. Mais importante ainda: sem ficar preso à uma metodologia de caixinha.

Uh-Éme-Éle

July 25th, 2008

Este tópico no GUJ chamou atenção, especialmente porque o li logo após um interessante texto noblog do Marcelo Araújo sobre uma outra faceta do mesmo tema: modelagem usando especificações não executáveis.

O que eu quero dizer com isso? Dada a tecnologia atual na maioria das empresas (desconsiderando o uso de coisas como DSLs ou mesmo alguma solução MDSD que, ao contrario de MDA, funcione) todos os documentos comumente utilizados para “modelagem” não são verificáveis, não são executáveis e requerem um trabalho manual enorme. Como bem disse o Emerson Macedo no post, você acaba programando duas vezes, uma na sua notação gráfica (UML) e uma na sua notação executável (Java, C#, Ruby… o que for).

Acontece que ao passar do diagrama (e diagramas aceitam qualquer besteira) para o código (onde o compilador e testes unitários são muito exigentes – fora os usuários) o tal do pseudo-modelo criado pelo “analista” no Rational Rose (porque a empresa não percebeu que o Rose foi descontinuado há anos) é completamente diferente do modelo implementado. As classes até têm o mesmo nome mas a mecânica interna é bem diferente. E por quê? Porque UML não vai te oferecer tudo o necessário para modelar. O mínimo que se espera de um modelo de um sistema é que ele seja verificável para saber se cumpre seus requisitos. Como é que eu vou saber isso com UML? Como eu testo UML?

Há algum tempo que eu me pergunto porque que se usa UML para “modelar”. Não me entenda mal, UML é uma ótima noção gráfica para Orientação a Objetos e com ela você consegue passar uma big picture muitas vezes mais rapidamente do que código; mas ela fica por aí: comunicação.

Quando modelamos o comportamento de objetos nós estamos descrevendo como este se comporta em diversas situações. Ao modelar uma determinada atividade você precisa descrever um conjunto grande de detalhes sobre esta, e UML não esta pronta para este tipo de coisa - e nem é a idéia por trás dela. Se modelar em UML fosse eficiente nós estaríamos programando em UML não em C#, Java ou o que for.

O primeiro problema ao tentar introduzir este pensamento na indústria é: mas eu não posso deixar que meus “implementadores” façam o que quiserem. Pergunta: o que raios é um implementador? Um datilografo de luxo?

Supondo que sua empresa seja a típica empresa de três letrinhas, aquelas que contratam qualquer um como “programador júnior” porque ele vai receber instruções do analista. Neste cenário eu pergunto: para que o tal programador? Que tal dar ao seu “analista” uma ferramenta mágica em que ele consiga criar um modelo abstrato e ao mesmo tempo executável? Que tal se ao invés de gastar rios de dinheiro pagando 10-reais-mais-o-busão para seus implementadores você eliminasse completamente a necessidade deles, fazendo com que o “analista” sozinho cuide do serviço?

Não precisa sacar seus milhões do banco nem pensar em como justificar o gasto com esta ferramenta no orçamento, você muito provavelmente já possui o necessário dentro da sua empresa: uma linguagem de programação decente. Faça seu “analista” usar código para expressar a modelagem. No final dá no mesmo para o nível de modelagem desejada e você ainda ganha algo verificável e executável. Economia de rios de dinheiro quase que instantânea.

Mas como assim? O “modelo lógico” é muito diferente do “modelo físico” e o “analista” não pode perder tempo com essas coisinhas de tecnologia, tem que pensar em modelar o negocio. Muito bem, duas coisas:

  1. Analista de Sistemas não é analista de negócios. Se você não sabe o que é um analista de negócios eu recomendo que você mande um e-mail pro Paulo Vasconcellos perguntando.
  2. Há mais de cinco anos que não há quase nenhum motivo para que uma aplicação Java ou C# não seja uma cópia de 1-para-1 de um modelo UML do tipo “diagrama de domínio”. POJO/POCO, Hibernate, IoC, Camadas, OO, AOP, EJB3, MVC… toda essa parafernália de letrinhas permite que seus objetos de negocio não tenham o menor traço de infra-estrutura. Claro que alguém vai ter que fazer a infra-estrutura –ainda que seja mínima. Caso seus “analistas” sejam de qualidade extremamente baixa eu recomendo que você contrate um bom técnico para atuar como arquiteto e líder técnico do time.

Verdade seja dita: esta não é a primeira vez que este blog trata do tema e desde a última vez muita coisa melhorou, mas ainda há muito que melhorar.

Rastreabilidade em User Stories

July 18th, 2008

Pergunta na XP-Rio:

Me ajudem no seguinte, no processo unificado, espera-se que todos os casos de uso gerem algum tipo de artefato dentro do projeto. Na verdade, com os casos de uso espera-se chegar em classes que juntas formarão o sistema. Dentro da metodologia existe artefatos que quando feitos, permitem mapear todos os artefatos gerados a partir de um caso de uso. A matriz de rastreabilidade.

Gostaria de saber como o XP permite chegar as classes (código) a partir das User Stories. Por exemplo, dado uma User Stories, quais são as classes geradas para atender essa User Stories?

O XP permite isso? Como é feito?

Provavelmente a primeira coisa a se perguntar é: por que eu precisaria deste dados? Alguns modelos de processo como CMMI definem rastreabilidade, que geralmente é definida nesta linha:

Requirements traceability refers to the ability to describe and follow the life of a requirement, in both forwards and backwards direction (i.e. from its origins, through its development and specification, to its subsequent deployment and use, and through all periods of on-going refinement and iteration in any of these phases.)

- Gotel O.C.Z and Finklestein A.C.W (tirado da Wikipedia)

A necessidade parece justa e real, é preciso saber quais partes do software são afetadas por mudanças no negocio e vice-versa. O grande problema desta métrica é que transformações de modelo não são mais tão drásticas há algumas décadas. Ao utilizar qualquer tecnologia relativamente comum (Java, Ruby, .Net, Python, JavaScript…) não existe motivo técnico para que o software não modele o negocio em uma relação próxima de 1-para-1, desta forma saber qual parte de um código realiza qual tarefa torna-se bem simples.

Outro problema é que eu nunca vi um artefato de rastreabilidade, seja qual for, que não ficasse defasado. Se você abraça uma metodologia ágil sabe que refactoring faz parte da vida.

Mesmo com todos os problemas que esta métrica possui eu entendo que existem casos onde seu uso se faz necessário –especialmente por equipes tentando introduzir agilidade em ambientes tradicionais. Supondo que exista uma necessidade justificável para algum controle deste tipo, metodologias ágeis em geral possuem um mecanismo muito mais eficiente que documentos ou mesmos sistemas: testes.

Toda história, todo cartão, deve ter um critério de aceite. O critério de aceite traz o que o cliente considera ser o mínimo para que a história seja considerada concluída. Por exemplo:

História: Como administrador eu quero criar um novo grupo para que possa aplicar permissões à diversos usuários de uma só vez.
Critérios de Aceite:

  • O novo grupo criado deve possuir o usuário que o criou como seu administrador
  • O novo grupo não deve conter outros membros
  • O novo grupo não deve ser visível na listagem de grupos

Cada um destes critérios via um teste unitário e/ou funcional que é executado como parte da integração contínua. Para saber exatamente quais classes e códigos são afetados por cada um deles é simples: use uma ferramenta de test coverage como o Emma e execute apenas os testes de aceitação para aquele cartão. A ferramenta irá gerar relatórios como estes:

[class, %]	[method, %]	[block, %]	[line, %]	[name]
25%  (1/4)!	25%  (3/12)!	40%  (3012/7446)!	25%  (3/12)!	com.sun.tools.javac.v8.resources
94%  (16/17)!	49%  (41/83)!	48%  (1111/2292)!	45%  (201.1/450)!	com.sun.tools.javac.v8
88%  (45/51)!	61%  (242/397)!	54%  (3070/5729)!	52%  (809.6/1563)!	com.sun.tools.javac.v8.tree

Classes que possuem alguma cobertura foram afetadas pelo teste, logo são parte da execução daquela história. Não é difícil escrever um script que –talvez como parte do build- processe estes relatórios e gere o artefato de rastreabilidade que seu processo te obrigar a fazer. Melhor ainda: este artefato nunca estará desatualizado.

Só não se esqueça que melhoria contínua é um conceito importante. Se o artefato de rastreabilidade é algo tão ineficiente a obrigação de um desenvolvedor que perceba isto é mostrar para sua organização que o valor desta pratica é nulo. Não se acomode.

User Stories: Não Crie Godzillas!

July 10th, 2008

Hoje de manhã eu acabei iniciando um debate interessante com o Antônio Carlos via twitter. Tudo começou quando o Fernando Meyer postou uma foto dos cartões que ele usa para planning poker. O meu primeiro comentário foi sobre as cartas com valores exorbitantes como 40 e 100.

Qual o problema destes valores?

Eles são um sintoma de um problema. São um bad smell. Dado que toda história tem algum valor para o usuário do sistema, como alguém pode ter no mesmo sistema, na mesma iteração, duas histórias que valem 2 e 100? Existem tarefas que realmente envolvem tão pouco esforço que o numero é baixo. Mudar um texto para itálico, por exemplo, mas se isso é um dois o que seria tão grande a ponto de ser (40/2 =) 20 vezes mais trabalhoso e ainda assim ser considerado uma história?

Possibilidades:

  1. Histórias mal formuladas: O analista de negocio cria histórias que não possuem valor nenhum e/ou histórias que não são atômicas.
  2. Incerteza: Os desenvolvedores fazendo a estimativa de tamanho da história não tem idéia de como fazer aquilo e chutam o tamanho para o alto, o mais alto que conseguirem.

O primeiro problema é muito comum –tem um post nos rascunhos sobre minhas experiências recentes com este. Analistas –especialmente os com experiência em desenvolvimento- tendem a pensar em tarefas que são decomposições funcionais e não partições verticais dos sistemas. Invés de pensas no ponta-a-ponta da tarefa eles criam histórias apenas sobre um pedacinho específico. No outro lado da moeda o analista não consegue particionar o sistema em histórias. No final os cartões trazem histórias gigantescas.

É fácil estimar histórias pequenas. Na verdade o princípio que move as estimativas de métodos ágeis é que é muito mais fácil estimar alo pequeno do que algo grande. O problema é que estimar o tamanho uma história grande é muito difícil.

A solução neste caso provavelmente passa por ter histórias melhor definidas. É claro que vão existir histórias que são tão simples que até um 2 é muito para elas, nesse caso use algo menor como um numero abaixo da sua menor história “normal” (quase sempre isso quer dizer 1).

A segunda causa é bem simples de resolver, creio. A incerteza é algo comum, especialmente antes de executar algumas histórias para saber onde se pisa. Neste caso ao invés de se chutar a estimativa para a Lua com um 100 você pode começar usando uma carta que deixa claro que não se consegue estimar este cartão com as informações atuais:

E prosseguir criando um spike com objetivo de obt6er mais dados sobre aquela tarefa. Estimar um spike é bem mais simples do que uma história.

Mas e para estimarmos em alto-nível estas histórias enormes, confusas e distantes? Algo assim não é história, é um épico. Neste caso estimativas de alto nível são aceitáveis enquanto o épico estiver no backlog mas antes de serem agendados os épicos são desmembrados em histórias.

Agile Software Deployment?

June 20th, 2008

A melhor coisa sobre meu trabalho atual é que, como consultores, tentamos sempre pensar fora da caixa. Isso não é fácil numa consultoria, você pode imaginar, já que o mercado tende à empresas de três letrinhas que não se interessam tanto em otimizar ou melhorar e sim em cobrar por hora.

Um desses momentos recentes me fez passar por algo que eu nunca havia visto na pratica: como fazer deployment (instalação) e administração de software de maneira ágil?

O cliente em questão possui times ágeis em diversos segmentos há alguns anos. Durante o andamento de um projeto enorme surgiram algumas dificuldades em gerenciar ambientes e versões do software. Apesar de toda a agilidade os testadores ainda precisam que versões específicas, com bases de dados específicas, estejam instaladas em ambientes para homologar o sistema. Para piorar mesmo os desenvolvedores precisam ter uma instância de uma search engine (algo como o Lucene) e popular esta engine para que seja usável demora por volta de 10 horas.

Era preciso controlar qual desenvolvedor possui acesso à qual instalação da search engine e quais as versões instaladas em quais ambientes. No passado já ocorreu algumas vezes de um deployment para QA demorar mais que o esperado pela confusão generalizada de ambientes e isso atrasar o release para produção em uma semana.

A primeira proposta que eles tiveram foi clássica: vamos automatizar tudo. Construir um mega-sistema que controle o que está instalado onde, avise por email os responsáveis, tenha um controle de workflow, se integre ao sistema de QA, seja parte do IBM Tivoli, faça café… e seja extremamente caro.

Uma outra proposta surgiu do “grupo de governança” da empresa: ninguém faz deployment de nada sem ser autorizado. Toda vez que alguém precisa subir algo para QA ou outro ambiente precisa usar o Jira, toda vez que alguém precisar de uma modificação numa instancia da search engine precisa usar o Jira. Todos os pedidos são aprovados pelas pessoas competentes.

Aí começa o trabalho da nossa equipe: como ter o benefício esperado sem ter que vender a empresa pra comprar o sistema ou passar 20% do tempo preenchendo formulários?

O início deste trabalho foi feito seis meses atrás e foi parte do meu primeiro projeto aqui. Apos analisarmos o sistemas vimos que ele era estupidamente complexo sem a menor necessidade. O débito técnico foi se acumulando com o passar dos anos e uma coisa simples como instalar um WAR no Tomcat estava levando mais de duas horas, e trabalhando em par! Um dos grandes problemas era que das duas horas uma você gastava andando pelo corredor perguntando para outras pessoas o que fazer no caso X, qual a versão que está no servidor Y, etc.

A primeira coisa a se fazer era resolver o débito técnico. Não há solução que agüente mais de um mês em pé com aquela quantidade de problemas para resolver (incluindo um build que durava mais de quarenta minutos). Para isso os donos do negocio aceitaram alocar 10% dos pontos de uma iteração e, conforme a previsão, a maioria dos problemas graves se solucionou em cinco meses.

Enquanto isso, para amenizar o problema de maneira imediata, nós resolvemos usar um quadro-branco com a configuração dos ambientes. Quando a pressão do release passou o quadro foi para no wiki interno, numa grande tabela que qualquer um editava. Para o problema das instâncias de search engine nós criamos tokens: cada instancia tinha um cartão. Se você precisa de uma instancia você vai até uma parede e pega um cartão, adiciona as configurações daquela instancia na sua máquina e cola o cartão no seu monitor.

Simples e eficiente, a solução do cartão dura até hoje. O mesmo não se pode dizer do wiki. Enquanto o grupo de usuários era pequeno o wiki serviu de maneira ótima, após passarmos de algumas dezenas de pessoas -e diversos sub-projetos e spikes rodando em paralelo- ele se tornou inviável. O problema é que a tabela não comporta mais todos os dados de maneira eficiente e qualquer tentativa de organizar em sub-páginas faz com que as pessoas “esqueçam” de atualizar o wiki. Solução? Seguir o exemplo do que deu certo: cartões!

Acima você pode ver uma das paredes de cartões para nosso controle de mudanças e versões. O cartão rosa, no topo, tem o nome do ambiente. Cada um dos cartões azuis abaixo deste representa um dos componentes, os post-it laranja indicam as versões que foram instaladas. Os amarelos indicam qual instancia da search engine é usada em qual ambiente.

Os outros post-its colados nos cartões são meta-dados, eles indicam, por exemplo, que um serviço está inativo:

Esta solução tem funcionado nos últimos meses de maneira exemplar. Na verdade ela funciona tão bem que o problema agora é convencer os analistas de negocio que o problema do deployment não está solucionado e que eles ainda precisam investir nele.

Uma das conseqüências dessa técnica é que as duas pessoas que ficavam alocadas 100% do tempo gerenciando ambientes estarão em breve voltando a desenvolver as histórias e gerar valor de negócios ao invés de dar suporte aos desenvolvedores.

Muitas vezes você não precisa de milhões de dólares nem de burocracia, basta pensar fora da caixa.

Ruby é JavaScript ao Avesso

June 12th, 2008

O titulo é uma brincadeira mas é uma boa forma de lembrar algumas coisinhas sobre programação nestas linguagens. Cada vez mais lidamos no dia-a-dia com conceitos que estão presentes há décadas em linguagens mais esotéricas mas nunca deram as caras no mainstream, um deles é o uso de funções como abstração. Existe um conflito de termos aqui então só para deixar claro eu não estou falando de funções como em programação procedural mas sim de funções como vemos em closures.

Muita gente tem escrito sobre como devemos aprender programação funcional. Eu concordo mas não posso deixar de notar que quando alguém diz programação funcional geralmente ela quer dizer Higher-Order Functions.

E o que é isso? Bom, uma linguagem possui higher-order quando uma função pode receber como parâmetro outra função. O nome deriva do fato de que uma função que recebe outra é considerada de ordem 1, uma função que recebe outra que recebe outra é considerado 3 e assim em diante.

JavaScript possui higher-order programming. Funções são a abstração principal em javaScript e elas podem ser passadas à vontade pelo programa. Por exemplo, vamos supor que queremos comparar dois objetos de acordo com um critério arbitrário. Em JavaScript podemos fazer algo assim:

function melhorEntre(um, outro, criterio){
  if(criterio(um, outro)){
    return um;
  }
  else {
    return outro;
  }
}

sorvete1 = {sabor: 'morango'};
sorvete2 = {sabor: 'chocolate'};

prefiroChocolate = (function (s1, s2){
                                   return (s1.sabor === 'chocolate');
                            });

prefiroMorango = (function (s1, s2){
                                  return (s1.sabor === 'morango');
                            });

alert(melhorEntre(sorvete1, sorvete2, prefiroMorango).sabor);
alert(melhorEntre(sorvete1, sorvete2, prefiroChocolate).sabor);

Em Ruby o código ficaria um pouco diferente:

def melhor_entre(um, outro, criterio)
    if criterio.call(um, outro)
        um
    else
        outro
    end
end

sorvete_1 = { :sabor => 'chocolate' }
sorvete_2 = { :sabor => 'morango' }

prefiro_chocolate = lambda {|s1,s2| s1[:sabor] == 'chocolate'}
prefiro_morango = lambda {|s1,s2| s1[:sabor] == 'morango'}

puts melhor_entre(sorvete_1, sorvete_2, prefiro_morango)[:sabor]
puts melhor_entre(sorvete_1, sorvete_2, prefiro_chocolate)[:sabor]

Agora vamos pensar: as duas linguagens possuem higher-order programming? Não, só JavaScript possui. Em Ruby o que é passado não é uma função e sim um objeto, veja só:

prefiro_chocolate = lambda {|s1,s2| s1[:sabor] == 'chocolate'}
puts prefiro_chocolate
#=> #<Proc:0x00028a64@tmp/compara.rb:19>
puts prefiro_chocolate.class
#=> Proc

O principal divergente da solução em Ruby é que você deve passar a mensagem call para o objeto (ou usar a palavra-chave yield). Na pratica do dia-a-dia não tem tanta diferença e é comum falar em higher-order Ruby.

Em Ruby não precisamos de funções de verdade para termos higher-order programming, podemos usar objetos para modelar as funções. Em JavaScript não temos construções especiais para objetos, mas utilizamos funções:

function Sorveteiro(){
  this.numeroDeVendidos = 0;
  this.vender = function(){this.numeroDeVendidos++;};
};

s = new Sorveteiro();
alert(s.numeroDeVendidos);
s.vender();
alert(s.numeroDeVendidos);
s.vender();
s.vender();
alert(s.numeroDeVendidos);

Alguém me disse essa semana que uma das grandes vantaens em aprender higher-order programming (a pessoa falou em programação funcional mas não é bem isso que ela quis dizer) é que com ela você simula objetos mas o contrario não é verdade. Bom, não é assim. Nada impede de você ter higher-order programming em uma linguagem Orientada a Objetos (JavaScript é Orientada a Objetos!) e com objetos você pode facilmente modelar higher-order programming.

E qual a diferença disso tudo para programação funcional? Bom, programação funcional usa higher-order programming, mas não é isso que define uma linguagem funcional (JavaScript não é funcional).

Em 1984, John Hughes publicou um paper chamado “Why Functional Programming Matters”. Este paper é, até hoje, uma das obras mais importantes para o paradigma. Nele o autor descreve:

The special characteristics and advantages of functional programming are often summed up more or less as follows. Functional programs contain no assignment statements, so variables, once given a value, never change. More generally, functional programs contain no side-effects at all. A function call can have no effect other than to compute its result. This eliminates a ma jor source of bugs, and also makes the order of execution irrelevant - since no side-efect can change the value of an expression, it can be evaluated at any time. This relieves the programmer of the burden of prescribing the flow of control. Since expressions can be evaluated at any time, one can freely replace variables by their values and vice versa - that is, programs are “referentially transparent”. This freedom helps make functional programs more tractable mathematically than their conventional counterparts.

Erik Meijer apresentou uma palestra no JAOO chamada “Why Functional Programming (still) Matters” onde ele afirma que nenhuma linguagem é realmente funcional, provando que se pode simular efeitos colaterais em Erlang, Haskell, F# e várias outras.

A conclusão do Erik -é claro que defendendo suas decisões ao criar o LINQ- é que os conceitos por trás das linguagens ditas funcionais são mais importantes do que ser uma linguagem puramente funcional ou não.

Isso significa que você deve aprender sobre programação funcional e aplicar suas técnicas sempre que necessário mas cuidado com o termo “funcional”. Na maioria das vezes você quis dizer Higher-Order Programming.

Update: Alguns dos comentários msotram uma confusão com funções javaScript e objetos. Não só o texto falou que em JavaScript funções são objetos bem como ele mostrou o exemplo do sorveteiro, mas tentando deixar ainda mais claro:

ds

Singletons…

May 28th, 2008

Singleton Considered Stupid

A Completa Irrelevância do Certified Scrum Master

May 27th, 2008

Semana passada o Richard Durnall me chamou para assistir a uma aula que ele deu na University of Melbourne. O Rich é o guru local de Lean e é uma figura.

A aula foi interessante. O curso é o mestrado em alguma das 343.435 ramificações de Tecnologia da Informação, basicamente uma escolinha para CIO-wannabe. Para se ter uma idéia todas as perguntas da sessão foram sobre implantação de ERPs, você via claramente que nenhum dos estudantes tinha a mínima vivencia na indústria e acreditavam piamente nas suas Info Corporate da vida.

A matéria era sobre comparação de metodologias e o Rich não foi o único. Antes dele apresentou um senhor, que é professor da instituição e arquiteto do maior banco da Austrália, onde inclusive tenho conta (brr…). A palestra do senhor arquiteto foi sobre como ele participou da salvação de um projeto de data mining usando o bom e velho waterfall. O único ponto diferente de uma lição clássica sobre como não fazer software foi sobre o uso de técnicas de Six Sigma para avaliação e priorização dos requisitos.

Quando chegou a vez do Rich apresentar Agile/Lean foi um contraste enorme. Na sessão de perguntas:

- Você falou sobre estes métodos ágeis e sobre como eles…er.. não ligam para requisitos. O [cara defendendo waterfall] apresentou um caso real de um grande banco. Você realmente acha que as técnicas de algo como agile podem competir com Six Sigma? [nota: uh?]
- Então, na minha apresentação eu fui bem ralo e essa é uma palestra introdutória, então foi bom você perguntar isso. Eu trabalhei na [top 5 montadora de automóveis], na [maior empresa aérea do mundo] e em alguns bancos. Nas duas primeiras empresas eu fiz parte da implantação de Six Sigma, inclusive eu sou Black Belt. O que eu vi deste processo foi […] e por isso que agile/lean é uma boa escolha.

Agora pense que em vez de “Six Sigma Black Belt” ele tivesse dito algo como “inclusive eu sou Agile Software Specialist e os problemas de Six Sigma são […]”. Teria o mesmo efeito?

Outro caso recente e interessante foi no Australian Architecture Fórum em Sydney. O Halvard estava apresentando uma palestra extremamente interessante sobre governança de projetos SOA onde a única ferramenta é um wiki. Em algum momento alguém levanta:

- Ok, ok, isso aí é muito Web 2.0, muito legal mas não é aplicável no meu cenário.
- E qual seu cenário?
- Eu trabalho em um banco, faço parte do grupo de controle de serviços de segurança. Isso de REST, wiki é muito legal mas vocês não entrariam num banco de modo algum!
- Uhm.. interessante você falar disso porque segurança em serviços é uma das minhas áreas de estudo… eu concluí meu Ph. D. em SOA na Universidade de Sydney e meu foco é exatamente segurança. Na verdade, na ThoughtWorks a maioria dos clientes são bancos e, inclusive, tivemos hoje de manhã o arquiteto principal do banco [top 5 banco australiano] falando exatamente sobre como usaram este tipo de técnica para governança num projeto que participei.

Imagine que ele tivesse falado “Eu sou um Wiki Certified Contributor e um RESTafarian Official Gold Partner”. Teria o mesmo efeito?

Por que das historinhas? Para argumentar numa discussão que eu tive com o grande Juan Bernabó sobre a total e completa ausência d sentido em algo como Certified Scrum Master. O Juan argumentou que certificações são valorizadas -e requeridas- pelas empresas. Meus pontos são:

  • Eu não conheço nenhuma pessoa que acredite que um curso de dois dias, sem sequer uma avaliação final –pagou, passou na pratica- deva ser valorizada. Se nós sabemos que a certificação não tem valor por que a venderíamos de outra forma? Agile não é sobre trazer valor e melhorar praticas?
  • O mercado também quer porque quer e acha que precisa de cronogramas detalhados, requisitos esmiuçados e projetos que terminam em uma grande fase de testes. Nós sabemos que isso não traz valor não fazemos apologia a este tipo de coisa, por que com certificação seria diferente? Por que ao invés de combater a ineficiência e a busca por respostas fáceis nós criamos e glorificamos nossos próprios selos?
  • Uma certificação emitida por si mesmo não vale nada. A menos que alguém já acredite que Scrum traz algum valor essa certificação é como acreditar que o Inri Cristo é Jesus porque ele afirma o ser.

Em resumo, eu acho o curso que é dado com o CSM ótimo. Ele abre mentes e é uma fantástica introdução. E só. O certificado emitido por este curso não tem qualquer valor real e propagandear o contrario, ajudando empresas a continuar glorificando certificações sem sentido, vai contra a primeira linha do Manifesto Ágil:

We are uncovering better ways of developing
software by doing it and helping others do it.

O debate completo está aqui.

Nem só de troca de mensagens vivem os objetos

May 25th, 2008

Percebi que boa parte das dúvidas quanto ao meu post sobre como objetos não possuem atributos se deve ao fato das pessoas não terem geralmente um conhecimento real sobre o que é troca de mensagens.

Perfeitamente compreensível. Na maior parte dos livros e faculdades as pessoas aprendem que Orientação a Objetos é sobre como utilizar classes e sobre como as funções são chamadas de métodos. Por algum motivo esquecido nas areias do tempo decidiu-se que chamar o método em uma classe era passar uma mensagem e por isso algumas pessoas notoriamente pedantes usam este termo ao invés de dizer apenas “chama a função”.

Bem, os conceitos no parágrafo acima estão errados. Orientação a Objetos não é sobre classes e sim sobre…er… objetos. Você pode ter OO sem ter classes, como JavaScript e Io e pode ter também OO sem mensagens.

Troca de mensagens é um conceito utilizado em diversas áreas, não apenas Orientação a Objetos. Você pode ter um Sistema Operacional baseado neste conceito -como o MINIX por exemplo- ou criar uma arquitetura de computação distribuída como SOAP.

O que distingue a passagem de mensagens é o fato de que o recipiente da mensagem, seja um objeto, um serviço ou um processo, é quem decide o que é feito em decorrência de sua invocação.

Para tentar içar um pouco mais claro eu criei um meta-modelo bem bobinho em Java. Este representa um sistema Orientado a Objetos com classes e passagem de mensagens. O código abaixo mostra como declarar uma Classe calculadora e enviar uma mensagem dizendo para que esta multiplique números.

BlocoDeCodigo bloco = new BlocoDeCodigoImpl<Integer, Integer, Integer>(){
			public Integer executar(Integer a, Integer b){
				return a * b;
			}
		};

		Classe classeCalculadora = novaClasse("Calculadora");
		classeCalculadora.declaraMensagem("multiplique", bloco);

		Instancia calculadora = instanciar("Calculadora");

		assertEquals(8, calculadora.enviaMensagem("multiplique", 2, 4));

Note os passos realizados. Primeiro criamos um bloco de código, uma função. Depois dizemos ao sistema que existe uma classe chamada calculadora. Logo apos registramos o fato de que calculadora responde a uma mensagem executando o bloco que havíamos declarado.

Em termos de semântica, este código é mais ou menos equivalente a este:

public class Calculadora{
 public Integer multiplicar(Integer a, Integer b){
  return a * b;
}
}

Depois nós instanciamos a classe e passamos uma mensagem para ela, o que seria equivalente a:

Calculadora calc = new Calculadora();
calc.multiplicar(2,4);

As classes relevantes:

public class Ambiente {
	static Map<String, Classe> classesDeclaradas = new HashMap<String, Classe>();

	static Classe novaClasse(String nomeDaClasse) {
		ClasseImpl classe = new ClasseImpl(nomeDaClasse);
		classesDeclaradas.put(nomeDaClasse, classe);
		return classe;
	}

	static Instancia instanciar(String nomeDaClasse) {
		return new Instancia(classesDeclaradas.get(nomeDaClasse));
	}
}

public class ClasseImpl implements Classe {

	private final String nome;

	private Map<String, BlocoDeCodigo> mensagens = new HashMap<String, BlocoDeCodigo>();

	public ClasseImpl(String nome) {
		this.nome = nome;
	}

	public void declaraMensagem(String nomeDaMensagem, BlocoDeCodigo blocoASerExecutado) {
		mensagens.put(nomeDaMensagem, blocoASerExecutado);
	}

	public String nome() {
		return nome;
	}

	public boolean respondeA(String nomeDaMensagem) {
		return mensagens.containsKey(nomeDaMensagem);
	}

	public BlocoDeCodigo codigoParaMensagem(String nomeDaMensagem) {
		return mensagens.get(nomeDaMensagem);
	}

}

public class Instancia {
	private final Classe minhaClasse;

	public Instancia(Classe classe) {
		this.minhaClasse = classe;
	}

	public Object enviaMensagem(String mensagem, Object... args) {
		Object primeiro = args.length > 0 ? args[0] : null;
		Object segundo = args.length > 1 ? args[1] : null;

		return minhaClasse.codigoParaMensagem(mensagem).executar(primeiro,
				segundo);
	}

	public Classe classe() {
		return minhaClasse;
	}

}

Esse meta-modelo é baseado em troca de mensagens. A classe Calculadora não recebe código a ser executado ela apenas recebe o nome de uma mensagem e parâmetros. Imagine que eu registre o mesmo bloco de código para várias mensagens, ou que eu use recursos de AOP e intercepte a execução do bloco. Nada disso é relevante para quem invoca a mensagem, ele apenas a envia e o que acontece em decorrência disso é responsabilidade do receptor.

Como quase todas as linguagens atuam desta forma pode ser difícil entender o conceito já que nunca se viu nada diferente. Vamos então implementar outro meta-modelo que não usa troca de mensagens mas sim uma outra forma chamada Data-Directed.

Nesta forma de invocar operações em objetos –que, como a anterior não é específica de OO- quem decide qual função será aplicada é o ambiente de execução, o runtime. Quando você invoca uma operação o ambiente vai procurar dentre os métodos registrados qual é o aplicável para aquele objeto e vai executar o método nele. Common Lisp utiliza este recurso de maneira tão poderosa em suas Generic Functions que praticamente elimina a necessidade de coisas como proxies e AOP.

Nosso meta-modelo para Data-Directed é executado dessa forma:

BlocoDeCodigo bloco = new BlocoDeCodigoImpl<Integer, Integer, Integer>(){
			public Integer executar(Instancia instancia, Integer a, Integer b) {
				return a * b;
			}
		};

		Classe classeCalculadora = novaClasse("Calculadora");
		registrarMetodo("multiplique", classeCalculadora, bloco);

		Instancia calculadora = instanciar("Calculadora");

		assertEquals(8, executarMetodo("multiplique", calculadora, 2, 4));

Repare que agora o bloco de código recebe como seu primeiro argumento uma referência para a instancia a qual se aplica (se você já usou java.lang.Method sabe que isso não é incomum quando se desce ao nível de implementação de linguagem). Caso nosso exemplo fosse minimamente usável seria desta forma que o bloco obteria acesso ao objeto em si.

Logo depois criamos a classe como antes mas ao invés de registrar uma mensagem na classe nós registramos um método no ambiente, dizendo que o método se aplica àquela classe. A invocação em si é bem parecida com a anterior.

Na implementação a única classe mais interessante é o Ambiente, que agora é bem mais esperto:

public class Ambiente {
	static Map<String, Classe> classesDeclaradas = new HashMap<String, Classe>();
	static Map<String, Map<Classe, BlocoDeCodigo>> metodos = new HashMap<String, Map<Classe, BlocoDeCodigo>>();

	static Classe novaClasse(String nomeDaClasse) {
		ClasseImpl classe = new ClasseImpl(nomeDaClasse);
		classesDeclaradas.put(nomeDaClasse, classe);
		return classe;
	}

	static Instancia instanciar(String nomeDaClasse) {
		return new Instancia(classesDeclaradas.get(nomeDaClasse));
	}

	static void registrarMetodo(String nomeDoMetodo, Classe tipoEmQueSeAplica,
			BlocoDeCodigo bloco) {
		metodo(nomeDoMetodo).put(tipoEmQueSeAplica, bloco);
	}

	static Object executarMetodo(String nomeDoMetodo, Instancia instancia,
			Object... args) {
		Map<Classe, BlocoDeCodigo> tiposAceitaveis = metodo(nomeDoMetodo);
		if (!tiposAceitaveis.containsKey(instancia.classe()))
			throw new RuntimeException("Metodo inexistente");

		BlocoDeCodigo bloco = tiposAceitaveis.get(instancia.classe());

		Object primeiro = args.length > 0 ? args[0] : null;
		Object segundo = args.length > 1 ? args[1] : null;

		return bloco.executar(instancia, primeiro, segundo);
	}

	private static Map<Classe, BlocoDeCodigo> metodo(String nomeDoMetodo) {
		if (!metodos.containsKey(nomeDoMetodo))
			metodos.put(nomeDoMetodo, new HashMap<Classe, BlocoDeCodigo>());

		return metodos.get(nomeDoMetodo);
	}

}

Agora não apenas registra as classes mas também os métodos que são aplicáveis à cada classe e faz a invocação dos métodos em si.

Estes exemplos são bem educacionais, sem muita aplicação pratica, mas como já vimos em posts passados o fato de se usar message-passing ou Data-Directed ou properties, ou componentes ou qualquer outra coisa interfere no modo como devemos projetar nosso software. Existe uma vasta literatura sobre este tema mas ainda assim é uma das coisas mais desconhecidas pelo programador profissional.

Domain-Driven Design é Simples: Basta Chamar DAOs de Repositórios

May 22nd, 2008

Um fenômeno notado no Oriente e no Ocidente é a notável incapacidade de se entender o que raios é Domain-Driven Design. Na minha opinião isso é causado elo fato de que para chegar num nível onde DDD te ajuda você já precisa ter uma base formada e essa base não é comum. Eu vejo muitas pessoas tentando entender a solução quando na verdade elas deviam estar tentando chegar ao problema primeiro.

Uma das conseqüências deste comportamento é a síndrome do Padrão-de-Prata. Todo mundo sabe que Não existe bala de prata mas, hei, ninguém falou nada sobre padrões (ou frameworks, ou plataformas…) então, buscando respostas fáceis é comum se associar Domain-Driven Design aos padrões Entity, Repository, Value Object e amigos.

O que parece bem difícil de entender é que o ponto todo não é usar os padrões e sim porque você os usa. As técnicas dos padrões em si é muito antiga e o livro não traz nada de novo exceto sobre como utiliza-los para atingir o Domain-Driven Design. O que qualquer tópico no GUJ sobre o assunto (e mesmo na lista sobre Domain-Driven Design) não parece entender é que os padrões são um meio e não um fim.

Eu já repeti algumas vezes que você pode utilizar todos os padrões do Eric Evans e ainda assim não usar DDD. Nos últimos meses eu vivi um exemplo claro.

O cliente em questão é uma empresa de comunicação. Ela produz algo que você pode simplificar como um jornal de classificados. Os jornais em si são gerenciados e impressos por um sistema antigo e uma equipe de mais de 30 pessoas foi destacada para criar a versão online deste.

Como o sistema é antigo ele não oferece qualquer interface para conexão, logo a solução encontrada foi acessar o banco de dados diretamente. Como os dados continuam sendo inseridos pelo sistema antigo (e este não muda desde 1998) não existe muito problema nisso.

O projeto possui um time excelente, um dos grupos de pessoas mais capacitadas com quem já trabalhei, mas ainda assim não conseguiam andar. A velocidade da entrega das histórias estava bem abaixo do esperado e o nível de retrabalho era ridiculamente grande, mesmo com clientes on-site. Apos verificar que se a deadline não fosse cumprida eles teriam corte no orçamento chamaram consultores para avaliar a situação.

A primeira coisa que um consultor pensa quando chega num lugar desse é que eles não estão seguindo um processo ágil de verdade. É extremamente comum entrar numa empresa “Agile de carteirinha”e ver um processo que na verdade é composto por mini-waterfalls, tão comum que a solução default é mudar o processo. Não era o caso. O processo era legitimamente ágil, da análise de negócios à homologação, e a equipe, como disse, era excelente.

Apos alguns dias fazendo pair programming percebi uma coisa errada com o vocabulário. Era extremamente difícil entender conceitos simples da aplicação e cada reunião que se ia o vocabulário era diferente. Daí vamos analisar o caso melhor.

O banco de dados em questão, como era de se esperar numa aplicação legada, trazia um bando de regras de negocio embutidas em flags absurdos. O time fez um fabuloso trabalho criando um mecanismo que transformava dados do domínio antigo para o novo, formando um excelente Context Map.

Dentro do domínio o código era extremamente enxuto, fazendo uso de JPA e Spring para deixar o Domain Model apenas com regras de negocio. Eles usam Repositories como interfaces para DAOs que implementam a lógica JPA de maneira bem interessante.

Ainda assim a velocidade era ridícula. 5 pares e apenas 4 pontos por iteração(semanal). Após verificar que o problema não era nem o processo nem o código em si só restava continuar pareando para tentar ver o que estava acontecendo.

Um dia, após reescrever a mesma funcionalidade duas ou três vezes, meu par e eu saímos para um café na Starbucks. Enquanto conversávamos eu perguntei:

- Mas quando é que vocês vão começar a outra parte do sistema?
- Outra parte?
- Sim, a parte que substitui o legado…
- Ah. Não, não vamos.
- Não?
- Quer dizer, vamos sim mas ele não vai ficar muito diferente, na verdade para nós do sistema web a única diferença é que eles vão disponibilizar um web service ao invés do banco de dados…
- Mas vocês não vão mudar aqueles conceitos legados para o modelo novo?!?
- Conceitos legados? Aqueles não são conceitos legados, são os conceitos que nossa indústria usa. Se você parar de usar aqueles termos seus clientes não vão entender o que está falando…

E aí eu entendi o problema da comunicação. Na retrospectiva eu levantei um ponto e conversamos sobre o problema.

A coisa era bem simples, em verdade. Os usuários internos do sistema são vendedores. Quando você vende um anuncio você fala em estilos e estes estilos são padronizados nacionalmente. O sistema antigo, por pior que seja, tem os estilos e os outros conceitos editoriais modelados mas nós não tínhamos isso no sistema web. O nosso domínio, por mais bonito e bem-feitinho, foi criado pensando na melhor forma de disponibilizar dados na Internet e por isso o nosso modelo não falava a língua do usuário. Os usuários falavam os conceitos do modelo antigo e para entender o que eles diziam nós tínhamos que fazer todo o mapeamento para o que aquilo representava em código.

Com apenas algumas iterações para um grande release não há a menor possibilidade de mudar todo o domínio. A solução vai ser implementar as mudanças de maneira incremental, toda vez que código novo é escrito ou código antigo refatorado caminha-se para o novo modelo, que é algo parecido com o abaixo.

Este foi um exemplo real do que não é Domain-Driven Design. Todos os desenvolvedores desta empresa possuíam o livro do Evans nas suas baias, não existia BO ou VO no sistema e as Camadas eram bem definidas. Ainda assim a linguagem do código não era a linguagem do usuário e sem isso você pode até ter um modelo Orientado a Objetos de alta qualidade mas não tem Domain-Driven Design.