Practical Go articles by Bartłomiej Klimczak. No fluff, just code that works.
While working with structures, there’s a possibility to initialize the structure without providing the keys of fields.
type SomeSturct struct {
FirstField string
SecondField bool
}
// ...
myStruct := SomeSturct{"", false}
If we want to force other (or even ourselfs) to explicitly providing the keys, we can add _ struct{} in the end of the structure.
type SomeSturct struct {
FirstField string
SecondField bool
_ struct{}
}
// COMPILATION ERROR
myStruct := SomeSturct{"", false}
The code above will produce too few values in SomeSturct literal error. Try it yourself.
Recently I’ve been on the GoGoConf conference in Cracow. It was a cool opportunity to learn more and meet interesting persons. Today I’ll tell you about my thoughts regarding every talk from 2019 edition. Most of the talks don’t have video available yet but when the videos will be published I’ll update the post.
Tackling contention: the monsters inside the ’sync.Locker’ - Roberto Clapis
I personally like Roberto a lot for the way he behaves and how professional he is. In his talk he explained how to fix performance problems using tools like pprof and trace. One of my takeaways from this talk is that we should use atomic package only really when necessary. It’s not worth use the atomic package instead of mutex because the mutexes use the atomic anyway.
There are, at least, two excellent articles about Software 2.0. Andrzej Karpathy and Carlos E. Perez wrote blog posts which cover the topic. In this article, I will explain how I understand the term “Software 2.0” what, I hope, will give you a different point of view on the main topic.
Software 2.0 is a set of three things which combined complement each other: tools, people, and process. All of them relates to each other closely. Because of the relationship, keeping all of the three areas separately in good condition helps to keep them all healthy in general.
Kafka is speedy and fault-tolerant distributed streaming platform. However, there are some situations when messages can disappear. It can happen due to misconfiguration or misunderstanding Kafka’s internals. In this article, I’ll explain when the data loss can happen and how to prevent it.
Publisher: Acknowledgment
When a message is sent to the publisher, the publisher waits for an acknowledgment (ACK) from the broker. There are three configuration options which can be used:
While developing a library, we create a directory structure to keep the code organized. However, some exported functions or struct should not be used by users of the library. The achieve that, call the package internal.
.
├── bar
│ ├── bar.go
│ └── internal
│ └── foobar.go
├── internal
│ └── foo.go
└── main.go
In the example above, the foo.go can be included only in the main.go. What’s more, only the bar.go is able to include the foobar.go file. It means, that only the direct parent of the internal package is allowed to use it’s internals.
There are many situations which can cause unavailability. One of them can be a bug in a software, bad architecture design decisions or even a human error. Depending on how the numbers are calculated, from 22% to even 70% of outages are caused by human error. Software engineers, DevOps or administrators cannot prevent all the outages but we can learn from ourselves to improve the stability and reliability of systems we are creating. In this article, I will present how Brainly learns on his mistakes to improve the stability and latency of its infrastructure.
In the microservices’ world, one thing what’s worth considering is a graceful shutdown. This is important to not lose data while shutting down a container. The container orchestrator like Kubernetes can restart the container by sending SIGTERM or SIGINT signal. Those signals can be handled to safely close all connections and finish background tasks.
Signals are propagated using os.Signal channel. You can add the above code to your main.
var gracefulStop = make(chan os.Signal)
signal.Notify(gracefulStop, syscall.SIGTERM)
signal.Notify(gracefulStop, syscall.SIGINT)
Then, we need a goroutine to handle signals.
When it comes to interfaces, a good practice is to create an interface where you’ll use it. Creating interfaces in advanced is not recommended in Go. There are two exceptions:
- you’re creating a library which will be used in different projects
- you’ll have more than 1 implementation
In the example below, we have a storage implementation.
type inMemoryStorage struct {
mutex *sync.Mutex
storage map[string]*Value
}
func NewStorage() *inMemoryStorage {
return &inMemoryStorage{
storage: map[string]*Value{},
mutex: &sync.Mutex{},
}
}
func (s inMemoryStorage) Set(ctx context.Context, value *Value) error {
s.mutex.Lock()
s.storage[value.key] = value
s.mutex.Unlock()
return nil
}
func (s inMemoryStorage) Get(ctx context.Context, key string) (*Value, error) {
if val, ok := s.storage[key]; ok {
return val, nil
}
return nil, nil
}
func (s inMemoryStorage) GetAll(ctx context.Context) map[string]*Value {
return s.storage
}
func (s inMemoryStorage) Remove(ctx context.Context, key string) error {
s.mutex.Lock()
delete(s.storage, key)
s.mutex.Unlock()
return nil
}
As you can see, we skipped the interface(s) because they are not needed here.
You should use the package github.com/pkg/errors instead of errors package for errors in your applications. The default package lacks a few things like:
- stack trace
- easy appending message to the error
- and more
It helps with debugging a lot. Below you can find an example error message with the stack trace.

An important thing to remember is that you should wrap every error which is from any external library or your freshly created error you want to return.
Golang uses a connection pool to manage opened connections for us. As a result, new connections are used when no free connection left and reuses them when golang finds an idle connection. The most important thing is that when two queries are called one by one it does not mean that the queries will use the same connection. It may be true if not in every case.
In the example below, you can find two queries which may seem to be executed in one connection. The problem is that the first query may use a different connection than the second one.