2 min read

gohatch: A Project Scaffolding Tool for Go

Go has an official tool for creating projects from templates: gonew. I tried it, got frustrated, and built my own. Here's why.

The problem with gonew

gonew uses Go's module system under the hood. That's fine in principle, but it comes with a side effect I didn't anticipate: caching.

I had created a template repository, used gonew to scaffold a new project, spotted a mistake in the template, fixed it, and ran gonew again. Same old template. The fix wasn't there. Unless you're disciplined about version tags, gonew pulls from its cache—and you're stuck with stale code.

For me, a template is a living thing. I tweak it, improve it, experiment with it. I don't want to version it like a library. I just want the current state, every time.

That was the initial frustration. The second was feature envy: I've always liked how tools like cookiecutter and Django's startproject handle variable substitution in templates. gonew doesn't do that.

So I asked myself: how hard can this be?

One afternoon

Not very hard, as it turns out. One afternoon got me from nothing to a working tool.

The problem isn't complicated once you break it down: clone a repository (or copy a local directory), rewrite the module path in go.mod and all .go files, optionally replace some variables, initialise git. That's it.

Building gohatch was also a learning exercise. I'm a Python developer by trade, but I've been circling Go for years. With this project, something finally clicked. I'm now planning to write all my hobby projects in Go.

Why Go?

Wait—aren't you the Python guy?

I'm 55. I remember shareware. You downloaded a file, double-clicked it, and it ran. No Docker, no Snaps, no container orchestration. Just software.

I've grown increasingly interested in self-hosting, and I've noticed that most modern applications come with surprisingly heavy deployment requirements. For personal use, though? A single binary and an embedded SQLite database cover 99% of what I need.

Go excels at this. Cross-compile for any platform, ship one file, done. Rust can do the same, but Go is simpler—and for my purposes, that trade-off makes sense.

What gohatch does

The basic usage is straightforward:

gohatch user/template github.com/me/myproject

This clones the template from GitHub, rewrites the module path, and gives you a ready-to-go myproject/ directory.

Variable substitution works via placeholders:

const AppName = "__ProjectName__"
const Author = "__Author__"

Pass --var Author="Oliver Andrich" and these get replaced throughout the project. Variables also work in file and directory names—cmd/__ProjectName__/main.go becomes cmd/myproject/main.go.

Templates can include a .gohatch.toml configuration file to specify which additional file types should be processed. That way, template users don't need to remember a list of -e flags.

Other bits: automatic git initialisation with an initial commit, support for specific tags or branches (user/template@v1.0.0), dry-run mode, and local directories as template sources for when you're iterating on a template.

What's next

One feature I'd like to add: interactive variable prompts. Currently, you pass variables on the command line. It would be nicer to define them in the TOML config—types, defaults, allowed values—and have gohatch prompt you, like cookiecutter does.

But the tool already does what I need. I use it regularly, and it stays out of my way.

Build your own tools

If there's one thing I've taken from this project: build your own tools.

Modern tooling makes it surprisingly accessible. An afternoon can get you further than you'd expect. And while there's value in established solutions, conforming to tools that don't quite fit your workflow can be genuinely frustrating.

gohatch exists because gonew's caching didn't match how I work. It's a small thing—but it's mine, and it does exactly what I need.


gohatch is open source under the EUPL-1.2 licence: codeberg.org/oliverandrich/gohatch