My GoLang background
3 months ago I joined Lightstreams to develop together with the team an authorisation protocol to manage programmable file permission access to protected content in decentralised networks in order to bring privacy and confidentiality into the peer to peer ecosystem.
As you may now, GoLang is widely used in blockchain community for its performance and flexibility. What you maybe don’t know is the fact, it’s VERY highly opinionated language.
To be honest, for first 2 weeks I didn’t like it. After all, I was developing in an objected oriented PHP for 7 years. Now? Have deep affection for it it!
Top 5 things I like
1. Compiler
Damn fast compiler. Change in a medium size project? Less than 2 seconds.
2. It gets things done
The whole ideology behind GoLang seems to be oriented on practicality. I can setup a HTTP server, build an executable, make a github release and run it straight away, make a low level TCP proxy/bridge in just few lines of code without importing any 3th party library. No boilerplate.
Straight to the point, cuts through the bullshit, gets the job done while being maintainable. DONE. NEXT.
3. Sharing by communicating
Do not communicate by sharing memory; instead, share memory by communicating.
Go’s concurrency primitives - goroutines and channels - provide an elegant and distinct means of structuring concurrent software. When working with technology that heavily relies on asynchronous communication over network, being supported by language without spending 50% of your time on handling threads/locking/mutexes/race conditions manually, allows you to focus on the business logic and ship faster.
4. Packaging
I hated this one at the beginning because I was stubborn. If you spend 7 years creating entity
, repository
, controller
, service
directories, the first experience with GoLang will feel unnatural. Later you realise, it kind of, has baked-in DDD methodologies in the core. At least in my opinion.
To make an example:
- tar package implements access to tar archives
- bufio package implements buffered I/O
- net package provides a portable interface for network I/O, including TCP/IP, UDP, domain name resolution, and Unix domain sockets
This naturally encapsulates responsibilities into separate packages and makes it easier to argue about bounded contexts because comparing to other languages PHP/Java, the “Class visibility public/protected/private” is defined on a package(export) level which is sooo weird when starting with the language but beautiful once you realise how to leverage it.
5. Pointers/Slices/Buffers/GC?
Pointers / Slices(dynamic arrays implemented with pointers) / Buffers / Garbage collector. Ideal combination for building long running daemons/streams support, without the memory overhead.
Extra. No exceptions!
I guess the best at the end? PHP/Java developers, including myself, often fall into the trap of structuring the flow of the program with exceptions
and wrapping everything in a try catch
. Or worst, letting the exceptions bubble up over stacktrace, without even documenting it ofc.
Go made this very clear. You have a: error and a panic.
Is absolutely normal to see code like:
createNodeDir() (string, error) {...}
_, err := createNodeDir()
if err != nil {
...
}
You always check for error, which is something, expected to happen and encourages you to handle them while simply ignoring the rest of the returned values if err
is not nil
.
In case something really unexpected happens, you raise a well named: panic()
:)
Extra Extra. Multiple return values! ☆
As from the example above, you can return multiple values from a method which is SOOO USEFUL, PRACTICAL without the necessity of creating a specific DTO (Data Transfer Object) for each use case. Don’t take me wrong, I am still fan of VOs (Value Objects) as they ensure data integrity but minimising amount of DTO really speeds up the development from my, so far collected, experience.
Top 3 things I am not a big fan of
1. Struct constructors
Given a struct:
type BrokenEncapsulation struct {
requireAttribute1 string
requiredAttribute2 int
}
the fact I can construct it without actually passing/initializing the required attributes is a no go
(pan intended) for me.
I know, is very practical, flexible, it’s not a typical “OOP Class” but it will surely make large codebase harder to maintain once more developers join the project without strict code reviews in place. We all have some lazy/rushed days and then the next day, when you will try to access one of the attributes from already initialized Struct, bum bum.
Is the same problem like being able to pass Null
where I expected a proper Class in Java… WTF? Luckily Kotlin will save them.
ALWAYS use a factory method like: New(requireAttribute1, requiredAttribute2): Struct {
Thx to: @TitPetric for suggesting to add it to the article.
2. Interfaces
The thing is… there is no Class implements Interface
.
Which makes it very powerful, but also inconvenient to refactor. As far as your Struct
implements the same methods signatures as some Interface
, you are satisfying it.
The problem with this flexibility is the ability to refactor. When you change the signature, at least my IDE (GoLand) right now, is unable to figure, which Structs
were originally implementing it. The moment I do any change to a Interface signature
, the link between the Struct and Interface
is broken.
BUT! There is a way around it. You can make an extra assertion to validate that a Struct
implements a specific Interface
on compilation time:
package main
import (
"fmt"
)
type I interface {
pleaseImplementMe()
}
type S struct {
}
var _ I = S{}
var _ = I(&S{})
func main() {
fmt.Println("Testing interface validation")
}
Thx to: @sh0dan for suggesting to add it to the article.
3. Dependency manager
One would think… such a important component would be implemented from the beginning but only recently they added, kind of working, version support <~=
…
It’s really painful to maintain project dependencies currently (using godep
at the moment) but well, it’s a new trendy language, this will get solved soon.
We can just commit vendors for now. Just joking, kind of, let’s try to forget about the days where there was no vendor
folder…
Next steps
Read this whole book and get deeper understanding of the design decisions behind GoLang and master buffers/networking/security/concurrency!
… Devil is in the details.