Things I miss about Spring Boot after switching to Go
I wrote production systems for a startup in Java & Spring Boot for 1.5 years before switching to Golang. That was a high-paced startup, so we were shipping features every week. There I got my hands dirty on some absolute backend technologies that are powering business-orientated backends, such as Java, Spring Boot, MySQL, Redis and Kafka.
Recently I made a switch to Golang because I wanted to move forward from just writing APIs & microservices. Go has been fantastic to work with, but moving away from the Spring ecosystem made me appreciate several things Spring Boot gets right.
A Complete System
Spring Boot is a complete system for backend stuff. It is designed with the "Batteries Included" philosophy, so you get most of the things out of the box. You will get most of the production features out of this box.
While Golang does not have any framework as mature as Spring Boot. This is not because the Golang ecosystem is not mature or Golang is not capable; this is because Golang is designed with the philosophy of "minimalism". Go have small libraries instead of giant frameworks.
Dependency Injection
While working on large codebases, this is the most helpful thing I've seen. The Spring Framework automatically manages all dependencies for you. In Spring Boot, dependencies are wired automatically through annotations like @Service and @Autowired. See this code example.
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}or even simpler
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}Using `@Autowired` is not recommended, but at least it works.
In Go, dependency wiring is usually done manually through constructors. This makes the dependency graph explicit but also means the application bootstrap code grows as the system gets larger.
type PaymentService struct {
}
func NewPaymentService() *PaymentService {
return &PaymentService{}
}
type OrderService struct {
paymentService *PaymentService
}
func NewOrderService(paymentService *PaymentService) *OrderService {
return &OrderService{
paymentService: paymentService,
}
}
func main() {
paymentService := NewPaymentService()
orderService := NewOrderService(paymentService)
_ = orderService
}This doesn't bother too much right now but will bother once we have hundreds of dependencies.
In Build Validations
In Spring Boot you get built-in validations for API requests. It helps you prevent ugly if/else statements.
public class CreateUserRequest {
@NotNull
@Email
private String email;
}These @NotNull & @Email annotations will make sure that the user has given a non-empty and valid email.
While in Golang you would need to do something like this:
type CreateUserRequest struct {
Email string `json:"string"`
}
// Validations
if req.Email == "" {
// Handle Error
}
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,6}$`)
if emailRegex.MatchString(req.Email) == false {
// Handle Invalid Email
}Spring's Highly Mature Ecosystem
In order to build a backend system, there are some essential things such as auth & security, database access, and health checks. If we build something modern, the system will need microservices, an API gateway, load balancing, service discovery, etc.
As Spring Boot comes with a "batteries included" mindset, it has all the tools required for fulfilling the above requirements.
Spring Security
Do you need JWT auth or form-based login or basic auth or OAuth? Spring Security has all of this in-built. You can have a fully working auth system within hours without even writing boring boilerplate code.
Spring Data
Spring Data is a part of the Spring Framework designed to significantly reduce the boilerplate code required to implement data access layers for various storage technologies. A fantastic thing about this is that it can generate queries automatically based on the method name you gave to it.
See this example:
@Entity
public class User {
@Id
private Long id;
private String email;
private String name;
}@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}That's it. You can use the findByEmail method in your service. While in Golang you would need to write a complete implementation of this method.
But one thing I would like to mention is that the Spring Data approach is not always best. Golang will give you complete control and transparency over your SQL queries, hence making it easy to debug. Also, it's hard to write complex JOINs in Spring Boot. Writing complex queries can sometimes feel less straightforward when using ORM abstractions.
Spring Boot Actuator
It proved you health monitoring, metrics collection and application insights with just a few lines of code. Golang might also have tools for this, but I feel Spring Boot Actuator is more integrated as being a part of a large framework.
Spring Cloud
Now we come to microservices and distributed systems. Spring Cloud offers everything we need to make, maintain and manage microservices in a distributed environment.
Where Go Actually Feels Better
After working with Spring Boot for around one and a half years, moving to Go made me appreciate a very different philosophy: simplicity over abstraction.
Spring Boot tries to reduce boilerplate through frameworks and automation.
Go takes a different approach — it removes abstraction layers and encourages explicit code.
In practice, this leads to some very interesting advantages.
Simpler Operational Model
Spring Boot applications run on the JVM. That means you deal with things like JVM startup time and memory tuning & GC tuning in some cases.
Go services are just compiled binaries. You build once and run.
go build
./serviceDeployment becomes extremely simple. A Go service can often be shipped as a single static binary, which makes container images smaller and startup times significantly faster.
Instant Startup
Spring Boot applications usually take several seconds to start. Go services typically start almost instantly. This becomes useful when running serverless workloads, autoscaling systems, short-lived background workers, etc.
Also, starting a debugger on a Spring Boot application locally took a few seconds, while Golang does it instantly. It makes my development life easier.
Concurrency Is Awesome
In Golang, concurrency feels very effective because it is built directly in the language, not as a framework feature. Golang does not have threads. Instead, it has something called 'goroutines'. These are extremely lightweight, and independent execution of work is handled by Go runtime instead of OS.
You need to create a goroutine? Just add go before a function call.
go processOrder(order)Golang also has something called 'channels' that are used to communicate between goroutines. Trust me, when writing highly concurrent systems, channels make things straightforward and easy.
I've heard that Java also introduced something called 'virtual threads' that makes concurrency easier and lightweight in Java as well. But I'm yet to try them.

This is a comparison done by a person on Medium.
So Which is Best?
Actually, this is the wrong question. It's not about which language is better. It's about what kind of systems you are trying to build.
If I were building a large enterprise platform with complex domain logic, Spring Boot would still be a fantastic choice.
But if I were building infrastructure tools, high-concurrency systems, or lightweight services, Go feels incredibly natural.
Systems Built Using Java
- Netflix
- Amazon
- Uber
- Spotify
Systems Built Using Go
- Docker
- Kubernetes
- Prometheus
- etcd
- CockroachDB
- Cloudflare
- Dropbox
Before You Go
If you made it this far, Thank You.
I usually write about backend engineering, distributed systems, and things I learn while working on real problems. Not theory — mostly practical stuff that I wish someone had explained to me earlier.
I run a free newsletter where I share these kinds of write-ups. No spam. Just occasional backend engineering notes.