Posts Marcados Ágil

Como eu Desenvolvo Software – Conclusão

Olá pessoal, este é último post desta série. No post anterior descrevi como construo software até o ponto onde desenvolvemos as telas do sistema que vão de fato gerar valor para o cliente. Estas telas são a própria razão do software sendo construído e são elas que geram o retorno para o custo de desenvolvimento. No fundo, estas telas são a razão de ser do sistema.

A mesma idéia de telas complexas pode ser aplicada para itens como serviços ou aplicações Windows. Um serviço normalmente é feito para atender uma necessidade de integração, seja com outros sistemas, com outras plataformas (mobile, por exemplo) ou para ser parte de um barramento corporativo. Para os serviços, eu sigo praticamente os mesmos passos utilizados em telas complexas. Primeiro identifico o que é desejado do serviço. Aí isto é descrito na forma de interfaces e objetos de transporte – eu uso o excelente Web Service Software Factory, que é um plugin DSL para o Visual Studio 2010. Se a integração é complexa, eu tento simular o máximo antes de efetivamente integrá-lo aos serviços de domínio, para, da mesma forma, identificar todos os aspectos e evitar retrabalho. Finalmente, com todas as interfaces definidas, eu as “preencho” fazendo as implementações acionar os serviços de domínio. Aplicações Windows também seguem estes mesmos passos.

Claro que há situações de exceção. Isto é especialmente válido para requisitos não funcionais como segurança e desempenho. Embora hoje seja relativamente fácil atender a requisitos de segurança com a própria infra-estrutura da plataforma .NET, em algumas situações é necessário que criemos mecanismos específicos para atender um determinado requisito. Um exemplo disto são aplicações multi-tenant, onde é necessário um cuidado especial para que o usuário de um cliente não tenha acesso a dados de outro. Para estes casos, o recomendável é tentar ver a literatura existente e pesquisar outras situações similares e as soluções que foram empregadas.

Outro ponto que gera situações de exceção é a questão de desempenho. Um problema típico é fazer uma aplicação que roda bem em desenvolvimento e testes, mas que, ao ser colocada em produção com um número grande de usuários, falha por completo. Existem vários problemas que podem gerar este cenário e cuja solução às vezes não é simples. Às vezes não é simples nem detectar a causa ou simular o problema em ambiente de testes, portanto boas práticas na hora de desenvolver podem significar menos dor de cabeça no futuro. Agora, se por um lado é ruim pensar em otimização prematura, tentando fazer código pra evitar um problema de desempenho para o qual não se conhece a gravidade ou a frequência de ocorrência; é também ruim não pensar em desempenho durante o desenvolvimento, utilizando práticas que podem gerar problemas potenciais. Aqui o melhor caminho ainda é a experiência, se a aplicação sendo desenvolvida tem estas características, o melhor é ter na equipe alguém que já tenha tido oportunidade de lidar e desenvolver soluções para estes tipos de requisitos.

Acho que deu para dar uma idéia do processo que eu sigo. Mas, além do processo, acho importante reforçar o ponto mais importante que eu tentei passar no decorrer destes post: fazer software é entregar algo que funcione, dentro de custo e prazo que em que o cliente possa ter ROI. A maior parte das vezes esta medida se prolonga por toda a vida útil do software e o ROI continua sendo medido a cada atividade de manutenção e evolução. Se tudo correr conforme previsto, um cliente satisfeito vai ter seus objetivos atendidos com o software que possui e a empresa que o construiu vai ter lucro neste processo.

Eu acredito que fazer software é mais parecido com artesanato do que com uma linha de produção. No fundo, a qualidade do software que vai ser entregue e a questão de se conseguir cumprir prazo e custo em posteriormente, dar manutenção, tem uma relação direta com a qualidade das pessoas que o construíram. Assim, para se tornar um bom desenvolvedor, é necessária uma atualização e melhoria constante no processo de desenvolvimento. Isto não só em aspectos tecnológicos, mas em áreas como habilidade de comunicação, design, capacidade de trabalhar em equipe e até entendimento dos negócios da empresa onde se está inserido e do cliente. Tudo isto está obviamente muito ligado à maturidade e à experiência de quem desenvolve, mas são características que podem ser melhoradas em quaisquer estágios da nossa carreira. Como qualquer tipo de artesanato, fazer bom software significa ter uma boa combinação de talento, conhecimento e experiência. O talento é de cada um e a experiência vem com o tempo. O fator que está mais sob nosso controle é o aspecto técnico, que podemos sempre aprimorar com estudo.

Neste aspecto, apesar da tecnologia evoluir muito rápido, existem conceitos fundamentais e comuns a qualquer tipo de software que permanecem quase imutáveis ao longo do tempo. Assim, coloco a seguir uma relação bibliográfica que abordam estes aspectos mais fundamentais e que eu achei especialmente importantes pra mim:

Code Complete 2 – Acho este livro essencial para quem quer codificar bem. É sobre técnicas para melhorar o código que escrevemos, muito bom mesmo!

Clean Code – Na mesma linha do Code Complete, também muito bom!

The Mythical Man-Month – Este livro foi escrito em 1967, mas é incrível como o que ele apresenta é aplicável até hoje! Muitas vezes vemos conceitos defendidos por profissionais “atuais” que repetem os mesmos erros descritos há mais de 30 anos… Acho que é uma leitura indispensável para qualquer profissional da nossa área (é bem curtinho).

The Object Primer – Embora um pouco antigo, a parte de conceitos sobre programação orientada à objeto é fantástica. A parte de UML hoje nem é tão importante, mas mesmo assim vale – afinal quem não vai esbarrar com um diagrama UML em algum momento?

Refactoring – Os livros do Martin Fowler em geral são todos muito bons. Mas deles, o que eu mais gosto é este. Leitura obrigatória!

The Pragmatic Programmer – É um livro que eu gostei muito, sobre programação baseada em realidade, utilizando conceitos ágeis.

Domain-Driven Design – O assunto está na moda, mas independente de modismos, os conceitos colocados neste livro são muito bons. Não é um livro fácil de ler, mas definitivamente vale a pena.

Domain-Specific Development – Apesar de já desatualizado no que diz respeito ao Visual Studio, a parte inicial deste livro, que descreve o que é e qual o propósito de uma DSL, é muito boa. Não sei como este assunto vai evoluir no futuro, mas este livro influnciou bastante a nossa linha de construção de frameworks. E muitos plugins do Visual Studio 2010 e geradores de código .tt são derivações das idéias criadas por este time.

É isto pessoal. Até a próxima!

, , , , ,

1 comentário

Como eu Desenvolvo Software – Parte 5

Olá pessoal, um ótimo 2011 para todos! Apesar de muita gente estar de férias, o nosso ano começa a todo vapor, com um projeto grande em andamento e toda a manutenção usual. Continuando a série sobre desenvolvimento, fechei o último post com telas de CRUD já disponíveis no sistema.

Apesar de já termos o sistema com muitas telas operacionais até aqui, para mim isto é de pouco valor já que, para o cliente, o produto ainda não traz nenhum benefício de negócio. Ter um sistema que permita cadastrar dados de apoio não traz nenhum valor agregado e, portanto, possui um ROI nulo. Claro que é uma etapa necessária (e nem sempre muito rápida, dependendo do número de dados de apoio), mas é importante que o cliente e a equipe estejam sempre atualizados nesta visão.

Com os CRUDs prontos, podemos voltar às tarefas que geram valor. As telas construídas nestas tarefas normalmente são bem mais complexas do que um CRUD e são suportadas por regras de negócio que também podem ser complexas. É nestas telas e respectivos serviços (que implementam as regras de negócio) que iremos gastar a maior parte do esforço de construção (e posteriormente, de manutenção). A minha abordagem para estas telas é algo bastante top-down. Eu inicio pela definição do layout da tela. Normalmente uso algum produto para desenhar um protótipo e uso-o para conversar com o cliente e tentar identificar se aquilo atende o que ele espera da funcionalidade. As necessidades de negócio são identificadas e classificadas em métodos de classes da camada de serviços. Por exemplo, suponhamos que ao cadastrar uma despesa de aluguel de veículo, haja uma necessidade de se gerar cálculos consolidados por operadora de aluguel. Neste caso poderíamos criar uma área de serviço responsável por estas atividades (uma classe chamada “GerenteAluguelVeiculos”, por exemplo) e um método para aquele serviço (por exemplo, “RegistrarAluguel”). A partir daí isto é incorporado à linguagem e até o cliente vai saber que existe uma serviço de registro de aluguel no sistema que é responsável por gerar os dados consolidados. Durante a análise, estas necessidades são registradas em novas tarefas, que são cadastradas para priorização e implementação.

Esta separação em classes especializadas para serviços é algo que é bastante polemizado em listas de discussão, já que isto gera um modelo bem anêmico. Em uma abordagem DDD clássica, estes métodos de serviço ficariam nas próprias entidades (veículo?, aluguel?). Porém, na minha experiência e com a tecnologia atual, não há ganhos de produtividade em se fazer isto, muito ao contrário, aliás. Além da complicação gerada pela parte subjetiva, que é de cada desenvolvedor, de identificar qual a classe é responsável pelo quê, o posicionamento de regras de negócio em métodos específicos das entidades de domínio tornaria muito mais complicada a gestão dos aspectos não-funcionais das regras, tais como a necessidade ou não de log, aspectos de segurança, aspectos transacionais etc. Isto é uma particularidade do nosso framework, mas com as regras de negócio somente em classes de serviço, esta gestão fica muito mais simples e com isto temos uma maior produtividade escrevendo estas regras – mais sobre isto no posto sobre camada de serviços da série de produtividade.

Continuando em uma abordagem top-down, eu passo à criação da tela em si. Nesta parte temos um grande apoio do framework, mas ainda assim temos que criar artefatos como views e rotinas para preenchimento de dados. As etapas de criação dos componentes das telas, preenchimento dos dados da mesma e execução de regras de negócio ficam bem separadas, graças ao framework – ver mais no post sobre camada de interface. As ações da tela que irão acionar serviços são criadas, mas, inicialmente, sem executar nada (stubs). Eu tento fazer o máximo de comportamento da tela possível antes de iniciar a construção dos serviços. A idéia é tentar ter um protótipo com pelo menos a parte de navegação funcionando e mostrar isto para o cliente antes de continuar. Isto porque, na minha experiência, quando o cliente vê funcionando, pode acontecer dele se lembrar de algo que não foi incluído na análise inicial e pedir modificações. E também porque o esforço gasto nestas telas é tão grande (de longe, a maior parte do sistema) que é importante ter as definições o mais corretas possível antes de fechar para evitar retrabalho. Isto pode fazer até com que às vezes eu utilize mocks para gerar os dados que seriam retornados por serviços, para que a tela possa funcionar antes deles estarem prontos. Quando o serviço é muito simples, pode ser que eles sejam de fato implementados neste ponto. Algo que é muito comum ser feito nesta hora são consultas específicas de repositório. Coisas como “ObterFuncionariosEmViagem” podem ser criadas como métodos do repositório e já implementados. O framework fornece muitos mecanismos para facilitar a geração deste tipo de consulta, desde expressões lambda até uma fluent language para gerar consultas NHibernate e para manipulação de dados. Para isto eu também utilizo muito raramente testes, pois a linguagem de expressão de consultas é muito simples e raramente gera erros ou dúvidas. E por ser compilada, qualquer problema com alterações de entidades de domínio é rapidamente detectada e corrigida. Os casos que exigem testes são consultas complexas que são expressadas em HQL; como estas são baseadas em strings, podem quebrar muito facilmente com alterações em entidades, além de serem difíceis de escrever.

Quando a tela está praticamente pronta, aí sim começo a escrever a parte de serviços. Muita gente acha que a maior parte do esforço está aqui. Mas em geral, a maior parte do esforço está na tela. Escrever serviços, tirando algumas poucas exceções, é algo bastante simples, especialmente com todo o apoio fornecido pelo framework. Claro que existem regras de negócios complexas em muitos sistemas, mas ainda assim, o esforço gasto nelas não se compara com o de construir todas as telas que não são CRUD. O framework tira do desenvolvedor toda a parte de infra-estrutura, então ele não se preocupa com coisas como transações, gerenciamento de exceções, uso ou não de log, identificação do usuário corrente etc…. Ele praticamente escreve só a regra de negócio mesmo. A consolidação de regras de negócio em classes de serviço separadas por “áreas” facilita o reuso e a localização rápida de regras similares. E o uso dos repositórios para consultas mais complexas permite que o serviço seja basicamente direcionado para as alterações da transação.

Para as regras de negócio o ideal é ter pelo menos um teste unitário com o caminho mais usado da mesma. Não uso TDD por achar que, para o meu caso e para os desenvolvedores com quem trabalho, ele diminui produtividade – conheço pessoas que dizem ser o contrário, mas cada caso é um caso. Na criação de regras de negócio, o importante é ser eficaz e eficiente, ou seja, produzir regras que façam o previsto, no menor prazo possível, e que sejam fáceis de entender e manter. Acho que cada um tem um estilo pra atingir este objetivo, o meu definitivamente não é com TDD. Nunca trabalhei com alguém que usasse TDD de maneira consistente e produtiva em todos os casos, mas se alguém funcionar bem assim, nada contra. Um teste ao menos é importante porque regras de negócio são muito sensíveis a mudanças de negócio. Confesso que nem sempre eu mesmo sigo esta regra; muitas vezes a regra é tão simples (menos de 5 linhas é o típico para regras simples) que o esforço para produzir teste fica questionável. Mas acho que no geral faz sentido ter ao menos um teste por regra.

Finalmente, as regras são integradas à interface para que tudo funcione. Eu faço isto de maneira incremental, ou seja, conforme as regras vão ficando prontas eu já vou incorporando-as à tela. Eu gosto mais dos testes finais (integração), ou seja, de ver a tela funcionando e fazendo que ela foi projetada que fazer. No meu caso, é onde eu pego a maior parte dos erros de construção. E, quando acabo este processo, a tela está quase totalmente pronta.

Embora utilizemos uma fase de homologação pelo cliente, eu uso isto mais como um processo de validação/aceitação do que de testes. Acredito que um bom desenvolvedor deva entregar seu software pronto, e pronto significa sem erros. Assim, não acho que o usuário final ou cliente deva participar do processo de depuração. É claro que algumas vezes eles vão encontrar erros ou bugs; mas isto tem que ser exceção, não regra.

Durante todo este processo, o framework é também evoluído. Se for detectado algo que está dando algum excesso de trabalho braçal, ou alguma coisa que poderia ser feita pra aumentar produtividade, isto já é feito durante o desenvolvimento (desde, é claro, que não seja algo muito grande). É claro que isto não pode ser feito por desenvolvedores inexperientes, daí a necessidade que temos de termos somente equipes formadas predominantemente por seniores. Com isto, o objetivo é que nossa produtividade não só seja alta agora, mas que ela continue a aumentar com o tempo.

Em linhas gerais, é este o processo que eu sigo. No próximo e último post eu vou falar um pouco sobre casos de exceção e pontos como construção de serviços e exportação/importação de dados, muito comuns a todos os sistemas. Até breve.

, , , , ,

1 comentário

Como eu Desenvolvo Software – Parte 4

Oi pessoal, espero que todos tenham tido um ótimo natal! Eu consegui descansar um pouco, aproveitando para passar um fim de semana agradabilíssimo com a família em Bonito – MS. Para quem não conhece, recomendo muito, a região é fantástica, com locais maravilhosos.

Retomando o post anterior, eu estava falando sobre a minha maneira de desenvolver software, com o objetivo de fornecer uma perspectiva possível para os que estão começando, ou uma outra visão para os que estejam tentando definir uma forma prática para trabalhar com desenvolvimento. No post anterior, eu apresentei um problema fictício e a decisão de construir um sistema web para resolvê-lo. Iniciei com a análise sobre as condições de contorno do sistema e toda a parte de infra-estrutura envolvida. Estávamos no ponto onde tínhamos o esqueleto definido, com a parte de segurança já estabelecida. Até este momento, estávamos vendo questões de caráter não-funcional do sistema. Num cenário real, estas questões podem ser muito mais complexas do que o meu exemplo, pois pode já haver algum tipo de restrição de desempenho ou de segurança, por exemplo, que exija uma infra-estrutura muito mais complexa, fazendo com que esta etapa se torne um projeto em si.

Mas assumindo que seja algo padrão, a próxima fase seria começar a trabalhar na parte funcional do sistema. Neste momento, o que eu faço é começar por uma tarefa que seja algo que possa dar valor imediato para o cliente. Se o objetivo do sistema é controlar despesas de viagem de funcionários, algo que poderia ter valor imediato seria disponibilizar a lista das últimas viagens realizadas, com a consulta de viagens individuais e o cadastro de nova viagem. Claro que fazer algo de grande valor pode fazer com que seja necessário existir previamente toda uma gama de telas de apoio. Por exemplo, se estamos controlando viagens, teríamos que ter o cadastro de funcionários, de destinos, de tipos de despesa contabilizadas etc. Neste ponto começa a surgir o conceito de “Domínio” do problema (ver DDD), onde os termos já passam a ter um significado específico (ubiquitous language) e são utilizados para uma melhor comunicação com o cliente. Esta comunicação é fundamental para o sucesso e a evolução posterior do sistema, portanto é de grande importância tentar definir os termos corretos e já empregá-los toda vez que houver alguma comunicação.

Cada entidade de apoio deve ter seu mecanismo de persistência definido. No meu caso, diferentemente do DDD, eu não utilizo agreggates para centralizar a persistência. Para mim, cada entidade de domínio vai ser traduzida diretamente em uma tabela do banco de dados – a única exceção são as entidades que são classificadas como enumerations (enums), estas ficam só em código. O que diferencia enums de entidades de apoio, para mim, é o significado para o sistema da inclusão de novos registros. Se um usuário puder incluir uma instância da entidade e o sistema não tiver que ter sua parte de negócio alterada por causa disto, ela é uma tabela; caso contrário, é uma enum. Por exemplo, um cliente pode cadastrar uma cidade de destino a qualquer momento e nenhuma regra de negócio precisa ser alterada. Agora, imaginemos que existam categorias de faturamento pré-definidas que vão definir graus de reembolso. Se cadastrar um novo tipo de categoria ou uma categoria inteira implicar na necessidade de alteração do sistema, ela é um enum. Claro que sempre pode-se tentar modelar um sistema onde haja pouca necessidade de modificações, com quase tudo parametrizado em entidades e praticamente nenhuma enum. Mas pode ser que isto não valha a pena em determinados cenários.

Uma vez que as entidades de apoio estejam definidas, vão ser criados os objetos de domínio que as irão representar. No meu caso, esta tarefa é feita na seguinte ordem: 1) Primeiro a entidade tem seu principais atributos identificados (mentalmente); 2) É criada a tabela no banco de dados que vai armazenar a entidade – eu tento manter a tabela com os nomes de colunas o mais próximo possível dos atributos do objeto; 2) Usamos o framework, para gerar a entidade no diagrama do ActiveWriter; 3) Usamos os geradores de código .TT do framework para efetivamente construir a entidade e o respectivo repositório (sim, cada entidade tem o seu) e factory. Este processo é muito rápido, é questão de segundos fazer uma entidade usando esta sequência. Como tudo é feito no framework, a partir da geração tudo está 100% funcionando, já que não há nenhuma intervenção manual; assim nem é necessário utilizar testes unitários para entidades. Alguns até argumentam que eles seriam úteis para facilitar na evolução no caso de modificações de entidades; porém na minha experiência, este tipo de coisa é tão rapidamente detectado e corrigido que não justifica o esforço de testes (nem mesmo o esforço de tentar automatizar a construção dos mesmos via framework, pelo menos não até o momento).

Sempre com o objetivo de gerar a tela de melhor valor, deve-se então identificar, das entidades de apoio, quais são as necessárias para cadastrar dados que irão ser utilizadas na tela. É importante detectar dados de apoio que são tão importantes que merecem ser tratados como uma tarefa própria. Neste nosso exemplo, existe uma grande chance de o cadastro de funcionário ser um caso destes, seja pelo volume de informações, seja pela necessidade de um maior número de regras de negócio que devem ser seguidas durante o cadastro. Para cada um destes casos identificados é criada uma nova tarefa, de prioridade maior e da qual a tela de melhor valor será dependente.

Para os outros casos, é criada uma tela de CRUD. Telas deste tipo são também feitas com suporte do framework; construí-las é simplesmente definir colunas de visualização e busca na lista de entidades, definir os campos que serão utilizados no formulário de inclusão/alteração e escrever eventuais regras de validação de inclusão/modificação. Um CRUD simples pode ser feito no framework em poucos minutos (o meu record é 8 minutos para uma entidade simples, considerando a criação da tabela, da entidade e das telas). E como todas as telas de CRUD também são construídas de forma padronizada, também não utilizamos testes unitários para este tipo de tela. A criação de CRUDs é um processo tão rápido que há uma tentação de se tentar fazer um sistema inteiro usando telas deste tipo. Isto dificilmente é viável. Primeiro porque telas que possuem formulários muito grandes podem ser confusas para o usuário. Segundo porque para fazer uma atividade simples, um usuário teria que navegar em muitas telas, o que também atrapalharia a usabilidade. Assim, eu prefiro somente utilizar CRUDs para as entidades de apoio mesmo.

Neste ínterim o cliente está sempre sendo atualizado. Ele é notificado e participa cada vez que uma nova tarefa é gerada. A cada CRUD liberado ele também é notificado. Em novos sistemas, os primeiros releases já podem conter várias telas de CRUD 100% operacionais. Isto contribui para que o cliente perceba o andamento do projeto e já possa atuar em eventuais falhas de comunicação ou entendimento.

Novamente este post já está muito grande. Vou parar por aqui, continuo na semana que vem. Acho que em mais 1 ou 2 posts eu consiga finalizar esta série. O assunto é muito extenso, cada um dos tópicos que eu mencionei poderia se tornar um post completo. Espero que esteja conseguindo ao menos dar uma visão geral do processo. Por favor, fiquem à vontade para perguntar ou sugerir.

Desejo a todos um feliz 2011, repleto de realizações e com muita saúde e sucesso!

, , , , ,

Deixe um comentário

Como eu Desenvolvo Software – Parte 3

Continuando o post anterior sobre desenvolvimento, estava falando sobre como um software surge a partir de uma necessidade. É importante notar que, apesar da necessidade orientar o desenvolvimento do um sistema, ele sempre vai existir em uma plataforma, sendo exemplos de plataformas comuns: Windows (consumindo serviços WCF ou acessando direto o banco), web (hospedado localmente ou em algum provedor e acessado por um browser) ou alguma plataforma móvel (android, iOS). A definição da plataforma normalmente implica em algum tipo de restrição ou característica especial no software sendo construído.

Existem ainda as diversas questões de infra-estrutura: a aplicação vai usar banco de dados (SGBD)? Qual? Onde será hospedado? Qual versão de .NET? MS-MVC ou Monorail? E assim por diante… Para simplificar, vou assumir a plataforma web MS-MVC (devo comentar algo sobre outras plataformas no decorrer dos artigos), utilizando a versão mais atual do .NET, utilizando um banco de dados típico (que no meu caso usualmente é o Microsoft SQL Server) e que será desenvolvida utilizando o nosso framework – aplicações que não fazem uso dele ficam tão mais caras que na maior parte das vezes são inviáveis.

Voltando ao exemplo do post anterior, imaginem uma planilha para, por exemplo, controlar viagens de funcionários de uma empresa. As viagens são controladas por funcionário e cada viagem possui informações como data, custo, meio de transporte, destino, despesas diversas e reembolsos. Por alguma razão (volume, por exemplo), alguém avaliou que um sistema poderia diminuir custos de gestão desta planilha, disseminar melhor a informação e facilitar a vida do usuário permitindo, por exemplo, que ele digite as informações diretamente por uma página da intranet da empresa.

Se fosse assumir um sistema deste, a primeira coisa seria entender qual o custo atual do cliente para operar as planilhas, o quanto ele espera gastar e obter com o sistema e avaliar se o que é esperado é factível dentro do que eu acho que seria gasto, baseado na minha experiência no desenvolvimento de aplicações com nosso framework. Teria que ver também se o cliente já está familiarizado com o formato ágil de desenvolvimento e com a nossa forma de trabalho. Caso positivo, teria que identificar a seguir aspectos sobre a infra, ver se já existe um SGBD disponível, onde o servidor web está localizado, se a equipe de infra tem experiência em instalar e manter a aplicação, se os servidores estão atualizados com as últimas versões do .NET etc… Se houver algum problema com algum destes pontos, eu tento resolvê-lo antes mesmo de iniciar o desenvolvimento, pois ele pode se tornar um impeditivo para todo o projeto.

A seguir, ainda na parte de infra, eu identifico como será feita a autenticação de usuários. Normalmente, para aplicações intranet, espera-se uma autenticação integrada com o Active Directory ou algum serviço similar. Para aplicações públicas da Internet, é necessário ver também as questões de segurança, como acesso seguro, certificados para HTTPS, firewalls etc… Muitos desenvolvedores acham que estas tarefas não cabem a nós; eu não consigo concordar com isto, pois falhas nestas áreas podem tornar o melhor software do mundo completamente inútil.

Finalmente, tem-se que decidir como fica o direito de propriedade e uso do software e se o cliente deseja exclusividade sobre algumas porções do software – caso positivo, isto também torna o desenvolvimento mais caro. Como se pode ver, é um grande esforço destinado somente à análise de custo e da infraestrutura, antes mesmo que qualquer hora seja empregada no software em si. Não adianta fazer um excelente software se ele não puder se pagar, ser instalado ou ser mantido adequadamente. Mas apesar da quantidade de tópicos, um checklist com todos estes itens pode ser preenchido em uma ou duas reuniões com as pessoas certas.

Após esta fase, eu posso iniciar a construção da infra-estrutura do sistema. Utilizando o framework, eu posso montar a parte básica da aplicação, que contém uma página default, o banco de dados vazio com a tabela de usuários do sistema, a tela de login com seus respectivos controllers e toda a parte de integração com um sistema de autenticação. Normalmente eu consigo colocar isto no ar em menos de 4h após o início do projeto, pois tenho estas estruturas padronizadas no framework de desenvolvimento. Os casos que demoram mais são os que exigem uma aparência específica do site feito por um designer que me obriga a recortar htmls para a geração dos layouts. Em casos extremos, isto pode levar alguns poucos dias. Esta etapa seria o que alguns chama de Iteração 0. Eu não uso um nome específico, simplesmente utilizo uma parte do esforço da primeira iteração. Como resultado, o cliente já tem o site rodando e logando, caindo na página inicial, com toda a parte de segurança funcionando, e, quando aplicável, com funcionalidades como troca de senhas, “esqueci minha senha” e login integrado.

Paralelamente a esta fase, é feita a primeira reunião de priorização de atividades com o cliente. Nesta reunião são identificadas as funcionalidades principais do sistema e feita a priorização das mesmas. Quando eu faço isto, eu tento já identificar o que pode trazer maior ROI ou maior impacto positivo para o cliente é já sugiro uma ordem de atividades. Quando mais experiência o cliente tem em desenvolvimento no formato ágil, mais ele consegue contribuir nesta etapa. Mas, de uma forma ou de outra, eu tento sempre conduzir para que ele utilize o esforço de desenvolvimento da maneira mais benéfica possível para atingir o objetivo do sistema. No caso da planilha, o que seria mais positivo? Colocar a página pública logo para que os funcionários pudessem já ver o novo sistema e começar a testar o lançamento de dados? Ou colocar a apuração de despesas disponível? Ou facilitar, por exemplo, uma exportação de dados da planilha? Estas decisões são feitas caso a caso, mas imaginemos que no nosso o mais benéfico fosse o lançamento de dados pelo funcionário.

Bom, o post está ficando muito longo, assim vou ter que continuar num próximo, provavelmente a ser publicado só depois do Natal. Até lá e aproveito para desejar a todos um Feliz Natal e Boas Festas!

, , , , ,

Deixe um comentário

Como eu Desenvolvo Software – Parte 2

Se da última vez que escrevi, eu achava que estava sobrecarregado, eu ainda não tinha idéia do que estava vindo! Estes últimos meses tem sido extremamente agitados na White Fox, estamos abrindo várias frentes, criando iniciativas onde um bom software possa ser um diferencial competitivo. Estamos tendo mais sucesso do que eu esperava inicialmente, este ano está sendo fechado com chave de ouro. Em breve, vou escrever sobre algumas destas inciativas aqui. Mas deixe-me continuar finalmente o post anterior, falando sobre desenvolvimento de software.

Como citei no post anterior, a ideia é tentar falar um pouco da maneira pela qual eu desenvolvo software, considerando as metodologias e correntes atualmente em voga. Para começar, acredito que o mais importante de todo o processo é entender que desenvolver software é entregar, dentro de um prazo e um custo acordado, algo que atenda uma necessidade de alguém de maneira satisfatória. Por mais óbvio que isto pareça, a maior parte dos desenvolvedores esquece estes objetivos primordiais. Acredito que isto aconteça porque, como desenvolvedores, tendemos a não lembrar que desenvolver software é uma atividade extremamente cara para quem compra. Assim, não cumprir um prazo pode significar uma perda de oportunidade que inviabiliza todo o investimento. E é claro que não cumprir custo pode fazer com que um investimento seja inviável em termos de resultado financeiro, determinando a falência do projeto. E mesmo atingindo ambos os objetivos, o software ainda tem que servir para melhorar algum processo ou trazer alguma receita, de forma que o investimento feito seja compensado. Claro que muitas vezes isto é difícil de ser avaliado, mas, por mais difícil que seja, isto é o que queremos obter quando desenvolvemos comercialmente. É importante ter isto em mente porque nossas decisões, como desenvolvedores ou arquitetos, vão justamente impactar estas características e elas só fazem sentido quando permitem que a construção do software seja financeiramente viável.

Entregar um software que funciona bem também parece ser algo óbvio, mas que na prática é justamente uma dos pontos mais subjetivos da nossa profissão. Por exemplo, na prática poderíamos construir praticamente qualquer sistema utilizando somente telas de CRUD (listar, editar, alterar, remover). Apesar de funcionar, um sistema assim muito provavelmente seria quase impossível de ser utilizado com eficiência pelos usuários – eu mesmo já tive a chance de conhecer vários sistemas feitos desta forma! No extremo oposto, o software pode ter poucas telas, extremamente complexas e que fazem “tudo”, o que podem pro um lado ser um pesadelo para quem usa ou, mesmo sendo fácil de usar, ser um pesadelo para manter e evoluir – novamente, já vi vários assim. Encontrar o ponto ideal de usabilidade versus produtividade é pra mim um dos maiores desafios do desenvolvimento. No entanto este é um assunto que eu raramente vejo em fóruns técnicos.

Um outro ponto importante dos conceitos gerais de desenvolvimento é a questão da manutenabilidade de software. Esperamos que o software que construímos seja usado por muito tempo, talvez até muitos anos. Assim, se um software for extremamente caro para manter (e manter aqui significa alterar e evoluir o mesmo conforme necessário para o negócio), ele também pode se tornar inviável para quem está comprando. Daí toda esta preocupação de desenvolvedores em fazer software utilizando uma estrutura que permita que ele seja entendido e alterado com facilidade por alguém no futuro. O problema é que isto é também é algo extremamente subjetivo, já que é difícil determinar qual o grau de estruturação que vai permitir atingir este custo ideal de manutenção. Em outras palavras, é muito complicado determinar o quanto a mais tem-se que gastar no desenvolvimento e calcular em quanto isto deixa a manutenção mais barata. Alguns acham, por exemplo, que somente software que faça uso da última moda em BDD, TDD, DDD e etc. teria um custo de manutenção adequado. Mas muitas vezes, o que se gasta para se incluir uma determinada metodologia ou técnica em um processo de construção torna o software tão caro pra fazer que a conta não fecha. Por outro lado temos exemplos de software com anos de uso, em FORTRAN ou CLIPPER, que estão em uso até hoje e que possuem ainda um custo de manutenção que não justifica uma eventual migração. Claro que cada caso é um caso, porém, na minha visão, este é um dos conceitos que mais geram dúvida e que mais é erroneamente empregado por desenvolvedores em geral.

Pra mim a manutenabilidade é super crítica, já que o software que eu construo tem a tendência de ser usado por muito tempo – tenho alguns que estão em uso há mais de 10 anos. Ao longo dos anos, surgem e desaparecem metodologias e técnicas, plataformas e linguagens são evoluídas. A minha solução para isto foi sempre criar um framework que abstraia os artefatos produzidos, na medida do possível, de toda esta mudança, sem perder as novidades. Pra mim, de longe, o mais caro no software é construir interfaces. Assim, desde os tempos do ASP eu trabalho com algum tipo de abstração de interface para tornar isto um pouco mais barato – ver posts sobre produtividade. E interessante que, apesar de todas as mudanças, estes frameworks têm alguns pontos comuns. Claro que hoje em dia, com MVC e jQuery, a facilidade para abstração é muito maior. Porém algumas áreas como visualização e separação lógica, permanecem de maneira muito similar.

Com relação à usabilidade, eu tento manter a criação de telas complexas no limite do que o framework permite. Claro que com a evolução do framework, hoje é possível fazer praticamente qualquer tela. Mas ainda assim, eu sempre prefiro utilizar telas que sejam mais simples de expressar no framework, desde que não atrapalhe muito a usabilidade. É claro que é sempre possível fazer algo super complexo, porém deixo claro para quem compra que a escolha por uma interface complexa vai gerar um maior custo pra fazer e um muito maior pra manter, o que usualmente é um argumento bastante convincente. E interessante de ser ter software usado por muito tempo é que ao longo dos anos, os indicadores mostram que a maior parte do custo de evolução se concentra justamente nas telas complexas, confirmando a teoria de que telas grandes são algo bastante difícil de se justificar, financeiramente.

Então, retomando o tópico principal, como desenvolvo software? Tudo começa com uma necessidade. Para efeito de ilustração, vamos assumir daqui por diante uma necessidade de um cliente que deseja, por exemplo, passar a controlar dados que estavam numa planilha. A partir daqui vou tentar imaginar um software que atenda a necessidade, que seja feito utilizando os artefatos do framework e que custe algo pra fazer que possa ser retornado em algum tempo. Neste exemplo, de uma planilha, o ganho do cliente seria a redução da perda é de tempo dos usuários (afinal manter planilhas grandes é algo complicado), a diminuição do risco de um erro de digitação (planilhas grandes são difíceis de operar) e no aumento da disponibilidade da informação proporcionada por um sistema web.

No próximo post, continuarei deste ponto mostrando como uma necessidade é transformada em software e quais os critérios que uso na construção dos artefatos e na implantação final do produto. Prometo não demorar pra escrever a continuação!

, , , , ,

1 comentário

Como eu Desenvolvo Software – Parte 1

Olá pessoal. Acabei ficando um longo tempo sem escrever, tenho estado anormalmente ocupado nestes últimos meses. Junho foi um mês muito complicado, com as paradas para a Copa do Mundo e a presença no Agile Brazil – isto sem falar em alguns projetos críticos sendo entregues e o tempo dedicado a novos projetos. Com isto, na White Fox, passamos o mês bem sobrecarregados e só agora em Julho é que começamos a voltar ao ritmo normal. Finalmente agora volto a ter um pouco de tempo para escrever alguns artigos!

O evento Agile Brazil foi muito bom. Contou com a presença de grandes nomes do desenvolvimento mundial, como o Martin Fowler e o Philippe Kruchten. E foi excelente pra fazer contato com o pessoal da comunidade .NET do Brasil. Mas este post é sobre algo que conversei bastante com outras pessoas lá e que gera sempre discussões “acaloradas” toda vez que é levantado em uma roda de desnvolvedores (ou em listas de desenvolvedores como a excelente dotnetarchitects): qual o melhor “jeito” de desenvolver software.

Com a aceitação cada vez maior das metodologias ágeis, a minha impressão era que a comunidade estaria mais tranquila sobre metodologias e técnicas para desenvolvimento. Elas sempre evoluem, claro, mas pelo menos em linha gerais, estaríamos em um caminho mais definido. Mas o que eu vejo é que ocorreu algo diferente, uma profusão de metodologias e técnicas e de pessoas que pregam que determinada linha é muito melhor do que qualquer outra. Acho que isto piorou ainda mais porque o modelo “waterfall”, hoje praticamente execrado pela maior parte dos desenvolvedores “modernos”, foi pintando como o maior vilão e origem de todos os males e que a metologia XXX ou YYY chegou para resolver isto!

Longe de mim defenter o uso de waterfall, na maior parte dos cenários. Mas, apesar de todos os problemas e falhas deste tipo de desenvolvimento, o fato é que até hoje temos muitos e muitos exemplos de sucesso,  e que continuam a acontecer ainda hoje. A palestra do Kruchetn foi muito boa neste aspecto, ele citou justamente esta “demonização” do waterfall como um sinal de que nem tudo está bem.

E para quem está começando, eu tenho notado, em geral, uma total confusão. Como as universidades estão alguns ciclos atrás (a maior parte delas ainda prega o waterfall como única e melhor maneira de desenvolver) e sem ter a experiência de ter passado por sucesso e falhas em determinada metologias, os iniciantes ficam sem saber que caminho seguir: Waterfall? nem pensar! Scrum? Como viver com todos os scrum-buts? XP? Como aplicar no meu dia a dia? Isto sem falar nas múltiplas variantes técnicas da construção em si, onde entra TDD, BDD, DDD etc., aliada a uma miríade de plataformas, frameworks, ORMs etc. E sem nem entrar no mérito de linguagens em si, onde temos novamente uma outra gama de escolhas como C#, C++, Java, Rubi, Python etc. E em todos estes, temos muitos “gurus” que afirmam que aquilo é o supra-sumo da agilidade e é a melhor invenção do mundo deste a pólvora, sendo a única maneira de fazer software bem. Realmente é difícil não ficar confuso. Felizmente temos algumas vozes de bom senso, como o Alistair Cockburn, no seu “Juramento de Não-Lealdade”, que talvez comecem a melhorar este cenário.

Na minha visão, escolher uma forma de desenvolver não deveria causar tanta dificuldade. É certo, desenvolver software é algo complicado, exige um misto de dedicação, talento, experiência e muito, muito estudo. Mas, em toda a minha experiência, o que eu tenho visto é que as pessoas que se eforçam para se tornar bons codificadores acabam o sendo de uma maneira geral. Ou seja, um bom programador em C# vai ser um bom programador em Python (claro que uma transição não é simples, pelo volume de novas informações, mas se isto for desejado ou necessário, vai acontecer). Ou seja, os princípios básicos, o cuidado e o gosto pelo que se faz, são fatores mais importantes no sucesso do que qualquer linguagem metodologia, tecnologia ou plataforma que exista. E pra mim, isto vai continuar sendo assim mesmo com o todo o nosso ritmo de evolução… (acho que só vai parar o dia que pudermos solicitar um programa verbalmente e o próprio computador  o gerar… ou seja, daqui a muito tempo!). Óbvio também que nada existe sem contexto, isto é especialmente válido para desenvolvimento de software. Assim, levando em consideração o contexto, algumas escolhas podem levar a uma maior produtividade do que outras.

Para tentar ajudar nestes dilemas, resolvi escrever este e mais um artigo sobre desenvolvimento, e como eu o vejo. Vou colocar alguns princípios que adoto, na medida do possível, de onde eles vieram, e como eu vejo a inserção de toda esta parafernália metodológica/técnica no meu processo de desenvolvimento. Meu objetivo é tentar defender a idéia mais importante dos princípios ágeis, de que pessoas são mais importantes do que qualquer processo, metodologia ou ferramenta, e que os principios básicos ainda são comuns a qualquer atividade de desenvolvimento. Vou também colocar um pouco do meu contexto, para justificar algumas escolhas que faço e como isto afeta a nossa produtividade.

Até a próxima!

, , , , , ,

3 Comentários

Manutenção de Sistemas

Hoje passei grande parte do dia conversando com clientes e parceiros sobre o processo de desenvolvimento em sistemas onde a maior parte do código é legado. Feliz ou infelizmente este é o caso da White Fox, onde o maior volume de esforço é feito em sistemas que já possuem muitos (alguns mais de 10) anos de existência (vieram por herança da empresa de onde a White Fox foi spin-off). Se por um lado isto demonstra um certo grau de satisfação do cliente por ter mantido o desenvolvimento conosco este tempo todo; por outro significa uma maior complexidade para manter e evoluir o sistemas.

Um ponto interessante que muitas pessoas parecem não compreender é que um sistema “envelhece”. Um argumento comum é “se funcionou até hoje, porque iria parar de funcionar?”. O que este argumento não leva em consideração é que todo sistema tem partes mutáveis. Sejam os dados que mudam de forma ou de volume, seja a plataforma que tem que ser evoluída por obsolescência de hardware ou software. E quanto menos se mexe, mais este processo é visível; tivemos um caso recente em que vulnerabilidades no IIS 6.0 fizeram-nos mudar para o IIS 7.0, porém componentes feitos para rodar no IIS antigo não funcionavam mais no novo e para completar, o fabricante do componente nem existia mais, o que impedia o seu simples upgrade. Acho que a comparação é como a da manutenção de um carro: é possível rodar sem manutenção até um ponto onde componentes começam a parar ou apresentar defeito…. Mas quanto mais se demora para se fazer as manutenções iniciais, mais caro vai ser quando elas se tornarem inevitáveis.

Outro ponto interessante comumente visto é a concepção errônea de que um modelo waterfall seria menos propenso a erros em um processo de manutenção. Definitivamente não é verdade, o que um modelo waterfall normalmente define é um tempo alocado explicitamente para planejamento de migração de legado, testes e garantia. Porém, isto acontece a um custo bem mais elevado do que em uma metodologia ágil. Em uma metodologia ágil também seria possível planejar e gerar testes caso isto traga valor para o cliente. A único questão é quantificar este valor para que a relação custo/benefício faça sentido e estas tarefas sejam adequadamente priorizadas, de forma tão ou mais importante do que as novas funcionalidades.

E interessante que a maior parte da literatura para o desenvolvimento de software trate de novos projetos, existindo pouca coisa específica para o processo de manutenção. Metodologias como o Scrum nem sequer se aplicam a processos de manutenção. Isto me deixa intrigado pois, a meu ver, a maior parte do esforço mundial de desenvolvimento deveria ser em manutenção; não faz sentido nem é sempre economicamente viável reescrever um sistema do zero a cada grande mudança. E nas metodologias ágeis há um dilema já logo no início, pois todas pregam a preparação de um sprint com aquelas tarefas que geram maior valor para o cliente. Ora, evoluir algo que já funciona dificilmente vai ter mais valor para o cliente do que qualquer nova funcionalidade, por menor que seja. Assim, evoluções acabam sempre ficando para segundo plano até o ponto onde acontece um problema grave com uma funcionalidade antiga e aí é um deus-nos-acuda para portar a mesma à toque de caixa ou achar um guru que ainda consiga mexer no legado de uma forma relativamente segura.

Há algum tempo atrás escrevi um post sobre a necessidade de refatoração de grandes sistemas. Hoje vou mais adiante, acho que além da refatoração, é necessário um esforço constante de migração e evolução dos sistemas e componentes legado. Isto deve ser feito enquanto o conhecimento de negócio e técnico ainda está disponível na empresa, para que o custo de atualização fique em um patamar viável. Neste aspecto, a existência de testes unitários e de integração são de grande valia, pois o tempo gasto na geração deste tipo de artefato acaba também se pagando na hora de uma eventual atividade de evolução. Toda a questão é como vender este tipo de atividade para um cliente. O que temos feito recentemente é tentar definir um percentual fixo das horas de desenvolvimento exclusivamente para serem gastas na evolução de partes antigas do sistema. Claro que estas tarefas também estão sujeitas a uma priorização entre si, usando como critério as funcionalidades que são mais críticas para o negócio do cliente e o grau de obsolescência das mesmas.

Outro ponto crítico na questão da manutenção é o procedimento de deploy. Em um sistema novo, fazer deploy é sempre algo trivial (acabamos de encerrar um sistema, por exemplo, onde o deploy é uma simples cópia de arquivos e uma pequena configuração do IIS; daqui a 3 anos duvido que este processo seja tão simples se a aplicação não for evoluída). E o esforço para se manter atualizado uma documentação de deploy para cada Service Pack ou alteração de infra-estrutura não é trivial e convencer o cliente de que isto é algo prioritário e que deve ser feito durante algum sprint é também algo extremamente complicado. De novo o grande motivador é a ocorrência de um erro mais grave, porém aí tudo já vai ser feito sob uma considerável quantidade de stress.

Ainda não sei qual é a melhor maneira de lidar com estas situações. Na White Fox estamos trabalhando com alguns clientes no sentido mitigar estes problemas e trabalhar ativamente na migração de legado. Os sistemas são tão grandes, no entanto, que é necessário um bom trabalho de priorização e um critério rígido de limites para que este esforço não consuma todo o previsto de uma deterimada sprint. Estamos tendo sucesso em alguns casos, porém ainda temos muito a melhorar e acredito também que algumas ferramentas poderiam ser de grande valia na avaliação dos resultados de determinada linha de ação.

, , , ,

2 Comentários