This episode unpacks how Go works behind the scenes: the runtime, garbage collector, goroutine scheduler, and compilation process. You will also get to know the key standard packages and how packages, modules, and go mod shape your project architecture.

In episode 1 you learned why Go exists. Now it's time to unpack how Go works behind the scenes. Understanding the runtime and project architecture is not just theoretical knowledge — it drives your design decisions when you write production applications.
Episode 2 explains three layers: the Go runtime made up of the garbage collector and goroutine scheduler, the key standard packages that back almost every application, and the module system that shapes your project structure. By the end of the episode, you will see the complete map of how a Go program is built from code into a binary.
Go uses a garbage collector (GC) to manage heap memory automatically. You allocate memory without having to free it manually like in C or C++. Go's GC uses the tricolor concurrent mark-sweep algorithm, which runs in parallel with the program, so the pause per cycle is very short — typically under one millisecond.
Go's scheduler maps many goroutines onto a smaller number of OS threads. This model is called M:N scheduling: M goroutines run on top of N threads. The scheduler shares time cooperatively, moving goroutines off a thread when they call blocking operations such as I/O, so one waiting goroutine does not stall other goroutines.
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("goroutine aktif:", runtime.NumGoroutine())
go func() {
fmt.Println("goroutine baru")
}()
fmt.Println("maksimum CPU:", runtime.NumCPU())
}Go compiles directly to machine code, not to interpreted bytecode. The compiler processes packages, performs type checking, and produces a static binary. No VM needs to be installed on the target machine. That's why Go binaries can run directly even in minimal containers.
The three most commonly used packages: fmt for formatting and text-based I/O, io for read and write interfaces, and os for operating system access such as arguments, environment variables, and files. Their combination forms the foundation of almost every program.
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) > 1 {
fmt.Println("argumen pertama:", os.Args[1])
}
fmt.Fprintf(os.Stdout, "PID proses: %d\n", os.Getpid())
}context carries request-scoped values as well as cancellation and deadline mechanisms for async operations. sync provides synchronization primitives: Mutex, WaitGroup, and Once. You will dive deeper into these three packages in episodes 10 and 12.
A package is a collection of Go files in the same directory, with a package declaration on the first line. The main package produces an executable binary; other packages are libraries that get imported. Identifiers starting with a capital letter are exported, while lowercase ones are private.
package greeter
func Halo(nama string) string {
return "Halo, " + nama
}
func rahasia() string {
return "fungsi privat"
}A module is the distribution unit that bundles many packages together, defined through a go.mod file. The go mod init command creates one, go mod tidy synchronizes dependencies, and go mod vendor downloads all dependencies into a local directory. go.mod records the Go version and dependencies along with their checksums. Try running go mod init github.com/contoh/modul in an empty directory to see the contents of the generated file.
go mod init github.com/nama/belajar-go
go mod tidy
cat go.modGo allows you to define methods on types through a receiver. This is how Go implements concepts like class methods, without needing a class itself.
package main
import "fmt"
type Persegi struct {
Panjang int
Lebar int
}
func (p Persegi) Luas() int {
return p.Panjang * p.Lebar
}
func main() {
s := Persegi{Panjang: 4, Lebar: 3}
fmt.Println("luas:", s.Luas())
}Go has no inheritance. Instead, Go uses interfaces and composition. An interface is defined by a set of methods, and a type implicitly satisfies it if it has the matching methods — without an explicit declaration. Meanwhile, structs can be embedded in one another to build complex behavior from simple components.
package main
import "fmt"
type Deskriptor interface {
Deskripsi() string
}
type Dasar struct {
Nama string
}
func (d Dasar) Deskripsi() string {
return "objek bernama " + d.Nama
}
type Lanjutan struct {
Dasar
Versi int
}
func main() {
var d Deskriptor = Lanjutan{Dasar: Dasar{Nama: "aplikasi"}, Versi: 2}
fmt.Println(d.Deskripsi())
}Here's the complete flow for building a Go application: the compiler reads the main package, follows every import, performs type checking, and produces a binary. The go build ./... command compiles all packages in the module, while go install places the binary into the bin directory of GOPATH.
go build -o app ./...
ls -la appThe resulting binary can be tested, pushed to a container registry, or run directly on a server. This process is what makes Go a top choice for deployment to Kubernetes and other cloud platforms — we will cover it in depth in episode 16.
Episode 2 mapped out Go's architecture: the runtime with its garbage collector and goroutine scheduler, compiling once into a static binary, the key standard packages, the module system via go mod, and the concepts of interfaces, methods, and composition that replace inheritance.
Key takeaways:
In the next episode you will write your first Go program — file structure, package declarations, imports, basic data types, variables, constants, functions with multiple return values, and idiomatic error handling. Everything we covered in this episode will be put into practice with real code.