title: Code Generation sidebar_position: 90 id: codegen license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
:::warning Experimental Feature Code generation is an experimental feature in Fory Go. The API and behavior may change in future releases. The reflection-based path remains the stable, recommended approach for most use cases. :::
Fory Go provides optional ahead-of-time (AOT) code generation for performance-critical paths. This eliminates reflection overhead and provides compile-time type safety.
| Aspect | Reflection-Based | Code Generation |
|---|---|---|
| Setup | Zero configuration | Requires go generate |
| Performance | Good | Better (no reflection) |
| Type Safety | Runtime | Compile-time |
| Maintenance | Automatic | Requires regeneration |
Use code generation when:
Use reflection when:
Install the fory generator binary:
go install github.com/apache/fory/go/fory/cmd/fory@latest GO111MODULE=on go get -u github.com/apache/fory/go/fory/cmd/fory
Ensure $GOBIN or $GOPATH/bin is in your PATH.
Add the //fory:generate comment above structs:
package models //fory:generate type User struct { ID int64 `json:"id"` Name string `json:"name"` } //fory:generate type Order struct { ID int64 Customer string Total float64 }
Add a go:generate directive (once per file or package):
//go:generate fory -file models.go
Or for the entire package:
//go:generate fory -pkg .
go generate ./...
This creates models_fory_gen.go with generated serializers.
The generator creates:
A compile-time check to detect struct changes:
// Snapshot of User's underlying type at generation time type _User_expected struct { ID int64 Name string } // Compile-time check: fails if User no longer matches var _ = func(x User) { _ = _User_expected(x) }
Strongly-typed serialization methods:
type User_ForyGenSerializer struct{}
func NewSerializerFor_User() fory.Serializer {
return &User_ForyGenSerializer{}
}
func (User_ForyGenSerializer) WriteTyped(ctx *fory.WriteContext, v *User) error {
buf := ctx.Buffer()
buf.WriteInt64(v.ID)
ctx.WriteString(v.Name)
return nil
}
func (User_ForyGenSerializer) ReadTyped(ctx *fory.ReadContext, v *User) error {
err := ctx.Err()
buf := ctx.Buffer()
v.ID = buf.ReadInt64(err)
v.Name = ctx.ReadString()
if ctx.HasError() {
return ctx.TakeError()
}
return nil
}
Serializers are registered in init():
func init() {
fory.RegisterSerializerFactory((*User)(nil), NewSerializerFor_User)
}
Generate for a specific file:
fory -file models.go
Generate for a package:
fory -pkg ./models
Specify types explicitly:
fory -pkg ./models -type "User,Order"
Force regeneration even if up-to-date:
fory --force -file models.go
Regenerate when any of these change:
//fory:generateFory includes a compile-time guard:
// If struct changed, this fails to compile var _ = func(x User) { _ = _User_expected(x) }
If you forget to regenerate, the build fails with a clear message.
When invoked via go generate, the generator detects stale code and retries:
Code generation supports:
bool, int*, uint*, float*, string)All nested structs must also have //fory:generate:
//fory:generate type Address struct { City string Country string } //fory:generate type Person struct { Name string Address Address // Address must also be generated }
Recommended for libraries:
go generate ./... git add *_fory_gen.go git commit -m "Regenerate Fory serializers"
Pros: Consumers can build without generator; reproducible builds Cons: Larger diffs; must remember to regenerate
Recommended for applications:
steps: - run: go install github.com/apache/fory/go/fory/cmd/fory@latest - run: go generate ./... - run: go build ./...
Generated code integrates transparently:
f := fory.New() // Fory automatically uses generated serializer if available user := &User{ID: 1, Name: "Alice"} data, _ := f.Serialize(user) var result User f.Deserialize(data, &result)
No code changes needed - registration happens in init().
You can mix approaches:
//fory:generate type HotPathStruct struct { // Performance-critical, use codegen } type ColdPathStruct struct { // Not annotated, uses reflection }
If codegen fails, Fory falls back to reflection:
// If User_ForyGenSerializer not found, uses reflection f.Serialize(&User{})
Ensure the binary is in PATH:
export PATH=$PATH:$(go env GOPATH)/bin
Regenerate:
go generate ./...
Or force:
fory --force -file yourfile.go
The compile-time guard catches this:
cannot use x (variable of type User) as type _User_expected in argument
Run go generate to fix.
myproject/ ├── models/ │ ├── models.go # Struct definitions │ ├── models_fory_gen.go # Generated code │ └── generate.go # go:generate directive ├── main.go └── go.mod
models/generate.go:
package models //go:generate fory -pkg .
models/models.go:
package models //fory:generate type User struct { ID int64 Name string }
No. Reflection-based serialization works without code generation.
Yes. Generated code is plain Go with no version-specific features.
Yes. Fory automatically uses generated serializers when available.
Run go generate ./... after struct changes.
For libraries: yes. For applications: either works.