# Go : Les bases pour développeurs Java/Python en 2026 > Apprenez Go rapidement en partant de vos acquis Java ou Python. Goroutines, channels, interfaces et patterns essentiels expliqués pour une transition fluide. - Published: 2026-01-19 - Updated: 2026-03-28 - Author: SharpSkill - Tags: go, golang, concurrency, goroutines, backend - Reading time: 12 min --- Go (ou Golang) s'est imposé comme le langage de choix pour les microservices, les CLI et les systèmes distribués. Créé par Google en 2009, il combine la simplicité de Python avec les performances du C. Pour un développeur venant de Java ou Python, la transition vers Go est étonnamment fluide une fois les concepts clés assimilés. > **Pourquoi Go en 2026 ?** > > Go alimente Docker, Kubernetes, Terraform et de nombreuses infrastructures cloud. Sa compilation rapide, sa gestion native de la concurrence et son déploiement en binaire unique en font un choix idéal pour le backend moderne. ## Installation et configuration de Go L'installation de Go est simple et uniforme sur toutes les plateformes. L'outil `go` intègre compilation, gestion des dépendances et tests. ```bash # install.sh # Installation sur macOS avec Homebrew brew install go # Installation sur Linux (Ubuntu/Debian) sudo apt update && sudo apt install golang-go # Vérification de l'installation go version # go version go1.22.0 linux/amd64 ``` La structure d'un projet Go suit des conventions strictes mais simples. Le fichier `go.mod` définit le module et ses dépendances. ```bash # project-setup.sh # Création d'un nouveau projet mkdir mon-projet && cd mon-projet go mod init github.com/user/mon-projet # Structure générée : # mon-projet/ # ├── go.mod # Manifeste du module # └── main.go # Point d'entrée # Commandes essentielles go build # Compile le projet go run main.go # Compile et exécute go test ./... # Lance tous les tests go fmt ./... # Formate le code automatiquement ``` ## Premier programme Go Voici un programme simple qui illustre la syntaxe de base de Go. La comparaison avec Java et Python aide à visualiser les différences. ```go // main.go package main import "fmt" // Point d'entrée du programme func main() { // Déclaration avec inférence de type message := "Hello, Go!" fmt.Println(message) // Déclaration explicite var count int = 42 fmt.Printf("Count: %d\n", count) } ``` Ce qui frappe immédiatement : pas de point-virgule, pas de parenthèses autour des conditions, et l'inférence de type avec `:=`. Go privilégie la concision sans sacrifier la lisibilité. ## Variables et types fondamentaux Go est statiquement typé mais offre une excellente inférence de types. Les types de base couvrent la majorité des besoins. ```go // types.go package main import "fmt" func main() { // Déclaration courte (dans les fonctions uniquement) name := "Alice" // string age := 30 // int height := 1.75 // float64 active := true // bool // Déclaration explicite var score int = 100 var rate float64 = 3.14 // Déclaration multiple var ( firstName string = "Bob" lastName string = "Smith" points int = 0 ) // Zéro-valeurs (valeurs par défaut) var count int // 0 var text string // "" (chaîne vide) var flag bool // false var ptr *int // nil fmt.Println(name, age, height, active) } ``` > **Zéro-valeurs en Go** > > Contrairement à Java ou Python, Go initialise automatiquement les variables à leur "zéro-valeur" : 0 pour les nombres, "" pour les strings, false pour les bools, nil pour les pointeurs et slices. ## Fonctions et retours multiples Go permet de retourner plusieurs valeurs, une fonctionnalité utilisée massivement pour la gestion des erreurs. ```go // functions.go package main import ( "errors" "fmt" ) // Fonction simple avec paramètres typés func add(a, b int) int { return a + b } // Retours multiples (pattern idiomatique pour les erreurs) func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } // Retours nommés func getUser(id int) (name string, age int, err error) { if id <= 0 { err = errors.New("invalid ID") return } name = "Alice" age = 30 return } // Fonction variadique func sum(numbers ...int) int { total := 0 for _, n := range numbers { total += n } return total } func main() { // Appel simple result := add(5, 3) fmt.Println("5 + 3 =", result) // Gestion d'erreur explicite quotient, err := divide(10, 3) if err != nil { fmt.Println("Error:", err) return } fmt.Printf("10 / 3 = %.2f\n", quotient) // Ignorer une valeur retournée avec _ name, _, _ := getUser(1) fmt.Println("User:", name) // Appel variadique total := sum(1, 2, 3, 4, 5) fmt.Println("Sum:", total) } ``` ## Structures et méthodes Les structs sont les blocs de construction des types personnalisés en Go. Les méthodes s'attachent aux types via des receivers. ```go // structs.go package main import "fmt" // Définition d'une struct type User struct { ID int Username string Email string Active bool } // Constructeur (convention : NewTypeName) func NewUser(id int, username, email string) *User { return &User{ ID: id, Username: username, Email: email, Active: true, } } // Méthode avec receiver valeur (copie) func (u User) FullInfo() string { status := "inactive" if u.Active { status = "active" } return fmt.Sprintf("%s <%s> (%s)", u.Username, u.Email, status) } // Méthode avec receiver pointeur (modification possible) func (u *User) Deactivate() { u.Active = false } // Méthode avec receiver pointeur pour modification func (u *User) UpdateEmail(newEmail string) { u.Email = newEmail } func main() { // Création avec constructeur user := NewUser(1, "alice", "alice@example.com") fmt.Println(user.FullInfo()) // Modification via méthode user.Deactivate() fmt.Println(user.FullInfo()) // Création directe user2 := User{ ID: 2, Username: "bob", Email: "bob@example.com", } fmt.Println(user2.FullInfo()) } ``` > **Receiver valeur vs pointeur** > > Utiliser un receiver pointeur (`*User`) quand la méthode modifie l'état ou quand la struct est volumineuse. Utiliser un receiver valeur (`User`) pour les méthodes en lecture seule sur des structs légères. ## Interfaces : polymorphisme implicite Les interfaces Go sont implémentées implicitement. Un type satisfait une interface s'il implémente toutes ses méthodes, sans déclaration explicite. ```go // interfaces.go package main import ( "fmt" "math" ) // Définition d'une interface type Shape interface { Area() float64 Perimeter() float64 } // Rectangle implémente Shape implicitement type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) } // Circle implémente aussi Shape type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius } // Fonction acceptant l'interface func PrintShapeInfo(s Shape) { fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter()) } func main() { rect := Rectangle{Width: 10, Height: 5} circle := Circle{Radius: 7} // Polymorphisme via interface PrintShapeInfo(rect) PrintShapeInfo(circle) // Slice d'interfaces shapes := []Shape{rect, circle} for _, shape := range shapes { PrintShapeInfo(shape) } } ``` Cette approche est radicalement différente de Java où `implements` est obligatoire. En Go, la conformité est structurelle et non nominale. ## Slices et Maps : collections dynamiques Les slices sont des vues dynamiques sur des arrays, et les maps sont des tables de hachage natives. ```go // collections.go package main import "fmt" func main() { // Slice : tableau dynamique numbers := []int{1, 2, 3, 4, 5} // Ajouter des éléments numbers = append(numbers, 6, 7) // Slicing (similaire à Python) subset := numbers[1:4] // [2, 3, 4] fmt.Println("Subset:", subset) // Créer un slice avec make scores := make([]int, 0, 10) // len=0, cap=10 scores = append(scores, 100, 95, 88) // Itération avec range for index, value := range numbers { fmt.Printf("numbers[%d] = %d\n", index, value) } // Map : table de hachage users := map[string]int{ "alice": 30, "bob": 25, } // Ajouter/Modifier users["charlie"] = 35 // Vérifier l'existence age, exists := users["alice"] if exists { fmt.Println("Alice's age:", age) } // Supprimer delete(users, "bob") // Itération sur map for name, age := range users { fmt.Printf("%s is %d years old\n", name, age) } } ``` ## Gestion des erreurs idiomatique Go n'a pas d'exceptions. Les erreurs sont des valeurs retournées explicitement, ce qui force une gestion rigoureuse. ```go // errors.go package main import ( "errors" "fmt" "os" ) // Erreur sentinelle (pour comparaison) var ErrNotFound = errors.New("resource not found") var ErrInvalidInput = errors.New("invalid input") // Erreur custom avec contexte type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message) } // Fonction retournant différents types d'erreurs func GetUser(id int) (string, error) { if id <= 0 { return "", &ValidationError{ Field: "id", Message: "must be positive", } } if id > 1000 { return "", ErrNotFound } return "Alice", nil } // Wrapping d'erreurs (Go 1.13+) func ReadConfig(path string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading config %s: %w", path, err) } return data, nil } func main() { // Pattern de base user, err := GetUser(-1) if err != nil { fmt.Println("Error:", err) // Type assertion pour erreur custom var valErr *ValidationError if errors.As(err, &valErr) { fmt.Printf("Field: %s\n", valErr.Field) } // Comparaison avec erreur sentinelle if errors.Is(err, ErrNotFound) { fmt.Println("User not found") } } else { fmt.Println("User:", user) } } ``` > **errors.Is et errors.As** > > Depuis Go 1.13, utiliser `errors.Is()` pour comparer avec des erreurs sentinelles et `errors.As()` pour extraire un type d'erreur spécifique d'une chaîne d'erreurs wrappées. ## Goroutines : concurrence légère Les goroutines sont des threads légers gérés par le runtime Go. Lancer une goroutine coûte seulement quelques Ko de mémoire. ```go // goroutines.go package main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // Décrémente le compteur à la fin fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup // Lancer 5 goroutines for i := 1; i <= 5; i++ { wg.Add(1) go worker(i, &wg) // Préfixe 'go' lance la goroutine } // Attendre la fin de toutes les goroutines wg.Wait() fmt.Println("All workers completed") } ``` Le `sync.WaitGroup` permet d'attendre la fin de plusieurs goroutines. C'est le pattern de base pour le parallélisme en Go. ## Channels : communication entre goroutines Les channels sont des conduits typés pour la communication entre goroutines. Ils permettent de synchroniser et d'échanger des données de manière sûre. ```go // channels.go package main import ( "fmt" "time" ) func producer(ch chan<- int) { for i := 1; i <= 5; i++ { fmt.Println("Producing:", i) ch <- i // Envoie sur le channel time.Sleep(100 * time.Millisecond) } close(ch) // Ferme le channel quand terminé } func consumer(ch <-chan int, done chan<- bool) { for value := range ch { // Itère jusqu'à fermeture fmt.Println("Consuming:", value) } done <- true } func main() { ch := make(chan int) // Channel non bufferisé done := make(chan bool) go producer(ch) go consumer(ch, done) <-done // Attend la fin du consumer fmt.Println("All done") } ``` ### Channels bufferisés et select ```go // channels_advanced.go package main import ( "fmt" "time" ) func main() { // Channel bufferisé (capacité 3) buffered := make(chan int, 3) buffered <- 1 buffered <- 2 buffered <- 3 // buffered <- 4 // Bloquerait car buffer plein fmt.Println(<-buffered) // 1 // Select : multiplexage de channels ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(100 * time.Millisecond) ch1 <- "from ch1" }() go func() { time.Sleep(200 * time.Millisecond) ch2 <- "from ch2" }() // Attend le premier message disponible for i := 0; i < 2; i++ { select { case msg1 := <-ch1: fmt.Println("Received:", msg1) case msg2 := <-ch2: fmt.Println("Received:", msg2) case <-time.After(500 * time.Millisecond): fmt.Println("Timeout!") } } } ``` > **Philosophie Go : CSP** > > Go suit le modèle CSP (Communicating Sequential Processes) : "Ne communiquez pas en partageant la mémoire, partagez la mémoire en communiquant." Les channels évitent les race conditions. ## Tests en Go Go intègre un framework de test minimaliste mais efficace. Les fichiers de test se terminent par `_test.go`. ```go // calculator.go package calculator func Add(a, b int) int { return a + b } func Divide(a, b int) (int, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } ``` ```go // calculator_test.go package calculator import ( "testing" ) // Test de base func TestAdd(t *testing.T) { result := Add(2, 3) expected := 5 if result != expected { t.Errorf("Add(2, 3) = %d; want %d", result, expected) } } // Tests table-driven (pattern recommandé) func TestAddTableDriven(t *testing.T) { tests := []struct { name string a, b int expected int }{ {"positive numbers", 2, 3, 5}, {"negative numbers", -2, -3, -5}, {"zero", 0, 0, 0}, {"mixed", -5, 10, 5}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Add(tt.a, tt.b) if result != tt.expected { t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected) } }) } } // Test d'erreur func TestDivideByZero(t *testing.T) { _, err := Divide(10, 0) if err == nil { t.Error("Expected error for division by zero") } } // Benchmark func BenchmarkAdd(b *testing.B) { for i := 0; i < b.N; i++ { Add(100, 200) } } ``` Les tests s'exécutent avec `go test ./...` et les benchmarks avec `go test -bench=.`. ## HTTP : serveur web minimaliste Go excelle dans la création de serveurs HTTP performants grâce à sa bibliothèque standard. ```go // server.go package main import ( "encoding/json" "log" "net/http" ) type User struct { ID int `json:"id"` Name string `json:"name"` } func main() { // Route simple http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, Go!")) }) // Route JSON http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) { users := []User{ {ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(users) }) // Route avec méthode http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": w.Write([]byte("Get user")) case "POST": w.Write([]byte("Create user")) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } }) log.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } ``` Ce serveur gère des milliers de connexions simultanées grâce aux goroutines. Chaque requête est traitée dans sa propre goroutine automatiquement. ## Conclusion Go offre une approche pragmatique du développement backend : syntaxe simple, compilation rapide, concurrence native et excellent outillage. Pour les développeurs Java ou Python, la transition demande d'accepter quelques conventions différentes (gestion explicite des erreurs, pas de génériques complexes avant Go 1.18), mais les bénéfices en performance et maintenabilité sont immédiats. ### Checklist pour bien démarrer - ✅ Installer Go via le site officiel ou gestionnaire de paquets - ✅ Maîtriser les commandes `go build`, `go run`, `go test`, `go fmt` - ✅ Comprendre la différence entre slices et arrays - ✅ Adopter le pattern `if err != nil` pour la gestion des erreurs - ✅ Utiliser goroutines et channels pour la concurrence - ✅ Écrire des tests table-driven avec le package `testing` L'écosystème Go est mature avec des frameworks populaires comme Gin, Echo et Fiber pour le web, et des outils comme Cobra pour les CLI. Avec ces bases solides, l'exploration de sujets avancés comme les génériques (Go 1.18+), le context package et les patterns de concurrence devient accessible. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/go/go-bases-developpeurs-java-python