Os erros mais comuns de quem começa a programar têm pouco a ver com sintaxe. Os programadores novos saltam a mensagem de erro, escrevem cinquenta linhas antes de executar alguma, ignoram o caso em que o input é inválido e tentam adivinhar a causa dos bugs em vez de olharem para os valores. São hábitos, e os hábitos corrigem-se cedo.
Este artigo aborda 13 deles. Cada um traz um pequeno exemplo em Go, quando o código ajuda, e a correção. O Go é uma boa linguagem para ensinar isto porque o compilador e as ferramentas recusam logo à partida vários destes erros. A secção perto do fim trata desses.
Resumo rápido
- Lê a mensagem de erro inteira. Diz-te o ficheiro, a linha e o que correu mal.
- Executa o código de poucas em poucas linhas. Passos pequenos tornam os bugs fáceis de encontrar.
- Não coles código, incluindo o que vem de uma IA, que não consegues explicar linha a linha.
- Trata o caminho de erro. Em Go, verifica cada
errantes de usares o valor que veio com ele. - Dá às coisas nomes que digam o que guardam, mantém as funções pequenas e testa os casos vazios e os limites.
- Usa Git desde o primeiro dia, guarda os segredos em variáveis de ambiente e escreve um teste antes de achares que precisas dele.
- Depura imprimindo valores ou avançando passo a passo com o Delve, não alterando código ao acaso.
- Põe o código a funcionar antes de o tornares abstrato ou rápido. Depois constrói algo que não seja um tutorial.
Índice
- 1. Não ler a mensagem de erro
- 2. Escrever muito código antes de executar qualquer parte
- 3. Copiar e colar código que não consegues explicar
- 4. Programar apenas o caminho feliz
- 5. Nomes vagos
- 6. Uma função enorme que faz tudo
- 7. Erros off-by-one e entradas vazias
- 8. Não usar controlo de versões desde o primeiro dia
- 9. Pôr configuração e segredos diretamente no código
- 10. Nunca escrever um teste
- 11. Depurar por palpite
- 12. Abstrair ou otimizar antes de funcionar
- 13. Ficar preso aos tutoriais e pedir ajuda sem um exemplo reproduzível
- Que erros destes o Go apanha por ti?
1. Não ler a mensagem de erro
Muitos principiantes veem texto a vermelho e voltam logo ao código para mudar alguma coisa. A mensagem de erro é a coisa mais útil que tens no ecrã. Normalmente indica o ficheiro, a linha, a coluna e o que correu mal.
Eis um pequeno carregador de configuração de um servidor que não compila:
example.gogopackage main import ( "fmt" "os" ) func main() { port := os.Getenv("PORT") fmt.Println("starting server") }
example.texttext./main.go:9:2: declared and not used: port
Lê-a da esquerda para a direita. main.go é o ficheiro, 9 é a linha, 2 é a coluna, e o resto diz que a variável port nunca é usada. Ou a usas ou a apagas. Não há mais nada de errado no programa.
Os erros em tempo de execução funcionam da mesma maneira. Quando um programa Go entra em panic, o stack trace mostra a função e a linha onde aconteceu, com a chamada mais recente primeiro. Procura a primeira linha que aponta para um ficheiro teu e começa por aí.
Se as mensagens de erro te parecem crípticas, não és só tu. Um grupo de trabalho do ITiCSE que reviu a investigação em 2019 concluiu que as mensagens de erro dos compiladores «representam uma dificuldade substancial» para principiantes (Becker et al., 2019). Lê-las devagar e à letra é uma competência, e melhora depressa com a prática.
2. Escrever muito código antes de executar qualquer parte
Escrever um programa inteiro e só depois executá-lo pela primeira vez parece eficiente, mas torna os bugs caros de encontrar. Quando 80 linhas novas falham, o bug pode estar em qualquer uma delas. Quando falham 5 linhas novas, está nessas 5.
A solução é um ciclo curto. Escreve umas quantas linhas, executa-as, lê o output e repete. Imprime valores intermédios enquanto constróis e apaga os prints quando essa parte funcionar. O Go compila tão depressa que correr go run . depois de cada pequena alteração custa-te um ou dois segundos.
3. Copiar e colar código que não consegues explicar
Copiar do Stack Overflow ou de um assistente de IA não tem mal. O erro é colar código que não consegues explicar. Quando deixar de funcionar, e vai deixar, não fazes ideia por onde começar. Também saltas a parte em que aprendes.
Impõe-te uma regra. Antes de colar, lê cada linha e diz o que faz. Se não conseguires, pede à IA que te explique essa linha, ou reescreve-a tu a partir da explicação. A regra pesa mais agora que a IA faz parte da forma como a maioria das pessoas aprende. Entre quem está a aprender a programar, 73,3 % usa ou tenciona usar ferramentas de IA, e 39,5 % usa-as todos os dias (Stack Overflow Developer Survey, 2025). O argumento completo está em ainda deves escrever código à mão?.
4. Programar apenas o caminho feliz
O código de principiante tende a assumir que o ficheiro existe, que a rede está disponível e que o utilizador escreveu um número. O input real é mais confuso. Este programa lê um número de porta e ignora o erro:
example.gogopackage main import ( "fmt" "strconv" ) func main() { input := "80a0" port, _ := strconv.Atoi(input) fmt.Println("listening on port", port) }
example.texttextlistening on port 0
A gralha transformou-se na porta 0 sem qualquer aviso. O Go devolve os erros como valores normais, por isso a correção é verificá-los e dizer o que falhou:
example.gogopackage main import ( "fmt" "os" "strconv" ) func parsePort(raw string) (int, error) { port, err := strconv.Atoi(raw) if err != nil { return 0, fmt.Errorf("invalid port %q: %w", raw, err) } if port < 1 || port > 65535 { return 0, fmt.Errorf("port %d out of range 1-65535", port) } return port, nil } func main() { port, err := parsePort("80a0") if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Println("listening on port", port) }
example.texttextinvalid port "80a0": strconv.Atoi: parsing "80a0": invalid syntax exit status 1
Para cada input, pergunta o que acontece se estiver em falta, vazio, for do tipo errado ou grande demais. Cada resposta passa a ser uma verificação no teu código.
5. Nomes vagos
data, tmp, res, x2 e thing não dizem nada a quem ler o código a seguir, e daqui a duas semanas essa pessoa és tu. Um nome deve dizer o que o valor guarda, com as palavras do problema.
example.gogo// Hard to follow for _, d := range data { if d.s == 500 { tmp++ } } // Clear for _, req := range requests { if req.Status == 500 { serverErrors++ } }
A convenção do Go é usar nomes curtos para âmbitos curtos e nomes mais longos para âmbitos mais amplos. i serve bem para um ciclo de três linhas. Uma variável ao nível do pacote chamada c já não serve. Se tens dificuldade em dar nome a alguma coisa, muitas vezes é sinal de que faz duas coisas.
6. Uma função enorme que faz tudo
Um main com 200 linhas que lê um ficheiro, o analisa, calcula totais e imprime um relatório é difícil de testar e ainda mais difícil de alterar. Divide-o por tarefa. Eis uma pequena CLI que conta os códigos de estado HTTP num access log:
example.gogopackage main import ( "bufio" "fmt" "io" "maps" "os" "slices" "strings" ) func main() { if len(os.Args) != 2 { fmt.Fprintln(os.Stderr, "usage: logstats <access.log>") os.Exit(2) } f, err := os.Open(os.Args[1]) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer f.Close() counts, err := countStatusCodes(f) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } printReport(os.Stdout, counts) } // countStatusCodes reads access log lines and counts the status code, // which is the last field on each line. func countStatusCodes(r io.Reader) (map[string]int, error) { counts := make(map[string]int) sc := bufio.NewScanner(r) for sc.Scan() { fields := strings.Fields(sc.Text()) if len(fields) == 0 { continue } counts[fields[len(fields)-1]]++ } return counts, sc.Err() } func printReport(w io.Writer, counts map[string]int) { for _, code := range slices.Sorted(maps.Keys(counts)) { fmt.Fprintf(w, "%s %d\n", code, counts[code]) } }
example.texttext200 2 404 1 500 1
Agora o main limita-se a ligar as peças. countStatusCodes recebe um io.Reader em vez de um nome de ficheiro, por isso um teste pode passar-lhe um strings.Reader sem tocar no disco. printReport recebe um io.Writer pela mesma razão. Cada função cabe num ecrã e faz uma só coisa.
7. Erros off-by-one e entradas vazias
Ciclos que dão um passo a mais são um clássico. Este analisa uma linha de CSV e usa <= onde precisa de <:
example.gogopackage main import ( "fmt" "strings" ) func main() { fields := strings.Split("alice,admin,active", ",") for i := 0; i <= len(fields); i++ { fmt.Println(fields[i]) } }
example.texttextalice admin active panic: runtime error: index out of range [3] with length 3 goroutine 1 [running]: main.main() /home/you/csvrow/main.go:11 +0xc0 exit status 2
O panic diz exatamente o que aconteceu. O índice 3 foi usado num slice de comprimento 3, cujo último índice válido é 2, na linha 11. Em Go, a correção mais simples é nem sequer gerir o índice: for _, field := range fields não consegue passar do fim.
Um erro parecido, e mais fácil de passar despercebido, é a entrada vazia. Esta função calcula a média das latências de pedidos:
example.gogopackage main import "fmt" func averageLatency(ms []float64) float64 { var total float64 for _, v := range ms { total += v } return total / float64(len(ms)) } func main() { fmt.Println(averageLatency([]float64{120, 80, 100})) fmt.Println(averageLatency(nil)) }
example.texttext100 NaN
Sem pedidos, há uma divisão por zero, que dá NaN com floats e um panic com inteiros. Pergunta sempre o que a tua função faz com zero elementos, com um elemento e com o máximo. Esses três casos apanham muitos bugs de limites.
8. Não usar controlo de versões desde o primeiro dia
Muitos principiantes adiam o Git até terem um projeto «a sério». Depois estragam algo que funcionava uma hora antes e não têm forma de voltar atrás. O Git é o botão de desfazer do projeto inteiro, e começar a usá-lo quase não custa nada:
example.bashbashgit init git add . git commit -m "Parse port from PORT env var"
Faz um commit sempre que algo funciona, com uma mensagem que diga o que mudou. Quando estragares alguma coisa, git diff mostra o que alteraste desde o último estado bom. Se fizeres push para o GitHub ou outro serviço, ficas também com uma cópia de segurança e um portefólio.
9. Pôr configuração e segredos diretamente no código
Pôr uma palavra-passe de base de dados ou uma chave de API diretamente no código-fonte funciona até fazeres push para um repositório público. A GitGuardian contou 28,65 milhões de novos segredos escritos no código adicionados a commits públicos no GitHub em 2025, mais 34 % do que no ano anterior (GitGuardian, 2026). Portas, URLs e credenciais também são diferentes no teu portátil e em produção, por isso escrevê-los no código obriga-te a editar código para fazer deploy.
Em vez disso, lê-os do ambiente:
example.gogopackage main import ( "cmp" "fmt" "os" ) func main() { addr := ":" + cmp.Or(os.Getenv("PORT"), "8080") apiKey := os.Getenv("PAYMENTS_API_KEY") if apiKey == "" { fmt.Fprintln(os.Stderr, "PAYMENTS_API_KEY is not set") os.Exit(1) } fmt.Println("listening on", addr) }
cmp.Or devolve o primeiro valor não vazio, por isso PORT fica com um valor por omissão razoável. A chave de API não tem valor por omissão, e o programa recusa-se a arrancar sem ela. Se guardas valores locais num ficheiro .env, adiciona-o ao .gitignore antes do primeiro commit.
10. Nunca escrever um teste
Os principiantes testam executando o programa e olhando para o output. Isso funciona uma vez. Não te avisa quando uma alteração posterior estraga algo que antes funcionava. O Go traz os testes na toolchain padrão, por isso não há nada para instalar. Primeiro, corrige averageLatency do erro 7 para que indique se havia dados:
example.gogofunc averageLatency(ms []float64) (float64, bool) { if len(ms) == 0 { return 0, false } var total float64 for _, v := range ms { total += v } return total / float64(len(ms)), true }
Depois acrescenta um teste em tabela ao lado:
example.gogofunc TestAverageLatency(t *testing.T) { tests := []struct { name string in []float64 want float64 wantOK bool }{ {"three requests", []float64{120, 80, 100}, 100, true}, {"one request", []float64{42}, 42, true}, {"no requests", nil, 0, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, ok := averageLatency(tt.in) if got != tt.want || ok != tt.wantOK { t.Errorf("averageLatency(%v) = %v, %v, want %v, %v", tt.in, got, ok, tt.want, tt.wantOK) } }) } }
example.texttext--- PASS: TestAverageLatency (0.00s) --- PASS: TestAverageLatency/three_requests (0.00s) --- PASS: TestAverageLatency/one_request (0.00s) --- PASS: TestAverageLatency/no_requests (0.00s) PASS
Põe-no num ficheiro terminado em _test.go e executa go test ./.... Cada linha da tabela é um caso, por isso acrescentar um novo caso-limite custa mais uma linha. A linha no requests é o input que devolvia NaN no erro 7. Agora tem uma resposta definida e um teste que a mantém assim.
11. Depurar por palpite
O ciclo de depuração de um principiante é assim: muda alguma coisa, executa, continua avariado, muda outra coisa. Ao fim de dez voltas, o código tem bugs novos e o original continua lá. Num estudo com 21 estudantes de sete universidades, alguns «usavam poucas estratégias e aplicavam-nas de forma ineficaz» e introduziam novos bugs enquanto depuravam (Murphy et al., 2008).
Observa antes de mudares. Forma uma hipótese sobre o que está errado e depois confirma-a imprimindo os valores reais:
example.gogolog.Printf("order=%+v total=%d items=%d", order, total, len(order.Items))
%+v imprime os nomes dos campos da struct, o que te poupa a adivinhar que número é qual. Quando os prints se tornam demasiado ruidosos, usa um depurador. O Delve é o padrão em Go, e tanto o VS Code como o GoLand usam-no por baixo:
example.bashbashdlv debug . -- access.log (dlv) break main.go:43 (dlv) continue (dlv) print fields []string len: 3, cap: 3, [ "GET", "/api/orders", "200", ]
A linha 43 é a linha counts[...]++ do contador de logs do erro 6. O programa para aí e inspecionas o valor real de fields em vez de o adivinhares.
Quando encontrares o bug, escreve um teste que o reproduza antes de o corrigires. Assim não pode voltar sem que dês por isso.
12. Abstrair ou otimizar antes de funcionar
É tentador desenhar interfaces, sistemas de configuração e camadas de plugins para um programa que ainda nem corre. Ou trocar um ciclo claro por algo engenhoso porque talvez seja mais rápido. As duas coisas tornam o código mais difícil de alterar antes de saberes o que ele precisa de fazer.
Põe-no a funcionar com o código mais simples que conseguires escrever. Espera até teres escrito a mesma coisa três vezes antes de a extraíres para uma função partilhada. Em Go, define uma interface quando aparecer de facto uma segunda implementação, não quando a imaginas. Otimiza só depois de medir, e o Go dá-te as ferramentas para isso: go test -bench para benchmarks e pprof para perfis.
13. Ficar preso aos tutoriais e pedir ajuda sem um exemplo reproduzível
Os tutoriais fazem-te sentir produtivo porque cada passo funciona. Na primeira vez que abres um ficheiro em branco, descobres quanto é que realmente retiveste. A certa altura tens de construir algo em que ninguém te guiou: uma CLI que muda o nome a ficheiros, uma pequena API HTTP, um bot para uma aplicação de chat. Vai sair confuso, e vais aprender mais com isso do que com os próximos três tutoriais. Como aprender a programar em 2026 explica como estruturar essa prática, e como aprender Go apresenta um plano completo para Go.
Quando ficares preso, vais pedir ajuda, e a forma como pedes conta. «O meu código não funciona» não te leva a lado nenhum. Reduz o problema ao programa mais pequeno que ainda mostra o bug e depois partilha esse programa, o output de erro exato, o que esperavas e o que já tentaste. Muitas vezes encontras o bug sozinho enquanto o reduzes. Para Go, o Go Playground dá-te um link partilhável para um programa executável.
Que erros destes o Go apanha por ti?
O Go foi pensado para grandes bases de código mantidas por muitas pessoas, e várias das suas regras acabam por bloquear erros de principiante antes de o código correr.
- Variáveis e imports não usados não compilam. O compilador rejeita
declared and not usede"strings" imported and not used. As FAQ do Go descrevem isto como «trocar conveniência a curto prazo por velocidade de build e clareza do programa a longo prazo» e notam que uma variável não usada pode indicar um bug. - O
gofmtformata todos os ficheiros Go da mesma maneira, por isso nunca discutes espaçamento ou a posição das chavetas, e uma indentação desarrumada não consegue esconder um bug (gofmt). - O
go vetapanha código suspeito. Assinala coisas como um verbo%dque recebe uma string, que compila sem problemas mas imprime lixo (cmd/vet). - Os erros são valores que tens de tratar. Uma função que pode falhar devolve um
error. Quando devolve um valor e um erro, não consegues usar o valor sem receberes também o erro, mesmo que depois o descartes com um_bem visível. O erro do caminho feliz da secção 4 passa a ser algo que tens de escrever de propósito (Errors are values). - Os testes vêm incluídos. O
go teste o pacotetestingvêm com o Go, por isso não há nenhuma framework para escolher antes do primeiro teste (testing). - O detetor de races encontra data races. Executa testes ou programas com
-racee o Go reporta acessos concorrentes a memória partilhada, com o ficheiro e a linha de ambos os acessos (Data Race Detector).
Eis o go vet a apanhar um bug de formatação que compila:
example.gogopackage main import "fmt" func main() { active := "12" fmt.Printf("%d active users\n", active) }
example.texttext./main.go:7:14: fmt.Printf format %d has arg active of wrong type string
O Go não te impede de escrever uma função com 200 linhas nem de chamar tmp a uma variável. Apanha os erros mecânicos, o que te deixa mais tempo para os que exigem critério. Para armadilhas específicas do Go, como erros sombreados ou surpresas com append, vê erros comuns em Go que deves evitar.
Perguntas frequentes
Qual é o erro mais comum dos programadores principiantes?
No maior estudo sobre erros de compilação de principiantes, que abrangeu 37 milhões de compilações feitas por mais de 250.000 estudantes de Java, o erro mais frequente foi parênteses ou aspas desemparelhados. Esses eram corrigidos depressa. Os erros caros eram os que o compilador não assinalava, como ignorar um valor devolvido, que normalmente levavam mais de 17 minutos a corrigir ou nunca chegavam a ser corrigidos (Altadmri & Brown, 2015). Como hábito, não ler a mensagem de erro é o que atrasa tudo o resto.
Como deixo de cometer os mesmos erros de programação?
Escreve um teste para cada bug que corriges, para que não possa voltar sem dares por isso. Mantém uma pequena lista dos erros que já cometeste mais do que uma vez e confronta o teu código com ela antes de fazeres commit. As ferramentas também ajudam. Em Go, o go vet e o gofmt apanham automaticamente toda uma categoria de erros.
O Go é uma boa linguagem para principiantes que cometem muitos erros?
Sim. O compilador do Go rejeita variáveis e imports não usados, os erros são valores de retorno explícitos, e o go vet, o gofmt e o go test vêm com a linguagem. As mensagens de erro são curtas e apontam para um ficheiro e uma linha. Vê o Go é uma boa primeira linguagem de programação? para a comparação completa.
Os principiantes devem usar IA para escrever código?
Usa-a para explicar código, erros e conceitos, não para escrever código que não consegues acompanhar. Uma boa regra é nunca colar código que não conseguirias reescrever de memória no dia seguinte. Vais precisar dessa compreensão para corrigir o código da IA quando estiver quase certo.
Quanto tempo demora a deixar de cometer erros de principiante?
Para a maioria das pessoas, estes hábitos desaparecem ao fim de alguns meses a escrever código com regularidade, porque cada um te custa tempo até o corrigires. Praticar todos os dias em programas reais leva-te lá mais depressa do que sessões longas e esporádicas. Os erros mecânicos são os primeiros a desaparecer. As questões de critério, como os nomes e o tamanho das funções, demoram mais.
Começa a escrever Go hoje
Cada erro desta lista fica mais fácil de evitar quanto mais código escreves e quanto mais depressa vês o que correu mal. É essa a ideia por trás do LevelUpGo. Cada lição é um exercício de Go que corre numa toolchain Go real no teu navegador, por isso desde o primeiro dia lês erros verdadeiros do compilador, tratas valores err e vês testes a falhar. Começa pelo percurso Go Fundamentals.
