What is your approach when you need to learn something new? I have a very specific one and once again I tested it while learning Golang!
There's too much content to talk about, but my aim here is to list things that I found useful and that I specifically took the time to learn properly.
For the last 2 weeks I've been learning and building small applications with Golang. At the moment it's been almost 50h of code through many livestreams and it's been pretty awesome to learn something that I previously had a some small issues with the language.
In this two weeks journey I've crafted:
And all this because my boss asked me, once again, to learn a new technology to work on some ScyllaDB PoC's and demos... I wasn't too happy with the decision, but well, it's my job.
During the last year I've been studying Rust, and it's probably still too complex for me, but I've learnt some really cool concepts that made my switch to Go work like a charm!
In this article I'll give you some tips and advice to speed up your learning flow.
I'm a PHP developer and I'm used to the BEST CLI ever made (yes, it's Artisan), however through my journey as a developer I've been through awesome projects many of which have been..:
When I got to the Go environment, it started as a real problem. At least for me, the developer experience of Golang in terms of tools and documentation could be much better. Thinking about this, I decided to go through 3 commands that you HAVE TO LEARN at the beginning.
Remember: this is just a walkthrough with my own explanation of things. If you want detailed information, open the docs :)
Also: go docs sucks please someone put a syntax highlighter there
Depending on whether you want to modularise your application or have an organised environment, this will be the most useful command at first.
The go mod command manages all the dependencies within your project and also takes care of autoremoving anything that's no longer used.
First, inside your new empty folder, let's init a new module inside the project with go mod init:
mkdir go-fodase cd go-fodase # go mod init <module-name> go mod init github.com/danielhe4rt/go-fodase
This will create a new file in the project root called go.mod, which is basically the contents at the moment:
Here's the file, if you want to check it yourself:
# folder: ~/go-fodase cat go.mod # module github.com/danielhe4rt/gofodase # # go 1.23.2
After that, the next thing I really liked was the go mod tidy, which basically adds any missing dependencies and removes unused ones.
go mod tidy
This second one is just to keep into your mind that this exists and it's really useful! Probably your environment will run it EVERY TIME and you will get used to see imports vanishing :p
This is probably the most common command you'll use, since you HAVE to run your project, but here's how it works:
The most important thing to remember about this command is that when you run the go run command, it will look for the go.mod file in your current directory and use it as the basis for mapping your whole project (imports, packages, etc). Here's some examples:
# go run <your-main-file> # Environment 1 # . # ├── go.mod # └── main.go go run app.go # Environment 2 # . # ├── go.mod # └── src # └── main.go go run src/app.go
Here's our app.go content:
package main import( "fmt" ) func main () { fmt.Println("whats up! don't forget to like this article <3") }
Now you know the basics to execute an project! Literally, hello world!
My problem with Go has always been the way it's written, but after hours of coding I realised that it's simpler than I thought. As you might have guessed, I have a strong background in PHP and some experience with Rust.
When I started to learn Rust in 2023, fortunately a guy I'm a big fan of, Nuno Maduro (Laravel), gave a talk called "PHP for Rust Developers", which gave me some basic introduction to the syntax and gave me some breathing space while I was completely STOMPED by Rust.
Anyway, it was useful to me at the time, so why not do some comparisons?
In OOP we have classes, which is a really cool way of abstracting your code into small pieces, and you have something "like that". Golang can be seen as an odyssey, because it can be an epic development to turn the environment into whatever you want it to be.
Remember, Golang is a "high level language" that provides a "system level" syntax that allows you to easily work with "low level" implementations.
Under the Go syntax, you can
And you can use it for: Structs, Struct Fields, Struct Methods. Take a closer look:
mkdir go-fodase cd go-fodase # go mod init <module-name> go mod init github.com/danielhe4rt/go-fodase
In Rust, you have an more explicit approach (more oop like languages) where:
# folder: ~/go-fodase cat go.mod # module github.com/danielhe4rt/gofodase # # go 1.23.2
I'd like to make things explicit like PHP, Java and so on but if you stop to think is LESS CODE to write, but it also impacts the readability.
To be really honest, I'm the kind of person who would try to put, I don't know... LARAVEL in Go Environment, but that was already done in Goravel. Anyway, I really like the idea of working with "Interface/Contract Driven Development", and for the first time I got stuck with it in a language.
In Go, interfaces aren't "implemented" in a structure/class, and for an OOP guy like me, it's just crazy to have such a design decision fit into my head. Have a look at what is expected:
mkdir go-fodase cd go-fodase # go mod init <module-name> go mod init github.com/danielhe4rt/go-fodase
Now, when it comes to go: you don't have this explicit implementation of an "interface" inside a structure, and that's, hmm... weird? Instead, you just implement the interface's required methods, which go will check for you at compile time. It's fair to know that this is a compiled language and it should never be a problem, but I'm talking about my perspective with Developer Experience!
# folder: ~/go-fodase cat go.mod # module github.com/danielhe4rt/gofodase # # go 1.23.2
In any case, with some time coding in the language you will get used with it. Now, let's talk about what the base environment offers to you without download anything!
Now I'm talking about everything that Go serves you with the Standard Library, without download an third party package. Here's some chronological timeline for you:
I'm not joking about that, every day that I explore go I found some cool library under the standard ones. So, let's start talking about primitive types.
Like PHP, and unlike many other languages (Rust, Java, JS, etc), Golang needs "helper" functions to perform most of the related type operations. We can think of them as "anemic" types, since they don't have "utility" attached to them.
go mod tidy
So if you're working with a "String" type, you have other packages like strconv or strings to manipulate it! But here's a golden rule to never forget which package to look at: if your type is string, look for the same package with a pluralised name!
In a nutshell, this will give you functions related to []Type and Type:
Take a look at the code, so you can validate by yourself:
# go run <your-main-file> # Environment 1 # . # ├── go.mod # └── main.go go run app.go # Environment 2 # . # ├── go.mod # └── src # └── main.go go run src/app.go
That's supposed to be simple, but I struggled with that for a while until gets into my head. Maybe using Laravel and their helper functions for too many years made me forget how tough is to code without a Framework :D
While I was exploring tools and projects, I got a really good introduction to many projects and I'd like to list each of them and the libs I've used:
Here's a scrollable view of all the package implementations so you can check them out. There are PLENTY of cool std packages that can be cited here.
ATTENTION: that's a LOT OF CODE! :p
Don't forget to comment your favorite features (besides goroutines and channels) :p
mkdir go-fodase cd go-fodase # go mod init <module-name> go mod init github.com/danielhe4rt/go-fodase
Seriously, that's just amazing! So, lets keep going for tests now.
In my second project using Go, I saw an opportunity to learn tests while creating Requests and Responses objects. Inside the PHP environment, you are probably using a 3rd party library like PHPUnit or Pest. Right? Inside the Go environment, this is EASY! All you need to do is:
Let's say we have requests.go and requests_test.go in our package folder, where requests.go is:
# folder: ~/go-fodase cat go.mod # module github.com/danielhe4rt/gofodase # # go 1.23.2
A test in Go is considered PASSED (green) if (t *Testing.T).Errorf() is not called within your test function. It also follows the same concept of encapsulation introduced earlier:
mkdir go-fodase cd go-fodase # go mod init <module-name> go mod init github.com/danielhe4rt/go-fodase
You can do your own helper functions to test. Just make sure to not trespass the module domain on these tests!
I've been using Goland since day one, so most things have been easier for me. So every time I start a new test, I get this boilerplate with a Unity test structure that runs in parallel (goroutines) by default.
# folder: ~/go-fodase cat go.mod # module github.com/danielhe4rt/gofodase # # go 1.23.2
Ok, now we know how easy it is to write tests in Go, but how about running them? Simple task! All you need to do is navigate to the package folder and run:
go mod tidy
Please write down some tests for your stuff. It's not that hard if you decouple what's needed :p
During my last few years of development, I've always tried to modularise all my projects in a way that suits my needs, without getting stuck on "Clean Arch" or "Domain Driven Design" stuff. However, in my first attempts at splitting my packages, I got the "Cyclic Import" error and thought to myself: HOW LONG SINCE I'VE SEEN SOMETHING LIKE THAT?
During my 2 years in PHP, I had the same problem with import hell, where you couldn't not import the same file TWICE in a particular flow. This was before I met the PSR-4 (Autoloading) (which changed my PHP days forever!!) and now, years ago, I'm struggling with this in Go.
Let's consider a scenario of cyclic imports:
# go run <your-main-file> # Environment 1 # . # ├── go.mod # └── main.go go run app.go # Environment 2 # . # ├── go.mod # └── src # └── main.go go run src/app.go
When you try to compile something that flags Cyclic Imports in Go, you will receive an error like:
package main import( "fmt" ) func main () { fmt.Println("whats up! don't forget to like this article <3") }
And in this moment, you have to start breaking down your dependencies/packages in order to avoid it.
TLDR: don't import the same package in a place that will be loaded many times.
I didn't look it up, but it was the first time I'd seen the reserved word defer in a programming language. And since it was not part of the "generic reserved words", I ignored it for a whole week!
Then one of my work mates, Dusan, gave me a memory management lesson in Go after seeing me struggle with the language for a couple of hours. (Yes, this is a shout-out :p)
The thing is: whenever you open a buffer/connection to something, you SHOULD CLOSE IT! I remember when I was working with MapleStory servers (Java) in 2014, the most common problem was memory leaks, simply because the developers didn't close the connections to the DB.
This is OK to FORGET! But it's not OK to pass in the Code Review LOL
Here's an example in Java:
mkdir go-fodase cd go-fodase # go mod init <module-name> go mod init github.com/danielhe4rt/go-fodase
While coding Golang, they give this defer attribute for you to remember to close your stuff right after opening it.
Defer stands for "Deference" which is a way to "Clean" your resources after the execution of that specific portion of code ends.
# folder: ~/go-fodase cat go.mod # module github.com/danielhe4rt/gofodase # # go 1.23.2
You can also have many defer's within a function and the DEFER ORDER matters! If you defer database2 and then defer database1, both processes will be cleaned in the same order.
go mod tidy
This is a really simple way to not fuck up prevent your project from having a memory leak. Please remember to use it whenever you stream anything.
Error handling at first will be something like: check if the function you're using returns an error type and validate it EVERY FUCKING TIME! Here's an example of what I'm talking about:
# go run <your-main-file> # Environment 1 # . # ├── go.mod # └── main.go go run app.go # Environment 2 # . # ├── go.mod # └── src # └── main.go go run src/app.go
To be honest, I HATE this syntax. However, it's part of the language and will be something you'll come across during your days of coding.
Functions with errors can return error or (T, error), and fortunately Go will not let you forget that.
package main import( "fmt" ) func main () { fmt.Println("whats up! don't forget to like this article <3") }
Spam your code with err != nil and you will be fine, I promise! :D
Aside from all the stress and hours spent trying to understand the environment, it was a cool challenge to learn a new language together with my Twitch viewers. Many of them have been asking me for a long time to check it out and here we are.
All of these points reflect my personal development experience with the language, and the goal was to share things I've gone through during these 2 weeks of studying it.
Recently I was challenged by my teammate to complete a ScyllaDB challenge and it taught me a lot about parallelism, pools and rate limiting. This is the kind of challenge that many companies face to make their products perform better!
The goal of the challenge is to create a small Go command line application that inserts some random data into ScyllaDB while rate limiting the number of requests.
You can find the repository challenge here: github.com/basementdevs/throttling-requests-scylla-test. We're also hiring! You can find the open positions in our careers section!
Thank you for reading! I hope this article has provided valuable insights into learning Golang. Feel free to share your thoughts or experiences.
The above is the detailed content of This is all what Ive learned about Go in TWO Weeks!. For more information, please follow other related articles on the PHP Chinese website!