Go (Golang) Tutorial 0/45 lessons ~6 min read Lesson 3

    Your First Go Program

    Every Go journey starts with a small program that compiles, runs, and prints output.

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Every Go journey starts with a small program that compiles, runs, and prints output. Understanding package main, import paths, and the func main() entry point is essential for reading production codebases at companies like Stripe and Datadog.

    Unlike languages with classes, Go organizes code into packages. The main package is special — it produces an executable. Other packages are libraries. This lesson shows the anatomy of a Go file so you are not confused when opening a payment service repository with dozens of packages.

    You will compile with go build, run with go run, pass command-line arguments via os.Args, and interpret exit codes — the same flow used in Docker HEALTHCHECK scripts and CI smoke tests.

    The story

    An SRE at a fintech company writes a nightly cron job that reconciles payment gateway logs: ./reconcile --date=2026-03-15. If the operator forgets the date flag, the program must print usage to stderr, exit with code 1, and leave a clear audit trail — the same contract Kubernetes Jobs and GitHub Actions expect from CLI tools.

    Understanding package main, os.Args, and exit codes is how you read real infrastructure repos — not just tutorial snippets with hard-coded values.

    Understanding the topic

    Key concepts

    • package main declares an executable; other package names create libraries.
    • import groups standard library and third-party packages; unused imports are compile errors.
    • func main() is the entry point — no parameters, no return value.
    • Exported identifiers start with uppercase; unexported with lowercase (package visibility).
    • go build produces a binary named after the directory; -o flag sets output path.
    • os.Args is a []string where index 0 is the program name.
    text
    flowchart TD
    Start([main]) --> Init[Package init]
    Init --> Logic[Business Logic]
    Logic --> Output[fmt.Println]
    Output --> End([Exit 0])

    Step-by-step explanation

    1. Write source in .go files; all files in a directory share the same package name.
    2. go build compiles packages; errors show file:line with clear messages.
    3. go run compiles to a temp binary, runs it, and deletes it.
    4. Args pass from shell to os.Args slice.
    5. os.Exit(code) sets exit code for scripting; defer runs before exit.
    6. Debug in IDE with breakpoints — delve or IDE debugger attaches to the binary.

    Practical code example

    A complete first program with imports, args handling, and structured output:

    go
    package main
    import (
    "fmt"
    "os"
    )
    const appName = "TechLearningPro CLI"
    func main() {
    if len(os.Args) < 2 {
    fmt.Fprintf(os.Stderr, "Usage: %s <name>\n", os.Args[0])
    os.Exit(1)
    }
    name := os.Args[1]
    fmt.Printf("[%s] Hello, %s! Welcome to Go.\n", appName, name)
    }

    Output

    usage: reconcile --date=YYYY-MM-DD

    Line-by-line code explanation

    • package main marks this file as part of an executable, not a reusable library.
    • import ("fmt"; "os") groups standard-library imports for formatting and OS interaction.
    • func main() is where execution begins — no classes, no inheritance.
    • if len(os.Args) < 2 checks whether the user supplied a required argument.
    • fmt.Fprintln(os.Stderr, "usage: ...") writes help text to the error stream, not stdout.
    • os.Exit(1) signals failure to shells, cron, and CI pipelines checking exit codes.
    • date := os.Args[1] reads the first user-supplied argument after the program name.
    • fmt.Printf("Reconciling %s\n", date) prints a confirmation with formatted output.
    • os.Exit(0) is implicit at the end of main when no error occurs.

    Key takeaway: os.Exit skips deferred functions in main — use return from main when possible. Fprintf to os.Stderr separates errors from stdout for Unix pipelines.

    Real-world use

    Where you'll use this in production

    • CLI tools for DevOps automation and CI/CD pipeline scripts.
    • Docker HEALTHCHECK binaries that exit 0 on success.
    • Migration and seed scripts run via kubectl exec or systemd.
    • Developer productivity tools: code generators, linters, and scaffolding CLIs.

    Best practices

    • Use fmt.Fprintf(os.Stderr, ...) for usage messages and errors.
    • Return exit code 1 (or higher) on failure; 0 on success.
    • Keep main thin — delegate to internal packages for testability.
    • Use constants for app name and version strings.
    • Add -h flag with flag package for user-facing CLIs.

    Common mistakes

    • Forgetting package main — library packages cannot produce executables.
    • Leaving unused imports — Go rejects them at compile time.
    • Using println (lowercase) instead of fmt.Println — println is for bootstrap only.
    • Calling os.Exit inside defer-heavy code — defers won't run.
    • Hardcoding paths instead of using os.Args and flag package.

    Advanced interview questions

    Q1BeginnerWhat is the entry point of a Go program?
    func main() in package main — the runtime calls it after package initialization.
    Q2BeginnerWhat does os.Exit(1) signify?
    Non-zero exit code signals failure to shells, CI systems, and orchestrators.
    Q3IntermediateDifference between go run and go build?
    go run compiles and executes in one step; go build produces a reusable binary artifact.
    Q4IntermediateHow are command-line arguments passed?
    os.Args []string — index 0 is program name; flag package provides typed parsing.
    Q5AdvancedTrace execution from go run to main.
    Toolchain compiles packages → linker builds binary → runtime initializes → init functions run → main() invoked on main goroutine.

    Summary

    package main + func main() is the executable entry point. go build creates binaries; go run compiles and executes immediately. os.Args and flag package handle CLI arguments professionally. Keep main thin; push logic into testable internal packages. Next lesson: variables, types, and Go's type system fundamentals.

    Ready to mark this lesson complete?Track your journey across the entire course.