Archive for the ‘java’ Category

Ruby “ou” Rails?

Tuesday, October 2nd, 2007

Esse post no GUJ me fez pensar sobre a melhor maneira de absorver algo como o Rails. Rails é uma plataforma de desenvolvimento altamente produtiva e boa parte da produtividade vem do fato de que não é preciso abstrair um domínio na linguagem.

Desenvolvimento de aplicações web é um domínio que inclui diversos conceitos e abstrações. Vejam por exemplo uma sessão web. Se uma pessoa ler sobre o protocolo HTTP em si vai perceber que não existem sessões, o protocolo não mantém estado entre as requisições. Para burlar este problema nós utilizamos cookies ou URIs especiais para informar ao servidor o ID da sessão do cliente. Este é um conceito.

Em Java (ou outra plataforma parecida) vamos abstrair a sessão em uma classe. É desta forma que trabalhamos em Java: criamos classes para representar os conceitos do domínio.

O problema é que até conhecer o suficiente para utilizar de maneira adequada esta abstração na forma de classe você precisa conhecer o que é uma classe e todos os conceitos derivados desta. Basicamente não se consegue criar algo razoável sem saber um mínimo de programação orientada a objetos.

E como Rails resolve isso? Rails abstrai boa parte destes conceitos na linguagem. Ruby é uma linguagem OO e é possível representar a sessão da mesma maneira que se faz em Java mas este não é o meio utilizado em Rails e esta forma de representar as coisas é seu maior diferencial.

Uma sessão em Rails está embutida implicitamente dentro do controlador. Trabalhar com elas é muito simples, para efeito de comapração é como se seu controlador em Java herdasse uma classe que possuísse o objeto que representa a session (que tem a mesma interface que um Map) como atributo protected. Exceto que o acoplamento gerado para acessar a session da classe-mãe em Ruby é muito fraco enquanto em Java seria enorme (na verdade provavelmente a melhor opção em Java seria um método e não um atributo. Em Ruby estes conceitos são bem mais flexíveis) é mais uma questão de filosofia do framework do que de linguagem utilizada em si.

Apesar da polêmica se é ou não uma Domain-Specific Language, Rails é um exemplo claro de Language-Oriented Programming. Neste paradigma de programação (praticado em Lisp desde…sempre!) a linguagem utilizada é modificada e estendida para acomodar os conceitos do domínio. No caso do Rails a linguagem Ruby ganha características que permitem ser estupidamente simples criar uma aplicação web.

E o que isso representa para quem está aprendendo? Eu diria que existem 2 tipos de pessoas que desenvolvem em Rails: desenvolvedores e desenvolvedores de aplicações web. Qual a diferença?

Desenvolvedores aos quais me refiro são desenvolvedores profissionais de software (analistas, programadores, hackers, o que quer que você queira chamar). São pessoas que se dedicam profissionalmente a entender as milhões de coisas que são importantes no desenvolvimento de projetos de software. Utilizar Rails para eles é apenas se beneficiar de uma boa ferramenta que implementa conceitos de MVC, ActiveRecord, LOP, Domain Model, Meta-Programação, convention over configuration, JavaScript, etc.

Para eles eu recomendo primeiro aprender Ruby. Rails sem Ruby é exotérico demais, você não vai entender como é possível que sua classe ganhe métodos conforme precisa deles e outras coisas estranhas (principalmente se você vem de Java ou C#).

O outro estereótipo, o desenvolvedor de aplicações web, geralmente é umc ara com menos conhecimento técnico, menos interesse em construção de software e habilidades em outras áreas. Pode ser o designer que quer fazer seus projetos com relativa independência de programadores, pode ser o cara que tem um estalo e uma brilhante idéia para uma aplicação Web 2.0 que o fará milionário… O ponto é que desenvolver software para este cara é só uma parte do processo, o meio, e não o fim. Este cara não precisa aprender tantos conceitos, ele pode se basear em receitas prontas e correr para um técnico quando precisar de algo mais heavy-metal. Para este cara eu recomendo aprender diretamente Rails, eventualmente ele pode melhorar Ruby e programação em geral com a evolução do seu projeto.

Interessante notar o conceito que funciona com Rails e com desenvolvimento baseado em Domain-Specific Languages (sendo Rails uma ou não): o usuário não vai desenvolver o software sozinho. Ele se baseia em algo construído para ele por um técnico (seja o framework Rails ou uma DSL) mas não consegue sair muito daquele escopo específico e limitado sem acompanhamento profissional. Este é o objetivo dos pesquisadores de DSLs neste momento.

JustJava 2007

Friday, September 28th, 2007

O JustJava parece que dessa vez vai e eu e o Paulo vamos dar uma palestra dia 04. Devo estar por lá dias 4 e 5 e para variar deve ter o’Malley’s. Abaixo um email de divulgação que rolou nas listas.

> From: Mauricio Leal
> Date: Sep 26, 2007 6:08 AM
> Subject: [noticias-list] As estrelas do JustJava’2007: Paulo Silveira
> & Philip Calcado [Arquitetura e Design de um Projeto Java EE]
> To: noticias-list@soujava.dev.java.net
>
> JUST JAVA 2007 - 6a. edicao
>
> Nos dias 03, 04 e 05 de outubro de 2007 estara acontecendo um dos
> maiores eventos da comunidade brasileira de Java, que teremos diversos
> assuntos apresentados, tais como:
>
> JavaFX, Java EE, Java ME, SOA, Java 3D, Java 7, Web 2.0 e Ajax
>
> Faca ja a sua inscricao no site:
> http://www.sucesusp.org.br/eventos2007/justjava07/
>
> Voce pode verificar a grade do evento no site
> http://www.soujava.org.br/display/v/Grade+de+Palestras
>
> No dia 04/Outubro (quinta-feira) as 10hs, teremos
> ARQUITETURA E DESIGN DE UM PROJETO JAVA EE
>
> Arquitetar e desenhar uma aplicação Java EE sempre foi uma tarefa
> difícil. A decisão entre usar ou não usar os EJBs esbarrava na
> complexidade da especificação 2.x, na baixa produtividade, além das
> sérias limitações dos Entity Beans CMP.
> Com a chegada do Java EE 5.0, em especial do JSF e EJB3, utilizar as
> tecnologias da especificação tornaram-se muito mais atrativas e
> produtivas, além de muitos padrões antigos, como DTOs e BOs, não serem
> mais necessários, mesmo se EJBs fizerem parte da arquitetura.
>
> Nesta palestra veremos as principais arquiteturas que costumam aparecer
> nos novos projetos web: de um simples JSP + controlador + Hibernate até
> rebuscadas escolhas como JSF + Spring + EJB3. Qual a vantagem de cada
> uma? Vale a pena interfacear todos os serviços como WebServices? Devemos
> utiliziar JSon, POX, um modelo Restful ou o bom e velho SOAP?
> Essas e outras discussões serão apresentadas.
>
> *** Apresentando
>
> PAULO SILVEIRA foi instrutor da Sun por 2 anos, é formado em Ciência da
> Computação e possui mestrado na mesma área pela USP, trabalhando em
> diversas consultorias no Brasil e na Alemanha. Atualmente é instrutor e
> consultor pela Caelum.com.br. É editor técnico da revista Mundo Java e
> um dos fundadores do portal GUJ.com.br.
>
> PHILIP CALCADO é líder do RioJUG (Grupo de Usuários Java do RJ), do GUJ
> (http://www.guj.com.br), colunista do Portal Java
> (http://www.portaljava.com.br) e participa ativamente nas comunidades
> Java e Ruby. Programa em Java desde 2003 e já atuou nas áreas de análise
> de risco, previdência privada, gestão de conteúdo, redes de telefonia e
> energia. Participa de projetos open-source e mantêm um blog em
> http://www.fragmental.com.br.
>
> SouJava: Quais são as primeiras considerações que precisamos considerar
> ao começar o desenvolvimento de um Projeto em Java EE ?
>
> Paulo: Existem centenas de opções para o arquiteto Java EE. A maioria é
> apenas mais uma variação do mesmo estilo ou padrão. O maior desafio do
> arquiteto hoje é saber separar a escolha de padrões e conceitos da
> escolha de ferramentas, levando em conta os impactos tecnológicos e
> humanos do desenvolvimento de software.
>
> SouJava: O que os profissionais devem fazer para se tornar um bom
> Arquiteto na tecnologia Java EE ?
>
> Paulo: Conhecer principalmente os conceitos de arquitetura de software
> antes de conhecer ferramentas que implementam estes conceitos e estar
> aberto à inovação dentro e fora da plataforma. Lembrar de que não existe
> a bala de prata.
>

Introduzindo Agilidade num Ambiente

Wednesday, August 15th, 2007

Toda empresa em que eu já tenha trabalhado, atendido como fornecedor ou prestei consultoria até hoje tem o mesmo discurso: esse negócio de [qualquer novidade] é legal mas aqui dentro não funciona. Pode até funcionar nas empresas certinhas, mas aqui nosso negócio é muito específico, nossa tecnologia é muito específica, nossos banheiros são muitos específicos…. Tenho uma notícia para quem pensa assim: não existe empresa certinha. Todo mundo acha que é diferente dos demais.

Ainda bem que no meu trabalho as coisas não são assim. Todo mercado é dinâmico mas o de Internet chega ao ridículo, da noite para o dia todos os paradigmas mudam. Ao mesmo tempo nós somos o braço de Internet de um dos maiores grupos de mídia do mundo e temos que seguir os mesmos critérios de qualidade, agilidade e inovação dos nossos “irmãos” nos canais de TV, Jornais e Rádios.

Quando fui contratado um dos requisitos era exatamente trabalhar o processo de desenvolvimento da empresa. Não somos diferentes de ninguém, temos um processo que foi criado e adaptado por sete anos e está longe de ser perfeito, como em qualquer lugar padrão. Após algum tempo observando como as coisas funcionam começamos a introduzir mudanças.

Somos uma equipe grande (incluindo alguns colegas blogueiros) que tem como trabalho cuidar de diversos sistemas entre sites, ferramentas de edição e produção de conteúdos e utilitários diversos.

Antes da mudança que introduzimos nosso fluxo de trabalho era o mais caótico possível: alguém enviava um e-mail com algo que estava errado ou precisava ser feito. Este e-mail originava uma tarefa num Gantt Chart. Uma das minhas tarefas diárias era correr atrás da equipe com o cronograma na mão perguntando o que estavam fazendo, depois sentar na minha mesa e atualizar o cronograma. Cada desenvolvedor pegava uma tarefa, ocasionalmente dois dividiam uma mais complexa. Ao terminar o desenvolvedor enviava um pacote para a equipe de testes (nenhum teste além do eventual feito pelo desenvolvedor era feito por aqui) que testava e homologava. Eventualmente o pacote voltava e ia para produção.

A primeira providência foi acabar com o cronograma. Nós temos uma licença do Jira, que apesar dos seus problemas funcionais é um dos melhores issue trackers disponíveis. O fluxo mudou: agora o cliente enviava um e-mail, este entrava no Jira e era designado para a pessoa que dava um prazo para a solução.

Dentro os diversos problemas que ainda tínhamos era colocar coisas em produção. Para homologar, agendar para subir em uma janela, acompanhar a janela, etc etc perde-se muito tempo e a cada tarefa concluída o ciclo era repetido. Solução? Só subir software a cada 15 dias. Em vez de mandarmos 5 ou 7 pacotes por todo o processo mandávamos apenas um e desta forma economizamos muito tempo. Eventualmente conseguíamos fazer mais coisas por parar menos com distrações do processo.

O próximo problema era no mundo das prioridades. Antes alguém mandava um e-mail e olhávamos para ele. Se fosse algo muito grave no nosso ponto de vista (ou se o cliente ligasse reclamando) nós colocávamos a alteração no topo da lista, caso contrário ia para quando “tivéssemos tempo” (i.e. nunca). Obviamente o cliente não ficava satisfeito com essa priorização e nós perdíamos muito tempo trabalhando no que era importante para nós sem saber se era importante (ou mesmo útil!) para o cliente ou não.

Solução? Jogo do Planejamento. Temos diversos clientes internos (e alguns milhões de externos) então elegemos o grupo que realmente representa nossos clientes. Em reuniões semanais de uma hora nós passávamos todas as issues abertas (depois da primeira reunião isso foi rápido) e agendamos possíveis subidas para estas. O cliente pode alterar qualquer iteração, menos a que está em execução. Quando o sistema vai para o ar o cliente recebe uma lista com todas as modificações que subiram para testar e mandar o feedback.

Estava melhor mas ainda não era o ideal. Nossos projetos ainda eram planejados em extensos Microsoft Projects que em algumas semanas perdiam sua sincronia com… o mundo real. A visão era de que o esquema de Jira/Jogo do Planejamento/Release de 15 dias funcionava bem para a manutenção mas não para projetos.

Então mudamos algumas coisas. Após quatro membros da equipe participarmos do curso de Scrum da Teamware nós resolvemos colocar em prática alguns conceitos, melhorando o processo utilizado para manutenção de maneira que servisse para nossos projetos.

Colocar em prática? Como assim?!? O projeto já possui cronograma definido, equipe definida, o escopo vêm sendo discutido há meses! Por que não esperar uma boa oportunidade, com tudo calmo? Simples: porque ela não vai acontecer. Qualquer empresa que queira se manter competitiva vai estar sempre bolando algo novo e mantendo suas pessoas ocupadas com isso. Oportunidade não se espera, se cria.

Para minimizarmos o risco da adoção nós introduzimos as coisas aos poucos. O foco não era implantar o processo X ou o processo Y mas sim coisas que nos ajudassem. O Scrum é um bom molde, um bom guideline. Vamos tentar implantar o molde, se virmos algo melhor ou ruim no caminho adaptamos. Vamos seguir, devagar quando necessário, mas movendo para frente.

Primeiro precisamos diminuir a importância do Jira em favor de post-its. Issue trackes são ferramentas bem interessantes, com o passar dos releases fizemos os usuários aprenderem a abrir issues no jira e acompanhar o andamento por ele (muito menos telefonemas de ’status report’!). Um dos problemas da ferramenta era presença. Para termos o Jira numa reunião precisamos de um notebook e nem todos poderiam utilizar a ferramenta ao mesmo tempo. Outro problema era a granularidade das tarefas. Outro era que cada um tinha sua lista de issues para resolver então eu passava horas (eu diria quase que 8) todo santo dia priorizando todas as issues abertas para cada desenvolvedor.

Picture 001

Para os desenvolvedores deixar o Jira um pouco de lado está sendo simples. Eles já não atualizavam o status dele mesmo! Eu tinha que encher o saco diariamente. Para os outros interessados no andamento do projeto não, após termos ensinado a todos a utilizar a ferramenta falar para olharem para uma outra não seria muito bem recebido. Além disso, muitos dos nossos clientes estão em outros prédios da empresa e não conseguem olhar nosso quadro. A solução encontrada foi mantêr as user stories no Jira, mas não as tarefas.

As user stories passaram a ser blogs. Quando alguém enfrenta uma dificuldade e resolve o problema ele “bloga” um comentário, mas não chega nem a atualizar o status. O uso passou a ser muito mais documental e menos de visão de status. Quando alguma story atinge o status de ‘done’ eu atualizo o status no Jira. A documentação não é tão util, já temos um wiki com especificações técnicas (segundo princípio de Agile Modeling, geralmente fotos dos whiteboards e descrições de alto nível apenas) e estou louco para me ver livre da obrigação de sincronizar com o issue tracker todo dia.

Um dos problemas é que não tínhamos espaço disponível. O escritório oferece uma boa disposição de mesas em baias de 4 pessoas, mas não há paredes disponíveis para pregarmos cartazes.

Picture 009
(desconte a poluição visual da minha mesa)

A solução imediata foi pregar o cartaz com os post-its num rack de servidores para desenvolvimento que fica no corredor.

DSC00460

Hoje já pegamos um antigo laboratório para nossas reuniões mais importantes, mas ficamos um bom tempo apenas com o rack e ainda o utilizamos. Salas de reunião ou mesmo o laboratório são fechadas e os cartazes lá dentro não estão 100% do tempo visíveis. Seguimos com o planejamento do sprint atual no rack e os outros cartazes e artefatos no laboratório. Antes que alguém pergunte tudo é registrado em câmeras digitais e como já me reportou uma vez o Vinícius sobre suas experiências as fotos nunca foram usadas além de decorar Flickrs e 8Ps.

Então todos os dias nos reunimos em frente ao rack para nosso Daily Scrum, uma reunião rápida diária. Um dos problemas que as pessoas sempre levantam para fazer este pequeno “compartilhamento de status” é a hora. Fazer muito cedo as pessoas podem não chegar, muito tarde não vai ser produtivo… bem, aqui a coisa é bem pior. Nossa equipe não exige a priori nenhuma restrição de horário, trabalhamos com pessoas que criam software e soluções, não com recepcionistas que precisam atender o telefone. Como consenso decidimos que as 11 da manhã todos estaríamos aqui. Temos ainda problemas, alguns dias falta um ou outro mas no geral vamos bem.

DSC00462

A reunião dura quase que exatos quinze minutos, para evitar que as pessoas se dispersem precisamos conversar algumas vezes e explicar que o foco é nos objetivos, não no desenrolar das tarefas. Outro ponto foi esclarecer que a equipe se reporta a ela mesma, não a mim. Eu viajei bastante nas últimas semanas e eles precisaram tocar sozinhos, o que fizeram com maestria.

O projeto já estava com as entregas definidas e renegociar não era uma opção. Infelizmente não conseguimos fazer reuniões de priorização com o cliente mas o responsável pela definição de negócio senta aqui do lado e nossos Sprint Backlogs, a lista de coisas que implementaremos a cada Sprint, já estavam bem definidas, com pouca margem para reagendamento (que foi devidamente utilizada).

Para fazer o pouco de planejamento que restava nós nos trancamos numa sala e estimamos todas as tarefas. Nas primeiras iterações mesmo explicando que um Story Point é dado por relatividade (primeiro você acha o mais simples depois estima os outros usando da métrica do “o quanto mais difícil é fazer X do que fazer aquele mais fácil?”) o time surgiu com valores quase que aleatórios para as tarefas, basicamente marcaram um número muito alto para todos. Nas iterações seguintes as estimativas foram ficando mais reais e hoje todos fazemos piada dos valores originais. Descobri que incluir uma reavaliação das estimativas na reunião de retrospectiva pós-sprint é útil para termos um melhor critério.

Na reunião de planejamento de Sprint nós deveríamos fazer uma versão inicial das listas do que precisamos fazer (tarefas) para atingir o objetivo (user story) de cada item do Sprint Backlog. Com medo de introduzir muita coisa nova eu optei por fazer apenas a lista da primeira Story. No final isso se mostrou irrelevante.

DSC00455

A empresa possui seu fluxo de processo já estabelecido e precisamos fazer nosso processo ser compatível com o das outras equipes, que inclui um grande teste de homologação no final. Antes das mudanças o desenvolvedor não executava teste algum, a equipe de QA era a responsável. Com o tempo percebemos o quanto isso era trabalhoso e propenso à erros. Uma das soluções que buscamos é ter testadores na nossa equipe mas enquanto isso não acontece nós mudamos a forma de pensar.

Lá no início da jornada o Guilherme e eu introduzimos testes unitários com JUnit no build. Demorou um pouco mas fatalmente as pessoas começaram a fazer (e as palestras do Guilherme fizeram com que pessoas de outras equipes começassem a fazer testes unitários por si só!). Quando o Bruno entrou na equipe, antes do projeto, uma das primeiras tarefas que dei para ele foi configurar o CruiseControl para fazer build e executar testes unitários. Mais problemas: não havia um servidor disponível para instalar. Conversa aqui, conversa ali conseguimos espaço num servidor de testes que roda diversas máquinas virtuais de VMWare, instalamos Ubuntu e o dito cujo (hoje usaria o Buildx), pronto. Após o CruiseControl o Bruno tratou de integrar o Fit ao nosso processo para fazermos testes de integração, e rodando com o CruiseControl.

Como ninguém tinha experiência com o Fit, o Bruno trabalhou em par com quem pegava a tarefa de implementar os testes. Com uma semana as pessoas estavam escrevendo Fixtures razoáveis, mesmo os estagiários.

Outro ponto que precisávamos testar era a interface, quase sempre web. Avaliamos o Selenium e mesmo com limitações ele parecia razoável mas não ideal. O Tiago investiu um dia integrando o Selenium no nosso build e trabalhando com test-cases em XSTL e fez o mesmo esquema que o Bruno para passar conhecimento. Agora nosso desenvolvimento só está “pronto” quando o código foi criado, com JUnit, com testes no Fit e com Selenium. O último QA achou apenas um bug em um módulo que alguém esqueceu de testar com caracteres especiais.

No primeiro Sprint eu não fiz reunião de revisão. Foi um erro cometido pela pressa mas imperdoável. No segundo nós fizemos uma reunião extremamente útil, levantamos pontos bons e ruins e colocamos num quadro (cuidado, termos fortes abaixo. ambiente irreverente é assim mesmo):

Picture 029

A maior parte dos pontos ruins (no “nem F$#%endo”) eram interrupções externas que eu preciso trabalhar. Minha viagem (que era um dos pontos ruins) recente fez com que os desenvolvedores respondessem por compromissos meus (outro ponto: reuniões que duram dias inteiros) , além de deixá-los sem proteção de interrupções externas (outro ponto citado) como telefonemas e até suporte a outras equipes integrando com nossos sistemas. Tirei daí que se precisar me ausentar preciso deixar alguém como Scrum Master, ainda que o time perca um membro ativo. Diluir o papel de Scrum Master causa mais prejuízos do que tirar uma pessoa do time de desenvolvedores.

Os pontos positivos incluíram o trabalho em equipe, os mecanismos de teste que ajudaram no desenvolvimento, o QA não ter encontrados bugs, a janela para produção ter sido tranquila e outras coisas que basicamente derivam da nova maneira de trabalhar.

Dos pontos ruins o que me preocupou mais foram os referentes a plataforma tecnológica da aplicação. Fizemos uma mudanças grande na direção de uma arquitetura SOA e como qualquer empresa não temos tempo de treinar todo mundo rapidamente.

O desenvolvimento desta arquitetura foi feita basicamente antes do projeto iniciar. Eu incubi o Guilherme de levantar possíveis soluções para nossos problemas de integração e esboçamos o projeto dos WebServices com tudo que tínhamos de melhor, incluindo REST. O primeiro uso desta infra-estrutura foi num projetinho bem curto que realizamos no início do ano e ela se mostrou muito boa.

O problema foi que neste projeto apenas o arquiteto (o próprio Guilherme) e eu metíamos a mão neste sistema. Quando começamos o projeto maior os conceitos ali dentro precisavam ser espalhados. Na primeira iteração com algumas cabeçadas conseguimos um desempenho bom mas ainda não o suficiente. Também vimos alguns pontos onde a arquitetura estava diminuindo muito a produtividade das entregas e que a introdução dos conceitos de WebServices REST, Hibernate/JPA, caches distribuídos e outras coisas menores precisa ser feita com mais cuidado. Algumas pessoas aprendem rápido e sozinhas, outras não e isso não tem a ver com habilidade técnica e sim com perfil (coisa, aliás, que defendo aqui desde sempre).

A solução que pensamos foi adicionar como tarefa um workshop arquitetural. Eu não queria cometer o mesmo erro e trancar o projeto na mão de um membro do time apenas (ou alguns), então nos reunimos durante a tarde. Na primeira parte da reunião o Guilherme explicou a arquitetura do início, mesmo para quem teoricamente já sabia foi útil.

Picture 019

Para estruturar a coisa eu pedi ao Guilherme quatro visões: a estrutura lógica (componentes), a estrutura física (servidores, bancos de dados, etc.), a de pacotes (como os módulos são divididos) e a de caso de uso (descrição do que acontece numa user story relativamente complexa do início ao fim).

Depois nós realizamos um exercício que aprendi no curso da TeamWare: cada um recebeu um bloco de post-it e uma caneta. Por cinco minutos escrevemos tudo que atrapalha nossa produtividade rapidamente, se alguém não pensasse em nada escrevia “nada” no papel. Depois discutimos cada ponto destes sorteando um de forma aleatória. Da forma escolhida para sortear surgiu o nome interno da técnica: a Dança do Siri (derivado da maneira “curiosa” de obter os papéis pelo Tiago):

Picture 027

Houveram poucos pontos técnicos, a maioria de infra-estrutura. Neste meio surgiram também coisas de processo como o papel dos estagiários no time. Um dos pontos levantados é de que nosso time não é tão homogêneo (e qual é?) e nem todos conseguem trabalhar em todas as partes do sistema. Uma estratégia que definimos foi de fazer pares quando ocorrerem estes casos e se tudo mais falhar partir para outra tarefa. O importante é quebrarmos os feudos de sistemas.

Todas foram discutidas, os pontos cabíveis foram anotados e mantivemos o histórico:

Picture 028

Na reunião para a entrega do primeiro pacote tínhamos um problema: descobrimos um erro no cronograma inicial (criado antes do projeto iniciar e antes de adotarmos o processo novo) que iria atrasar o projeto em oito dias. Começamos a reunião com uma descrição do processo de desenvolvimento para os clientes (aqueles que não participam no dia a dia do produto), revisão dos fatos ocorridos que desencadearam no atraso, o que faríamos para voltar ao rumo e a demonstração das funcionalidades. Para nossa surpresa a resposta foi excelente. Os clientes elogiaram a transparência do desenvolvimento, elogiaram o fato de verem algo em produção tão rapidamente e terminamos tendo que responder a uma pergunta: mas por que não fizemos assim antes?

Nós não implementamos o Scrum por completo (me viu falar em burndown chart aqui?), ainda falta bastante. Também não conseguimos eliminar problemas de prazo, tivemos que trabalhar algumas horas extras no último Sprint e possivelmente neste também. Algumas pessoas não se adaptaram aos novos processos e deixaram a equipe. Mas o importante aqui é que elevamos a qualidade do nosso trabalho consideravelmente -a ponto do cliente perceber- e as pessoas estão felizes em trabalhar desta forma, tanto as de fora quanto as de dentro. Paramos de correr atrás do prórpio rabo resolvendo problemas aleatórios diários e estamos trazendo resultados.

Engraçado que outro dia alguém falou comigo no corredor “Poxa, tô sabendo que vocês estão indo muito bem no projeto, heim? Perguntei pra Fulana -uma cliente- e ela disse: ‘É, eles estão gerenciando na linha dura desta vez, estilo sargentão’”.

Será?
Picture 023

Construindo Expressividade com Linguagens Elegantes

Friday, July 27th, 2007

Ainda não vi uma definição sobre o que seria uma linguagem elegante, então lá vai a minha:

Uma linguagem elegante é baseada em primitivas simples e extensíveis e, principalmente, sem muitas exceções.

Segundo esta definição Ruby, Smalltalk e LISP são elegantes. Java é um pouco, C# com suas centenas de exceções (sobrecarregar + é diferente de sobrecarregar [], dentre outras várias exceções) não é.

Uma linguagem elegante é simples e compacta. É de se esperar que não tenha sobrecarga de operadores, mas isso não é verdade. O problema das pessoas com sobrecarga de operadores é porque elas vêem isso como uma quebra da rule of least surprise. Essa regra diz (implora, eu diria) para não surpreendermos nossos usuários modificando o mundo que eles já conhecem para que uma ação traga consequências não esperadas. Por exemplo imagine que você vai modificar um programa como o ls para dar mais performance (uau, você é bom!). Faça o que quiser mas não mude os parâmetros de entrada e saída ou você vai quebrar um zilhão de programas e scripts pelo mundo todo. Caso introduza uma feature nova faça o usuário explicitamente solicitar por ela (passando um flag no estilo ls -lira –my-fancy-new-feature) e mantenha o default como o antigo.

E onde entra sobrecarga de operadores nisso? Lá no seu primeiro curso de programação (ou no seu primeiro livro se você opta pelo caminho mais difícil) deve ter aprendido sobre literais, variáveis, condicionais e… operadores. Operadores são caracteres especiais com uma função bem definida na linguagem, por exemplo o operador de divisão, de resto, de adição…

Em Java, quando você chama:

String texto = "abc".toUpperCase();

É fundamentalmente diferente de quando chama:


int numero = 12 + 24;

Porque um método e um operador são coisas diferentes e você aprendeu a pensar desta forma. Se amanhã eu puder sobrescrever o operador + para que escreva algo na tela você vai se surpreender porque não era isso que o manual da linguagem te disse.

Ainda assim, existem muitas ocasiões onde sobrecarga de operador são mais que convenientes, são semânticos. Imagine o exemplo do nosso BigDecimal:


BigDecimal a = new BigDecimal("100");
BigDecimal b = new BigDecimal("300");

Some os dois. Ok, você é um programador Java esperto e sabe que vai ter que fazer algo como:


BigDecimal resultado = a.add(b);

Mas disso até me dizer que é melhor que usar ‘+’ é outra história.

Mas e a simplicidade? Não é legal termos um conjunto pequeno de operadores que faz coisas previsíveis e que servem na maioria das vezes? Sim, é! Mas num mundo onde tudo migra para mais expressividade, com Model-Driven Development e Domain-Specific Languages ter este tipo de limitação não é interessante. As melhores linguagens para construir DSLs são as mais flexíveis e por acaso as mais elegantes, que coincidência, não? E como Ruby, que se enquadra nestes dois aspectos, lida com isso?

Simples. Em Ruby não existem operadores, pelo menos não como você está acostumado em Java, operadores são apenas apelidos para métodos. A vantagem disso é que se muda o modelo mental, a partir do momento que você sabe que um operador e um método são a mesma coisa você tem com operadores o mesmo cuidado que tem com métodos. Ao chamar add() do BigDecimal você tem que saber se o método vai retornar o resultado como um objeto separado, se vai modificar os objetos passados como parâmetros, etc. Como você faz isso? Lendo a documentação até se familiarizar com a classe e seu idioma. Após familiarizado você começa a usar sem pensar.

E quando você espera estar lidando com uma classe que sobrescreve um operador e na verdade recebe como parâmetro uma subclasse dela? Sem problemas! Se o autor seguiu os princípios básicos da Orientação a Objetos, que derivam do Design by Contract e o Princípio de Substituição de Liskov a subclasse tem que obedecer o contrato da classe-pai, e se você obedeceu os mesmos princípios não precisa de nada que não esteja no contrato.

Esse tipo de resistência com funcionalidades é o tipo de coisa que se elimina aprendendo várias linguagens e estudando suas motivações. Existem algumas linguagens como as citadas que merecem ser estudas ainda que você nunca as vá utilizar de fato. Elas abrem a sua cabeça.

Model-Driven Development é Durepoxi

Friday, July 27th, 2007

O Paulo Vasconsellos lá do finito ensaiou sua opinião sobre como código não pode representar a documentação sobre um processo de negócios. O post que originou a conversa é bem fraquinho, mas o ponto é bom.

Basicamente a idéia é que não dá para confiar no código porque ele possui o que Joel Spolsky catalogou como ‘leaks de abstração’ em um dos seus artigos mais citados. ~Tenho que admitir que isso pode ser verdade na maioria dos casos mas apenas porque as pessoas ainda não aprenderam o conceito simples de separação de responsabilidades, e isso é muito pior na cultura .Net que é a do autor do artigo original.

Imagina que temos um processo de negócio automatizado num software OO. A coisa é simples, o clássico e onipresente exemplo de adicionar usuários a um grupo:


void adicionarUsuario(Usuario u, Grupo g){
g.adiciona(u);
}

E como estamos falando de um software OO as regras de negócio estão nos respectivos objetos (grupo e usuario) e não nesse método de serviço. Muito bem, eu consigo gerar uma documentação em fluxograma, UML, o que for desta sequência simples de instruções imperativas. O problema é que software não é simples assim.

Eventualmente nós precisaremos persistir a alteração feita no banco de dados.


void adicionarUsuario(Usuario u, Grupo g){
g.adiciona(u);
repositoriousuarios.atualizar(u);
}

E possivelmente precisamos saber se o usuário logado possui direito a fazer esta alteração, esse é um requisito não-funcional de segurança. Adicionar um log também é útil para auditoria.


void adicionarUsuario(Usuario modificador, Usuario modificado, Grupo g){
if(podeAlteraroutrosUsuarios(modificador){
g.adiciona(modificado);
repositoriousuarios.atualizar(modificado);
logger.info("Alterou");
}
else{
throw new IllegalQualquerCoisaException();
logger.error("Tentou alterar");
}
}

E isso pode piorar ainda mais. Agora utilize o código acima como documentação de negócios por favor. Mostre ao seu cliente e peça para ele tentar te explicar. não dá, né? E por quê? Porque cada uma destas coisas são conceitos ortogonais, que deviam ser implementados por técnicas de AOP e não embolados no meio do código de negócio.

Domain Driven Design possui a modelagem de negócios no código como sua essência. Um dos pontos que mais causam dúvidas, por exemplo, é o da diferença entre Repositórios eDataMappers (DAOs no mundo Java EE). A diferença é que eu coloco um repositório num diagrama de negócios (ainda que chame de outra coisa) mas não coloco um DataMapper . E por quê? Porque a responsabilidade de um Repositório é referente à objetos que mapeiam o negócio enquanto a responsabilidade de umDataMapper é transformar objetos em tabelas (ou coisa do gênero) e objetos ou tabelas não são conceitos de negócio. Assim ainda que um DataMapper implemente um Repositório eles são tratados sob pontos de vista bem diferentes para análise de sistemas.

No passado nós tínhamos uma discrepância enorme entre os modelos de negócio e o modelo físico do software. Há mais de vinte anos que não há motivo para esta diferença existir, exceto uma cultura dominante. O problema, como sempre, está nas pessoas.

Model-Driven Development, conjunto de idéias que Domain-Driven Design faz parte, é exatamente sobre isso. Por enquanto nós ainda precisamos educar os desenvolvedores para que usem as ferramentas de maneira correta, num futuro próximo eu espero que Domain Specific Languages poupem este trabalho na maioria dos casos.

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.

Falando novamente

Monday, June 25th, 2007

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

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?

“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!