Retour au blog

13 erreurs de débutant en programmation (et comment les corriger)

Les 13 erreurs les plus courantes des débutants en programmation, de l'erreur ignorée au test jamais écrit, chacune avec un court exemple en Go et sa correction.

13 erreurs de débutant en programmation (et comment les corriger)

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 err avant 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

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.gogo
package 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.gogo
package main

import (
	"fmt"
	"strconv"
)

func main() {
	input := "80a0"
	port, _ := strconv.Atoi(input)
	fmt.Println("listening on port", port)
}
example.texttext
listening 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.gogo
package 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.texttext
invalid 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.gogo
package 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.texttext
200 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.gogo
package main

import (
	"fmt"
	"strings"
)

func main() {
	fields := strings.Split("alice,admin,active", ",")
	for i := 0; i <= len(fields); i++ {
		fmt.Println(fields[i])
	}
}
example.texttext
alice
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.gogo
package 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.texttext
100
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.bashbash
git 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.gogo
package 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.gogo
func 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.gogo
func 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.gogo
log.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.bashbash
dlv 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 used et "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.
  • gofmt formate 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 vet repère le code suspect. Il signale par exemple un verbe %d auquel 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 test et le package testing sont 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 -race et 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.gogo
package 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.

Écrivez du Go comme un ingénieur senior

Des leçons interactives dans votre navigateur. Les premières sont gratuites.

Essayer une leçon gratuiteOu créer un compte gratuit