Zen go



Avaliando meu trabalho, recentemente pensei muito em como escrever um bom código. Dado que ninguém está interessado em escrever código incorreto , surge a pergunta: como você sabe se escreveu código bom no Go ? Se existe algum tipo de escala entre bom e ruim, como entender quais partes da escala pertencem ao bem? Quais são suas propriedades, atributos, características distintivas, padrões e expressões idiomáticas?

Idiomatic go


Essas considerações me levaram ao Go idiomático. Se chamamos algo de “idiomático”, esse algo corresponde a um certo estilo de algum tempo. Se algo não é idiomático, não corresponde ao estilo dominante. Isso não está na moda.

Mais importante, quando dizemos que o código de alguém não é idiomático, isso não explica o motivo. Por que não idiomático? A resposta é dada pelo dicionário.

Expressão idiomática (n.): Uma revolução da fala, usada como um todo, não sujeita a decomposição adicional e geralmente não permitindo permutações dentro de si.

Expressões idiomáticas são características de significados comuns. Os livros não ensinam o Go idiomático; só é conhecido quando você se torna parte de uma comunidade.

Estou preocupado com o mantra idiomático do Go, porque geralmente é restritivo. Ela diz: "você não pode se sentar conosco". Não é isso que queremos dizer quando criticamos o trabalho de outra pessoa como "não idiomático"? Eles fizeram errado. Isso não parece certo. Isso não está de acordo com o estilo da época.

Acredito que o Go idiomático não é adequado para ensinar como escrever um bom código, porque, em essência, significa dizer às pessoas que elas fizeram algo errado. É melhor dar um conselho que não afaste uma pessoa no momento em que ela mais deseja receber esse conselho.

Provérbios


Vamos nos distrair dos problemas idiomáticos. Quais outros artefatos culturais são inerentes aos programadores do Go? Vá para a bela página de Provérbios Go . Esses ditados são uma ferramenta de aprendizado adequada? Eles dizem aos iniciantes como escrever um bom código Go?

Acho que não. Não quero menosprezar o trabalho do autor. Os ditos que ele compôs são meramente observações, não definições de significados. O dicionário vem em socorro novamente:

Provérbio (n.): Uma declaração curta que tem significado literal ou figurativo.

A missão de Go Proverbs é mostrar a essência profunda da arquitetura da linguagem. Mas será útil aconselhar como "A interface vazia não diz nada " para um iniciante que veio de um idioma sem digitação estrutural?

Em uma comunidade em crescimento, é importante reconhecer que o número de estudantes da Go é muito maior do que o número de pessoas fluentes nesse idioma. Ou seja, provérbios provavelmente não são a melhor maneira de aprender em tal situação.

Valores de Design


Dan Liu encontrou uma apresentação antiga de Mark Lukowski sobre a cultura de design na equipe de desenvolvimento do Windows NT-Windows 2000. Mencionei isso porque Lukowski descreve a cultura como uma maneira comum de avaliar arquiteturas e fazer compromissos.


A idéia principal é tomar decisões baseadas em valor dentro de uma arquitetura desconhecida . A equipe do NT tinha esses valores: portabilidade, confiabilidade, segurança e extensibilidade. Simplificando, os valores do projeto são uma maneira de resolver problemas.

Go Values


Quais são os valores explícitos do Go? Quais são os principais conceitos ou filosofias que determinam como os programadores do Go interpretam o mundo? Como eles são proclamados? Como eles são ensinados? Como eles são seguidos? Como eles mudam com o tempo?

Como você converte um programador Go, obtém os valores do design Go? Ou como você, um Go-pro experiente, proclama seus valores para as gerações futuras? E para que você entenda, esse processo de transferência de conhecimento não é opcional? Sem o influxo de novos participantes e novas idéias, nossa comunidade se torna míope e murcha.

Valores de outras línguas


Para preparar o caminho para o que quero dizer, podemos prestar atenção a outras linguagens, a seus valores de design.

Por exemplo, em C ++ e Rust, acredita-se que um programador não deve pagar por um recurso que ele não usa . Se o programa não usar algum recurso que consome muitos recursos da linguagem, não poderá ser forçado a arcar com o custo de manutenção desse recurso. Esse valor é projetado da linguagem para a biblioteca padrão e é usado como critério para avaliar a arquitetura de todos os programas escritos em C ++.

Valor principal em Java, Ruby e Smalltalk - tudo é um objeto. Esse princípio é subjacente ao design do programa em termos de transferência de mensagens, ocultação de informações e polimorfismo. As arquiteturas que estão em conformidade com um paradigma processual ou funcional são consideradas errôneas nessas linguagens. Ou, como diria um programador do Go, não idiomático.

Vamos voltar à nossa comunidade. Quais valores de design os programadores Go professam? As discussões sobre esse tópico geralmente são fragmentadas, portanto, não é fácil formular um conjunto de significados. É imperativo chegar a um acordo, mas a dificuldade de alcançá-lo cresce exponencialmente com o crescente número de participantes na discussão. Mas e se alguém fizesse esse trabalho difícil para nós?

Zen Python Go


Algumas décadas atrás, Tim Peters sentou-se e escreveu PEP-20 - O Zen de Python . Ele tentou documentar os valores de design aos quais Guido Van Rossum seguia como o ditador generoso do Python para a vida.

Vamos dar uma olhada no The Zen of Python e ver se podemos aprender algo sobre os valores de design do designer Go.

Um bom pacote começa com um bom nome


Vamos começar com o afiado:

Os namespaces são uma ótima idéia, vamos aumentá-los!

O Zen de Python, registro 19.

Sem ambiguidade: os programadores de Python devem usar espaços de nome. Muitos espaços.

Na terminologia Go, um espaço para nome é um pacote. Não há dúvida de que a agregação favorece o design e a reutilização. Mas pode haver confusão sobre como fazer isso, especialmente se você tiver muitos anos de experiência em programação em outra linguagem.

No Go, todos os pacotes devem ser projetados para algo. E o nome é a melhor maneira de entender esse destino. Reformulando os pensamentos de Peteres, cada pacote no Go deve ser projetado para uma coisa.

A ideia não é nova, eu já falei sobre isso . Mas por que essa abordagem deve ser usada, e não outra, em que pacotes são usados ​​para as necessidades de uma classificação detalhada? É tudo sobre as mudanças.

— , .


Mudança é o nome do jogo em que estamos participando. Nós, como programadores, gerenciamos as mudanças. Se fizermos isso bem, chamamos de arquitetura. E se estiver ruim, chamamos de dívida técnica ou código legado.

Se você escrever um programa que funcione perfeitamente uma vez com um conjunto fixo de dados de entrada, ninguém ficará interessado em saber se possui um bom código, porque apenas o resultado do trabalho é importante para os negócios.

Mas isso não acontece . Existem erros nos programas, requisitos e alteração dos dados de entrada, e muito poucos programas são gravados com uma única expectativa de execução. Ou seja, o programa irá mudar com o tempo. Talvez essa tarefa seja dada a você, mas provavelmente alguém o fará. Alguém precisa acompanhar esse código.

Como facilitamos a troca de programas? Adicionar interfaces em todos os lugares? Faça tudo adequado para criar stubs? Implantar dependências firmemente? Talvez, para alguns tipos de programas, essas técnicas sejam adequadas, mas não para muitos. No entanto, para a maioria dos programas, criar uma arquitetura flexível é mais do que design.

E se, em vez de expandir os componentes, os substituiremos? Se o componente não fizer o que está especificado nas instruções, é hora de alterá-lo.

Um bom pacote começa com a escolha de um bom nome. Considere uma breve apresentação que descreve a função de um pacote com apenas uma palavra. E quando o nome não atender mais ao requisito, encontre uma substituição.

A simplicidade é importante


Simples é melhor que complexo.

O Zen de Python, entrada 3.

O PEP-20 afirma que o simples é melhor que o complexo, e eu concordo completamente. Alguns anos atrás eu escrevi:


A maioria das linguagens de programação tenta ser simples no começo, mas depois decide ser poderosa.

De acordo com minhas observações, pelo menos naquela época, não conseguia me lembrar de uma linguagem que sabia que não seria considerada simples. Como justificativa e tentação, os autores de cada nova linguagem declararam simplicidade. Mas descobri que a simplicidade não era o valor principal de muitas linguagens da mesma idade que Go (Ruby, Swift, Elm, Go, NodeJS, Python, Rust). Talvez isso chegue a um ponto crítico, mas talvez o motivo seja que nenhuma dessas línguas seja simples. Ou seus autores não os consideraram simples. A simplicidade não foi incluída na lista de valores fundamentais.

Você pode me considerar antiquado, mas quando essa simplicidade saiu de moda? Por que a indústria de software comercial esquece constantemente e com alegria essa verdade fundamental?

Há duas maneiras de criar uma arquitetura de software: simplificar de maneira que a falta de falhas seja óbvia e torná-la tão complexa que não possui falhas óbvias. O primeiro método é muito mais difícil.

Charles Hoar, Roupas Antigas do Imperador, Palestra do Prêmio Turing, 1980

Simples não significa fácil, sabemos disso. Muitas vezes, é preciso mais esforço para garantir a facilidade de uso, em vez da facilidade de criação.

Simplicidade é a chave para a confiabilidade.

Edsger Dijkstra, EWD498, 18 de junho de 1975

Por que lutar pela simplicidade? Por que é importante que os programas Go sejam simples? Simples significa bruto, legível e fácil de seguir. Simples não significa sem arte, significa confiável, inteligível e compreensível.

O núcleo da programação é o gerenciamento de complexidade.

Brian Kernigan, Ferramentas de software (1976)

Se o Python segue seu mantra de simplicidade é uma questão discutível. At Go, no entanto, a simplicidade é um valor essencial. Acho que todos concordamos que no Go o código simples é preferível ao código inteligente.

Evite estados no nível do pacote


Explícito é melhor que implícito.

O Zen de Python, entrada 2

Aqui, Peters, na minha opinião, prefere sonhos do que aderir aos fatos. Em Python, muito não é explícito: decoradores, métodos de dunder, etc. Sem dúvida, essas são ferramentas poderosas e existem por uma razão. Na implementação de cada recurso, especialmente complexo, alguém trabalhou. Mas o uso ativo desses recursos dificulta a avaliação do custo da operação ao ler o código.

Felizmente, programadores do Go Go podem opcionalmente tornar o código explícito. Talvez, para você, manifestação possa ser sinônimo de burocracia e verbosidade, mas essa é uma interpretação superficial. Será um erro focar apenas na sintaxe, cuidar do comprimento das linhas e da aplicação dos princípios DRY às expressões. Parece-me mais importante fornecer explicações em termos de conexão e estados.

Conectividade é uma medida da dependência de um em outro. Se um está intimamente relacionado ao outro, ambos se movem juntos. Uma ação que afeta um é refletida diretamente no outro. Imagine um trem no qual todos os carros estão conectados - ou melhor, conectados - juntos. Onde o trem a vapor vai, há os carros.

A conectividade também pode ser descrita pelo termo coesão - coesão. Esta é uma medida de quanto um pertence ao outro. Em uma equipe soldada, todos os participantes são tão adequados um ao outro, como se tivessem sido criados especialmente dessa maneira.

Por que a coerência é importante? Como no caso do trem, quando você precisa alterar um trecho de código, é necessário alterar o restante do código intimamente relacionado. Por exemplo, alguém lançou uma nova versão de sua API e agora seu código não é compilado.

Uma API é uma fonte inevitável de ligação. Mas pode ser apresentado de formas mais insidiosas. Todo mundo sabe que se a assinatura da API foi alterada, os dados transferidos de e para a API também serão alterados. É tudo sobre a assinatura da função: pego os valores de um tipo e retorno os valores de outros tipos. E se a API começar a transferir dados de uma maneira diferente? E se o resultado de cada chamada da API depender da chamada anterior, mesmo que você não tenha alterado suas configurações?

Isso é chamado de estado, e o gerenciamento de estado é um problema na ciência da computação.

package counter

var count int

func Increment(n int) int {
        count += n
        return count
}

Aqui temos um pacote simples counter. Para alterar o contador, você pode ligar Increment, você pode até recuperar o valor se incrementar com um valor zero.

Digamos que você precise testar esse código. Como redefinir o contador após cada teste? E se você deseja executar testes em paralelo, como isso pode ser feito? E suponha que você queira usar vários contadores no programa, terá sucesso?

Claro que não. Obviamente, a solução é encapsular a variável variableno tipo

package counter

type Counter struct {
        count int
}

func (c *Counter) Increment(n int) int {
        c.count += n
        return c.count
}

Agora imagine que o problema descrito não se limita aos contadores, mas também afeta a principal lógica de negócios de seus aplicativos. Você pode testá-lo isoladamente? Você pode testar em paralelo? Você pode usar várias instâncias ao mesmo tempo? Se a resposta for não para todas as perguntas, o motivo é o estado no nível do pacote.

Evite essas condições. Reduza a conectividade e o número de ações remotas de pesadelo, fornecendo aos tipos as dependências necessárias como campos, em vez de usar variáveis ​​de pacote.

Faça planos para o fracasso, não para o sucesso


Nunca passe erros silenciosamente.

O Zen de Python, entrada 10

Isso é dito sobre idiomas que incentivam o tratamento de exceções no estilo samurai: volte com uma vitória ou nunca mais volte. Nos idiomas baseados em exceções, as funções retornam apenas resultados válidos. Se a função não puder fazer isso, o fluxo de controle será de uma maneira completamente diferente.

Obviamente, exceções não verificadas são um modelo de programação inseguro. Como você pode escrever um código confiável na presença de erros, se você não sabe quais expressões podem gerar uma exceção? Java tenta reduzir riscos com o conceito de exceções verificadas. E até onde eu sei, em outras línguas populares, não há análogos dessa solução. Existem exceções em muitas linguagens e, em todos os lugares, exceto Java, elas não são verificadas.

Obviamente, Go seguiu um caminho diferente. Os programadores da Go acreditam que os programas confiáveis ​​são compostos de peças que lidam com falhas antes de processar caminhos bem-sucedidos. Dado que o idioma foi criado para o desenvolvimento do servidor, a criação de programas multithread, bem como programas que processam a entrada de dados na rede, os programadores devem se concentrar em trabalhar com dados inesperados e danificados, tempos limite e falhas de conexão. Claro, se eles querem fazer produtos confiáveis.

Acredito que os erros devem ser tratados explicitamente, esse deve ser o principal valor da linguagem.

Peter Burgon, GoTime # 91

Junto às palavras de Pedro, elas serviram de impulso à redação deste artigo. Acredito que o Go deve seu sucesso ao tratamento explícito de erros. Os programadores pensam principalmente em possíveis falhas. Primeiro, resolvemos problemas como "e se". O resultado são programas nos quais as falhas são tratadas no estágio de escrita do código, e não como ocorrem durante a operação.

A verbosidade deste código

if err != nil {
    return err
}

Supera a importância de lidar deliberadamente com cada estado com falha no momento em que ocorre. A chave para isso é o valor de lidar explicitamente com cada erro.

Melhor voltar cedo do que investir profundamente


Irmão é melhor do que aninhar

The Zen of Python, entrada 5

Esse conselho sábio vem de uma linguagem na qual o recuo é a principal forma de fluxo de controle. Como interpretamos esta dica na terminologia Go? O gofmt gerencia toda a quantidade de espaço vazio nos programas Go, portanto não temos nada a fazer aqui.

Eu escrevi acima sobre nomes de pacotes. Talvez seja aconselhável evitar uma hierarquia complexa de pacotes. Na minha experiência, quanto mais um programador tenta separar e classificar uma base de código no Go, maior o risco de importação cíclica de pacotes.

Acredito que o melhor uso da quinta entrada de The Zen of Python é criar um fluxo de controle dentro de uma função. Em outras palavras, evite um fluxo de controle que exija recuo de vários níveis.

A visibilidade direta é uma linha reta ao longo da qual a vista não é obscurecida por nada.

May Ryer, Code: Alinhe o caminho feliz à borda esquerda

May Ryer descreve essa idéia como programação em linha de visão direta:

  • Use as instruções de controle para retornar cedo se a pré-condição não for atendida.
  • Colocar a declaração de retorno bem-sucedido no final da função e não dentro do bloco condicional.
  • Reduza o nível geral de aninhamento extraindo funções e métodos.

Tente garantir que funções importantes nunca saiam da linha de visão para a borda direita da tela. Esse princípio tem um efeito colateral: você evitará disputas sem sentido com a equipe sobre o comprimento das linhas.

Cada vez que você recua, você adiciona mais uma pré-condição aos chefes dos programadores, ocupando um dos seus 7 ± 2 slots de memória de curto prazo. Em vez de aprofundar o aninhamento, tente manter o caminho bem-sucedido da função o mais próximo possível do lado esquerdo da tela.

Se você acha que algo está correndo devagar, prove com uma referência


Desista da tentação de adivinhar diante da ambiguidade.

O Zen do Python 12

A programação é baseada em matemática e lógica. Esses dois conceitos raramente usam o elemento da sorte. Mas nós, como programadores, fazemos inúmeras suposições todos os dias. O que essa variável faz? O que essa opção faz? O que acontece se eu não passar por aqui? O que acontece se eu ligar para o registro duas vezes? Na programação moderna, você precisa assumir muito, especialmente ao usar as bibliotecas de outras pessoas.

A API deve ser fácil de usar e difícil de usar incorretamente.

Josh Bloch

Uma das melhores maneiras que conheço para ajudar um programador a não adivinhar ao criar uma API é se concentrar nos métodos de uso padrão . O chamador deve poder executar operações normais da maneira mais fácil possível. No entanto, antes de escrever muito e falar sobre o design da API, aqui está minha interpretação do registro 12: não adivinhe o tópico de desempenho .

Apesar da sua atitude em relação aos conselhos de Knut, uma das razões do sucesso da Go é a eficácia de sua execução. Programas eficazes podem ser escritos neste idioma e, graças a isso, as pessoas irãoescolha ir. Existem muitos conceitos errados relacionados ao desempenho. Portanto, quando você estiver procurando maneiras de melhorar o desempenho do código ou seguir dicas dogmáticas, como "deixar as prateleiras mais lentas", "CGO é caro" ou "sempre use operações atômicas em vez de mutexes", não adivinhe.

Não complique seu código devido a dogmas desatualizados. E se você acha que algo está funcionando lentamente, primeiro certifique-se disso com a ajuda de uma referência. O Go possui ótimas ferramentas gratuitas de benchmarking e criação de perfil. Use-os para encontrar gargalos no desempenho do seu código.

Antes de iniciar o gorutin, descubra quando ele irá parar


Acho que listei os itens valiosos do PEP-20 e talvez tenha expandido sua interpretação além do bom gosto. Isso é bom, porque, embora este seja um dispositivo retórico útil, ainda estamos falando de dois idiomas diferentes.

Escreva g, o, um espaço e, em seguida, uma chamada de função. Três pressionamentos de botão, não podem ser mais curtos. Três cliques no botão e você iniciou o subprocesso.

Rob Pike, Simplicidade é Complicada , dotGo 2015

As próximas duas dicas que eu dedico às goroutines. Gorutins são uma característica da linguagem, nossa resposta à competitividade de alto nível. Eles são muito fáceis de usar: coloque uma palavra gona frente do operador e você executa a função de forma assíncrona. Sem encadeamentos de execução, executores de conjunto, sem IDs, sem rastreamento de status de conclusão.

Gorutins são baratos. Devido à capacidade do ambiente de tempo de execução de multiplexar goroutines em um pequeno número de threads de execução (que você não precisa gerenciar), é possível criar facilmente centenas de milhares ou milhões de goroutines. Isso permite criar arquiteturas que seriam impraticáveis ​​ao usar outros modelos competitivos, na forma de encadeamentos de execução ou retornos de chamada de evento.

Mas por mais baratas que sejam as goroutinas, elas não são gratuitas. A pilha deles leva pelo menos alguns kilobytes. E quando você tem milhões de goroutines, isso se torna perceptível. Não quero dizer que você não precisa usar milhões de goroutines, se a arquitetura o levar a isso. Mas se você usá-lo, é extremamente importante monitorá-los, uma vez que em tais quantidades as goroutines podem consumir muitos recursos.

Goroutines são a principal fonte de propriedade do Go. Para ser útil, a goroutine deve fazer alguma coisa. Ou seja, quase sempre ele contém um link para um recurso, ou seja, informações de propriedade: bloqueio, conexão de rede, buffer de dados enviando o final do canal. Enquanto a goroutine permanece, o bloqueio é mantido, a conexão permanece aberta, o buffer é salvo e os destinatários do canal aguardam novos dados.

A maneira mais simples de liberar recursos é vinculá-los ao ciclo de vida das goroutinas. Quando concluído, os recursos são liberados. E como é muito fácil executar a goroutine, antes de escrever "vá e espaço", verifique se você tem respostas para estas perguntas:

  • Sob que condição a goroutine para? Go não pode dizer para a goroutine terminar. Por um motivo específico, não há função para parar ou interromper. Não podemos ordenar que as goroutines parem, mas podemos pedir educadamente. Isso quase sempre está relacionado à operação do canal. Quando fechado, o alcance é feito em loop para sair do canal. Ao fechar o canal, você pode selecioná-lo. O sinal de uma goroutine para outra é melhor expresso como um canal fechado.
  • ? , , : ?
  • , ? , - . , . . , .


Provavelmente em qualquer um dos seus programas sérios do Go, a simultaneidade é usada. Isso geralmente leva ao problema de um padrão de trabalho - uma goroutine por conexão.

Um exemplo principal é net / http. É muito simples parar o servidor que possui o soquete de escuta, mas e as goroutines geradas por esse soquete? O net / http fornece um objeto de contexto dentro do objeto de solicitação que pode ser usado para informar ao ouvinte que a solicitação precisa ser cancelada e, portanto, interromper a goroutine. Mas não está claro como descobrir quando tudo isso precisa ser feito. Uma coisa é chamar context.Cancel, outra é saber que o cancelamento está concluído.

Costumo encontrar falhas no net / http, mas não porque é ruim. Pelo contrário, é a API mais bem-sucedida, mais antiga e mais popular da base de código Go. Portanto, sua arquitetura, evolução e falhas são cuidadosamente analisadas. Considere esta lisonja, não crítica.

Então, quero trazer net / http como um contra-exemplo de boas práticas. Como cada conexão é processada pela goroutin criada dentro do tipo net/http.Server, o programa fora do pacote net / http não pode controlar as goroutins criadas pelo soquete receptor.

Esta área da arquitetura ainda está em desenvolvimento. Você pode recuperar run.Groupo go-kit, ou ErrGroup, da equipe de desenvolvimento Go, que fornece uma estrutura para executar, cancelar e aguardar funções executadas de forma assíncrona.

Para quem escreve código que pode ser executado de forma assíncrona, o princípio principal da criação de arquiteturas é que a responsabilidade pela execução de goroutines deve ser transferida para o chamador. Deixe-o escolher como ele deseja executar, acompanhar e aguardar a conclusão de suas funções.

Escreva testes para bloquear o comportamento da API do seu pacote


Você pode ter esperado que, neste artigo, não mencionarei testes. Desculpe, outra hora.

Seus testes são um acordo sobre o que seu programa faz e o que não faz. Os testes de unidade devem bloquear o comportamento de suas APIs no nível do pacote. Os testes descrevem em forma de código o que o pacote promete fazer. Se houver um teste de unidade para cada conversão de entrada, você, na forma de código , e não na documentação, definiu um acordo sobre o que o código fará.

A aprovação deste contrato é tão simples quanto escrever um teste. A qualquer momento, você pode afirmar com um alto grau de confiança que o comportamento em que as pessoas se baseavam antes das alterações feitas continuará a funcionar após as alterações.

Os testes bloqueiam o comportamento da API. Quaisquer alterações que adicionem, alterem ou removem a API pública devem incluir alterações nos testes.

Moderação é uma virtude


Go é uma linguagem simples, com apenas 25 palavras-chave. De certa forma, isso destaca os recursos incorporados ao idioma. Esses são os recursos que permitem que a linguagem se promova: competição simples, tipagem estrutural etc.

Acho que todos nós estamos confusos ao tentar usar todos os recursos do Go de uma só vez. Quantos de vocês foram tão inspirados pelo uso de canais que os usaram sempre que podem? Eu descobri que os programas resultantes são difíceis de testar, são frágeis e muito complexos. E você?

Eu tive a mesma experiência com goroutines. Tentando dividir o trabalho em pequenos fragmentos, criei a escuridão da goroutina, que era difícil de controlar, e perdi completamente de vista o fato de que a maioria deles sempre estava bloqueada devido à expectativa de seus antecessores em concluir o trabalho. O código era completamente consistente e eu tive que aumentar bastante a complexidade para obter uma pequena vantagem. Quantos de vocês já encontraram isso?

Eu tive o mesmo com a incorporação. No começo eu confundi com herança. Então, ele se deparou com o problema de uma classe base frágil, combinando vários tipos complexos que já tinham várias tarefas em tipos enormes ainda mais complexos.

Este pode ser o conselho menos eficaz, mas considero importante mencioná-lo. O conselho é o mesmo: mantenha a moderação e os recursos do Go não são excepção. Sempre que possível, não use goroutines, canais, estruturas de incorporação, funções anônimas, uma abundância de pacotes e interfaces. Use soluções mais simples que as inteligentes.

Facilidade de manutenção


Por fim, darei outra entrada do PEP-20:

Legibilidade é importante.

O Zen de Python, registro 7

Muito foi dito sobre a importância da legibilidade do código em todas as linguagens de programação. Quem promove o Go usa palavras como simplicidade, legibilidade, clareza, produtividade. Mas todos estes são sinônimos de um conceito - conveniência de manutenção.

O objetivo real é criar um código fácil de manter. O código que sobrevive ao autor. Um código que pode existir não apenas como investimento de tempo, mas como base para a obtenção de valor futuro. Isso não significa que a legibilidade não seja importante, apenas a conveniência da manutenção é mais importante .

O Go não é um desses idiomas otimizados para programas de linha única. E não um daqueles idiomas otimizados para programas com um número mínimo de linhas. Não otimizamos o tamanho do código-fonte no disco ou a velocidade de gravação de programas no editor. Queremos otimizar nosso código para que ele se torne mais compreensível para os leitores. Porque são eles que terão que acompanhá-lo.

Se você escreve um programa para si mesmo, talvez ele seja iniciado apenas uma vez ou você é o único que vê seu código. Nesse caso, faça qualquer coisa. Porém, se mais de uma pessoa trabalhar no código ou se ele for usado por um longo período de tempo e os requisitos, recursos ou tempo de execução puderem mudar, o programa deve ser conveniente de manter. Se o software não puder ser mantido, não poderá ser reescrito. E essa pode ser a última vez que sua empresa investe no Go.

O que você trabalha duro será conveniente para acompanhar após a sua partida? Como você pode facilitar a manutenção do seu código para aqueles que o buscam hoje?

All Articles