Ir para o conteúdo

Blogue ·

Documentação escrita antes da fusão

Duas frases de uma nova página de documentação eram falsas no dia em que saiu, porque outro ramo entrou antes. O que uma guarda apanha e o que só um grep vê.

Por Provibr

Uma página de documentação é um conjunto de afirmações sobre código. Parece evidente até reparar que nada na compilação verifica nenhuma delas. Os testes afirmam coisas sobre código. As verificações de tipos afirmam coisas sobre código. A prosa sobre código afirma coisas sobre código e é verificada por uma pessoa que a lê uma vez, no dia em que foi escrita.

Escrevemos duas novas páginas de documentação numa ronda: uma para o editor de automação, cerca de 2930 palavras inglesas repartidas por oito secções, e outra para o painel, cerca de 909. Mais uma secção acrescentada a duas páginas que já existiam. Duas frases da página de automação já eram falsas quando foi publicada.

Ninguém tinha escrito uma falsidade. Ambas as frases estavam certas em relação ao ramo em que foram escritas.

A frase que era verdadeira quando foi escrita

A documentação de uma funcionalidade escreve-se normalmente depois da funcionalidade, o que quer dizer que se escreve enquanto outros ramos ainda estão abertos. A nossa foi. A página descrevia um nó de notificação e dizia, no essencial, que não envia correio eletrónico. No ramo contra o qual foi escrita, isso estava exatamente certo: o nó escrevia uma notificação no painel e nada saía do edifício.

Uma ronda paralela foi integrada primeiro e deu ao nó uma caixa de seleção para correio eletrónico, desligada por omissão, uma categoria de cancelamento de subscrição própria e uma regra que impede uma execução de teste de enviar seja o que for. Cada um destes factos interessa a quem lê. Nenhum deles estava na página, e o que estava na página passou a dizer o contrário.

A segunda frase descrevia como um plano se liga a um fluxo. Dizia que o plano executa o fluxo que está por trás dele, de uma altura em que essa relação era de um para um. A mesma integração substituiu-a por uma junção: um plano pode pendurar no máximo um fluxo por momento, de entre três momentos, e um fluxo despoletado manualmente ou por webhook não pode pendurar-se ali de todo. «Não pode ser ligado» é uma resposta melhor do que «ligado e sem fazer nada em silêncio», mas é uma resposta diferente, e a página continuava a dar a antiga.

A reparação foi pequena. O interessante é como foi encontrada, ou seja, alguém foi procurar de propósito, porque uma ronda que toca num assunto é o único momento em que alguém tem uma razão para reler a prosa sobre esse assunto.

O que uma guarda consegue apanhar

Há um segundo modo de falha nas mesmas páginas e, ao contrário do primeiro, esse é mecanizável.

A página do painel tem uma tabela que lista os cartões que um utilizador pode acrescentar. Esses cartões têm nomes, em nove línguas, e esses nomes já existem: são as etiquetas do seletor em que o utilizador clica de facto. Escrever uma tabela de documentação significa escrevê-los uma segunda vez.

Correu mal logo à primeira, no primeiro rascunho, em inglês:

No seletorNa página de documentação
Integration listConnections
Open ticketsTickets
Credit balanceCredit

Nenhum deles está errado como inglês. Os três estão errados como documentação, porque quem é mandado acrescentar «Connections» vai procurar um cartão chamado Connections e encontra um cartão chamado Integration list. O desvio é silencioso por construção: ambos os textos estão bem por si sós, e ninguém põe um manual ao lado de um seletor.

A tabela passa portanto a usar as mesmas chaves de catálogo que o seletor usa, e um teste exige que sejam idênticas palavra por palavra nas nove línguas. A chave de documentação é derivada do identificador do cartão em vez de ser listada à mão, para que o sétimo cartão opcional que alguém acrescente no próximo ano seja medido sem que ninguém se lembre de o acrescentar aqui. Esta é a mesma forma da guarda que corremos entre as duas cópias das mensagens de servidor em inglês, e existe pela mesma razão: dois sítios que guardam uma verdade precisam de um teste que o diga, não de uma convenção.

Os nomes dos botões na mesma página tinham sido adivinhados em cinco das nove línguas. Passam também a sair do catálogo. E três línguas tinham traduzido a palavra Scripts, que é o nome de um item de menu que não se traduz no produto, pelo que a página mandava um leitor espanhol, português e ucraniano clicar em algo que não existe com esse nome.

Um símbolo técnico não é uma palavra

Nomes de produto, portas, caminhos, nomes de módulo e códigos de estado vão num símbolo monoespaçado e ficam em inglês, onde quer que apareçam, em todas as línguas. Essa regra não é nova. Novo foi que escrevê-la nestas páginas colidiu com outras duas guardas ao mesmo tempo.

Pôr um elemento <code> literalmente dentro de um vetor de linhas de tabela faz disparar a regra de lint que quer uma chave em cada elemento JSX de um vetor, mesmo aqui, onde o componente de tabela envolve cada célula no seu próprio elemento com chave e a chave nunca é lida. Pôr-lhe uma chave faz disparar outra guarda, que vê um filho com chave ao lado de chamadas de tradução cujas chaves não consegue resolver nesse ficheiro.

A saída foi uma função de uma linha que devolve o elemento. Para a regra de lint é uma chamada de função e não um elemento de vetor; para a outra guarda não é uma chave. A saída desenhada é idêntica byte a byte. É uma coisa pequena, e é o tipo de coisa pequena que se resolve com um comentário de desativação quando ninguém escreve porque é que as duas regras discordaram.

Capturas de ecrã que pareciam bem-sucedidas

A medição destas páginas incluía capturas de ecrã por âncora: rolar até cada secção, fotografá-la, olhar para ela.

Nove delas mostravam o cabeçalho da página.

O comando do navegador que captura uma região para lá da janela de visualização calcula o recorte a partir do topo do documento e não da posição de rolagem atual. Role até uma âncora oito ecrãs abaixo, peça uma região nas coordenadas que o elemento indica, e fotografa antes o topo da página, exatamente com o tamanho certo, num ficheiro com o nome certo. Cada uma dessas capturas era uma imagem plausível da coisa errada.

Somar o desvio de rolagem atual resolveu. A forma geral é uma com que continuamos a esbarrar: uma medição que falha produzindo saída de aspeto normal é pior do que uma que estoira, porque um estoiro é investigado no próprio dia.

A parte que nenhuma guarda alcança

Os nomes dos cartões eram mecanizáveis porque ambos os lados são cadeias de texto num só repositório. As duas frases desatualizadas não eram, e vale a pena ser preciso quanto ao porquê, porque é tentador acreditar que todo o problema de documentação tem um teste à espera de ser escrito.

Uma frase que diz que um nó não envia correio eletrónico é uma afirmação sobre comportamento espalhado por quatro ficheiros, um valor por omissão, uma caixa de seleção e uma lista de categorias. Não há nenhuma expressão com que a possa comparar. O único mecanismo que a apanharia é um teste que desenha o comportamento e lê a frase, o que é um teste que tem de ser escrito frase a frase, por alguém que já sabe que a frase está errada.

O que fizemos em vez disso foi mais barato e merece ser nomeado como prática: quando a correção foi feita, procurou-se em toda a subárvore de documentação qualquer outra afirmação sobre correio eletrónico e qualquer outra afirmação sobre um fluxo por plano. Não havia um terceiro sítio. Isso é um resultado, não uma formalidade. Sem ele, a ronda teria corrigido duas frases e deixado o leitor com a crença razoável de que a página tinha sido verificada.

A mesma disciplina já existe noutro ponto deste projeto para um par diferente: os nossos artigos longos podem desatualizar-se em relação à lista de integrações em produção, e nada fica vermelho quando isso acontece. Dois artigos passaram uma vez seis semanas a dizer que dois hipervisores estavam no plano futuro quando ambos já estavam em produção. A regra que saiu daí é a mesma: se uma ronda muda o estado de uma coisa, procure o nome dela na prosa com um grep.

O que registaríamos

  1. A prosa sobre código é uma afirmação por testar. Nada na compilação a lê. Isso está bem, desde que toda a gente o saiba e ninguém suponha que uma execução verde diz seja o que for sobre o parágrafo.
  2. A documentação escreve-se contra um ramo, não contra o main. Se houver outras rondas abertas, a página é um instantâneo de um estado que pode nunca chegar a sair. Releia-a depois da integração, não antes.
  3. Um nome que existe no produto deve ser lido, não redigitado. Três de seis nomes de cartão desviaram-se logo no primeiro rascunho. Em nove línguas isso é uma classe de erro, não um acaso.
  4. Derive a chave de documentação do identificador. Assim o cartão seguinte fica coberto sem que ninguém se lembre desta regra.
  5. Uma palavra adivinhada numa língua que não lê é invisível. Cinco línguas tinham nomes de botão adivinhados e nada estava vermelho.
  6. Traduzir o nome de um produto é uma instrução partida. Se o menu diz Scripts em todas as línguas, o manual diz Scripts em todas as línguas.
  7. Quando duas regras de lint discordam, registe a qual obedeceu e porquê. A alternativa é um comentário de desativação que ninguém consegue avaliar mais tarde.
  8. Uma medição que produz saída plausível quando falha não será questionada. Fotografe algo que consiga reconhecer, e olhe para as imagens.

Para que fique claro o que isto é: ambas as páginas estão em produção nas nove línguas, as duas frases desatualizadas foram corrigidas nas nove, e a guarda dos nomes de cartão está na bateria e foi provada vermelha antes de ser provada verde. As afirmações das páginas vêm de ler o código e não de o executar; o exemplo com curl na página de automação não foi disparado contra um ponto de acesso real; e cada tradução teve um leitor, não dois.

Do trabalho de plataforma na Provibr. Os cartões a que esses nomes se referem estão em um atalho é uma referência, não um direito, e o que custa manter honestos nove catálogos está em dezasseis mil chaves, nove idiomas.

Comece com um host e um agente

Crie uma conta, instale o agente com um único comando e ligue o seu primeiro host Proxmox. O período experimental dura 14 dias e não pede quaisquer dados de pagamento.