Go dans la grande entreprise : pourquoi ce n'est plus simplement « un langage pour microservices »
\n\nQuand vous entendez « Enterprise Go », qu'est-ce qui vous vient à l'esprit ? Des règlements d'entreprise ennuyeux ? Des revues de code interminables ? Ou peut-être le sentiment que Go est réservé aux startups et au DevOps ?
\n\nEn réalité, Go dans l'entreprise est un tout autre niveau de discipline et de culture d'ingénierie. Dans les grandes entreprises (Uber, Dropbox, Salesforce, Twitch), on écrit avec Go non seulement des microservices, mais des plateformes entières traitant des millions de requêtes par seconde. Et ici, « ça marche » ne suffit pas.
\n\nDans cet article, nous allons analyser les principales particularités du développement Go dans l'entreprise : de la conception des packages à la journalisation en production. Il y aura beaucoup de code et un minimum de blabla.
\n\n1. Architecture : ne répétez pas l'erreur du « monolithe dans les microservices »
\n\nLe problème le plus fréquent que je vois dans les projets Go de niveau enterprise est la tentative de transposer une architecture de style Java à Go. Résultat : des centaines d'interfaces inutiles et des couches d'abstraction qui ne font que ralentir le développement.
\n\nRègle « Package par fonctionnalité, pas par couche »
\n\nAu lieu de créer des dossiers /models, /handlers, /services — regroupez par domaines métier :
myapp/\n├── cmd/\n│ └── server/\n│ └── main.go\n├── internal/\n│ ├── user/ # Tout ce qui concerne les utilisateurs\n│ │ ├── handler.go\n│ │ ├── service.go\n│ │ ├── repository.go\n│ │ └── user.go # Modèle de données\n│ ├── payment/ # Tout ce qui concerne les paiements\n│ │ ├── handler.go\n│ │ ├── service.go\n│ │ ├── gateway.go\n│ │ └── payment.go\n│ └── pkg/ # Utilitaires communs\n└── go.mod\n\nCette approche est l'une des bonnes pratiques Go pour l'entreprise. Elle réduit le couplage et rend le code prévisible. Lorsqu'un nouveau développeur ouvre le projet, il voit immédiatement la logique métier, et non des couches d'abstraction.
\n\nInterfaces : acceptez des interfaces, retournez des structures
\n\nC'est un conseil classique de la communauté Go, mais dans l'entreprise, il est crucial. Les interfaces doivent être petites (1-2 méthodes) et définies là où elles sont utilisées, et non là où elles sont implémentées.
\n\n// MAUVAIS : interface définie à côté de l'implémentation\npackage user\n\ntype UserRepository interface {\n FindByID(id string) (*User, error)\n Save(user *User) error\n}\n\ntype postgresRepo struct { ... }\n\n// BON : interface là où elle est nécessaire\npackage user\n\ntype Storage interface {\n Get(id string) (*User, error)\n Put(user *User) error\n}\n\ntype service struct {\n db Storage\n}\n\nfunc NewService(db Storage) *service {\n return &service{db: db}\n}\n\nPourquoi est-ce important ? Parce que dans l'entreprise, vous changez souvent de backend (PostgreSQL -> CockroachDB, Redis -> KeyDB). Les interfaces définies du côté du consommateur permettent de le faire sans douleur.
\n\n2. Gestion des erreurs : arrêtez d'ignorer _
\n\nGo n'a pas d'exceptions, et c'est parfait. Mais dans les projets d'entreprise, les erreurs ne sont pas simplement if err != nil. C'est toute une culture.
Wrapper ou ne pas wrapper ?
\n\nUtilisez fmt.Errorf avec %w pour créer des chaînes d'erreurs. Cela permet de conserver la pile d'appels et simplifie le débogage.
func (s *service) ProcessOrder(ctx context.Context, orderID string) error {\n order, err := s.orderRepo.Get(ctx, orderID)\n if err != nil {\n return fmt.Errorf("getting order %s: %w", orderID, err)\n }\n\n if err := s.paymentGateway.Charge(ctx, order.Amount); err != nil {\n return fmt.Errorf("charging for order %s: %w", orderID, err)\n }\n\n return nil\n}\n\nAu niveau supérieur (dans le handler/middleware), utilisez errors.Is() et errors.As() pour vérifier les types d'erreurs :
if errors.Is(err, ErrNotFo