Mostre-me uma solução por causa da qual os desenvolvedores não discutirão, e eu colocarei uma cerveja para você



Existem dois campos inconciliáveis. Alguns dizem: você deve descrever com precisão e competência seus commits. Cada confirmação é um trabalho completo e significativo. Se você não pode fazer uma descrição clara e simples de uma confirmação, você tem as confirmações erradas.

Outros consideram que você faz confirmações como quiser, isso faz parte do seu fluxo de trabalho pessoal. Mas o conjunto de solicitações é descrito em detalhes: o que é feito, como é feito, por que é feito. Conforme testado, qual problema resolve, em que você deve prestar atenção.

Sou um firme defensor da segunda abordagem - é inconveniente para mim transformar meu trabalho em pedaços. Pego uma pequena tarefa e corro aleatoriamente em torno da base de código, experimentando e fazendo alterações na ordem em que ela ocorre. Se eu pudesse clicar em um botão, e meu trabalho fosse reestruturado em boas confirmações, eu clicaria nele. Mas não há botão, mas não quero me quebrar. Ao mesmo tempo, alcancei certa habilidade na descrição do pool de solicitações. Por acaso, descobri o código na Microsoft (através da equipe externa, isso não conta) e lá tenho os principais padrões para processar um pool de solicitações. Ao longo dos anos, acabei de desenvolver essa prática. Geralmente, conseguia convencer a equipe a usar exatamente essa abordagem.

Mas, no último trabalho, fiquei com o devlid - um forte defensor de confirmações detalhadas. Oh, discutimos por um longo tempo. Minha posição subordinada desempenhou um papel, o hábito de Sovkovsky de concordar com o principal não é fácil de calar a boca. Eu não era tão categórico como de costume e foi colocado nas duas lâminas. Empilhados, mas não convencidos.



Se de repente os desenvolvedores se unirem em um único governo, estabelecerem regras e começarem a proclamar certas abordagens proibidas, uma guerra civil começará no dia seguinte. Tudo porque no mundo dos desenvolvedores há uma crença no objetivo e na mensuração. Na verdade, independente da percepção humana. E se tudo isso existe, significa que um dos disputantes está claramente objetivamente errado.

Minha primeira disputa séria de trabalho ocorreu muito cedo. Eu ainda era um junho verde, que acha que ele é o meio melhor e, de fato, um desenvolvedor muito legal e inteligente. Um desenvolvedor real da minha equipe lançou PR. Tivemos uma prática em que todos os membros da equipe fazem um código de revisão. Abri o código e quase imediatamente vi o problema. Um homem escreveu um teste para alguma função que levou um número. Então ele deu a ela 0, 1000 e aleatoriamente (0, 1000).

Naquela época, senti muito bem que outros colegas de equipe me viam como um novato estúpido. E ele esperou um momento para manchá-los com sua visão. Eu tive sorte - aleatória nos testes!

Eu realmente não entendi a teoria dos testes de unidade, mas li alguns livros e lembrei-me firmemente - o mesmo teste de unidade na mesma base de código deveria dar o mesmo resultado. Passei cerca de uma hora pensando no comentário para que ele não parecesse tóxico, mas deixei claro que apenas o macaco, que foi ensinado a contar ontem, poderia pensar em tal decisão. No final, é claro, ele parecia extremamente estúpido, como tudo o que os estagiários de ontem que se imaginavam desenvolvedores se espremendo.

No dia seguinte, percebi que havia aberto um portal para o inferno. Havia mais cem sob o meu comentário. Durante o resto do dia, em vez de uma equipe de seis desenvolvedores, éramos uma equipe de seis macacos. Argumentamos em inglês no próprio PR, embora todos fossem russos. Quando o estoque de palavras não era suficiente, eles telefonaram. Eles se jogaram um no outro com links, citações de livros, estudos, voltados para o indivíduo. Um colega respondeu a um dos meus comentários com uma captura de tela de um dicionário de inglês para ridicularizar meu inglês. Mas não chegamos a um compromisso.

A pergunta foi complicada. Por um lado, nem todos sabíamos de que maneira esse parâmetro era responsável, nossa função o passava para o outro, a partir de uma rede interna na qual não havia docas. Portanto, teoricamente, a função pode recair sobre qualquer valor, o que significa que o teste pode capturá-la e, portanto, podemos antecipar o erro no prod. Por outro lado, já tínhamos o problema de construir aleatoriamente chafurdar. Por esse motivo, se o projeto estava sendo construído localmente e estava travando, pedimos estupidamente uma nova compilação e não analisamos nenhum registro. Afinal, a construção levou quatro horas e ninguém examinaria a solicitação de recebimento até que ela passasse no CI.

Além disso, os carros estavam frequentemente ocupados - era muito mais pragmático encomendar uma construção enquanto ela ainda estava sendo encomendada do que procurar um "problema" em potencial (que quase sempre era um tempo limite no teste de integração). Isso significava que, mesmo que o teste aleatório falhasse, não prestaríamos atenção.

Tudo foi resolvido no próximo acordo. Telefonamos para toda a equipe e, durante meia hora, continuamos discutindo - em russo. Não percebemos como nosso gerente de desenvolvimento americano entrou na chamada e continuamos a nos esmagar com argumentos em tom elevado. Até eles ouvirem “Ei, pessoal, isso não é absolutamente importante. Basta mesclar como está. " Não sei quanto tempo ele nos ouviu antes e como ele entendeu o que estávamos falando, mas paramos bruscamente de discutir. Eles congelaram e se esqueceram disso. Separados em paz, mas não convencidos.



Desde então, adquiri experiência para entender - mas não dou a mínima, casos aleatórios, desvantagens ou limites. Este maldito teste de unidade irá interferir - nós reescreveremos. Tudo é melhor do que discutir o dia todo. Mas se você imaginar que estou em um mundo ideal, onde é sempre necessário tomar apenas decisões mais corretas, não sei o que fazer. Congelamos um teste com aleatoriedade e agora entendo isso como uma escolha em favor da confiabilidade, mas em detrimento da conveniência do desenvolvimento.

No outro dia, enfrentei um dilema semelhante. Eu brinquei com o novo código de tempo e escrevi algo que pode ser usado assim

// LessThan<T>  MoreThan<T> -    ,   . 
// ,        

//      , 
//   ,   100
const consumeNumberLessThan100 = (v: LessThan<100>) => 
    doWork(v);

//     ,   100
const consumeNumberMoreThan100 = (v: MoreThan<100>) => doWork(v);

const sample = (x: number) => {

    //     check  , 
    //   x  100
    //  "<"  ">"  , 
    //     -   
    const lessThan100 = check(x,'<', 100);

    if (lessThan100) {
        //     -  
        //       
        //    ,  lessThan100  - LessThan<100>
        //    
        consumeNumberLessThan100(lessThan100); 

        //   -  ,   , 
        //  ,   > 100.
        consumeNumberMoreThan100(lessThan100);
    }

    const moreThan100 = check(x, '>', 100);

    if (moreThan100) {
        consumeNumberMoreThan100(moreThan100); // 
        consumeNumberLessThan100(moreThan100); // 
    }

    consumeNumberLessThan100(x); // 
    consumeNumberMoreThan100(x); // 
}

Eu pensei, caramba, isso é muito legal - para que você possa detectar um grande número de erros em um estágio anterior. O design das minhas ferramentas de restrição de valor é terrível, mas por enquanto é apenas um conceito. No futuro, você poderá expandi-lo facilmente, criar uma DSL poderosa e formular condições realmente complexas para a correspondência de parâmetros garantida no estágio de compilação. Eu descobri como isso pode aumentar a confiabilidade de qualquer base de código, animou-se e enviei um trecho para vários desenvolvedores familiares.

As opiniões foram divididas novamente, e não na minha direção. Replicação, excesso de engenharia, não são suportadas, todos irão atolar sua guarda com a ajuda de um elenco para eni. Ilegível. Bom como um experimento é ruim no presente projeto. Exemplos de uso são retirados do dedo. Vamos direto ao assunto, Phil.

Os defensores da abordagem disseram que sim, tão confiáveis. Quanto mais cedo você cometer um erro, melhor. Além disso, se você escrever uma função que funcione com um número limitado, ainda precisará fazer uma verificação, mas ela funcionará apenas em tempo de execução e aumentará a quantidade de código no corpo da função.

Agora estou um pouco mais esperto do que antes e aprendi a ouvir os argumentos. Imaginei-me escrevendo uma função protegida pelo meu tipo em um projeto real. Como todo mundo que usa, está me perguntando o que diabos é isso. Como, depois de atrapalhar o chip, eles começam a revestir a base de código com uma DSL personalizada e de aparência podre. Como verificamos trezentas vezes os valores que de fato nunca excederão o permitido. A abordagem é realmente terrível de usar, alguns problemas podem ser resolvidos ou suavizados, mas, por exemplo, nesse caso

consumeNumberLessThan100(90);

Não alise de forma alguma. Vou ter que provar ao compilador que 90 é menor que 100. Fornecerei qualquer atividade e isso resultará

consumeNumberLessThan100(assert(90, '<', 100)); 

Não parece muito legal. Todos os argumentos contra estão à mão, mas eles não contradizem os argumentos a favor. Acontece um dilema - facilidade de desenvolvimento ou confiabilidade. Aqui caímos na armadilha, começamos a pensar que precisamos calcular que tipo de conveniência existe e que tipo de confiabilidade existe. Mas conveniência e confiabilidade no desenvolvimento são coisas muito, muito complexas. Eles consistem em milhares de parâmetros.

Por exemplo, conveniência é quando o IDE compila o código para você em um par de caracteres digitados, quando o código é fácil de ler, quando você pode alterar a funcionalidade do método sem consultar o documento. Quando o compilador não é carregado com análise estática, a compilação é rápida, o editor de texto renderiza caracteres instantaneamente. Mas quando o compilador detecta e destaca o erro para você, isso também é uma conveniência. E também é confiabilidade. Que por sua vez é montado a partir de um grande número de coisas completamente diferentes.

Você precisa se sentar e calcular quanto tempo o projeto durará, em que direção seguirá, quantas vezes na história da humanidade alguém chamará um ou outro método de sua base de código, quais desenvolvedores trabalharão aqui. Esta é uma lista interminável de perguntas para a metade boa das quais é impossível calcular a resposta correta. Apenas adivinhe.



Uma vez, um amigo me pediu para preparar perguntas técnicas para uma entrevista com Andrei Breslav. Fui ao cais de Kotlin para encontrar pontos controversos no design. Eles estão lá, como em outros lugares, na escuridão. Mas o que mais me interessou foi a abordagem para lidar com exceções. Um manipulador de exceções no Kotlin é uma expressão, uma abordagem completamente funcional e confiável. Isso é apenas para lidar com erros não é necessário. O que reduz toda a confiabilidade a zero. E isso é interessante porque, logo no banco dos réus, os desenvolvedores não tiveram preguiça de explicar sua escolha: "existem estudos em que o tratamento obrigatório de erros reduz bastante a produtividade dos desenvolvedores com uma ligeira diminuição nos erros".

Eu sou louco, como você pode escrever um código se não tem idéia do que fazer quando não funciona corretamente? Se você não souber como lidar com a exceção, não a lide - não é uma solução para o problema, mas as prateleiras. Em algum momento, o código cairá e um problema que sempre existe causará danos que poderiam ter sido previstos.

Mas um argumento lógico contra não é necessário, você pode fazer apenas estatísticas. Para desenvolvedores do kotlin, a pesquisa quebra a lógica porque eles têm uma filosofia. A filosofia deles é o pragmatismo. Ferro, pragmatismo inquebrável, consistentemente incorporado a todos os recursos dessa linguagem de programação. Os idealistas que viram os Haskels / Idris e os governantes que escrevem o golang fazem exatamente o mesmo. A filosofia do compromisso razoável reina na base de código do F #. E não há sensação de que um deles esteja certo, e o resto seja tolo.

Todas essas pessoas são muito mais inteligentes e experientes do que pessoas como eu, e parecem ter entendido há muito tempo que você está desenvolvendo uma filosofia para si mesmo, e então simplesmente a segue. Porque a característica principal de qualquer filosofia é resolver os dilemas.

E assim a filosofia apareceu em tudo na TI, e a filosofia é exatamente o oposto da idéia de uma objetiva única e verdadeira, porque a filosofia se oferece para escolher a verdade subjetiva para si mesmo.

Cada um de nós tem sua própria filosofia - não é descrita em uma palavra, é um conjunto complexo de padrões para a tomada de decisões. E frequentemente os mudamos ou expandimos. Na prática, você tem um projeto com sua própria filosofia, que é escrito em uma linguagem de programação com sua própria filosofia, usando estruturas e ferramentas, cada uma das quais com sua própria filosofia. E os desenvolvedores escrevem esse projeto, cada um com sua própria filosofia. E toda vez que você precisa tomar algum tipo de decisão, apenas uma combinação de circunstâncias afeta qual delas será tomada. Não é gestão da complexidade, nem experiência, nem abordagem, nem conhecimento, mas apenas um acidente.

E todos esses projetos funcionam. A maior parte de todas as tarefas que os desenvolvedores resolvem é a eliminação das consequências de erros de outros desenvolvedores, mas os projetos funcionam e resolvem os problemas das pessoas. Pessoas inteligentes criam práticas e padrões arquiteturais. Linguagens de programação com poderoso controle de tipo e contratos - tudo, se apenas os desenvolvedores estivessem menos enganados. E todos os dias abro o Edge Board no trabalho e vejo que há mais erros do que tarefas. Onde está todo bug, esse é outro, gerenciando a complexidade, desenvolvedor de porcaria.

A filosofia de desenvolvimento é simplesmente a escolha da maneira como você coloca as mãos na final. Mas se não houver filosofia, mas apenas lógica objetiva pura, qualquer argumento se resumirá ao problema do criador onipotente e da pedra insuportável.



Estou em desenvolvimento há muito tempo e aprendi a fazer o que eles dizem, mesmo quando discordo fundamentalmente, e comecei a escrever confirmações super detalhadas. Meu devlid é um vampiro de verdade. Requer não apenas uma descrição - ele quer que eu escreva o que cada arquivo faz, por que faz, como faz. Qual problema o commit confirma?

Adicionei oito confirmações extra-detalhadas, a mesma descrição detalhada do pool de solicitações - e gostei de kapets. Desde então, quase não trabalhamos nesse projeto em particular, mas de tempos em tempos eu vou lá para admirar esses compromissos. Sério, muito, muito legal. E em todos os outros projetos, exceto os pessoais, agora aplico essa abordagem.

Foi muito difícil reconstruir o fluxo de trabalho. Um mês e meio se passou, e ainda é difícil escrever código como este. Mas parece que vale a pena.

Ou não. Sinceramente, não sei. E não sei se, se definitivamente vale a pena, isso me faz dois meses de idade um completo idiota. Cerca de dez pessoas andam por algum lugar do mundo que aprendi a não fazer tais compromissos. Eles são idiotas? Eu penso que não.



Meu podcast

All Articles