Archive for June, 2007

Revisão de Código Com Testes Unitários

Saturday, June 30th, 2007

Estou eu esperando uma s boas duas horas para embarcar no Santos Dummont para palestrar no Falando em Java amanhã quando me passa pela cabeça algo que acho que pode ser bem útil para levar à uma empresa burocrática um mínimo de agilidade.

Existem muitos lugares, especialmente lugares de três letrinhas com certificações de 3(+1) letrinhas, onde revisão por par é algo obrigatório. Note que revisão por par não é algo ruim, pelo contrário. Revisão assim é tão boa que XP prega que ela seja feita o tempo todo. O ponto é que existe uma diferença entre a aplicação útil desta técnica e o que geralmente é feito nestes lugares, de maneira extremamente burrocrática (conheço lugares onde a revisão resulta em uma declaração que é impressa e assinada pelas partes).

Fazer um desenvolvedor revisar linha por linha do que o outro fez é besteira. Quer saber se ele seguiu as regras de nomenclatura e os seus padrões? Quer saber se ele não introduziu nenhum bug óbvio? Se seguiu boas práticas? Não quebrou Camadas? Existem ferramentas que fazem isso automaticamente e o fazem enquanto a pessoa trabalha, não depois. Fazer a pessoa explicar ao revisor em linhas gerais é a técnica que eu havia julgado ser mais interessante até então, mas acho que além disso pode haver algo melhor.

E minha proposta é que o revisor revise apenas os testes unitários do desenvolvedor. Ele vai poder criar novos testes e questionar os que não julgar adequados, mas o código em si é irrelevante. Por quê? Porque não é eficiente. Não adianta, a pessoa não vai conseguir revisar cada linha, pensar em cada problema, lendo um grupo de blocos de código.

Em compensação, se a classe possuir uma interface bem construída, típica de Test-Driven Development, o código em si não é o foco. Claro que haver um código de qualidade e bem construído é fundamental, mas essa revisão tem foco no comportamento dos componentes (provavelmente classes)afetados.

Imagine que nós temos uma classe Pedido, imagine que o teste unitário dela é o abaixo:


class PedidoTest extends testCase{
public void testFechaPedido(){
Pedido pedido = new Pedido();
assertEquals("Pedido esta aberto", Pedido.Status.ABERTO, pedido.getStatus());
pedido.adiciona(produto1, 2);
pedido.adiciona(produto2, 1);
pedido.fechar();
assertEquals("Pedido esta fechado", Pedido.Status.FECHADO, pedido.getStatus());
assertEquals("Pedido calculou preco correto", ((produto1.getPreco() * 2) + produto2.getPreco()), pedido.getTotal());
}
}

Agora imaginemos que existe uma requisição para mudar esta classe. O governo acabou de decidir morder mais algum % do capital gerado no país, então existe uma nova regra de negócios que diz que toda compra vai ter 10% de imposto. O desenvolvedor cria a classe e cria seus testes, o que o revisor vai fazer? Reler todo o código? Não, ele revisa os testes e vê que o desenvolvedor não fez o suficiente. Na verdade o desenvolvedor deixou o código acima exatamente como ele está (ok, esse é um exemplo bobo). O que o revisor faz? Modifica o caso de teste:


class PedidoTest extends testCase{
public void testFechaPedido(){
Pedido pedido = new Pedido();
assertEquals("Pedido esta aberto", Pedido.Status.ABERTO, pedido.getStatus());
pedido.adiciona(produto1, 2);
pedido.adiciona(produto2, 1);
pedido.fechar();
assertEquals("Pedido esta fechado", Pedido.Status.FECHADO, pedido.getStatus());
long valorTotal = ((produto1.getPreco() * 2) + produto2.getPreco());
valorTotal = valorTotal+ (valorTotal * 0.1);//+10%
assertEquals("Pedido calculou preco correto", valorTotal, pedido.getTotal());
}
}

E pronto. Ao invés de tediosamente ler centena de linhas de código e tentar fazer de cabeça a execução do programa o revisor se concentra no que importa: o funcionamento.

Claro que num ambiente TDD este tipo de teste provavelmente já estaria escrito, mas dificilmente um ambiente TDD utilizaria revisões formais como técnica habitual do processo de desenvolvimento, esta prática tem mais a ver com processos extremamente burocráticos, geralmente certificados por algum órgão de três letrinhas.

Mas e a qualidade do código escrito? Primeiro estes são verificados com as ferramentas acima, que são mais eficientes que qualquer desenvolvedor (o cara não consegue fazer um parsing linha-a-linha como a ferramenta faz), mas ainda é preciso falar em evolução constante.

Um dos princípios básicos de qualquer metodologia iterativa (isso inclui XP e RUP, por exemplo) é de que você não vai acertar da primeira vez. Esquece.Então o que fazemos? Crie o código que cumpra o seu papel e o evolua aos poucos, com refactoring. Bons programadores tendem a gerar código de qualidade nas primeiras iterações, mas programadores mais medianos geralmente vão ter que refatorar bastante. Quando você possui testes unitários refatorar é algo seguro, se você mudar a implementação de Pedido e não mudar o comportamento seu sistema vai funcionar, e para verificar se o sistema funciona você não precisa mais de um time com 3543 pessoas dedicadas a apertar todos os botões existentes, apenas rode o teste de regressão unitário dentro da sua própria IDE.

A idéia deste post é dar uma ferramenta para os pobres desenvolvedores presos em processos burocráticos e inflexíveis sobre como dar ao seu dia-a-dia características ágeis. Agilidade não precisa ser aprovada pela diretoria, agilidade pode ser feita na sua baia.

Epílogo: Meu vôo foi cancelado e eu cheguei no evento de ônibus. :P

Testando Constantemente

Thursday, June 28th, 2007

O Guilherme Chapiewski e eu vivemos hoje um diazinho de cão. Ao contrário do que muitos pensam, aqui no trabalho nós temos algumas dezenas de sistemas, todos necessários para o processo de criação, codificação, decodificação e oferta de vídeos na Internet. É coisa pra caramba. E um destes sistemas é um legado de três anos, em vias de se aposentar, que todas as pessoas da empresa já passaram por, mas ninguém é dono ou admite que tem código seu ali. Eis que após uma subida para produção o sistema apresenta lá seus problemas. Nada funciona.

Neste cenário caótico, após quase 10 horas de crise, alguém acaba achando um daqueles trechos de código que fazem você querer ter virado sacerdote em vez de desenvolvedor. Um arquivo de constantes Strings. E alguém resolve fazer algo como:


if(resultSet.getString("USUARIO_TXT").equals(Constantes.PARAMETRO_ADMINISTRADOR){
facaTudoDeMaisImportanteQueOSistemaFaz();
}

Que já é uma pérola por si só, mas não bastante isso o conteúdo da constante é ainda mais legal:


public static Constantes.PARAMETRO_ADMINISTRADOR = "Usuário Administrador Básico";

Percebeu algo errado? Acentos! Primeira regra de hoje:

  • Se você precisa de acentos no código fonte, numa literal String ou algo do tipo, sempre escape os acentos.

Por quê? Porque a possibilidade de alguém corromper seus acentos com um encoding diferente é enorme. No nosso caso identificamos nos logs co CVS o momento exato em que um desenvolvedor fez checkout do arquivo, alterou outros arquivos e fez o commit dele no meio dos legitimamente alterados. O arquivo passou de ISO para UTF-8 (que aliás, é o padrão do departamento) nesta operação e todas as Strings com acentos viraram coisas aleatórias. Como as Strings do banco não foram alteradas a comparação acima falhava miseravelmente e o resultado foi um sistema dando problemas em produção e dez horas de trabalho para descobrir isso.

Ainda falta uma medida básica de segurança neste exemplo: testes unitários. Se você é do time que não acredita em testes unitários, um simples teste em JUnit poderia pegar o problema na hora da geração do build que foi para produção. A menos que déssemos o azar de que todas as classes que envolvem esta constante tenham sido alteradas o teste ia falhar e acusar o erro.

Ah, mas e se dermos este azar e o teste unitário não pegar? Teste de integração. Com um servidor de integração contínua você faz com que a cada commit seu código seja baixado, compilado, testado unitariamente e, no final, seja feito um teste que envolve outras partes. O Fit é ótimo para isso.

Não julguem o processo ou ambiente mal: existem responsáveis pela homologação do sistema e eles cumpriram seu papel. Infelizmente não é viável testar manualmente todo o sistema para saber que uma mudança na ponta de cá causou uma mudança acidental na ponta de lá, ainda mais com um sistema deste histórico.

O problema é que ao contrário do que é senso comum testes não são apenas parte da homologação. testes são parte de todo o ciclo de desenvolvimento, tanto na criação como na manutenção de produtos. Ou isso ou você faz como eu e perde sua quinta-feira.

Encapsulando o Futuro Incerto

Tuesday, June 26th, 2007

Após as turbulências voltamos ao normal, ou quase. O número de visitas caiu pela metade neste período, então por favor avisem a seus amigos que o blog mudou: philcalcado.com. As URIs antigas devem funcionar mas esta é a oficial.

Nestes últimos dias acabei migrando para o Gnome. Eu nunca fui muito com a cara do gerenciador, mas acho que sempre foi por birra minha com o Miguel de Icaza. Eu nunca engoli direito a história de se implementar as pseudo-especificações da Microsoft como Software Livre em vez de investir em uma plataforma como Java, Python, Ruby ou Strongtalk, mas enfim, hoje em dia eu até Mono uso…

O Gnome segue um paradigma mais minimalista, quem está acostumado com muitas opções sofre um pouco mas anda fácil depois de um tempo. Eu não diria que é melhor ou pior que o KDE< é questão de costume e gosto apenas. O problema que eu venho experimentando não está no Gnome ou KDE e sim nos softwares que os acompanham. Para a maioria das pessoas que usam um computador as configurações de fábrica são mais que suficientes. para um programador este não é o caso. Nós sempre temos que experimentar,t estar e acabar com um HD torrado ou algo parecido. No caso destes ambientes, "algo parecido" geralmente é uma aplicação travando. O problema acontece mais frequentemente com aplicações pequenas, aquelas que ficam nas respectivas docks. É interessante que na maioria dos casos as aplicações funcionam normalmente, mas basta quebrar uma dependência, tentar algo muito diferente e elas desabam. Aí você procura uma solução na internet e descobre que aquele comportamento se deve ao fato da aplicação fazer várias suposições sobre o ambiente que roda, por exemplo que está no KDE ou Gnome. E falha.

Vivi um caso parecido há poucas horas. Era uma página de Internet simples, o problema consistia apenas em XHTML e JavaScript. Uma determinada <div> continha uma objeto flash dentro de si e em alguns casos especiais ele deveria ser substituído por outro. Aí entra o problema. O objeto original faz parte de um determinado framework de anúncios, o novo faz parte de outro. Ao trocar o conteúdo havia um problema de JavaScript que só se manifestava no pior browser: Internet Explorer 6.0. Este browser possui recursos pífios de depuração e por anos é a dor-de-cabeça do pessoal de implementação client-side. Eventualmente, após muitos alert()s, descobrimos que o problema era que o framework original estava no time dos que fazem diversas suposições sobre o ambiente. Ele supunha que existiria um objeto com determinado nome, que uma determinada função estaria disponível, etc. etc. etc.

Estes são dois exemplos simples que mostram o quanto encapsulamento é importante. Você não precisa de objetos para ter encapsulamento (apesar de que se você tem objetos deveria ter encapsulamento), você pdoe encapsular algo como o que sua aplicação expõe e espera encontrar do lado de fora ou como seu JavaScript reage a mudanças na página - Web 2.0, meus caros, Web 2.0 -, o que importa é que seu código abstraia a implementação do que acontece no exterior (uma <div> é uma <div>, não importa se dentro possui o objeto A ou B), qual a implementação das coisas (KDE e Gnome fazem basicamente a mesma coisa) e, principalmente, colabore com as outras aplicações escondendo delas os detalhes da sua implementação.

A “culpa” da quebra de encapsulamento geralmente não está na classe que usa outra e sim na que não oculta dos seus clientes os seus segredos mais íntimos: como diabos ela é implementada.

Falando novamente

Monday, June 25th, 2007

Post dando um up no Falando em Java, evento da Caelum no próximo sábado.

“Estamos aprendendo a desenvolver com quem não desenvolve”

Monday, June 25th, 2007

Excelente podcast da ImproveIT.

Cuidado com Domain-Driven Design

Friday, June 22nd, 2007

Em tecnologia existe um fenômeno interessante. Existe um problema qualquer. Alguém resolve o problema de um modo e as pessoas começam a usar este modo. O detalhe é que as pessoas não param para ler as fundamentações técnicas, a coisa vira um grande grupo de achismo. Aí surge outra solução que é mais eficiente e utilizada por uns poucos que tentam convencer os outros. Quando finalmente as pessoas se convencem elas repetem o ciclo, não estudando a fundo as bases e caindo no conto do vigário.

Este post no GUJ mostra um claro desvio dos padrões estipulados por Domain-Driven Design. Vamos desmistificar a coisa: DDD é uma forma disciplinada de criar um Domain Model, só isso. O foco da técnica é criar um domínio que “fale a língua” do usuário. Isso não quer dizer que você vá “mapear o mundo real” com objetos, esse não é o objetivo nem da técnica nem de Orientação a Objetos em primeiro lugar.

Passeando pela thread, você pode perceber diversas coisas fora do que é definido em DDD. Os conceitos de Domínio e Contexto confusos. Domínio é o que o programa (seja um exercício de faculdade ou um sistema empresarial) modela, o que ele se propõe a resolver. O modelo que você criou deste domínio (o Domain Model) certamente possui intersecções com modelos criados em outros sistemas, serviços, etc. Neste caso o dividimos em Contextos:

Explicitly define the context within which a model applies. Explicitly set boundaries in terms of team organization, usage within specific parts of the application, and physical manifestations such as code bases and database schemas. Keep the model strictly consistent within these bounds, but don’t be distracted or confused by issues outside.

A analogia do círculo é péssima porque ela foca em uma caixa-preta, que não é um Módulo em Domain-Driven Design. Em DDD um módulo é quase que exatamente como um pacote em Java ou namespace em C#. O Contexto é dividido em Módulos, que agrupam entidades com conceitos em comum. A comunicação entre Contextos e entre Módulos é dada através de diversos padrões e técnicas.

Existe uma confusão também com Value Object. Em DDD um Value Object é um conceito de domínio como qualquer Entidade a diferença é que ele não tem identidade própria. Se eu pegar uma nota de dez reais emprestado de você você não exige que eu te devolva a mesma nota, apenas que devolva uma nota de mesmo valor ou equivalente. Este conceito do “mesmo valor” é o coração do Pattern. O tópico coloca o pobre VO como um TO, mero agrupamento de dados.

A parte da transação também é muito complicada. Desde o início deste século que nós estamos separando estas responsabilidades (autenticação, transações, log, etc.) como conceitos ortogonais. Conceitos ortogonais não devem, quando a tecnologia permite, estar implementados junto com regras de negócio, junto com entidades de domínio. Para isso suamos a AOP expressa por ferramentas como Spring Framework ou EJB (seja 2.1 ou 3.0). Eric Evans fala sobre separação entre domínio e tecnologia:

The domain model is a set of concepts. The “domain layer” is the manifestation of that model and all directly related design elements. The design and implementation of business logic constitute the domain layer. In a MODEL-DRIVEN DESIGN, the software constructs of the domain layer mirror the model concepts.

It is not practical to achieve that correspondence when the domain logic is mixed with other concerns of the program. Isolating the domain implementation is a prerequisite for domain-driven design.

Falar que ActiveRecord não funciona é negar a realidade. Frameworks como Ruby on Rails, Castle e Grails se baseiam nele, não é porque não até até então nenhuma proposta Java de framework que o conceito não funciona. O exemplo dado não representa qualquer problema, já que transações são tratadas em um conceito ortogonal separado, como descrito acima. Se AR se aplica bem ou mal no caso X ou Y, com DDD ou o que for é outro assunto, que aliás já tratamos aqui mais de uma vez.

Interessante que toda a thread teria começado porque o autor original achou que os artigos deste blog não são completos o suficiente. Independente de serem (e não são) ou não, o que eu sempre recomendo éleia a bibliografia. Infelizmente tem (muita) gente que prefere simplesmente ter um pseudo-resumo rápido num fórum. Fazendo uma análise dos pontos que levantei aqui e de outros no texto em questão eu percebo que o autor original em si não teve muito sucesso em aprender os conceitos de Domain-Driven Design porque procurou o meio errado. Outro dia uma thread no mesmo fórum sobre o mesmo tema corria parecido, com uma pessoa fazendo críticas em cima de um modelo usando Repositórios. O problema é que o cidadão em questão nem sequer leu sobre a técnica antes de criticar, nem mesmo no resumo disponível gratuitamente apenas pedia um exemplo como se quatro linhas de código fossem passar 500 páginas de conhecimento. O mundo tem pressa e preguiça, mas até onde isso leva?

URI Nova, Tema Novo

Friday, June 22nd, 2007

Acho que já terminei a migração. A esperança é não perder nada, usei um plugin do wordpress para migração de URI e agora temos URIs amigáveis apra engines de busca (i.e. Google). Teoricamente os links antigos (incluindo feeds) devem ser preservados, se notarem algo me avisem.

A URI nova do blog passa para http://philcalcado.com, mas a antiga deve ter um redirect quase imperceptível.

Os artigos também migrou de URI, versão do MediaWiki e servidor. Agora a URI oficial é http://philcalcado.com/bliki (afinal, não é um wiki), e as URIs antigas devem funcionar normalmente.

Para comemorar, mudança no tema. E vamos voltar à programação normal.

Formulários

Saturday, June 16th, 2007

A maioria das aplicações web de hoje em dia são apenas um grande conjunto de formulários e relatórios derivados dos dados inseridos. A maioria das empresas (especialmente as de 3 letrinhas) não consegue enxergar o investimento em usabilidade, arquitetura da informação e design como algo rentável neste ambiente, deixando a cargo dos pobres programadores a criação das telas. Se você está neste barco, dê uma olhada pelo menos nos guidelines que Luke Wroblewski postou sobre formulários. Fantástico.

“I feel your pain”

Saturday, June 16th, 2007

Sidu Ponnappa, colega Indiano do cv na ThoughtWorks mostra no seu blog que problemas com DTOs, pessoas chamando get/set de encapsulamento e falta de Domain Models em favor de projeto procedural são um mal global.

Falando nisso, essa resposta do Guilherme Chapiewski (não acredito que aprendi a escrever este nome) foi sensacional:

Olá,

Alguém conhece o livro “Padrões de Arquitetura de Aplicações Corporativas”? A tradução está boa, ou seria melhor comprar a versão em inglês mesmo?

————————————————————————————————

Phillip, já viu que daqui a pouco você além de caçar VO’s e DTO’s vai ter que caçar OV’s e OTD’s!

Objetando: Objetos e o Mundo Real

Saturday, June 16th, 2007

E-mail do Caike:

Olá Phillip, tudo bom ?

Leio sempre seus posts em seu blog e no fórum do GUJ e vejo como algumas vezes suas constatações vão por caminhos diferentes dos do ’senso-comum’ (Freakonomics?). Gostaria de agradecer as ‘guerras’ (inclusive algumas vezes levadas muito mais a sério do que deveriam, por outras pessoas) sobre diferentes arquiteturas e aplicações de OO. Graças a elas, tenho ganho diversos pontos a serem investigados em meus estudos.
Sou recém graduado em Ciência da Computação por uma faculdade de Belém, PA, e a pergunta que estou prestes a fazê-lo envolve a tâo polêmica orientação a objetos.

Batendo a cabeça sobre tudo o que o pessoal vem falando e a idéia que você vem tentando passar, gostaria que avaliasse a minha possível avaliação de OO.

Vejo a OO como algo que tenta expressar mais como o mundo real funciona, e não como ele realmente é. Acredito que o foco da abordagem orientada a objetos deva estar nas mudanças de processos e, por conseqüência desta, acaba dando uma idéia de como as coisas são. Durante a modelagem de uma idéia devemos prezar pela situação que melhor expressar seu funcionamento, e não sua estrutura. A grosso modo: Se nosso sistema resolve um problema relativo a segurança de uma casa em Brasília, não deveríamos nos preocupar com a ameaça de um marémoto. Não devemos fazer nossos objetos serem capazes de fazer coisas que eles não vão precisar fazer. Um pensamento como este construirá uma boa arquitetura que terá sempre a robustez para se adequar a mudanças, ou, complementos de atividades.
Parabéns pelos posts e espero estar pensando de maneira coerente.
- Caike

Este é um ponto deveras interessante, como diria um grande amigo.

Orientação a Objetos é pura e simplesmente um conceito técnico. É pegar dados e lógica e manter num mesmo componente, seja através de classes ou qualquer outra forma (por isso tanta gente se espanta quando descobre que JavaScript é OO). É por isso, por exemplo, que Ma href=”http://www.objectmentor.com/ “>Uncle Bob introduz sua apresentação sobre design eliminando o mito de que “objetos modelam melhor o mundo real” e focando sua utilidade em gerenciamento de dependências.

Alan Kay, inventor de Smalltalk e um dos pioneiros na área (ele criou o termo object-oriented descreve em seu paper “The Early History of Smalltalk”:

In computer terms, Smalltalk is a recursion on the notion of computer itself. Instead of dividing “computer stuff” into things each less strong than the whole–like data structures, procedures, and functions which are the usual paraphernalia of programming languages–each Smalltalk object is a recursion on the entire possibilities of the computer. Thus its semantics are a bit like having thousands and thousands of computer all hooked together by a very fast network. Questions of concrete representation can thus be postponed almost indefinitely because we are mainly concerned that the computers behave appropriately, and are interested in particular strategies only if the results are off or come back too slowly.

Para Kay, objetos são mini-computadores especializados em um conjunto específico de comportamentos. A idéia do autor era utilizar estes mini-computadores para aproveitar os recursos das máquinas mais eficientes dos anos 70.

Com o passar do tempo surgiu outro tipo de profissional, o que não estava mais tão preocupado assim em resolver problema computacionais mas sim em como a tecnologia pode ajudar seus clientes (este é o elo perdido entre os programadores e os pseudo-analistas de sistemas de hoje). As pessoas desta linha de pensamento começaram a fazer paralelos entre objetos e conceitos do mundo real e perceberam que podiam utilizar as boas características da OOP (herança, encapsulamento, contratos…) para modelar o domínio de um software.

Por isso um objeto serve para modelar o processo de negócios de uma empresa de uma maneira relativamente eficiente (pelo menos mais eficiente que abordagem de programação estruturada) assim como serve para modelar um Servlet, algo completamente tecnológico sem paralelo no domínio de negócios. Ambos, Servlet e objetos de domínio, tiram proveito das características de OO.

Esta falta de relacionamento real entre OO e “modelar o mundo” dá uma pista de que você está no caminho certo. Abstrair é tirar detalhes não-interessantes dentro de um contexto. Quando abstraímos algo nós nos concentramos apenas no que é necessário para fazer o que precisamos, é como quando eu falo para você “meu vôo sai assim que o sol nascer”. O vôo não é meu e ele não depende do Sol nascer para partir, mas você entendeu a mensagem mesmo abstraindo os detalhes. Objetos servem para modelar, seja br.com.empresa.Pedido ou javax.swing.JButton de forma abstraída.

Note que OO nem sempre é o ideal. Cada domínio já desenvolveu na sua história meios para modelar seu domínio. Pense no sempre utilizado exemplo de contabilidade. Por séculos os contadores utilizaram estruturas tabulares para efetuar seus cálculos, estruturas que evoluíram até planilhas eletrônicas como a Google Spreadsheet ou OpenOffice.org Calc. A tecnologia evolui e talvez estes modelos caiam, mas se você se manter próximo destes paradigmas vai conseguir muito mais satisfação ao lidar com usuários. Por isso os domínios expostos com orientação a objetos estão sendo desafiados por domínios expostos por Domain-Specific Languages (DSLs).

Seu exemplo, entretanto, foge mais do conceitual sobre modelagem e entra em princípios de processo de desenvolvimento. Se você mostrar este exemplo para algumas pessoas elas certamente dirão “Ah, mas amanhã pode ser que a empresa venda o produto para alguém no Rio, ou pode ser que o Sertão vire Mar e o Mar vire Sertão. O que fazer neste caso? YAGNI. Implemente o que você precisa agora, deixe o amanhã para amanhã. Claro que é papel fundamental de um bom arquiteto (e não, arquiteto não é aquele cara que faz diagrama o dia inteiro, na maioria dos projetos todos somos arquitetos em maior ou menor nível) é prever possibilidade de extensão e flexibilidade, mas a minha experiência diz que a melhor maneira de criar flexibilidade sem overengineering é utilizar um ótimo design OO.