Miłosz Smółka is one of the co-founders of Three Dots Labs - a consulting and development agency specializing in building business applications in Go. He is also the co-creator of Watermill - a popular library for building event-driven applications using Go. Last but not least, Miłosz and his co-founder Robert Laszczak recently published a book on building modern business software using our favorite language. The book grabbed my attention because it shares a thought I have been playing around with for a long time. While there are many resources about Go available online or in print, only a handful of guides approach the building of business software from first-hand experience. This is why I am so excited to have Miłosz tell our readers a bit more about their work and the book.
Q: What brought you to Go?
Miłosz: I had a chance to work with many languages. We worked on several projects with Robert in the past but chose different paths at one point. I was writing low-level Linux software with C and Python, and Robert worked closer to the product with Python and PHP.
Around 2015, Go was already getting traction as a system programming language, so I was interested right away. Robert also found use cases for what he did at the time. We've learned Go on our own, loved it, and wanted to write it full-time.
Back then, it wasn't easy to find a company using Go. We were lucky to end up at the same startup where we started working closely again. Even though we specialized in different areas, we felt so productive with Go we didn't want to go back.
Q: What excites you the most about the language? Is there anything you think would help increase its adoption?
Miłosz: I switched several times between compiled and interpreted languages in my career. Working with Go, I feel you get the best from both worlds. You use strong typing, but it doesn't get in the way. You get compile-time safety, but Go compiles so fast you don't even think about it.
When you work in a team, having some limits boosts productivity. Go is explicit, minimal in features, and comes with a formatter. It helps you focus on the important things instead of endlessly discussing the approach.
Coming from Python, I loved the idea of building a single binary and deploying it anywhere. Dependency management was a pain for a while, but since Go Modules, it keeps getting better.
Go's concurrency support is one of a kind, and having goroutines as part of the language is a game-changer. We've used these patterns a lot in Watermill.
I'm sure generics will solve some workarounds we have to use now and encourage more people to try Go. I'm happy they're coming, even though we don't miss them right now.
When you work in a team, having some limits boosts productivity.
Q: After your first encounter with Go, you co-created Watermill - a popular library for building event-driven applications. What pushed you to do so?
Miłosz: Watermill started with a project where we had good reasons to use event-driven patterns.
While all Pub/Subs share the same core idea, each works differently, and libraries often expose a low-level API. We found working with them to be like calling HTTP by sending bytes over TCP.
We wanted a high-level library that's easy to use and abstracts away the implementation details like an HTTP router. It should work with any Pub/Sub in a plug-in style.
While we played with this idea, the startup we worked at didn't raise the next round of funding. Together with Robert and one other friend, Maciej, we decided not to look for a new job for a while and build Watermill instead.
I remember this time as both fun and scary. For a few months, we worked from a free co-working space and burned through our savings. We soon had the first stable version, and it took us a few more months to release the 1.0.
There are other libraries for working with Pub/Subs, but some feel more like a framework. It's the critical difference for us — Watermill is a library that's easy to plug in and doesn't force any way of building applications. I think using specialized libraries for each area works the best in Go.
I think using specialized libraries for each area works the best in Go.
Q: You co-founded a company that specializes in building Web and cloud-native applications in Go. How do you see Go fighting for attention with more traditional stacks?
Miłosz: There is no standard Web application framework in Go, like Django or Rails. Some frameworks are getting traction, but forcing this approach in Go won't be a good experience. It's far better to use smaller libraries that do one thing well. The standard library is so good that you often don't even need external packages.
Because there's no framework, starting a project in Go takes a bit more time and can be off-putting. But if you want to maintain the project over time in good shape, Go is a great choice. You're not locked to a framework and can add and remove libraries as you go. The initial effort pays off in the long run.
The initial effort pays off in the long run.
Q: Many people associate Go with Kubernetes and Microservices. Do you have an example project where you kept things simple?
Miłosz: The first post on our blog was Robert's Why using Microservices or Monolith can be just a detail?. We had a chance to build a product as a modular monolith, and it was a great experience. The project was designed with modules completely isolated from each other. If there was a need, we could quickly move one of them to a separate service. Until then, we didn't need to tackle the tricky things about distributed systems.
We still reinforce this idea in our articles. The pattern we found the most useful is Clean Architecture. It helps us keep loosely coupled packages within a single service.
Microservices have their uses, but they don't solve any design issues by themselves. You can just as well achieve loosely coupled architecture in a monolith. And if you're not careful, you can end up with the worst of both worlds, tightly coupled microservices (a distributed monolith).
I'm happy to see microservices are no longer the default choice for everyone. It seems we finally treat this pattern as a solution to organizational issues, not technical ones.
Microservices have their uses, but they don't solve any design issues by themselves.
Q: What is your opinion on Kubernetes?
Miłosz: I remember when Docker was considered a tool for local development. The regular opinion was that running containers on production is reckless. Now it's the default operating mode in the industry.
I think Kubernetes is well designed, and I like the move towards a unified way of deployments. Infrastructure as Code is a step in this direction, but it varies from company to company with many available tools. I'd love to have a standard, boring way of deploying software, with no need to learn deep stacks.
Q: Kubernetes is becoming the new standard, but is it always worth the setup overhead? Especially for small teams and early-phase projects?
Miłosz: My opinion is what I hope is common sense.
If you can afford a team maintaining the cluster and setting everything up, you can probably leverage it. But there are simpler ways to set up infrastructure for small teams. For example, we had a great experience with Google Cloud Run — you're still deploying containers, but it's stripped from features you don't need yet. We used to deploy Go binaries on Linux cloud instances, and it was also good enough.
For a new project, I'd choose a single cloud provider and pick the simplest way to deploy containers. I wouldn't worry much about vendor lock-in because we make all infrastructure-related code easy to replace (it's the core idea behind Clean Architecture).
When you're just starting, you should focus on the essential part of your product, the domain logic. If you do it right, all the rest is implementation details. We often release MVPs using an in-memory database as storage. It's easy to replace later, and we can quickly validate if we've built the right thing.
Focusing on the infrastructure at the beginning ends up with serious issues. You can have an automated multi-cloud Kubernetes environment, but it won't matter if your application is coupled to one database or your microservices are incorrectly split.
I know using new tools is fun. But in the end, the satisfaction of a working product beats it.
Q: You've recently released a book about building business software with go. What prompted you to do so?
Miłosz: Things are changing now, but when we started writing Web applications in Go, there were only a few resources on how to design them correctly. Most of Go's initial traction was thanks to infrastructure tools, which are quite different from business software.
Our central insight in the book is that business applications aren't only enterprise Java monoliths. Any Web application suffers if you don't approach the domain correctly.
The most common tip in the Go community is "just keep it simple." Sure, I agree, but that's a bit of lazy advice. Would you base your product's success on some vague idea of simplicity? When you build a business, you need a real strategy you can follow.
Patterns like Domain-Driven Design aren't rocket science. Still, it's not reasonable to ask the entire team to read a 500-page book and expect everyone to understand it the same. Many examples are available, but they often present too simple use-cases to justify using the domain-driven approach.
We had no background in writing enterprise software, so we learned the patterns from scratch and applied them in Go. We wanted to share how we did it in a way that's easy to grasp.
We decided to create a fully functional and quite complex open-source project that will seem like a modern application, but we'd hide some hard-to-spot issues under the hood. Then, we'd show how to fix the anti-patterns by refactoring. It's what Go with the Domain covers.
The book is just the beginning of what we'd like to share. We're currently working on more content and ways to present it, also for people just starting with Go.