Les erreurs les plus courantes des débutants en programmation ont peu à voir avec la syntaxe. Les nouveaux programmeurs sautent le message d'erreur, écrivent cinquante lignes avant d'en exécuter une seule, ignorent le cas où l'entrée est invalide et devinent l'origine d'un bug au lieu de regarder les valeurs. Ce sont des habitudes, et une habitude se corrige tôt.
Cet article en passe 13 en revue. Chacune s'accompagne d'un court exemple en Go quand le code aide, et de sa correction. Go est un bon langage pour apprendre ici, parce que son compilateur et ses outils refusent purement et simplement plusieurs de ces erreurs. La section vers la fin de l'article les passe en revue.
En bref
- Lisez le message d'erreur en entier. Il vous donne le fichier, la ligne et ce qui ne va pas.
- Exécutez votre code toutes les quelques lignes. Avancer par petites étapes rend les bugs faciles à trouver.
- Ne collez pas de code, y compris celui généré par une IA, que vous ne savez pas expliquer ligne par ligne.
- Gérez le cas d'erreur. En Go, vérifiez chaque
erravant d'utiliser la valeur renvoyée avec lui. - Nommez les choses d'après ce qu'elles contiennent, gardez des fonctions courtes et testez les cas vides et les cas limites.
- Utilisez Git dès le premier jour, gardez les secrets dans des variables d'environnement et écrivez un test avant de penser en avoir besoin.
- Déboguez en affichant les valeurs ou en avançant pas à pas avec Delve, pas en modifiant le code au hasard.
- Faites fonctionner le code avant de l'abstraire ou de l'optimiser. Ensuite, construisez quelque chose qui n'est pas un tutoriel.
Table des matières
- 1. Ne pas lire le message d'erreur
- 2. Écrire beaucoup de code avant d'en exécuter la moindre ligne
- 3. Copier-coller du code que vous ne savez pas expliquer
- 4. Ne coder que le cas nominal
- 5. Des noms vagues
- 6. Une énorme fonction qui fait tout
- 7. Les erreurs off-by-one et les entrées vides
- 8. Ne pas utiliser de gestion de versions dès le premier jour
- 9. Coder en dur la configuration et les secrets
- 10. Ne jamais écrire de test
- 11. Déboguer au hasard
- 12. Abstraire ou optimiser avant que le code fonctionne
- 13. Rester dans les tutoriels et demander de l'aide sans exemple reproductible
- Quelles erreurs Go détecte-t-il pour vous ?
1. Ne pas lire le message d'erreur
Face à du texte rouge, les débutants retournent souvent directement dans le code pour changer quelque chose. Le message d'erreur est pourtant l'information la plus utile à l'écran. Il indique en général le fichier, la ligne, la colonne et ce qui ne va pas.
Voici un petit chargeur de configuration serveur qui ne compile pas :
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
Lisez-le de gauche à droite. main.go est le fichier, 9 la ligne, 2 la colonne, et le reste indique que la variable port n'est jamais utilisée. Utilisez-la ou supprimez-la. Rien d'autre n'est faux dans le programme.
Les erreurs d'exécution fonctionnent de la même façon. Quand un programme Go panique, la stack trace liste la fonction et la ligne où c'est arrivé, en commençant par l'appel le plus récent. Cherchez la première ligne qui pointe vers l'un de vos fichiers et partez de là.
Si les messages d'erreur vous semblent obscurs, vous n'êtes pas le seul dans ce cas. En 2019, un groupe de travail ITiCSE qui a passé en revue la recherche sur le sujet a conclu que les messages d'erreur du compilateur « posent des difficultés considérables » aux débutants (Becker et al., 2019). Les lire lentement et au pied de la lettre est une compétence, et elle progresse vite avec la pratique.
2. Écrire beaucoup de code avant d'en exécuter la moindre ligne
Écrire un programme entier puis le lancer pour la première fois donne une impression d'efficacité, mais cela rend les bugs coûteux à trouver. Quand 80 nouvelles lignes échouent, le bug peut se trouver n'importe où. Quand 5 nouvelles lignes échouent, il est dans ces 5 lignes.
La solution est une boucle courte. Écrivez quelques lignes, exécutez-les, lisez la sortie, recommencez. Affichez les valeurs intermédiaires pendant que vous construisez, puis supprimez ces affichages une fois que le morceau fonctionne. Go compile assez vite pour qu'un go run . après chaque petite modification ne vous coûte qu'une seconde ou deux.
3. Copier-coller du code que vous ne savez pas expliquer
Copier depuis Stack Overflow ou un assistant IA n'a rien de grave. L'erreur, c'est de coller du code que vous ne savez pas expliquer. Quand il casse, et il cassera, vous ne savez pas par où commencer. Vous sautez aussi la partie où vous apprenez.
Fixez-vous une règle. Avant de coller, lisez chaque ligne et dites ce qu'elle fait. Si vous n'y arrivez pas, demandez à l'IA d'expliquer cette ligne, ou réécrivez-la vous-même à partir de l'explication. Cette règle compte d'autant plus que l'IA fait désormais partie de la façon dont la plupart des gens apprennent. Parmi les personnes qui apprennent à coder, 73,3 % utilisent ou prévoient d'utiliser des outils d'IA, et 39,5 % s'en servent tous les jours (Stack Overflow Developer Survey, 2025). L'argumentaire complet se trouve dans faut-il encore écrire du code à la main.
4. Ne coder que le cas nominal
Le code de débutant a tendance à supposer que le fichier existe, que le réseau fonctionne et que l'utilisateur a bien tapé un nombre. Les vraies entrées sont plus désordonnées. Ce programme lit un numéro de port et ignore l'erreur :
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
La faute de frappe est devenue le port 0 sans le moindre avertissement. Go renvoie les erreurs comme des valeurs ordinaires, donc la correction consiste à les vérifier et à dire ce qui a échoué :
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
Pour chaque entrée, demandez-vous ce qui se passe si elle est absente, vide, du mauvais type ou trop grande. Chaque réponse devient une vérification dans votre code.
5. Des noms vagues
data, tmp, res, x2 et thing n'apprennent rien au prochain lecteur, et dans deux semaines, ce lecteur, ce sera vous. Un nom doit dire ce que contient la valeur, avec les mots du problème.
example.gogo// Hard to follow for _, d := range data { if d.s == 500 { tmp++ } } // Clear for _, req := range requests { if req.Status == 500 { serverErrors++ } }
La convention en Go veut des noms courts pour les portées courtes et des noms plus longs pour les portées plus larges. i convient pour une boucle de trois lignes. Une variable de niveau package appelée c, non. Si vous avez du mal à nommer quelque chose, c'est souvent le signe que cela fait deux choses à la fois.
6. Une énorme fonction qui fait tout
Un main de 200 lignes qui lit un fichier, le parse, calcule des totaux et affiche un rapport est difficile à tester et encore plus difficile à modifier. Découpez-le par tâche. Voici un petit outil en ligne de commande qui compte les codes de statut HTTP dans un journal d'accès :
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
main se contente désormais d'assembler les morceaux. countStatusCodes prend un io.Reader au lieu d'un nom de fichier, donc un test peut lui passer un strings.Reader sans toucher au disque. printReport prend un io.Writer pour la même raison. Chaque fonction tient sur un écran et fait une seule chose.
7. Les erreurs off-by-one et les entrées vides
Les boucles qui vont un tour trop loin sont un grand classique. Celle-ci parse une ligne CSV et utilise <= là où il faut < :
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
La panique dit exactement ce qui s'est passé. L'index 3 a été utilisé sur une slice de longueur 3, dont le dernier index valide est 2, à la ligne 11. En Go, la correction la plus simple consiste à ne pas gérer l'index du tout : for _, field := range fields ne peut pas dépasser la fin.
Sa variante plus discrète, c'est l'entrée vide. Cette fonction calcule la latence moyenne des requêtes :
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
Aucune requête signifie une division par zéro, ce qui donne NaN pour les flottants et une panique pour les entiers. Demandez-vous toujours ce que fait votre fonction avec zéro élément, un élément et le maximum. Ces trois cas suffisent à attraper une bonne partie des bugs aux limites.
8. Ne pas utiliser de gestion de versions dès le premier jour
Beaucoup de débutants repoussent Git jusqu'au « vrai » projet. Puis ils cassent quelque chose qui fonctionnait une heure plus tôt et n'ont aucun moyen de revenir en arrière. Git est le bouton « annuler » de tout votre projet, et s'y mettre ne coûte presque rien :
example.bashbashgit init git add . git commit -m "Parse port from PORT env var"
Faites un commit chaque fois que quelque chose fonctionne, avec un message qui dit ce qui a changé. Quand vous cassez quelque chose, git diff montre ce que vous avez modifié depuis le dernier état fonctionnel. Poussez sur GitHub ou un autre hébergeur et vous avez en plus une sauvegarde et un portfolio.
9. Coder en dur la configuration et les secrets
Mettre un mot de passe de base de données ou une clé d'API directement dans le code source fonctionne, jusqu'au jour où vous poussez ce code sur un dépôt public. GitGuardian a recensé 28,65 millions de nouveaux secrets codés en dur ajoutés à des commits publics sur GitHub en 2025, soit une hausse de 34 % par rapport à l'année précédente (GitGuardian, 2026). Les ports, les URL et les identifiants diffèrent aussi entre votre ordinateur et la production, donc les coder en dur vous oblige à modifier le code pour déployer.
Lisez-les plutôt depuis l'environnement :
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 renvoie la première valeur non vide, donc PORT reçoit une valeur par défaut raisonnable. La clé d'API n'a pas de valeur par défaut, et le programme refuse de démarrer sans elle. Si vous gardez des valeurs locales dans un fichier .env, ajoutez-le à .gitignore avant votre premier commit.
10. Ne jamais écrire de test
Les débutants testent en lançant le programme et en regardant la sortie. Cela fonctionne une fois. Cela ne vous dit pas quand une modification ultérieure casse quelque chose qui fonctionnait. Go intègre les tests à sa chaîne d'outils standard, donc il n'y a rien à installer. Commencez par corriger averageLatency, vue dans l'erreur 7, pour qu'elle indique s'il y avait des données :
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 }
Ajoutez ensuite un test en table juste à côté :
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
Placez-le dans un fichier qui se termine par _test.go et lancez go test ./.... Chaque ligne du tableau est un cas, donc ajouter un nouveau cas limite ne demande qu'une ligne de plus. La ligne no requests correspond à l'entrée qui renvoyait NaN dans l'erreur 7. Elle a maintenant une réponse définie et un test qui la garantit.
11. Déboguer au hasard
La boucle de débogage du débutant ressemble à ceci : changer quelque chose, lancer, toujours cassé, changer autre chose. Après dix tours, le code a de nouveaux bugs et le bug d'origine est toujours là. Dans une étude portant sur 21 étudiants de sept universités, certains « utilisaient peu de stratégies, les appliquaient de manière inefficace » et introduisaient de nouveaux bugs en déboguant (Murphy et al., 2008).
Regardez avant de modifier. Formulez une hypothèse sur ce qui ne va pas, puis vérifiez-la en affichant les valeurs réelles :
example.gogolog.Printf("order=%+v total=%d items=%d", order, total, len(order.Items))
%+v affiche les noms des champs de la struct, ce qui vous évite de deviner quel nombre correspond à quoi. Quand les affichages deviennent trop bruyants, utilisez un débogueur. Delve est le débogueur standard pour Go, et VS Code comme GoLand s'en servent en coulisses :
example.bashbashdlv debug . -- access.log (dlv) break main.go:43 (dlv) continue (dlv) print fields []string len: 3, cap: 3, [ "GET", "/api/orders", "200", ]
La ligne 43 est la ligne counts[...]++ du compteur de logs de l'erreur 6. Le programme s'y arrête et vous inspectez la vraie valeur de fields au lieu de la deviner.
Une fois le bug trouvé, écrivez un test qui le reproduit avant de le corriger. Il ne pourra plus revenir sans que vous le remarquiez.
12. Abstraire ou optimiser avant que le code fonctionne
Il est tentant de concevoir des interfaces, des systèmes de configuration et des couches de plugins pour un programme qui ne tourne pas encore. Ou de remplacer une boucle claire par quelque chose d'astucieux parce que ce serait peut-être plus rapide. Dans les deux cas, le code devient plus difficile à modifier avant même que vous sachiez ce qu'il doit faire.
Faites-le fonctionner avec le code le plus simple possible. Attendez d'avoir écrit la même chose trois fois avant de l'extraire dans une fonction partagée. En Go, définissez une interface quand une deuxième implémentation apparaît réellement, pas quand vous l'imaginez. N'optimisez qu'après avoir mesuré, et Go vous donne les outils pour cela : go test -bench pour les benchmarks et pprof pour le profilage.
13. Rester dans les tutoriels et demander de l'aide sans exemple reproductible
Les tutoriels donnent une impression de productivité parce que chaque étape fonctionne. La première fois que vous ouvrez un fichier vide, vous découvrez ce que vous avez vraiment retenu. À un moment, il faut construire quelque chose que personne ne vous a guidé à faire : un outil en ligne de commande qui renomme des fichiers, une petite API HTTP, un bot pour une application de messagerie. Ce sera brouillon, et vous en apprendrez plus qu'avec les trois prochains tutoriels. Comment apprendre à coder en 2026 explique comment structurer cette pratique, et comment apprendre Go présente un plan complet pour Go.
Quand vous serez bloqué, vous demanderez de l'aide, et la façon de demander compte. « Mon code ne marche pas » ne vous mènera nulle part. Réduisez le problème au plus petit programme qui montre encore le bug, puis partagez ce programme, la sortie d'erreur exacte, ce que vous attendiez et ce que vous avez essayé. Vous trouverez souvent le bug vous-même en réduisant le problème. Pour Go, le Go Playground vous donne un lien partageable vers un programme exécutable.
Quelles erreurs Go détecte-t-il pour vous ?
Go a été conçu pour de grandes bases de code maintenues par de nombreuses personnes, et plusieurs de ses règles bloquent au passage des erreurs de débutant avant même l'exécution du code.
- Les variables et imports inutilisés ne compilent pas. Le compilateur rejette
declared and not usedet"strings" imported and not used. La FAQ de Go parle d'« échanger la commodité à court terme contre la vitesse de build et la clarté du programme à long terme » et précise qu'une variable inutilisée peut signaler un bug. gofmtformate chaque fichier Go de la même manière, donc vous ne débattez jamais d'espacement ou de position des accolades, et une indentation brouillonne ne peut pas cacher un bug (gofmt).go vetrepère le code suspect. Il signale par exemple un verbe%dauquel on passe une string, ce qui compile sans problème mais affiche n'importe quoi (cmd/vet).- Les erreurs sont des valeurs à gérer. Une fonction qui peut échouer renvoie une
error. Quand elle renvoie une valeur et une erreur, vous ne pouvez pas utiliser la valeur sans recevoir aussi l'erreur, même si vous l'ignorez ensuite avec un_bien visible. L'erreur du cas nominal vue en section 4 devient quelque chose que vous devez écrire délibérément (Errors are values). - Les tests sont intégrés.
go testet le packagetestingsont livrés avec Go, donc il n'y a aucun framework à choisir avant votre premier test (testing). - Le détecteur de courses (race detector) trouve les data races. Lancez vos tests ou vos programmes avec
-raceet Go signale les accès concurrents à la mémoire partagée, avec le fichier et la ligne des deux accès (Data Race Detector).
Voici go vet qui détecte un bug de formatage qui compile :
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
Go ne vous empêchera pas d'écrire une fonction de 200 lignes ni d'appeler une variable tmp. Il détecte en revanche les erreurs mécaniques, ce qui vous laisse plus de temps pour celles qui demandent du jugement. Pour les pièges propres à Go comme les erreurs masquées ou les surprises d'append, consultez les erreurs courantes en Go à éviter.
Questions fréquentes
Quelle est l'erreur la plus courante chez les programmeurs débutants ?
Dans la plus grande étude sur les erreurs de compilation des débutants, qui porte sur 37 millions de compilations effectuées par plus de 250 000 étudiants en Java, l'erreur la plus fréquente était une parenthèse, une accolade ou un guillemet mal apparié. Ces erreurs étaient corrigées rapidement. Les erreurs coûteuses étaient celles que le compilateur ne signalait pas, comme ignorer une valeur de retour, qui demandaient en général plus de 17 minutes à corriger ou n'étaient jamais corrigées (Altadmri & Brown, 2015). En tant qu'habitude, ne pas lire le message d'erreur est celle qui ralentit tout le reste.
Comment arrêter de refaire les mêmes erreurs de programmation ?
Écrivez un test pour chaque bug que vous corrigez, afin qu'il ne puisse pas revenir sans que vous le remarquiez. Tenez une courte liste des erreurs que vous avez commises plus d'une fois et relisez votre code à la lumière de cette liste avant chaque commit. Les outils aident aussi. En Go, go vet et gofmt détectent automatiquement toute une catégorie d'erreurs.
Go est-il un bon langage pour les débutants qui font beaucoup d'erreurs ?
Oui. Le compilateur Go rejette les variables et les imports inutilisés, ses erreurs sont des valeurs de retour explicites, et go vet, gofmt et go test sont livrés avec le langage. Ses messages d'erreur sont courts et pointent vers un fichier et une ligne. Consultez Go est-il un bon premier langage de programmation pour la comparaison complète.
Les débutants devraient-ils utiliser l'IA pour écrire du code ?
Utilisez-la pour expliquer du code, des erreurs et des concepts, pas pour écrire du code que vous ne comprenez pas. Une bonne règle : ne collez jamais de code que vous ne pourriez pas réécrire de mémoire le lendemain. Vous aurez besoin de cette compréhension pour corriger le code de l'IA quand il est presque juste.
Combien de temps faut-il pour ne plus faire d'erreurs de débutant ?
Pour la plupart des gens, ces habitudes s'estompent après quelques mois de programmation régulière, parce que chacune vous fait perdre du temps tant que vous ne l'avez pas corrigée. Une pratique quotidienne sur de vrais programmes vous y mène plus vite que de longues sessions occasionnelles. Les erreurs mécaniques disparaissent en premier. Les questions de jugement comme le nommage et la taille des fonctions prennent plus de temps.
Commencez à écrire du Go dès aujourd'hui
Chaque erreur de cette liste devient plus facile à éviter à mesure que vous écrivez du code et que vous voyez rapidement ce qui n'a pas marché. C'est le principe de LevelUpGo. Chaque leçon est un exercice Go qui s'exécute sur une vraie chaîne d'outils Go dans votre navigateur. Dès le premier jour, vous lisez donc de vraies erreurs de compilation, gérez des valeurs err et voyez des tests échouer. Commencez par le parcours Go Fundamentals.
