My relationship with Go goes back to my time at Moltin, a YC-backed startup where the backend was built entirely in Go and deployed on Kubernetes. I wasn’t writing Go full-time then, but being surrounded by developers who discussed goroutines over breakfast rubbed off. In Developer Relations, I needed to write examples and demos in multiple languages, including our Go SDK, so I picked up the basics early on.
Fast-forward a few years, and while on holiday this time last year, I decided to dive deeper. I read two books on Go to properly refresh my knowledge. By then, I was already six months into my role at Turso, where, surprise — the platform stack is built in Go.
When I recently led a new Turso integration, I had to level up fast. Glauber (Turso CEO) had been helping with some platform updates that I couldn’t comfortably handle yet, but getting hands-on with Go again made me realise it’s actually not that bad. The standard library is solid, everything feels intentional, and there’s usually one obvious way to do things. That simplicity is refreshing.
I even like the error handling in Go. (Yeah, I said it.)
To really learn something, I have to go all in. So I made a decision — I’d go cold turkey from Node and rebuild the backend for my side project, CartQL, in Go.
I spent hours structuring files and folders, borrowing patterns from years of Rails and PHP. I leaned into service and repository layers, and probably read too many articles by matryer on “the right way” to organise things in Go.
I now have a starter structure I’m pretty happy with:
/myapp
│
├── cmd/
│ └── myapp/
│ └── main.go # Entry point
│
├── internal/
│ ├── http/ # HTTP layer (transport)
│ │ ├── handler/ # Route handlers
│ │ │ └── user_handler.go
│ │ ├── middleware/ # Middleware (logging, auth, etc)
│ │ │ └── logging.go
│ │ ├── context/ # Custom context helpers
│ │ │ └── context.go
│ │ ├── response/ # Common response formatting
│ │ │ └── json.go
│ │ └── routes.go # Router setup
│ │
│ ├── service/ # Business logic
│ │ └── user_service.go
│ │
│ ├── repository/ # Data persistence abstraction
│ │ └── user_repository.go
│ │
│ ├── model/ # Domain models, validation
│ │ └── user.go
│ │
│ └── config/ # App config and environment
│ └── config.go
│
├── pkg/ # Optional shared utilities
│ └── logger/
│ └── logger.go
│
└── go.mod
Go’s built-in HTML templating gets the job done, but just barely. It’s safe and minimal.
Right now, I’m using Templ for the frontend. It brings a JSX-like syntax to Go, with type safety and speed that makes rendering fun again. For interactive pages that don’t need to be full SPAs, HTMX adds just the right layer of dynamic behavior. And when something needs a little more UI interactivity, I try to avoid jumping straight to React. I’ve been playing with Alpine.js, which scratches the declarative itch, but anything beyond basic behaviors quickly turns into verbose, class-like structures that honestly feel like a step backward.
The truth is, I haven’t found my perfect frontend stack in Go, but I don’t really need to yet.
But for the backend? Let’s Fucking Go.