De volta 2!

Oi pessoal!! Depois de um looooongo tempo sem postar, finalmente consegui um tempinho. Olhando o último post, dá pra notar que estávamos extremamente ocupados no início do ano passado. Mal podíamos imaginar o que viria pela frente. Do ano passado para cá, iniciamos um enorme projeto com um de nossos clientes, o Sistema de Ensino Poliedro, que nos tomou praticamente 1 ano de contínua dedicação e deve ainda prosseguir por bastante tempo.

O projeto foi participar da construção de aplicativos móveis, iOS (iPhone e iPad) e Android (smartphones e tablets), para disponibilizar para alunos uma grande parte das informações até então só existentes no site, como agenda, notas, resultados de simulados etc. Nossa parte foi expor em serviços toda a infraestrutura do domínio que tínhamos em sistemas, de forma a funcionar tanto para os aplicativos móveis quanto para uma nova versão web, também a ser desenvolvida. Trabalhamos com empresas parceiras, que ficaram responsáveis pela construção das interfaces móveis e web.

O maior desafio foi a definição desta API de serviços, para ser o mais reusável possível sem comprometer a facilidade e uso pelas interfaces. E tinha que ser extremamente escalável, já que o número de alunos previstos para os aplicativos era superior a 30.000.

A nossa opção foi utilizar a ASP NET MVC Web API. Uma das grandes vantagens desta arquitetura foi que o Web API já é capaz de retornar dados em vários formatos, como XML e JSon, o que foi providencial para nossa meta de reusabilidade. E por ser bem leve, atendeu bem também a necessidade de escalabilidade.

Como o sistema é, em quase todas as telas, somente leitura, optamos por otimizar o acesso a dados com a criação de banco de dados construídos especialmente para este fim. Assim, desenvolvemos procedimentos para trazer dados dos nossos bancos transacionais e produzir uma informação desnormalizada, pronta para consumo pela interfaces. Embora isto tenha exigido uma grande capacidade de processamento para gerar esta informação consolidada (optamos por executar isto durante as madrugadas), o acesso de cada uma das interfaces ficou extremamente rápido e escalável.

O resultado foi um sucesso completo. Os aplicativos iOS e Android tiveram centenas de downloads nos primeiros dias e hoje, menos de 6 meses após o lançamento, já temos mais de 10.000 aparelhos, entre tablets e smartphones, com acessos de alunos. O resultado foi tão positivo que já continuamos no mesmo ritmo para construção de mais aplicativos, agora voltados para professores e gestores. Nosso cliente provavelmente é hoje uma das instituições mais modernas do país, em termos de software. Ficamos super orgulhosos de termos participado deste projeto!

Em paralelo a isto, outros clientes nossos estão nos demandando bastante. Com isto, a White Fox está totalmente ocupada e estamos em busca urgente por bons desenvolvedores. Se você quer saber mais ou se candidatar, envie-nos seu currículo para rh@whitefox.com.br

Espero não demorar mais um ano para o próximo post! Até breve!

P.S.: Para quem ainda não se inscreveu e está no rio, não percam o DNAD 2013, nestes dias 2 e 3 de agosto. A grade de palestras está excelente e a White Fox é uma das patrocinadoras!

, , , , , , , , ,

Deixe um comentário

De volta!

Oi pessoal! Longo tempo sem postar! Nos últimos meses acabamos ficando extremamente ocupados na White Fox, graças à coincidência da finalização de um grande projeto de um dos nossos maiores clientes com a finalização e o lançamento de mais um produto com a marca White Fox. Felizmente tudo correu excepcionalmente bem, apesar de ter dado muito trabalho e ainda estarmos trabalhando em um ritmo acima do normal. E, felizmente ou infelizmente (depende do ponto de vista), tudo indica que o ritmo este não vai diminuir em curto prazo, já que estamos com algumas iniciativas em andamento que, se concretizadas, irão significar mais um grande esforço adicional.

O projeto cuja primeira entrega foi no final do ano passado, representa o resultado de quase um ano de trabalho. Nele, tivemos que desenvolver um novo mecanismo de comunicação com o Banco Central do Brasil (BACEN), baseado em troca de mensagens e em substituição a um antigo formato baseado em emulação de terminais (sim, isto ainda existe!). O projeto foi de âmbito nacional e envolveu todos os bancos e corretoras de câmbio do país. A segunda fase do projeto está em andamento neste primeiro semestre e deve consumir esforço até meados deste ano. Por ser uma área de infraestrutura (mensageria), fizemos praticamente tudo utilizando o Web Service Factory, da Microsoft, que facilitou enormemente a criação de mensagens e serviços. As mensagens do BACEN são artefatos XML, o que nos fez utilizar XSLT para a geração e transformação das mesmas. As nossas escolhas permitiram que tivéssemos um desenvolvimento rápido e uma grande facilidade de manutenção, o que foi crucial para finalizarmos tudo a tempo. Estamos mantendo estas escolhas para esta segunda fase e, graças a elas, estamos adiantados no nosso cronograma.

O segundo grande evento foi a finalização da plataforma da XChange. Este produto é um site de comércio eletrônico de Internet, destinado à venda de cartões pré-pagos VISA Travel Money (VTM) para quem viaja ao exterior a turismo ou trabalho, no qual tenho participação. A White Fox foi chamada a fazer um software que funcionasse com o site estático desenvolvido por uma agência de propaganda. O desafio foi criar mecanismos que permitissem trabalhar as áreas dinâmicas do site sem prejuízo para o layout e para as funcionalidades de navegação. Para atender a este objetivo, tivemos que fazer uma evolução significativa no nosso framework de interface. O resultado foi melhor do que esperávamos, e agora o nosso framework suporta a integração em qualquer layout HTML, com um mínimo de esforço.

O princípio que utilizamos foi uma evolução do funcionamento anterior da nossa engine MVC, que era gerar HTML através de transformações XSLT. Nesta nova versão, ao invés de gerar a página inteira, nós somente geramos trechos da página. Assim, podemos apontar um determinado <DIV> em um HTML e ele é substituído, em tempo de renderização, por um <DIV> de conteúdo dinâmico. Isto facilita a construção e a visualização de páginas, permitindo a “injeção” de trechos dinâmicos nos arquivos HTML provenientes das agências, praticamente sem modificações nos mesmos (nem mesmo os famigerados <% %> das engines tradicionais MVC). O resultado foi excepcional e graças a esta nova versão, somos capazes de gerar sistemas de maneira ainda mais rápida.

Espero voltar a escrever posts com uma regularidade mínima. No próximo, vou falar um pouco sobre os desafios que enfrentamos recentemente com desenvolvimento para alto desempenho, com paralelismo em SQL Server. Até a próxima!

, , , , , ,

1 Comentário

White Fox: Ano I

Oi pessoal! Este mês comemoramos um ano de White Fox… Nem parece que já se passou um ano desde o início! Acho que o tempo passa mais depressa quando estamos fazendo algo que gostamos. Depois de definirmos o conceito do que buscávamos e iniciar a empresa, passamos este ano consolidando e definindo processos de trabalho e refinando nossos objetivos e áreas de atuação.

Na parte interna, conseguimos evoluir muito mais do que esperava. Nosso framework está extremamente maduro, conseguimos atingir o que eu sempre buscava em termos de produtividade, para manutenção e sistemas novos. Claro que ainda tem muita coisa a evoluir, especialmente nas partes mais complexas, ligadas à interface. Mas nas áreas de infra-estrutura, de suporte ao domínio (repositórios, serviços) e de serviços horizontais (segurança, log, agendamento), atingimos um nível ótimo. Atualmente, quando iniciamos uma nova aplicação, perdemos muito pouco tempo até termos algo básico no ar, com todas as funcionalidades mínimas necessárias (login etc.). Vamos continuar evoluindo, mas ele está tão maduro que hoje precisamos gastar muito pouco tempo na sua manutenção e evolução.

Outra área que evoluiu muito mais do que esperava foi a parte de gestão de processos de desenvolvimento. Graças ao esforço do Christian (que gosta muito desta área), conseguimos definir, implantar e criar ferramentas de gestão dos processos de desenvolvimentos ágeis que usamos nos nossos projetos novos e de manutenção. Ainda tem muita coisa a ser feita, queremos automatizar mais, termos uma ferramenta mais robusta (e talvez até um produto derivado dela) e dar ainda mais transparência para nossos clientes. Estamos criando algo muito interessante para a gestão de projetos de manutenção no formato ágil. No decorrer deste segundo ano, com certeza teremos novidades neste aspecto.

Refinamos um pouco os objetivos da empresa. Continuamos a buscar uma excelência técnica e um local de trabalho extremamente agradável para o desenvolvedor. E preferimos manter uma equipe pequena, mantendo nossa maneira de trabalho, do que crescer e sacrificar qualidade. Hoje o software White Fox é utilizado principalmente quando o cliente quer algum diferencial de qualidade e velocidade de construção. Continuamos a selecionar também nossos projetos, preferindo clientes que possuam experiência na contratação de desenvolvimento ágil e que consiga identificar um ROI nos projetos que necessita.

Consolidamos também nosso programa de novos integrantes. Hoje a White Fox possui somente 3 posições: estagiários, sócios juniores e sócios sêniores. O programa de estágio é utilizado para identificar e treinar novos talentos. Sócios júniores são pessoas que possuem um talento excepcional, estão identificados com os nossos valores e estão em um momento de transição até ter todo o necessário para ser um sócio sênior. E os sócios seniores participam da gestão da empresa e são convidados a atuar como investidores em todos os nossos empreendimentos. O modelo enxuto e claro facilita a conversa com novos interessados e auxilia na identificação do que é necessário para se fazer para ser parte dela.

As perspectivas para o segundo ano são ótimas. Continuaremos a atuar nas melhorias do nosso processo interno de desenvolvimento, buscando cada vez mais inserir o cliente com o uso de ferramentas automatizadas. Continuaremos a buscar projetos onde o software seja o diferencial competitivo. E continuaremos a trabalhar pra fazer produtos cada vez melhores, de maneira rápida e com um custo adequado. Vou mantê-los informados de como isto está evoluindo aqui por este blog. Até a próxima!

, ,

2 Comentários

C#, Closures e Resharper

Olá pessoal! Tivemos um problema esta semana, em tese de origem simples, mas que nos tomou um tempo enorme de debug. Quero compartilhar aqui para tentar evitar que outros sigam pelo mesmo caminho!

Para começar, imaginem este código ilustrativo abaixo. Uma rotina simples para enumerar duas vezes por uma coleção e retornar a mesma, modificada. Claro que isto é somente representativo de um código real, mas imaginem qualquer função que por alguma razão modifica elementos de uma lista e retorna a mesma ou um subconjunto da mesma. Trivial, certo? O resultado esperado desta execução são duas linhas com o número “2”.

class MinhaClase {
    public decimal Valor { get; set; }
}
    
class Program {
    static IList<MinhaClase> Calcular(IList<MinhaClase> classes) {
        foreach (var minhaClase in classes) {
            minhaClase.Valor = 1;
        }
        foreach (var minhaClase in classes) {
            minhaClase.Valor++;
        }
        return classes;
    }
    static void Main(string[] args) {
        var classes = new[] {
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 1}
                            };
        var result = Calcular(classes.Where(c => c.Valor != 1).ToList());
        foreach (var minhaClase in result) {
                Console.WriteLine(minhaClase.Valor.ToString());
        }
        Console.ReadLine();
    }
}

Todo o problema começa quando a gente tenta refatorar e melhorar o código. Como todo bom desenvolvedor, nós usamos o Resharper, que é uma ferramenta fantástica da JetBrains para nos ajudar a gerar um bom código. Na White Fox, nós a usamos praticamente todo o tempo, ela nos poupa muito esforço de código e ajuda a evitar problemas graves de maneira completamente automática. Porém, neste caso, como a rotina “Calcular” simplesmente enumera, o Resharper vai sugerir que a gente mude o IList<> para sua classe base, um IEnumerable<>. A regra seguida por ele faz sentido pois se só estamos usando métodos da classe base, não seria necessário usar a classe derivada na assinatura. O código gerado então após a refatoração fica da seguinte forma:

class MinhaClase {
    public decimal Valor { get; set; }
}
    
class Program {
    static IEnumerable<MinhaClase> Calcular(IEnumerable<MinhaClase> classes) {
        foreach (var minhaClase in classes) {
            minhaClase.Valor = 1;
        }
        foreach (var minhaClase in classes) {
            minhaClase.Valor++;
        }
        return classes;
    }
    static void Main(string[] args) {
        var classes = new[] {
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 1}
                            };
        var result = Calcular(classes.Where(c => c.Valor != 1));
        foreach (var minhaClase in result) {
                Console.WriteLine(minhaClase.Valor.ToString());
        }
        Console.ReadLine();
    }
}

Depois deste refactor, o resultado do programa não deveria mudar, certo? Errado! Os mais experientes irão saber que o resultado da execução deste segundo código é uma lista vazia!

A explicação para isto tem a ver com o suporte do C# para closures (ver este post, que explica o que são de maneira simples, ou esta explicação mais acadêmica aqui). As operações LINQ simplesmente geram predicados para uma closure que é executada no momento que a coleção é acessada. Tem a ver também com a diferença básica entre um IList<> e um IEnumerable<>, já que num IList<>, um GetEnumerator (executado em um foreach) sempre aponta pra coleção interna, já existente… Já em um IEnumerable<>, o GetEnumerator vai sempre acionar o container para criar coleção e aí enumerar os elementos… Isto significa que a coleção de um IEnumerable<> produzido por uma closure vai ser gerada A CADA foreach, e não uma única vez. Ou seja, cada foreach da rotina “Calcular” executa a closure, bem como o foreach do resultado final. Como neste caso a closure é um filtro de Valor != 1, no segundo foreach da rotina “Calcular” e no último (onde ele imprime o resultado no console) ele simplesmente vai retornar 0 elementos!

Onde erramos? Os mais experientes vão notar que erramos na hora que tiramos o .ToList() do final da closure que define o filtro. A razão da remoção foi que como a rotina “Calcular” agora aceita um IEnumerable<>, o ToList() não seria mais necessário. Sem o ToList(), a closure não é mais executada naquele momento e sim passada para a variável e aí toda a rotina Calcular para de funcionar. Basta colocar o .ToList() de volta que tudo passa a funcionar novamente. Este erro, aparentemente óbvio, na prática é muito fácil de ser cometido, pois o uso de expressões LINQ é cada vez mais comum e, em um código complexo de negócios, se a função sendo chamada tem parametros IEnumerable<>, uma closure pode acabar sendo enviada inadivertidamente.

Portanto, neste caso, o Resharper não só facilita os erros, como pode ainda produzir alguns muito difíceis de identificar (no nosso casso, foram horas gastas até achar onde estava a closure sendo passada). Claro que desligamos esta sugestão na nossa configuração do Resharper e o problema foi resolvido. É importante ressaltar que, mesmo apesar deste caso, continuamos a achar o Resharper uma ferramenta essencial de trabalho para nós.

Outro ponto que merece ser comentado, é que pesquisando este problema, entendemos melhor o funcionamento do IEnumerable<> e o IList<>. Como o IEnumerable<> sempre tem que gerar a coleção a cada GetEnumerator(), podemos ter um desempenho pior das coleções, na maioria dos cenários, comparado a um IList<>. Só isto já seria motivo suficiente para favorecer o uso do IList<> ao invés do IEnumerable<>.

Bom, espero ter ajudado a poupar o tempo de outras pessoas ao deparar com problemas similares. Por causa deste problema, acabamos tendo que conhecer bem mais sobre closures e coleções no .NET, assim, no final, a existência deste bug até que foi benéfica!

Até a próxima!

, ,

4 Comentários

Autenticação e Segurança

Olá pessoal! Esta semana quero falar de um assunto de infra-estrutura, mas extremamente crítico em nossas aplicações: a parte de autenticação e segurança de usuários. Quando fazemos uma aplicação Web, temos que definir como nossos usuários serão autenticados. Os desenvolvedores felizes são aqueles que utilizam autenticação integrada em suas aplicações e deixam a cargo da rede e do Active Directory (AD) a tarefa de identificar quem está logado. Claro que isto funciona bem em ambiente de Intranet. Na Internet é até possível, mas em geral é uma alternativa que não é utilizada por consumir uma banda excessiva e ser estranho para o usuário, abrindo aquela tela de login Windows cada vez que se acessa um site. Existe ainda a autenticação básica, utilizada em sites HTTPS e que basicamente mapeia o usuário logado para um usuário de rede. Esta alternativa também consome bastante banda e é pouco amigável para o usuário.

Nos cenários comuns, a autenticação é feita diretamente pela aplicação e o usuário se depara com campos de “login/senha” em alguma página da aplicação Web. Nestes casos, o mecanismo mais usado no .NET é o FormsAuthentication, com cookies. Nesta modalidade, após fornecer uma combinação login/senha válida, o sistema cria um cookie que é utilizado para autenticar as requisições subsequentes do usuário. Este cookie é associado à web session do usuário (na verdade, ele serve para “amarrar” a sessão, identificando-a a partir de sua criação) e permanece válido pelo tempo definido de expiração – normalmente 30 minutos. É possível ter FormAuthentication no formato cookieless, mas é menos utilizado por ser menos seguro.

Usar o FormsAuthentication (com cookies) no .NET é algo relativamente simples, todo o trabalho é feito pelo próprio framework. Para a maior parte dos sistemas, isto atende completamente a necessidade de autenticação. Em sistemas um pouco maiores, é possível também incluir uma implementação do security membership provider (ver estes excelentes posts aqui sobre o assunto), e ter a definição de perfis por usuário, resolvendo também as necessidades de autorização. É possível encontrar na web vários exemplos de implementação até com algumas customizações destes providers, como esta aqui fazendo uso de WCF.

Os problemas começam a surgir quando a aplicação cresce ao ponto de ter que ser integrada a outros sistemas e plataformas ou ter que coexistir com outras, ou ainda ter que suportar múltiplas formas de acesso (Windows Forms p. ex.). Nestes casos, temos que ter maneiras alternativas para identificar usuários e até fazer transferências de uma aplicação para outra. Em grandes empresas, surgem necessidades como definir uma política de segurança de expiração de senhas, um banco de dados único para os usuários entre as aplicações e até a necessidade de fazer login integrado ao AD em algumas situações. Aplicações de maior porte podem também necessitar de melhor desempenho na infra-estrutura de segurança, já que isto é utilizado em praticamente todos os requests. Finalmente, em aplicações corporativas é usualmente necessário ter coletas de estatísticas de uso e usuários ativos, para efeito de monitoramento e planejamento de carga. Nestes casos, a implementação padrão de segurança pode não atender completamente e é necessário desenvolver uma própria.

No nosso caso, desenvolvemos uma solução de segurança que já está em uso há mais de 12 anos. É uma solução WCF bastante simples, com um banco de dados de usuários, perfis e aplicações, classificados por empresa. Os serviços expõem mecanismos de autenticação direta por senha e por integração ao AD. A senha não é armazenada, utilizamos um hash gerado pelo próprio .NET. A integração ao AD é algo simples ainda, baseado no ID de rede (só liberamos para algumas poucas aplicações, onde o ambiente é controlado e os riscos baixos), há planos de se utilizar o Federated Services para algo mais avançado. Todo o acesso aos serviços são feitos por um intermédio de um agente de segurança, que implementa as classes de segurança e faz a tradução para os DTOs do WCF. O sistema suporta políticas de segurança, complexidade e expiração de senhas e coleta todas as estatísticas de uso das aplicações.

A solução possui ainda métodos para suportar a geração de tokens de segurança. Este tokens são identificadores (guid) que são passados, via querystring ou qualquer outro mecanismo, de um aplicação para outra. Eles possuem uma expiração rápida e podem ser utilizados apenas uma vez. Cada token está associado à sessão do usuário que o criou e pode ser utilizados para permitir o login rápido em outra aplicação. Com isto, conseguimos fazer a transferência de uma aplicação para outra de maneira transparente para o usuário.

Todos os nossos sistemas fazem uso da mesma solução de segurança, sempre através da utilização do agente. O FormsAuthentication continua sendo usado, a diferença é que a identificação do usuário é feita por nossa solução, não utilizando o membership provider. A única coisa que fazemos na aplicação é implementar classes para encapsular as chamadas ao agente e utilizar a session do ASP.NET para armazenar o usuário ativo e as respectivas permissões. Isto garante um melhor desempenho, evitando que a cada request os dados do usuário e respectivos perfis tenham que ser obtidos via chamadas WCF.

Aplicações Windows também utilizam o agente de segurança. A diferença é que como não temos um SessionID, como na Web, acabamos por simular um criando uma chave guid cada vez que a aplicação é inicializada. Esta chave é utilizada como se fosse o identificador daquela sessão Windows e pode também ser usada para se gerar tokens de autenticação. Assim é possível, por exemplo, clicar em um botão de uma aplicação Windows Form e se abrir um browser, com uma página de uma aplicação Web, já autenticada.

É isto, espero ter dado uma idéia de como lidamos com autenticação e autorização em nossos sistemas. Se alguém tiver alguma dúvida específica, basta entrar em contato. Até a próxima!

, ,

4 Comentários

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

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.

Junte-se a 229 outros seguidores