Archive for the ‘engenharia’ Category

Analista Pedreiro

Saturday, August 9th, 2008

Meu post sobre Análise de Sistemas já teve 65 comentários (incluindo respostas, é claro). Acho que já descartei uns 20 comentários anônimos para este. Nem todos falavam da minha mãe, alguns tinham coisas interessantes mas infelizmente este blog não aprova comentários anônimos, sejam criticas interessantes ou xingamentos.

Esta manhã eu recebi um destes anônimos interessantes e, apesar de não o aprovar, gostaria de comentar trecho deste:

Francamente modelar com o código é o mesmo que fazer um projeto de uma casa com tijolo e cimento.

Este trecho me despertou o interesse porque há algum tempo fiz um experimento e gostaria de convidar vocês para repeti-lo. Como falei em posts anteriores Melbourne, minha cidade atual, é um lugar razoavelmente conhecido por suas boas universidades e centros de pesquisa. Como conheço alguns estudantes de graduação ou recém-formados (A ThoughtWorks possui um programa para contratar graduandos) eu conheci algumas pessoas que lecionam em universidade locais, entre eles uma simpática senhora que ensina disciplinas num curso de arquitetura e edificações local.

O dialogo foi sobre especificações, plantas e modelos.

- Imagine que a senhora pudesse, ao invés de criar um modelo em papel, construir seu projeto incrementalmente. Em dois dias a senhora tem o banheiro pronto, em três dias a varanda.
- Como numa maquete?
- Não, eles estão prontos mesmo. Você pode usar o banheiro, pode dormir no quarto, pode efetivamente utilizar o prédio.
- Mas e se eu precisar mudar algo? Isso iria ficar muito caro.
- Imagine que o custo é apenas o de dois dias de trabalho. Sem custo material. A cada dois dias você termina um pedaço da casa e mudar é tão simples quanto construir.
- Ok. Seria ótimo. Qual seu ponto?
- Isso ainda afaria requerer uma planta baixa, um modelo em papel ou AutoCAD?
- Precisaria porque eu preciso comunicar o projeto aos trabalhadores.
- Ah, sim, claro, mas imagina que construir não seja um trabalho braçal. Você tem, sei lá, robôs que sobem paredes e fazem o trabalho pesado, você só diz qual a dimensão, inclinação, que material, etc. e eles constroem. Você desenha no AutoCAD e os Robôs constroem imediatamente, daí você pode entrar no prédio e se tem algo errado em segundos você conserta. Você pode levar seu cliente para andar no prédio.
- Ok. Mas neste caso você não está eliminando o modelo gráfico.
- Não?
- Não, neste caso você automatizou a construção. O que você fez foi fazer com que o trabalho do arquiteto seja simplificado eliminando o custo da construção. Se qualquer coisa que eu mudar no modelo muda no prédio real imediatamente e sem custo o papel do modelo é ainda mais importante. Você não tem real diferença entre modelo e prédio. Você não tem transformação real entre um e outro. O modelo é o prédio.

Deste ponto eu comecei a explicar para ela como engenharia de software funciona. A conclusão que nós chegamos é que engenheiros de software possuem o poder que falta para engenheiros civis/arquitetos e ainda assim usam as ferramentas de quem não tem este poder.

Arquitetos adorariam modelar com tijolos, paredes e coisas reais. Eles não podem. Nós podemos.

Uh-Éme-Éle

Friday, 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.

Agile Software Deployment?

Friday, 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

Thursday, 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

Nem só de troca de mensagens vivem os objetos

Sunday, 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

Thursday, 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.

Trilha de Livros: Desenvolvedor

Tuesday, May 20th, 2008

Esta então é a prometida lista de livros para desenvolvedores. Claro que não é nenhuma lista exclusiva ou “o guia definitivo”, apenas minha recomendação de leitura, partindo do principio que você já sabe programar em uma linguagem como Ruby, C# ou Java.

Este guia é bem genérico, sem tentar se especializar em nada e sem tentar abranger mais que o mínimo necessário. Espere modificações nesta lista.

  1. Operating Systems: Design and Implementation: Este ode não ser o melhor livro sobre Sistemas operacionais –ou pelo menos não o mais didático- mas eu gosto bastante. A maioria dos conceitos básicos de um Sistema Operacional está presente mesmo nas máquinas virtuais e seja como for, antes de abstrair você precisa entender como seu computador funciona. Claro que se você cursou SO na faculdade e, principalmente, se lembra de como memória virtual, filesystems e demais funcionam pode passar por este item –eu acho que uns 5% dos desenvolvedores que conheço se enquadram nisso, entretanto.
  2. Fundamentals of Object-Oriented Design in UML: Este livro ensina métricas e princípios básicos para desenvolvimento de sistemas Orientados a Objetos. Se você passou por projeto estruturado provavelmente conhece o autor e algumas de suas métricas.
  3. Head First Design Patterns: Uma introdução suave aos Design Patterns. A vantagem em começar com este ao invés do clássico (que é o próximo na lista de qualquer modo) é que você não tem que lidar logo de cara com Smalltalk e C++. Aprender um conceito não-tão-simples quanto Design Patterns enquanto tenta entender uma sintaxe fora do dia-a-dia é criar complexidade acidental.
  4. Design Patterns: Elements of Reusable Object-Oriented Software: Apesar de não ser indicado ao iniciante eu não acredito que voc6e consiga ir muito longe sem ler este livro. Pelo menos as narrativas são fundamentais para entender as motivações e a evolução do conceito de patterns. A falta desta leitura faz com que pessoas cometam erros grotescos.
  5. Agile Software Development, Principles, Patterns, and Practices: (em versão Java ou .Net) este livro traz uma visão bem pratica sobre alguns aspectos mais teóricos da Orientação a Objetos. Como u organizo pacotes? Qual o problema em ter dependências e como me livro delas? Devo sempre retornar null? O que significa herança na pratica?
  6. Refactoring: Improving the Design of Existing Code: Conforme você for entendendo mais sobre design de software vai sentir uma vontade enlouquecedora de apagar todo o seu sistema e começar de novo. Antes de sair por aí cometendo carreiracídio leia este livro, ele vai te ensinar a fazer pequenas mudanças que melhoram a qualidade do sistema e identificar código que fede.
  7. Patterns of Enterprise Application Architecture: A maioria dos patterns que você teve contato até aqui tratam de design em um nível micro. Como classes interagem, como elas colaboram e como gerenciar seus problemas. Este livro, entretanto, fala sobre padrões em um contexto mais amplo, sobre arquitetura de software.
  8. Domain Driven Design: Os livros até então falam de sotware pelo software. Como criar uma classe, como gerenciar dependências entre classes… mas ninguém te mostrou o que deve ser uma classe e o que não deve. Eric Evans supre esta demanda mostrando uma abordagem simples e eficiente para criar domínios que se aproximam do mundo real.
  9. POJOs in Action e/ou Applying Domain-Driven Design and Patterns: With Examples in C# and .NET: É normal que exista uma certa dificuldade em levar estes conceitos para o dia-a-dia. Estes livros não são indispensáveis mas eles ajudam bastante a entender como aplicar conceitos, ainda que algumas vezes de maneira “não canônica” mas ainda assim eficaz.
  10. The Pragmatic Programmer: From Journeyman to Master: Infelizmente muitas vezes, especialmente em consultorias de três letrinhas e faculdades McDonald’s, não temos um ambiente sadio para nos ensinar como programadores profissionais se comportam. Este genial livro traz uma boa parcela deste conhecimento condensado. Eu adicionaria o The Art of UNIX Programming, especialmente para aqueles que vêm de uma cultura drag’n'drop para o mundo real
  11. Ship it! A Practical Guide to Successful Software Projects Este livro é bem interessante para entender como um desenvolvedor utiliza ferramentas simples como controle de versão e issue tracker. Ótimo par aquém está profissionalizando uma empresa.
  12. Agile and Iterative Development: A Manager’s Guide Antes de você se dedicar a estudar mais uma metodologia de desenvolvimento em específico uma boa idéia é ler este livro, que traz um apanhado de diversas metodologias e as compara.

Objetos não são atributos + funções

Sunday, May 18th, 2008

Quando simplificamos demais ao ensinar conceitos acabamos criando problemas que demoram anos para se resolver, se é que são resolvidos. Um exemplo que eu observei hoje foi como algumas pessoas pensam sobre objetos.

É comum que seja ensinado no primeiro contato das pessoas com orientação a Objetos que objetos representam atributos e funções embaladas no mesmo saco. Bom, isso é uma simplificação extrema utilizada para fazer com que quem conheça programação procedural trabalhe com objetos de maneira mais simples, mas é uma simplificação tão grosseira que atrapalha algumas pessoas para o resto da vida.

Objetos não possuem propriedades + funções, eles possuem estado + comportamento. Você provavelmente vai usar atributos (i.e. variáveis de instancia) e funções (de instancia também) para implementar isso mas é um detalhe de implementação, não algo que deva ser exposto.

Um agravante deste problema são propriedades. Sejam propriedades bem resolvidas como as do C# ou meia-boca como do Java, elas representam apenas mais uma mensagem que o objeto recebe (já que ambas as linguagens são baseadas em troca de mensagens) e não “atributos” do objeto.

get/set em Java são herança de um modelo de componentes. Componentes não são necessariamente objetos, o que a (finada?) especificação JavaBeans fez foi utilizar classes Java para implementar componentes gráficos. Como não havia metadados (annotations em Java) naquela época o melhor era adotar uma convenção, daí os setXxx e getXxx da vida.

O problema é que este idioma saiu do controle. Ao invés de utilizarmos um método que faça sentido dentro do domínio preferimos criar um get/set burro, típico de arquiteturas BOLOVO.

Por exemplo, suponha que você possua uma classe Pedido que possui um status. Quando alguém vai atualizar o status do pedido para “finalizado” qual idioma é mais comum?

//1) set/get
pedido.setStatus(Pedido.Status.FINALIZADO);

//2) Mensagem
pedido.finalizar();

Creio que você concorda comigo que o primeiro é de longe mais utilizado. Qual o problema com ele? Como dissemos antes, Java é uma linguagem baseada em troca de mensagens. Uma mensagem é algo que o objeto recebe e decide o que fazer, no caso de Java isso acontece quando chamamos um método. Quem deve decidir o que deve ser feito não é o cliente e sim o destinatário da mensagem. Quando você diz setStatus(Status) você está dizendo para a classe que quer que os status dela seja o especificado. Pensa bem, é isso que você quer?

O que você quer em verdade é que o pedido seja finalizado. Como isso é feito, se muda status ou não, não é papel do código cliente, é papel do objeto (e deve ser documentado, especificado e verificado através de testes unitários).

Da mesma forma, quando você executa um getStatus() no objeto o que você faz com isso? Exceto em alguns casos específicos, um método getXxx() só deveria ser utilizado porque você precisa executar algo com ele que não vai envolver mudança de estado, como por exemplo exibir numa interface gráfica. Se você faz um getXxx() porque baseado no retorno você chama uma lógica ou outra provavelmente você está fazendo algo errado, já que é uma violação gritante da Lei de Deméter.

O que eu deveria fazer, então? Suponha que eu tenha o seguinte código:

public void notificarMudancaEm(Pedido p){
 if(p.getStatus().equals(Pedido.Status.FINALIZADO){
     servicoEmail.enviamensagemConclusao(p);
 }
}

Isso parece bem razoável, não? A lógica de enviar um e-mail de conclusão não está no Pedido e sim em algum serviço próprio para isso. Ele provavelmente é um observador do pedido e cada vez que você chama setStatus() esta é notificado. Num caso real recente eu estava trabalhando numa aplicação legada com o código exatamente como o acima. De repente alguém decidiu que um Pedido também está finalizado se estiver em status CANCELADO.

Essa é a hora em que o programador xinga a mãe do analista de negócios e vai caçar um email de três meses atrás que contem uma documentação, assinada em sangue, que contraria isso. O gerente de projetos vê aquilo, dá um sorriso e manda um email para o cliente daquela consultoria de três letrinhas dizendo que é uma mudança de escopo e que vai precisar de 50 horas a mais para isso. Ele diz ao programador que na verdade só tem 5 horas porque as outras 45 ele vai usar como desculpa quando o projeto atrasar. De quem é a culpa?

Bom, de quem é a culpa é assunto para outro post mas eu te garanto que não é da Orientação a Objetos. O problema no código acima (e certamente este código está repetido em milhões de outros lugares) é que ele não cumpre o princípio básico de deixar o objeto ser responsável pela sua própria vida. Você precisa saber se o pedido está finalizado mas ao invés de perguntar isso ao objeto você pergunta qual seu status.

Imagine que ao invés do código acima você possuísse algo mais… OO:

public void notificarMudancaEm(Pedido p){
 if(p.isFinalizado()){
     servicoEmail.enviamensagemConclusao(p);
 }
}

E suponha que você tenha que alterar o que FINALIZADO significa. Qual é mais fácil?

É este tipo de coisa que você perde ao transformar objetos em atributos+funções. Ao invés de pensar “quais são os atributos deste objeto?” pense “quais mensagens este objeto responde?”.

Mais sobre o Australian Architecture Fórum – Melbourne

Saturday, May 17th, 2008

Com mais calma agora, volto a falar do evento. O fórum foi patrocinado pela IASA, que possui um capítulo bem forte em Melbourne. A idéia é prover mais mesas redondas do que apresentações, minha palestra foi uma das três únicas que não seguiam este formato.

Eu cheguei atrasado e fui direto para uma mesa redonda sobre REST x SOAP. O apresentador/moderador era claramente tendencioso a favorecer REST e, apesar de adorar essa arquitetura, eu acho que isso foi uma falha grave. A parte boa foi que diversas pessoas tentaram defender SOAP usando argumentos bem interessantes. Nenhum deles me convenceu inteiramente mas me fizeram pensar sobre alguns cenários onde usar REST seria reinventar SOAP, ainda que mais coeso.

Minha apresentação foi bem interessante, eu não sei estimar tempo de palestras em inglês ainda e acabei usando apenas 30 minutos dos 50 esperados. Isso acabou sendo bom porque houveram muitas perguntas, especialmente sobre os widgets em JavaScript. No final da apresentação eu tive uma conversa muito interessante com uma pessoa que foi arquiteto de um dos principais canais de TV australianos e é impressionante como os cenários e problemas são os mesmos.

Durante o resto do dia eu fiquei no stand da ThoughtWorks conversando com as pessoas. Foi bem interessante ver o que os arquitetos presentes pensam da ThoughtWorks e fazer contatos profissionais, tanto para possíveis futuros colegas quanto para futuras oportunidades de projetos.

Uma coisa engraçada em eventos ora do Brasil é que apesar de nos consideramos os seres mais sociais do universo conhecido nossos ventos são sobre grupinhos. As pessoas não interagem com desconhecido. Nos eventos aqui e em outros países, no entanto, é tudo sobre networking. Chega a ser meio constrangedor você está saindo do banheiro e alguém fala um “Oi, eu sou X, quem é você?”.

Segunda-feira eu embarco para Sydney para mais um dia de evento. Isto é algo também interessante, como o evento é durante a semana e Melbourne e Sydney são cidades distantes o mesmo evento ocorre nos dois lugares.

Estes últimos dias (meses?) foram bem corridos e eu fico feliz de pelo menos esta tarefa se concluir.

Apresentação no Australian Architecture Forum 2008

Friday, May 16th, 2008

A apresentaçao em Melbourne acabou de terminar. Foi bem interessante e o evento em si está sendo uma experiência diferente. É algo mais como mesas redondas do que apresentações, mais sobre isso depois.

Aproveitando, agradecendo novemente ao Antônio Carlos pela liberação dos nomes e marcas, a apresentação ia ficar bem sem graça sem os screenshots :)