goderive writes the Equal method you were not going to maintain
Derives and generates mundane golang functions that you do not want to maintain yourself
At a glance
- What is it?
- goderive parses Go source for functions that are declared but not implemented, derives their bodies from the parameter types, and writes them into a generated file. The generated code is committed, and CI fails if it drifts, which makes the tool's value a review artefact rather than a build step you can ignore.
- Who is it for?
- Adopt goderive if you have value types in Go with fields that change often and you are tired of writing and reviewing structural equality by hand. Do not adopt it as a replacement for hand-written code where semantics matter, since a derived Equal is only correct if your field choices are obvious.
- Can I use it commercially?
- Yes. Apache-2.0 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?
- No. The owners have archived the repository on GitHub, so it is read-only and no longer receives changes.
- 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
- awalterschulze/goderive1,265 ★ · +0
| Repository | 2026-10-17 | 2026-11-07 |
|---|---|---|
| awalterschulze/goderive | 1.3k | 1.3k |
0 Stars in the last 21 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The mechanism is a missing body
goderive works by spotting functions that do not exist yet. It parses your Go code for functions that are not implemented, or were previously derived, and generates them from the input parameter types.
The example is the clearest possible case. You write the method with a call to an undefined function:
func (this *MyStruct) Equal(that *MyStruct) bool {
return deriveEqual(this, that)
}and goderive produces the body in a `derived.gen.go` file in the same package:
func deriveEqual(this, that *MyStruct) bool {
return (this == nil && that == nil) ||
this != nil && that != nil &&
this.Int64 == that.Int64 &&
((this.StringPtr == nil && that.StringPtr == nil) ||
(this.StringPtr != nil && that.StringPtr != nil && *(this.StringPtr) == *(that.StringPtr)))
}The important detail in that generated code is the pointer handling. For a plain `int64` it compares directly. For a `*string` it expands into four cases covering both nil, neither nil, and both dereferenced. That nil-safety is the part you would otherwise get wrong, and it is the part that costs the most to review by hand across a large struct.
The README identifies the output as `derived.gen.go` in the same package as the calling code.
What is derivable, by category
The plugin set is organised into categories, and reading the function signatures tells you the design constraints.
The recursive family covers structural operations over a type: `deriveEqual(T, T) bool` and a curried `deriveEqual(T) func(T) bool`, `deriveCompare(T, T) int` in both forms, `deriveDeepCopy(dst *T, src *T)` with variants for slices and maps, `deriveClone(T) T`, `deriveGoString(T) string` and `deriveHash(T) uint64`.
DeepCopy having distinct signatures for pointers, slices and maps is the clearest sign that these are plugins doing type-directed generation rather than one generic function with reflection inside.
The set family treats a type as a collection: keys, sort, unique, set, min, max, contains, intersect and union.
The functional family is about composition, and it is the most interesting one to read because it shows how types are matched structurally. `deriveFmap` has variants mapping over a slice, over the runes of a string, over functions returning a value and an error, over functions returning several values, and in the concurrency plugin, over a channel. `deriveCompose` chains `func() (A, error)` with `func(A) (B, error)` into `func() (B, error)`, and also handles the variadic case. `deriveJoin`, `deriveFlip`, `deriveCurry`, `deriveUncurry`, `deriveTuple`, `deriveMem`, `deriveTraverse`, `deriveToError` and `deriveApply` complete the set.
Concurrency adds `deriveFmap` over channels and `deriveJoin` over nested channels, both directions of channels and slices of channels, plus the `pipeline` and `do` examples.
The practical question for any of these is whether the generated implementation matches your intent. `deriveEqual` on a struct containing a mutex, or `deriveHash` on one containing a cache, will compile and be wrong.
Generated code is committed, and CI proves it
This is the part of the design that separates goderive from a code generator you run once.
The repository has a `vendor/` directory, and the Makefile's targets make the workflow explicit. `make test` installs the binary, then runs the test suite and the examples. The `action` target does more: gofmt, print the Go version, run the tests, then `go vet` across `derive/`, `example/`, `plugin/` and `test/normal/`, and finally `make diff`.
And `make diff` is:
diff:
git diff --exit-code .That is a continuous integration assertion that generated code is in the repository and is up to date with the sources. If a field is added to a struct and the `derived.gen.go` file was not regenerated, the build fails.
So the generated code is a review artefact. Every derivation is visible in a diff, and a reviewer reads it like any other generated file. That is a different contract from running a generator at build time and trusting the output.
The `updatedeps` target runs `go mod tidy`, then `go mod vendor`, then checks out `vendor/vendortest/vendortest.go` from git.
The module is `awalterschulze.org/go/goderive`, which is not a GitHub path. That change is called out in the release notes for v0.6.0, whose title is the move to a go dependency at awalterschulze.org/go/goderive. Getting the import path right is the first thing to check if you are following an older blog post about this tool.
Examples show how it is wired in
The `example/` directory is where the integration questions get answered, and the folder names correspond to distinct ways of adopting the tool.
The repository contains `gogenerate/`, `pluginprefix/`, `prefix/`, `plugin/` and `talk/` directories.
The repository includes `example/Makefile`, and the top-level `make test` invokes `make -C example example`.
That is a small thing, but for a tool whose correctness depends on parsing someone else's source, having the examples run in CI is the difference between documentation and a test suite.
Generics, versions and the maintenance picture
The release history shows the project reacting to Go's own direction. v0.5.0 is titled before generics, dated 2025-01-13. v0.5.1 updated to Go version 1.24 in February 2025. v0.6.0, dated 2026-08-04, moved the Go dependency to awalterschulze.org/go/goderive.
The current `go.mod` declares `go 1.26.3`, with two direct dependencies: `github.com/kisielk/gotool` and `golang.org/x/tools v0.6.0`. The x/tools version is old for a project on a recent Go, and it is pinned in a module file rather than kept current, which is worth knowing if you hit an incompatibility with a newer parser.
The recorded last push was on 2026-08-24; the repository is now archived and read-only. The topic tags include generics, so the pre-generics design has clearly been revisited, though the README's example is still written in the pre-generics style with an explicit `deriveEqual` call rather than a type-parameterised function.
The licence is Apache 2.0, with a LICENSE file at the root and a Contributing.md.
The honest summary: this is a small, self-contained generator with a plugin model, a real test suite and a CI check that generated code stays current. The cost is that the generated code becomes part of your repository's surface, and every new field in a struct produces a diff someone has to read.
Editorial conclusion
Adopt goderive if you have value types in Go with fields that change often and you are tired of writing and reviewing structural equality by hand. Do not adopt it as a replacement for hand-written code where semantics matter, since a derived Equal is only correct if your field choices are obvious. Verify first that the plugin you want exists in `plugin/`, commit the generated file, and read what the generator produced for pointer fields before trusting it in code that compares identity.
Frequently asked questions
How does goderive know which functions to generate?
It parses your Go code for functions that are not implemented, or were previously derived, and derives their bodies from the parameter types. The example uses the name `deriveEqual`.
Where does goderive put the generated code?
In a derived.gen.go file in the same package as the calling code. The repository's Makefile runs `git diff --exit-code` as part of its action target, so CI fails if that generated file is out of date with your sources.
Which functions can goderive derive?
The plugins cover recursive ones such as Equal, Compare, DeepCopy, Clone, GoString and Hash, set operations such as keys, sort, unique, min, max, contains, intersect and union, functional ones such as Fmap, Join, Compose, Mem, Traverse and ToError, and concurrency variants that operate over channels.
What Go version does goderive need?
The current go.mod declares go 1.26.3. The release notes for v0.5.1 say the project updated to Go version 1.24, and the module path is awalterschulze.org/go/goderive since v0.6.0.
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/awalterschulze-goderive)Star history chart for your README
[](https://hysenlabs.com/projects/awalterschulze-goderive)Was this useful?