{"id":26865791,"url":"https://github.com/dorianneto/ff4go","last_synced_at":"2026-05-05T01:03:36.145Z","repository":{"id":283931389,"uuid":"953255835","full_name":"dorianneto/ff4go","owner":"dorianneto","description":"ff4go is a simple and lightweight feature flag solution 100% Open Source.","archived":false,"fork":false,"pushed_at":"2026-04-25T22:01:06.000Z","size":15,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-26T00:02:49.172Z","etag":null,"topics":["beginner-friendly","continuous-deployment","devops","feature-flag","feature-flags","feature-toggle","feature-toggles","feature-toggling","go","go-feature-flag","golang","golang-library","toggles"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/dorianneto/ff4go","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dorianneto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-22T23:23:54.000Z","updated_at":"2026-04-25T22:01:48.000Z","dependencies_parsed_at":"2026-04-26T00:00:40.426Z","dependency_job_id":null,"html_url":"https://github.com/dorianneto/ff4go","commit_stats":null,"previous_names":["dorianneto/ff4go"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/dorianneto/ff4go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fff4go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fff4go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fff4go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fff4go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dorianneto","download_url":"https://codeload.github.com/dorianneto/ff4go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorianneto%2Fff4go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32631059,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"ssl_error","status_checked_at":"2026-05-04T10:08:02.005Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["beginner-friendly","continuous-deployment","devops","feature-flag","feature-flags","feature-toggle","feature-toggles","feature-toggling","go","go-feature-flag","golang","golang-library","toggles"],"created_at":"2025-03-31T04:44:15.476Z","updated_at":"2026-05-05T01:03:36.139Z","avatar_url":"https://github.com/dorianneto.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/dorianneto/ff4go/go.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/dorianneto/ff4go)](https://goreportcard.com/report/github.com/dorianneto/ff4go)\n![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/dorianneto/ff4go)\n![GitHub License](https://img.shields.io/github/license/dorianneto/ff4go)\n\n\u003e Thank you for using ff4go! Feel free to report any [issue or improvement](https://github.com/dorianneto/ff4go/issues) 🙏\n\n\n## Installation\n\n```\ngo get github.com/dorianneto/ff4go\n```\n\n\n## Configuration\n\nCreate an `ff4go.json` file at your project root. Each flag supports the following fields:\n\n```json\n{\n  \"flags\": [\n    {\n      \"name\": \"new-ui\",\n      \"enabled\": true,\n      \"description\": \"Enables the redesigned UI\",\n      \"rules\": {\n        \"users\": [\"user1\", \"user2\"],\n        \"environments\": [\"development\", \"staging\"],\n        \"percentage\": 25.0,\n        \"endAt\": \"2026-12-31T23:59:59Z\"\n      }\n    }\n  ]\n}\n```\n\n### Rules\n\n| Field          | Type       | Description |\n|----------------|------------|-------------|\n| `users`        | `[]string` | List of user identifiers that the flag is active for. |\n| `environments` | `[]string` | List of environment names that the flag is active for. |\n| `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. |\n| `endAt`        | `string`   | RFC 3339 timestamp after which the flag is treated as disabled, regardless of other rules. Omit to disable expiry. |\n\n\u003e `users` and `environments` are only evaluated when `percentage` is **not** set.\n\n\n## Initialization\n\n### From a file\n\nReads `ff4go.json` from the process working directory.\n\n```go\nm, err := ff4go.NewManagerFromFile()\nif err != nil {\n    panic(err)\n}\n```\n\n### From bytes\n\nUseful when you load the configuration from a remote source, an environment variable, or embed it with `//go:embed`.\n\n```go\ndata := []byte(`{\"flags\":[{\"name\":\"new-ui\",\"enabled\":true}]}`)\n\nm, err := ff4go.NewManagerFromBytes(data)\nif err != nil {\n    panic(err)\n}\n```\n\n\n### From a file with watch\n\nReads `ff4go.json` from the process working directory and automatically reloads flags when the file changes. Useful for long-running services that need to pick up flag updates without restarting.\n\n```go\nm, err := ff4go.NewManagerFromFileWithWatch()\nif err != nil {\n    panic(err)\n}\n```\n\nThe returned `*Manager` pointer is updated in place when the file changes — no need to re-initialize.\n\n\n## API\n\n```go\n// IsEnabled returns the flag's top-level enabled value.\nm.IsEnabled(\"new-ui\")\n\n// HasFlag reports whether a flag with the given name exists.\nm.HasFlag(\"new-ui\")\n\n// IsEnabledForUser returns true when the flag is enabled and the user\n// matches the users rule (or falls within the percentage rollout).\nm.IsEnabledForUser(\"new-ui\", \"user1\")\n\n// IsEnabledForEnvironment returns true when the flag is enabled and the\n// environment matches the environments rule (or falls within the percentage rollout).\nm.IsEnabledForEnvironment(\"new-ui\", \"development\")\n\n// IsEnabledForUserAndEnvironment returns true only when both the user\n// and environment checks pass simultaneously.\nm.IsEnabledForUserAndEnvironment(\"new-ui\", \"user1\", \"development\")\n```\n\nAll targeting methods return `false` when the flag does not exist, is disabled, or has expired (`endAt` is in the past).\n\n\n## Examples\n\n### Basic flag check\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/dorianneto/ff4go\"\n)\n\nfunc main() {\n    m, err := ff4go.NewManagerFromFile()\n    if err != nil {\n        panic(err)\n    }\n\n    fmt.Println(m.IsEnabled(\"new-ui\"))                                        // true\n    fmt.Println(m.IsEnabledForUser(\"new-ui\", \"user1\"))                        // true\n    fmt.Println(m.IsEnabledForEnvironment(\"new-ui\", \"staging\"))               // false\n    fmt.Println(m.IsEnabledForUserAndEnvironment(\"new-ui\", \"user1\", \"development\")) // true\n}\n```\n\n### Loading from bytes (embedded config)\n\n```go\npackage main\n\nimport (\n    _ \"embed\"\n    \"fmt\"\n\n    \"github.com/dorianneto/ff4go\"\n)\n\n//go:embed ff4go.json\nvar flagsConfig []byte\n\nfunc main() {\n    m, err := ff4go.NewManagerFromBytes(flagsConfig)\n    if err != nil {\n        panic(err)\n    }\n\n    fmt.Println(m.IsEnabled(\"new-ui\")) // true\n}\n```\n\n### Percentage rollout\n\n```json\n{\n  \"flags\": [\n    {\n      \"name\": \"new-checkout\",\n      \"enabled\": true,\n      \"rules\": { \"percentage\": 10.0 }\n    }\n  ]\n}\n```\n\n```go\n// The result is deterministic per (flag name, user id) pair.\nfmt.Println(m.IsEnabledForUser(\"new-checkout\", \"user42\")) // stable true/false\n```\n\n### Scheduled flag expiry\n\n```json\n{\n  \"flags\": [\n    {\n      \"name\": \"beta-banner\",\n      \"enabled\": true,\n      \"rules\": {\n        \"users\": [\"tester1\"],\n        \"endAt\": \"2026-06-01T00:00:00Z\"\n      }\n    }\n  ]\n}\n```\n\nAfter `2026-06-01T00:00:00Z` all targeting methods return `false` for `beta-banner`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdorianneto%2Fff4go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdorianneto%2Fff4go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdorianneto%2Fff4go/lists"}