Architecture et backend16 min

La structure d'un projet (1) : le standard qui n'en était pas un

Par · fondateur et PDG de Hábil ·

Le dépôt de structure Go le plus populaire se dit non officiel. Le guide de Go demande cmd/ et internal/, et l'outil ne surveille qu'internal/. Avec modèle.

La structure d'un projet · épisode 1 sur 7 · Go

Scénario : les personnages et l'histoire sont inventés. Les données techniques et les citations ont leur source à la fin.

En 1 minute

  • Le dépôt de « structure standard » de Go le plus populaire (56,7 mille étoiles le 22 octobre 2026) dit dans son README qu'il n'est pas un standard officiel et que, pour un projet simple, c'est excessif [2][3].
  • Le guide officiel de Go demande peu pour un serveur : les programmes dans cmd/ et la logique dans internal/ [1].
  • Parmi les dossiers où vit ton code, seul internal/ pose une frontière que l'outil go fait respecter : en pratique, un autre module ne peut pas l'importer [6][7].
  • Tu repars avec un modèle qui se clone et tourne du premier coup, avec un script qui démontre cette frontière en passant au rouge quand il le faut.
  • Pour lundi : ouvre ton dépôt et demande à chaque dossier quel problème il résout aujourd'hui. Celui qui ne sait pas répondre est en trop.

Dans cet article

  1. Que s'est-il passé à 23:40 ?
  2. Que demande vraiment le guide de Go ?
  3. Quelles sont les cinq erreurs les plus courantes ?
  4. Exercice : peux-tu faire passer la frontière au rouge ?
  5. À retenir
  6. Et ensuite ?
  7. Le modèle et le cours
  8. Que fait Hábil ?
  9. Références

Que s'est-il passé à 23:40 ?

Iván a hérité d'un programme de 300 lignes réparti dans quatorze dossiers, et aucun ne lui disait où mettre une nouvelle fonction.

Lundi, 23:40. Bureau vide, troisième étage.

C'était son premier jour comme alternant. Septième semestre, le café froid depuis neuf heures et une seule tâche au tableau : « Que le vérificateur prévienne quand un service met plus d'une seconde. »

Le vérificateur était un programme en Go qui interrogeait une liste de services par HTTP et disait lesquels répondaient. Il avait été écrit par Rodrigo, l'alternant précédent, parti le vendredi sans rien laisser d'autre que le dépôt.

Iván l'a cloné et est resté à fixer l'écran.

api/  assets/  build/  cmd/  configs/  deployments/  docs/
examples/  init/  internal/  pkg/  scripts/  test/  web/

Quatorze dossiers pour environ 300 lignes de Go. api/ : un README.md qui disait « ici va le contrat ». web/ : vide. deployments/ : un fichier de 2019 sur un serveur qui n'existait plus. pkg/util : onze fonctions sans aucun rapport entre elles.

La tâche était petite. Dix lignes, peut-être. Mais où ? Dans pkg/util, à côté d'une fonction qui formatait des dates ? Dans internal/ ? Y avait-il des règles, ou était-ce l'habitude ? Le README de Rodrigo disait : « Structure standard de Go », avec un lien.

Il l'a suivi : golang-standards/project-layout, plus de 56 mille étoiles. Iván a respiré. Si tant de gens l'utilisaient, ce devait être le standard. Et dans le deuxième paragraphe de l'introduction du README, en gras, il a trouvé quelque chose qu'il n'attendait pas :

« NOT an official standard defined by the core Go dev team » [2].

Ce n'est pas un standard officiel de l'équipe de Go. C'est le dépôt lui-même qui le dit.

Iván s'est renversé dans sa chaise. Si le standard n'en était pas un, quelle partie de ces quatorze dossiers comptait vraiment ?

À 23:58, il a écrit à Lucía, la responsable technique. La réponse est arrivée en deux minutes :

— Sur ces quatorze, lequel l'outil surveille-t-il ? Lequel change ce que la commande go te laisse importer ?

Iván a regardé la liste encore une fois. Aucun n'avait l'air de surveiller quoi que ce soit.

— Je ne sais pas, a-t-il écrit.

— Un seul. Demain je te montre lequel, et pourquoi les treize autres sont une affaire d'opinion.

Que demande vraiment le guide de Go ?

Pour un serveur, le guide officiel de Go tient en deux dossiers : le programme dans cmd/ et tout le reste dans internal/.

Il s'intitule Organizing a Go module [1] et va du plus petit au plus grand, en sept cas : un paquet, un programme, l'un des deux avec des paquets de support, plusieurs paquets, plusieurs programmes, paquets et programmes ensemble, et enfin un serveur. Pour ce dernier cas, il dit deux choses.

La première : un serveur n'a normalement pas de paquets destinés à être importés par d'autres, donc sa logique va dans internal/ [1]. La seconde : comme il a souvent beaucoup de dossiers qui ne sont pas du Go, il convient de regrouper les programmes dans cmd/ [1].

C'est tout. Il ne nomme pas pkg/ ; les dossiers sans Go, comme api/ ou web/, il les laisse à ton jugement [1].

En 2021, dans une discussion sur ce même dépôt, Russ Cox l'a dit plus brièvement : le standard minimal d'un dépôt Go importable (un dépôt dont d'autres importent le code) est un LICENSE et un go.mod à la racine, et le code là où ça t'arrange. « That's it. That's the "standard" », a-t-il écrit [5]. Et il a ajouté que même cmd/ n'est pas obligatoire [5].

Le modèle de cet épisode, estructura-go, suit le guide à la lettre :

estructura-go/
├── go.mod
├── README.md
├── LICENSE
├── LICENSE-CONTENIDO.md
├── cmd/
│   └── revisor/main.go          el programa: lee la configuración, arma y arranca
├── internal/
│   ├── servicio/                el vocabulario: Servicio, Estado. Sin red ni archivos
│   ├── revisar/                 consulta muchos servicios a la vez
│   ├── httpapi/                 rutas, JSON y errores
│   └── config/                  banderas, entorno y la lista de servicios
├── testdata/servicios.json
├── herramientas/probar-frontera.sh
└── .github/workflows/verificar.yml

Passons dossier par dossier, et pour chacun la question est la même : quel problème résout-il aujourd'hui ?

Pourquoi le programme vit-il dans cmd/ ?

main.go est mince exprès : si quelque chose là-dedans mérite un test, sa place n'est plus là.

cmd/revisor/main.go lit la configuration, assemble les pièces de internal/ et démarre le serveur. Il ne décide pas de ce qu'est un service en bonne santé ni de la manière de l'interroger. Pourquoi si maigre ? Parce que main termine le processus avec os.Exit et lit l'environnement réel : ce qui reste là reste sans test. Le modèle utilise une fonction ejecutar qui renvoie une erreur, et le test l'appelle avec un port libre.

Un deuxième programme, comme une tâche de nuit, irait dans cmd/tarea-nocturna/ et partagerait tout ce qui se trouve dans internal/.

Qu'a de spécial internal/ ?

internal/ est le seul dossier de l'arbre doté d'une frontière que l'outil go fait respecter : le code qui y vit ne peut pas être importé depuis l'extérieur de l'arbre qui le contient ; en pratique, depuis un autre module.

Selon la documentation de la commande go, un paquet situé dans un dossier internal ne peut être importé que par le code qui partage le chemin au-dessus de ce dossier [6]. Si ton module est github.com/tu-usuario/revisor, tout ce module peut utiliser revisor/internal/servicio ; un module étranger, non. Attention : cela se joue sur le chemin, pas sur le module. Un module nommé github.com/tu-usuario/revisor/otro le pourrait, et le script de la frontière utilise ce cas.

Ce n'est pas le langage qui le fait respecter, mais la commande go. Les notes de version de Go 1.4 le disent ainsi : le langage n'a aucun moyen d'imposer cette distinction, et la commande go introduit un mécanisme pour définir des paquets « internal » [7]. Depuis Go 1.5, cela vaut pour n'importe quel dépôt [7][8].

Et qu'est-ce que ça apporte à un serveur ? Ce que personne n'importe de l'extérieur, tu le modifies sans rien casser chez personne. C'est pourquoi le guide conseille de laisser dans internal/ autant que possible [1].

Et les treize autres dossiers d'Iván ? Aucun ne décide qui importe qui. La commande go traite différemment deux autres noms qui ne figurent pas sur sa liste. Elle ignore testdata/ [6].

vendor/, lui, barre le passage : si un module importe quelque chose par le chemin du vendor/ d'un autre, l'outil répond use of vendored package not allowed (mesuré avec Go 1.27.1). Mais ton code ne va pas là : il garde des copies de tes dépendances, et go mod vendor l'efface et le reconstruit à chaque fois [20]. Pour ton code, la frontière, c'est internal/.

Pourquoi servicio/ n'importe-t-il personne ?

Le paquet dont tous dépendent ne dépend d'aucun, et c'est pour cela qu'il ne peut pas fermer un cycle.

internal/servicio/ définit Servicio (nom, URL, délai maximal) et Estado (ce qu'il a répondu, combien de temps il a mis, pourquoi il a échoué). Il n'ouvre pas de connexions, ne lit pas de fichiers, ne sait rien de JSON. Tous les paquets de internal/ l'importent, et lui n'importe personne du projet :

Pourquoi servicio/ n'importe-t-il personne ?
PaquetImporte
cmd/revisorconfig, httpapi, revisar
httpapirevisar, servicio
revisarservicio
configservicio
serviciopersonne du projet

Ce n'est pas de l'esthétique : la spécification interdit qu'un paquet s'importe lui-même, directement ou indirectement [11]. Comme servicio n'importe personne, il ne peut pas faire partie d'un cycle. Et comme les autres flèches vont dans un seul sens (cmd → httpapi → revisar → servicio), aujourd'hui il n'y en a aucun.

Le nom compte aussi : par convention, les paquets portent des noms en minuscules et d'un seul mot [17]. servicio dit ce qu'il y a dedans ; models ou types ne diraient que de quelle sorte de chose il s'agit.

Où va l'interface ?

L'interface vit dans le paquet qui l'utilise, pas dans celui qui l'implémente.

revisar interroge beaucoup de services à la fois, et pour cela il a besoin de quelqu'un qui en interroge un. Il le déclare sur place :

// Consultor es lo único que Todos necesita: alguien que consulte UN servicio y
// diga cómo lo encontró. Un método, y nada más.
type Consultor interface {
	Consultar(ctx context.Context, s servicio.Servicio) servicio.Estado
}

func Todos(ctx context.Context, c Consultor, servicios []servicio.Servicio, paralelo int) []servicio.Estado

L'interrogation réelle est un type concret, *revisar.HTTP, qui satisfait cette interface sans le déclarer. La doublure de test, consultorFalso, vit dans un _test.go et ne voyage pas avec le programme.

C'est ce que demande Go Code Review Comments, le guide de revue du projet lui-même : en général, les interfaces vont dans le paquet qui utilise les valeurs, pas dans celui qui les implémente [10]. C'est l'erreur 4 ci-dessous.

Et la porte HTTP ?

httpapi ne fait que traduire : de la requête à l'appel, et du résultat au JSON.

Elle a trois routes : GET /salud, GET /servicios et GET /servicios/{nombre}, avec le routeur de la bibliothèque standard. Depuis Go 1.22, net/http comprend la méthode et les jokers dans le motif, et répond à un POST par 405 Method Not Allowed, avec les méthodes valides dans l'en-tête Allow [12]. Mesuré sur le modèle : Allow: GET, HEAD.

Les erreurs qu'écrit le modèle sortent au format de la RFC 9457, Problem Details [13] ; celles que le routeur renvoie de lui-même, comme ce 405 ou une route qui n'existe pas, sortent en texte brut (mesuré). Sans type d'erreur propre, on utilise about:blank et le titre est la phrase standard du code, comme « Not Found » [13] :

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{"type":"about:blank","title":"Not Found","status":404,"detail":"No hay ningún servicio llamado \"nomina\".","instance":"/servicios/nomina"}

Encore une chose, parce qu'un modèle enseigne aussi de mauvaises habitudes : l'URL de chaque service sort sans utilisateur, mot de passe ni paramètres. https://usuario:clave@... ou ?token=... portent un secret, et ce qui sort par une API finit dans les journaux des autres. La configuration refuse le premier cas et la porte nettoie le second, chacun avec son test. Le chemin, lui, sort, parce qu'il dit ce qui est vérifié : un jeton glissé dans le chemin serait publié en entier.

Qu'est-ce que testdata/ et pourquoi ne gêne-t-il pas ?

La commande go ignore les dossiers nommés testdata : c'est l'endroit pour les données de support.

Il reste ainsi disponible pour les données dont les tests ont besoin [6]. Dans le modèle, il garde servicios.json, la liste de l'exemple, et un test la valide : si quelqu'un la casse, il s'en rend compte avant le premier lecteur.

Et où vont les dix lignes d'Iván ?

La règle va dans le domaine ; la porte ne fait que la publier.

« Prévenir si un service met plus d'une seconde » a deux parties. Décider de ce qui est lent relève du vocabulaire du programme, tout comme décider de ce qui est en bonne santé : cela va dans servicio, comme méthode de Estado à côté de OK(), et son test n'a pas besoin de réseau. Le dire vers l'extérieur est le travail de httpapi : un champ de plus dans son JSON. Si la seconde doit pouvoir changer sans recompiler, config la lit. Aucun nouveau dossier.

Le mardi, Iván a écrit Lento() dans servicio, son test, et un champ dans httpapi. Dix lignes, comme il le pensait. Aucune dans pkg/util.

Comment savoir que la frontière existe ?

Parce que la CI la casse exprès à chaque changement et exige que l'outil la rejette.

Une règle que personne ne vérifie est une règle dont personne ne sait si elle est encore en vie. C'est pourquoi le modèle fournit herramientas/probar-frontera.sh, avec deux expériences dans un dossier temporaire :

  1. Contrôle. Un module dont le chemin commence comme celui du modèle (…/estructura-go/control) importe internal/servicio. La règle se joue sur le chemin, donc elle le laisse passer : il doit compiler. S'il ne compile pas, l'expérience est mal montée et le script s'arrête au rouge.
  2. Violation. Un module au chemin étranger (ejemplo.com/intruso) importe le même paquet, par le même chemin d'accès. Il doit échouer, et avec le message de la règle.

Voici la sortie réelle :

1/2 control: github.com/HabilMX/estructura-go/control importa github.com/HabilMX/estructura-go/internal/servicio
   ✅ compiló: con la misma ruta de arriba, la regla lo deja pasar
2/2 violación: ejemplo.com/intruso importa github.com/HabilMX/estructura-go/internal/servicio
     package ejemplo.com/intruso
     	main.go:3:8: use of internal package github.com/HabilMX/estructura-go/internal/servicio not allowed
   ✅ la herramienta go lo rechazó por la regla de internal/
✅ frontera: se sostiene

Pourquoi le contrôle ? Parce qu'une expérience qui n'attend qu'un rouge passe aussi au « vert » quand elle échoue pour la mauvaise raison, comme un chemin mal écrit. Le contrôle cherche le paquet par le même chemin d'accès que la violation, donc un montage cassé le fait passer au rouge, lui (nous l'avons vérifié en cassant le chemin).

Et la violation exige le message exact de la règle, pas n'importe quel échec. Entre les deux étapes, seul change celui qui importe : la règle est démontrée des deux côtés.

La CI de GitHub exécute ce script avec gofmt, go vet, les tests avec le détecteur de concurrence et la compilation de cmd/, à chaque changement, à la main et chaque lundi, au cas où une nouvelle version de Go casserait quelque chose. Si le dépôt passe 60 jours sans activité, GitHub désactive l'exécution du lundi et il faut la réactiver [21].

Quelles sont les cinq erreurs les plus courantes ?

Les trois premières viennent de créer le dossier avant le problème qu'il résout ; les deux dernières, de mettre le code du mauvais côté d'une flèche.

1. Copier golang-standards/project-layout dans un petit projet

Le dépôt lui-même le déconseille pour cela.

Le README dit que, si tu apprends Go ou si tu fais une preuve de concept ou un projet simple, cette structure « is an overkill » [2]. Et il recommande de commencer avec un main.go et un go.mod [2]. Le dépôt a 19 dossiers à la racine [3]. Les copier tous, c'est hériter de dix-neuf questions sans réponse, comme cela est arrivé à Iván.

2. Créer pkg/ par habitude

Le guide officiel ne le mentionne pas, et Russ Cox a souligné que la grande majorité de l'écosystème ne l'utilise pas.

Dans l'issue #117 de ce dépôt, le 9 avril 2021, Russ Cox a écrit que « the vast majority of packages in the Go ecosystem do not put the importable packages in a pkg subdirectory » [4]. Le guide officiel n'utilise pkg/ dans aucun exemple [1]. Et un serveur n'a normalement rien à exporter, donc sa logique va dans internal/ [1].

3. Des paquets util, common ou misc

Un nom qui ne dit pas ce qu'il y a dedans devient un tiroir où tout tombe.

Le blog de Go l'explique dans Package names : « Packages named util, common, or misc provide clients with no sense of what the package contains » [9]. Avec le temps, ils accumulent des dépendances et ralentissent la compilation [9]. Go Code Review Comments y ajoute api, types et interfaces [10]. La sortie : diviser le tiroir et nommer chaque partie d'après ce qu'elle fait (fecha, notificar).

4. Déclarer l'interface là où elle est implémentée, « pour les mocks »

L'interface va du côté de celui qui l'utilise ; du côté qui implémente, on renvoie un type concret.

Go Code Review Comments le dit sans détour : « Do not define interfaces on the implementor side of an API “for mocking” » [10]. Et il demande de ne pas la déclarer avant de l'utiliser : sans usage réel, on ne sait pas de quelles méthodes elle a besoin [10]. Dans le modèle, revisar déclare Consultor parce qu'il l'utilise, et la doublure vit dans un _test.go.

5. Découper par couches jusqu'à fermer un cycle

Si models a besoin de quelque chose de services et que services importe models, le programme ne compile plus.

Cela arrive avec les dossiers par couche (handlers/, services/, models/) : tôt ou tard, celle du dessous a besoin de quelque chose de celle du dessus. La spécification l'interdit [11], et go build le dit ainsi (mesuré) :

package ejemplo.com/capas/models
	imports ejemplo.com/capas/services from models.go
	imports ejemplo.com/capas/models from services.go: import cycle not allowed

L'extrême inverse n'aide pas non plus : Andrew Gerrand avertissait en 2012 que découper en paquets trop petits te laisse coincé à concevoir des interfaces au lieu de finir le travail [16]. La sortie du modèle : un paquet de domaine qui n'importe personne, et les flèches vers lui.

5. Découper par couches jusqu'à fermer un cycle
ErreurCe qui la trahitSource
1. Copier le layout completDes dossiers vides ou avec un seul fichier[2]
2. pkg/ par habitudeUn serveur avec un pkg/ que personne n'importe[1][4]
3. util, common, miscDes fonctions sans aucun rapport entre elles[9][10]
4. Interface du côté qui implémenteUne interface avec une seule implémentation et un Mock à côté[10]
5. Des couches qui s'importent entre ellesimport cycle not allowed[11]

Exercice : peux-tu faire passer la frontière au rouge ?

Tu vas casser la frontière exprès, et l'outil te dira, tout seul, si tu as réussi.

Il te faut Go 1.27 ou plus récent (go version), Git et bash, environ 20 minutes. Si ton système fournit un Go plus ancien, comme les dépôts de Linux Mint, installe-le depuis go.dev [22]. Linux et macOS fournissent déjà bash ; sous Windows, utilise WSL ou Git Bash.

Étape 1. Clone et vérifie que tout est au vert.

git clone https://github.com/HabilMX/estructura-go
cd estructura-go
go test ./...
bash herramientas/probar-frontera.sh

Ce que tu dois voir : cinq lignes qui commencent par ok et, à la fin, ✅ frontera: se sostiene.

Étape 2. Prédis avant de toucher. Note ce qui se passe si tu renommes internal/ en interno/ et que tu corriges les imports. Est-ce que ça compile ? Que dit le script ?

Étape 3. Renomme et corrige les imports.

Copie seulement la ligne sed de ton système : celui de macOS demande un argument vide après -i.

mv internal interno

# En Linux:
grep -rl 'estructura-go/internal/' --include='*.go' . | xargs sed -i 's#estructura-go/internal/#estructura-go/interno/#'

# En macOS:
grep -rl 'estructura-go/internal/' --include='*.go' . | xargs sed -i '' 's#estructura-go/internal/#estructura-go/interno/#'

go build ./...

Si go build ne dit rien, ça a compilé : le programme n'a pas besoin de internal/ pour fonctionner.

Étape 4. Lance le script.

bash herramientas/probar-frontera.sh

Il doit sortir au rouge, mais regarde lequel :

❌ el control NO compiló; el experimento está mal montado:

Le script cherche toujours internal/servicio, qui n'existe plus, et il a refusé de te donner un vert. C'est le contrôle qui travaille.

Étape 5. Pointe le script vers le nouveau paquet. Ouvre herramientas/probar-frontera.sh, cherche la ligne qui commence par paquete= et remplace internal par interno. Relance le script :

❌ COMPILÓ: otro módulo pudo importar github.com/HabilMX/estructura-go/interno/servicio. La frontera no existe.

Cette fois, oui : un module étranger a importé ton code et personne ne l'a arrêté. Sous un autre nom, le dossier n'est qu'un dossier.

Étape 6. Remets tout en place.

git checkout -- . && git clean -fd
bash herramientas/probar-frontera.sh

Attention : git clean -fd efface aussi les nouveaux fichiers que tu as créés là, comme tes notes.

Comment savoir que tu as réussi : tu as vu trois issues différentes en quatre exécutions, dans cet ordre : vert à l'étape 1, rouge « el control NO compiló » à la 4, rouge « COMPILÓ » à la 5, et de nouveau vert à la 6. Si à l'étape 5 « el control NO compiló » se répète, la ligne paquete= n'est pas restée avec interno (ou tu n'as pas enregistré). Si ça sort au vert, internal/ existe encore : revois l'étape 3.

Pour aller plus loin (facultatif) : ajoute à servicio une fonction qui utilise quelque chose de revisar. Avant de compiler, prédis le message. Puis compare-le avec celui de l'erreur 5.

À retenir

Trois questions à poser à ton dépôt cette semaine.

  1. Pour chaque dossier de la racine : quel problème résout-il aujourd'hui ? Si la réponse est « au cas où, un jour », supprime-le et note-le dans le README.
  2. Si une autre équipe importait ton code demain, quels paquets la laisserais-tu utiliser ? Tout le reste est-il dans internal/ ?
  3. Quel est le paquet dont tous dépendent ? Importe-t-il quelque chose du projet ? S'il importe quelque chose, c'est là qu'est le prochain cycle.

Et ensuite ?

En Go, c'est l'outil qui surveille la frontière. En Python, c'est l'installation, et il est facile de ne pas s'en rendre compte jusqu'à ce que ça casse.

Trois jours plus tard, Iván déplaçait déjà des dossiers sans crainte. Puis on lui a confié un projet Python. Les tests, au vert. Sa collègue a installé le paquet : ModuleNotFoundError.

Le fichier existait. Il était dans le dossier. Les tests le trouvaient. Mais il n'était jamais entré dans le paquet.

Comment un test peut-il passer sur un code qui n'existe pas pour celui qui l'installe ? Dans l'épisode 2 : un dossier nommé src/, et pourquoi il change tout.

Le modèle et le cours

  • Modèle : github.com/HabilMX/estructura-go. Il s'utilise comme modèle GitHub ou se clone [19]. Le README explique chaque dossier, ce qu'il ne contient pas, quand ajouter chaque chose et comment retirer l'exemple. Le code est sous MIT ; le texte et les explications, sous CC BY-SA 4.0.
  • Versions : go.mod demande Go 1.27, la version sortie en août 2026 [15]. Il a été testé avec Go 1.27.3, la version stable la plus récente au 22 octobre [14], et n'utilise que la bibliothèque standard. La CI prend la version de go.mod et cherche le correctif le plus récent de la 1.27 [18].
  • Cours de Go de Hábil : si internal/, les paquets ou les tests de table sont nouveaux pour toi, la leçon 5 les présente depuis zéro : Cours de Go · Leçon 5, Modules et tests.

J'écris à partir de ce que nous voyons tous les jours chez Hábil, où nous construisons des systèmes pour la banque, l'assurance et les entreprises au Mexique. Si, au travail ou à l'école, ton équipe tombe sur un dépôt que personne ne sait par où prendre, parlons-en : habil.mx

Que fait Hábil ?

Nous enseignons la programmation avec ce que nous utilisons pour construire : du code testé, expliqué et ouvert.

Cette série naît du même endroit que nos cours. Le Cours de Go va de zéro à l'écriture d'un bon programme, et nous le publions en accès libre, sous licence Creative Commons ; sa leçon 5 présente les modules, les paquets et les tests qu'utilise le modèle de cet article. Et le catalogue compte d'autres cours de programmation, gratuits, avec du code testé et expliqué pas à pas.

Découvre le Cours de Go et le catalogue des Cours de programmation.

Si ton équipe hérite d'un système que personne ne sait par où prendre, ou doit en construire un qui puisse continuer à être maintenu, nous le faisons aussi : découvre Construire votre système sur mesure. Demande ton premier échange, sans frais. Nous y examinons s'il convient de configurer ce que ton équipe a déjà, d'utiliser un produit ou de construire, et ce qu'elle recevrait dans chaque cas. Écris-nous sur WhatsApp.

Sources consultées le 22 octobre 2026. Les citations en anglais sont reproduites telles quelles, sans les italiques ni le gras de l'original.

Références

  1. Go Team, « Organizing a Go module », go.dev. https://go.dev/doc/modules/layout
  2. golang-standards, « Standard Go Project Layout » (README), GitHub. https://github.com/golang-standards/project-layout
  3. GitHub, données du dépôt golang-standards/project-layout (56 702 étoiles ; 19 dossiers à la racine ; premier par nombre d'étoiles dans la recherche « go project layout », 812 résultats, le suivant avec 954), mesurées le 22 octobre 2026. https://api.github.com/repos/golang-standards/project-layout
  4. Russ Cox, « this is not a standard Go project layout », issue #117, 9 avril 2021. https://github.com/golang-standards/project-layout/issues/117
  5. Russ Cox, commentaire dans l'issue #117, 28 avril 2021. https://github.com/golang-standards/project-layout/issues/117#issuecomment-828503689
  6. Go Team, « Command go » : « Internal packages » et « Test packages », pkg.go.dev. https://pkg.go.dev/cmd/go
  7. Go Team, « Go 1.4 Release Notes » : « Internal packages », go.dev. https://go.dev/doc/go1.4
  8. Go Team, « Go 1.5 Release Notes », go.dev. https://go.dev/doc/go1.5
  9. Sameer Ajmani, « Package names », The Go Blog, 4 février 2015. https://go.dev/blog/package-names
  10. Go Team, « Go Code Review Comments » : « Interfaces » et « Package Names », go.dev. https://go.dev/wiki/CodeReviewComments
  11. Go Team, « The Go Programming Language Specification » : « Import declarations » (version go1.27, 26 mai 2026). https://go.dev/ref/spec#Import_declarations
  12. Jonathan Amsterdam, « Routing Enhancements for Go 1.22 », The Go Blog, 13 février 2024. https://go.dev/blog/routing-enhancements
  13. M. Nottingham, E. Wilde et S. Dalal, « RFC 9457: Problem Details for HTTP APIs », IETF, juillet 2023. https://www.rfc-editor.org/rfc/rfc9457
  14. Go Team, téléchargements de Go (version stable en vigueur : go1.27.3). https://go.dev/dl/
  15. Go Team, « Go 1.27 Release Notes », go.dev. https://go.dev/doc/go1.27
  16. Andrew Gerrand, « Organizing Go code », The Go Blog, 16 août 2012. https://go.dev/blog/organizing-go-code
  17. Go Team, « Effective Go » : « Package names », go.dev. https://go.dev/doc/effective_go#package-names
  18. GitHub, « actions/setup-go » v5, README. https://github.com/actions/setup-go/blob/v5/README.md
  19. GitHub Docs, « Creating a template repository ». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
  20. Go Team, « Go Modules Reference » : « Vendoring », go.dev. https://go.dev/ref/mod#vendoring
  21. GitHub Docs, « Disabling and enabling a workflow ». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
  22. Go Team, « Download and install », go.dev. https://go.dev/doc/install

Votre activité fait face à ces défis ?

Parlons de votre cas

Vous préférez l'e-mail ? Écrivez-nous à hola@habil.mx