Structure de projet Go, un agencement pratique

Comment structurer un projet Go de façon idiomatique : modules, package main, internal et cmd, le débat pkg, et pourquoi rester à plat au début.

Sur cette page

Go n’impose aucune structure de dossiers via un framework, ce qui est à la fois libérateur et déroutant. Ce guide présente un agencement pratique et idiomatique qui passe à l’échelle, du petit outil au vrai service, et explique les compromis derrière chaque choix.

Commencez par un module

Tout projet Go moderne est un module. Initialisez-en un avec un chemin de module, généralement l’URL de votre dépôt :

go mod init github.com/votrenom/greet

Cela crée un fichier go.mod qui enregistre le chemin du module, la version de Go et la liste des dépendances. Quand vous ajoutez une dépendance, Go écrit aussi un fichier go.sum avec des sommes de contrôle cryptographiques pour des builds reproductibles. Versionnez les deux fichiers ; ne les éditez jamais à la main.

Restez à plat tant que possible

L’erreur la plus fréquente est de trop organiser trop tôt. Un petit programme tient très bien en quelques fichiers dans un seul package, à la racine du dépôt :

greet/
├── go.mod
├── go.sum
├── main.go
└── greeting.go

N’ajoutez des dossiers que lorsqu’un vrai besoin apparaît : un second binaire, ou du code à masquer aux importateurs. Une hiérarchie prématurée ne fait qu’allonger les chemins d’import à écrire.

Le package main et le point d’entrée

L’exécutable vit dans package main avec un point d’entrée func main. Gardez main mince : lire la configuration, câbler les dépendances, puis déléguer la vraie logique à un autre package.

// Package main est le point d’entrée de la commande greet.
package main

import (
	"fmt"

	"github.com/votrenom/greet/internal/greeting"
)

func main() {
	fmt.Println(greeting.Message("Go"))
}

internal/ pour le code privé

Go applique une règle de structure via le compilateur : le code sous un dossier internal/ ne peut être importé que par des packages situés sous le parent de internal/. C’est la façon idiomatique de garder des détails d’implémentation privés à votre module.

greet/
├── go.mod
├── main.go
├── internal/
│   └── greeting/
│       └── greeting.go
└── cmd/
    └── greet/
        └── main.go

cmd/ et pkg/ : le débat

Deux dossiers conventionnels sont très répandus, mais souvent mal employés.

  • cmd/ contient un sous-dossier par binaire, chacun avec son propre main.go. Il se justifie dès que vous avez plus d’un exécutable. Avec un seul binaire, un main.go à la racine est plus simple.
  • pkg/ est censé regrouper du code destiné à être importé par d’autres projets. Il est contesté : beaucoup de mainteneurs estiment qu’il ajoute un niveau de dossier sans signification pour le compilateur, contrairement à internal/. Préférez internal/ pour le code privé, et ne recourez à pkg/ que si vous publiez délibérément une bibliothèque réutilisable.

Le nommage

  • Les noms de packages sont courts, en minuscules et en un seul mot : greeting, pas greetingUtils. Le nom du dossier doit correspondre à celui du package.
  • Évitez le bégaiement : une fonction du package greeting doit s’appeler greeting.Message, pas greeting.GreetingMessage.
  • En Go idiomatique, pas de package fourre-tout utils ou helpers. Nommez les packages d’après ce qu’ils fournissent.

Pour la référence officielle sur les modules et la conception des packages, consultez la documentation sur go.dev, qui fait foi à mesure que la chaîne d’outils évolue.

Résumé

  • Tout projet est un module : versionnez go.mod et go.sum, sans les éditer à la main.
  • Restez à plat au début ; ajoutez internal/ et cmd/ seulement en cas de besoin réel.
  • Préférez internal/ à pkg/ pour la confidentialité, et nommez les packages d’après leur rôle.

Ensuite, affinez votre style avec le guide des conventions Go, ou suivez la roadmap développeur Go pour la vue d’ensemble.

Recherche

La recherche s’exécute entièrement dans votre navigateur.