69 pontos por xguru 2024-07-15 | 6 comentários | Compartilhar no WhatsApp

Se você continua atirando no próprio pé, conserte a arma

  • Em equipes, muitas vezes há partes do sistema em que erros acontecem com frequência, mas pouca gente pensa em como reduzir esses erros
  • Nesses casos, o importante é melhorar o sistema para diminuir a chance de erro
  • Experiência:
    • Ao usar CoreData no desenvolvimento iOS, atualizações de UI só podem ser feitas na thread principal
    • O callback de assinatura acontecia tanto na thread principal quanto em threads de background, o que frequentemente causava problemas
    • Os membros antigos da equipe sabiam disso e lidavam bem com a situação, mas isso aparecia com frequência nas revisões de colegas novos
    • Quando algum erro acontecia, a solução vinha sendo olhar o relatório de crash e adicionar DispatchQueue.main.async
    • Para resolver isso, a camada de assinatura foi atualizada para chamar os assinantes na thread principal. Levou só 10 minutos.
    • Isso eliminou toda uma classe de crashes e também uma pequena carga mental
  • Qualquer pessoa teria percebido que era um problema óbvio se parasse alguns minutos para pensar
  • Mas esse tipo de problema estranhamente dura muito tempo porque nunca existe um momento natural para corrigi-lo
    • Ou seja, quando você passa muito tempo em uma equipe, esses problemas tendem a virar pano de fundo
  • É preciso mudar a mentalidade
    • De vez em quando, é bom lembrar a si mesmo que você pode tornar a vida da equipe — e a sua — mais fácil quando esse tipo de problema aparece

Equilibrar qualidade e velocidade

  • Sempre existe um trade-off entre velocidade de implementação e confiança na correção
    • Vale se perguntar o quanto é aceitável lançar bugs na situação atual
    • Se a resposta a isso não afeta sua forma de trabalhar, então você está sendo rígido demais
  • No meu primeiro emprego, trabalhando em projetos de processamento de dados, havia um bom sistema para reprocessar dados retroativamente
    • O impacto de lançar bugs era muito pequeno e, nesse ambiente, dava para confiar mais nos guardrails e andar mais rápido
    • 100% de cobertura de testes ou processos extensos de QA só atrasariam o desenvolvimento
  • No segundo emprego, eu lidava com um produto usado por dezenas de milhões de pessoas, com dados financeiros de alto valor e informações de identificação pessoal, então bugs eram críticos
    • Mesmo um bug pequeno exigia análise pós-incidente
    • As funcionalidades eram lançadas muito devagar, mas acho que naquele ano não colocamos nenhum bug em produção
  • Na maioria dos casos, você não está numa situação como a da segunda empresa
    • Quando bugs não são catastróficos (como em 99% dos web apps), é melhor lançar rápido e corrigir rápido
    • Isso permite avançar mais do que gastar tempo tentando lançar algo perfeito desde o início

O tempo gasto afiando o serrote quase sempre vale a pena

  • É importante dominar bem suas ferramentas
  • Você deve conseguir escrever código rápido, conhecer os principais atalhos e ter intimidade com o sistema operacional e com o shell
    • Você vai renomear coisas, ir para definições de tipo, encontrar referências etc. o tempo todo
    • Deve conhecer os principais atalhos do editor e digitar com rapidez e confiança
    • Também é importante usar bem as ferramentas de desenvolvedor do navegador
  • Escolher boas ferramentas e usá-las com fluência é uma grande vantagem
  • Um dos maiores sinais positivos em engenheiros novos é justamente o interesse por escolher bem as ferramentas e usá-las com habilidade

Se você não consegue explicar com simplicidade por que algo é difícil, provavelmente é complexidade acidental — e vale a pena resolver esse problema

  • Um dos gestores de quem mais gostei tinha o hábito de continuar pressionando sempre que eu dizia que algo era difícil de implementar
    • Muitas vezes a resposta dele era algo como: "no fim das contas, não é só enviar X ao fazer Y?" ou "isso não é parecido com o Z que fizemos alguns meses atrás?"
    • Eram objeções em um nível muito alto, e não no nível real das funções e classes que eu estava tentando explicar
  • A visão comum é que esse tipo de simplificação por parte de um gestor é só irritante
  • Mas, surpreendentemente, em uma porcentagem alta dos casos, eu percebia que boa parte da complexidade que eu estava descrevendo era complexidade acidental
  • E, na prática, ao resolver isso primeiro, o problema realmente podia se tornar tão trivial quanto o gestor sugeria
  • Esse tipo de abordagem também tende a facilitar mudanças futuras

Tente resolver bugs um nível mais fundo

  • Em vez de corrigir bugs de forma superficial, é importante encontrar e corrigir a causa raiz
  • Quando há um componente React no dashboard que trabalha com um objeto User obtido do estado do usuário atualmente logado
    • Surge no Sentry um relatório de bug dizendo que user era null durante a renderização
      • Você pode rapidamente adicionar if (!user) return null
    • Ou investigar um pouco mais e descobrir que a função de logout faz duas atualizações de estado separadas
      • A primeira define o usuário como null, e a segunda redireciona para a homepage
    • Se você inverter a ordem das duas, nenhum componente terá esse bug de novo
    • Porque, dentro do dashboard, o objeto do usuário nunca deveria ser null
  • Se você continuar fazendo o primeiro tipo de correção, tudo vira uma bagunça, mas
    se continuar fazendo o segundo tipo, vai acabar com um sistema limpo e com uma compreensão profunda das invariantes

Não subestime o valor de cavar o histórico para investigar bugs

  • Eu era bem bom em depurar problemas estranhos usando ferramentas comuns, como println e o debugger
  • Por isso, eu não costumava olhar muito para o git para entender o histórico de um bug, mas em alguns casos isso é extremamente importante
  • Recentemente, parecia haver um vazamento contínuo de memória em um servidor, que acabava sendo encerrado por OOM e reiniciado
    • Toda causa plausível já tinha sido descartada, e não dava para reproduzir localmente
    • Parecia que eu estava jogando dardos de olhos vendados
    • Ao olhar o histórico de commits, vi que o problema começou depois da adição de suporte a pagamentos da Play Store
    • Eram só algumas requisições HTTP, então eu nunca teria procurado ali em um milhão de anos
    • No fim, descobri que havia entrado em um loop infinito para obter access tokens depois que o primeiro expirava
    • Cada requisição talvez adicionasse só cerca de 1 kB de memória, mas, ao tentar de novo a cada 10 ms em várias threads, isso acumulava rapidamente
    • Normalmente isso teria causado stack overflow, mas como eu estava usando recursão assíncrona em Rust, não houve stack overflow
    • Eu nunca teria pensado nisso sozinho, mas ao olhar para um trecho específico de código claramente ligado ao problema, a teoria surgiu de repente
  • Não existe uma regra para saber quando fazer esse tipo de abordagem
    • Isso depende de intuição; é um tipo diferente de "ué?" em relação a um bug report que dispara esse tipo de investigação
    • Com o tempo, dá para desenvolver essa intuição, mas às vezes já basta saber que isso pode ser extremamente valioso
  • Quando o problema se encaixar, tente usar git bisect
    • Quando você tem um commit que sabe que está errado e outro que sabe que está bom

Código ruim dá feedback, código perfeito não. Erre para o lado de escrever código ruim

  • Escrever código horrível é muito fácil
  • Mas também é muito fácil escrever código seguindo absolutamente todas as boas práticas
    • Com testes unitários, de integração, fuzz, mutação e tudo mais, a startup fica sem dinheiro antes de terminar
  • Grande parte da programação consiste em encontrar equilíbrio
  • Se você errar para o lado de escrever código rápido...
    • Às vezes vai sofrer com dívida técnica ruim
    • Vai aprender que "precisa adicionar ótimos testes para processamento de dados"
      • Porque muitas vezes corrigir isso depois é impossível
    • Também vai aprender que "precisa pensar muito bem no design das tabelas"
      • Porque mudar isso sem downtime pode ser muito difícil
  • Se você errar para o lado de escrever código perfeito...
    • Não vai receber nenhum feedback
    • Tudo vai levar mais tempo, em todos os casos
    • Você não vai saber onde está investindo bem o tempo e onde está desperdiçando
    • Mecanismos de feedback são essenciais para aprender, mas assim você deixa de tê-los
  • Esclarecendo o que significa código "ruim"
    • Não significa algo como "usei um loop interno duas vezes porque não lembrava a sintaxe para criar um hashmap"
    • Significa coisas como:
      • Em vez de reescrever a coleta de dados para tornar impossível representar certos estados, adicionar algumas assertions de invariantes em pontos-chave
      • Como o model do servidor é exatamente igual ao DTO que ele vai escrever, simplesmente serializá-lo; se necessário, o DTO pode ser criado depois em vez de escrever todo o boilerplate agora
      • Pular a escrita de testes para componentes triviais, em que bugs não causariam grande impacto

Torne o debugging mais fácil

  • Ao longo dos anos, aprendi muitos truques pequenos para tornar software mais fácil de depurar
    • Se você não se esforçar para facilitar o debugging, vai acabar gastando uma quantidade enorme de tempo para depurar cada issue à medida que o software ficar mais complexo
    • Você vai começar a ter medo de fazer mudanças, porque entender alguns bugs novos pode levar uma semana
  • Preste atenção em quanto tempo do debugging vai para setup, reprodução e limpeza posterior
    • Se isso passar de 50%, vale procurar uma forma de tornar esse processo mais fácil, mesmo que desta vez leve um pouco mais
    • Mantidas as demais condições, corrigir bugs deveria ficar mais fácil com o tempo

Ao trabalhar em equipe, faça perguntas sempre

  • Existe um espectro entre "tentar descobrir tudo sozinho" e "incomodar colegas com perguntas triviais"
    • Acho que a maioria das pessoas no início da carreira pende demais para o primeiro lado
  • Sempre haverá por perto alguém que está há mais tempo no codebase, conhece muito melhor a tecnologia X, entende melhor o produto ou simplesmente é mais experiente como engenheiro
  • Nos primeiros seis meses em um lugar, é comum desperdiçar mais de uma hora tentando descobrir algo que poderia ser respondido em alguns minutos
  • Faça perguntas. A única situação em que perguntar realmente incomoda alguém é quando está óbvio que você mesmo poderia encontrar a resposta em poucos minutos

O ciclo de deploy é extremamente importante. Pense com cuidado em como deployar rápido e com frequência

  • Startups têm runway limitado, e projetos têm prazos
  • Quando você sai do emprego para trabalhar por conta própria, o dinheiro guardado vai durar só alguns meses
  • Idealmente, a velocidade do projeto aumenta de forma composta com o tempo, e você acaba lançando funcionalidades mais rápido do que imaginava ser possível
  • Para deployar rápido, muitas coisas são necessárias
    • Um sistema que não seja frágil a bugs
    • Tempo de resposta rápido entre equipes
    • Vontade de cortar 10% de uma nova funcionalidade (a parte que consumiria 50% do tempo de engenharia) e insight para saber identificar essas partes
    • Padrões consistentes e reutilizáveis para compor novas telas/funcionalidades/endpoints
    • Deploy rápido e fácil
    • Processos que não atrasem a velocidade (testes instáveis, CI lento, linters chatos, revisão de PR demorada, JIRA tratado quase como religião etc.)
    • E mais um milhão de coisas
  • Deployar devagar deveria gerar uma análise pós-incidente tão séria quanto derrubar a produção
    • Nossa indústria não funciona assim, mas isso não significa que você não possa, pessoalmente, tratar deploy rápido como sua estrela-guia

6 comentários

 
carnoxen 2024-07-19

"Atirar no próprio pé" = significa algo como cavar a própria cova?

 
yunghn 2024-07-25

Se um problema surgir por causa de um código defeituoso (uma arma quebrada) e você acabar se prejudicando por isso (dando um tiro no próprio pé), a ideia é que você conserte a arma.

 
gargoyle92 2024-07-16

Foi um choque, parece exatamente como se tivessem tirado o que estava na minha cabeça e colocado ali, nossa..

 
cbbatte 2024-07-16

Li com muito gosto!!

 
hannah0su 2024-07-15

Li com atenção.

 
arfwene 2024-07-15

Não sou desenvolvedor, mas há muitas partes com as quais me identifico.