Na véspera do Moscow Python Conf ++, conversamos com Nikita Sobolev, CTO da empresa We Make Services, sobre o problema global de gerenciar a complexidade do código no contexto do desenvolvimento de linguagens de programação. E também sobre por que aqui, com o tempo, a situação só piora. Além disso, perguntaram por que ele precisava criar seu próprio linter.
- Diga-nos algumas palavras sobre você e seu trabalho.Eu sou o diretor técnico de "Nós fazemos serviços". Falando o nome da empresa, costumo fazer a pergunta: "O que você acha, o que estamos fazendo?". De fato, somos especializados em desenvolvimento web: front-end e back-end para clientes corporativos. E trabalhamos de acordo com nossa própria metodologia, que estamos melhorando paralelamente ao desenvolvimento da empresa - Processo de Desenvolvimento de Software Repetível (RSDP).
- No Moscow Python Conf ++, você conversará, inclusive sobre seu próprio linter. Como o seu trabalho está relacionado à auditoria e ao gerenciamento da complexidade do código?Em geral, temos duas áreas principais: o desenvolvimento em si e tudo que o rodeia: consultoria, elaboração de requisitos e, em particular, auditoria, durante as quais vejo muitos códigos de outras pessoas. O código é completamente diferente: o que está agora em desenvolvimento e o legado, que ninguém jamais corrigirá; e o código que os especialistas do cliente escrevem e o que eles pediram ao lado. E em todas as versões do código há muitos problemas: iguais e diferentes.
- Você conversará com os desenvolvedores especificamente em Python. O Python possui algum recurso em termos de gerenciamento da complexidade do código?Claro!
Em primeiro lugar, todos os idiomas com digitação dinâmica sofrem mais com a complexidade injustificada, pelo menos devido à falta de contexto adicional ao ler o código. E você tem mais sujeira.
Em segundo lugar, o Python está se desenvolvendo ativamente. Possui novos elementos de sintaxe, novos conceitos e módulos em bibliotecas padrão que quebram tudo o que era antes.
- Quão ruim é tudo em Python? Afinal, existem outras linguagens em desenvolvimento ativo, por exemplo, JavaScript, que é frequentemente criticado por isso. O JavaScript é melhor?Não. Eu diria até que, em termos de complexidade, o Python é muito bom em relação a outras linguagens. O JavaScript é muito ruim por um motivo simples: no código do projeto JS, várias entidades que não estão relacionadas à linguagem em si são misturadas de uma só vez - plug-ins e bibliotecas de terceiros usados para construir o projeto. Por exemplo, se você usa o Webpack, pode escrever a função `import ()`, que carrega os módulos de forma assíncrona. Acontece que o coletor coloca um pouco de seu interior na sua linguagem de programação e, no final, geralmente não está claro o que está acontecendo.
É difícil gerenciar a complexidade quando o idioma muda com a instalação do Babel ou de seus plugins. E para entender como eles funcionam, você precisa seguir os padrões de linguagem, a implementação específica etc.
No Python, a situação é muito melhor. A linguagem está se desenvolvendo de forma bastante sistemática, e esse desenvolvimento tem marcos compreensíveis. Nele, você não pode alterar radicalmente a sintaxe com duas linhas na configuração. E isso ainda é um back-end, ao qual estamos acostumados a fazer demandas mais altas do que ao front-end. No entanto, na minha opinião, em Python, existem algumas mudanças novas que quebram o que era antes, trazendo benefícios duvidosos.
- Ou seja, com o desenvolvimento da linguagem, tudo fica pior?Se você se lembra que o AsyncIO apareceu - essencialmente uma segunda linguagem dentro do Python - é claro, a complexidade aumentou muito. De fato, agora existem duas linguagens de programação completamente independentes com sintaxe semelhante: Python e Python + AsyncIO. Ou seja, o Python como entidade se tornou mais complicado duas vezes, porque tem dois descendentes separados trabalhando de acordo com regras diferentes.
A opinião de que essas são linguagens de programação diferentes não é popular. No entanto, quando você pergunta aos oponentes dessa opinião, por exemplo, para executar uma função assíncrona a partir do código síncrono, eles falham. Bibliotecas também são completamente diferentes. Deseja usar uma biblioteca síncrona para trabalhar com o banco de dados - por favor. E você quer assíncrono - não é.
Mas no Python, que foi escrito há cinco anos, nada realmente mudou, e até vice-versa, surgiram ferramentas que simplificaram o código, por exemplo, anotações e verificação de tipo.
- O gerenciamento da complexidade afeta o fato de que na programação agora existem muitas pessoas com uma base técnica fraca?Claro. Para essas pessoas, eles chegaram a criar uma linguagem de programação especial. Chama-se Go. Eu não estou brincando. De fato, o objetivo de criar a linguagem Go era uma tentativa de envolver estudantes e estagiários do Google que não podem aprender C ++ na escrita de código. O Python não combinava com eles no desempenho, eles precisavam de outra coisa, e o Google criou o Go. Como se viu, muitas pessoas estão prontas para escrever, porque é muito simples. Mas a que custo essa simplicidade foi alcançada? Eles não nos dão uma linguagem de programação normal, mas sua versão muito truncada - praticamente não há conceitos complicados por design nela. Não existem genéricos, não existem exceções, etc. E há muitos fãs dessa abordagem.
Mas existem outros desenvolvedores e, para eles, o problema é que existem idiomas nos quais não há equilíbrio: você pode fazer coisas simples e não pode fazer coisas complexas. Ou pelo menos através da dor - você tem que lutar com uma ferramenta para fazer alguma coisa. Aqui, parece-me, está o problema de gerenciar a complexidade.
- Quais são os problemas típicos do código de outra pessoa?Geralmente eles são divididos em duas partes.
O primeiro são os problemas associados ao fato de as pessoas não poderem concordar sobre onde colocar vírgulas condicionais. Você lê um código e vê vírgulas em um lugar, alterna para outro arquivo - e vê vírgulas em outro lugar. Isso complica a percepção, como se estivesse lendo um livro impresso em um lugar em negrito e em outro em itálico. Isso distrai o conteúdo, porque o cérebro precisa reconhecer que é uma maneira diferente de escrever a mesma coisa.
Quando você corrige a sintaxe, começa a prestar atenção na semântica, porque as pessoas escrevem conceitualmente de maneira diferente. Infelizmente, não há como chegar a um acordo sobre esse nível - é impossível chegar a um acordo que resolvamos esses problemas dessa maneira, mas são esses. Não é possível cobrir todos os casos inicialmente. Esse processo ocorre durante uma revisão de código de uma tarefa imediata: quando o desenvolvedor é explicado por que sua decisão não pode ser tomada. Se a prática da revisão de código for aplicada e os revisores forem bons, eles cortam as curvas da solução e não há problemas no código. Mas geralmente chegamos à auditoria onde esse processo não está estabelecido. E os problemas de semântica e arquitetura são muito mais difíceis de resolver, porque às vezes são difíceis de formular e definir por si mesmos.
- E como é na prática?Por exemplo, as pessoas podem resolver o mesmo problema em modelos, visualizações ou modelos. E não há um entendimento geralmente aceito sobre onde exatamente essa tarefa deve ser resolvida: nenhuma documentação ou padrões aplicáveis especificamente a este projeto (por exemplo, aqui usamos modelos espessos e colocamos toda a lógica neles, mas aqui usamos modelos finos; bons ou ruins, agora isso não importa, mas concordamos que sim).
"Qual é a principal razão pela qual esses problemas existem?"Todas as pessoas não sabem escrever código.
Esta tese é decifrada da seguinte forma: o problema é que somos pessoas. E, em geral, é muito difícil escrever algo estruturado e lógico. E aqui também temos dois tipos diferentes de destinatários. Primeiro, esta é a pessoa que lerá esse código e, segundo, esta é a máquina que deve executá-lo. O código para a máquina deve ser criado de acordo com os critérios de desempenho, consumo de memória e tempo de CPU, e o código para a pessoa deve ser baseado nos princípios de legibilidade, inteligibilidade etc. Essas são duas tarefas opostas. E uma pessoa que, de fato, não pode resolver completamente nem um deles, é forçada a resolver as duas tarefas contraditórias ao mesmo tempo.
"Mas o uso de diferentes padrões de programação é essencialmente uma pesquisa de engenharia?" Isso é muito ruim?Obviamente, a pesquisa em engenharia é importante e necessária. Mas ele também deve ser administrável. Antes de cada tarefa, é necessário definir critérios e limitações claros: de acordo com o tempo gasto, nos requisitos de negócios, nas práticas e ferramentas de engenharia.
É muito mais provável que eu observe pesquisas de criativos. Também não existem restrições, validação dos resultados obtidos. A qualidade - como na arte contemporânea - não pode ser medida.
Quase todos os clientes que recorrem a nós para uma auditoria sofrem de uma situação típica: alguém fez algo com eles, eles contrataram um desenvolvedor para desenvolver uma solução, mas ele veio e abriu as mãos: "Não sei o que aqui para fazer, vamos reescrever tudo. " Seria bom reescrever? Não será. Quando você decide reescrever, pisa exatamente no mesmo rake: confia a tarefa a outro desenvolvedor que comete outros erros, mas no final tudo acaba exatamente da mesma maneira.
- Precisa de alguma outra abordagem?Sim Durante a auditoria, tentamos encontrar a causa dos problemas com o código: por que alguém não pegou e inflou o módulo a tal ponto que seria difícil rolar a tela e por que a decisão errada foi tomada inicialmente. E estamos tentando automatizar ou simplificar o máximo possível as decisões corretas dentro dos limites estabelecidos.
Vou dar uma pequena parte interna do relatório. Todo mundo entende que o código consiste em linhas - essa é a entidade mais simples da qual ele pode consistir. Cada linha pode ser escrita como
x = 1
ou talvez como
x = Math.median(forecast_data) if forecast_data else compute_probability(default_model)
.
Há uma diferença muito grande entre essas duas linhas, porque você entende facilmente a primeira e muita lógica está concentrada na segunda. É necessário executá-lo na cabeça em paralelo com o intérprete. Portanto, você precisa começar a controlar como escreve o código, com o controle de uma única linha de código. Além disso, a linha se transforma em conceitos mais complexos - funções, classes, módulos, etc. Mas as regras que você aceita devem ser uma.
Como resultado, não proibimos que muitas coisas sejam feitas. Porque o gerenciamento é sobre proibições impostas.
- Você já se deparou com alguma coisa engraçada no código de outra pessoa?Claro. Eu até tenho um
repositório onde coleciono esses exemplos de código.
O exemplo mais assustador que vi me mostrou que dentro de um loop de cem iterações, você pode definir uma função. Para ser sincero, quando olhei, meu intérprete quebrou. Eu imaginei, mas não sabia que era possível.
Houve um caso em que vimos muitos comentários engraçados no código. Alguém reclamou da vida, do trabalho, quem escreveu: "Entendo que estou escrevendo bobagens, mas o cliente me obriga a fazê-lo". No entanto, os clientes geralmente não o forçam a escrever códigos incorretos. Eles pedem para resolver o problema deles, e qual código você escreve lá, eles não se importam.
- Linter, revisão de código - não salva?Eu tenho duas respostas. Sim eles fazem. Não, eles não salvam. Ajuda se você seguir rigorosamente as regras e os regulamentos fornecidos pelos lanters alinhados (aqueles que fazem muito trabalho duro para você: verifique as funções quanto à complexidade, semântica de códigos etc.). Este item deve estar bloqueando. Às vezes, você não pode simplesmente executar o linter para ver o resultado. Se você falhou nessas regras, não deve liberar o código em produção.
Mas, de fato - eles não salvam. Porque os projetos que o utilizam são raros.
Aliás, eles costumam me perguntar: como introduzir isso? E eu respondo: é muito simples, você coloca uma linha no IC - verifique meu código - e se ele travar, é isso, você o implementou. Resta apenas refatorar tudo. Felizmente, agora existem autoformadores e a capacidade de refatorar código por arquivo. A próxima pergunta é tradicional: como explicar aos negócios que isso é importante?
- Existe alguma resposta geral para esta pergunta?Para cada caso, as respostas são diferentes, portanto, no caso geral, é difícil formular (você precisa pensar sobre isso, sobre isso ...). Mas geralmente as empresas que lidam com esse problema vêm do lado técnico. I.e. os técnicos nos perguntam, como as pessoas que sabem falar sobre negócios e sobre tecnologia entendem como explicar isso aos negócios em seu caso particular. Com essa afirmação do problema, isso simplesmente funciona. Quando você vem, tudo já está ruim, e todo mundo entende isso. Uma conversa com os negócios começa assim: "Você provavelmente pensa que seus programadores estão sentados e sem fazer nada?" E os negócios concordam. E você diz que não é esse o ponto. Programadores são ótimos profissionais que tentam resolver seus problemas. Mas sem uma abordagem integrada ao gerenciamento de projetos, tudo cai no caos, e isso é normal.
E propomos criar regras para evitar certos problemas. Consideramos o custo da introdução de peças diferentes e, em seguida, avaliamos as perdas reais (realizadas) pelo fato de ainda não existirem. Por exemplo, os programadores corrigem um bug há um mês que não existe ou que pode ser encontrado em 30 segundos, se você usar uma abordagem e uma ferramenta específicas. Os números convencem bem.
- No final, isso é um problema administrativo?Claro. Estou convencido de que os programadores querem escrever um bom código. Mas existem vários obstáculos. Alguém não sabe como por causa da inexperiência. Alguém perdeu a motivação, porque todo mundo não se importa. Alguém não sabe exatamente o que é um bom código pelo motivo, digamos, da criação criativa. Eles pressionam alguém - ele quer e pode escrever, mas dizem a ele que deve ser amanhã. E, em vez de criar parcerias com empresas e explicar por que isso não acontecerá amanhã (ou, se acontecer, será necessário governar por mais três dias), ele o faz de qualquer maneira. E essas parcerias são interessantes para o próprio negócio. Ele também precisa fazê-lo funcionar por um longo tempo e ser barato de manter.
Ou seja, todas as questões são resolvidas aqui: não há contradições insolúveis.
- Existe um estilo de código - PEP 8. Não ajuda a entender rapidamente o que é bom?Em termos de vírgulas, ajuda. Mas qual é o sentido de colocar as vírgulas certas e todo o resto é ruim?
- Está faltando alguma coisa bem conhecida de nível superior?Em teoria, existem algumas boas práticas de engenharia. Mas eles são desconhecidos ou ignorados. Quando você pergunta por que o desenvolvedor não seguiu essa prática, ele diz que parece ter ouvido que esse é um bom tópico, mas o código funciona dessa maneira. Quando o código para de funcionar, você pergunta se ele entendeu de onde veio a melhor prática correspondente e por que segui-la? Não, eu não entendi. Ele acredita que estava simplesmente enganado.
É muito difícil explicar para uma pessoa que cometer erros é normal. Todo mundo está errado, somos todos humanos. Mas a melhor prática de engenharia foi inventada para salvá-lo de um erro ou protegê-lo das consequências. I.e. é uma ferramenta de segurança, como nas empresas. É escrito não apenas com sangue, mas com tempo e dinheiro abandonados.
Em geral, nossa tarefa global inatingível é automatizar a revisão de código para que o próprio Python (se estamos falando do nosso caso) saiba como escrevê-lo. Essa deve ser uma ferramenta que fornece não apenas oportunidades, mas também limitações para os desenvolvedores.
- Por que você está desenvolvendo um linter? É possível usar (ou desenvolver) os existentes?De fato, fazemos isso. Nosso linter de fato é um plugin para o Flake8. Simplesmente o posicionamos como uma ferramenta completa, e não apenas como um plug-in.
Por que Flake8 e não Pylint? Pylint faz muito do que o linter não deve fazer. Por exemplo, ele implementa um número muito grande de verificações de tipo, embora o verificador de tipos deva lidar com tipos. Além disso, produz um número muito grande de erros, que na verdade não são. E não gosto da documentação dele, e tenho medo de sua própria implementação do ast. É difícil de configurar. Ao ativar a configuração, você permite que as pessoas façam as escolhas erradas. Portanto, nossa tarefa é criar uma ferramenta que não possa ser configurada. Então você coloca - é tudo.
- Quais guias formaram a base desse linter? Ou é apenas a sua própria experiência aqui?Agora, ele se baseia nas regras que formulamos para nós mesmos na revisão de código por muitos anos. Algumas regras foram transportadas de outros linters: ESLint, Pylint, SonarQube, Credo. Muito foi retirado do excelente trabalho
do CognitiveComplexity . Sempre olhou para trás na carteira de Miller. Regras separadas - esta é a minha visão, que apareceu depois de avaliar um grande número de códigos de outras pessoas. Ou seja, nesta fase, é uma "mistura".
- O que você vai falar no Moscow Python Conf ++?Primeiro de tudo, sobre
gerenciamento de complexidade . Este tópico é próximo e compreensível para todos os desenvolvedores. Examinaremos diferentes métricas, sobre maneiras de transferir a complexidade do código de componente mais simples - linhas - para o mais complexo - módulo. E então falaremos sobre a parte holística, onde apresentarei minha visão de como escrever ou não escrever em Python e pedir aos usuários que votem no que gostam e no que não. Para muitos desenvolvedores, as restrições (execução A, mas não execução B) são uma tentativa em seu espaço criativo; portanto, elas reagem violentamente a isso. E apenas aqui você pode iniciar uma discussão interessante.
- Em quem o relatório está focado?Eu acho que esses desenvolvedores ainda estão estabelecidos, porque os programadores iniciantes ainda não formaram sua opinião clara. Embora seja interessante para eles ouvirem e falarem. Eles são definitivamente nossos usuários.
, ,
Moscow Python Conf++ . . ,
.