go-enum: typed enums generated from a comment
An enum generator for go
At a glance
- What is it?
- Go has no enums, and the usual workaround is a block of constants plus a hand written string method that quietly stops matching the constants when someone adds a value. go-enum generates that whole surface from a comment on the type declaration, so the string method, the parser and the marshalling code cannot drift out of step with the list.
- Who is it for?
- go-enum suits a Go project with enums that are serialised, stored in a database or exposed in an API, because the generated text marshalling methods remove the class of bug where a hand maintained string method falls out of date. It is a poor fit if your enums are internal constants you never serialise, and a poor fit if you dislike generated files in a repository, since the whole design assumes a generated file next to the source.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 41 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- abice/go-enum961 ★ · +0
| Repository | 2026-10-21 | 2026-11-07 |
|---|---|---|
| abice/go-enum | 961 | 961 |
0 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The declaration is a comment
The whole idea fits in two lines of Go source.
// ENUM(red, green, blue)
type Color intThat comment on the type declaration is the input. The generator reads it and writes a second file next to it containing the constants and the methods that make the enumeration usable.
The key features list spells out what you get. Type-safe enums, meaning the constants are declared with the proper type rather than as untyped integers. Automatic string conversion, so a string method and a parse function are generated. JSON marshalling support, which comes from the text marshalling methods the JSON encoder already knows how to use. SQL integration, with database scan and value functions. Custom templates, so you can extend the generation with your own code. Support for multiple base types, including integers and strings. And flexible syntax, because the declaration is a comment and not a rigid configuration format.
The goal section says it plainly: the goal is to create an easy to use enum generator that will take a decorated type declaration like a typed integer and create the associated constant values and functions that will make life a little easier for adding new values. The author also credits the inspiration, the Stringer command from the Go tools, whose output was used as the basis for the string method, with a parse function added on top.
The author's own summary of the result is worth keeping in mind, because it sets expectations correctly: it is not perfect, but it is useful.
What the generated file actually contains
The generated code is shown in full in the readme, and it is worth reading because it defines the contract your project will depend on.
The constants come first, as an iota block, and each one gets a doc comment. The naming is the type name followed by the capitalised value name, so the enumeration type becomes the prefix for every constant.
const (
// ImageTypeJpeg is a ImageType of type Jpeg.
ImageTypeJpeg ImageType = iotaThen the methods. The string method implements the Stringer interface, so a value prints as its name rather than as a number, and the parse function attempts the reverse conversion and returns an error when the string does not match.
func ParseImageType(name string) (ImageType, error)And then the two text methods, which implement the text marshaller and unmarshaller interfaces.
There is a note about those two, and it is the sort of detail that saves a debugging session. The methods generated are the text ones rather than JSON ones, because the standard JSON encoder uses the text marshaller when it finds one, so you get JSON behaviour for free without the generator having to know which JSON library you use. The readme phrases it as fear not the fact that they are generated rather than JSON methods, they will still be used by the default JSON encoding methods.
That design also explains why the custom JSON package option exists as a separate feature. If you use a different JSON library, that library may or may not consult the text marshaller, and the generator needs to know which one to target.
String enums, and the flag for the database
The string typed enums section covers a case that the integer version handles badly, and it is the feature that makes the tool usable in a service with an external API contract.
A string typed enumeration is declared the same way, with the underlying type being a string, and the generated constants hold string values rather than iota numbers.
// ENUM(pending, running, completed, failed)
type StrState stringThe problem that solves is that an integer enumeration in a JSON payload tells the consumer nothing about which values are possible, while a string enumeration is self-describing. But the database might want integers, and many databases index small integers efficiently.
The resolution is a flag. You assign an integer value in the declaration as usual and specify the integer flag, and those values are then used to convey the integer to the database while the strings are used everywhere else. The readme gives the reason in a sentence that shows the author has thought about a real API: it may be helpful for things like API documentation, where you want the same type on the API layer and the database layer, and you do not want the documentation to assume the enumerations are integers when they are in fact strings.
// swagger:enum StrState
// ENUM(pending, running, completed, failed)
type StrState stringThere is also a documentation comment convention, so a generator for API documentation can find the enumeration too.
Recent flags, and the custom template escape hatch
The recent features section lists three options, and each is a small but real request from users.
The first is custom JSON package support, added in a later release. You can specify a custom JSON package for the imports instead of the standard encoding package, with a flag naming the package, and it is used together with the flag that enables marshalling code in the first place.
go-enum --marshal --jsonpkg="github.com/goccy/go-json" -f your_file.goThe second is the ability to disable iota, for cases where you do not want the generated enumerations to use it, which matters when the numeric values need to be stable across a rename or when you want explicit values.
go-enum --no-iota -f your_file.goThe third is a configurable output suffix, so the generated file does not have to be called the default one.
go-enum --output-suffix="_generated" -f your_file.go # Creates your_file_generated.goThen there is the custom template option, which is the escape hatch for the whole tool. If the generated options are not adequate for your use case, you can add a custom template to the processing engine so that your own code is produced instead.
That is the right design for a generator, because the default output is a guess about what you need and the template makes the guess replaceable without forking the project.
Four ways to get the tool
The installation section covers the ways to obtain it, and the newest one is the most interesting.
The recommended method is to install it as a project tool, which requires Go 1.24 or later, and the command adds a tool dependency to the go.mod file with dependency tracking.
go get -tool github.com/abice/go-enum@latestThe go.mod entry it produces is a single line, which is the entire point of the modern tool mechanism.
tool github.com/abice/go-enumAnd the tool is then run through the go tool command.
go tool go-enum [options]For older Go versions, the classic install command is still supported, with Go 1.23 as the stated minimum for that route. Pre-built binaries are published with the release workflow, and there is a container image if you would rather not install anything locally, with a volume mounted so the tool sees your code.
docker run -w /app -v $(pwd):/app abice/go-enum:latestThe requirements are stated as two lines: Go 1.24.0 or later is recommended for the new tool installation method, and Go 1.23.0 or later is the minimum for the legacy one. The module itself declares a newer Go version still, which is normal for a project that supports older toolchains as consumers.
The build targets, and the test that fails on dirty files
The Makefile is where the engineering discipline shows, and one target in it is worth singling out.
The default target runs build, formatting, test, the example generation, coverage and install. The build step runs a code generation step for the generator package before compiling, which is the usual pattern for a project whose own source includes generated code.
The test step runs the tests with the race detector enabled and with the shuffle flag, so the test order changes between runs and the race detector is on. It also runs the tests a second time with a build tag, which is how the example package gets exercised.
Then there is the target that made this section worth writing. There is a target that asserts there are no changes: it checks whether the working tree is dirty and fails the build if it is. In a project that generates code, that is the guard that stops a pull request from committing generated files that do not match the source.
The supporting pieces are consistent with that. There is a script to update the snapshots, and the dependency list includes a snapshot testing library, so the generated output is compared against recorded expectations rather than only checked for compiling. There are also mock and test assertion libraries, and a template library, which is what the custom template feature is built on.
The example directory is the other thing to look at, because it is effectively a catalogue of the edge cases the generator handles, with pairs of source and generated files for colour types, string types, commented declarations, custom prefixes, 32 and 64 bit types, and a deliberately broken input. A generator with a directory like that is a generator whose author expected the awkward cases to be found by users.
Editorial conclusion
go-enum suits a Go project with enums that are serialised, stored in a database or exposed in an API, because the generated text marshalling methods remove the class of bug where a hand maintained string method falls out of date. It is a poor fit if your enums are internal constants you never serialise, and a poor fit if you dislike generated files in a repository, since the whole design assumes a generated file next to the source. Before you adopt it, look at the example directory, which is effectively a catalogue of the edge cases, and read the custom template option, because the default output will not cover every codebase.
Frequently asked questions
How does go-enum work?
You put a comment-annotated enumeration on the type declaration, for example an enum comment listing the values above a typed integer, and the generator writes a second file containing the constants and the methods. The generated string method implements the Stringer interface and there is a matching parse function that returns an error for an unknown value.
Does go-enum generate JSON marshalling code?
It generates the text marshaller and unmarshaller methods, and the default JSON encoding methods use them, which is why you get JSON behaviour without the generator knowing which JSON library you use. If you use a different JSON package, there is a flag for specifying it.
Can go-enum generate string typed enums?
Yes. Declare the type as a string and the generated constants hold string values, so the enumeration is self-describing in an API payload. If the database should store integers, you assign integer values in the declaration and pass the integer flag, so the same type can be a string in the API and an integer in the database.
How do I install go-enum?
With Go 1.24 or later you can install it as a project tool, which records it in go.mod and lets you run it through the go tool command. Older Go versions can use the classic install command, pre-built binaries are published with the releases, and there is a container image if you do not want to install anything locally.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/abice-go-enum)Star history chart for your README
[](https://hysenlabs.com/projects/abice-go-enum)Was this useful?