La estructura de un proyecto (1): el estándar que no era estándar
Por Dorian Chávez · fundador y CEO de Hábil ·
El repo de estructura de Go más popular dice que no es oficial. La guía de Go pide cmd/ e internal/, y solo internal/ la vigila la herramienta. Con plantilla.
La estructura de un proyecto · entrega 1 de 7 · Go
Escenario: los personajes y la historia son inventados. Los datos técnicos y las citas llevan su fuente al final.
En 1 minuto
- El repositorio de «estructura estándar» de Go más popular (56.7 mil estrellas el 22 de octubre de 2026) dice en su README que no es un estándar oficial y que, para un proyecto simple, es exagerado [2][3].
- La guía oficial de Go pide poco para un servidor: los programas en
cmd/y la lógica eninternal/[1]. - De las carpetas donde vive tu código, solo
internal/pone una frontera que la herramientagohace cumplir: en la práctica, otro módulo no la puede importar [6][7]. - Te llevas una plantilla que se clona y corre a la primera, con un guion que demuestra esa frontera poniéndose rojo cuando debe.
- Para el lunes: abre tu repositorio y pregúntale a cada carpeta qué problema resuelve hoy. La que no sepa contestar, sobra.
En este artículo
- ¿Qué pasó a las 23:40?
- ¿Qué pide de verdad la guía de Go?
- ¿Cuáles son los cinco errores más comunes?
- Ejercicio: ¿puedes poner la frontera en rojo?
- Para llevar
- ¿Qué sigue?
- La plantilla y el curso
- ¿Qué hace Hábil?
- Referencias
¿Qué pasó a las 23:40?
Iván heredó un programa de 300 líneas repartido en catorce carpetas, y ninguna le decía dónde poner una función nueva.
Lunes, 23:40. Oficina vacía, piso tres.
Era su primer día como dual. Séptimo semestre, el café frío desde las nueve y una sola tarea en el tablero: «Que el revisor avise cuando un servicio tarde más de un segundo».
El revisor era un programa en Go que consultaba una lista de servicios por HTTP y decía cuáles respondían. Lo había escrito Rodrigo, el dual anterior, que se fue el viernes sin dejar más que el repositorio.
Iván lo clonó y se quedó viendo la pantalla.
api/ assets/ build/ cmd/ configs/ deployments/ docs/
examples/ init/ internal/ pkg/ scripts/ test/ web/Catorce carpetas para unas 300 líneas de Go. api/: un README.md que decía «aquí va el contrato». web/: vacía. deployments/: un archivo de 2019 sobre un servidor que ya no existía. pkg/util: once funciones sin nada que ver entre sí.
La tarea era chica. Diez líneas, quizá. Pero ¿dónde? ¿En pkg/util, junto a una función que formateaba fechas? ¿En internal/? ¿Había reglas, o era costumbre? El README de Rodrigo decía: «Estructura estándar de Go», con un enlace.
Lo siguió: golang-standards/project-layout, más de 56 mil estrellas. Iván respiró. Si tanta gente lo usaba, tenía que ser el estándar. Y en el segundo párrafo de la introducción del README, en negritas, encontró algo que no esperaba:
«NOT an official standard defined by the core Go dev team» [2].
No es un estándar oficial del equipo de Go. Lo dice el propio repositorio.
Iván se recargó en la silla. Si el estándar no era estándar, ¿qué parte de esas catorce carpetas importaba de verdad?
A las 23:58 le escribió a Lucía, la líder técnica. La respuesta llegó en dos minutos:
—De esas catorce, ¿cuál vigila la herramienta? ¿Cuál cambia lo que el comando go deja importar?
Iván volvió a mirar la lista. Ninguna tenía cara de vigilar nada.
—No sé —escribió.
—Una. Mañana te enseño cuál, y por qué las otras trece son opinión.
¿Qué pide de verdad la guía de Go?
La guía oficial de Go cabe en dos carpetas para un servidor: el programa en cmd/ y todo lo demás en internal/.
Se llama Organizing a Go module [1] y va de lo más chico a lo más grande, en siete casos: un paquete, un programa, uno de los dos con paquetes de apoyo, varios paquetes, varios programas, paquetes y programas juntos, y al final un servidor. Para ese último caso dice dos cosas.
La primera: un servidor normalmente no tiene paquetes para que otros los importen, así que su lógica va en internal/ [1]. La segunda: como suele tener muchas carpetas que no son de Go, conviene juntar los programas en cmd/ [1].
Eso es todo. No nombra pkg/; las carpetas sin Go, como api/ o web/, las deja a tu criterio [1].
En 2021, en una discusión de ese mismo repositorio, Russ Cox lo dijo más corto: el estándar mínimo de un repositorio de Go importable (uno cuyo código importan otros) es un LICENSE y un go.mod en la raíz, y el código donde te acomode. «That's it. That's the "standard"», escribió [5]. Y agregó que ni siquiera cmd/ es obligatorio [5].
La plantilla de esta entrega, estructura-go, sigue la guía al pie de la letra:
estructura-go/
├── go.mod
├── README.md
├── LICENSE
├── LICENSE-CONTENIDO.md
├── cmd/
│ └── revisor/main.go el programa: lee la configuración, arma y arranca
├── internal/
│ ├── servicio/ el vocabulario: Servicio, Estado. Sin red ni archivos
│ ├── revisar/ consulta muchos servicios a la vez
│ ├── httpapi/ rutas, JSON y errores
│ └── config/ banderas, entorno y la lista de servicios
├── testdata/servicios.json
├── herramientas/probar-frontera.sh
└── .github/workflows/verificar.ymlVamos carpeta por carpeta, y en cada una la pregunta es la misma: ¿qué problema resuelve hoy?
¿Por qué el programa vive en cmd/?
main.go es delgado a propósito: si algo de ahí merece una prueba, ya no pertenece ahí.
cmd/revisor/main.go lee la configuración, arma las piezas de internal/ y arranca el servidor. No decide qué es un servicio sano ni cómo se consulta. ¿Por qué tan flaco? Porque main termina el proceso con os.Exit y lee el entorno real: lo que se queda ahí se queda sin prueba. La plantilla usa una función ejecutar que devuelve un error, y la prueba la llama con un puerto libre.
Un segundo programa, como una tarea nocturna, iría en cmd/tarea-nocturna/ y compartiría todo lo de internal/.
¿Qué tiene de especial internal/?
internal/ es la única carpeta del árbol con una frontera que la herramienta go hace cumplir: el código que vive ahí no se puede importar desde fuera del árbol que la contiene; en la práctica, desde otro módulo.
Según la documentación del comando go, un paquete dentro de una carpeta internal solo lo puede importar el código que comparte la ruta de arriba de esa carpeta [6]. Si tu módulo es github.com/tu-usuario/revisor, todo ese módulo puede usar revisor/internal/servicio; un módulo ajeno, no. Ojo: va por ruta, no por módulo. Un módulo llamado github.com/tu-usuario/revisor/otro sí podría, y el guion de la frontera usa ese caso.
No lo hace cumplir el lenguaje, sino el comando go. Las notas de Go 1.4 lo dicen así: el lenguaje no tiene cómo imponer esa distinción, y el comando go introduce un mecanismo para definir paquetes «internal» [7]. Desde Go 1.5 vale para cualquier repositorio [7][8].
¿Y a un servidor qué? Que lo que nadie de fuera importa, lo cambias sin romperle nada a nadie. Por eso la guía aconseja dejar en internal/ tanto como se pueda [1].
¿Y las otras trece carpetas de Iván? Ninguna decide quién importa a quién. El comando go trata distinto a otros dos nombres que no están en su lista. A testdata/ la ignora [6].
vendor/ sí cierra el paso: si un módulo importa algo por la ruta del vendor/ de otro, la herramienta contesta use of vendored package not allowed (medido con Go 1.27.1). Pero ahí no va tu código: guarda copias de tus dependencias, y go mod vendor la borra y la rehace cada vez [20]. Para tu código, la frontera es internal/.
¿Por qué servicio/ no importa a nadie?
El paquete del que dependen todos no depende de ninguno, y por eso no puede cerrar un ciclo.
internal/servicio/ define Servicio (nombre, URL, tiempo límite) y Estado (qué contestó, cuánto tardó, por qué falló). No abre conexiones, no lee archivos, no sabe de JSON. Todos los paquetes de internal/ lo importan, y él no importa a nadie del proyecto:
| Paquete | Importa a |
|---|---|
cmd/revisor | config, httpapi, revisar |
httpapi | revisar, servicio |
revisar | servicio |
config | servicio |
servicio | nadie del proyecto |
No es estética: la especificación prohíbe que un paquete se importe a sí mismo, directa o indirectamente [11]. Como servicio no importa a nadie, no puede formar parte de un ciclo. Y como las demás flechas van en un solo sentido (cmd → httpapi → revisar → servicio), hoy no hay ninguno.
El nombre también cuenta: por convención, los paquetes llevan nombres en minúsculas y de una sola palabra [17]. servicio dice lo que hay adentro; models o types dirían solo de qué tipo es.
¿Dónde va la interfaz?
La interfaz vive en el paquete que la usa, no en el que la implementa.
revisar consulta muchos servicios a la vez, y para eso necesita a alguien que consulte uno. Lo declara ahí mismo:
// Consultor es lo único que Todos necesita: alguien que consulte UN servicio y
// diga cómo lo encontró. Un método, y nada más.
type Consultor interface {
Consultar(ctx context.Context, s servicio.Servicio) servicio.Estado
}
func Todos(ctx context.Context, c Consultor, servicios []servicio.Servicio, paralelo int) []servicio.EstadoLa consulta real es un tipo concreto, *revisar.HTTP, que cumple esa interfaz sin declararlo. El doble para pruebas, consultorFalso, vive en un _test.go y no viaja en el programa.
Así lo pide Go Code Review Comments, la guía de revisión del propio proyecto: por lo general, las interfaces van en el paquete que usa los valores, no en el que los implementa [10]. Es el error 4 de abajo.
¿Y la puerta HTTP?
httpapi solo traduce: de petición a llamada, y de resultado a JSON.
Tiene tres rutas: GET /salud, GET /servicios y GET /servicios/{nombre}, con el enrutador de la biblioteca estándar. Desde Go 1.22, net/http entiende el método y los comodines en el patrón, y a un POST le contesta 405 Method Not Allowed con los métodos válidos en el encabezado Allow [12]. Medido en la plantilla: Allow: GET, HEAD.
Los errores que escribe la plantilla salen en el formato de la RFC 9457, Problem Details [13]; los que contesta el enrutador por su cuenta, como ese 405 o una ruta que no existe, salen en texto plano (medido). Sin un tipo de error propio, se usa about:blank y el título es la frase estándar del código, como «Not Found» [13]:
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{"type":"about:blank","title":"Not Found","status":404,"detail":"No hay ningún servicio llamado \"nomina\".","instance":"/servicios/nomina"}Una cosa más, porque una plantilla también enseña malos hábitos: la URL de cada servicio sale sin usuario, contraseña ni parámetros. https://usuario:clave@... o ?token=... llevan un secreto, y lo que sale por una API termina en bitácoras ajenas. La configuración rechaza lo primero y la puerta limpia lo segundo, cada uno con su prueba. La ruta sí sale, porque dice qué se revisa: un token metido en la ruta se publicaría entero.
¿Qué es testdata/ y por qué no estorba?
El comando go ignora las carpetas llamadas testdata: son el lugar para los datos de apoyo.
Así queda disponible para los datos que necesitan las pruebas [6]. En la plantilla guarda servicios.json, la lista del ejemplo, y una prueba lo valida: si alguien lo rompe, se entera antes que el primer lector.
¿Y dónde van las diez líneas de Iván?
La regla va en el dominio; la puerta solo la publica.
«Avisar si un servicio tarda más de un segundo» tiene dos partes. Decidir qué es lento es vocabulario del programa, igual que decidir qué es sano: va en servicio, como un método de Estado junto a OK(), y su prueba no necesita red. Decirlo hacia fuera es trabajo de httpapi: un campo más en su JSON. Si el segundo debe cambiarse sin recompilar, config lo lee. Ninguna carpeta nueva.
El martes, Iván escribió Lento() en servicio, su prueba, y un campo en httpapi. Diez líneas, como pensaba. Ninguna en pkg/util.
¿Cómo sé que la frontera existe?
Porque el CI la rompe a propósito en cada cambio y exige que la herramienta la rechace.
Una regla que nadie comprueba es una regla que nadie sabe si sigue viva. Por eso la plantilla trae herramientas/probar-frontera.sh, con dos experimentos en una carpeta temporal:
- Control. Un módulo cuya ruta empieza igual que la de la plantilla (
…/estructura-go/control) importainternal/servicio. La regla va por ruta, así que lo deja pasar: tiene que compilar. Si no compila, el experimento está mal montado y el guion se detiene en rojo. - Violación. Un módulo con una ruta ajena (
ejemplo.com/intruso) importa el mismo paquete, por el mismo camino. Tiene que fallar, y con el mensaje de la regla.
Ésta es la salida real:
1/2 control: github.com/HabilMX/estructura-go/control importa github.com/HabilMX/estructura-go/internal/servicio
✅ compiló: con la misma ruta de arriba, la regla lo deja pasar
2/2 violación: ejemplo.com/intruso importa github.com/HabilMX/estructura-go/internal/servicio
package ejemplo.com/intruso
main.go:3:8: use of internal package github.com/HabilMX/estructura-go/internal/servicio not allowed
✅ la herramienta go lo rechazó por la regla de internal/
✅ frontera: se sostiene¿Por qué el control? Porque un experimento que solo espera un rojo se pone «verde» también cuando falla por la razón equivocada, como una ruta mal escrita. El control busca el paquete por el mismo camino que la violación, así que un montaje roto lo pone rojo a él (lo comprobamos rompiendo la ruta).
Y la violación exige el mensaje exacto de la regla, no cualquier fallo. Entre los dos pasos solo cambia quién importa: la regla queda demostrada por sus dos lados.
El CI de GitHub corre ese guion junto con gofmt, go vet, las pruebas con el detector de carreras y la compilación de cmd/, en cada cambio, a mano y cada lunes, por si una versión nueva de Go rompe algo. Si el repositorio pasa 60 días sin actividad, GitHub apaga la corrida de los lunes y hay que encenderla otra vez [21].
¿Cuáles son los cinco errores más comunes?
Los tres primeros salen de crear la carpeta antes del problema que resuelve; los dos últimos, de poner el código del lado equivocado de una flecha.
1. Copiar golang-standards/project-layout en un proyecto chico
El propio repositorio lo desaconseja para eso.
El README dice que, si estás aprendiendo Go o haciendo una prueba de concepto o un proyecto sencillo, esa estructura «is an overkill» [2]. Y recomienda empezar con un main.go y un go.mod [2]. El repositorio tiene 19 carpetas en la raíz [3]. Copiarlas todas es heredar diecinueve preguntas sin respuesta, como le pasó a Iván.
2. Crear pkg/ por costumbre
La guía oficial no lo menciona, y Russ Cox señaló que la gran mayoría del ecosistema no lo usa.
En el issue #117 de ese repositorio, el 9 de abril de 2021, Russ Cox escribió que «the vast majority of packages in the Go ecosystem do not put the importable packages in a pkg subdirectory» [4]. La guía oficial no usa pkg/ en ningún ejemplo [1]. Y un servidor normalmente no tiene nada que exportar, así que su lógica va en internal/ [1].
3. Paquetes util, common o misc
Un nombre que no dice qué hay adentro se vuelve un cajón donde cae todo.
El blog de Go lo explica en Package names: «Packages named util, common, or misc provide clients with no sense of what the package contains» [9]. Con el tiempo acumulan dependencias y hacen más lenta la compilación [9]. Go Code Review Comments suma api, types e interfaces [10]. La salida: partir el cajón y nombrar cada parte por lo que hace (fecha, notificar).
4. Declarar la interfaz donde se implementa, «para los mocks»
La interfaz va del lado de quien la usa; del lado que implementa se devuelve un tipo concreto.
Go Code Review Comments lo dice de frente: «Do not define interfaces on the implementor side of an API “for mocking”» [10]. Y pide no declararla antes de usarla: sin un uso real no se sabe qué métodos necesita [10]. En la plantilla, revisar declara Consultor porque la usa, y el doble vive en un _test.go.
5. Partir por capas hasta cerrar un ciclo
Si models necesita algo de services y services importa models, el programa deja de compilar.
Pasa con las carpetas por capa (handlers/, services/, models/): tarde o temprano, la de abajo necesita algo de la de arriba. La especificación lo prohíbe [11], y go build lo dice así (medido):
package ejemplo.com/capas/models
imports ejemplo.com/capas/services from models.go
imports ejemplo.com/capas/models from services.go: import cycle not allowedEl extremo contrario tampoco ayuda: Andrew Gerrand advertía en 2012 que partir en paquetes demasiado chicos te deja atorado diseñando interfaces en lugar de terminar el trabajo [16]. La salida de la plantilla: un paquete de dominio que no importa a nadie, y las flechas hacia él.
| Error | Lo que lo delata | Fuente |
|---|---|---|
| 1. Copiar el layout completo | Carpetas vacías o con un solo archivo | [2] |
2. pkg/ por costumbre | Un servidor con pkg/ que nadie importa | [1][4] |
3. util, common, misc | Funciones que no tienen nada que ver entre sí | [9][10] |
| 4. Interfaz del lado que implementa | Una interfaz con una sola implementación y un Mock al lado | [10] |
| 5. Capas que se importan entre sí | import cycle not allowed | [11] |
Ejercicio: ¿puedes poner la frontera en rojo?
Vas a romper la frontera a propósito y la herramienta te va a decir, sola, si lo lograste.
Necesitas Go 1.27 o más nuevo (go version), Git y bash, unos 20 minutos. Si tu sistema trae un Go más viejo, como los repositorios de Linux Mint, instálalo desde go.dev [22]. Linux y macOS ya traen bash; en Windows, usa WSL o Git Bash.
Paso 1. Clona y comprueba que todo está en verde.
git clone https://github.com/HabilMX/estructura-go
cd estructura-go
go test ./...
bash herramientas/probar-frontera.shLo que debes ver: cinco líneas que empiezan con ok y, al final, ✅ frontera: se sostiene.
Paso 2. Predice antes de tocar. Anota qué pasa si renombras internal/ a interno/ y corriges los imports. ¿Compila? ¿Qué dice el guion?
Paso 3. Renombra y corrige los imports.
Copia solo la línea de sed de tu sistema: el de macOS pide un argumento vacío después de -i.
mv internal interno
# En Linux:
grep -rl 'estructura-go/internal/' --include='*.go' . | xargs sed -i 's#estructura-go/internal/#estructura-go/interno/#'
# En macOS:
grep -rl 'estructura-go/internal/' --include='*.go' . | xargs sed -i '' 's#estructura-go/internal/#estructura-go/interno/#'
go build ./...Si go build no dice nada, compiló: el programa no necesita internal/ para funcionar.
Paso 4. Corre el guion.
bash herramientas/probar-frontera.shDebe salir rojo, pero fíjate en cuál:
❌ el control NO compiló; el experimento está mal montado:El guion sigue buscando internal/servicio, que ya no existe, y se negó a darte un verde. Ése es el control trabajando.
Paso 5. Apunta el guion al paquete nuevo. Abre herramientas/probar-frontera.sh, busca la línea que empieza con paquete= y cambia internal por interno. Corre el guion otra vez:
❌ COMPILÓ: otro módulo pudo importar github.com/HabilMX/estructura-go/interno/servicio. La frontera no existe.Ahora sí: un módulo ajeno importó tu código y nadie lo detuvo. Con otro nombre, la carpeta es solo una carpeta.
Paso 6. Regresa todo.
git checkout -- . && git clean -fd
bash herramientas/probar-frontera.shOjo: git clean -fd también borra los archivos nuevos que hayas creado ahí, como tus notas.
Cómo sabes que lo hiciste bien: viste tres desenlaces distintos en cuatro corridas, en este orden: verde en el paso 1, rojo «el control NO compiló» en el 4, rojo «COMPILÓ» en el 5, y verde otra vez en el 6. Si en el paso 5 se repite «el control NO compiló», la línea paquete= no quedó con interno (o no guardaste). Si sale verde, internal/ todavía existe: revisa el paso 3.
Para ir más lejos (opcional): agrega a servicio una función que use algo de revisar. Antes de compilar, predice el mensaje. Luego compara con el del error 5.
Para llevar
Tres preguntas para hacerle a tu repositorio esta semana.
- De cada carpeta de la raíz: ¿qué problema resuelve hoy? Si la respuesta es «por si algún día», bórrala y anótalo en el README.
- Si otro equipo importara tu código mañana, ¿qué paquetes le dejarías usar? Todo lo demás, ¿está en
internal/? - ¿Cuál es tu paquete del que dependen todos? ¿Importa algo del proyecto? Si importa algo, ahí está el próximo ciclo.
¿Qué sigue?
En Go, la frontera la vigila la herramienta. En Python, la vigila la instalación, y es fácil no darse cuenta hasta que truena.
Tres días después, Iván ya movía carpetas sin miedo. Entonces le tocó un proyecto de Python. Las pruebas, en verde. Su compañera instaló el paquete: ModuleNotFoundError.
El archivo existía. Estaba en la carpeta. Las pruebas lo encontraban. Pero nunca había entrado al paquete.
¿Cómo pasa una prueba sobre un código que no existe para quien lo instala? En la entrega 2: una carpeta llamada src/, y por qué cambia todo.
La plantilla y el curso
- Plantilla: github.com/HabilMX/estructura-go. Se usa como plantilla de GitHub o se clona [19]. El README explica cada carpeta, lo que no trae, cuándo agregar cada cosa y cómo quitar el ejemplo. El código es MIT; el texto y las explicaciones son CC BY-SA 4.0.
- Versiones:
go.modpide Go 1.27, la versión que salió en agosto de 2026 [15]. Se probó con Go 1.27.3, la estable más reciente el 22 de octubre [14], y solo usa la biblioteca estándar. El CI toma la versión dego.mody busca el parche más reciente de la 1.27 [18]. - Curso de Go de Hábil: si
internal/, los paquetes o las pruebas de tabla son nuevos para ti, la lección 5 los cuenta desde cero: Curso de Go · Lección 5, Módulos y pruebas.
Escribo desde lo que vemos todos los días en Hábil, donde construimos sistemas para banca, seguros y empresas en México. Si en tu trabajo o en tu escuela se topan con un repositorio que nadie sabe dónde tocar, platiquemos: habil.mx
¿Qué hace Hábil?
Enseñamos a programar con lo mismo que usamos para construir: código probado, explicado y abierto.
Esta serie nace del mismo lugar que nuestros cursos. El Curso de Go va de cero a escribir un buen programa, y lo publicamos abierto, con licencia Creative Commons; su lección 5 cuenta los módulos, los paquetes y las pruebas que usa la plantilla de este artículo. Y en el catálogo hay más cursos de programación, gratuitos y con código probado y explicado paso a paso.
Mira el Curso de Go y el catálogo de Cursos de programación.
Si tu equipo hereda un sistema que nadie sabe dónde tocar, o necesita construir uno que se pueda seguir manteniendo, también lo hacemos: mira Construir su sistema a la medida. Pide tu primera conversación, sin costo. En ella revisamos si conviene configurar lo que ya tienen, usar un producto o construir, y qué recibirían en cada caso. Escríbenos por WhatsApp.
Fuentes consultadas el 22 de octubre de 2026. Las citas en inglés se reproducen tal cual, sin las cursivas ni las negritas del original.
Referencias
- Go Team, «Organizing a Go module», go.dev. https://go.dev/doc/modules/layout
- golang-standards, «Standard Go Project Layout» (README), GitHub. https://github.com/golang-standards/project-layout
- GitHub, datos del repositorio
golang-standards/project-layout(56,702 estrellas; 19 carpetas en la raíz; primer lugar por estrellas en la búsqueda «go project layout», 812 resultados, el siguiente con 954), medidos el 22-oct-2026. https://api.github.com/repos/golang-standards/project-layout - Russ Cox, «this is not a standard Go project layout», issue #117, 9 de abril de 2021. https://github.com/golang-standards/project-layout/issues/117
- Russ Cox, comentario en el issue #117, 28 de abril de 2021. https://github.com/golang-standards/project-layout/issues/117#issuecomment-828503689
- Go Team, «Command go»: «Internal packages» y «Test packages», pkg.go.dev. https://pkg.go.dev/cmd/go
- Go Team, «Go 1.4 Release Notes»: «Internal packages», go.dev. https://go.dev/doc/go1.4
- Go Team, «Go 1.5 Release Notes», go.dev. https://go.dev/doc/go1.5
- Sameer Ajmani, «Package names», The Go Blog, 4 de febrero de 2015. https://go.dev/blog/package-names
- Go Team, «Go Code Review Comments»: «Interfaces» y «Package Names», go.dev. https://go.dev/wiki/CodeReviewComments
- Go Team, «The Go Programming Language Specification»: «Import declarations» (versión go1.27, 26 de mayo de 2026). https://go.dev/ref/spec#Import_declarations
- Jonathan Amsterdam, «Routing Enhancements for Go 1.22», The Go Blog, 13 de febrero de 2024. https://go.dev/blog/routing-enhancements
- M. Nottingham, E. Wilde y S. Dalal, «RFC 9457: Problem Details for HTTP APIs», IETF, julio de 2023. https://www.rfc-editor.org/rfc/rfc9457
- Go Team, descargas de Go (estable vigente: go1.27.3). https://go.dev/dl/
- Go Team, «Go 1.27 Release Notes», go.dev. https://go.dev/doc/go1.27
- Andrew Gerrand, «Organizing Go code», The Go Blog, 16 de agosto de 2012. https://go.dev/blog/organizing-go-code
- Go Team, «Effective Go»: «Package names», go.dev. https://go.dev/doc/effective_go#package-names
- GitHub, «actions/setup-go» v5, README. https://github.com/actions/setup-go/blob/v5/README.md
- GitHub Docs, «Creating a template repository». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
- Go Team, «Go Modules Reference»: «Vendoring», go.dev. https://go.dev/ref/mod#vendoring
- GitHub Docs, «Disabling and enabling a workflow». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
- Go Team, «Download and install», go.dev. https://go.dev/doc/install
¿Su operación tiene estos desafíos?
¿Prefiere correo? Escríbanos a hola@habil.mx