para servicios Go
Dos líneas, y la consulta tiene quien la llame.
La instalación aquí es explícita — un middleware, un envoltorio del driver y dos líneas al principio de las funciones que merecen medirse — porque Go no tiene paso de compilación al que engancharse ni forma de alcanzar el contexto de quien llama sin que te lo den. Lo que consigues por esas dos líneas es aquello de lo que depende todo lo demás: una consulta con una función encima, para que un N+1 tenga dónde aterrizar.
Qué mide
- Cada función que marques
- ctx, span := sixty.Start(ctx, "orders.List") y una captura diferida. Dos líneas, y el contexto hay que ir pasándolo — que es el precio de que en Go no haya contexto ambiental, y la razón por la que el número es fiable cuando llega.
- Cada consulta, en cualquier driver de database/sql
- El agente envuelve el driver y no la conexión, así que pgx, lib/pq, MySQL y cualquier otro registrado contra database/sql se miden — y se replica cada interfaz opcional que implemente el driver real, así que nada de lo que pudiera hacer deja de funcionar.
- Filas, contadas según se leen
- No de una porción devuelta, porque database/sql no tiene ninguna. El span se cierra cuando se cierran las filas, que es lo que hace que el recuento sea real en vez de inferido.
- Qué función emitió qué consulta
- El contexto lleva el span actual, así que una consulta se acredita a la función que la emitió. Esto es lo que convierte «el endpoint se volvió lento» en «esta función empezó a hacer veinte llamadas».
- Tus rutas HTTP, de entrada y de salida
- sixty.Middleware envuelve cualquier cosa que hable net/http — chi, gorilla, echo, el mux de patrones de Go 1.22. Donde el router conoce un patrón que la ruta no, sixty.SetRoute lo nombra.
Qué encuentra
Cada una de estas se compara con la versión anterior, así que el hallazgo nombra el cambio y no el día.
Un N+1 que no estaba en la versión anterior
Una función que hacía una llamada a la base de datos por petición ahora hace veinte. Anclado al despliegue, así que el hallazgo nombra la versión y no la hora.
Una consulta que empezó a leer la tabla entera
Las filas por llamada pasaron de 30 a 30.000 — y aquí ese recuento se toma según itera quien llama, así que es lo que tu código leyó de verdad y no lo que la sentencia podría haber devuelto.
Tu propio código volviéndose más lento
Tiempo en la propia función, separado del tiempo en lo que llamó, para que abras el archivo correcto al primer intento.
Algo que ahora se llama dentro de un bucle
Misma velocidad por llamada, mismos datos, llamado cientos de veces donde antes se llamaba una.
Una consulta que calladamente no devuelve nada
Sin error, sin llamada lenta, y vacía donde antes tenía filas. Nada en un servicio Go reporta esto como un fallo, porque no falló nada.
Errores, agrupados por de dónde vinieron
Por ubicación en el código y no por mensaje — un fallo es una entrada por muchos valores que formatee dentro.
Qué aparece en tu lista
No es un panel que leer. Una tarjeta por hallazgo, con las cifras, la versión que las cambió y las pruebas que tu agente de código necesita para escribir el arreglo.
Estas son las dos regresiones que apps/demo-go incluye a propósito, contra el conjunto de datos que crea su comando de semilla — unos 1.000 usuarios y 30.000 pedidos. Puedes ejecutarlo y verlas llegar.
orders.GetUserOrdersUna refactorización se dejó la cláusula WHERE, así que cada llamada se trae la tabla entera y la filtra en Go. La latencia apenas se movió en una base de datos caliente — el número de filas es lo que lo delata, y es real porque se toma según se leen las filas.
desde la versión v2, frente a la v1
orders.EnrichOrdersUna única consulta por lotes se convirtió en una búsqueda por pedido. Cada una es rápida y está indexada, así que nada en un gráfico de latencia se mueve — solo el número.
desde la versión v2, frente a la v1
Cómo se instala
Dáselo al agente de código que ya tienes abierto. Encuentra main(), el handler y el sql.Open, pasa el contexto donde hay que pasarlo y marca las funciones que merecen medirse — que es el paso fácil de describir y tedioso de hacer a mano.
claude mcp add sixty \
-e SIXTY_API_KEY=sixty_sk_… \
-e SIXTY_ENDPOINT=https://ingest.sixty.sh \
-- npx -y @sixty-sh/mcp
# or by hand:
go get github.com/sixty-sh/sixty-go
// main(), before the server starts
defer sixty.Init(sixty.Config{}).Shutdown(context.Background())
// requests — anything that speaks net/http
http.ListenAndServe(":8080", sixty.Middleware(mux))
// queries — the driver you already use
db, err := sixty.Open("pgx", dsn)
// functions — the ones worth measuring
func (s *Store) GetUserOrders(ctx context.Context, id int64) (_ []Order, err error) {
ctx, span := sixty.Start(ctx, "orders.GetUserOrders")
defer span.Capture(&err)
...
}El identificador de versión se recoge por su cuenta de Vercel, Render, Railway, Fly, Heroku o GitHub Actions, y SIXTY_RELEASE lo fija en cualquier otro sitio. La primera comparación llega tras tu siguiente despliegue, porque no hay nada con lo que comparar una sola versión.
Qué no hace
- Hay que ir pasando el contexto. Una goroutine a la que no se le pasa el ctx hace su trabajo fuera de la operación — correcto para trabajo en segundo plano, incorrecto para un fan-out que querías medir. No hay una versión de esto con contexto ambiental y no la vamos a construir, porque la versión que adivina es la versión que atribuye las consultas de una petición a otra.
- No se capturan planes de consulta. Ese es el hallazgo que nombra una causa en vez de un síntoma — «esto dejó de usar su índice» — y en un servicio Go no llega.
- La CPU y la espera no se separan. Las goroutines migran entre hilos, así que un reloj por hilo no describe un span, y reportar uno sería un número equivocado justo cuando importa.
- Postgres es lo que el detector entiende en profundidad. Otra base de datos registrada contra database/sql sigue reportando tiempos, filas y recuentos; las conclusiones a nivel de sentencia son más débiles.
- Aquí nada marca tus funciones por ti. Saltarse ese paso deja la lista con rutas y consultas y nada en medio, que es la mitad que hace que el resto merezca leerse.
Si estás aquí por una de estas
Descubre qué hizo tu último cambio.
¡Pruébalo!¿Construyes otra cosa? Mira servicios Node, servicios Python o aplicaciones Ruby y Rails — esas páginas son distintas, y también lo es lo que podemos ver.