When It’s Time to Let Yourself Go
Developers experienced in PHP, Java, or C# often have a clear idea of what is meant by code quality and maintainability, in an OOP context. However, as anyone who has ever trained developers for a living will tell you: learning is easy; un-learning is hard. Learning to write Go is partly about learning unfamiliar syntax, of course. The concurrency model takes some getting used to. But part of letting yourself Go is forgetting about things that are considered important in OO languages.
It’s worth it, because Go plays a very different role in the tech ecosystem than what you’re used to. However, it means letting go of some familiar concepts, to let yourself Go.
Letting Yourself Go is a series of blog posts exploring ideas that developers (with a background in PHP, Java, C# etc.) will need to set aside while learning Go. Have a look through the collection of ideas, and let yourself Go.
This exploration of how to let yourself Go focuses on OOP class sizes and Go packages.
Class Files, Visibility Keywords, and the Indecent Exposure Anti-Pattern
Class Sizes
As OO developers, we often seek to keep class sizes under control (though you wouldn’t know it to look at some of the legacy codebases we have to work with). When we do code katas, recruitment tests etc., we rarely let the size of a class get much over 30 lines before we consider extracting logic into a separate collaborator and constructor injecting it. It feels like too much of a Single Responsibility Principle violation if we can’t.
Access Modifiers, and the Indecent Exposure Anti-Pattern
Access modifiers, or visibility keywords, are a common feature of OO languages. Terms like private
and public
determine whether a method is invocable from outside the scope of the current class. The Go equivalent is that a
function may be ’exported’ by making it start with a capital letter - but this is with regards to the whole package
within the codebase. An unexported function or variable may still be accessible from other files.
It’s important to note that the Indecent Exposure Anti-Pattern - making your privates public - is a cardinal sin in OO languages. Anyone unnecessarily adding to the public API of their class can expect pillory and opprobrium at code review.
The Blast Radius of Change
If a class has 25 lines of code, and 5 of those are in a private method, what do you need to consider before changing that method? The answer is not very much. The entire blast radius of any change you make likely fits on your screen. There are no invocations you aren’t looking at, no unexpected consequences elsewhere. It’s easy to reason about the effects of your change.
As a PHP developer, this seems like an important feature of high-quality, maintainable code. Decoupling is key. This may not be a concept that translates well into Go.
It’s not uncommon to see, for example, an 80 line file in a Go programme. No-one would bat an eyelid if 10 such files were in the same module. The blast radius of a change seems very different here. An un-exported (i.e. private) function may be invoked from anywhere within hundreds of lines of code, scattered across multiple files.
Packages in Go
It may feel like packages should be tiny, but that just isn’t the way things are done in Go. When writing idiomatic Go, we use a separate package only if we can sensibly imagine it being reused on its own. Tight coupling to multiple other packages in the same codebase might be a sign that a package is too small.
A package shouldn’t be too big to reason about, manage, or test. However, reducing package sizes specifically to limit the scope of unexported functions would be a mistake. Cobra is all one package. Many Go standard library packages contain dozens if not hundreds of files. It’s not the end of the world if your unexported function or variable is visible in other files, particularly as your IDE can find any usages for you. Trying to use Go function exporting and packages to mimic the behaviour of access modifiers isn’t necessary. Tightly controlling visibility is an idea you can let go of, when it’s time to let yourself Go.