Home    |   Treinamento   |   Serviços   |   TechZone   |   TechTalk   |   Clientes   |   Fale conosco 

TechTalk: Os maiores fatores de fracasso para projetos em C++
Esse texto descreve os fatores que mais contribuem para o fracasso de sistemas desenvolvidos primariamente em linguagem C++, com base na visão pessoal baseada na experiência do autor.
Alguns destes fatores podem ser aplicados ao desenvolvimento de software independente da linguagem de programação utilizada, outros são mais aplicáveis ou mesmo específicos para os desenvolvidos em C++.

Fatores aplicáveis a projetos em geral

A panacéia da Engenharia de software

Uma das maiores transformações na área de Software e que resultou em grande mudança no modo de trabalho foi a recente explosão da engenharia de software. Os processos e metodologias estão sendo adotados por praticamente todas as empresas do setor, muitas vezes de uma forma até obsessiva. A empresa que hoje não tem sua metodologia é mal vista, e no caso de uma consultoria que presta serviços para outras empresas pode estar fora de mercado.
A panacéia da engenharia de software ao contrário do que pode parecer não é uma crítica de um técnico; antes de todo esse movimento do pessoal da engenharia de software, o que ocorria era a panacéia da programação. Acabamos de sair da era em que se achava que tudo se resolvia codificando diretamente, ou projetando módulos e funções para codificar em seguida. Pouca importância era dava as atividades de captação de requisitos e planejamento, documentos formais, etc. Obviamente o resultado final era ruim.
Hoje ao contrário desse passado recente quem rouba a cena é a engenharia de software e os “papas” do assunto literalmente torcem o nariz ao falar com profissionais técnicos, e imaginam que os processos podem eliminar a necessidade de skill técnico, ou mesmo que o processo é mais importante ou mais complexo ou mais nobre que o projeto técnico e codificação. Agora se pensa que a codificação e tudo que endereça as questões técnicas tem menor importância e o que sempre esteve errado era somente a falta de “processo”.
O fato é que, matematicamente, a resultante da somatória destas vertentes ficou na mesma, as empresas (claro que referindo-se a maioria delas, nunca se pode generalizar) continuam produzindo Software da forma errada, apenas o que mudou foi o sentido do erro. É provável que estejamos a uma ou duas décadas da “era de aquário”, quando o mercado perceber que o equilíbrio é a única forma de trazer resultados satisfatórios.
Voltando a realidade de hoje, os efeitos mais comuns da panacéia da engenharia de software são:
  1) Excesso de foco no processo, gerando meses, anos de esforço na geração de documentação cuja maior parte nunca será utilizada ou poderá ficar obsoleta ao final do processo, reuniões desnecessárias ou com pessoas desnecessárias, perda do foco do que realmente é importante, falta da visão de que é necessário ter feedback do software “palpável” ou de um protótipo tão cedo quanto possível. Esse efeito é chamado informalmente de “Analysis paralysis”.
  2) Falta de projeto de Software. Uma definição clara dos objetivos finais do sistema a ser desenvolvido é muito pouco diante do objetivo maior que é produzir software com qualidade. O conceito de qualidade vai além de gerar software que “funciona” como esperado, pois além disso o software tem que sofrer manutenção, oferecer performance, suportar mudanças, etc. Não basta definir o que deverá se feito, mas também como deverá ser feito; não se pode criar um abismo entre análise e codificação.

Falta de gerenciamento de bugs

Em relação ao controle de bugs, o que se costuma fazer é, durante a fase de testes, listar os erros do sistema e gerenciar essa lista com base no cliclo de correções e testes. Porém quando se começa a codificar, já é hora de começar a anotar os bugs. Se isso parecer não fazer sentido, é porquê tudo depende do conceito de bug.
Vamos a alguns exemplos de problemas em sistemas:
 • um campo de digitação em um sistema de controle de estoque, que determina quantidade do produto, está desalinhado horizontalmente em relação ao demais campos
 • o mesmo campo está aceitando números absurdamente grandes
 • o mesmo campo está aceitando números negativos ou letras
 • as senhas de acesso a esse sistema estão em uma base de dados, e o campo que armazena a senha não está criptografado
 • no mesmo sistema os campos estão aceitando injeção de cláusulas SQL, e que podem retornar dados confidencias da base de dados
 • as queries de consulta não estão usando índices, ou ainda não estão otimizadas, e ficarão lentas com grande volume de dados
Qual dessas situações é um bug ?
Dependendo do contexto da aplicação, as situações acima podem ser mais ou menos importantes e críticas. Isso pode variar também com a qualidade do trabalho que o programador ou empresa fornecedora se propôs a entregar ao cliente.
A princípio em um sistema profissional todas essas situações devem ser corrigidas; pode ser que algumas delas sejam aceitáveis para um primeiro release, mas certamente não o serão no release final. Para que se tenha esse controle é importante gerenciar os bugs e melhorias desde o início da codificação, pois desde o início o programador costuma observar a necessidade de melhorias futuras que podem se tornar bugs potenciais, e nunca se deve confiar inteiramente na memória.
Sistemas maiores com grandes equipes demandam um sistema comercial de gerenciamento de bugs, uma ferramenta de "bug tracking". As ferramentas profissionais não são apenas um repositório centralizado de bugs, mas também contemplam o seu ciclo de vida, a integração entre as equipes de correção e de testes, etc. A classificação dos bugs também é muito importante, em termos de criticidade e prioridade. Com tudo isso fica muito mais simples garantir a cobertura de todos os erros e o workflow mais eficiente de correção.

Biblioteca de classes onde deveria haver um Framework

Partindo-se do conceito: uma classe pode ser entendida como uma estrutura computacional que expõe uma interface ao mundo externo e encapsula o acesso aos seus dados internos e a funcionalidade a que se propõe. As classes contidas em uma biblioteca tipicamente não estão acopladas a nenhum domínio de aplicação específico e podem ser reusadas em diversos cenários. Um exemplo de biblioteca de classes pode ser a própria C++ Standard Library, onde classes para operações com strings, containers e outras podem ser utilizadas em diversos cenários. Outro exemplo é a biblioteca boost, que provavelmente terá classes incorporadas ao C++ padrão em breve.
Um framework estende o conceito de biblioteca de classe, pois as classes colaboram para definir uma arquitetura reutilizável para um certo domínio de aplicações. As classes em uma biblioteca são passivas no sentido de que são chamadas pelo programador da aplicação de acordo com o fluxo que ele mesmo definiu. Em um framework, as classes irão definir o fluxo da aplicação, baseadas em algum padrão (pattern), adequado para aquele domínio de aplicação para o qual o framework foi escrito. Um exemplo de framework seria a MFC, que define um modelo para construção de aplicações para Windows (a classificação da MFC como framework C++, framework O.O., ou bliblioteca de classes é uma longa discussão da comunidade de programadores, e está fora do escopo desse texto). Outro exemplo conhecido é o “struts”, bastante utilizado por programadores java, e que implementa o modelo MVC (ou seus derivados).
Projetos de grande porte precisam de um framework, não só de uma biblioteca. Nesse tipo de projeto normalmente o desenvolvimento é realizado por equipes de desenvolvedores alocadas para cada módulo do sistema. Sem um framework, a lógica de controle que une as classes para formar o módulo da aplicação tem que ser escrita em cada equipe. Não é incomum as situações onde cada módulo é implementado seguindo um modelo completamente diferente. Como sempre, os reflexos dessa estratégia (ou falta de estratégia) ficam mais claros na fase de manutenção, porém mesmo o desenvolvimento fica mais caro.

Reinventar a roda (ainda outra leitura do problema)

Nós programadores nos sentimos mais seguros quando sabemos exatamente o que faz cada linha de código sob nossa responsabilidade, e a única maneira de obter essa segurança é escrevendo todo o código com as nossas próprias mãos. Alterar código "alheio" passa pela entediante fase de estudar o que foi feito pelo outro programador, entender como ele pensou na solução do problema. Programar é como jogar xadrez; a cada partida se imagina uma abordagem diferente, mesmo que seja contra o mesmo adversário, mesmo que seja resolvendo o mesmo problema, apenas as construções clássicas de programação ou problemas recorrentes podem fugir a essa regra. Se a visão da solução ideal para um determinado problema muda com o tempo para um mesmo profissional, o que dirá entre profissionais diferentes. Com isso vem os inevitáveis pensamentos do tipo "eu não teria feito assim", ou ainda pior "isso não está bem feito", quem nunca pensou assim nunca programou. É interessante que o mesmo acontece analisando a posteriori o próprio trabalho !
Essa armadilha, é também conhecida como síndrome do “não inventado aqui” (ou NIH – “Not Invented Here”), porém o programador não costuma negar apenas código escrito por empresas ou entidades externas, mas nega também código escrito pelo próprio colega de empresa ou de equipe, gerando uma síndrome ainda muito mais abrangente e fatal para a saúde do projeto que poderia ser chamada de síndrome do “não criado por mim”.
Este problema clássico é mais sério do que aparenta. A primeira vista tem-se apenas o custo adicional na fase de desenvolvimento decorrente do aumento do tempo\esforço geral de programação. A falta de reutilização contribui não só para o aumento do custo do projeto, mas para baixa da qualidade. Este custo pode ser muito maior na fase de manutenção quando é necessário alterar determinada funcionalidade do sistema. Uma alteração que aparentemente afeta um único ponto do código pode ser mais abrangente e complexa devido a duplicação de código. Ao executar a manutenção parcialmente, o “bug” será descoberto mais tarde, causando um novo ciclo de manutenção, que terá um custo muito maior do que se previu inicialmente, além do desgaste com o cliente, etc. Na próxima manutenção o gerente do sistema estará ciente do problema de duplicidade, mas o custo continuará sendo mais caro do que deveria varrendo todo o código sendo o correto localizar o ponto de implementação da funcionalidade e a alteração reflete para o sistema todo.


Fatores específicos para projetos em C++


Otimização prematura

Otimização prematura é uma armadilha típica do C++, citada por diversos autores de livros e artigos, e a frase de C.A.R. Hoare é bastante conhecida:
   Premature optimization is the root of all evil in programming.
   (C.A.R. Hoare)
Apesar de conhecida, é muito comum programadores experientes caindo constantemente nessa armadilha, que é um vício do programador.
O problema ocorre quando o programador se detém demasiadamente nos aspectos de otimização do código em detrimento da estrutura geral, e dos verdadeiros gargalos que podem estar em um nível mais alto. Muitas vezes o código final se torna muito mais difícil de manter pois o programador codificou em um nível muito baixo para otimizar, por exemplo, código que acessa base de dados! Na falta de uma boa arquitetura e estrutura, seja procedural ou orientada a objeto, etapas posteriores de otimização em níveis mais altos ficam muito mais difíceis.
A estrutura de um programa como um todo, seus algoritmos mais globais, influem na performance mais do que aspectos de micro otimização. Essa é uma outra forma de se enxergar o desvio de foco que acontece nessas situações. Claro que sempre há os casos onde o “tunning” de código tem que ser feito, efetivamente considerando em baixo nível o código gerado, mas essa etapa tem sua hora, e seu lugar no código, o que varia de acordo com a natureza do programa sendo codificado.

Preconceito do programador C em relação ao C++

O programador com vício em otimização prematura é um forte candidato a ver com preconceito o overhead de performance nas estruturas do C++. Os programadores mais acostumados com o baixo nível de programação como C “puro” e assembler, onde todas as construções incorrem em operações “curtas”, a passagem de parâmetros se dá por valor para os tipos abstratos e por referência para vetores e estruturas, e o nível máximo de indireção de chamada é um ponteiro de função, se assustam ao imaginar a execução de construtores e chamadas de métodos passando por virtual tables.
Nem sempre é tão simples avaliar problemas de performance de uma linguagem ou programa, pois isso depende de uma série de fatores. Por exemplo nos programas que acessam bases de dados normalmente o gargalo está neste ponto. Claro que há situações onde o código é mesmo responsável por lentidão de resposta e precisa ser otimizado. Há situações onde a passagemn por um construtor de objeto é inadimissível, tudo depende do contexto. A questão é saber discernir quais são essas situações, pois sempre que possível, a otimização da estrutura geral de um programa deve ter prioridade sobre a micro otimização.
Em relação a outras linguagens, como as baseadas nos modernos ambientes gerenciados (não só o fato de executar em ambiente gerenciado mas também a estrutura da linguagem faz diferença), o C++ está muito distante em performace, e muito próximo ao C, desde que se programe da forma correta. De uma forma muito simples, sem levar a risca as diferenças de performance representadas, isso poderia ser representado graficamente da seguinte forma:


O risco associado ao preconceito com o C++ é deixar de utilizar as grandes facilidades que o C++ oferece em relação ao C, principalmente no sentido de produtividade e estruturação do código, disponibilidade de bibliotecas, etc.

Nivelamento do código muito conservador ou muito arrojado

Uma estratégia adotada por muitos gerentes de sistemas é nivelar por baixo as construções da linguagem utilizadas na implementação, diminuindo o skill técnico necessário para entendimento do código. O benefício esperado é que qualquer programador possa entender o código, e que haja facilidade na alocação de profissionais para manutenção. Por um lado isso é uma completa verdade. Por outro lado, as vezes resolver um determinado problema da forma mais simples pode se revelar numa tentativa simplista, e com resultados muito ruins. O fato é que as linguagens de programação e o domínio típico de aplicação que as cerca não são iguais. A maioria dos programadores já ouviu a frase:
    “Quem sabe programar bem em uma linguagem sabe programar em todas”.
Essa frase é um dos maiores absurdos que se pode proferir na área de TI, e não é incomum ouví-la seja por palestrantes de mercado como por professores de nível acadêmico. Pensar dessa forma é ver o processo de desenvolvimento de Software de uma forma muito superficial. É algo semelhante a se dizer que quem sabe dirigir um carro de passeio sabe também dirigir uma carreta; e sabe, se saber é colocar a carreta em movimento e com sorte chegar ao seu destino sem nenhum estrago. Não existe o conceito de programador genérico; um programador pode ser excelente com uma linguagem, porém inexperiente e medíocre com outra, se ele se tornou excelente com uma linguagem é muito provável que possa se tornar excelente em outras, mas isso não lhe confere nenhuma credencial para trabalhar com qualquer linguagem.
Se a decisão é usar C++ “vanilla”, então é linguagem de programação escolhida provavelmente está errada. O outro extremo, muito menos comum, é montar um “time dos sonhos”, e codificar empregando os conceitos mais modernos e complexos da linguagem sem que seja necessário, isso também será fatal mais cedo ou mais tarde.

Desconhecimento do C++ moderno

Alguns dos termos que envolvem bibliotecas e técnicas modernas de programação com C++ úteis para aferir o quanto se está atualizado: stl, boost, smart pointers, template metaprogramming ... já ouviu falar disso tudo mas não sabe o que é ? Hora de se atualizar.
Visitar freqüentemente sites oficiais e páginas de bloggers como as listadas no TechZone também são úteis para se manter atualizado. Qual a perda em se desconhecer o C++ moderno ? é deixar de obter o grande ganho de produtividade com o uso dele.

Alexandre Guimarães


     Home    |   Treinamento   |   Serviços   |   TechZone   |   TechTalk   |   Clientes   |   Fale conosco 
NEXSUN • Todos os direitos reservados