In another post, I described what are the criteria I would use to use to select a programming language. I chose Go
as the primary one.
However, after using it, those are not the only thing I like about Go. Here is the list of things I like about Go that stand out.
Go Routines (async by default)
This is the biggest thing
Java
code is synchronous, if we want to have async processing then we have to use Reactive Programming (which is the hardest paradigm I have learnt). Every new request in java creates a new OS thread - which takes up too many resources.
JavaScript
uses async
and await
to accomplish this. Same for c#
and rust
.
Go
does async IO operation by default, you don’t need to explicitly write anything to achieve async.
Go Routines are awesome. It creates lightweight threads (which are not related to OS threads) hence we can create thousands of them without worrying about resources.
[Case study] I wanted to do performance testing. I started using JMeter but soon I had to have multiple instances running just to do performance testing. Then I chose Gatling because of its async actor model; it was nice but the setup process was a bit tedious. Then I came across k6, it makes use of go routines the best way, now I could generate the same amount of traffic from my local machine.
[Case study]
We made a mistake in our Java code. There was async operation that had to be started after 2 seconds. So we added Thread.sleep(2)
. Even though it was async, it created threads and (Little did we know) there were apparently thousands of such requests, hence our “async” operation crashed the whole server. That can never happen in Go.
Best of everything
For readability use python
For performance use rust
For everything use java (nobody ever got fired for chosing java)
For fast delivery use python, javascript
Notice how Go is not there in any of choice? It is because Go is #2 in everything.
If you use Python then you compromise performance, if you use C then you compromise readability. But if you chose Go, then you get the best of everything.
It is easiest to write clean and performant code with GO (and fewer ways to go wrong)
Minimal keywords
Go doesn’t have a rich set of keywords.
For example, other programming language has while
, for
, and do while
for loops; Go
has only for
.
Go doesn’t have ternary operator, because it has if
.
Formatting
I personally have some issues with Go formatter. However, I love the fact that go has formatter.
The formatter is built inside Go lang. So every code in Go across geography and projects is formatted equally.
+clean coding
Easy learning curve
Based on the above two points, since it has minimal keywords hence there are minimal ways something can be achieved and the code is formatted the same way, it is easy to learn Go.
Compiler
If a variable is not used but declared or the import is not used but declared then it does not allow to compile.
Along with that, the compiler is one of the fastest. (as compared to other languages where we need to integrate sonar to get formatting or unused variable issues. These things take time, while Go compiler does all those things blazingly fast)
+clean coding
Minimal magic
If you add @Transactional
in spring(java), it takes care of starting, ending, and committing your transactions. It is magic. But it is double edged sword. It can do harm if not coded properly.
Go is IdiotProof
Also it is hard to master the magic code. It would take weeks or months for anyone to learn Spring annotations or Ruby coding styles.
I could not figure out from where my spring/micronaut project is injecting java.time
module of jackson
library - I have been working in java for 10 years. But I could figure out from where aws-otel
library is injecting otel
library’s code by searching for just a few mins - I never worked on Go PROD code
Testing (and code analysis)
Go has testing, coverage, benchmarking, race condition detection, test with random data generation, static code analysis, and http mock server in built.
Testing
go test
to test all the *_test.go files
Coverage
go build -cover
to check coverage with tests
Benchmarking
go test -bench="."
to benchmark, example output of a function that calculates primenumber as following
go test -bench=. -benchtime=10s
BenchmarkPrimeNumbers/input_size_100-4 3010218 4073 ns/op
BenchmarkPrimeNumbers/input_size_1000-4 143540 86319 ns/op
BenchmarkPrimeNumbers/input_size_74382-4 451 26289573 ns/op
BenchmarkPrimeNumbers/input_size_382399-4 43 240926221 ns/op
PASS
ok github.com/xyz/random 54.723s
Race detection
go test -race
can check if two go routines are trying to read write the same memory address
Test with random data generation
In the following example, we don’t need to input any hinduArabic
integers, Go will randomly provide integers and test agains inverted method (Roman to Hindu-Arabic and Hindu-Arabic to Roman conversion should be the same)
The quick.Check
method will randomly suply data and will call assertion
method
func TestPropertiesOfConversion(t *testing.T) {
assertion := func(hinduArabic int) bool {
roman := ConvertToRoman(hinduArabic)
fromRoman := ConvertToHinduArabic(roman)
return fromRoman == hinduArabic
}
if err := quick.Check(assertion, nil); err != nil {
t.Error("failed checks", err)
}
}
Reference: Learn Go with tests
Static code analysis
go vet
to check for potential issues our code might have. (Remember, unused imports and variables do not count in this because those are issues for sure and it won’t allow you to compile)
Http mock
We have to use WireMock
(arguably awesome tool) to create mock api servers. Go has httptest package, that can create the same thing out of box from language.
Statically typed
So you don’t get surprises in PROD
Smaller binary
Since it compiles to assembly directly, it is only one library and we don’t need any dependency or need to install any software (I am talking to you JDK, Python, and NodeJs) to run the Go binary.
In fact, I’ve seen that binary running inside scratch
container. That means that it does not even require proper OS (almost).
My Java Spring project was 50 MiB jar file (plug OS plus JDK) but the same Go program was having 15 MiB
Faster startup
I have seen Go projects starting as fast as python or nodejs.
My Java Spring project took 30 seconds to start but the same Go program took 5 seconds.
Smaller resource footprint
Because of the above reasons, it requires minimal resources to run
My Java Spring project took 50+ MiB of RAM after start but the same Go program consumed 5 MiB.
Cloud Ready
Because of the above reasons, it is best suited for cloud deployment
Many (if not most) cloud tools are created in Go. Examples, Kubernetes, Docker, OpenTelemetry, Sops, CockroachDB, Consul, Hugo, Terraform, etc.
Dependency
You don’t have to import any binary to the project, Everything is source code. You just provide github link and go will fetch sourcecode and make the dependency available.
Since, everything is source code (and no class), the final binary will have only code available that are used. (Compare this with java, where even if you use only one method of a utility library, but if the jar file is available in dependency then it will include in final binary and the every class from that utility potentially can be loaded in JVM)
Immutability
What do you think output of the following code?
account := Account{InitialBalance: 50.0}
account.Add(50.0)
fmt.PrintF(account.Balance())
Do you think output will be 100.0
? Then, you are wrong. Go code is immutable by default, you either have to assign it to a new variable or pass as a pointer
Better pointer
Sometimes it is better to have pointers for performance reasons.
Even in that case, it does not allow us to do pointer arithmetics.
IDE
VS Code has plugins that work like charm. IntelliJ GoLand is (paid) awesome tool.