Although I primarily write PHP, I also use Go (and you can hire me to write it for you). There are lots of things I like and dislike about the language.
The good thing about Google being heavily invested in Go is that they pay people to work on the language. The downside is that it can sometimes feel like Go is developed for Google, and not the community. You can use it if you want, but it’s not going to change for the needs of the community if they conflict with the needs of Google. Unfortunately Google also has a habit of cancelling projects and closing teams as well, so there could be disruption in the future. Google has attempted to add opt out telemetry in the past, which went down badly and was eventually changed to opt in telemetry.
Static binaries
By default, Go produces a single static binary for a project, with all dependencies included. This makes deployment extremely easy, as only one file needs to be copied, and there are a variety of ways to do this – even via USB stick if necessary for machines without a network connection. It’s particularly useful when deploying to platforms where I’m not as familiar with dynamic linking – especially Windows.
One slight issue I came across with this is that Go dynamically links against glibc on Linux, so you have to set an environment variable to force static binaries.
Cross compiling
Go has the best cross-platform toolchain for generating binaries. This is really useful if, like me, you develop on one platform but deploy to others. I prefer developing on Linux, because it has all the tools I need to automate parts of my workflow and make me as productive as possible. However, I often need to deploy software to Windows – unsurprisingly given its market share. For example, one of my clients needed software to download data from an FTP server automatically on a regular schedule, without any dependencies and with support for multiple versions of Windows. The only downside is that support for older versions of Windows is occasionally dropped, which is understandable but a challenge if you need to support them (you can of course use an older version of Go, with the caveat that the standard library may be missing some features that would be useful).
Whilst you can get other languages to compile to different platforms, in my experience it’s nowhere near as easy as Go. Take C for example – pretty much every platform of note has a C compiler, if only because the operating system is usually written in that language. Cross-compiling C can be a nightmare though – a combination of trial and error with strange linker errors that don’t describe the source of the problem. I spent a long time trying to get a GCC cross compiler working, and whilst I did make some progress, it still failed whenever it needed to link against libraries (which nearly all C code needs to do at some point).
Single coding style
I don’t agree with everything that gofmt requires, but having a single coding style does make it easier to read code written by other people – including a past version of myself. It’s also easy to get editors such as Visual Studio Code to auto-format a Go file on save, so you don’t even have to remember all the rules. My understanding is that a single coding style also simplifies the parser, and allows for some optimisations.
Errors not warnings
Compiling Go is a bit like using -Werror with C – every warning is an error. I like this behaviour, because it forces you to fix warnings instead of ignoring them, though very occasionally it’s annoying. For example, if you’ve added a package or variable that you’re not using yet but will be later, you won’t be able to compile the code until you use or remove the package or variable. Sometimes the static analysis tools erroneously report a package as not being used – though there is a way around this by putting a blank identifier (the underscore character) before the package name.
IDE integration
Go integrates better than any other language with my IDE of choice, Visual Studio Code. I install the official Go extension and everything works – syntax highlighting, static analysis, debugging, tests. It’s just such a smooth experience, especially compared with PHP.
Testing
The built-in testing library is pretty good, although I usually reach for testify. It feels like testing is part of the language and toolchain though, which is a change from languages like PHP where it’s been bolted on by third party libraries much later.
Function arguments
It doesn’t feel like there is a clear consensus – either in the language or the community – about whether functional arguments should be passed by value or reference. Some aspects are particularly confusing, such as when a slice is technically passed by value, but it’s the header that’s passed by value, so changing the slice in the function will change the original as well.
Arrays and slices
Slices are effectively arrays that can grow or shrink in size. Arrays have a fixed size, and if you write a function that takes an array as an argument, only an array of that exact size can be passed. I’m sure there are some edge cases where arrays are useful or more efficient than slices, but it feels like they could have been left out of the core.