Go is a simple, fast, and concurrent programming language. Its simplicity in design makes it an amazing programming language to work with. Go is currently gaining a lot of popularity, and a lot of organizations now prefer to write their backend in Go.

go logo

Go was designed at Google in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson to improve programming productivity in an era of multicore, networked machines and large codebases.The designers wanted to address criticisms of other languages in use at Google, but keep their useful characteristics:

  • Static typing and run-time efficiency (like C)
  • Readability and usability (like Python)
  • High-performance networking and multiprocessing

Its designers were primarily motivated by their shared dislike of C++.

Go was publicly announced in November 2009,and version 1.0 was released in March 2012. Go is widely used in production at Google and in many other organizations and open-source projects like Kubernetes, Prometheus, and Docker.

In retrospect the Go authors judged Go to be successful due to the overall engineering work around the language, including the runtime support for the language’s concurrency feature.

Although the design of most languages concentrates on innovations in syntax, semantics, or typing, Go is focused on the software development process itself. The principal unusual property of the language itself—concurrency—addressed problems that arose with the proliferation of multicore CPUs in the 2010s. But more significant was the early work that established fundamentals for packaging, dependencies, build, test, deployment, and other workaday tasks of the software development world, aspects that are not usually foremost in language design.

Go Installation for linux

  1. Download Binary
  2. Remove old go version (if any)
    sudo rm -rf /usr/local/go
    
  3. Install go binary
    sudo tar -C /usr/local -xzf go1.24.2.linux-amd64.tar.gz
    
  4. Add /usr/local/go/bin to the PATH environment variable
    echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
    source ~/.bashrc
    
  5. Verify installation
    go version
    
    Outputs:
    go version go1.23.1 linux/amd64
    

Let’s write some code

  1. Make a directory
    mkdir go-example
    cd go-example
    
  2. Enable dependecy tracking
    go mod init example/hello
    
  3. Create a file hello.go in which to write our code.
    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello, World!")
    }
    
    In this code, we:
    • Declare a main package (a package is a way to group functions, and it’s made up of all the files in the same directory).
    • Import the popular fmt package, which contains functions for formatting text, including printing to the console. This package is one of the standard library packages we got when we installed Go.
    • Implement a main function to print a message to the console. A main function executes by default when we run the main package.
  4. Run the code
    go run hello.go
    
    Output:
    Hello, World!
    

The go run command is one of many go commands we’ll use to get things done with Go. Use the go help command to get a list of the others:

govind@debian:~$ go help
Go is a tool for managing Go source code.

Usage:

	go <command> [arguments]

The commands are:

	bug         start a bug report
	build       compile packages and dependencies
	clean       remove object files and cached files
	doc         show documentation for package or symbol
	env         print Go environment information
	fix         update packages to use new APIs
	fmt         gofmt (reformat) package sources
	generate    generate Go files by processing source
	get         add dependencies to current module and install them
	install     compile and install packages and dependencies
	list        list packages or modules
	mod         module maintenance
	work        workspace maintenance
	run         compile and run Go program
	telemetry   manage telemetry data and settings
	test        test packages
	tool        run specified go tool
	version     print Go version
	vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Packages and Imports

Go does not support Classes like in OOPs programming languages such as Java, it uses the package system instead. Each package is a directory in your workspace, and each go file must belong to some package. Hence, each file should start with the keyword package followed by the package name. A go executable must contain package main.

A package can be imported by using the import keyword followed by the list of packages inside parenthesis.

The standard library comes preinstalled, with Go, and contains the most essential and useful packages. “fmt” is used to export Println which prints to console.

Go does not allow unused imports.

package main

import (
	"fmt"
	"log"
	"time"
)

Using an external package

When we need our code to do something that might have been implemented by someone else, we can look for a package that has functions we can use in our code.

Let’s Make our printed message a little more interesting with a function from an external module.

  1. Visit pkg.go.dev and search for a “quote” package.
  2. Locate and click the rsc.io/quote package in search results (if we see rsc.io/quote/v4, ignore it for now).
  3. In the Documentation section, under Index, note the list of functions we can call from our code. we’ll use the Go function.
  4. At the top of this page, note that package quote is included in the rsc.io/quote module.

we can use the pkg.go.dev site to find published modules whose packages have functions we can use in our own code. Packages are published in modules like rsc.io/quote where others can use them. Modules are improved with new versions over time, and we can upgrade our code to use the improved versions.

  1. Create a file quote.go and add the following code

    package main
    
    import (
    	"fmt"
    	"rsc.io/quote"
    )
    
    func main() {
        fmt.Println(quote.Go())
    }
    
  2. Add new module requirements and sums.

    Go will add the quote module as a requirement, as well as a go.sum file for use in authenticating the module.

    go mod tidy
    
  3. Run the code to see the message generated by the function we’re calling.

    go run quote.go
    

    Output:

    Don't communicate by sharing memory, share memory by communicating.
    

    Notice that our code calls the Go function, printing a clever message about communication.

When we ran go mod tidy, it located and downloaded the rsc.io/quote module that contains the package you imported. By default, it downloaded the latest version.

Language concepts

1. Variables

Go’s basic primitive types are bool, string, int, uint, float, and complex, the size of the type can be specified next to the type, uint32. A variable is declared by the var keyword followed by the variable name and the type.

variables can also be initialized with a shorthand notation := as Go can infer the type.

Just like imports, unused variables are not allowed.

package main

var x int = 5
var y int = 6

sum := x + y

Also, Go does not use semicolons to end a statement.

An important point to note is how Go scopes variables with a package, a variable is public if the first letter is in Captial, else private, same goes for functions.

package main

X := 5 // public
y := 6 // private

func Add() { // public

}

func add() { // private

}

2. Functions

Functions are an essential part of Go, and of course, the above won’t work as execution has to happen in a function body. Functions are declared with the keyword func followed by the function name, arguments, and return type. A Go application must contain the main function which is the entry point to the application. It does not take any arguments or return anything. The opening braces of the function must start at the same level as the function and cannot move to the new line.

function parameters are declared with their name followed by the type and separated by a comma. The return type must be provided if the function returns, as a shortcut the return variable can also be declared to avoid declaring another variable inside the function, here’s an example.

package main

func sum(x int, y int) int {
	sum := x + y
	return sum
}

// OR

func sum(x int, y int) (sum int) {
	sum := x + y
	return
}

3. Arrays, Slices, and Maps

Arrays can be declared by simply specifying the datatype next to brackets with an integer denoting the size of the array. Then, arrays can be assigned by their index, a more convenient way to initialize is to use the shorthand syntax along with the data in parenthesis.

package main

var arr [4]int
arr[0] = 1
arr[1] = 2

// OR

arr := [4]int{1,2,3,4}

But there’s a problem here. You cannot modify the length of the array, wouldn’t it be more convenient when you don’t know the size yet? That’s where Slices come in, slices are simply dynamic arrays. you can declare a slice just like arrays, without specifying the size.

Slices can be really useful in performing a lot of operations. The copy or append function can be used to manipulate the slice. Slices can also be concatenated using the append with the spread operator(…). A slice can be sliced using its indices within the brackets. Below are some examples.

package main

import "fmt"

func main() {
	slice := []int{1,2,3,4,5}

	slice1 := slice[2:]
	slice2 := slice[:2]

	slice = append(slice ,4)

	slicecat := append(slice, slice...)

	fmt.Println(slice, slice1, slice2, slicecat)
}

The append function does not modify the slice but returns a new slice from the given one. Here’s the output.

# output
[1 2 3 4 5 4] [3 4 5] [1 2] [1 2 3 4 5 4 1 2 3 4 5 4]

Maps are equivalent to a HasMap in Java or a Dictionary in python. They store key-value pairs. A map can be created using the make keyword followed by the keyword map and the datatype of the key in brackets and value next to it.

Maps are simple to operate on, they can be assigned values by using the [ ] operator specifying the key and value, and a key can be removed by using the delete function.

package main

import "fmt"

func main() {
	elements := make(map[string]int)

	elements["first"] = 1
	elements["second"] = 2

	fmt.Println(elements)

	delete(elements, "first")

	fmt.Println(elements)
}

Output:

map[first:1 second:2]
map[second:2]

3. Loops

Loops in Go exist in the simplest form, there is only one looping syntax, the for loop. The for loop can be written in multiple ways to meet your looping needs. The first syntax is a familiar one starting with the pointer variable i and followed by condition and incrementation. The below example will print 1 to 5.

package main

import "fmt"

func main() {
	arr := []int{1,2,3,4,5}

	for i := 0; i < len(arr); i++ {
		fmt.Println(arr[i])
	}
}

Oh! you badly miss the while loop? Don’t worry, Go has you covered, all you have to do is mention the condition with the for loop and use a pointer declared outside the loop just how you would when using a while loop.

package main

import "fmt"

func main() {
	arr := []int{1,2,3,4,5}

	i := 0
	for i < len(arr) {
		fmt.Println(arr[i])
		i++
	}
}

The range function provides an easy way to access the index as well as value.

package main

import "fmt"

func main() {
	arr := []int{1,2,3,4,5}

	for i,v := range(arr) {
		fmt.Println(i, v)
	}
}

4. Struct

The struct keyword is to define a shape to your data. Since Go does not support classes, data of a certain shape requirement can be stored in variables of that type of struct. A struct is created using the keyword type and its properties can be accessed by the . operator.

package main

import "fmt"

func main() {
	type Animal struct {
		Name 		string
		animalType 	string
	}

	giraffe := Animal("Giraffe", "Mammal")

	fmt.Println(giraffe.Name, giraffe.animalType)
}

5. nil, error, and multiple return values

Go provides some smooth ways to handle the error and nil values. Both error and nil are native built-in types that can be used to perform validation before performing some operation. Go also supports returning multiple types from a function, this can be done using specifying the type within parenthesis in place of the return type.

package main

import "fmt"

func main() {
	a,b := sum(5,10)
}

func sum(x int, y int) (sum int, diff int) {
	sum = x + y
	diff = x - y
	return
}

errors or nil can be returned depending on the operation performed using an if check. Here’s an example showing how you can handle errors by checking the input for a square root function.

package main

import (
	"fmt"
	"math"
	"errors"
)

func main() {
	result, err := sqrt(25)

	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(result)
	}
}

func sqrt(x float64) (float64, error) {
	if x < 0 {
		return 0 , errors.New("x must be non negative number")
	}

	retun math.Sqrt(x), nil
}

6. Pointers

Pointers in Go are similar to pointers in other languages, you can refer to the memory address of the variable by prefixing the variable with an ampersand(&) symbol and dereference it using an asterisk(*), by default, go passes arguments by value not reference, you can accomplish this by prefixing that type of the argument in the function with an asterisk(*), here’s an example.

package main

import "fmt"

func main() {
	i := 5
	increment(&i)
	fmt.Println(i)
}

func increment(i *int) {
	*i++
}

without the & it’d print 5 as a copy of the variable would have been passed, and once we have the reference, we need to deference the memory to get the value by using * on the variable again.

That’s it! you should be now ready to write your first program in Go, try practicing the code snippets and if you feel like challenging yourself more, try writing a basic HTTP server using the net package. This should give you enough practice to write some awesome packages or contribute to your favorite Go repository.

We’ll explore concurrency and other powerful features of Go in another blog—so stay tuned.

Thanks for sticking around and coding along!

Here are some additional resources if you wish to dive deeper.

Until next time happy coding, and go Go!