Volver al blog

Cómo estructurar un proyecto en Go (2026): guía idiomática

La estructura de carpetas que la mayoría de principiantes copia de un repositorio con 50.000 estrellas no es un estándar oficial de Go, y el propio equipo de Go lo ha dicho. Este es el enfoque oficial para estructurar un proyecto Go, y es más sencillo de lo que imaginas.

Cómo estructurar un proyecto en Go (2026): guía idiomática

Busca «estructura de proyecto Go» y en dos clics acabas en golang-standards/project-layout, un repositorio de GitHub con más de 50.000 estrellas y un árbol de directorios lleno de cmd/, pkg/, internal/, api/ y build/. Parece una referencia oficial, y la mayoría de principiantes lo copia tal cual en un programa de cinco archivos.

Sin embargo, no es un estándar oficial, y el equipo de Go lo ha dicho públicamente. La guía oficial es más sencilla: empieza con una estructura plana y añade carpetas solo cuando el código las necesite. Las secciones siguientes recorren esa guía carpeta por carpeta, con una CLI del tiempo y una API para una tienda de libros como ejemplos a lo largo del texto.

¿Cuál es la estructura estándar de un proyecto Go?

No hay un árbol obligatorio. Go no exige src/ ni cmd/, ni ninguna carpeta más allá de un archivo go.mod en la raíz del módulo. La guía oficial, «Organizing a Go module», relaciona la estructura con el tamaño del proyecto. Muestra cuatro estructuras, cada una un paso por encima de la anterior: un paquete básico, un comando básico, un paquete que incorpora paquetes auxiliares y un proyecto de «servidor» con varios binarios. (fuente)

Así que la pregunta útil es cuánta estructura necesita tu proyecto ahora mismo, y para la mayoría de programas la respuesta es muy poca. El resto de este artículo recorre las cuatro estructuras de la más simple a la más compleja. Busca la que encaja con tu proyecto y quédate ahí.

Empieza con una estructura plana: un solo paquete es idiomático

Las dos primeras estructuras de la documentación oficial son un único paquete y un único comando, ambos directamente en la raíz del módulo y sin subdirectorios. Para una pequeña herramienta de línea de comandos que recibe una ciudad e imprime el tiempo que hace, este es el proyecto entero:

example.texttext
weather/
├── go.mod
├── main.go
├── weather.go
└── weather_test.go

go.mod declara la ruta del módulo (por ejemplo, module github.com/you/weather). main.go contiene package main y el punto de entrada. weather.go contiene la lógica, en el mismo paquete. weather_test.go contiene los tests, justo al lado del código que prueban, que es donde Go espera encontrarlos. Puedes publicarlo tal como está. La documentación oficial lo llama un «comando básico», y es la forma adecuada para la mayoría de herramientas que escribirás en tu primer año.

Una regla explica la mayoría de las decisiones de estructura que vienen después: un directorio es un paquete. Todos los archivos de un directorio declaran el mismo paquete, y el nombre del paquete es el último elemento de la ruta de importación. Si añades un directorio weather/store/, cada archivo que contenga dirá package store. Lo importas como github.com/you/weather/store y lo usas como store.Get.

No añadas carpetas todavía. Un directorio utils/ con una sola función, o un directorio models/ con un solo struct, añade una ruta de importación y una frontera de paquete y no te da nada a cambio. Mantén el proyecto plano hasta que esa estructura plana empiece a causar problemas reales.

¿Cuándo conviene añadir un directorio internal/?

La primera buena razón para añadir estructura es la privacidad. Go tiene dos niveles de visibilidad para los identificadores: los nombres que empiezan por mayúscula se exportan y los que empiezan por minúscula son privados del paquete. Eso funciona dentro de un solo paquete. Pero cuando tu módulo tiene varios paquetes, a menudo quieres código que todos los paquetes dentro del módulo puedan usar y que nada fuera de él pueda importar. Eso es lo que te da internal/, y el compilador lo hace cumplir.

Esto forma parte del lenguaje, no es una convención de nombres. Un paquete situado bajo un directorio llamado internal/ solo puede importarse desde código que esté dentro del directorio padre de ese internal/, y una importación desde otro módulo no compila. La documentación oficial recomienda mantener los paquetes en internal «siempre que sea posible». El código externo no puede importarlos, así que no puede llegar a depender de ellos, y nunca acabas manteniendo una API que no querías publicar. (fuente)

Supón que la CLI del tiempo se convierte en una pequeña API HTTP para una tienda de libros. Ahora tiene autenticación de peticiones y una capa de acceso a datos, y ninguna de las dos pertenece a una API pública:

example.texttext
bookstore/
├── go.mod
├── main.go
└── internal/
    ├── auth/
    │   ├── auth.go
    │   └── auth_test.go
    └── store/
        ├── store.go
        └── store_test.go

main.go está dentro del módulo bookstore, así que puede importar github.com/you/bookstore/internal/auth y github.com/you/bookstore/internal/store libremente. El código de cualquier otro módulo no puede. Si alguien ejecuta go get sobre tu módulo e intenta importar esos paquetes, el compilador rechaza la importación. Como nadie fuera de tu módulo puede depender de auth o store, puedes renombrar sus funciones, cambiar sus firmas o reorganizarlos cuando quieras sin romper el código de nadie.

¿Cuándo conviene añadir un directorio cmd/?

cmd/ es para repositorios que compilan más de un binario, o que publican a la vez un binario y una biblioteca importable. Si solo tienes un binario, no lo necesitas, y main.go en la raíz queda más limpio.

Dentro de cmd/, cada binario tiene su propio subdirectorio, y el nombre del directorio pasa a ser el nombre del programa. La estructura oficial de «proyecto de servidor» hace esto: cada comando vive en cmd/<name>/ con un main.go pequeño que conecta los paquetes de internal/. (fuente) Supón que la tienda de libros ahora incluye un servidor de API y una herramienta de migración de base de datos:

example.texttext
bookstore/
├── go.mod
├── cmd/
│   ├── api/
│   │   └── main.go
│   └── migrate/
│       └── main.go
└── internal/
    ├── auth/
    └── store/

go build ./cmd/api genera un binario llamado api, y go build ./cmd/migrate genera migrate. Los dos archivos main.go se mantienen ligeros e importan el trabajo real desde internal/.

Esta es toda la progresión en una tabla. Elige la fila que corresponde a tu proyecto tal como está hoy, no al proyecto en el que imaginas que se convertirá.

Tipo de proyectoEstructura recomendadaPor qué
Herramienta pequeña o scriptPlana: main.go + archivos de lógica en la raízUn solo paquete es idiomático y completo
Un binario con código interno reutilizableAñade internal/ para los paquetes privadosPrivacidad impuesta por el compilador, sin API prematura
Dos o más binariosAñade cmd/<name>/ por cada binarioCada main tiene su propio directorio
Repositorio con varios servicios (binarios + código compartido)cmd/ + internal/, separados por dominioLa estructura oficial de «proyecto de servidor»

El mito de golang-standards/project-layout

El repositorio golang-standards/project-layout lleva «standards» en el nombre de su organización de GitHub, tiene más de 50.000 estrellas y un árbol grande con api/, build/, configs/, deployments/, pkg/ y test/. Es fácil suponer que el equipo de Go lo respaldó, pero no lo hizo. El README del repositorio incluye ahora un aviso que dice que no es un estándar oficial, y la objeción vino de los propios responsables de Go.

En el issue #117, Russ Cox, líder técnico de Go, escribió que esto «no es una estructura estándar de proyecto Go», y que la mayoría de repositorios Go son «mucho más simples» y ni siquiera usan un directorio pkg/. El hilo llegó a la portada de Hacker News, donde los desarrolladores Go con experiencia le dieron la razón en su mayoría. La estructura puede tener sentido para un monorepo grande compartido por varios equipos, pero como plantilla inicial para un proyecto normal hace daño. Si la copias en un programa pequeño, tus cinco archivos acaban bajo ocho carpetas casi vacías.

De aquí salen unas cuantas reglas, que coinciden con lo que suelen aconsejar desarrolladores Go como Eli Bendersky y Alex Edwards:

  • No uses un directorio src/. Esa costumbre viene de Java y de proyectos Node antiguos. El código Go vive en la raíz del módulo, y ninguna estructura oficial tiene src/.
  • No anides carpetas antes de tener código que meter en ellas. Las carpetas api/, build/ y deployments/ vacías no aportan nada. Crea una carpeta el día que tengas algo para ella.
  • Nombra los paquetes por lo que ofrecen, no por el papel que cumplen. Un paquete llamado utils, common o helpers no le dice nada a quien lo lee y acaba convertido en un cajón de sastre. Llámalo store, auth, weather, retry o slugify. El consejo oficial de Go sobre nombres de paquetes es que sean cortos y claros y que se lean bien en el punto donde se usan, así que store.Get es mejor que utils.GetFromStore. (fuente)

Para los errores que aparecen en el código y no en las carpetas, consulta nuestra guía sobre errores comunes en Go que debes evitar.

Regla práctica para principiantes sobre la estructura de un proyecto Go

La progresión se reduce a unas pocas preguntas sobre tu proyecto. Respóndelas empezando por arriba y añade un directorio solo cuando una respuesta lo pida.

flowchart TD
    A["New Go project"] --> B["Is it one package<br/>of related code?"]
    B -->|Yes| C["Keep it flat:<br/>main.go + logic in the root"]
    B -->|"Grew past that"| D["Need to hide code<br/>from outside importers?"]
    D -->|Yes| E["Add internal/<br/>put private packages there"]
    E --> F["More than one<br/>binary to build?"]
    F -->|No| G["Done. main.go in root,<br/>logic in internal/"]
    F -->|Yes| H["Add cmd/name/<br/>one dir per binary"]
    H --> I["Many services sharing code?"]
    I -->|Yes| J["Server layout:<br/>cmd/ + internal/ by domain"]

Cada paso responde a una necesidad concreta: una frontera de paquete, un segundo binario o código que quieres mantener privado. Si no puedes señalar ninguna de ellas, probablemente no necesitas la carpeta. Empieza con una estructura plana, añade internal/ cuando quieras que el compilador imponga la privacidad, añade cmd/ cuando tengas un segundo binario y pasa a la estructura completa de servidor solo cuando el repositorio contenga de verdad varios servicios.

Preguntas frecuentes

¿Existe una estructura oficial para proyectos Go?

No, no existe una única estructura de carpetas obligatoria. El documento oficial, «Organizing a Go module», describe una estructura que crece con el tamaño del proyecto en lugar de una estructura fija. Va de un paquete plano a internal/, luego a cmd/ y después a una estructura completa de servidor. Usa la estructura más pequeña que le sirva a tu proyecto. (fuente)

¿Debería usar el directorio pkg/?

Normalmente no. Russ Cox, líder técnico de Go, señaló que la mayoría de repositorios Go no usan un directorio pkg/ y «suelen ser mucho más simples». Pon el código privado en internal/, donde el compilador impone la privacidad, y expón paquetes en la raíz del módulo solo cuando pienses mantenerlos como API pública. (fuente)

¿Qué va en internal/ y qué va en cmd/?

internal/ contiene código de biblioteca importable que solo tu módulo puede usar, y el compilador lo hace cumplir. cmd/<name>/ contiene el paquete main ligero de cada binario que compilas, que conecta el código de internal/. La lógica va en internal/, y los puntos de entrada van en cmd/. (fuente)

¿Puede un proyecto Go real ser un solo archivo o paquete?

Sí. El primer ejemplo de la guía oficial de estructura es un único paquete en la raíz del módulo, y muchas herramientas en producción se distribuyen así. Un paquete con un go.mod, un main.go y sus tests es idiomático y completo. Añade estructura cuando al código se le quede pequeño. (fuente)

¿En qué se diferencia un proyecto Go de uno en Java o Node?

Go no tiene directorio src/. El código vive en la raíz del módulo, cada directorio es un paquete y el nombre del paquete es el último elemento de la ruta. Ninguna herramienta de compilación impone un árbol como lo hacen Maven o un bundler, así que las estructuras Go se mantienen más planas y solo crecen cuando hace falta. (fuente)

Por dónde seguir

La estructura es una de las partes más fáciles de Go. Publicas un programa con estructura plana y añades internal/ y cmd/ cuando el código los necesite. Si no sabes explicar por qué existe una carpeta, probablemente puedas borrarla.

La mejor forma de cogerle el truco es construir un par de programas reales, por ejemplo una CLI y un servicio HTTP, y dejar que la estructura crezca sobre la marcha. La ruta Clean Go Code de LevelUpGo trata el diseño idiomático de paquetes y la estructura de proyectos mediante ejercicios que escribes y ejecutas en el navegador, y Go Fundamentals te acompaña desde tu primer go.mod y las herramientas de Go. También puedes explorar la hoja de ruta interactiva completa.

Escribe Go como un ingeniero sénior

Lecciones interactivas en tu navegador. Las primeras son gratis.

Prueba una lección gratisO crea una cuenta gratuita