Category Programming
Introduction
In the Go programming ecosystem, developers have a plethora of libraries available for solving common problems. However, some libraries may not always be the best fit for every project or developer preference. This article highlights a few Go libraries that I personally avoid using, along with the reasons behind these choices. The intention is not to discourage the use of these libraries universally but to shed light on potential challenges that may arise when using them, especially in larger or more complex projects.
Introduction
In this blog post, I want to share my approach to testing functions involving randomness in Go. Recently, I was asked how I would test a specific function that calculates possible directions for an object to move. Initially, I didn’t come up with a good idea. Here, I’ll discuss how I’d solve this problem in a real-world application with a detailed explanation.
The Function in Question
The function calculates all possible directions that an object can move (up, down, left, right) without violating boundaries. It then randomly selects a valid direction and returns the new coordinates.
The best to hash passwords in Go is using golang.org/x/crypto/bcrypt:
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
You should use the default bcrypt.DefaultCost just in case that the current value will become not sufficient and the default cost will increase.
We can trace NodeJS GC by using
node --trace-gc app.js
And use the performance tool to get the data in runtime.
const { PerformanceObserver } = require('perf_hooks');
// Create a performance observer
const obs = new PerformanceObserver((list) => {
const entry = list.getEntries()[0]
/*
The entry would be an instance of PerformanceEntry containing
metrics of garbage collection.
For example:
PerformanceEntry {
name: 'gc',
entryType: 'gc',
startTime: 2820.567669,
duration: 1.315709,
kind: 1
}
*/
});
// Subscribe notifications of GCs
obs.observe({ entryTypes: ['gc'] });
// Stop subscription
obs.disconnect();
To gather memory usage we can use the following code
The memory wall problem refers to a phenomenon that occurs in computer architecture when the processor’s speed outpaces the rate at which data can be transferred to and from the memory system. As a result, the processor must wait for the data to be fetched from memory, which slows down its performance and limits its speed.
The memory wall problem has become increasingly significant as processors have become faster and more powerful while memory speeds have not kept pace with these advancements. This means that even though the processor can execute instructions quickly, it spends a significant amount of time waiting for data to be transferred to and from memory.
The else keyword is a commonly used control structure in programming. It allows us to execute a block of code if a condition is not true. However, overusing else statements can lead to less readable and maintainable code. In this article, we’ll explore why we should avoid using else clauses in our code and look at some alternatives that can make our code more concise and readable.
Why Overusing else is a Bad Idea
One of the main arguments against using else statements is that they can make our code more complex and harder to read. As the number of conditions and branches increases, the code becomes harder to understand and maintain. Additionally, else clauses can make it difficult to follow the control flow of a program.
There are many practices and tactics that tackle testing. Today, I’ll share with you how I write tests for my projects. Please notice that you may find it useful when starting a new project or an independent part of existing applications. You may find it difficult to apply in an already existing application. It’s not impossible but it may be challenging.
Table of content
General rules for tests
Works out of the box
When someone clones our project, the person should be able to run basic tests without any setup. It’s a good thing when you have an open-source project as well as when you have a new team member. Or even for you after you reinstall your PC. I remember many projects where I had to spend a day or two to make it work and actually start developing something. It’s frustrating when you have to manually set up a DB connection, get proper permissions to an AWS account, configure it correctly, and so on.
I like having the core logic of our application free of distractions like too many technical “details” like logging or generating metrics. Of course, sometimes it’s hard to avoid it. I found in many projects a situation where we put the logger very deeply inside of the code. At the end of the day, we had the logger almost everywhere. In tests, we had to provide the mocked implementation everywhere as well. In most cases, the logger is a redundant dependency. In this article, I’ll argue that we should have the logger only in top-level functions.
Sometimes, we may want to use a library but a slightly modified version. It happens very often when we develop the library but test it in the context of an application. Go has a handy mechanism in go modules that can help us with it.
To make it work, we have to clone the library somewhere near the target project and run the following command in the application’s folder.
go mod edit -replace github.com/my/library ../path
The path can be both relative (to the application root folder) or absolute. The go.mod file will be edited as follows.
gRPC supports authentication. Adding it to your project is simple. All you have to do is configure it with just a few lines of code. One of the authentication types that gRPC supports is SSL/TLS. From the server-side, the code looks like this:
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
if err != nil {
// handle the error - no ignore it!
}
s := grpc.NewServer(grpc.Creds(creds))
The client has to update the code as shown below.
creds, err := credentials.NewClientTLSFromFile(certFile, "")
if err != nil {
// handle the error - no ignore it!
}
conn, _ := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
But, from where take the certificate? One of ways is using the openssl.