Gestão científica de equipes e projetos

Na escola, já aprendemos que o engenheiro estadunidense Frederick Taylor é considerado o pai da administração científica pela invenção do modelo de administração cunhado como taylorismo. Apesar de essa linha de abertura ser demasiado entediante por trazer consigo um breve flashback do sofrimento psicológico para os que, assim como eu, foram alunos de escolas estaduais, relembrá-la é importante. Taylor tem muito o que nos ensinar no que diz respeito ao gerenciamento de equipes de engenharia de software.

Apesar de o livro Princípios da Administração Científica ter sido publicado há mais de um século, trazendo um enorme avanço para a eficiência de empresas do mundo todo, muitos dos gerentes, nos dias atuais, continuam atrasados em mais de um século quanto às suas práticas de gestão de equipes e projetos. Apesar também de a obra citada ter sido escrita com foco na indústria do início do século XX e conter diversas dicas voltadas para a produção industrial que não são aplicáveis à engenharia de software dos dias atuais, ela ensina que devemos estudar nosso trabalho cientificamente.

Se você está com sua memória dos tempos de escola afiada, já deve ter se lembrado de que o modelo de Taylor foi tão bem sucedido no aspecto da produtividade que teve um efeito negativo para a indústria estadunidense com a criação de estoques de produtos que superava a demanda pelos mesmos, o que levou os japoneses a inventarem um modelo mais eficiente no que diz respeito ao equilíbrio entre oferta e demanda, que foi apresentado para nós nas escolas brasileiras como Sistema Toyota de Produção, ou simplesmente Toyotismo. Uma vez que os japoneses introduziram o modelo de administração científica na Toyota, conseguiram, por meio desse processo, evoluir rapidamente e tornaram obsoleto o então modelo de produção mais avançado. Uma das principais conclusões que podemos tirar desse evento e dos que deram sequência é que uma vez que colocamos o nosso trabalho sob o microscópio e uma abordagem científica, somos capazes de alcançar um estado de melhoria contínua. Os japoneses não demoraram para perceber isso após o sucesso do Toyotismo, cunhando então o Kaizen.

Quando o assunto é engenharia de software, devemos nos lembrar de que a engenharia mecânica está presente no mundo desde o século XVIII, naturalmente sendo muito mais avançada e madura em suas práticas do que a engenharia de software. Por isso, devemos olhar com cuidado para esses eventos históricos em torno da engenharia mecânica para que possamos absorver o máximo de aspectos e princípios que possam ser adaptados para a nossa realidade de engenheiros de software. Foi exatamente o que Jeff Sutherland fez com o desenvolvimento do Scrum, que apesar de tratar de diversos aspectos da construção de uma aplicação, aqui iremos abordar apenas o aspecto oferecido por ele no que diz respeito ao estabelecimento de um método científico para a análise do trabalho de sua equipe. O objetivo é entender a importância que esse processo tem para o nosso trabalho e como devemos agir para desfrutar o máximo dele.

Medição dos fatores de sucesso

Toda equipe de engenharia de software possui objetivos e toda vez que nos propomos a executar um projeto, assumimos que seremos então capazes de executá-lo até o fim e entregá-lo no prazo esperado de acordo com todos os requisitos especificados, isto é, se tudo correr bem. A frase “se tudo correr bem” tem uma grande importância para a execução de qualquer tipo de projeto, portanto devemos ser capazes de desenvolver mecanismos para medir e traduzir esse termo para uma linguagem mais científica. Cada projeto tem um conjunto de variáveis distintas que juntas irão definir o que é o famoso “se tudo correr bem” daquele projeto em questão. Seu trabalho como gestor é identificar quais são esses fatores e criar mecanismos para os medir. Fatores relacionados ao sucesso de um projeto que são fáceis de se medir e normalmente medidos podem ser o número de bugs encontrados por período de tempo, quantidade de trabalho realizado em um dado período de tempo e nível de satisfação de clientes e usuários do seu produto. Uma vez que você começar a acompanhar esses tais indicadores, você poderá ver quando eles irão oscilar e então poderá ir para a fase seguinte, que é associação de causas e efeitos. Para um maior aprofundamento nessa atividade, recomendo a verificação do PMC (Project Monitoring and Control), uma process area muito importante do CMMI (Capability Maturity Model Integration) que explora esse conceito ao extremo para as mais variadas necessidades da indústria de desenvolvimento de software.

Uma vez que você identificou os fatores de sucesso mais importantes a serem medidos, irá notar no que diz respeito ao desenvolvimento contínuo que todos esses fatores deverão ser medidos em duas dimensões, sendo uma delas o tempo e a outra a quantificação do fator em si. Isso ocorre, dado que o filósofo alemão Martin Heidegger estava corretíssimo, nós somos seres temporais e portanto no que diz respeito à indústria as coisas não são diferentes. Nós somos bons ou ruins em uma dimensão temporal, logo todos seus indicadores de sucesso devem ser medidos contra o tempo para que você consiga entender se você está melhorando ou piorando com o passar dele e não existe uma melhor forma de medir esses indicadores contra a dimensão temporal do que por meio de sprints, isto é, que eu saiba. Particularmente gosto do conceito de sprints, pois todo o time é envolvido no processo de identificação das causas de oscilação dos indicadores, tornando o processo muito mais eficiente já que iremos reunir diversas perspectivas e promover o debate entre a equipe.

É importante manter em mente que os seus indicadores devem buscar representar a relação direta que a engenharia de software tem com o sucesso do seu negócio, portanto se você acredita que a produção de mais funcionalidades irá ajudar na retenção ou aquisição de consumidores novos, é necessário então que você busque um meio de associar a relação de causa e efeito que seu software tem com seu negócio. Um erro que já cometi foi o de me importar somente com a entrega do produto no prazo especificado, sem muito me importar com o negócio que iria se beneficiar daquele produto. Quando fazemos isso, inevitavelmente o produto entregue dificilmente estará otimizado em seu potencial máximo para o negócio em questão, na verdade, toda otimização aplicada foi feita somente para reduzir o tempo de entrega e ficar dentro do orçamento planejado. Portanto, tenha em mente que indicadores genéricos como número de funcionalidades ou story points entregues por período de tempo são essenciais, mas se você se mantiver preso somente a esses indicadores genéricos, não será capaz de utilizar o método científico para entender a totalidade da relação entre software e o sucesso do seu negócio.

Estabelecendo o método científico

Uma vez que você tem suas medições configuradas e operando, vem então a hora de aplicar o método científico. Sempre que houver uma mudança nos indicadores, você irá junto da sua equipe tentar entender o motivo de determinada mudança. Produzirão então uma tese e, em seguida, tentarão verificar se o resultado é verdadeiro por meio da escolha de um experimento. Essa mudança pode ser para melhor ou para pior, não importa, o que importa é que você e sua equipe estarão utilizando o método científico para entender o que funciona, o que não está funcionando muito bem para vocês e os motivos por trás disso. Essa é a forma reativa de trabalhar com o método científico e esse é um dos mais importantes princípios do desenvolvimento ágil de software: responding to change over following a plan. Você também poderá utilizar o método científico em seu favor de uma maneira mais proativa, isto é, aplicando teses encontradas em literatura e demais meios de comunicação e acompanhando como os indicadores irão reagir a essas mudanças.

Um bom mecanismo para estabelecer a relação entre causa e efeito no que diz respeito aos indicadores de sucesso são as sprint reviews e as sprint retrospectives, cerimônias presentes no Scrum Framework. Ao término de cada sprint, você irá se reunir com sua equipe e juntos irão fazer uma revisão de todo o trabalho realizado, apresentando os objetivos alcançados, as novas funcionalidades e explicando como as coisas se deram durante a sprint. A revisão é uma cerimônia mais informal e normalmente os stakeholders do projeto são convidados para fazer parte da revisão dos trabalhos executados na sprint. Em seguida, você e seu time abandonarão a informalidade da revisão e entrarão na formalidade da retrospectiva, em que vocês irão analisar os indicadores, estabelecer relações entre causa e efeito e desenvolver teses que terão como objetivo tornar a equipe mais produtiva na próxima rodada de trabalho. Uma ferramenta que pode ajudar sua equipe no processo de retrospectiva é o EasyRetro, em que você tem um modelo de board para ser utilizado pela sua equipe. Antes de conhecer o EasyRetro, eu escreveria a opinião de cada um dos membros da equipe em um documento word, por exemplo, e disponibilizaria o documento para todos no fim da cerimônia para que tudo ficasse arquivado caso fosse necessário no futuro ou para consulta na próxima sprint, o que geralmente é necessário.

Conclusões

Aplicar o método científico para gestão de sua equipe não é algo de outro mundo. Apesar de exigir um determinado trabalho para estabelecer indicadores eficientes que incluirão algumas consultas no banco de dados e sincronização com alguma ferramenta para apresentação de gráficos, como algum BI, Google Sheets ou Microsoft Excel, uma vez que esse trabalho for feito, você não irá se preocupar muito com a manutenção de indicadores e irá focar mais no desenvolvimento de teses para tornar sua equipe mais eficiente, o que é fantástico. Embora muitas equipes se coloquem com os pés entre as mãos enquanto tentam estabelecer um determinado processo de gestão, recomendo inicialmente o foco exclusivo no método científico, pois esse em si é mais simples do que implementar o Scrum ou Kanban à risca, por exemplo, e irá lhe permitir identificar de antemão qual metodologia ágil é a mais eficiente para a sua equipe. Além disso, se você mantiver seus indicadores de uma forma transparente para sua equipe, será mais fácil lidar com as opiniões adversas de seus colaboradores, pois sempre que alguém tiver uma ideia nova, as decisões não serão tomadas com base em influência política de um membro de lábia afiada ou então em emoções irracionais. Elas serão tomadas com base na análise fria e calculista dos indicadores que demonstram o que é ou não melhor. Além de um resultado para a gestão de sua equipe a curto prazo, a utilização do método científico irá lhe garantir uma forma robusta de adquirir conhecimento ao longo de sua carreira, tornando-o um gestor mais eficiente e fazendo também com que os membros da sua equipe evoluam constantemente.

Evitando um código-fonte complexo

Criar uma aplicação na qual a execução de rotinas de manutenção é uma tarefa simples e objetiva não é uma atividade das mais fáceis e o problema torna-se ainda maior quando essa aplicação precisa de uma arquitetura escalável onde diversas mudanças no código irão ocorrer quase que de forma constante de acordo com as demandas de mercado. Quando não possuímos experiência no desenvolvimento de aplicações em um paradigma específico, tal como funcional ou orientado a objetos, é normal que nossa aplicação fique com um código-fonte muito limpo e simples durante os estágios iniciais de desenvolvimento, mas conforme novas funcionalidades são adicionadas o código começa a se tornar cada vez mais difícil de entender e consequentemente de se manter. O código-fonte de uma aplicação nasce como uma cidade e cresce da mesma forma, com pessoas diferentes juntas trabalhando para construir coisas ao mesmo tempo, que estarão de uma forma ou outra entre si conectadas. Em uma cidade onde falta um plano de desenvolvimento sustentável é normal que após um tempo a locomoção torne-se inviável ou ao menos difícil, junto também com a modificação das estruturas existentes e adição de novas estruturas, enquanto em uma cidade com um bom planejamento o crescimento não é um inimigo da qualidade de vida.

Definindo o que é um código complexo

Complexidade é tudo aquilo que torna um software difícil de se manter ou adicionar novas funcionalidades. Um software criado para detectar mísseis balísticos que se aproximam do espaço aéreo de um país são complexos devidos à natureza do problema, portanto essa não é a sua inimiga. Quando tratamos de complexidade de software é importante saber diferenciar aquilo que é a complexidade natural do problema e aquilo que é a complexidade adicionada e também a complexidade consequente. A complexidade adicionada ocorre quando são criadas mais estruturas na aplicação para resolver o problema do que aquilo que é necessário, esse tipo de complexidade é fruto do overengineering. A complexidade consequente surge pelo motivo oposto, isto é, quando você precisa fazer determinadas modificações na aplicação que não são suportadas pela sua estrutura, que fora planejada com demasiada simplicidade enquanto uma maior robustez era necessária. Quando uma estrutura é criada de forma demasiada simples é normal que as mudanças subsequentes tornem-se mais complexas do que deveriam a cada novo evento de mudança que ocorre, até chegar ao ponto de que não é mais possível de se manter a aplicação de forma sustentável. Quando o nível de complexidade da sua aplicação está desbalanceado em relação à complexidade natural, seja para mais ou para menos, os seguintes sintomas passam a se tornar recorrente durante suas atividades:

  • Change amplification: quando você precisa fazer alterações em diversos pontos da aplicação para introduzir uma mudança simples. É muito importante que seu código seja coeso, e que os pontos que relacionados uns aos outros estejam próximos e sejam fáceis de ser encontrados por quem quer que seja que esteja realizando a manutenção.
  • Cognitive load: quanto mais conhecimento específico sobre o sistema é necessário para fazer uma mudança mais complexa a aplicação se torna de se manter, por isso é muito importante que os design patterns sejam aplicados sempre que possível, pois esta é uma linguagem universal utilizada por engenheiros de todo o mundo para resolver problemas padrão. Aplique design patterns em tudo onde for plausível e limite-se à receitas secretas de família somente onde for necessário.
  • Unknown unknowns: geralmente você precisa saber de algumas coisas para realizar a manutenção de uma aplicação, e este fenômeno ocorre quando você sequer sabe que coisas são essas que você precisa saber. Um software precisa ser dedutível, capaz de facilmente ser analisado através da engenharia reversa. Quando mais fácil for realizar engenharia reversa do seu código, melhor, e isso inclui material de apoio e documentação, entretanto esforce-se para que seu código seja tão fácil de ser compreendido que materiais de apoio se tornarão desnecessários. Um bom material de apoio para desenvolver-se nesse sentido é o livro Clean Code.

E por fim, a complexidade de um código-fonte não é determinada pelo autor do código, mas sim pelo leitor, portanto a opinião de quem escreve um código não é muito relevante, pois seja la qual foi o erro cometido pelo autor ele ao menos tem a informação necessária para navegar em seus próprios erros, enquanto um leitor descontextualizado precisa aprender tudo que o autor já sabe de antemão.

É muito difícil acertar a arquitetura de primeira

O primeiro passo para construção de uma aplicação com uma boa arquitetura é entender que se você está lidando com um problema pela primeira vez há uma grande chance de você não acertar de primeira. Se você está desenvolvendo uma aplicação para cobrança de dívidas por exemplo e essa é sua primeira vez com uma aplicação do gênero você irá obter determinadas informações por parte do seu cliente ou chefe com as especificações do problema, mas assim que a aplicação entrar em produção e os usuários começarem a testar as funcionalidades você irá descobrir coisas novas junto do seu product owner que ambos não esperavam. É uma tendência natural que um sistema de informação irá replicar as estruturas de relacionamento humanas existentes entre seus usuários e essas estruturas humanas são complexas de se presumir pois elas surgem das mais variadas formas e pelos mais variados motivos. Imagine que você irá emitir cobranças e existe uma taxa de juros que é praticada para determinados seguimentos, mas para uma loja específica existe um contrato diferente, pois o dono da empresa que está a contratar seu serviço também é sócio da outra empresa a ser cobrada. Essa é uma estrutura de relacionamento hipotética, mas elas podem vir a existir pelos mais variados motivos e você precisará de um determinado tempo de profissão para aprender quais são as possíveis formas de se fazer negócio na área que você está atuando, portanto, esteja preparado para jogar código-fonte fora e trabalhar duro na reescrita de módulos a camadas da sua aplicação sempre que necessário para evitar que as coisas cresçam de forma descontrolada.

Agilidade é mais importante que velocidade

Nem tudo que é veloz é necessariamente ágil, um exemplo grotesco é o foguete que leva os astronautas à estação espacial internacional, ele é extremamente veloz, mas não é mais ágil do que uma bicicleta. Sua velocidade é determinada pelo tempo que você leva para chegar de um ponto ao outro, enquanto sua agilidade é determinada pelo tempo que você leva para se ajustar à uma mudança no seu trajeto de forma que poderá desenvolver sua velocidade máxima no menor espaço de tempo possível novamente. Sempre que os objetivos dos seus usuários mudam seu software também necessitará mudança e quanto menos ágil você e seu time forem maiores serão as chances de que vocês irão precisar implementar uma solução inadequada. Uma equipe ágil busca sempre se antecipar aos problemas mantendo-se atenta ao que está acontecendo no mercado no qual ela está inserida, no seu contexto corporativo, na demografia dos seus usuários e assim por diante. Quanto mais ágil sua equipe se tornar vocês poderão responder com maior precisão e força às mudanças necessárias. Lembre-se que a agilidade não se refere só ao caso de ser ágil em reescrever o código, mas também se refere à agilidade com a qual sua equipe é capaz de assimilar o problema, planejar a alteração necessária e realizar a execução. Normalmente equipes que não desenvolvem agilidade de assimilação e planejamento costumam pular essas etapas, o que contribui para a evolução da complexidade do código fonte.

Testes unitários são mais do que necessários

Além de garantir a que as coisas estão funcionando, quando bem escritos os testes unitários irão também atuar como uma documentação das funcionalidades do seu sistema, explicando como as coisas deveriam e não funcionar. Embora eles não sejam muito eficientes para explicar o motivo de as coisas serem daquela forma, uma tarefa para a engenharia de requisitos, eles são ótimos para descrever o estado atual das coisas. Os testes unitários irão ajudar você a descobrir informações necessárias sobre o funcionamento do sistema, evitando os unknown unknowns, e também irá te ajudar a ter uma noção científica a respeito do nível de change amplification do seu software. Além disso os testes unitários evitam a complexidade consequente, fruto do processo da criação de estruturas simplistas demais, uma vez que para escrever software possível de ser testado é necessário que determinados padrões de engenharia sejam seguidos.

Modularize seu sistema

Do ponto de vista do leitor, você acha que é mais fácil adquirir o conhecimento que você procura em uma livraria onde tudo é catalogado e devidamente categorizado ou em uma onde os livros estão aleatoriamente dispostos nas prateleiras? Com certeza é mais fácil para quem está colocando os livros no lugar não se importar em colocar no lugar certo, mas para quem precisa se servir do acervo encontrar a informação que deseja se torna um processo doloroso. Quando o assunto é software as coisas não são diferentes, por isso inclusive utilizamos o termo library para nos referir à uma coleção de códigos.

Além de deixar o seu código mais organizado em categorias e subcategorias a modularização irá lhe proporcionar um efeito chamado information hiding, que é quando classes e módulos tratam de problemas específicos de forma centralizada não fazendo com que outras partes do seu código precisem ter essa informação, para não confundir as coisas é importante ter em mente que a obscuridade surge quando você não consegue encontrar uma informação que busca, enquanto information hiding tem como objetivo esconder as informações desnecessárias de estarem presentes em um determinado trecho de código.

Imagine que você está indo para o módulo de cadastro de usuário de uma aplicação, naquele módulo você está procurando detalhes referentes somente ao cadastro dos usuários, não quer saber de outros detalhes tais como taxas de comissão, regras de autenticação e assim por diante. Esses outros devem estar centralizados em seus módulos específicos. A modularização torna-se muito eficiente nesse sentido quando você está desenvolvendo um sistema que cuida de diversas áreas de processos dentro uma mesma empresa e você divide seus módulos baseando-se em nessas áreas, assim sua regra de negócio ficará organizada na aplicação de forma coerente. Além disso, David Parnas têm um artigo muito bom onde ele busca definir os critérios para modularização de uma aplicação com o objetivo de aumento de produtividade. Apesar de o artigo ter sido publicado na década de 70 este continua ainda muito atual.

Evite classes muito pequenas ou extensas

Quanto maior for uma classes mais informações ela irá esconder que outras classes não precisam saber, possuir classes com diversos métodos não é algo necessariamente ruim, entretanto como uma regra geral é importante que suas classes não possuam mais do que duas mil linhas ao mesma tempo que não devem possuir menos de duzentas linhas. Quando uma classe é demasiadamente grande é muito difícil gerenciar a sua complexidade e quando ela é muito pequena seu código está provavelmente distribuído de uma forma demasiadamente granular, o que por sua vez irá aumentar a obscuridade da sua aplicação. Encontrar um bom balanceamento do que deve estar escondido em suas classes e módulos é uma tarefa que exige um bom julgamento do desenvolvedor e difícil de por em um padrão, mas uma vez que você tem em mente qual objetivo busca-se alcançar com esse princípio então você estará pronto para fazer o julgamento.