---
title: "Using Makefile in Go Projects (The Practical Way)"
description: "Learn how to use Makefile in Go projects with practical examples. Build, run, dev with live reload for your Go development workflow."
slug: "using-makefile-in-go-projects-the-practical-way"
url: "https://furkanbaytekin.dev/blogs/using-makefile-in-go-projects-the-practical-way"
author: "Furkan Baytekin"
category: "Software"
tags: []
published: 2025-12-21T10:15:43Z
updated: 2025-12-21T10:15:43Z
readTime: 3
cover: "https://myblogcms.furkanbaytekin.dev/uploads/cover/using-makefile-in-go-projects-the-practical-way+1766311821683"
---

Go already gives you a solid toolchain, but once a project grows, repeating commands gets old fast. A simple `Makefile` gives you **standardized commands**, **less typing**, and **zero dependencies**.

This post shows a **minimal, useful Makefile** for Go projects:

- `make build` → builds to `bin/projectname`
- `make run` → runs the built binary
- `make dev` → live reload with `air`
- `make clean` → removes build artifacts
- `make help` → self-documented commands

---

## Project Structure

```tree
.
├── cmd/
│   └── projectname/
│       └── main.go
├── bin/
├── air.toml
├── go.mod
├── Makefile
```

---

## Why Use Makefile with Go?

- One command everyone remembers
- Same workflow across OSes
- CI-friendly
- Self-documenting
- No bash scripts scattered around

Go stays simple, Make just **glues things together**.

---

## The Makefile

```makefile
APP_NAME := projectname
BIN_DIR := bin
BIN_PATH := $(BIN_DIR)/$(APP_NAME)

.PHONY: build run dev clean help

## build: Build the Go binary
build:
  @mkdir -p $(BIN_DIR)
  go build -o $(BIN_PATH) ./cmd/$(APP_NAME)

## run: Run the built binary
run: build
  @./$(BIN_PATH)

## dev: Run with live reload using air
dev:
  @air

## clean: Remove build artifacts
clean:
  @rm -rf $(BIN_DIR)

## help: Show available commands
help:
  @echo ""
  @echo "Available commands:"
  @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
    | awk 'BEGIN {FS = ":.*?## "}; {printf "  %-10s %s\n", $$1, $$2}'
```

---

## `make build`

```bash
make build
```

- Compiles your app
- Output goes to `bin/projectname`
- No surprises, no magic paths

This keeps your repo clean and binaries out of the way.

---

## `make run`

```bash
make run
```

- Ensures the binary is built
- Executes `./bin/projectname`

Simple dependency chain:

```makefile
run: build
```

---

## `make dev` (Live Reload)

Uses **Air** for hot reload.

```bash
make dev
```

Make sure Air is installed:

```bash
go install github.com/air-verse/air@latest
```

And you have an `air.toml`:

```toml
root = "."
tmp_dir = "tmp"

[build]
cmd = "go build -o bin/projectname ./cmd/projectname"
bin = "bin/projectname"
```

---

## `make clean`

```bash
make clean
```

Deletes: `bin/`

Useful for:

- fresh builds
- CI
- sanity resets

---

## `make help`

```bash
make help
```

Outputs something like:

```bash
Available commands:
  build      Build the Go binary
  run        Run the built binary
  dev        Run with live reload using air
  clean      Remove build artifacts
  help       Show available commands
```

This works because of the `##` comments — small trick, big win.

---

## Final Notes

- Keep Makefiles **boring**
- Don’t over-abstract
- If a command needs explaining, it’s probably wrong

For Go projects, this setup hits the sweet spot:
**simple, readable, and production-safe**.

Done.

Album of the blog:

<iframe data-testid="embed-iframe" style="border-radius:12px" src="https://open.spotify.com/embed/album/5sH9LAk6zTnbVGKWSKu8py?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
