Thank you for using ff4go! Feel free to report any issue or improvement 🙏
go get github.com/dorianneto/ff4go
Create an ff4go.json file at your project root. Each flag supports the following fields:
{
"flags": [
{
"name": "new-ui",
"enabled": true,
"description": "Enables the redesigned UI",
"rules": {
"users": ["user1", "user2"],
"environments": ["development", "staging"],
"percentage": 25.0,
"endAt": "2026-12-31T23:59:59Z"
}
}
]
}| Field | Type | Description |
|---|---|---|
users |
[]string |
List of user identifiers that the flag is active for. |
environments |
[]string |
List of environment names that the flag is active for. |
percentage |
float64 |
Percentage of users (0–100) to roll out to. When set, takes precedence over users/environments lists and uses a deterministic hash so the same user always gets the same result. |
endAt |
string |
RFC 3339 timestamp after which the flag is treated as disabled, regardless of other rules. Omit to disable expiry. |
usersandenvironmentsare only evaluated whenpercentageis not set.
Reads ff4go.json from the process working directory.
m, err := ff4go.NewManagerFromFile()
if err != nil {
panic(err)
}Useful when you load the configuration from a remote source, an environment variable, or embed it with //go:embed.
data := []byte(`{"flags":[{"name":"new-ui","enabled":true}]}`)
m, err := ff4go.NewManagerFromBytes(data)
if err != nil {
panic(err)
}// IsEnabled returns the flag's top-level enabled value.
m.IsEnabled("new-ui")
// HasFlag reports whether a flag with the given name exists.
m.HasFlag("new-ui")
// IsEnabledForUser returns true when the flag is enabled and the user
// matches the users rule (or falls within the percentage rollout).
m.IsEnabledForUser("new-ui", "user1")
// IsEnabledForEnvironment returns true when the flag is enabled and the
// environment matches the environments rule (or falls within the percentage rollout).
m.IsEnabledForEnvironment("new-ui", "development")
// IsEnabledForUserAndEnvironment returns true only when both the user
// and environment checks pass simultaneously.
m.IsEnabledForUserAndEnvironment("new-ui", "user1", "development")All targeting methods return false when the flag does not exist, is disabled, or has expired (endAt is in the past).
package main
import (
"fmt"
"github.com/dorianneto/ff4go"
)
func main() {
m, err := ff4go.NewManagerFromFile()
if err != nil {
panic(err)
}
fmt.Println(m.IsEnabled("new-ui")) // true
fmt.Println(m.IsEnabledForUser("new-ui", "user1")) // true
fmt.Println(m.IsEnabledForEnvironment("new-ui", "staging")) // false
fmt.Println(m.IsEnabledForUserAndEnvironment("new-ui", "user1", "development")) // true
}package main
import (
_ "embed"
"fmt"
"github.com/dorianneto/ff4go"
)
//go:embed ff4go.json
var flagsConfig []byte
func main() {
m, err := ff4go.NewManagerFromBytes(flagsConfig)
if err != nil {
panic(err)
}
fmt.Println(m.IsEnabled("new-ui")) // true
}{
"flags": [
{
"name": "new-checkout",
"enabled": true,
"rules": { "percentage": 10.0 }
}
]
}// The result is deterministic per (flag name, user id) pair.
fmt.Println(m.IsEnabledForUser("new-checkout", "user42")) // stable true/false{
"flags": [
{
"name": "beta-banner",
"enabled": true,
"rules": {
"users": ["tester1"],
"endAt": "2026-06-01T00:00:00Z"
}
}
]
}After 2026-06-01T00:00:00Z all targeting methods return false for beta-banner.