When I first started with Go, debugging felt like a mysterious art. Print statements everywhere, logging like a detective trying to solve a case… it worked until it didn’t. That’s when I met Delve, the Go debugger that actually made me feel in control of my programs.

In this blog, I’ll walk you through Delve not as a dry tool, but as a friend who helps you pause, peek, and play inside your Go code. We’ll run small programs, use commands, and peek under the hood a little to understand how it works.

Why Delve?

Go has always been marketed as simple, but debugging is not just about “seeing errors.” It’s about:

  • Inspecting variables at runtime
  • Stepping through code line by line
  • Understanding the state when something goes wrong
  • Catching those sneaky logical bugs that logs alone won’t show

Delve gives us that power in a Go-native way. Unlike generic debuggers, Delve is built for Go and knows about goroutines, channels, and Go’s runtime model

Installing Delve

First things first, get Delve installed. On most systems:

go install github.com/go-delve/delve/cmd/dlv@latest

Now you’ve got the dlv command ready to roll.

Your First Debug Session

Let’s write a small Go program:

package main

import "fmt"

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

func main() {
    x := 10
    y := 20
    result := add(x, y)
    fmt.Println("Result:", result)
}

Normally, you’d just go run main.go. But let’s debug it instead.

dlv debug main.go

This compiles the program with debug information and drops you into Delve’s prompt:

(dlv) 

Setting Breakpoints

Breakpoints are where you tell Delve: “Stop here, I want to see what’s going on.”

(dlv) break main.add
Breakpoint 1 set at 0x10509c5 for main.add() ./main.go:6
(dlv) continue

The program runs until it hits main.add. Now we can peek inside:

(dlv) print a
10
(dlv) print b
20

Magic. You’re inside the function, seeing what the code sees.

Stepping Through Code

Delve gives you control over how to move through execution:

  • next → move to the next line in the same function
  • step → dive into function calls
  • stepout → finish the current function and return

Example:

(dlv) next
(dlv) step
(dlv) stepout

This feels like walking with the program, one line at a time.

Internal Concepts: What’s Really Happening?

Here’s where things get interesting.

  1. Delve uses ptrace under the hood (on Linux/macOS). This system call lets one process control another, inspecting registers, memory, and signals. Delve tells your program: “Pause here, I want to look.”
  2. Breakpoints are actually instructions replaced in memory. When you set a breakpoint, Delve injects a special interrupt instruction at that address. When the program hits it, the OS notifies Delve, which takes over.
  3. Goroutines-aware debugging. Traditional debuggers know about threads, but Delve also understands goroutines. You can list them: (dlv) goroutines This is crucial in Go, where bugs often hide in concurrency.

Debugging Goroutines Example

Let’s say you’ve got this code:

package main

import (
    "fmt"
    "time"
)

func worker(id int) {
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(2 * time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    for i := 1; i <= 3; i++ {
        go worker(i)
    }
    time.Sleep(3 * time.Second)
}

Run it with:

dlv debug main.go

Inside Delve:

(dlv) break main.worker
(dlv) continue

Now list goroutines:

(dlv) goroutines

You’ll see multiple goroutines running your workers. You can switch between them:

(dlv) goroutine 3
(dlv) stack

This lets you debug concurrent code like a pro.


Going Beyond the Basics

  • Conditional breakpoints:
    (dlv) break main.add if a == 10
  • Evaluate expressions at runtime:
    (dlv) print a * b
  • Disassemble functions to see low-level Go assembly:
    (dlv) disassemble

This is where you start feeling close to Go’s runtime itself.

Debugging doesn’t have to feel like shooting arrows in the dark. With Delve, you can pause, inspect, and truly understand what’s happening inside your Go programs. Whether you’re tracking down a tricky goroutine bug or just curious about how your code flows, Delve makes the journey clearer and more manageable.

So next time your code misbehaves, don’t just log delve into it.