Go Micro
March 4, 2026 — By the Go Micro Team
Go Micro has always prioritized getting out of your way. But over time, the API accumulated multiple ways to do the same thing — micro.New(), micro.NewService(), service.New(), three different handler registration patterns. If you’re building something for AI agents or running a modular monolith, you shouldn’t have to choose between equivalent APIs.
We’ve cleaned it up. Here’s what changed and why.
Before, there were three ways to create a service:
// Old: three equivalent patterns
service := micro.New("greeter") // name only
service := micro.NewService(micro.Name("greeter")) // options only
service := service.New(service.Name("greeter")) // internal package
Now there’s one canonical pattern:
service := micro.New("greeter")
service := micro.New("greeter", micro.Address(":8080"))
Name is always the first argument. Options follow. NewService still works (it’s deprecated, not removed), but every example, doc, and guide now uses micro.New().
Registering handlers used to require reaching through to the server:
// Old: verbose, leaks abstraction
handler := service.Server().NewHandler(
&TaskService{tasks: make(map[string]*Task)},
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
)
service.Server().Handle(handler)
Now service.Handle() accepts handler options directly:
// New: clean, one call
service.Handle(
&TaskService{tasks: make(map[string]*Task)},
server.WithEndpointScopes("TaskService.Create", "tasks:write"),
)
For the common case with no options, it’s just:
service.Handle(new(Greeter))
Run multiple services in a single binary. Each service gets isolated state (server, client, store, cache) while sharing infrastructure (registry, broker, transport):
users := micro.New("users", micro.Address(":9001"))
orders := micro.New("orders", micro.Address(":9002"))
users.Handle(new(Users))
orders.Handle(new(Orders))
g := micro.NewGroup(users, orders)
g.Run()
Start as a monolith, split into separate binaries when you need independent scaling. The Group handles signals and coordinated shutdown — all services start together and stop together.
Every service is automatically an MCP tool. Add a gateway alongside your service with one option:
service := micro.New("greeter",
micro.Address(":9090"),
mcp.WithMCP(":3000"),
)
service.Handle(new(Greeter))
service.Run()
Your Go comments become tool descriptions. Your struct tags become parameter schemas. No glue code.
Stop() would silently swallow errors from BeforeStop hooks. Now all errors are properly propagated.service.Service interface, not a concrete type.If you’re building new services, use micro.New("name", opts...) and service.Handle(). That’s it.
If you have existing code using micro.NewService() or service.Server().Handle(), everything still works — we didn’t break anything. But the docs, examples, and guides all point to the new patterns now.
The goal is simple: when someone asks “how do I create a service?”, there should be exactly one answer.
See the updated Getting Started guide and the agent demo for working examples.