panicparse v1.6.0

This release was the hardest one ever
2020-12-19 golang panicparse

Astute readers using panicparse will ask themselves: Marc-Antoine, haven’t you released v2.0.1 several months ago and now you are releasing v1.6.0?

Yep I did.

Content warning

But oh boy I didn’t know what was coming.

This post is very rant-y. I’ve lost tens of hours on this topic in the past year.

Long term support

I’m the kind of person who likes to support things for a long time. I don’t enjoy breaking my users. In short, I wanted the following to work:

go get
go1.9.7 download
go1.9.7 get

go get
go1.16beta1 download
go1.16beta1 get

In practice, I only needed go1.11 to work, but I got up to 1.9.7 for free. I hit a few challenges:

In addition to the challenges mentioned above, I also found many annoyances but I’ll save them for another day.

My goal

My goal was simple:

How to get there

I worked it out with a combination of a few tricks.

  1. I had to drop support for really old versions. That was fine.
  2. I removed the previously vendored packages because it was incompatible with my GitHub Actions tests and it caused me headaches for testing.
  3. The really magic part was to vendor stack@v2.1.0 back into vendor/ in the v1.x branch. The magic here is that the vendor/ directory is going to be used only when not in go modules mode, which is exactly what we want.
  4. Thankfully, the executables where already mostly a shim, so copied executables source code and internal/ directly from v2.1.0.


I think my main take away is that in go modules days, you should seriously consider using separate git repositories for your executables and the packages. That’s all. In abstract it makes sense, you make separate versions, and it’s generally surprising for users if they get bumped to a new major version silently. Where this breaks down is in the following format (which panicparse follows):

- go.mod
- cmd/
  - tool_foo/
    - main.go
- foo/
  - foo.go

and you want to make a breaking change to the reusable package foo without having to bump the version of the CLI tool tool_foo.

The challenge is that often I’ll write a toy CLI tool, then will carve out a package that I think has potential for reuse, which leads to a period of uncertainty for the API. Is it too specific for the tool use case or is it too generic? Fleshing an API out requires time, but in the meantime as a tool maintainer you have a useful CLI tool that you don’t want to break in the process. That’s where I find the status quo hurts the most, because the git tag and the versions must match starting with v3 so versioning directories do not help, and having go.mod files in subdirectories breaks go test ./....

Anyhow, I’m glad I found a way and hope the learnings here will help another package maintainer write Go packages that predate Go modules.