5 proyectos reales para aprender Go si ya sabes programar
Proyectos prácticos para aprender Go: CLI, API REST, worker, consumidor Kafka y conversor de ficheros. Con enlaces a tutoriales paso a paso.

Los ejercicios de sintaxis no sirven. Haces veinte problemas de bucles, tres de slices, uno de interfaces, y al día siguiente no recuerdas nada. No porque seas mal estudiante, sino porque el cerebro no retiene lo que no tiene contexto. Y la sintaxis de Go fuera de un problema real no tiene contexto.
La forma de aprender Go de verdad es construir cosas pequeñas que resuelvan problemas reales. No un TODO app. No un “Hello World con goroutines”. Proyectos que se parezcan a lo que harías en tu trabajo o en un servicio que desplegarías en producción.
Llevo meses escribiendo tutoriales paso a paso sobre Go, cada uno centrado en un proyecto concreto. Este artículo es el mapa. Aquí te explico qué cinco proyectos construir, en qué orden, qué vas a aprender con cada uno, y por qué ese orden importa.
Por qué proyectos > tutoriales para aprender Go
Hay una diferencia enorme entre leer documentación y resolver un problema. Cuando lees sobre goroutines, entiendes el concepto. Cuando tienes que paralelizar diez peticiones HTTP y agregar resultados con un timeout, aprendes goroutines.
Esto aplica a cualquier lenguaje, pero en Go es especialmente cierto por dos razones:
- Go es minimalista por diseño. No tiene cien formas de hacer lo mismo. Eso significa que los patrones correctos los interiorizas rápido, pero solo si los usas en contexto. Leerlos no basta.
- El tooling de Go es parte del lenguaje.
go test,go build,go mod,go vet… No son extras. Son fundamentales. Y solo los aprendes usándolos en un proyecto con estructura real.
La trampa clásica es intentar aprender Go leyendo Effective Go de principio a fin, o completando un curso de sintaxis de cuatro horas. Terminas sabiendo que existen los channels pero sin haber escrito uno que resuelva algo. Sabes que defer existe pero no has sentido por qué importa en un handler HTTP que abre y cierra conexiones.
Los cinco proyectos que propongo aquí están diseñados para cubrir el espectro de lo que necesita un desarrollador backend. Cada uno ataca un dominio diferente y te fuerza a usar una combinación distinta de herramientas del lenguaje.
Proyecto 1: CLI para automatización local
Qué construyes: Una herramienta de línea de comandos que automatiza una tarea repetitiva. Puede ser renombrar ficheros, limpiar logs, generar reportes, lo que quieras. Lo importante es que reciba argumentos, procese algo y devuelva un resultado.
Por qué este primero: Porque elimina todas las distracciones. No hay HTTP, no hay base de datos, no hay Docker. Solo tú, el lenguaje y el sistema operativo. Es el terreno perfecto para asentar los fundamentos.
Qué vas a aprender
- Estructura básica de un proyecto Go con
go mod init - Cómo parsear argumentos con
os.Argso librerías comocobra - Manejo de ficheros: leer, escribir, recorrer directorios
- El patrón de error handling de Go (
if err != nil) - Compilar un binario y distribuirlo
Ejemplo mínimo
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
func main() {
if len(os.Args) < 3 {
fmt.Println("uso: renombrar <directorio> <prefijo>")
os.Exit(1)
}
dir := os.Args[1]
prefix := os.Args[2]
entries, err := os.ReadDir(dir)
if err != nil {
fmt.Fprintf(os.Stderr, "error leyendo directorio: %v\n", err)
os.Exit(1)
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
oldPath := filepath.Join(dir, entry.Name())
newPath := filepath.Join(dir, prefix+"_"+entry.Name())
if err := os.Rename(oldPath, newPath); err != nil {
fmt.Fprintf(os.Stderr, "error renombrando %s: %v\n", oldPath, err)
continue
}
fmt.Printf("%s -> %s\n", oldPath, strings.TrimPrefix(newPath, dir+"/"))
}
}Esto es un CLI real. No es sofisticado, pero ya tienes argumentos, manejo de errores, operaciones con el filesystem y un binario que funciona. Desde aquí puedes añadir flags, validaciones, output formateado, tests.
Tutorial completo: CLI en Go
Proyecto 2: API REST con estructura profesional
Qué construyes: Una API REST con endpoints CRUD, conexión a base de datos, validación de datos y una estructura de carpetas que escale. No una API de juguete con un map en memoria: una API con PostgreSQL, migraciones y tests.
Por qué este segundo: Porque después de la CLI ya dominas la base del lenguaje. Ahora toca aprender cómo Go maneja HTTP, JSON, middleware y todo lo que rodea al desarrollo web backend.
Qué vas a aprender
- El paquete
net/httpy cómo funciona un server en Go - Routing con el mux estándar o con frameworks como Gin
- Serialización y deserialización JSON con struct tags
- Conexión a PostgreSQL con
database/sqlosqlx - Organización de código: handlers, services, repositories
- Middleware para logging, autenticación, CORS
- Testing de endpoints con
httptest
Ejemplo: handler básico
func GetTaskHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var task Task
err := db.QueryRow(
"SELECT id, title, done FROM tasks WHERE id = $1", id,
).Scan(&task.ID, &task.Title, &task.Done)
if err == sql.ErrNoRows {
http.Error(w, "task not found", http.StatusNotFound)
return
}
if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(task)
}
}Fíjate en lo que no hay: no hay framework mágico que esconda el request y el response. No hay anotaciones. No hay inyección de dependencias automática. En Go ves exactamente lo que pasa en cada línea. Eso es incómodo al principio y una ventaja enorme cuando algo falla en producción.
Si quieres un proyecto más completo con PostgreSQL y Docker incluido, tengo un tutorial específico: API de tareas con Go, PostgreSQL y Docker.
Tutorial de la API: API REST con Go
Proyecto 3: Worker de procesamiento en background
Qué construyes: Un servicio que recoge tareas de una cola (puede ser Redis, una tabla en PostgreSQL o un canal en memoria) y las procesa en segundo plano. Redimensionar imágenes, enviar emails, generar PDFs, lo que encaje con tu caso.
Por qué este tercero: Porque aquí es donde Go empieza a brillar. Las goroutines y los channels dejan de ser teoría y se convierten en la herramienta que usa tu worker para procesar N tareas en paralelo sin reventar la memoria.
Qué vas a aprender
- Goroutines y channels en un escenario real
- El patrón worker pool
context.Contextpara cancelación y timeoutssync.WaitGrouppara esperar a que terminen los workers- Graceful shutdown con señales del sistema operativo
- Logging estructurado
Ejemplo: worker pool básico
func startWorkers(ctx context.Context, jobs <-chan Job, results chan<- Result, numWorkers int) {
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for {
select {
case <-ctx.Done():
log.Printf("worker %d: shutting down", workerID)
return
case job, ok := <-jobs:
if !ok {
return
}
result := process(job)
results <- result
}
}
}(i)
}
go func() {
wg.Wait()
close(results)
}()
}Este patrón es el pan de cada día en servicios Go de producción. Lo vas a ver en procesadores de colas, en pipelines de datos, en cualquier sistema que necesite hacer trabajo pesado sin bloquear el flujo principal.
Lo importante aquí no es solo el código. Es entender cómo context te permite propagar cancelaciones, cómo los channels te dan sincronización sin locks explícitos, y cómo el graceful shutdown evita que tu worker muera a mitad de un procesamiento cuando Kubernetes manda un SIGTERM.
Tutorial completo: worker en Go
Proyecto 4: Consumidor Kafka para procesamiento de eventos
Qué construyes: Un servicio que se suscribe a un topic de Kafka, lee mensajes, los deserializa, los procesa y gestiona offsets y errores. Es el proyecto más cercano a lo que encontrarías en una arquitectura de microservicios real.
Por qué este cuarto: Porque si ya has construido una API y un worker, tienes la base para entender sistemas distribuidos. Kafka añade la complejidad de particiones, consumer groups, offsets y rebalanceos. Y aprender a gestionar todo eso en Go te da un skill directamente aplicable en producción.
Qué vas a aprender
- Cómo funciona Kafka a nivel de consumidor (topics, particiones, offsets, consumer groups)
- Uso de librerías como
confluent-kafka-goosegmentio/kafka-go - Deserialización de mensajes (JSON, Avro, Protobuf)
- Gestión de errores y reintentos
- Procesamiento idempotente
- Métricas y health checks
Ejemplo: consumidor básico
func consume(ctx context.Context, reader *kafka.Reader) error {
for {
select {
case <-ctx.Done():
return reader.Close()
default:
msg, err := reader.ReadMessage(ctx)
if err != nil {
if errors.Is(err, context.Canceled) {
return nil
}
log.Printf("error reading message: %v", err)
continue
}
var event OrderCreated
if err := json.Unmarshal(msg.Value, &event); err != nil {
log.Printf("error unmarshalling message: %v", err)
continue
}
if err := processOrder(ctx, event); err != nil {
log.Printf("error processing order %s: %v", event.OrderID, err)
// Aquí decides: ¿reintentar? ¿enviar a DLQ? ¿loguear y continuar?
continue
}
log.Printf("processed order %s from partition %d offset %d",
event.OrderID, msg.Partition, msg.Offset)
}
}
}Kafka es una de esas tecnologías que puedes usar durante años sin entender del todo. Construir un consumidor desde cero te obliga a entender qué pasa cuando un consumer se cae y otro toma su partición, qué significa commit manual vs automático de offsets, y por qué el procesamiento idempotente no es opcional.
Tutorial completo: Kafka con Go
Proyecto 5: Conversor CSV a JSON
Qué construyes: Una herramienta que lee ficheros CSV (potencialmente grandes), los parsea, transforma y escribe como JSON. Parece simple. No lo es cuando el CSV tiene millones de filas, encodings raros o campos que no cumplen el esquema esperado.
Por qué este quinto: Porque toca un dominio completamente diferente: procesamiento de datos. Y porque te fuerza a pensar en streaming, en uso de memoria y en cómo Go maneja I/O de forma eficiente.
Qué vas a aprender
- Paquetes
encoding/csvyencoding/json - Lectura streaming sin cargar todo en memoria
- Interfaces
io.Readeryio.Writer - Buffered I/O con
bufio - Validación y transformación de datos
- Testing con ficheros de ejemplo
- Flags para configurar el comportamiento (delimitador, encoding, output format)
Ejemplo: streaming CSV a JSON
func convertCSVtoJSON(input io.Reader, output io.Writer) error {
reader := csv.NewReader(input)
headers, err := reader.Read()
if err != nil {
return fmt.Errorf("reading headers: %w", err)
}
encoder := json.NewEncoder(output)
encoder.SetIndent("", " ")
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return fmt.Errorf("reading record: %w", err)
}
row := make(map[string]string, len(headers))
for i, header := range headers {
if i < len(record) {
row[header] = record[i]
}
}
if err := encoder.Encode(row); err != nil {
return fmt.Errorf("encoding row: %w", err)
}
}
return nil
}Lo interesante de este proyecto es que trabaja con las interfaces de I/O de Go, que son una de las piezas más elegantes del lenguaje. io.Reader y io.Writer están en todas partes: ficheros, conexiones HTTP, buffers, compresores. Entender cómo componerlas es fundamental.
Además, este proyecto escala naturalmente. Empiezas con un conversor básico y puedes añadir: soporte para ficheros enormes con procesamiento concurrente, detección automática de tipos, output en múltiples formatos (JSON Lines, Parquet), validación contra un esquema.
Tutorial completo: CSV a JSON en Go
Bonus: web scraper con concurrencia
No está en los cinco principales porque el scraping tiene sus propias complicaciones (legales, de infraestructura, de estabilidad), pero como proyecto de aprendizaje es excelente.
Qué construyes: Un scraper que visita una lista de URLs, extrae datos estructurados y los almacena. La gracia está en hacerlo concurrente: lanzar N goroutines, limitar la tasa de peticiones, gestionar errores por URL sin que el sistema entero se caiga.
Qué vas a aprender
- HTTP client avanzado: timeouts, retries, headers personalizados
- Concurrencia controlada con semáforos (
chan struct{}) - Rate limiting con
time.Ticker - Parsing HTML con librerías como
goquery - Patrones de resiliencia: circuit breaker, backoff exponencial
func scrape(ctx context.Context, urls []string, concurrency int) []Result {
results := make([]Result, 0, len(urls))
sem := make(chan struct{}, concurrency)
var mu sync.Mutex
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
sem <- struct{}{} // adquirir semáforo
defer func() { <-sem }() // liberar semáforo
data, err := fetchAndParse(ctx, u)
mu.Lock()
defer mu.Unlock()
if err != nil {
results = append(results, Result{URL: u, Error: err})
return
}
results = append(results, Result{URL: u, Data: data})
}(url)
}
wg.Wait()
return results
}El semáforo con un channel buffered es un patrón que ves constantemente en Go. Es simple, no requiere ninguna librería externa, y te da control preciso sobre cuántas operaciones concurrentes quieres permitir.
Tutorial completo: scraper en Go
Cómo presentar estos proyectos en tu portfolio
Construir los proyectos es solo la mitad. La otra mitad es que alguien pueda mirar tu GitHub y entender qué hiciste y por qué. Esto aplica tanto si buscas trabajo como si quieres documentar tu aprendizaje.
Estructura de cada repositorio
Cada proyecto debería tener:
- README claro: Qué hace, cómo ejecutarlo, qué tecnologías usa. No un README de tres párrafos: uno de diez líneas con instrucciones concretas.
- Makefile o Taskfile: Comandos para build, test, lint, run. Que cualquiera pueda clonar y ejecutar en menos de un minuto.
- Tests reales: No tests de mentira que comprueban que
1+1 == 2. Tests que validan comportamiento, edge cases, errores esperados. - Docker (cuando aplique): Un
Dockerfilemulti-stage que produzca una imagen limpia. Undocker-compose.ymlsi el proyecto necesita bases de datos u otros servicios. - CI configurado: Un workflow de GitHub Actions que ejecute tests y linting en cada push. Es una línea en tu repo que dice “me tomo esto en serio”.
Lo que demuestra cada proyecto
| Proyecto | Demuestra |
|---|---|
| CLI | Dominio del lenguaje base, manejo de errores, testing |
| API REST | Arquitectura web, base de datos, middleware, testing HTTP |
| Worker | Concurrencia, patterns de producción, graceful shutdown |
| Kafka consumer | Sistemas distribuidos, event-driven architecture |
| CSV a JSON | I/O eficiente, streaming, composición de interfaces |
| Scraper (bonus) | Concurrencia avanzada, resiliencia, rate limiting |
No necesitas los seis. Tres proyectos bien hechos ya cuentan una historia coherente. Pero si haces los cinco principales, tienes un portfolio que cubre prácticamente todo lo que un equipo busca en un desarrollador Go.
Progresión: en qué orden construirlos
El orden importa. Cada proyecto asume que ya dominás lo que aprendiste en el anterior.
Nivel 1: CLI
Es tu primer contacto real con Go. Aquí aprendes el lenguaje: tipos, control de flujo, error handling, paquetes, compilación. Sin distracciones externas.
Tiempo estimado: 1-2 días si ya programas en otro lenguaje.
Nivel 2: API REST
Subida de complejidad. Ahora tienes HTTP, JSON, base de datos. Aprendes cómo Go estructura aplicaciones web y cómo se testean.
Tiempo estimado: 3-5 días. Más si incluyes PostgreSQL y Docker.
Nivel 3: Worker
El salto a concurrencia real. Goroutines, channels, context, wait groups. Aquí es donde Go deja de parecerse a cualquier otro lenguaje y empiezas a pensar de forma diferente.
Tiempo estimado: 2-3 días.
Nivel 4: Kafka consumer
Sistemas distribuidos. Complejidad operacional. Este proyecto no es solo código: es entender cómo funcionan las piezas a nivel de infraestructura.
Tiempo estimado: 3-5 días, incluyendo levantar Kafka en local con Docker.
Nivel 5: CSV a JSON
Vuelves a un problema aparentemente simple, pero lo atacas con todo lo que has aprendido. Streaming, interfaces, testing robusto, manejo de edge cases.
Tiempo estimado: 1-2 días para la versión básica. Más si añades concurrencia o soporte para ficheros enormes.
El roadmap completo
CLI → API REST → Worker → Kafka Consumer → CSV/JSON Converter
│ │ │ │ │
│ │ │ │ └─ I/O, streaming, interfaces
│ │ │ └─ Sistemas distribuidos, event-driven
│ │ └─ Concurrencia, goroutines, channels
│ └─ HTTP, JSON, DB, middleware, testing
└─ Fundamentos: tipos, errores, paquetes, compilaciónSi quieres un mapa más amplio de todo el ecosistema de Go, incluyendo conceptos intermedios y avanzados, lo detallo en la guía general: Aprender Go en 2026.
Errores comunes al aprender Go con proyectos
Antes de que empieces, algunos errores que he visto (y cometido):
Empezar demasiado grande
No hagas un “microservicio completo con Kafka, Redis, PostgreSQL y gRPC” como primer proyecto. Vas a abandonar al tercer día. La CLI es tu primer proyecto por algo: es pequeño, terminable y te da confianza.
No escribir tests desde el principio
En Go, testear es trivial. El paquete testing viene incluido, go test ./... ejecuta todo, y la convención de _test.go es tan natural que no hay excusa. Si no testeas tus proyectos de aprendizaje, estás dejando sobre la mesa una de las mejores herramientas del lenguaje.
Copiar sin entender
Los LLMs te generan código Go correcto en segundos. Pero si copias sin entender por qué se usa defer aquí, por qué el error se comprueba allá, por qué el channel es buffered en este caso y no en otro, no estás aprendiendo. Usa la IA como acelerador, no como sustituto.
Ignorar el tooling
go fmt, go vet, golangci-lint. Úsalos desde el primer proyecto. No después. El tooling de Go es parte de la cultura del lenguaje y te ahorra discusiones sobre estilo que en otros lenguajes consumen horas.
No leer código de otros
Después de cada proyecto, busca implementaciones similares en GitHub. Mira cómo lo hacen otros. El código Go idiomático tiene un estilo reconocible, y comparar tu solución con otras es la forma más rápida de mejorar.
Construir es la única forma de aprender de verdad
Go no se aprende leyendo. Se aprende construyendo. Y no cualquier cosa: proyectos que te fuercen a resolver problemas reales con las herramientas que el lenguaje te da.
Los cinco proyectos que he propuesto van desde una CLI en Go para asentar los fundamentos sin distracciones, pasando por una API REST con Go que te mete de lleno en desarrollo web con base de datos, hasta un worker en Go donde la concurrencia con goroutines y channels deja de ser teoría. Kafka con Go te lleva a sistemas distribuidos y event-driven architecture, y el conversor CSV a JSON en Go te obliga a trabajar con I/O eficiente, streaming e interfaces. Y como bonus, el scraper en Go para concurrencia avanzada y patrones de resiliencia.
No necesitas seguir el orden al pie de la letra, pero te recomiendo no saltarte los primeros dos. La CLI te da los fundamentos y la API te da el contexto web. A partir de ahí, puedes ir al worker, a Kafka o al conversor segun lo que mas te interese o lo que necesites para tu trabajo. Lo que si necesitas es empezar. Abre tu terminal, ejecuta go mod init y escribe el primer main.go. El mejor momento fue hace una semana. El segundo mejor momento es ahora.


