Go Essentialspdf
Go Essentialspdf
partharora2233@gmail.com
Introduction to Go - Mastering Backend
If you want a breadth- rst view of the Go programming language ecosystem, this
practical guide provides the essential background to write clear and idiomatic Go.
You can follow this guide to learn to Go fast, but that will demand that you are
already a programmer.
You are expected to follow this guide with a computer, so you can practice and
understand what you are learning easily. Without wasting much time, let's get
started.
Introduction to Go
If you want a breadth- rst view of the Go programming language ecosystem, this
practical guide provides the essential background to write clear and idiomatic Go.
You can follow this guide to learn to Go fast, but that will demand that you are
already a programmer.
You are expected to follow this guide with a computer, so you can practice and
understand what you are learning easily. Without wasting much time, I think we
simplicity, e ciency, and performance. This section explores some key bene ts of
during development.
Introduction to Go Sold to
partharora2233@gmail.com
Page 2
Introduction to Go
Dependency Management
Go has a built-in dependency management system called "go modules." This
go modules, you can easily version and track the external packages your project
relies on, ensuring that your code remains stable and consistent.
Ease of Understanding
Go is designed to be straightforward to read. It has a simple and clean syntax that
emphasizes clarity and readability. This makes it easier for developers to understand
Cross Compilation
Go supports cross-compilation, which means you can build executable binaries for
Garbage Collection
Go incorporates automatic memory management through a garbage collector. This
Introduction to Go Sold to
partharora2233@gmail.com
Page 3
Introduction to Go
Concurrency
Ah, Yes, the Trademark Feature: Concurrency is one of the de ning features of Go.
This guide's Advanced Go section will give you a better concurrency introduction.
Introduction to Go Sold to
partharora2233@gmail.com
Page 4
Introduction to Go
this section.
The Go Playground
The Go Playground is a web-based tool the Go team provides that allows you to
write, run, and share Go code snippets online. It's an excellent resource for quickly
semantics, and behavior. It is the de nitive source for understanding how Go code
should be written and executed. You can nd the Go Language Speci cation here.
Introduction to Go Sold to
partharora2233@gmail.com
Page 5
Introduction to Go
for common programming tasks. Familiarizing yourself with the standard library is
essential, as it can save you time and e ort when implementing various
documentation here.
blog regularly publishes informative articles, tutorials, and insights from the Go team
and experienced developers. You can access the o cial Go blog here.
Introduction to Go Sold to
partharora2233@gmail.com
Page 6
Setting up Go Environment - Mastering Backend
Setting up Go Environment
Before writing Go code, you must set up your Go development environment on
your computer. This involves installing Go on your system and con guring it
Step 1. Downloading Go
Visit the o cial Go download website and download the stable release for your
computer.
Please choose the appropriate installer for your system, download it, and run the
installer. The installation process typically involves accepting the license agreement
and choosing a destination path for the Go installation. Once the installation is
GOPATH : This variable de nes the workspace directory for your Go projects. All
your Go code and dependencies will be organized within this workspace. You
can choose a directory on your system as your workspace and set the GOPATH
variable accordingly.
PATH : Add the Go binary directory to your system's PATH environment variable
to use the Go tools and binaries from any directory. This allows you to run Go
commands from the command line easily.
go env
Open your terminal (or Command prompt) and type the following Command:
go version
This Command should display the installed Go version, con rming that Go is
Go follows a speci c workspace structure. Inside your GOPATH, create the following
directories:
src : This directory contains the source code of your Go projects and their
packages. When you run a go get Command, the imported package will live
locally in this directory.
bin : Go binaries (executable les) will be placed in this directory when you
build your projects.
pkg : Compiled package les will be stored here after building your projects.
write your Go code. Some popular options for seamless Go development include:
If you want a quick and easy build, the GoLand IDE is a more suitable choice for
beginners. With your Go environment properly set up, you can write your rst Go
program and explore the language's features. As you progress through this guide,
use the resources mentioned earlier and practice to become pro cient in Go
programming.
Constructs of Golang
The Go compiler provides tools or commands for interacting with Go code. Let's
Go language syntax
Package Declaration
Every Go le belongs to a package and must start with a package declaration. The
Importing Packages
To use functionality from other packages, you need to import them. Import
statements are typically placed at the beginning of the le after the package
package main
import (
"fmt"
"math"
)
Function Declarations
Functions in Go are de ned using the func keyword. The basic syntax for a function
declaration is as follows:
// Function body
return result
Control Structures
Go provides standard control structures, such as if , for , switch , and select , which we
will cover later. The syntax for these control structures is similar to other C-like
languages.
Comments
Go supports both single-line and multi-line comments. Single-line comments start
/*
This is a
multi-line comment
*/
constructs in Go. When writing modular Go code (we will see what that means later.
identi er whose Name begins with a lowercase letter or underscore can only be
var localName string // can only be accessed in the source le it is declared in.
Note that Go developers use camel case for naming identi ers instead of snake
The go Command
The Go command is a fundamental tool in Go that serves various purposes,
go run : Compiles and runs a Go program in a single step. This is useful for
quickly testing small programs.
For more details and a full list of go commands, you can execute go help or go help <
Command> in your terminal. However, we will use some of them in the Milestone
these tools, you can use the go get Command followed by the URL or import path of
the tool.
For example:
go get github.com/golangci/golangci-lint/cmd/golangci-lint
This Command will download and install the golangci-lint tool, a popular static code
Staying Up to Date
To stay up to date with the latest changes and improvements, you should keep
First, you can check if a new version is available by visiting the Go website or
using the Go version command.
If there is a new version, download the installer for your operating system from
the o cial Go website and run it.
The installer will detect your current Go installation and perform the update.
I'd like to point out that updating your Go environment periodically is essential to
strong typing helps ensure type safety and clarity in code. Let's explore how to
Variable Declaration
To declare a variable, use the var keyword followed by the variable name, its type,
and an optional initial value. If you don't specify the value, the Go compiler will
You can make a short assignment declaration if you declare the variable in a
function. When you do so, the Go compiler assumes the variable type, allowing you
only to specify the variable Name and the value it stores. For example:
package main
import "fmt"
func main() {
In a backend system, you often need to work with di erent data types. You declare
variables explicitly, indicating their types for clarity and type safety.
package main
import "fmt"
func main() {
var databaseURL string
databaseURL = "mongodb://localhost:27017/mydb"
fmt.Println("Database URL:", databaseURL)
variable declaration in two ways and in both local and global scopes. You can also
package main
import "fmt"
func main() {
var (
maxConnections int = 100
timeoutSeconds int = 30
)
Constant Declaration
To declare a constant, use the const keyword followed by the constant Name, its
type (optional), and the constant's value. Constants are preferably declared globally
(outside of functions) but can be declared globally. Unlike variables, constants must
In a backend system, you might have xed values that remain constant throughout
package main
import "fmt"
func main() {
const (
MaxRetries = 3
Bu erSize = 1024
RequestType = "POST"
)
Enumerated Constants
Go also supports enumerated constants using the iota keyword. Enumerated
related named constants. For example, say we have a backend system that allows
users to subscribe to services; here is what our subscription model will look like:
package main
import "fmt"
func main() {
const (
Basic = iota // 0
Premium // 1 (automatically incremented from iota)
Gold // 2 (automatically incremented from iota)
Enterprise // 3 (automatically incremented from iota)
)
Determining and using variables and constants is essential for writing e ective and
maintainable Go code. Following the explicit type declaration approach can make
your code more robust and easier to understand, especially in large projects.
Integers
Used for representing whole numbers. Depending on the architecture, you have int
and uint types with di erent sizes, such as int8 , int16 , int32 , int64 , uint8 , uint16 ,
package main
import "fmt"
func main() {
var userID int64 = 12345
fmt.Println("User ID:", userID)
Floating-Point Numbers
Used for representing decimal numbers. You have oat32 and oat64 data types.
package main
import "fmt"
func main() {
var pi oat64 = 3.14159
fmt.Println("Value of Pi:", pi)
}
Strings
package main
import "fmt"
func main() {
username := "john_doe"
fmt.Println("Username:", username)
}
Boolean
package main
import "fmt"
func main() {
isLoggedIn := true
fmt.Println("Logged in?", isLoggedIn)
}
Composite Types
Go provides composite types that allow you to group multiple values together.
Arrays
package main
import "fmt"
func main() {
var numbers [5]int
numbers = [5]int{1, 2, 3, 4, 5}
Slices
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
Maps
package main
import "fmt"
func main() {
userAge := map[string]int{
"John": 30,
"Jane": 25,
}
Structs
Custom data types with named elds that group related data.
package main
import "fmt"
func main() {
user := User{ID: 1, Name: "John Doe", Email: "john@example.com", IsActive: true}
It is important to familiarize yourself with Maps, Structs, and Slices, as they are
Control Structures
Software systems use control structures to make decisions and control the code
If and Else
The if and else statements allow you to perform conditional execution in your code.
package main
import "fmt"
func main() {
age := 30
if age >= 18 {
fmt.Println("You are an adult.")
} else {
fmt.Println("You are a minor.")
}
}
For Loops
Go o ers four types of loop, each serving a speci c purpose in backend
development.
The classic for loop allows you to iterate over a range of values.
package main
import "fmt"
func main() {
for i := 1; i <= 5; i++ {
fmt.Println("Iteration:", i)
}
}
This type of for loop acts like a while loop, where the loop continues as long as the
condition is true.
package main
import "fmt"
func main() {
i := 1
for i <= 5 {
fmt.Println("Iteration:", i)
i++
}
}
A for loop with range is commonly used to iterate over elements of an array , slice ,
map , or string .
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
Infinite Loop
An in nite loop continues inde nitely until it encounters a break statement or exits
the program.
package main
import "fmt"
func main() {
i := 1
for {
fmt.Println("In nite Loop:", i)
i++
if i > 5 {
break
}
}
}
The for loop with range keyword is usually used in software engineering, as well as
code.
package main
import "fmt"
func main() {
day := "Sunday"
switch day {
case "Monday":
fmt.Println("It's the start of the week.")
case "Friday":
fmt.Println("It's the end of the workweek.")
case "Saturday", "Sunday":
fmt.Println("It's the weekend.")
default:
fmt.Println("It's a regular weekday.")
}
}
Functions in Go
In Go, functions are treated as rst-class citizens, allowing you to pass them as
arguments, return them from other functions, and even create anonymous
functions.
The Go compiler automatically recognizes the main function we have used as the
entry-point function where all relevant functions and operations are carried out.
By the rule of thumb, a project that must be built must have a main function.
Let's see how other functions are written. Below is a basic function that has an
package main
import "fmt"
// add accepts two input parameters of type int, and returns a single output of type int.
func add(a, b int) int {
return a + b
}
func main() {
// add function is called and used here.
result := add(10, 20)
fmt.Println("Sum:", result) // Output: Sum: 30
}
Just so you know, a function with output speci ed must have a return statement by
the rule of thumb. Also, when calling a function, you must satisfy its required
In Go, you can assign functions to variables and use them like any other variable.
package main
import "fmt"
func main() {
var area calculate
area = rectangleArea
fmt.Printf("Rectangle Area with length %.2f and width %.2f: %.2f\n", length, width, result)
// Output: Rectangle Area with length 5.00 and width 10.00: 50.00
}
crucial for writing e cient and performant code. Let's explore the Go language
used for storing local variables and function call frames. It has a xed size and is
variables are allocated on the stack, and when the function returns, the stack space
is freed.
On the other hand, the heap is used for dynamically allocated memory. Variables
created using new() or make() functions are allocated on the heap. The heap
provides more exibility for managing memory but requires explicit garbage
Pointers in Go
Pointers are variables that store the memory address of another variable. They
indicate mutable parameters. They allow you to directly access and modify the
value of the variable they point to. Pointers are essential for working with large data
In Go, you can declare a pointer using the symbol followed by the variable type it
points to. To access the value stored at the memory address pointed by a pointer,
use the symbol again.
package main
import "fmt"
func main() {
x := 10
ptr := &x // ptr is a pointer to the memory address of x
package main
import "fmt"
func main() {
x := 10
fmt.Println("Original value of x:", x) // Output: Original value of x: 10
double(&x)
fmt.Println("Doubled value of x:", x) // Output: Doubled value of x: 20
}
function, you're passing a reference to the same underlying array, not a copy of the
data.
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
fmt.Println("Original Slice:", numbers) // Output: Original Slice: [1 2 3 4 5]
modifySlice(numbers)
fmt.Println("Modi ed Slice:", numbers) // Output: Modi ed Slice: [100 2 3 4 5]
}
world systems. This is because if pointers are not used carefully, they can hinder
organized and reusable code. They allow you to de ne abstractions and behaviors
that t your application requirements. Let's explore these concepts while handling
Custom Types
We may use custom types to provide more context and safety when working with
package main
import "fmt"
func main() {
user := User{
ID: 123456,
Name: "john_doe",
}
Methods
Go allows you to de ne methods on types, including custom types. Methods are
functions that have a receiver argument, which speci es the type the method
operates on. This enables you to de ne behaviors that are closely associated with
the type.
For ease of explanation, let's de ne a method for calculating the area of a circle:
package main
import "fmt"
func main() {
circle := Circle{radius: 5}
called a Circle with a radius. The (c Circle) declaration before the function name is
called the receiver, which shows us that the Circle type supports the function
calculateArea .
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
user := User{
ID: 1,
Username: "john_doe",
Password: "secretpassword",
}
err := user.HashPassword()
if err != nil {
fmt.Println("Error hashing password:", err)
return
}
shapes code, an asterisk is absent before the function name, but an asterisk is
Go uses parameters of pointer type to indicate the function might modify that
parameter. The same rules apply to method receivers, too. They can be pointer
receivers (the type is a pointer) or value receivers (the type is a value type). The
following rules help you determine when to use each kind of receiver:
If your method modi es the receiver, you must use a pointer receiver.
If your method needs to handle nil instances, it must use a pointer receiver.
You can use a value receiver if your method doesn't modify the receiver.
Whether or not you use a value receiver for a method that doesn't modify the
receiver depends on the other methods declared on the type. When a type
has any pointer receiver methods, a common practice is to be consistent and use
pointer receivers for all methods, even the ones that don't modify the receiver.
Interfaces
Interfaces in Go provide a way to de ne a set of methods that a type must
polymorphism and allows you to write more generic and exible code. Let's extend
package main
import "fmt"
// printArea is a function that accepts any shape, doesn't modify it, but prints out it's area.
func printArea(shape Shape) {
fmt.Printf("Area: %.2f\n", shape.calculateArea())
}
func main() {
circle := Circle{radius: 5}
square := Square{side: 4}
authentication providers.
package main
import "fmt"
func main() {
localAuthProvider := &LocalAuthProvider{}
externalAuthProvider := &ExternalAuthProvider{}
Using the same function call, we can use the AuthenticateUser function to authenticate
users against di erent providers. Running this code on the Go Playground will print
In general, remember that your code should accept interfaces and return structs.
Error Handling in Go
Error handling is a critical aspect of software engineering, as it helps ensure the
reliability and stability of your applications. Go promotes a simple yet e ective error-
handling approach, making identifying and handling errors consistently easier. Let's
Error Type
In Go, errors are represented by the built-in error type, which is an interface that
To create an error, you can use the errors.New function, which returns an error with
package main
import (
"errors"
"fmt"
)
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err) // Output: Error: division by zero
} else {
fmt.Println("Result:", result) // Output: Result: 5
}
}
Custom Errors
You can also create custom error types by implementing the error interface for a
struct.
package main
import "fmt"
func main() {
err := process()
if err != nil {
fmt.Println("Error:", err) // Output: Error: custom error occurred
}
}
Error Wrapping
Go 1.13 introduced the errors.Wrap function from the "errors" package allows you to
import (
"fmt"
"github.com/pkg/errors"
)
func main() {
err := process()
if err != nil {
fmt.Println("Error:", err) // Output: Error: error occurred while processing data: root cause error
}
}
Using errors.Wrap , you can wrap an error with additional context at di erent
error. This approach allows you to handle both the result and the error
simultaneously.
package main
import (
"fmt"
"os"
)
func main() {
content, err := readFile("sample.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("File Content:", string(content))
}
Using this multiple return value pattern lets you quickly detect and handle errors at
each stage of your code, making it easier to recover from unexpected situations
Go has the panic and recovery mechanisms for exceptional cases. panic is used to halt
the normal ow of the program and trigger a panic. In contrast, recover is used to
catch and handle panics, allowing the program to continue execution gracefully.
It is important to use panic and recover sparingly and only for exceptional situations,
package main
import "fmt"
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
and reusable backend applications. This section explores the key concepts related
to packaging Go code, including the go.mod le, imports and exports, Go's naming
module github.com/happycoder/demodependency
go 1.17
require (
github.com/somepackage v1.2.3
github.com/anotherpackage v0.4.0
)
functions, types, or variables from another package, you need to import it. Import
package main
import (
"fmt"
"github.com/somepackage"
)
func main() {
fmt.Println(somepackage.SomeFunction())
}
As we discussed earlier, identi ers starting with an uppercase letter are exported
and accessible from other packages. Identi ers starting with a lowercase letter are
Package names should be lowercase, single words, and descriptive (e.g., utils ,
models ).
Variable and function names should use camelCase (e.g., userID , getUserInfo() ).
Structs and types should be in UpperCamelCase (e.g., type User struct { ... } ).
Organizing Go Codebases
Organizing Go codebases e ectively is crucial to avoid chaos as your project grows.
demoproject/
|- cmd/
| |- main.go
|
|- internal/
| |- somepackage/
| |- source le.go
|
|- pkg/
| |- yourpackage/
| |- source le.go
|
|- go.mod
cmd : The cmd directory contains the main entry point of your application. It
should be minimal and act as a glue to connect di erent parts of your
application.
interna l: The internal directory is used for code that should not be imported by
code outside your project. It provides encapsulation and keeps internal code
private.
pkg : The pkg directory contains code that can be imported and used by other
projects. It should expose functionality that can be reused.
concerns, and makes it easier to scale your application as it grows. This proves that
Advanced Go Constructs
As a Golang developer, exploring advanced Go constructs allows you to tackle
more complex challenges and design more sophisticated software systems. Let's
Context
Go's context package provides a powerful mechanism for managing the lifecycle
The context.Context type typically carries deadlines, cancellation signals, and request-
scoped data across API boundaries. It is especially crucial when you need to
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
go worker(ctx)
time.Sleep(5 * time.Second)
fmt.Println("Main: Timeout reached. Cancelling worker...")
}
Concurrency
Go's support for concurrent programming using goroutines and channels is one of
its most powerful features. Goroutines allow you to run functions concurrently,
package main
import (
"fmt"
"sync"
)
func main() {
messages := make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go printMessages(messages, &wg)
go printMessages(messages, &wg)
close(messages)
wg.Wait()
}
developers to de ne functions and data structures that work with di erent types.
Generics o er more exibility and expressiveness, but Go's simplicity and lack of
generics have been part of its charm. The introduction of generics may come with
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
index := ndIndex(numbers, 3)
fmt.Println("Index of 3:", index)
build a simple HTTP server using Go. We'll create an HTTP server that handles basic
user registration requests. For simplicity, we won't use databases and store the user
data in memory.
user-registration/
|- main.go
|- user.go
|- go.mod
Run these commands to initialize the new repository for Go and add any
requirements:
go mod init
go mod tidy
If you check the content of the generated le, you will nd something like the
following:
go.mod
module user-registration
go 1.19
main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
)
w.Header().Set("Content-Type", "application/json")
w.Write(response)
}
func main() {
http.HandleFunc("/register", handleUserRegistration)
http.HandleFunc("/users", handleGetAllUsers)
JSON is actually outside the scope of this guide, but please know that it is a data
formatting system. Next, we can have a user.go le to handle all user operations, but
user.go
package main
Now, let's run the server and test our endpoints using curl :
go run main.go
2. Register a new user by opening a new terminal and pasting this shell script:
curl <http://localhost:8080/users>
You should see the list of users, including the newly registered user:
This simple HTTP server demonstrates how to handle HTTP requests for user
registration and retrieval using Go. Of course, in a real-world scenario, you would
use a database to persist the user data and implement proper error handling and
validation. But this project is a great starting point to understand the basics of
backend engineer.
project, great! You can scale it by adding features like deleting and modifying user
details and storing users in a repository. You can also split the logic into di erent
packages, as we saw in the guide, and add to the logic. You can also Dockerize the
application and deploy it with practice. Read our Ultimate Guide to Docker article
here.
If you don’t understand the logic in the milestone project, you should engage in
research to try to understand everything that happened. Then when you do, you
At 25% Discount
Sold to
partharora2233@gmail.com