rmoff's random ramblings
about talks

Learning Golang (some rough notes) - S02E07 - Splitting Go code into separate source files and building a binary executable

Published Jul 16, 2020 by in Go, Golang, Packages at https://rmoff.net/2020/07/16/learning-golang-some-rough-notes-s02e07-splitting-go-code-into-separate-source-files-and-building-a-binary-executable/

So far I’ve been running all my code either in the Go Tour sandbox, using Go Playground, or from a single file in VS Code. My explorations in the previous article ended up with a a source file that was starting to get a little bit unwieldily, so let’s take a look at how that can be improved.

Within my most recent code, I have the main function and the doProduce function, which is fine when collapsed down:

func01

But as soon as it’s not starts to get a bit long, so let’s split that out into its own file.

The Go docs have a whole section devoted to organising code, but I found it hard to follow. Part of it is probably because I’ve been living in VSCode and not using the Go environment with modules etc as the docs describe. This article helped clarify things a bit for me. Having played around I think I understand it a bit better now, thus:

  • If all you want to do is split a Go code file into separate ones, put them all in the same folder with the same package name and use go run . to execute them (or continue to run them from within your IDE such as VSCode)

  • Files in the same folder with the same package name share access to their functions

  • You don’t have to do anything with import or modules, or anything like that.

  • Leave package main alone - you come to change the package name when you’re actually building a package for import elsewhere

    • At some point you probably should, and I’ll get onto that another time when I need to :)

So I started with the same file as previously, but in a new folder and new name:

➜ ls -l listener_test_01

total 24
-rw-r--r--  1 rmoff  staff  5256 16 Jul 11:17 main.go

From there I split doProduce into its own source file, giving us:

➜ ls -l listener_test_01
total 32
-rw-r--r--  1 rmoff  staff   741 16 Jul 11:44 main.go
-rw-r--r--  1 rmoff  staff  4595 16 Jul 11:44 producer.go

The main.go file includes main() which then calls other functions

  • main.go

    package main
    
    import (
            "fmt"
            "os"
    )
    
    
    func main() {
    
    	if e := doProduce(broker, topic); e != nil {
    		fmt.Printf("\nThere was a problem calling the producer :-(\n%v", e)
    	} else {
    		fmt.Println("Oh joy! Oh rejoice! Calling the producer worked *just fine*.")
    	}
  • producer.go

    package main
    
    import (
            "errors"
            "fmt"
            "time"
    
            "gopkg.in/confluentinc/confluent-kafka-go.v1/kafka"
    )
    
    // Produce a test message to given broker and topic
    func doProduce(broker, topic string) error {
    
            // --
            // Create Producer instance

If you try to just run the main.go file you’ll get

➜ go run main.go
# command-line-arguments
./main.go:26:10: undefined: doProduce

So instead you use this (h/t):

➜ go run *.go

Alternatively you can use

➜ go run .

Building a binary executable 🔗

Instead of compiling and running the code each time with go run, you can also build a binary executable which can then be invoking directly. For this simply run:

go build

This builds an executable that takes the name of the parent folder by default

➜ ls -l listener_test_01

-rwxr-xr-x  1 rmoff  staff   6.6M 16 Jul 15:30 listener_test_01
-rw-r--r--  1 rmoff  staff   741B 16 Jul 15:01 main.go
-rw-r--r--  1 rmoff  staff   4.5K 16 Jul 15:01 producer.go

Now you can run it directly, passing commandline arguments as needed

./listener_test_01 localhost:9092

What about go.mod that you may have seen kicking about? For my very simple example here all it does is change the name of the executable that we build

➜ go mod init listenertest
go: creating new go.mod: module listenertest

➜ go build

➜ ll
total 27264
-rwxr-xr-x  1 rmoff  staff   6.6M 16 Jul 15:37 listenertest

📺 More Episodes… 🔗

  • Kafka and Go

    • S02E00 - Kafka and Go

    • S02E01 - My First Kafka Go Producer

    • S02E02 - Adding error handling to the Producer

    • S02E03 - Kafka Go Consumer (Channel-based)

    • S02E04 - Kafka Go Consumer (Function-based)

    • S02E05 - Kafka Go AdminClient

    • S02E06 - Putting the Producer in a function and handling errors in a Go routine

    • S02E07 - Splitting Go code into separate source files and building a binary executable

    • S02E08 - Checking Kafka advertised.listeners with Go

    • S02E09 - Processing chunked responses before EOF is reached

  • Learning Go

    • S01E00 - Background

    • S01E01 - Pointers

    • S01E02 - Slices

    • S01E03 - Maps

    • S01E04 - Function Closures

    • S01E05 - Interfaces

    • S01E06 - Errors

    • S01E07 - Readers

    • S01E08 - Images

    • S01E09 - Concurrency (Channels, Goroutines)

    • S01E10 - Concurrency (Web Crawler)


Robin Moffatt

Robin Moffatt works on the DevRel team at Confluent. He likes writing about himself in the third person, eating good breakfasts, and drinking good beer.

Story logo

© 2025