- A repetição de
if err != nil em Go permaneceu por anos como uma grande insatisfação nas pesquisas com usuários, mas a equipe de Go decidiu não avançar, por enquanto, com mudanças na sintaxe de tratamento de erros
- As propostas de
check/handle em 2018, try em 2019 e ? em 2024 não conseguiram consenso suficiente; em especial, try enfrentou forte resistência por causa do fluxo de controle oculto
- Pelo processo de propostas de Go, quando não há consenso geral, a proposta normalmente é rejeitada; mesmo entre membros seniores da equipe de Go no Google, não há unanimidade hoje sobre a melhor direção
- Quem defende manter o estado atual entende que Go já tem uma forma funcional de tratar erros e que uma nova sintaxe geraria custos altos em estilo de código, depuração, documentação, ferramentas e código existente
- A equipe de Go vai fechar, sem investigação adicional, propostas abertas e novas propostas que tenham como foco principal a sintaxe de tratamento de erros, e concentrar-se em outras oportunidades de melhoria até que surja uma percepção mais clara do problema
A antiga insatisfação criada por if err != nil
- Uma das insatisfações mais duradouras em Go é a verbosidade do código de tratamento de erros
- O padrão típico tem a seguinte forma
x, err := call()
if err != nil {
// handle err
}
- Em programas com muitas chamadas de API que simplesmente retornam erros,
if err != nil pode dominar o restante do código
- Na função de exemplo
printSum, das 10 linhas do corpo da função, 4 linhas parecem trabalho real, incluindo chamadas, impressão e retorno, enquanto as outras 6 parecem ruído
- Nas pesquisas anuais com usuários de Go, o tratamento de erros esteve por anos entre as principais insatisfações; por algum tempo, a ausência de generics ficou à frente, mas, depois que Go passou a dar suporte a generics, o tratamento de erros voltou ao topo das queixas
Três grandes propostas de sintaxe
- A primeira tentativa explícita da equipe de Go começou em 2018, como parte do esforço Go 2, quando Russ Cox organizou formalmente o problema
- O rascunho de design de Marcel van Lohuizen se baseava nos mecanismos
check e handle, e também incluía uma análise de abordagens de outras linguagens e alternativas
func printSum(a, b string) error {
handle err { return err }
x := check strconv.Atoi(a)
y := check strconv.Atoi(b)
fmt.Println("result:", x + y)
return nil
}
- A abordagem
check/handle foi considerada complexa demais e, em 2019, surgiu uma proposta try mais simples
- A palavra-chave semelhante a
check virou a função built-in try
- A parte
handle foi removida
- Foi criada a ferramenta tryhard para converter código existente de tratamento de erros para o estilo
try
- A issue no GitHub relacionada foi debatida intensamente, com quase 900 comentários
func printSum(a, b string) error {
// use a defer statement to augment errors before returning
x := try(strconv.Atoi(a))
y := try(strconv.Atoi(b))
fmt.Println("result:", x + y)
return nil
}
try afetava o fluxo de controle, retornando da função envolvente quando ocorria um erro, e o retorno poderia acontecer mesmo dentro de expressões profundamente aninhadas, o que muitos usuários acharam difícil de aceitar
- Na época, talvez tivesse sido melhor introduzir uma nova palavra-chave; hoje é possível controlar a versão da linguagem com granularidade por meio do arquivo
go.mod e de diretivas por arquivo
- A proposta recente de Jimmy Frasche segue a direção de voltar ao design original de
check/handle, tratando algumas desvantagens
Reflexões sobre o processo após try e a proposta ?
- Após a proposta
try, Russ Cox revisitou o processo de propostas na série “Thinking about the Go Proposal Process”
- “Go Proposal Process: Large Changes” avaliou que
try deveria ter sido um segundo rascunho de design, não uma proposta com cronograma de implementação
- Nos anos seguintes, a equipe de Go não impulsionou mudanças na sintaxe de tratamento de erros, enquanto a comunidade continuou enviando propostas parecidas, interessantes, difíceis de entender ou inviáveis
- Ian Lance Taylor criou uma umbrella issue para organizar o estado das propostas de melhoria no tratamento de erros, e também surgiu uma Go Wiki para reunir feedback e discussões relacionadas
- “go error handling proposals”, de Sean K. H. Liao, acompanha muitas propostas de tratamento de erros ao longo dos anos
- Com a continuidade das insatisfações, Ian Lance Taylor apresentou em 2024 uma proposta para reduzir boilerplate de tratamento de erros usando
?
- A notação foi emprestada do
? operator de Rust
- Em um pequeno estudo informal com usuários, a maioria dos participantes inferiu corretamente o significado do código Go usando
?
- Também foram criados uma ferramenta para converter código Go comum para a nova sintaxe e um protótipo de compilador
func printSum(a, b string) error {
x := strconv.Atoi(a) ?
y := strconv.Atoi(b) ?
fmt.Println("result:", x + y)
return nil
}
- Essa proposta também rapidamente recebeu muitos comentários e sugestões de ajustes detalhados baseados em preferências; Ian fechou a proposta e moveu o conteúdo para uma discussion
- Uma versão levemente modificada obteve uma reação um pouco mais positiva, mas não alcançou apoio amplo
Por que querem parar agora
- A equipe de Go concluiu que deve parar de tentar resolver o problema sintático do tratamento de erros no futuro previsível
- O processo de propostas dá suporte a essa decisão
- O objetivo do processo de propostas é chegar, em tempo adequado, a um consenso geral sobre o resultado
- Quando não se encontra consenso geral na discussão do issue tracker, a proposta normalmente é rejeitada
- Se não houver consenso nem um próximo passo claro, os Go architects revisam a discussão e tentam chegar a um consenso interno
- Nenhuma proposta de tratamento de erros chegou perto de obter apoio consensual, e todas foram rejeitadas
- Nem mesmo os membros seniores da equipe de Go no Google têm unanimidade hoje sobre a melhor direção a seguir, e não é possível avançar de forma razoável sem um consenso forte
Argumentos pela manutenção do estado atual e pela mudança
- Há razões práticas a favor de manter o estado atual, ligadas à maturidade de Go e aos custos para o ecossistema
- Se Go tivesse introduzido cedo um açúcar sintático dedicado para tratamento de erros, hoje haveria menos controvérsia, mas Go já tem 15 anos e possui uma forma de tratamento de erros que funciona
- Mesmo que uma solução perfeita fosse encontrada agora, a situação poderia apenas passar a desagradar os que preferem manter o estado atual em vez dos defensores da mudança
- Generics não precisam necessariamente ser usados diretamente pelos usuários, mas uma nova sintaxe de tratamento de erros poderia fazer o código parecer não idiomático se não fosse usada, tornando seu uso praticamente obrigatório para a maioria
- Adicionar uma nova sintaxe também pode conflitar com a regra de design de Go de não oferecer várias formas de fazer a mesma coisa
- O recurso de redeclaração da declaração curta de variáveis
:= foi introduzido para resolver um problema causado pelo tratamento de erros
- Sem redeclaração, cada verificação de erro consecutiva exigiria nomes diferentes para
err ou declarações separadas de variáveis
- Se na época houvesse um suporte sintático melhor para tratamento de erros, a regra de redeclaração e a complexidade relacionada talvez não fossem necessárias
- Quando os erros são devidamente enriquecidos e tratados, a proporção de repetição simples diminui
- Nas pesquisas com usuários, há comentários recorrentes sobre a ausência de stack trace nos erros
- É possível retornar erros enriquecidos por meio de funções auxiliares
- Ao acrescentar informações de entrada, como em
fmt.Errorf("invalid integer: %q", a), o peso relativo do boilerplate diminui
- Recursos da biblioteca padrão também podem reduzir boilerplate no tratamento de erros
- Isso segue uma direção semelhante à de “Errors are values”, de Rob Pike
- Em alguns casos,
cmp.Or permite lidar com vários erros de uma só vez
- Escrever, ler e depurar são atividades diferentes
- Escrever verificações repetitivas de erro é tedioso, mas IDEs e autocompletar com auxílio de LLM podem gerar facilmente verificações básicas de erro
- Na leitura, a verbosidade se destaca mais, e a IDE poderia oferecer um toggle para ocultar código de tratamento de erros
- Na depuração, já ter instruções
if separadas facilita adicionar println ou definir breakpoints
- Se o tratamento de erros ficar oculto atrás de
check, try ou ?, pode ser necessário convertê-lo de volta para instruções if, e esse processo pode complicar a depuração ou introduzir bugs sutis
- Mudanças na linguagem trazem custos não só de design e implementação, mas também de alteração de código existente, atualização de documentação e ajuste de ferramentas
- A equipe de Go é relativamente pequena e tem muitas outras prioridades a tratar
- Prioridades e tamanho da equipe podem mudar
- No Google Cloud Next 2025, alguns usuários de Go encontrados pela equipe de Go disseram com força que a linguagem não deveria ser alterada para melhorar o tratamento de erros
- Eles disseram que, ao vir de outras linguagens, a ausência de uma sintaxe dedicada de tratamento de erros em Go é o que mais chama atenção, mas que isso se torna menos importante conforme passam a escrever código Go mais idiomático
- Essa amostra não é grande o suficiente para ser representativa, mas pode ser um grupo diferente das pessoas vistas no GitHub
- Os argumentos a favor da mudança continuam válidos
- A ausência de suporte melhor para tratamento de erros permanece entre as principais insatisfações nas pesquisas com usuários
- Abordagens focadas apenas em reduzir o número de caracteres podem estar na direção errada
- Se o tratamento básico de erros fosse tornado bem visível por meio de uma palavra-chave, ao mesmo tempo removendo o boilerplate
err != nil, seria mais fácil verificar em code reviews se os erros estão sendo tratados
- Ainda não se sabe o bastante se o núcleo do problema é apenas a verbosidade sintática ou a verbosidade do bom tratamento de erros, isto é, construir erros significativos para APIs, desenvolvedores e usuários finais
A decisão da equipe de Go
- Até agora, nenhuma tentativa de tratar erros ganhou impulso suficiente
- A equipe de Go entende que falta uma compreensão compartilhada do problema e que nem todos concordam sequer que exista um problema
- No futuro previsível, não vai promover mudanças sintáticas na linguagem para tratamento de erros
- Propostas abertas e futuras propostas que tenham como foco principal a sintaxe de tratamento de erros serão fechadas sem investigação adicional
- A exploração e a discussão da comunidade não levaram a mudanças na sintaxe de tratamento de erros, mas resultaram em várias melhorias na linguagem Go e em seus processos
1 comentários
Opiniões no Hacker News
Se alguém quiser soltar de forma leviana uma sugestão do tipo “era só ter feito assim” para a equipe do Go, gostaria que antes olhasse a página da wiki linkada no texto, https://go.dev/wiki/Go2ErrorHandlingFeedback, e a busca de issues no GitHub, https://github.com/golang/go/issues?q=+is%3Aissue+label%3Aerror-handling
O que você está prestes a propor quase certamente não é inédito, e há uma boa chance de que muitas dessas ideias já tenham sido analisadas em profundidade
Vejo com bons olhos essa abordagem franca da equipe do Go, e ainda uso Go com prazer todos os dias no trabalho
Também não vi nada parecido em algumas páginas das issues do GitHub com mais comentários, e acho forçado supor que, por a equipe do Go ser formada por magos de design de linguagens, eles obviamente já avaliaram as soluções que as pessoas daqui jogam de forma leviana
A equipe do Go cometeu o mesmo erro do Java, isto é, ter tipagem estática sem polimorfismo paramétrico, e a raiz desse problema de tratamento de erros também está aí; parece que eles simplesmente levantaram as mãos e não vão corrigir
Pode soar grandioso e intimidador, mas é uma forma elegante e funcionalmente pura de propagar erros até onde eles possam ser tratados sem deixá-los serem esquecidos
Para quem escreve código Haskell, isso é algo tão enraizado que é difícil entender como não havia ninguém na comunidade Go que conhecesse e gostasse dessa abordagem
Agradeço pela página em si e pelos links, mas é desconcertante ver pessoas que se importam tanto com sua linguagem pularem uma solução tão bem estabelecida
Quase todas as linguagens têm, cada uma à sua maneira, uma abordagem melhor; queria saber se é apenas uma questão de não conseguirem decidir ou de não conseguirem agradar a todos, ou se há algum motivo concreto na própria linguagem Go que faz com que as soluções de outras linguagens não se encaixem
Na prática, em quase todos os casos, eles sabem muito mais
O amador pensa ingenuamente que a melhor linguagem é aquela em que se enfia o maior número possível de recursos, especialmente quando são recursos do seu gosto
É parecido com alguém que acabou de começar a fazer facas olhar para uma faca de chef japonesa e achá-la insuficiente, depois imaginar que ela ficaria melhor com um cabo impresso em 3D com sulcos para os dedos, compartimento secreto, isqueiro e alto-falante Bluetooth
Basta criar uma lista de checkboxes, discutir e preencher cada item, e depois não remover nada a menos que se descubra um erro semântico fatal ou uma brecha de soundness
Quando tudo estiver preenchido, implementem; e as pessoas que ficavam tagarelando se deveria ser
.await,/awaitou.await!()vão desaparecer de novoRust funciona assim e, embora algumas issues fiquem atrasadas por mais de 10 anos, no fim os itens são preenchidos e estabilizados no nightly mais recente
Se Go não consegue resolver um único problema que todo mundo encontra imediatamente, mesmo havendo várias propostas bem acabadas, porque não consegue escolher uma delas e fica esperando o bikeshedding parar, então o processo é uma comédia
Não é uma coleção de recursos que simplesmente são adicionados quando atendem a uma lista de requisitos com checkboxes
Rust terá virado uma bagunça como C++? Go continuará sendo uma linguagem atemporal, como era no lançamento?
Na pesquisa, a porcentagem que mencionou tratamento de erros foi de 13%, e há também pessoas que preferem exatamente o jeito atual
https://go.dev/blog/survey2024-h1-results
Já escrevi antes uma função Go peculiar que esperava que uma função interna retornasse um erro
Por isso, se a função interna não retornasse um erro, a função externa tinha que retornar um erro e fazer outro processamento; se a função interna retornasse um erro, tinha que retornar nil
Em resumo, eu precisava escrever
if err == nil { // return an error }, e nãoif err != nil { ... }, mas, por hábito, escrevi a primeira forma comum e levei um bom tempo para depurarFoi porque eu fiquei tão insensível a
if err != nilque meu cérebro nem considerou a possibilidade de que aquela construção não deveria estar aliPor isso acho que expressões comuns precisam de açúcar sintático. Se a diferença entre o extremamente comum
if err != nile o raroif err == nilfosse mais evidente, isso teria ajudado de verdadeif err == nil, coloco um comentário// invertedpara chamar atençãoSeria bom se isso fosse tratado no nível da linguagem, mas compartilho ao menos como uma forma de tornar mais visível
if fruit != "Apple" { ... }também pode criar exatamente a mesma situaçãoFico curioso se existe uma solução geral para melhorar isso, e ver isso como um problema exclusivo de tratamento de erros parece meio fora de foco
Não há nada especial ou único em erros; eles são apenas um estado como qualquer outro
Se surgir um padrão comum
if err == nil { return ... }, desta vez ele é que vai ficar espalhado pelo códigoA solução atual é boa, e parece que quem não gosta dela são principalmente pessoas que acabaram de começar em Go ou ainda são iniciantes
Ao meu redor, as pessoas gostam do tratamento de erros “verboso”, explícito, claro e fácil de ler
if err != nilcomo se fosse um único símbolo de ligadura pequeno, ou renderizá-lo esmaecido em segundo planoAssim, uma forma que difira exatamente dessa string, como
if err == nil, ficaria mais chamativaif err … {Gosto do tratamento explícito de erros em Go
Uma função sempre tem sucesso, ou pode ter sucesso ou falhar. Funções que sempre têm sucesso são simples; quando uma função que pode falhar falha, o código externo não pode continuar em estado de falha, então precisa lidar com isso
É aqui que as linguagens se dividem. Muitas linguagens lançam exceções, propagando-as até alguém capturá-las explicitamente, e fornecem algum tipo de stack trace
Em Go, gosto do fato de que, ao escrever código, você sempre tem escolhas a fazer: ignorar o erro e prosseguir (
foo, _ := doSomething()), retornar cedo sem informação significativa (return nil, err), retornar cedo adicionando contexto útil, ou interpretar o erro recebido e ramificarPor exemplo, se uma linha a ser modificada não for encontrada no banco de dados, a camada de serviço pode retornar um erro de not found e a API responder 404, ou uma função de exclusão idempotente pode interpretar not found como sucesso
Em Go 2 ou em outra linguagem, eu gostaria de ter, em vez de tuplas potencialmente nil, um tipo Result no estilo Rust/Swift e tipos de erro mais bem tipados e enumeráveis, em vez de usar sempre
errordiretamenteNo entanto, adicionar Result por cima do retorno idiomático por tupla do Go 1 criaria várias formas de fazer a mesma coisa, gerando confusão e divisão; por isso combina mais com Go 2 ou com uma nova linguagem
Camadas mais baixas da stack em geral não sabem o que fazer, então não deveriam tratar o erro
A política de tratar um erro acaba facilmente virando a política de envolver o erro e retorná-lo de volta para cima na stack, o que dá bastante trabalho braçal
Go permite ignorar erros completamente, e isso pode acabar levando a crashes
Acho meio difícil entender como alguém consegue identificar corretamente o requisito para criar software robusto e ainda assim gostar da forma de tratamento de erros de Go
[1]: https://borgo-lang.github.io/ | https://github.com/borgo-lang/borgo
Todas as linguagens funcionais, muitas linguagens modernas como Rust, e até Java com checked exceptions oferecem isso
Em uma linguagem com generics, dá para replicar em grande parte o “tratamento de erros” à moda Go, e provavelmente produzir um código melhor
Se a resposta for JavaScript ou Python, esse é mesmo um padrão comum de comparação
Para Go, esta é a decisão certa. Quando conheci Go, eu não gostava do tratamento de erros, mas hoje passei a gostar bastante
A virada veio por dois motivos. Li o texto https://go.dev/blog/errors-are-values e assimilei de verdade a perspectiva de que “erros são valores”; com base nisso, também criei um pacote relativamente popular, https://github.com/stytchauth/sqx
Além disso, fui me acostumando aos poucos a usar
panic(err)em estados inválidos que realmente não fazem sentidoNão há motivo para forçar o código pai a tratar todos os estados absurdos com os quais ele nem tem noção de como lidar, e um ou dois panics bem posicionados podem eliminar centenas de verificações de erro em uma base de código
Por exemplo, dá para pensar em algo como verificar se há um logger padrão dentro de ctx
Pelo contrário, é bem provável que melhore
bash também tem
-eQuando comecei a usar C# antigo, achava inteligente entender o fluxo de try/catch/finally, using, aninhamento, o que acontece se der erro no catch, o que acontece se der erro no finally
Hoje prefiro não ter que pensar nessas coisas
Não gosto da forma como este texto diz que o principal problema do tratamento de erros em Go é que a sintaxe é verbosa demais. Isso não me incomoda muito
O mais importante é que erros podem ser descartados silenciosamente ou ignorados por engano, que o resultado de uma chamada de função não é um valor e, portanto, não pode ser facilmente armazenado ou passado adiante, e que
errors.Isé necessário, sendo todo esse erro “aninhado” um mecanismo estranho de runtime que não combina bem com o sistema de tiposFazer switch sobre erros também é difícil, a biblioteca padrão usa valores sentinel, e a interação com generics não é boa, o que torna necessários pacotes como errgroup
Esqueci mais alguma coisa?
Em uma linguagem com exceções, ninguém faria isso
Decidiu-se não fazer novas tentativas de mudar a sintaxe de tratamento de erros no futuro previsível, e isso abre espaço de atenção para olhar outros problemas, sejam eles de erros ou de outros temas
A evolução de Go é glacialmente lenta e, para muita gente, isso é uma feature, não um bug
Acho engraçada a explicação de que “a opinião da pesquisa de que erros não têm stack trace pode ser resolvida fazendo uma função auxiliar criar e retornar um erro enriquecido”
É divertido chamar fornecer manualmente um stack trace de “tratamento de erros”, como em
if err != nil { return fmt.Errorf("invalid integer: %q", a) }Pela definição da equipe de Go, exceções acabam tratando erros automaticamente. Claro, nas linguagens exceto C++
Pode até ser, mas você realmente precisa de tudo isso? E o custo de logs?
Acho muito melhor um erro encapsulado de uma linha que corta o ruído de frameworks e do runtime
Quando bem encapsulado, também é muito fácil de pesquisar e, em geral, permite rastrear com mais eficácia do que um stack trace
Usando Go em tempo integral há mais de 10 anos, nunca precisei do ruído verboso de funções de runtime ou da pilha de chamadas
Do ponto de vista de um desenvolvedor Elixir, isso parece loucura
Em Erlang/Elixir, isso normalmente se resolve fazendo a função retornar uma tupla
{:ok, result}ou{:error, description_or_struct}Com a instrução
withdo Elixir, dá para concentrar o tratamento de erros na parte de baixo, o que melhora muito a legibilidadeGo poderia adicionar algo equivalente a uma cláusula
with, para continuar executando funções enquanto o erro for nil e ter uma cláusula de tratamento de erro no finalwithÉ interessante como Go adia por muito tempo estruturas básicas e claramente valiosas, como generics, tratamento de erros e gerenciamento de pacotes, por causa da falta de consenso da comunidade
Generics levaram 13 anos após a abertura do código; 16 anos depois, ainda não há tratamento de erros; e o gerenciamento de pacotes levou cerca de 9 anos
Deliberação tem valor, mas lançamento também tem. As pessoas que escrevem 900 comentários no GitHub acabarão continuando a usar Go, e é bem provável que colocar algo na linguagem fosse melhor do que continuar adiando
Uma função com vários tipos de retorno não permite fazer nada além de atribuir a variáveis
Mas em Erlang e Elixir eles são uma forma totalmente idiomática, sem nenhum peso
Na verdade, eles são até muito mais poderosos do que na família ML, porque ali os tipos soma são abertos
Não acompanhei essa discussão em detalhes, mas não entendo por que não adotam simplesmente o jeito do Rust
Depois que Go ganhou generics, esse também é o padrão que eu acabo acrescentando logo de cara
No texto linkado, só vejo a explicação de que “Rust não tem nada equivalente a
handle, e a conveniência do operador?pode levar as pessoas a omitirem o tratamento adequado”Mas não entendo por que algo ser conveniente significaria ignorar erros
Metade do problema do jeito do Go é que ele não força nada em relação ao resultado e só força a verificação de erro no mínimo possível
x, err := strconv.Atoi("123"); fmt.Println("result:", x)dádeclared and not used: err, mas depois de uma segunda conversão, mesmo sem verificarerr, isso pode rodar sem você perceber o problema por causa do valor zero padrão dey, que é 0Mesmo que você deixe vazio como
if err != nil { }, compila e roda, e não dá para saber que algo deu erradoSe o valor de retorno for um Result, você é obrigado a tomar uma decisão. Mesmo que alguém use
!de qualquer jeito ou suba o erro comodamente com?sem tratar o caso de erro, então vamos proibirpanictambém?E, por causa daquela obsessão estranha de que todo tipo precisa ter um valor zero definido, também não consegue adicionar tipos soma
?for fácil de usar, ninguém mais vai encapsular errosÉ uma lógica muito suspeita
Para começo de conversa, basta projetar
?de modo que ele incentive o encapsulamento de errosPor exemplo, como deveria ser, em Go, o equivalente ao
Fromdo Rust??tem baixa visibilidade e esconde uma ramificação de fluxo de controle dentro de uma única instrução ou expressãoEsse também é um dos motivos pelos quais Go não tem operador ternário e optou por instruções
ifem que cada ramo fica em uma linha separadaTambém não é fácil colocar breakpoint, e ele incentiva mais a simplesmente propagar o erro para cima do que a enriquecê-lo ou tratá-lo
:=era declaração e atribuição em uma única instrução, mas, na 5ª linha do exemplo,errnão está sendo declarado de novo, com o novoerrsombreando oerrexistente?Se for isso, como a nova variável
errnão foi usada, acho que deveria falhar comdeclared and not used: errOu, se a variável já existe,
:=simplesmente se comporta como uma atribuição normal?Dizer que “o fato de o erro não ter stack trace pode ser resolvido fazendo uma função auxiliar criar e retornar um erro enriquecido” é otimista demais em relação à realidade
Linguagens que têm stack trace dão isso de graça, mas em Go é preciso implementar toda vez
Você pode ser uma pessoa desenvolvedora disciplinada que sempre acrescenta detalhes, mas nem todo mundo da equipe terá a mesma disciplina
A melhor coisa de um stack trace é que ele fornece o caminho de chamadas até o erro
Quando ocorre um erro em um método chamado de vários lugares, o stack trace permite saber imediatamente por qual caminho ele chegou ali
Passei muitos anos fazendo algo parecido com sysadmin/SRE e resolvendo muitos problemas; quando havia stack trace, problemas fáceis terminavam em 1 ou 2 minutos porque a causa era óbvia
Em Go, se alguém não enriquece o erro ou reutiliza a mesma mensagem de erro, até problemas fáceis viram trabalho de dedução e acabam levando mais tempo