f in x
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Sviluppo di siti web

Go (Golang) per Backend — Concorrenza, API REST e Microservizi per Sviluppatori che Producono

[2026-06-22] Author: Ing. Calogero Bono

Hai un servizio che deve gestire migliaia di richieste al secondo, oppure un microservizio che deve partire e rispondere in millisecondi. Hai provato Node.js, Python, PHP, ma la concorrenza ti ha fatto impazzire tra callback, event loop e GIL. Go (Golang) è nato per risolvere esattamente questo: un linguaggio compilato, tipizzato, con concorrenza nativa e una libreria standard che sembra cucita addosso a chi costruisce backend. Noi, di Meteora Web, lo usiamo in produzione da anni per piattaforme proprietarie e API. In questa pillar ti portiamo tutto quello che serve per passare dalle prime righe di codice a un backend scalabile e manutenibile.

Perché Go per il backend è una scelta che paga?

Go è stato progettato in Google per risolvere i problemi della programmazione di sistema e dei servizi di rete. Non ha classi, non ha ereditarietà, ma ha interfacce implicite, goroutine leggere e un compilatore velocissimo. Per un backend vuol dire:

  • Performance: i binari compilati girano senza VM, consumano poca memoria.
  • Concorrenza semplice: goroutine e channel sono parte del linguaggio, non una libreria esterna.
  • Deploy facile: un singolo eseguibile statico, nessuna dipendenza runtime.
  • Ecosistema solido: net/http, database/sql, encoding/json sono nella stdlib.

Noi abbiamo scelto Go per la piattaforma di gestione social dei nostri clienti proprio per questi motivi: concorrenza per pubblicare su multipli canali in parallelo, basso consumo di RAM sui server, e un binario che si aggiorna con una semplice sostituzione di file.

Sponsored Protocol

Come installare Go e avviare il primo progetto backend?

Se non hai mai scritto una riga di Go, il setup è lineare. Scarica l'archivio da golang.org, estrai in /usr/local e aggiungi al PATH. Poi verifica:

$ go version
go version go1.22.4 linux/amd64

Il workspace moderno in Go si basa sui moduli. Non serve più $GOPATH. Crea una cartella e inizializza un modulo:

$ mkdir mio-backend && cd mio-backend
$ go mod init github.com/meteoraweb/mio-backend

Ora scrivi il primo server HTTP. Crea main.go:

Sponsored Protocol

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Ciao da Meteora Web!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Esegui go run main.go e apri http://localhost:8080. Hai un backend funzionante con meno di 10 righe. Nessun framework, nessun package manager da installare.

Quali sono i tipi fondamentali di Go per il backend?

In Go tutto ruota attorno a struct, interface, slice, map e puntatori. Se arrivi da linguaggi OOP devi abbandonare l'idea di classi e ereditarietà, ma in cambio ottieni composizione e interfaces implicite.

Struct e interfacce

type Utente struct {
    Nome  string
    Email string
}

type Salvatore interface {
    Salva() error
}

func (u Utente) Salva() error {
    // scrivi su database
    return nil
}

Una struct soddisfa un'interfaccia automaticamente se implementa i suoi metodi. Niente dichiarazioni esplicite. Questo rende il testing semplice: basta creare un mock che implementi la stessa interfaccia.

Sponsored Protocol

Slice, map e puntatori

Le slice sono array dinamici. Le map sono hashmap built-in. I puntatori servono per condividere dati senza copiarli, ma attenzione alla concorrenza.

utenti := []Utente{{Nome: "Alice"}, {Nome: "Bob"}}
mappa := map[string]int{"Alice": 30, "Bob": 25}
p := &utenti[0] // puntatore al primo elemento

Una regola pratica: se una funzione modifica un parametro, passa un puntatore. Se non lo modifica, passa per valore.

Come funziona la concorrenza in Go per il backend?

Qui sta il vero superpotere di Go. Le goroutine sono funzioni che girano in modo concorrente, gestite dal runtime. Un singolo processo Go può gestire milioni di goroutine senza esplodere di memoria.

Goroutine e channel

func main() {
    ch := make(chan string)
    go func() {
        ch <- "Hello"
    }()
    msg := <-ch
    fmt.Println(msg)
}

I channel sono il modo idiomatico per comunicare tra goroutine. Evitano lock espliciti e race condition se usati correttamente.

Pattern di concorrenza

Un classico worker pool:

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 10)
    results := make(chan int, 10)
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)
    for r := 1; r <= 5; r++ {
        <-results
    }
}

Nel backend usiamo questo pattern per processare webhook, inviare email in batch, o chiamare API esterne in parallelo. Con Python avremmo dovuto usare asyncio o thread con lock; in Go è nativo e performante.

Come costruire un HTTP server in Go per il backend?

La libreria net/http è completa. Puoi fare tutto senza framework, ma per progetti complessi ti conviene usare un multiplexer più flessibile come httprouter o il pattern di middleware.

net/http e mux di default

mux := http.NewServeMux()
mux.HandleFunc("/api/utenti", getUtenti)
http.ListenAndServe(":8080", mux)

Il default mux gestisce path fissi, ma non ha supporto per parametri URL (es. /utenti/{id}). Per quello devi usare Go 1.22 che ha introdotto il pattern matching, o un router esterno.

Middleware e routing

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

mux := http.NewServeMux()
// ... route
handler := loggingMiddleware(mux)
http.ListenAndServe(":8080", handler)

Noi usiamo una pila di middleware per logging, autenticazione, rate limiting e CORS. Go rende facile concatenarli senza ereditarietà.

Come progettare API REST in Go per il backend?

Se devi costruire API RESTful, hai due strade: usare la stdlib con un router custom, o adottare un framework leggero come Gin o Echo. Noi abbiamo usato entrambi; la scelta dipende dalla complessità del progetto.

Framework Gin vs Echo

Gin è più performante (basato su httprouter), ha un DSL più ricco per validazione e binding. Echo è altrettanto veloce e ha una sintassi molto pulita. Entrambi sono testati in produzione da migliaia di progetti.

// Esempio con Gin
import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/utenti/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.JSON(200, gin.H{"id": id})
    })
    r.Run()
}

Best practices che applichiamo in ogni progetto:

  • Versiona l'API (es. /api/v1/...).
  • Usa JSON per input/output.
  • Restituisci errori strutturati con codice e messaggio.
  • Documenta con OpenAPI/Swagger automaticamente.

Ricorda: un'API REST in Go deve essere progettata per fallire bene. Non lasciare che il server muoia per un errore di parsing.

Come gestire gli errori in Go per un backend robusto?

Go tratta gli errori come valori. Non ci sono eccezioni. Ogni funzione che può fallire restituisce un errore come ultimo valore di ritorno.

Errori come valori

f, err := os.Open("file.txt")
if err != nil {
    return fmt.Errorf("apertura file: %w", err)
}

Il pattern moderno è usare fmt.Errorf con %w per creare errori wrappati. Poi puoi usare errors.Is e errors.As per verificare la causa sottostante.

Sentinel errors e wrapping

var ErrNotFound = errors.New("risorsa non trovata")

func GetUtente(id int) (Utente, error) {
    if id <= 0 {
        return Utente{}, ErrNotFound
    }
    // ...
}

Nel backend di una piattaforma, ogni strato deve propagare l'errore aggiungendo contesto. Lo strato HTTP poi decide se restituire 404, 500, ecc. Non ingoiare mai un errore senza loggarlo.

Come gestire le dipendenze in Go per il backend?

Go modules sono lo standard dal 2019. Ogni progetto ha un file go.mod che elenca le dipendenze e la versione di Go. Aggiungi una dipendenza con:

$ go get github.com/gin-gonic/gin@v1.9.1

Poi esegui go mod tidy per pulire. Il file go.sum garantisce l'integrità dei moduli scaricati. Non c'è un package manager centralizzato: tutto è basato su repository Git e moduli.

Consigli pratici:

  • Mantieni il go.mod pulito con go mod tidy regolarmente.
  • Non committare la cartella vendor/ se non necessario; moduli scaricati on-demand bastano.
  • Usa versioni semver e blocca le dipendenze critiche.

Come testare il codice backend in Go?

Go ha un test runner integrato. I file di test finiscono in _test.go. Le funzioni di test iniziano con Test e prendono un *testing.T.

Test unitari e table-driven tests

func TestSomma(t *testing.T) {
    testCases := []struct {
        a, b, expected int
    }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
    }
    for _, tc := range testCases {
        result := Somma(tc.a, tc.b)
        if result != tc.expected {
            t.Errorf("Somma(%d,%d) = %d; expected %d", tc.a, tc.b, result, tc.expected)
        }
    }
}

Questo pattern riduce la duplicazione e rende facile aggiungere casi. Per API, usa httptest.NewServer per testare gli handler senza alzare il server reale.

Benchmark

Go rende banale scrivere benchmark:

func BenchmarkSomma(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Somma(1, 2)
    }
}

Esegui con go test -bench=.. Misurare le performance aiuta a scegliere tra un approccio e l'altro, specialmente nei colli di bottiglia del backend.

Come interagire con i database in Go per il backend?

Go offre database/sql come interfaccia generica per database relazionali. I driver specifici (MySQL, PostgreSQL, SQLite) si importano con side effect.

database/sql e GORM

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

db, err := sql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/dbname")
if err != nil { log.Fatal(err) }
defer db.Close()

rows, err := db.Query("SELECT id, nome FROM utenti")
if err != nil { log.Fatal(err) }
for rows.Next() {
    var id int
    var nome string
    rows.Scan(&id, &nome)
}

GORM è un ORM popolare che astrae le query. Noi lo usiamo per progetti con relazioni complesse, ma per operazioni critiche preferiamo SQL diretto per performance prevedibili. Controlla sempre il connection pooling: Go di default è configurato male (nessun limite). Imposta db.SetMaxOpenConns(25) e db.SetMaxIdleConns(10) per evitare di saturare il database. Approfondisci con la nostra guida SQL e database relazionali.

Come progettare microservizi in Go per il backend?

Go è il linguaggio più usato per microservizi dopo Java. Il motivo: binari piccoli, startup veloce, basso consumo di risorse. Per comunicare tra servizi hai due strade: REST over HTTP o gRPC.

gRPC e Docker

gRPC usa protobuf per serializzare messaggi e HTTP/2 per trasporto. In Go è supportato nativamente con la libreria google.golang.org/grpc. Esempio di server gRPC:

import (
    "log"
    "net"
    "google.golang.org/grpc"
    pb "mio/proto"
)

type server struct { pb.UnimplementedUtenteServiceServer }

func (s *server) GetUtente(ctx context.Context, req *pb.GetUtenteRequest) (*pb.Utente, error) {
    return &pb.Utente{Id: req.Id, Nome: "Mario"}, nil
}

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    s := grpc.NewServer()
    pb.RegisterUtenteServiceServer(s, &server{})
    log.Fatal(s.Serve(lis))
}

Ogni microservizio va containerizzato in Docker con un Dockerfile minimal:

FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server .

FROM scratch
COPY --from=builder /app/server /server
EXPOSE 50051
CMD ["/server"]

L'immagine risultante pesa pochi MB.

Deployment su Kubernetes

Su Kubernetes usiamo deployment, service e ingress. Go è perfetto per l'orchestrazione perché l'avvio è immediato e la terminazione è gestibile con signal.Notify. Un pattern che usiamo è il graceful shutdown:

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Spengo il server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
server.Shutdown(ctx)

Così Kubernetes può terminare i pod senza interrompere le richieste in corso.

Cosa fare adesso

Se vuoi iniziare con Go per il backend, ecco i tre passi immediati:

  1. Installa Go e scrivi il server HTTP di esempio sopra. Fallo partire e chiamalo con curl.
  2. Sperimenta la concorrenza: modifica l'esempio per leggere 10 URL in parallelo con goroutine e channel.
  3. Costruisci una mini API REST con Gin o Echo che interagisce con un database SQLite (leggero).

Poi, se hai già un progetto legacy, valuta di migrare un microservizio in Go: vedrai subito la differenza in termini di consumo di memoria e velocità di risposta. Noi lo facciamo quotidianamente. Se hai domande, contattaci: siamo a Sciacca, ma lavoriamo con tutta Italia.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // WEB AGENCY

Costruiamo la presenza digitale che la tua azienda merita.

Siti web, social, pubblicità online, e-commerce e hosting performante: ingegnerizzati con metodo da ingegneri informatici a Sciacca, per tutta Italia.

> MW_JOURNAL

> READ_ALL()