Golang for Beginners: The Complete 2026 Guide

Golang for Beginners: The Complete 2026 Guide

Golang for beginners: installation, syntax, data structures, goroutines. A step-by-step guide with code examples to get you started with Go development.

Golang (or simply Go) is a programming language from Google that has taken over the backend development world. Docker, Kubernetes, Terraform — all of it is written in Go. And if you want to break into modern development, Golang is a great choice.

In this guide, I’ll walk you from zero to writing your first application. No fluff — just practice and real examples.

Why Golang?

Before diving into syntax, let’s figure out why you’d want to learn yet another language in the first place.

Easy to learn

Golang was built on the philosophy of “less is more.” There are no classes, no inheritance, no exceptions in the traditional sense. You can learn the entire language in a couple of weeks.

// Here's what Hello World looks like in Go
package main

import "fmt"

func main() {
    fmt.Println("Привет, Golang!")
}

Performance

Go compiles to native code. That means speed close to C/C++, with the convenience of Python. No virtual machines, no JIT.

Built-in concurrency

Goroutines are Go’s killer feature. Want to run thousands of tasks in parallel? Easy. I wrote about this in detail in my article on concurrency in Go: channels vs sync .

A large community and plenty of jobs

Golang ranks in the top 10 most popular languages. There are plenty of job openings, salaries are high, and competition is lower than in Python or JavaScript.

Installing Golang

Windows

  1. Download the installer from the official website
  2. Run the .msi file
  3. Verify the installation:
go version
# go version go1.22.0 windows/amd64

macOS

# Via Homebrew (recommended)
brew install go

# Verify
go version

Linux

# Ubuntu/Debian
sudo apt update
sudo apt install golang-go

# Or download directly
wget https://golang.org/dl/go1.22.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

Environment setup

After installing, add the following to your .bashrc or .zshrc:

export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

Your first Golang program

Let’s write the classic Hello World and break down every line.

package main

import "fmt"

func main() {
    fmt.Println("Привет, Golang!")
}

Breaking down the code

  • package main — every Go file belongs to a package. main is the program’s entry point
  • import "fmt" — imports the standard package for formatted output
  • func main() — the main function, where execution begins
  • fmt.Println() — prints a string followed by a newline

Running the program

# Create a main.go file with the code above
go run main.go
# Привет, Golang!

# Or compile it into a binary
go build main.go
./main

Variables and data types

Go is a strictly typed language. That means every variable has a type, and the compiler enforces it.

Declaring variables

// Explicit type
var name string = "Алексей"
var age int = 30

// Short declaration (recommended)
name := "Алексей"
age := 30

// Multiple declaration
var (
    firstName = "Алексей"
    lastName  = "Горбунов"
    isActive  = true
)

Basic types

// Numbers
var i int = 42           // int, int8, int16, int32, int64
var f float64 = 3.14     // float32, float64
var c complex128 = 1+2i  // complex numbers

// Strings
var s string = "Hello"

// Booleans
var b bool = true

// Bytes and runes
var byte byte = 'A'      // alias for uint8
var r rune = 'Я'         // alias for int32 (Unicode)

Constants

const Pi = 3.14159
const (
    StatusOK    = 200
    StatusError = 500
)

// iota — auto-increment for constants
const (
    Sunday = iota  // 0
    Monday         // 1
    Tuesday        // 2
)

Golang data structures

Arrays

Arrays in Go have a fixed length:

// An array of 5 elements
var arr [5]int = [5]int{1, 2, 3, 4, 5}

// Short form
arr := [5]int{1, 2, 3, 4, 5}

// Auto-detect the length
arr := [...]int{1, 2, 3, 4, 5}

fmt.Println(arr[0])  // 1
fmt.Println(len(arr)) // 5

Slices

Slices are dynamic arrays, and they’re used far more often:

// Creating a slice
slice := []int{1, 2, 3, 4, 5}

// Via make
slice := make([]int, 5)      // length 5
slice := make([]int, 5, 10)  // length 5, capacity 10

// Adding elements
slice = append(slice, 6, 7, 8)

// Slicing an array
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]  // [2, 3, 4]

Maps

// Creating a map
m := map[string]int{
    "apple":  5,
    "banana": 3,
}

// Via make
m := make(map[string]int)

// Operations
m["orange"] = 7        // add
value := m["apple"]    // read
delete(m, "banana")    // delete

// Checking if a key exists
value, exists := m["apple"]
if exists {
    fmt.Println("Найдено:", value)
}

Functions in Golang

Basic syntax

// A simple function
func greet(name string) {
    fmt.Println("Привет,", name)
}

// A function with a return value
func add(a, b int) int {
    return a + b
}

// Multiple return values
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("деление на ноль")
    }
    return a / b, nil
}

Named return values

func rectangle(width, height float64) (area, perimeter float64) {
    area = width * height
    perimeter = 2 * (width + height)
    return // naked return
}

Variadic functions

func sum(numbers ...int) int {
    total := 0
    for _, n := range numbers {
        total += n
    }
    return total
}

// Call
sum(1, 2, 3, 4, 5)  // 15

Anonymous functions and closures

// Anonymous function
func() {
    fmt.Println("Я анонимная!")
}()

// Closure
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

c := counter()
fmt.Println(c())  // 1
fmt.Println(c())  // 2

Structs and methods

Defining a struct

type User struct {
    ID        int
    Name      string
    Email     string
    CreatedAt time.Time
}

// Creating an instance
user := User{
    ID:    1,
    Name:  "Алексей",
    Email: "alex@example.com",
}

// Accessing fields
fmt.Println(user.Name)

Struct methods

type Rectangle struct {
    Width  float64
    Height float64
}

// A method with a value receiver
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// A method with a pointer receiver (can modify the struct)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

rect := Rectangle{10, 5}
fmt.Println(rect.Area())  // 50
rect.Scale(2)
fmt.Println(rect.Area())  // 200

Embedding (composition)

type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person        // embedding
    Position string
    Salary   int
}

emp := Employee{
    Person:   Person{Name: "Алексей", Age: 30},
    Position: "Developer",
    Salary:   200000,
}

// Access Person's fields directly
fmt.Println(emp.Name)  // Алексей

Interfaces in Golang

Interfaces are a powerful tool for abstraction. In Go, they’re implemented implicitly.

// Defining an interface
type Writer interface {
    Write(data []byte) error
}

// A struct implementing the interface
type FileWriter struct {
    filename string
}

func (fw FileWriter) Write(data []byte) error {
    return os.WriteFile(fw.filename, data, 0644)
}

// Usage
func save(w Writer, data []byte) error {
    return w.Write(data)
}

fw := FileWriter{"output.txt"}
save(fw, []byte("Hello!"))

The empty interface

// interface{} or any — accepts a value of any type
func printAnything(v interface{}) {
    fmt.Printf("Тип: %T, Значение: %v\n", v, v)
}

printAnything(42)
printAnything("hello")
printAnything([]int{1, 2, 3})

Type assertion

var i interface{} = "hello"

// Type assertion
s := i.(string)
fmt.Println(s)  // hello

// Safe assertion
s, ok := i.(string)
if ok {
    fmt.Println("Это строка:", s)
}

// Type switch
switch v := i.(type) {
case string:
    fmt.Println("Строка:", v)
case int:
    fmt.Println("Число:", v)
default:
    fmt.Println("Неизвестный тип")
}

Error handling

Go has no exceptions. Instead, errors are handled explicitly by returning an error value.

func readFile(filename string) ([]byte, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return nil, fmt.Errorf("не удалось прочитать файл %s: %w", filename, err)
    }
    return data, nil
}

// Usage
data, err := readFile("config.json")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))

Creating your own errors

import "errors"

var ErrNotFound = errors.New("не найдено")

func findUser(id int) (*User, error) {
    // ... lookup logic
    if user == nil {
        return nil, ErrNotFound
    }
    return user, nil
}

// Checking the error type
if errors.Is(err, ErrNotFound) {
    fmt.Println("Пользователь не найден")
}

To learn how to avoid common mistakes when writing code, check out my article on Go anti-patterns .

Goroutines and channels

Concurrency is Go’s headline feature. Goroutines are lightweight threads, and channels are how they communicate with each other.

Goroutines

func sayHello(name string) {
    fmt.Println("Привет,", name)
}

func main() {
    // Launching a goroutine
    go sayHello("Алексей")
    go sayHello("Мария")
    
    // Wait for them to finish (a bad approach)
    time.Sleep(time.Second)
}

Channels

func main() {
    // Creating a channel
    ch := make(chan string)
    
    // Sending from a goroutine
    go func() {
        ch <- "Сообщение из горутины"
    }()
    
    // Receiving
    msg := <-ch
    fmt.Println(msg)
}

Buffered channels

// A channel with a buffer of 3 elements
ch := make(chan int, 3)

ch <- 1
ch <- 2
ch <- 3
// ch <- 4  // would block, buffer is full

fmt.Println(<-ch)  // 1

WaitGroup for synchronization

import "sync"

func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            fmt.Println("Горутина", n)
        }(i)
    }
    
    wg.Wait()  // wait for all goroutines to finish
    fmt.Println("Все горутины завершены")
}

For more on synchronization and choosing between channels and sync, read my article on concurrency in Go .

Working with JSON

import "encoding/json"

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
}

// Serialization (struct -> JSON)
user := User{ID: 1, Name: "Алексей"}
data, err := json.Marshal(user)
// {"id":1,"name":"Алексей"}

// Deserialization (JSON -> struct)
var user2 User
err = json.Unmarshal(data, &user2)

Working with HTTP

HTTP server

import "net/http"

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Привет, %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", helloHandler)
    
    fmt.Println("Сервер запущен на :8080")
    http.ListenAndServe(":8080", nil)
}

HTTP client

resp, err := http.Get("https://api.example.com/users")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))

Testing in Golang

Go has built-in tooling for testing. I’ve written about this in more detail in my article on testing in Go .

// math.go
package math

func Add(a, b int) int {
    return a + b
}

// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("Add(2, 3) = %d; want 5", result)
    }
}

Running the tests:

go test ./...
go test -v ./...  # verbose output
go test -cover    # code coverage

Modules and dependencies

Initializing a module

mkdir myproject
cd myproject
go mod init github.com/username/myproject

Adding dependencies

go get github.com/gin-gonic/gin
go get github.com/lib/pq

The go.mod file

module github.com/username/myproject

go 1.22

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/lib/pq v1.10.9
)

What to learn next

Once you’ve got the basic syntax down, I recommend digging deeper into:

  1. Concurrency — patterns for working with goroutines and channels
  2. Testing — table-driven tests, mocks, benchmarks
  3. Web frameworks — Gin, Echo, Fiber
  4. Databases — GORM, sqlx
  5. gRPC — for microservice architectures (read my article on gRPC in Go )

Useful resources

Conclusion

Golang is a language that makes development enjoyable. Simple syntax, fast compilation, a powerful standard library. Start with simple projects — CLI tools, HTTP servers, parsers — and gradually move on to more complex tasks.

If you have questions, leave a comment. And don’t forget to check out my other articles on Go — there’s a lot of practical advice drawn from real-world experience.

Good luck learning Golang! 🚀