Golang: How To Guide
Streamline Your Productivity in Go Using “Air”
A Simple Walkthrough Using Go Live Reload Development With Docker
Build and Run Burnout
As a software engineer, I can tell you that the efficiency of your software development is dependent not only on your tools, knowledge, and skills but also on proper setup and development environment configuration.
If you started out as a Go developer, you most likely had to repeatedly re-run or re-build a codebase after every change. Alternatively, if you’re coming from another language, such as PHP or NodeJS, you’re probably sick of the build and run cycle in Go development.
Live reloading can be done in a variety of ways, but there is an optimal method when using Go, in my opinion. And that’s to use Air
.
☝️ Live reloading is the process of keeping track of file changes based on the file types designated to be monitored inside of a directory (often an application directory), building the application, and relaunching it after a file save (or a delay duration). It differs slightly from hot reloading:
Live reloading: Re-compiles and reloads the entire application on file(s) changed and saved.
Hot reloading: Only refreshes the files that were changed and saved, not the entire application without losing the application state.
As a quick aside, I’ve tested the majority of live reloading methods on real projects, and have subsequently found Air
to be:
- Easily configurable (primary benefit)
- Portable and used with Docker (primary benefit)
- Chock-full of good documentation (honestly, no one wants to figure things out from code, do they?)
- Averagely fast
- Great at tracking errors in a log file
- Clear in the building process
That said, there are numerous approaches to live reloading Go applications that may suit you, depending on your preferences or use case: Gin, Fresh, Reflex, Nodemon (especially if you’re coming from NodeJS), etc.
Some are more flexible than others. Some are more performance-based. But, in my opinion, Air
offers the most streamlined workflow for Go applications, especially as your projects grow in size and dependencies.
So let’s explore Air
a bit, and if you have any questions or comments, feel free to leave them at the end of this article. I’ll be happy to respond.
Short Preface
By the end of this article, you should be able to:
[Feel free to skip ahead to a specific section]
- Install Air (with live reload example)
- Configure Air
- Use Air with environment variables
- Distinguish air arguments from passed arguments
- Use Air with Docker and Docker-compose
- Gather 3 main takeaways
So, without further ado, let’s get started.
Live reloading with Air
To gain a better understanding of Air
. Let’s start with a simple application.
👉 Note: Feel free to follow along with the project with your personal project or clone the article project from this repository.
Air installation
To install Air
, follow the instructions below in the terminal, but you should look at the other installation instructions and the updated script here.
curl -sSfL https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s
And to verify that Air
is installed, run the following command
air -v
and you should get an output similar to this:
Live reload example
Now that we have Air
installed on our machine, we’ll use the article sample project that you cloned a few steps ago, or you can use your own project. After every 10 minutes, the article application will display a random single quote from the Zenquotes API ‘https://zenquotes.io/api'.
Now, before we run the application with Air
, open the article project and replace the code on line 23 of the main.go
file with the code below.
baseUrl, ok := "https://zenquotes.io/api/", true //os.LookupEnv("BASE_URL")
Now run the application with Air
in the project root directory as shown below:
air
and your output should be similar to this:
Now that Air
is watching for our changes, return to the main.go
inside the main function and replace the code in line 42 with the code below and watch the magic on the terminal:
time.Sleep(time.Second * 10)
Terminal output:
main.go has changed
building...
running...
Air
will automatically recompile, build, and run our application.
👉 Note:
Air
does run the application binary in the temp directory.
Air configuration
Air
will use the default configuration, but wouldn't it be better to create our own? Let's see how we might be able to do that.
In our project root directory, run the following command:
air init
This is the output of the command above:
And if you open the project in your favorite IDE, you’ll notice that a new file named .air.toml
with default settings have been added.
I won’t go through every configuration in the .air.toml
file, but I will mention a few that I believe are important. You can tinker with the configuration, but the default settings should be sufficient for any basic Go
project.
👉 Note: Sample
.air.toml
file can be found here https://github.com/cosmtrek/air/blob/master/air_example.toml
[build]
cmd = "go build -o ./tmp/main ."
The command configuration will compile our application; you can change it depending on your application’s entry point:
[build]
bin = "./tmp/main"
The bin configuration specifies the location of the compiled binary.
👉 Note: I don’t recommend changing this configuration.
[build]
full_bin = ""
The full_bin specifies how an application will be launched after compilation; the way you run your application after building should be reflected in this configuration. And you can pass environment variables here; we’ll take a look at passing variables later:
[build]
delay=1500
The delay configuration simply delays the build trigger after a file change and save:
[build]
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
...
[build]
exclude_file = []
👉 Note: This configuration prevents
Air
from monitoring the directories and files listed respectively.
[build]
include_ext = ["go", "tpl", "tmpl", "html", "yaml", "yml"]
This is the gist of it; all files that you want to be monitored by Air
should have their extensions added here, for example, YAML or HTML files in my case.
[build]
log = "build-errors.log"
Air
logs output errors in a file, in this case, build-errors.log
, which can help you debug your application.
💡 Remember to add tmp in your .gitignore file unless your team is using the same configuration file
Passing arguments to Air
Air
can also be used by command-line applications created with tools such as go cobra. As demonstrated below, Air
can accept and work with arguments passed to it.
air serve
Using environment configuration with Air
Air
supports passing environment variables such as database configuration, ports, and so on. Although I prefer using .env
to manage environment variables, let’s look at how to use Air
with environment variables.
Updating the configuration on .air.toml
as shown below
[build]
full_bin = "BASE_URL=https://zenquotes.io/api/ ./tmp/main"
Now go ahead and revert the first change we made on main.go
file on line 21 to this.
baseUrl, ok := os.LookupEnv("BASE_URL")
Now hit it, stop Air
and run it one more time as shown
air
Basically, we are adding BASE_URL as an environment variable when running the application with Air
.
However, adding and updating environment variables in the .air.toml
file is not appealing. Rather, it’s best to include the environment variables in an .env
file.
Examine the project sample. Undo the .air.toml
file and use the terminal to run the following commands to add the environment variables:
export $(egrep -v ‘^#’ .env | xargs) && air
👉 Note: There are several methods for adding environment variables, one of which is to use this package. github.com/joho/godotenv.
Focus on one configuration file
For the sake of productivity, running Air init
and configuring .air.toml
in every project is counterproductive.
Instead, let’s just use the latter. Copy your default .air.toml
to your user directory:
cp .air.toml ~/.air.toml
👉 Note: When developing Go projects, you can use the default
.air.toml
file:
air -c ~/.air.toml
Better yet, we can add the above command as an alias by including the following content in the bashrc
or zshrc
file. Open the bashrc
or zshrc
file and add the alias below:
alias air="air -c ~/.air.toml -- "
💡 If you’ve noticed, I’ve added characters (i.e., the dashes) to the
bashrc
orzshrc
files to distinguish betweenAir
arguments and passed arguments.
As a result, we can still run the instruction below.
air serve
Using Air with Docker and Docker-compose
Let’s look at how to use Air
with Docker
first.
You can start our sample application Docker
container by following the steps below. Let’s take a closer look:
docker run -it --rm --env-file <ENV_FILE> -w <WORKING_DIR> -v <PROJECT_FOLDER>:<MOUNT_POINT> <IMAGE_NAME>
which equates to this in our case:
docker run -it --rm --env-file .env -w "/app" -v $(pwd):/app cosmtrek/air
👉 Note: Essentially, the
Docker
instruction mounts the project directory to theDocker
work directory using the-v
flag and the-w
flag to specify the working directory inside the container.Air
will monitor changes to the container's mounted directory.
Here’s how the—env-file
flag is read in a file of environment variables:
Docker-compose
We can also use Docker-compose with Air
.
Create a Docker-compose file called docker-compose-dev.yml
in our project’s root directory and fill it with the content below.
💡
Air
is a development tool that should not be used for production or deployment. That is why our Docker-compose file is calleddocker-compose-dev.yml
Now, if we run Docker-compose up in our project directory in the terminal as shown below…
docker compose -f docker-compose-dev.yml up
We get the following output:
Both Docker
and Docker-compose are outside the scope of this article, but all I wanted to do was demonstrate how to use Air
with Docker
and Docker-compose. Please update your Docker
and Docker-compose files to meet your development requirements.
With that, you can take control of the development process and increase your productivity by using Air
, Air
configuration, and Docker
.
3 Key Takeaways
Over time, you’ll realize that being an experienced developer revolves around being efficient and saving time, whether in application code or during development. Software development is similar to logistics delivery in that everyone benefits from faster delivery. That’s what Air
provides you during Go development
.
If you find yourself constantly restarting & recompiling a Go app in development, do yourself a favor and try Air
:
- It’s easy to install, highly configurable, portable, and works well with Docker-compose
- It will save you time and energy, and allow you to get out of the “Build and Run” burnout by speeding up your development process
- Don’t run
Air init
and configure.air.toml
simultaneously — it’s counterproductive
Thanks for reading. If you liked this article, please clap and share this with others so we can build on each others’ experiences.
Sample project code
The project’s source code can be found here.
Related Articles (if you liked this one)
Disclosure: In accordance with Medium.com’s rules and guidelines, I publicly acknowledge financial compensation from UniDoc for this article. All thoughts, opinions, code, pictures, writing, etc. are those of my own.