Go is a powerful language known for its simplicity, speed, and concurrency model. But the Go community also traditionally takes a strong stance against frameworks and centralized conventions — and that’s where many teams, especially those coming from structured environments like Rails, Django, or FastAPI, hit a wall.
This post explores the structural gap in Go development, why it exists, and how Encore is helping fill that void with a convention-driven framework that brings clarity, consistency, and productivity back to backend development.
There is no explicit style guide, although there is certainly a recognizable “Go style”.
From its inception, Go has been designed to avoid the complexity that often comes with large frameworks. Its creators intentionally left many things out — project structures, routing, migrations, background processing — in favor of giving developers freedom and flexibility.
This philosophy has real benefits. Go’s simplicity makes it easy to learn. Its concurrency primitives (goroutines and channels) are elegant and performant. And its standard library is solid and efficient.
But as many teams have learned firsthand: minimalism at the language level doesn’t scale effortlessly to the team level.
Frameworks like Ruby on Rails or Django exist for a reason: they encode best practices, reduce boilerplate, and help teams move faster by making thousands of decisions for you — decisions about naming, folder structure, routing, testing, and how to glue everything together.
They offer what Go intentionally avoids: opinions.
When you use Rails or Django, you get structure, defaults, and integrated tooling out of the box. When you use Go, you get a blank slate.
And that blank slate can be liberating — or paralyzing.
For individual developers or small projects, Go’s lack of structure may not seem like a problem. But for teams building serious systems, it introduces friction and inefficiency across the board.
Here’s what teams consistently run into:
There’s no standard for project layout, file structure, service boundaries, or even how to write HTTP handlers. This leads to:
In Go, many common tasks like background jobs, database migrations, and routing are handled through third-party libraries rather than built-in tooling. While the ecosystem offers high-quality options, there's no single "Go-endorsed" path or integrated framework, so teams often spend time researching libraries, wiring them together, and maintaining consistency across services.
Since Go’s culture is intentionally anti-framework, most teams end up building everything themselves. That typically means spending precious development time on tasks not building any unique product value:
Without enforced structure, codebases diverge. Teams working on different services develop their own conventions. As one engineer put it, "Every Go service felt like its own little world."
Encore.go is one example of a framework that aims to help teams transition to Go more smoothly. Encore gives teams conventions, built-in tooling, and cloud-native defaults that eliminate boilerplate and reduce DevOps overhead — so developers can focus on building products, not plumbing.
“We would never have been able to build our ebooks product in the time we did without Encore. It provided exactly the clarity, tooling, and stability we needed.”
Rather than starting from scratch with raw Go, some teams use Encore as a way to adopt Go incrementally — without giving up the structure they’re used to.
Here’s what that can look like in practice:
Encore recognizes a Go package as a service. When running your app or deploying, it automatically provisions the required infrastructure and generates the connection boilerplate.
/my-app
├── encore.app // ... and other top-level project files
│
├── hello // hello service (a Go package)
│ ├── hello.go // hello service code
│ └── hello_test.go // tests for hello service
│
└── world // world service (a Go package)
└── world.go // world service code
See the app structure documentation for more details.
Using the //encore:api
annotation, you define an API endpoint in pure Go:
package hello // service name
//encore:api public
func Ping(ctx context.Context, params *PingParams) (*PingResponse, error) {
msg := fmt.Sprintf("Hello, %s!", params.Name)
return &PingResponse{Message: msg}, nil
}
Encore then generates the required boilerplate and documentation automatically.
Learn more in the docs for defining APIs.
Just run:
encore run
Encore will automatically set up the local infrastructure and a local development dashboard with API docs, architecture diagrams, tracing, and logs.
Use Encore’s open-source tooling to build Docker images, or opt into Encore Cloud for automated infrastructure provisioning and deployment to your cloud on AWS or GCP.
Bookshop.org started moving from Rails to Go for better scalability. But the team quickly realized Go’s minimalism came at a cost.
“We didn’t have the conventions we were used to, and that created inconsistency in our applications. And while Go produces a single binary, there was still a lot of Terraform and bespoke deployment work.”
Adopting Encore brought back the structure and productivity they had with Rails — and the efficiency of Go and Google Cloud Run, automated through Encore Cloud, saved them serious money in the process.
“We’re on track to save over $60,000 per year compared to our legacy Rails application.”
Quiqup hit performance ceilings with Rails and wanted to move to Go — but without the microservices overhead. Encore’s modular monolith model struck the perfect balance.
"I loved the modular monolith concept of Encore. Microservices are too heavy to manage without a big team, and monoliths have their own issues. Encore was the perfect balance."
Today, Quiqup runs over 30 services and 200+ API endpoints — with no dedicated DevOps engineers — thanks to Encore Cloud’s automatic infrastructure and observability.
“If we were onboarding people onto Go without Encore, it would have taken 2–3x longer to get productive.”
Go’s minimalism is a mighty feature — but not when it slows teams down.
Encore.go brings the structure, defaults, and tooling that Go leaves out, helping teams ship faster with fewer DevOps headaches and less duplicated effort.
It’s not about replacing Go’s strengths, it's about not forcing every team to reinvent the same foundations every time.
Want more structure in your Go project?
We’re happy to chat and share what we’ve learned from helping other teams. Book a 1:1 intro — no pressure, just a conversation.