Há um ano, comecei a trabalhar em período integral na Bloomberg. E então eu decidi escrever este artigo. Eu pensei que estaria cheio de idéias que eu poderia jogar no papel quando chegasse a hora. Mas, dentro de um mês, percebi que tudo não seria tão simples: já comecei a esquecer o que aprendi. Ou o conhecimento foi tão bem adquirido que minha mente me fez acreditar que eu sempre soube, ou eles simplesmente voaram da minha cabeça.
1Essa é uma das razões pelas quais comecei a manter um diário. Todos os dias, entrando em situações interessantes, eu as descrevi. E tudo graças ao fato de estar sentado ao lado de um programador líder. Eu pude observar de perto o trabalho dele e vi como era diferente do que eu faria. Nós programamos muito juntos, o que tornou minhas observações ainda mais fáceis. Além disso, nossa equipe não condena a "espionagem" de pessoas que escrevem código. Quando me pareceu que algo interessante estava acontecendo, eu me virei e olhei. Graças ao constante aumento, eu sempre tive consciência do que estava acontecendo.
Passei um ano ao lado de um programador líder. Foi isso que eu aprendi.
Conteúdo
Escrita de código
Como nomear coisas no código
Uma das minhas primeiras tarefas foi trabalhar na interface do usuário do React. Tínhamos um componente principal que continha todos os outros componentes. Eu gosto de adicionar um pouco de humor ao código e queria nomear o componente principal do
GodComponent
. Chegou o momento de revisar o código e entendi por que é tão difícil dar nomes.
Cada pedaço de código que batizei ganhou um propósito implícito.
GodComponent
? Esse é o componente que recebe todo o lixo que eu não quero colocar no lugar certo. Ele contém tudo. Nomeie-o
LayoutComponent
, e no futuro eu decidiria que este componente atribui um layout. Que não contém um estado.
Outra lição importante que aprendi foi que, se algo parecer muito grande, como um
LayoutComponent
com um monte de lógica de negócios, é hora de refatorá-lo, porque não deve haver lógica de negócios aqui. E no caso do nome
GodComponent
presença da lógica de negócios não importará.
Precisa nomear os clusters? Ligar para eles após os serviços executados neles será uma ótima idéia até você executar outra coisa nesses clusters. Demos a eles um nome em homenagem à nossa equipe.
O mesmo se aplica às funções.
doEverything()
é um nome terrível com muitas consequências. Se a função fizer tudo, será muito difícil testar suas partes individuais. Não importa o tamanho dessa função, ela nunca parecerá muito estranha para você, pois deve fazer tudo. Então mude o nome. Refact.
Um nome significativo tem uma desvantagem. De repente, o nome será muito significativo e ocultará algum tipo de nuance? Por exemplo,
fechar sessões não fecha a conexão com o banco de dados quando
session.close()
chamado em SQLAlchemy. Eu deveria ter lido a documentação e evitado esse bug, mais sobre isso na seção de
bicicletas .
Desse ponto de vista, nomear funções como
x
,
y
,
z
vez de
count()
,
close()
,
insertIntoDB()
não nos permite colocar um certo significado nelas e me faz monitorar cuidadosamente o que essas funções fazem.
2Eu nunca pensei que escreveria sobre os princípios de nomear mais de uma linha de texto.
Código herdado e o próximo desenvolvedor
Você já olhou o código e isso lhe parece estranho? Por que você escreveu isso? Isso não faz sentido.
Eu tive a chance de trabalhar com uma base de código herdada. Isso, você sabe, com comentários como "Descomente o código quando Maomé entender a situação". O que você está fazendo aqui? Quem é Maomé?
Posso trocar de papéis e pensar na pessoa que receberá meu código, isso parecerá estranho para ele? A revisão parcial do seu código ajuda os colegas a revisá-lo. Isso me levou a pensar sobre o contexto: preciso lembrar o contexto em que minha equipe trabalha.
Se eu esquecer esse código, retornar a ele mais tarde e não conseguir restaurar o contexto, direi: “O que diabos eles fizeram? Isso é estúpido ... Ah, espere, eu consegui.
E aqui a documentação e os comentários no código entram em jogo.
Documentação e comentários no código
Eles ajudam a manter o contexto e transmitir conhecimento. Como Lee disse em
Como criar um bom software :
O principal valor do software não está no código criado, mas no conhecimento acumulado pelas pessoas que criaram esse software
Você tem um ponto de extremidade da API do cliente aberto que ninguém parece ter usado. Ele só precisa ser excluído? De um modo geral, é um dever técnico. E se eu lhe disser que em um dos países, 10 jornalistas enviam seus relatórios para esse endpoint uma vez por ano? Como verificá-lo? Se isso não for mencionado na documentação (era), não há como verificar isso. Nós não verificamos. Eles o removeram e, alguns meses depois, chegou aquele momento anual. Dez jornalistas não puderam enviar seus relatórios importantes porque o ponto final não existia mais. E pessoas com conhecimento de produto já deixaram a equipe. Obviamente, agora no código há comentários explicando por que isso é necessário.
Até onde eu sei, cada equipe está lutando com a documentação. E com a documentação, não apenas pelo código, mas também pelos processos associados a ela.
Ainda não chegamos à solução perfeita. Pessoalmente, gosto da maneira como Antirez dividiu os comentários no código
em diferentes tipos de valores .
Commits atômicos
Se você precisar reverter (e precisa. Consulte o capítulo
Teste ), isso confirmará o sentido como um único módulo?
Como excluir com segurança códigos ruins
Fiquei muito desagradável ao excluir o código ruim ou desatualizado. Pareceu-me que tudo o que foi escrito séculos atrás é sagrado. Eu pensei: "Eles tinham algo em mente quando escreveram assim". Este é um confronto entre tradição e cultura, por um lado, e pensar no estilo do "princípio primário", por outro. É o mesmo que a remoção do terminal anual. Eu aprendi uma lição especial.
3Eu tentava contornar o código e os principais desenvolvedores tentavam passar por ele. Apague. Uma expressão if que não pode ser acessada? Sim, nós apagamos. O que eu fiz? Acabei de escrever minha função em cima dela. Não reduzi a dívida técnica. Enfim, acabei de aumentar a complexidade e o encaminhamento do código. Será ainda mais difícil para a próxima pessoa juntar as peças da imagem.
Empiricamente, cheguei à conclusão: existe um código que você não entende, mas existe um código com o qual você definitivamente nunca entrará em contato. Apague o código que você não contata e tenha cuidado com o código que você não entende.
Revisão de código
Uma revisão de código é uma ótima ferramenta para auto-educação. Este é um loop de feedback externo, mostrando como eles escreveriam o código e como você o escreveu. Qual a diferença? De um jeito melhor que do outro? Eu me perguntava sobre isso em todas as resenhas: "Por que eles escreveram dessa maneira?" E se ele não conseguiu encontrar uma resposta adequada, foi e perguntou.
Após o primeiro mês, comecei a encontrar erros no código dos meus colegas (como eles encontraram nos meus). Era algum tipo de loucura. A revisão se tornou muito mais interessante para mim, tornou-se um jogo que eu estava perdendo, um jogo que melhorou meu "senso de código".
Na minha experiência, você não precisa aprovar o código até que eu entenda como ele funciona.
Minhas estatísticas do github.Teste
Eu me apaixonei tanto por testar que é desagradável escrever código em uma base de código sem testes.
Se o seu aplicativo fizer apenas uma coisa (como todos os meus projetos da escola), você ainda poderá testar manualmente.
4 Foi exatamente o que eu fiz. Mas o que acontece se um aplicativo executar 100 tarefas diferentes? Não quero passar meia hora testando e às vezes perco alguma coisa. Um pesadelo.
Testes e automação de teste ajudam aqui.
Trato o teste como documentação. Esta é a documentação das minhas idéias sobre código. Os testes me dizem como eu (ou qualquer outra pessoa antes de mim) imagino que o código funcione e onde algo esperado deve dar errado.
Hoje, quando escrevo testes, tento:
- Mostre como usar a classe, função ou sistema de teste.
- Mostre o que, na minha opinião, pode dar errado.
Como resultado, na maioria das vezes eu testo o comportamento, mas não a implementação (
eis um exemplo que escolhi durante os intervalos no Google).
No parágrafo 2, não mencionei as fontes de erros.
Quando percebo um bug, certifico-me de que a correção tenha um teste apropriado (isso é chamado de teste de regressão) para documentar as informações. Essa é outra razão pela qual algo pode dar errado.
5Obviamente, a qualidade do meu código melhora não porque escrevo testes, mas porque escrevo código. Mas ler os testes me ajuda a entender melhor as situações e escrever um código melhor.
Esta é a situação geral com o teste.
Mas este não é o único tipo de teste que eu aplico. Eu estou falando sobre ambientes de implantação. Você pode ter testes de unidade ideais, mas se você não tiver testes de sistema, algo como isto pode acontecer:

Isso também se aplica ao código testado: se você não possui as bibliotecas necessárias no seu computador, tudo irá falhar.
- Existem máquinas nas quais você está desenvolvendo (a fonte de todos os memes como "Funcionou no meu computador!").
- Existem máquinas nas quais você está testando (pode coincidir com as anteriores).
- Por fim, existem máquinas nas quais você está implantando (elas não devem coincidir com as máquinas nas quais você desenvolveu).
Se os ambientes de teste e implantação não corresponderem nas máquinas, você terá problemas. Os ambientes de implantação ajudarão a evitar isso.
Estamos desenvolvendo localmente no Docker em nosso computador.
Temos um ambiente de desenvolvimento, esses computadores estão equipados com um conjunto de bibliotecas (e ferramentas de desenvolvimento) e aqui instalamos o código escrito. Aqui pode ser testado com todos os sistemas necessários. Também temos um ambiente beta / intermediário que repete completamente o ambiente operacional. Finalmente, temos um ambiente operacional - máquinas que executam código para nossos clientes.
A idéia é capturar erros que não apareceram durante os testes de unidade e sistema. Por exemplo, a diferença na API entre os sistemas solicitante e respondente. Eu acho que no caso de um projeto pessoal ou de uma pequena empresa, a situação pode ser completamente diferente. Nem todo mundo tem a oportunidade de criar sua própria infraestrutura. No entanto, você pode recorrer a serviços em nuvem, como AWS e Azure.
Você pode configurar clusters individuais para desenvolvimento e operação. O AWS ECS usa imagens do Docker para implantar; portanto, processos em diferentes ambientes serão relativamente consistentes. Existem nuances em termos de integração entre diferentes serviços da AWS. Você está chamando o endpoint certo do ambiente certo?
Você pode ir ainda mais longe: baixe imagens de contêineres alternativas para outros serviços da AWS e configure um ambiente local totalmente funcional baseado no Docker-Compose. Isso acelera o ciclo de feedback.
6 Talvez eu ganhe mais experiência ao criar e lançar meu projeto paralelo.
Redução de risco
Que medidas você pode tomar para reduzir seu risco de desastre? Se estamos falando de uma nova mudança radical, como podemos verificar a duração mínima do tempo de inatividade, se algo der errado? "Não precisamos implantar completamente o sistema por causa de todas essas novas alterações." O que realmente? E por que eu não pensei nisso!
Arquitetura
Por que estou falando de arquitetura depois de escrever código e testar? Ele pode ser colocado em primeiro lugar, mas se eu não tivesse programado e testado no ambiente que usei, provavelmente não teria conseguido criar uma arquitetura que levasse em consideração os recursos desse ambiente.
7
Você precisa pensar muito ao criar uma arquitetura.
- Como os números serão usados?
- Quantos usuários haverá? Quanto pode aumentar seu número (o número de linhas no banco de dados depende disso)?
- Que recifes podem se encontrar?
Preciso transformar isso em uma lista de verificação chamada "Recolha de reivindicação". No momento, não tenho experiência suficiente, tentarei fazê-lo no próximo ano na Bloomberg. Esse processo é amplamente contrário ao Agile: quanto você pode projetar a arquitetura antes de avançar para a implementação? É tudo uma questão de equilíbrio, você precisa escolher quando e o que fará. Quando faz sentido avançar e quando - recuar? Obviamente, coletar requisitos não é o mesmo que considerar todos os problemas. Eu acho que vale a pena se incluirmos os processos de desenvolvimento no design. Por exemplo:
- Como o desenvolvimento local continuará?
- Como vamos embalar e implantar?
- Como conduziremos testes de ponta a ponta?
- Como conduziremos o teste de estresse do novo serviço?
- Como vamos guardar segredos?
- Integração de CI / CD?
Recentemente, desenvolvemos um novo mecanismo de pesquisa para o
BNEF . Foi maravilhoso trabalhar nisso, organizei um desenvolvimento local e descobri o DPG (pacotes e sua implantação), cobrando a implantação de segredos.
Quem pensaria que implantar segredos em um prod pode ser tão trivial:
- Eles não podem ser colocados no código, porque alguém pode notá-los
- Para armazená-los como uma variável de ambiente, pois a especificação oferece 12 fatores de aplicação? Não é uma má idéia, mas como colocá-los lá? (Ir ao produto para preencher variáveis de ambiente toda vez que o carro dá partida é uma dor)
- Implantá-los como arquivos? Mas de onde eles virão e como preenchê-los?
Não queremos fazer tudo manualmente.
Como resultado, chegamos a um banco de dados com controle de acesso baseado em função (somente nós e nossos computadores podemos nos comunicar com o banco de dados). Nosso código recebe segredos do banco de dados na inicialização. Essa abordagem é bem replicada dentro da estrutura de ambientes de desenvolvimento, preparo e operação; os segredos são armazenados em bancos de dados apropriados.
Novamente, com serviços em nuvem como a AWS, a situação pode ser completamente diferente. Você não precisa cuidar de segredos de alguma forma. Obtenha uma conta para sua função, insira os segredos na interface e seu código os encontrará quando forem necessários. Isso simplifica muito tudo, mas estou feliz por ter adquirido experiência, graças à qual posso apreciar essa simplicidade.
Criamos arquitetura, sem esquecer a manutenção
O design do sistema é inspirador. E a escolta? Não é demais. Minha jornada pelo mundo das escoltas me levou à pergunta: por que e como os sistemas se degradam? A primeira parte da resposta não está relacionada ao descomissionamento de todos os obsoletos, mas apenas à adição de um novo. A tendência de adicionar e não excluir (isso não lembra nada?). A segunda parte é projetar com o objetivo final em mente. Um sistema que, com o tempo, começa a fazer o que não foi planejado, não funcionará necessariamente, assim como um sistema originalmente projetado para as mesmas tarefas. Esta é uma abordagem de estilo de retrocesso, não truques e truques.
Conheço pelo menos três maneiras de reduzir a taxa de degradação.
- Lógica e infraestrutura de negócios separadas: a infraestrutura geralmente se degrada - a carga aumenta, as estruturas se tornam obsoletas, as vulnerabilidades de dia zero aparecem etc.
- Crie processos para suporte futuro. Aplique as mesmas atualizações para bits antigos e novos. Isso evitará diferenças entre o antigo e o novo e manterá todo o código em um estado "moderno".
- Certifique-se de jogar fora tudo o que é desnecessário e antigo.
Implantação
Vou reunir os recursos ou implantá-los um de cada vez? Dependendo do processo atual, se você juntá-los, aguarde problemas. Pergunte a si mesmo por que você deseja agrupar recursos?
- A implantação leva muito tempo?
- A revisão de código não é muito divertida?
Seja qual for o motivo, essa situação precisa ser corrigida. Conheço pelo menos dois problemas associados à embalagem:
- Você mesmo bloqueia todos os recursos se um deles tiver um bug.
- Você aumenta o risco de problemas.
Qualquer que seja o processo de implantação escolhido, você sempre quer que seus carros sejam como gado, não como animais de estimação. Eles não são únicos. Você sabe exatamente o que é executado em cada máquina, como recriá-los em caso de morte. Você não ficará chateado se algum carro morrer, basta pegar um novo. Você as pasta, não cresce.
Quando algo der errado
Caso algo dê errado - e aconteça -, existe uma regra de ouro: minimizar o impacto nos clientes. Em caso de falhas, meu primeiro desejo sempre foi corrigi-lo. Esta não parece ser a solução ideal. Em vez de consertar, mesmo que isso possa ser feito em uma linha, você precisa reverter primeiro. Retorne ao estado operacional anterior. Essa é a maneira mais rápida de retornar os clientes para uma versão funcional. Só então eu descubro qual é o problema e o soluciono.
O mesmo se aplica à máquina "danificada" no seu cluster: desligue-a e marque-a como inacessível, antes de descobrir o que aconteceu com ela. Acho estranho o quanto meus desejos e instintos naturais contradizem a solução ótima.
Eu acho que esse instinto também levou ao fato de eu corrigir bugs por mais tempo. Às vezes, percebi que algo não funcionava, porque o código que escrevi estava de alguma forma errado e subi na natureza, olhando cada linha. Algo como uma pesquisa "primeiro em profundidade". E quando se descobriu que o problema surgiu devido a uma alteração na configuração, ou seja, eu não o verifiquei em primeiro lugar, essa situação me perturbou. Eu perdi muito tempo procurando por um bug.
Desde então, aprendi a pesquisar “primeiro em profundidade” e, portanto, já “primeiro em profundidade” para excluir razões de nível superior. O que exatamente posso confirmar com os recursos atuais?
- O carro funciona?
- O código está instalado corretamente?
- Existe uma configuração?
- <Configuração específica do código>, como se o roteamento está escrito corretamente?
- A versão do esquema está correta?
- E então eu mergulho no código.
Achamos que o nginx foi instalado incorretamente, mas acabou que a configuração estava desativada
Claro, eu não preciso fazer isso o tempo todo. Às vezes, apenas uma mensagem de erro é suficiente para chegar ao código imediatamente. Quando não consigo determinar a causa, tento minimizar o número de alterações no código para descobrir o motivo. Quanto menos alterações, mais rápido posso encontrar a verdadeira raiz do problema. Além disso, agora tenho um memorando para bugs que me salvou mais de uma hora pensando "do que eu perdi?" Às vezes, esqueço as verificações mais simples, como configuração de roteamento, esquema correspondente e versões de serviço, etc. Esse é outro passo no desenvolvimento da pilha de tecnologia que eu uso, e o que você ganha apenas com experiência é a intuição para determinar o que exatamente não funciona.
Bicicleta
Este artigo não pode ser concluído sem uma história.
Eu gosto de lê-los e quero compartilhar um deles com você. Esta é uma história sobre pesquisa e SQLAlchemy. Existem muitos analistas no BNEF que escrevem relatórios de pesquisa. Quando o relatório é publicado, recebemos uma mensagem. Quando recebemos uma mensagem, acessamos o banco de dados por meio do SQLAlchemy, obtemos os dados necessários, os convertemos e os enviamos para indexação em uma instância do Solr. E de alguma forma um bug estranho surgiu.Todas as manhãs, a conexão com o banco de dados travava com o erro "O servidor MYSQL desapareceu". Às vezes isso acontecia durante o dia. Os carros ligam durante o dia, então essa foi a primeira coisa que verifiquei. Não, não houve erro ao ligar o computador. Fizemos milhares de consultas ao banco de dados durante o dia, estava tudo em ordem. Então, qual é o problema que levou ao fracasso?, ? , , . , ,
__exit__()
session.close()
.
, , . . . . .
Session.close()
MySQL- SQLAlchemy , NullPool. . , , . : StackOverflow (, !) , , SQL- . , . , , (), .
«» , 1 8. , , — .
, .
Monitoramento
, . , , . , .
, , , . , . , . «
?! , ? ».
, : , . , , . , , . , . — ? , , . . , -, , , , , .. .
. , , ? (, AWS CloudWatch Grafana). .
. , , 50 %, — . ? . , — (, ?).
. , , , , ? ? , ?
, . , . — - .
Conclusão
. , , , . , - !
. , !
, . , — How to Build Good Software .
. : ! , .
- ?
- ? , ? , ?
- . , ? ?
- (utils) (, , , ) , « »?
- ?
- , , - ?
- — API , ?
- ? , .
- , , . , , .
- PR: « , , 52 , , , , , ». ?
- . ?
- ?
- ?
- . , ? - ? , ?
- ,
x()
, y()
z(),
x()
, y()
z()
. , WYSIATI .
- .
- . , ?
- , - , , . , .
- , , Docker- AWS.
- .