segunda-feira, 7 de dezembro de 2009

Convention over Configuration no dia-a-dia

Recentemente fiz um curso de Ruby on Rails e estou brincando um pouco com esse Framework. Uma das características mais importantes do Rails é o extensivo uso de Convention over configuration.

Para quem não está familiarizado com o conceito, Convention over configuration em português seria algo como “convenção sobre configuração” no sentido de dar preferência a convenções ao invés de configurar tudo.

Grande parte da produtividade do Rails se dá devido as convenções. São coisas simples, mas que ajudam bastante na produtividade durante o desenvolvimento.
ConventionOverConfiguration Imagem retirada do site Geek & Poke.

Uso de convenções em .NET

Desenvolvedores .NET não estão muito acostumados ao uso de convenções. O mais comum é configurar explicitamente tudo, seja via código ou em arquivos XML. Porém, aos poucos estamos sendo influenciados por outras linguagens, adotando as boas práticas já adotadas por outros desenvolvedores a anos.

A própria Microsoft já está adotando convenções nos produtos dela, como por exemplo no ASP.NET MVC. Por exemplo, ao acessar a url http://site/usuario/cadastrar o framework entenderá que deve utilizar o controller UsuarioController e chamar a ação Cadastrar. Nenhuma configuração é necessária.

Outro exemplo do uso de convenções é o NHibernate. Ao mapear o atributo Nome da classe Pessoa, por padrão o NHibernate utilizará a tabela Pessoa e o campo Nome do tipo varchar. Ao mapear o atributo o framework irá verificar que o tipo dele é string e utilizará a convenção de nome de campo e tabela.

Criando convenções personalizadas

Alguns produtos definem uma convenção e temos que nos ater a ela. Porém, o ideal é que possamos criar nossas próprias convenções.

Um ótimo exemplo disso é o Fluent NHibernate. Para quem não conhece, o Fluent NHibernate permite criar o mapeamento do NHibernate diretamente via código, sem utilizar arquivos XML.

No Fluent NHibernate podemos mapear as classes de forma automatizada. Ao invés de definir manualmente cada tabela e campo, fazemos isso por meio de convenções. Podemos utilizar as convenções padrão ou criar nossas próprias. Por exemplo, se queremos que todas as tabelas sejam igual ao nome da classe com o prefixo “tbl”, simplesmente criamos uma nova convenção que define isso e pronto, todas as tabelas serão mapeadas dessa maneira.

Resolvendo problemas

O uso de convenções certamente nos auxiliam bastante, porém, em alguns momentos elas parecem “caixas pretas” ou simplesmente “mágica”. Há casos onde queremos saber exatamente o que está sendo feito por baixo dos panos.

Nesses casos, é essencial que o framework disponha de recursos para que possamos identificar qual a convenção utilizada e qual o resultado obtido. Por exemplo, no ASP.NET MVC, se criamos um Controller e esquecemos de criar a View recebemos um aviso conforme imagem abaixo. Repare que o framework detalha todos os possíveis locais onde a view poderia estar. Com essa informação fica claro qual o problema e sua solução.
erro_mvc

Em resumo

As convenções quando bem empregadas facilitam bastante o desenvolvimento e aumentam nossa produtividade. Só precisamos tomar cuidado para que essa “mágica” possa ser facilmente entendida quando precisamos resolver um problema.

A alguns meses, Jeremy Miller fez uma apresentação sobre o assunto. Recomendo o vídeo, muito interessante ouvir as experiências dele na área.

segunda-feira, 16 de novembro de 2009

Refatorar ou reescrever?

Uma situação que já presenciei algumas vezes é alguém dizendo: “devemos reescrever essa aplicação do zero”. Mas, realmente vale a pena? E quais as situações que levam a isso?

Nas situações que presenciei, normalmente haviam dois possíveis motivos: o código era muito ruim ou houve uma grande evolução tecnológica desde que a aplicação foi escrita.

Repare que ambos os motivos são de ordem tecnológica, não tendo nada a ver com o negócio em si.

Vamos reescrever!!!

Todo o desenvolvedor gosta de criar algo novo, dificilmente alguém gosta de manter um código existente e, principalmente, escrito por outros. Quando vemos um código ruim, sempre pensamos “eu poderia fazer muito melhor” e “é melhor jogar todo esse código fora e começar do zero”.

O mesmo ocorre quando uma tecnologia antiga é utilizada em um software.

Sempre temos algumas desculpas para reescrever:
  • A tecnologia atual é muito melhor;
  • Dessa vez vamos fazer bem feito, deixaremos o código correto;
  • Entendemos melhor o produto, não vamos cometer os mesmos erros;
  • Reescrever o produto vai ser rápido, afinal, todos os requisitos já foram levantados.

Mas, nem sempre é como esperamos

Certo, decidimos reescrever toda a aplicação. Vamos deixa o antigo código de lado. Utilizamos a antiga aplicação para olhar os requisitos e copiamos o funcionamento dela em uma aplicação novinha em folha.

Aos poucos alguns problemas vão surgindo. Alguns percebemos de imediato, outros só aparecem quando já é muito tarde:
  • Os requisitos estão embutidos no código fonte: mesmo que a aplicação tenha uma ótima documentação, não podemos confiar totalmente nela. Sempre há detalhes que só estão presentes na aplicação em si. Pequenas correções, algumas features e outros pequenos detalhes. Se olhamos apenas para a documentação, deixamos passar esses detalhes, e mesmo ao se basear no código fonte, certamente muitos detalhes passarão despercebidos;
  • O mundo não para de girar: enquanto a nova aplicação é escrita, a antiga continua sendo utilizada. Sempre será necessário incluir um novo recurso ou corrigir um bug. Nesse meio tempo, enquanto reescrevemos a aplicação, toda o desenvolvimento tem que ser duplicado, é feito na versão antiga e na nova. Além disso, evitamos adicionar qualquer recurso na versão antiga, afinal, o cliente que espere a versão nova e nela fazemos isso. Infelizmente, não é bem assim que funciona na prática;
  • Vai demorar tanto quanto a primeira versão ou ainda mais: a ilusão de que o desenvolvimento será rápido logo cai por terra. Criar uma aplicação baseando-se em outra é uma tarefa difícil, além disso, temos que manter as duas versões até que a nova esteja pronta;
  • A nova versão só poderá ser utilizada quando estiver pronta: para tirar proveito da nova aplicação precisamos esperar que ela esteja 100% concluída. Nesse meio tempo, ela não adiciona nenhum valor aos desenvolvedores e nem ao cliente. Imagine se, por algum motivo, o projeto é cancelado. Todo o código desenvolvido é perdido sem nunca ter tido nenhum valor.

Então, como fazer?

A melhor tática é refatorar a aplicação existente, fazendo ajustes de forma gradual até que todo o sistema esteja da forma que desejamos. Já escrevi a respeito de como utilizar a orientação a objetos para nos ajudar nessa refatoração (veja Parte 1, Parte 2 e Final).

Podemos alterar pequenas partes do sistema ou até mesmo jogar toda uma parte fora, porém, não o sistema inteiro.

Ao fazer ajustes graduais na aplicação evitamos toda a dor de cabeça gerada pela reescrita da aplicação inteira. É claro que normalmente é mais difícil ajustar uma aplicação do que reescrevê-la, mas no final das contas, vale a pena.

Quando reescrever?

Em algumas situações realmente vale a pena reescrever totalmente a aplicação. Uma aplicação desktop que precisa ser reescrita para web por exemplo, envolve uma mudança grande de tecnologia, nesse caso vale a pena reescreve-la totalmente.

Mesmo quando decidimos reescrever uma aplicação é recomendável ter uma estratégia de como fazer isso de forma gradual. Implementando as partes mais importantes na nova versão e deixando as duas versões viverem em conjunto enquanto essa migração é feita.

Um artigo bastante interessante escrito pelo Joel Spolsky também fala a respeito disso. Recomendo a leitura: Things You Should Never Do

terça-feira, 3 de novembro de 2009

Criando um protótipo rapidamente com Balsamiq Mockups

Frequentemente precisamos criar um protótipo de tela, seja simplesmente para mostrar uma idéia ou para mostrar para o cliente e verificar se é isso mesmo que ele deseja.

Nesse momento temos duas opções:
  • Criar o protótipo real, exatamente da maneira como será o resultado final;
  • Criar um esboço, destacando apenas aquilo que nos interessa;
login
Eu evito ao máximo ter que criar um protótipo fiel a realidade, primeiramente pelo trabalho, mas principalmente porquê isso acaba virando um compromisso. Na hora de implementar somos obrigados a seguir a risca aquele protótipo, e como bem sabemos, na hora de implementar nem sempre podemos seguir a risca o que desenhamos.

Minha primeira opção ao criar um esboço é o bom e velho papel e caneta, ou um quadro branco se disponível. Porém, na maioria das vezes isso não é suficiente, precisamos enviar a imagem por e-mail a outras pessoas, anexá-la a documentos, etc. Nesse caso, como não tenho habilidades artísticas e tampouco sei trabalhar muito bem com ferramentas de edição gráfica acabo optando pelo Paint. Bem, o Paint até que é rápido pra se desenhar, fácil, mas deus queira que eu não tenha que refazer algo!!

Descobri agora um software muito bom para isso, é o Balsamiq Mockups. Simplesmente ótimo! É exatamente aquilo que eu procuro ao criar um esboço. As principais características que posso destacar são:
  • Bastante intuitivo – em poucos minutos usando a ferramenta já sabemos como tudo funciona;
  • Rápido de criar;
  • Facilmente alterável – cada elemento na tela é um “componente", podemos modificá-los a qualquer momento;
  • Não requer habilidades artísticas;
  • E principalmente, tem cara de esboço. Não precisamos nos ater a detalhes. Nos focamos no fundamental apenas.
tela
O mais legal é que já criaram até um site com vários componentes e layouts comuns, como por exemplo a figura abaixo:
mytube

A única coisa que talvez esteja faltando é a possibilidade de criar nossas próprias bibliotecas de componentes. Porém, pelo visto, isso já está sendo desenvolvido.

No site do fabricante você encontra maiores informações do produto: Balsamiq.

quarta-feira, 28 de outubro de 2009

A teoria e prática caminham juntas

Frequentemente ouço alguém pedir: "devo aprender primeiro a tecnologia X ou Y?" ou, "o que é melhor eu conhecer, Java ou C# ?". Normalmente minha resposta vai ser: não importa qual você aprenda, desde que aprenda primeiro os conceitos por trás delas.

A importância da teoria

As formas de ensino as quais estamos acostumados normalmente separam a teoria da prática, nunca mostrando como aplicar toda a teoria no mundo real. A teoria e prática são separadas de tal modo que somos levados a crer que uma não tem relação com a outra.

Certamente você já ouvir frases do tipo "Isso é coisa de acadêmico", "No mundo real uma coisa dessas não se aplica" ou então "Eu sou uma pessoa pragmática não ligo pra essas teorias".

Essas frases são exemplos claros de como a teoria e prática são vistas como duas coisas distintas. Uma pessoa se diz pragmática e se afasta de qualquer teoria. A verdade é que a teoria está pros traz de tudo que fazemos, querendo ou não, ela é o fundamento.

O foco excessivo na tecnologia

Uma tendência que vemos atualmente é um foco excessivo na tecnologia em si e não nas motivações por traz dela. Todos querem estudar a nova ferramenta da moda.  As soluções são baseadas apenas em ferramentas.

O que adianta conhecer o framework de desenvolvimento web XYZ se a pessoa não sabe os conceitos básicos de desenvolvimento web? Ou aprender o lindo e maravilhoso framework de persistência recém lançado, sem saber quais seus objetivos e razões de existir.

Esse foco excessivo leva as pessoas a serem especialistas em uma determinada ferramenta, sendo incapazes de utilizar outras ferramentas de mesmo propósito ou até mesmo de fazer qualquer julgamento a respeito delas.

A teoria faz parte da prática

Sempre que preciso utilizar uma ferramenta procuro entender os problemas que motivaram o desenvolvimento dela e a forma que ela visa resolver esses problemas. Com isso em mente, fica fácil comparar diversas ferramentas e adotar outra se necessário.

Um exemplo prático é o desenvolvimento Web. Comecei a desenvolver sistemas web utilizando ASP.Net, e como todos devem saber, o conceito de WebForms adotado pelo ASP.Net tenta abstrair a complexidade da web e simular o desenvolvimento desktop. Nunca gostei muito dessa idéia de abstrair a web, por isso acabava estudando o funcionamento por traz daquela abstração. É importante saber como mostrar os dados em um Grid ou como utilizar um Repeater com dados aninhados, mas tem coisas muito mais importantes que isso.

Com o conhecimento de desenvolvimento web em geral que adquiri, e não apenas o conhecimento da abstração criada pelo ASP.Net, fui capaz de utilizar outros frameworks sem problemas. É só uma questão de traduzir a teoria para os comandos do novo framework.

Orientação a objetos na teoria e na prática

A orientação a objetos não é nova, porém somente na última década ela passou a ser amplamente utilizada.

Em qualquer projeto desenvolvido hoje, se alguém cogitar utilizar o desenvolvimento estruturado, certamente será motivo de piadas. Afinal, programação orientada a objetos (POO) é um paradigma muito superior a programação estruturada. Há apenas um problema: grande parte dos projetos que atualmente são desenvolvidos utilizando POO não utilizam os conceitos de POO, são apenas programas estruturados utilizando classes.

POO não se resume a conhecer o que é classe, objeto, método e herança. Há muitos outros princípios que se aplicados ajudam no bom desenvolvimento. Os princípios mais difundidos são os que conhecemos pelo acrônimo SOLID. São eles:
  • SRP - Single Responsibility Principle;
  • OCP - Open Closed Principle;
  • LSP - Liskov Substitution Principle;
  • ISP - Interface Segregation Principle;
  • DIP - Dependency Inversion Principle.
Apenas com estes princípios é que podemos realmente alcançar todo o potencial da POO e não apenas desenvolver programas estruturados utilizando classes. Em futuros posts devo falar mais sobre eles.
Mais uma vez, é um questão de mudar o foco da tecnologia para a teoria.

Em resumo

Para ter sucesso no desenvolvimento de software devemos tirar um pouco o foco da tecnologia e aprender os conceitos por traz dela. É uma pena que a cada dia as instituições de ensino estão focando menos nessa teoria e mesmo quando o fazem, é de forma distante da realidade, ficando difícil fazer uma ligação entre teoria e prática.

quinta-feira, 22 de outubro de 2009

Valores nulos em chave única no SQL Server

O Microsoft SQL Server tem uma característica que eu não gosto nem um pouco: em uma chave única dois valores nulos são considerados iguais.

Imagine uma tabela com os campos abaixo, tendo uma chave única com o campo CPF.
ID Nome CPF
1 João 123.456.789-00
2 Pedro NULL
3 Bino NULL

No SQL Server, ao contrário de outros bancos como o PostgreSQL, esses dados não seriam válidos. Ao tentar inserir o terceiro registro seria gerado um erro de chave duplicada.

Para contornar esse problema existe um truque denominado Nullbuster. Descobri essa técnica por acaso ao ler um post do Karl Seguin. Pesquisando na internet descobri que os créditos são dados ao Steve Kass, porém não encontrei nenhum link onde pudesse confirmar isso.

O truque é bastante simples, veja o SQL abaixo:
CREATE TABLE dupNulls (
  pk int identity(1,1) primary key,
  X  int NULL,
  nullbuster as (case when X is null then pk else 0 end),
  CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)
Repare que foi criado um campo adicional denominado “nullbuster” onde o valor será igual a PK se o valor for NULO ou 0 se o valor estiver preenchido. A chave única então é composta dos dois campos.

A tabela exibida acima ficaria com os valores:
ID Nome CPF Nullbuster
1 João 123.456.789-00 0
2 Pedro NULL 2
3 Bino NULL 3

Um truque bastante simples que resolve alguns problemas.