// 01 · fundamentos

Fundamentos y toolchain

Instala una sola cosa, aprende la sintaxis en una tarde y deja que gofmt discuta el estilo por ti.

// un binario que lo hace todo

Toolchain: instala una cosa y ya

Olvídate de instalar JDK + Maven + tu suite de plugins. En Go instalas un binario (go) y ese binario compila, ejecuta, testea, formatea, gestiona dependencias, documenta y detecta race conditions.

terminal — instalación y primer proyecto
# Windows (tu caso), macOS y Linux:
winget install GoLang.Go        # o: brew install go / sudo apt install golang

$ go version
go version go1.24.5 windows/amd64

# Un proyecto nuevo son dos comandos. Sin arquetipos, sin spring initializr:
$ mkdir hola; cd hola
$ go mod init github.com/daniel/hola
$ go run .                       # compila Y ejecuta en un paso

Los comandos que usarás a diario

ComandoQué haceTu equivalente mental
go run .Compila y ejecuta al vuelomvn spring-boot:run, npm run dev
go buildGenera el binario nativomvn package (pero sin JVM después)
go test ./...Corre todos los tests del módulomvn test, pytest
go get pkg@v1.2.3Añade/actualiza una dependencianpm install pkg, editar el pom
go install pkg@latestInstala una herramienta CLI globalnpm install -g, pipx install
go fmt ./...Formatea todo el código (estilo único oficial)Prettier, pero sin archivo de config
go vet ./...Análisis estático de errores comunesSonarLint / ESLint básico
go doc fmt.PrintlnDocumentación en la terminalJavadoc, pero instantáneo
go test -raceDetector de condiciones de carreraNo existe equivalente cómodo en Java
go mod tidyOrdena dependencias (añade/borra)mvn dependency:analyze que además arregla
i

Tu editorVS Code + extensión oficial Go (usa el language server gopls): autocompletado, refactors, test runner y debug con delve. Si vienes de IntelliJ, GoLand de JetBrains te resultará casa. Para hot-reload tipo nodemon/Spring DevTools: la herramienta air.

Variables de entorno y cross-compiling (GOOS, GOARCH, GOPATH…)

go env muestra la configuración. Las que importan:

  • GOPATH — carpeta de caché de módulos y binarios instalados (~/go). Desde los módulos (2019) ya no trabajas dentro de GOPATH; tu proyecto vive donde quieras. Si un tutorial habla de GOPATH/src, ciérralo: está obsoleto.
  • GOOS / GOARCH — sistema y arquitectura destino. Compilar desde tu Windows para un servidor Linux es una línea: GOOS=linux GOARCH=amd64 go build. Sin toolchains cruzados, sin Docker para compilar.
  • CGO_ENABLED=0 — desactiva el puente a C () y garantiza un binario 100% estático, ideal para contenedores FROM scratch.
  • GOTOOLCHAIN — desde 1.21 el propio go puede descargar y usar la versión que pida el go.mod: adiós a "en mi máquina compila".

// lo justo para leer cualquier código Go

Sintaxis esencial

Todo archivo Go pertenece a un paquete. El paquete main con una función main() es el punto de entrada — como tu public static void main, sin la liturgia.

main.go — anatomía mínima
package main

import "fmt" // la stdlib se importa por ruta; lo no usado NO compila

func main() {
	fmt.Println("Hola desde Go") // sin punto y coma: los pone el compilador
}

Variables, constantes y zero values

Dos formas de declarar: var (explícita) y := (inferencia, solo dentro de funciones). Toda variable no inicializada tiene un — en Go no existe la no-inicialización.

variables.go
package main

import "fmt"

const Pi = 3.14159 // const sin tipo: se adapta al contexto (más flexible que final)

// iota genera secuencias: el enum minimalista de Go
const (
	Pendiente = iota // 0
	EnCurso          // 1
	Hecha            // 2
)

func main() {
	var edad int          // 0     ← zero value, nunca "basura" ni null
	var nombre string     // ""    ← string vacío, NO nil
	var activo bool       // false
	precio := 19.99       // float64 inferido (:= solo dentro de funciones)
	x, y := 10, 20        // asignación múltiple, como Python

	fmt.Println(edad, nombre, activo, precio, x+y)
}
!

Trampa para javeros: shadowing con :=Dentro de un if o for, err := ... crea una variable nueva que tapa a la exterior. Es el bug clásico del que viene de Java. go vet y el linter avisan — hazles caso.

Funciones: retornos múltiples, el superpoder cotidiano

Las funciones son valores de primera clase (como en JS/Python) y pueden devolver varios valores sin tuplas ni wrappers. De aquí nace el patrón de errores de Go y el patrón valor, ok.

funciones.go
// Retorno múltiple: adiós Optional<T>, adiós tuplas artificiales
func dividir(a, b float64) (float64, error) {
	if b == 0 {
		return 0, fmt.Errorf("división entre cero")
	}
	return a / b, nil
}

// Variádicas, como los varargs de Java
func sumar(nums ...int) int {
	total := 0
	for _, n := range nums {
		total += n
	}
	return total
}

func main() {
	resultado, err := dividir(10, 3)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println(resultado)

	// Closures: idénticas a las de JavaScript que usas en React
	contador := 0
	incrementar := func() int {
		contador++
		return contador
	}
	fmt.Println(incrementar(), incrementar()) // 1 2

	fmt.Println(sumar(1, 2, 3, 4)) // 10
}

Tipos básicos frente a Java

GoJavaNota
int, int64int, longint es de 64 bits en máquinas modernas; sin autoboxing, sin Integer
float64doubleEl default para decimales; float32 existe pero es raro
stringStringInmutable, UTF-8 nativo; se compara con == (¡sí, de verdad!)
runechar-ishUn code point Unicode (int32); byte es un octeto crudo
boolbooleanSin conversión implícita: if 1 {} no compila (a diferencia de Python)
no existenullLos tipos valor nunca son nil; solo punteros, slices, maps, canales e interfaces
Conversiones: en Go todo casting es explícito, siempre

Java convierte int → long solo. Go, jamás: var l int64 = int64(unInt). Entre números y strings se usa el paquete strconv:

  • strconv.Itoa(42) → "42" (como String.valueOf)
  • strconv.Atoi("42") → 42, nil (devuelve error si no parsea, sin NumberFormatException)
  • fmt.Sprintf("%d-%s", 7, "go") → tu String.format de siempre

Molesta dos días. Después descubres que nunca más depuraste una conversión implícita sorpresa.

// un solo bucle para gobernarlos a todos

Control de flujo y defer

Go tiene un único bucle: for. Hace de for, while, do-while y for-each. Y el switch no necesita break — no hay fall-through accidental.

flujo.go
// 1. for clásico
for i := 0; i < 5; i++ { fmt.Println(i) }

// 2. for como while
for saldo > 0 { saldo -= pago }

// 3. for infinito (tu while(true))
for { procesarSiguiente() }

// 4. for range: el for-each universal (slices, maps, strings, canales)
for i, fruta := range []string{"mango", "kiwi"} {
	fmt.Println(i, fruta)
}

// 5. range sobre enteros (Go 1.22+): como range(5) de Python
for i := range 5 { fmt.Println(i) }

// if con sentencia de inicialización: el err vive SOLO dentro del if
if err := guardar(usuario); err != nil {
	return err
}

// switch sin break (rompe solo), acepta condiciones tipo if/else-if
switch {
case hora < 12:
	fmt.Println("buenos días")
case hora < 19:
	fmt.Println("buenas tardes")
default:
	fmt.Println("buenas noches")
}

defer: tu try-with-resources, pero mejor

defer pospone una llamada hasta que la función termina — por cualquier camino, incluso con panic. Es el idioma estándar para liberar recursos, y sustituye a tu try-with-resources de Java y al with de Python.

defer.go
func procesarArchivo(ruta string) error {
	f, err := os.Open(ruta)
	if err != nil {
		return err
	}
	defer f.Close() // se ejecuta al salir de la función, pase lo que pase

	// ... trabajar con f sin preocuparte de cerrarlo en cada return ...
	return nil
}

// Los defer se apilan en orden LIFO (último en entrar, primero en salir)
func demo() {
	defer fmt.Println("3º")
	defer fmt.Println("2º")
	fmt.Println("1º")
} // imprime: 1º, 2º, 3º
!

No hay operador ternariox = cond ? a : b no existe. Se escribe con if/else normal. Es la queja #1 de todo recién llegado y la decisión es final: los autores consideran que los ternarios anidados son ilegibles.

// nuevo · el acento nativo

Convenciones de nombres y godoc

En Go, las convenciones no son sugerencias de un wiki: la visibilidad depende de la primera letra y las herramientas oficiales asumen el resto. Escribir "con acento Java" es la señal #1 de recién llegado.

ReglaBien ✓Mal ✗ (acento Java/Python)
MixedCaps siempremaxRetries, ParseURLmax_retries, MAX_RETRIES
Acrónimos en bloqueuserID, servirHTTPuserId, servirHttp
Nombres cortos cerca de su usofor i, u := range usuariosindiceActualDeUsuario
Sin prefijo del paquetehttp.Client, bytes.Bufferhttp.HTTPClient (se leería http.HTTPClient)
Interfaces de un método: -erReader, NotificadorIUserService, UserServiceInterface
Sin getters con "Get"u.Nombre() o campo públicou.GetNombre()
Errores: Err… / …ErrorErrNoEncontrado, *ValidationErrorNOT_FOUND_EXCEPTION

Comentarios de documentación

Un comentario justo encima de una declaración exportada es su documentación oficial: go doc y pkg.go.dev lo publican tal cual. La convención: empieza con el nombre de lo que documenta.

doc.go
// Paquete precios calcula descuentos y totales con impuestos.
// (comentario sobre "package": documenta el paquete entero)
package precios

// AplicarDescuento devuelve el precio tras aplicar el cupón.
// Un cupón desconocido no es un error: simplemente no descuenta.
func AplicarDescuento(precio float64, cupon string) float64 {
	// ...
}
✓

Comparado con JavadocSin @param, sin @return, sin HTML: prosa simple en una o dos frases. La firma tipada ya documenta los parámetros; el comentario explica lo que la firma no puede decir. Si escribes párrafos de Javadoc, estás sobre-documentando.