Desenvolver sistemas é sobre pessoas.
Eu sei, parece contraditório, afinal passamos o dia na frente de telas e escrevendo. Mas passamos o dia inteiro nos comunicando com colegas, fornecedores, clientes. Seja em reuniões formais, ou nas salas de chat da empresa, pessoas estão sempre envolvidas. Converse com os colegas, troque informações, dê e receba feedback. Trabalhar em TI não é sobre falar com máquinas — por mais que possa parecer.
Somos pessoas trabalhando com sistemas complexos, atendendo demandas de clientes e do negócio, então quero abordar alguns conceitos técnicos que eu julgo básicos e que ajudam muito no dia a dia dos times. São temas que vejo se repetirem em diversas empresas. Teriam mais temas — como agilidade, visão holística do produto, liderança —, que são tão importantes quanto esses que resolvi abordar, mas por hora deixo para uma próxima série de posts.
Escolhas
Como pessoas adoramos fazer escolhas e dar a nossa opinião, quando criamos sistemas não seria diferente. Temos que escolher:
- Linguagem de programação
- Infraestrutura para execução
- Bibliotecas e frameworks
- Ferramentas para testes, validações, deploys
- Editores de texto / IDEs
Enquanto alguma dessas escolhas são totalmente pessoais (ou deveriam ser), algumas podem ter impacto nas pessoas que virão a trabalhar no projeto. Um exemplo disso é organizar o código em torno da escolha de IDE, e tornar difícil para outras pessoas — por exemplo precisar alterar vários caminhos em Makefiles sem a possibilidade de versionar a alteração para não quebrar os demais.
Pese o impacto que decisões pessoais tem sobre o time, ou até sobre o produto. Não escolha uma linguagem só porque você quer aprendê-la, pense que outras pessoas terão que dar manutenção ao sistema. Será que todo mundo vai conhecer a ferramenta que estava em voga em 2015, ela ainda é mantida?
O mesmo vale para infraestrutura, selecionar uma stack serverless pode fazer muito sentido para ganharmos velocidade na entrega, mas será que quando o volume de acessos crescer o custo vai continuar competitivo? Os demais colegas entendem dessa stack? E se o fornecedor descontinuar o sistema onde estamos rodando? Qual o esforço cognitivo no time para manter diversas stacks diferentes de software? Já vi times que rodavam coisas em: EC2, AWS Lambda, cluster kubernetes, cluster spark; era um time só, com todas essas infras diferentes. É impossível saber todas as boas práticas, os problemas, as soluções de contorno para todas essas diferentes infraestruturas.
O desenvolvimento é feito para atingir os objetivos do negócio, não metas pessoais de aprendizado - mas obviamente podemos (e devemos) aprender muito no processo. Pense sempre no seu eu futuro: será que você vai ficar feliz em revisitar esse projeto? será que um colega novo ficará feliz em começar a trabalhar nesse projeto? Lembrando que no meio tempo aprendemos outras coisas, crescemos e amadurecemos. Dos diversos projetos que revisitei ao longo da carreira teria simplificado o código, a infraestrutura e as linguagens usadas para facilitar o modelo mental que preciso ter para dar manutenção.
Código
Um parte importante das tarefas diárias é ler e escrever códigos — conforme a senioridade aumenta a quantidade de código diminui, infelizmente. Se passamos tanto tempo lendo e escrevendo códigos temos que priorizar escrever códigos que sejam de fácil leitura e compreensão.
Fazer aquela expressão regular gigante em uma linda linha é um desafio e tanto, super divertido, mas será que o seu eu futuro vai ter facilidade de entender o que acontece lá? Talvez adicionar dois parágrafos explicando tudo? Ou, para os que usam python, será que aquela list comprehension com lambdas e três níveis diferentes de dados valem a pena? Saber escrever código compacto, ou complexo, não te torna um desenvolvedor melhor.
As pessoas tem uma certa capacidade de entender coisas, ideias simples são de fácil entendimento, já ideas complexas e compostas precisam de mais tempo para serem processadas. Em linhas gerais isso é o esforço cognitivo necessários para compreender (e manter) algo no cérebro. Quando gastamos muito tempo processando as ideias acabamos não fazendo o que é importante para o negócio: gerar valor. Então, se o esforço cognitivo para entender o código for grande as chances de que alguém, ou você mesmo, adicione um bug são altas. Ou mesmo se não for adicionado um bug, compreender o que o código e suas nuances faz se torna difícil. Tem uma linha de pensamento que fala que escrever códigos é como escrever prosa, isto é, precisamos que ele seja legível, de fácil entendimento por humanos. Os compiladores e otimizadores de código são ótimos em transformar qualquer código em um binário executável, não vivemos mais em 1970 em que escrever código de uma certa maneira era requisito.
Nomear coisas
Nomear coisas (e pessoas e pets) é algo extremamente difícil. As possibilidades são enormes, posso usar todos os personagens do meu livro favorito, posso usar o nome dos filmes que amo, os jogadores do meu time do coração, os nomes científicos das flores que mais gosto. Se você identificou algum sistema que tenha nomes assim, bom, esses sistemas não dizem nada para ninguém, somente para as pessoas que criaram ele originalmente — muitas vezes, pessoas que já não trabalham mais na empresa e o nome ficou ainda mais sem sentido.
Os sistemas evoluem, os times mudam, as empresas crescem, adquirem outras empresas, e muitas vezes os sistemas permanencem. Como uma pessoa nova na empresa vai saber que MartyMcFly faz tracing do sistema — afinal no filme ele viaja no tempo, nada melhor para um sistema que deixa tu olhar o que aconteceu em algum momento do passado. Ou que o Ego é um sistema distribuído de fofoca — ok esse talvez mais gente entenda. O ponto é não devemos dar nomes que simplesmente achamos legais, ou que são divertidos ou uma piada interna. Nomes precisam fazer sentido, deixar claro o seu propósito, isso facilita entender em que parte da arquitetura o sistema se encontra.
Arquitetura de sistemas são quebra-cabeças (ou Legos) gigantes, dando nomes com significado faciltamos a montagem do quebra-cabeça na cabeça de cada um.
Fazer o mínimo
O objetivo de todo o desenvolvimento é entregar valor para os usuários. Uma arquitetura complexa, código díficil de entender, emaranhado de dependências podem até entregar valor em um momento inicial. Mas sistemas crescem, usuários têm desejos diferentes, regras mudam, e quando temos algo extremamente complexo adicionar ou trocar as partes se torna um exercício muito mais complicado. Queremos entregar o máximo de valor sempre, não ficar oscilando entre entregas e débitos técnicos (que se não forem pagos se tornam grandes bolas de neve).
Faça o mínimo para atender os requisitos de negócio, sem abrir mão de testes, qualidade e segurança. Testes são o melhor jeito de documentar o sistema, qualquer um consegue ler os testes e entender o comportamento. Com eles você também ganha velocidade para refatorar e não ter medo de quebrar uma parte não correlacionada. Tente simplificar o código e a arquitetura conforme o sistema for crescendo. Quanto mais simples mais fácil manter na cabeça um modelo de como funciona todo o sistema.
Segurança deve ser criada desde o primeiro momento. Por exemplo: já comece utilizando HTTPS para todas as chamadas. Não coloque senhas e outros dados sensíveis no código fonte. Veja segurança como parte de qualidade do software, não espere um incidente para focar em segurança.
Portanto, leve como lema Keep It Simple, mantenha o sistema simples - faça o exercício de tentar explicar o sistema para uma criança ou para seus avós. Ou, para os fãs de arquitetura, adotem Less is more.
Qualidade
Se você quer ter velocidade de entrega jamais abra a mão de qualidade. Qualidade é o que permite os times e a empresa acelerarem. No livro Accelerate há diversos dados e comprovações das hipóteses de que qualidade é essencial. Recomendo fortemente a leitura para todos que querem entender as relações entre código, times e retorno para a empresa.
Quando abrimos mão da qualidade começamos um ciclo vicioso. Entregamos algo rápido, mas talvez sem testes. Código funciona por um tempo, até que quebra em produção. Como não tínhamos testes, fica díficil encontrar o problema e garantir que não aconteça novamente. No meio tempo já estávamos lançando outras features, também sem testes e no modo "funciona na minha máquina". Quando tudo isso vai para a produção, ficamos algumas horas fora do ar. O impacto com nossos usuários é enorme. Paramos tudos (ie. deixamos de entregar valor) para tentar arrumar o problema. Não pagamos o débito técnico da falta de testes, fazemos outro paliativo, e a complexidade do software vai crescendo.
Eventualmente chegamos em um momento em que não dá mais. Adicionar funcionalidade está levando meses, porque qualquer alteração esbarra em milhares de problemas. Ninguém entende como o sistema funciona. Nossa entrega de valor, que era rápida, está péssima; passamos meio ano só pagando débito técnico. Quando finalmente voltamos a ter velocidade, o mercado já nos abandonou, e só perdemos dinheiro. Claro, isso é o pior cenário possível, existem outros menos piores mas que geram muitos problemas nos times. Evite o débito técnico priorizando qualidade.
Lembrando que existem diversos tipos de débito técnico, nem todos são possíveis de identificar de forma simples. Alguns são sutis, ficam escondidos dentro do código com testes funcionais. Mas, na hora de trocar alguma peça da arquitetura, acabamos abrindo a caixa de pandora. Nem todos os débitos técnicos são previsíveis, alguns simplesmente aparecem nas horas mais inoportunas.
Prioridades
No dia a dia do time sempre temos muitas decisões a tomar e devemos pensar como priorizar, tendo em vista que queremos garantir os requisitos do negócio. Às vezes os requisitos não são tão fáceis de saber. Por exemplo: se o sistema atende usuários internos então dar suporte faz parte do negócio (claro, em alguns lugares teremos um time de suporte, mas mesmo assim vamos ter que ensinar esse time). Manter os usuários satisfeitos e criar uma cultura de colaboração é fundamental.
Eu gosto sempre de priorizar débitos técnicos toda a vez que encontro um, mesmo que isso não esteja no escopo da sprint. Alguns débitos técnicos podem ser enormes e precisam ser negociados com o resto do time. Tente sempre fazer com que o time compre a ideia de eliminar débitos.
Resolvido os débitos, o foco é o negócio, como eu maximizo o valor entregue. Fazer cinco mil tarefas simultâneas não leva a nada, só stress e burnout. E as tarefas (seguindo o pensamento ágil) são do time. Divida as tarefas entre todos, aprenda junto ou ensine o que você já sabe. Se o time tem um backlog para mais de ano, provavelmente não é um backlog pois o negócio vai mudar de hoje até lá. Aqueles vários cards parados vão deixar de fazer sentido. Minimize o backlog, mas garanta que o time tenha uma visão a longo prazo (construída junto com os clientes).
Ter um panorama de onde estamos e para onde estamos indo ajuda muito o time para saber o que priorizar. Torna fácil pegar as tarefas de maior valor do backlog e divide a carga de priorização com todos. O importante é todos estarem a par do que se passa no time, e entenderem qual o momento do grupo.
Realimentação
Feche o ciclo de desenvolvimento observando as métricas do sistema em produção e colhendo feedback dos usuários. O comportamento está como o esperado? As métricas atendem? O que você pode aprender sobre o sistema observando ele rodando? O que os usuários estão falando?
O trabalho de desenvolvimento vai além de só escrever as linhas de código e mandar para produção. Temos que acompanhar os sistemas, entender se temos melhorias técnicas para fazer. A partir do momento em que o software está em produção precisamos desse acompanhamento. Já vi sistemas que no ambiente de testes funcionavam perfeitamente com latências mínimas, quando foi para produção, e teve um alto volume de acesso, parou de funcionar. Ter métricas, logging, tracing — quiçá fazer um profiling — trará as respostas para os problemas técnicos encontrados em produção, ou talvez só um caminho para onde devemos explorar.
Livros
Alguns livros que trazem muita informação boa para o dia adia. Sem ordem de preferência. Sem julgamentos aos autores.