FlagShark detects feature flags by analyzing the method calls in your code. It knows that client.BoolVariation("my-flag", ctx, false) is a LaunchDarkly flag evaluation and that unleash.isEnabled("my-toggle") is an Unleash toggle check. But how does it know? And what happens when your team uses a custom in-house flag library that FlagShark has never seen before?
The answer is .flagshark.yml -- a configuration file that lives in the root of your repository and tells FlagShark exactly how your codebase evaluates feature flags. It maps SDK import paths to method signatures and tells the detection engine where to find the flag key in each method call.
This guide walks through the .flagshark.yml schema, provides ready-to-use configurations for popular providers, and shows you how to configure detection for custom in-house flag libraries that are unique to your organization.
What .flagshark.yml does
When FlagShark analyzes a pull request, it parses the code changes using tree-sitter (a production-grade parser that understands the actual syntax of 13 programming languages). It looks for method calls that match the patterns defined in .flagshark.yml and extracts the flag key from the arguments.
The detection flow works like this:
- FlagShark receives a webhook for a pull request
- It fetches the diff and parses the changed files
- For each file, it identifies the programming language and finds matching providers from
.flagshark.yml - It scans for method calls matching the configured method names
- It verifies the import/package path matches the configured provider
- It extracts the flag key from the argument at the configured index
- The detected flag is tracked in FlagShark's lifecycle database
Without a .flagshark.yml file, FlagShark falls back to built-in detection patterns for common providers. But the YAML configuration gives you full control over what gets detected and how.
The schema: Understanding every field
Here is the complete schema for .flagshark.yml:
providers:
- name: "Provider Display Name" # Human-readable name for this provider
language: "go" # Which language this provider applies to
importPattern: "github.com/..." # The import/package path to match (optional)
enabled: true # Toggle detection on/off without removing config
methods:
- name: "MethodName" # The method/function name to detect
flagKeyIndex: 0 # Position of the flag key in the argument list
Let's break down each field in detail.
Provider fields
| Field | Required | Type | Description |
|---|---|---|---|
name | Yes | string | Display name shown in FlagShark's dashboard and PR comments |
language | Yes | string | The single language this provider applies to. One provider entry maps to exactly one language. Valid values: typescript, javascript, go, python, java, kotlin, swift, ruby, csharp, php, rust, cpp, objc |
importPattern | No | string | The import or package path that identifies this SDK. For Go: the module path. For JS/TS: the npm package name. For Python: the module name. If omitted, FlagShark matches on method name alone. |
enabled | No | boolean | Default true. Set to false to temporarily disable detection without removing the config. |
Method fields
| Field | Required | Type | Description |
|---|---|---|---|
name | Yes | string | The exact method name to detect. Case-sensitive. |
flagKeyIndex | Yes | integer | Zero-based index of the flag key in the argument list. |
Understanding flagKeyIndex
The flagKeyIndex is the most important field to get right. It tells FlagShark which argument in the method call contains the flag key string.
Index: 0 1 2
| | |
client.BoolVariation("my-flag", context, false)
^^^^^^^^
flagKeyIndex: 0
Index: 0 1
| |
zeroflag.Bool(ctx, "feature-flag")
^^^^^^^^^^^^^^
flagKeyIndex: 1
Index: 0 1 2
| | |
client.Treatment("user-key", "feature-flag", nil)
^^^^^^^^^^^^^^
flagKeyIndex: 1
Ready-to-use configurations for popular providers
Below are complete, tested configurations for the most popular feature flag providers. Copy the relevant sections into your .flagshark.yml.
LaunchDarkly
LaunchDarkly has language-specific SDKs with slightly different method signatures. Here are configurations for the three most common languages.
Go:
- name: "LaunchDarkly Go SDK"
language: "go"
importPattern: "github.com/launchdarkly/go-server-sdk/v7"
enabled: true
methods:
- name: "BoolVariation"
flagKeyIndex: 0
- name: "StringVariation"
flagKeyIndex: 0
- name: "IntVariation"
flagKeyIndex: 0
- name: "Float64Variation"
flagKeyIndex: 0
- name: "JSONVariation"
flagKeyIndex: 0
- name: "BoolVariationDetail"
flagKeyIndex: 0
- name: "StringVariationDetail"
flagKeyIndex: 0
- name: "IntVariationDetail"
flagKeyIndex: 0
- name: "Float64VariationDetail"
flagKeyIndex: 0
- name: "JSONVariationDetail"
flagKeyIndex: 0
TypeScript (Node.js server SDK):
- name: "LaunchDarkly JS SDK"
language: "typescript"
importPattern: "@launchdarkly/node-server-sdk"
enabled: true
methods:
- name: "variation"
flagKeyIndex: 0
- name: "boolVariation"
flagKeyIndex: 0
- name: "stringVariation"
flagKeyIndex: 0
- name: "intVariation"
flagKeyIndex: 0
- name: "jsonVariation"
flagKeyIndex: 0
- name: "variationDetail"
flagKeyIndex: 0
JavaScript (Node.js server SDK):
- name: "LaunchDarkly JS SDK"
language: "javascript"
importPattern: "@launchdarkly/node-server-sdk"
enabled: true
methods:
- name: "variation"
flagKeyIndex: 0
- name: "boolVariation"
flagKeyIndex: 0
- name: "stringVariation"
flagKeyIndex: 0
- name: "intVariation"
flagKeyIndex: 0
- name: "jsonVariation"
flagKeyIndex: 0
- name: "variationDetail"
flagKeyIndex: 0
Python:
- name: "LaunchDarkly Python SDK"
language: "python"
importPattern: "ldclient"
enabled: true
methods:
- name: "variation"
flagKeyIndex: 0
- name: "variation_detail"
flagKeyIndex: 0
- name: "bool_variation"
flagKeyIndex: 0
Unleash
Unleash uses consistent method names across its SDKs, but the import paths differ by language.
Go:
- name: "Unleash Go SDK"
language: "go"
importPattern: "github.com/Unleash/unleash-client-go"
enabled: true
methods:
- name: "IsEnabled"
flagKeyIndex: 0
- name: "GetVariant"
flagKeyIndex: 0
TypeScript:
- name: "Unleash JS SDK"
language: "typescript"
importPattern: "unleash-client"
enabled: true
methods:
- name: "isEnabled"
flagKeyIndex: 0
- name: "getVariant"
flagKeyIndex: 0
JavaScript:
- name: "Unleash JS SDK"
language: "javascript"
importPattern: "unleash-client"
enabled: true
methods:
- name: "isEnabled"
flagKeyIndex: 0
- name: "getVariant"
flagKeyIndex: 0
Python:
- name: "Unleash Python SDK"
language: "python"
importPattern: "UnleashClient"
enabled: true
methods:
- name: "is_enabled"
flagKeyIndex: 0
- name: "get_variant"
flagKeyIndex: 0
Split.io
Split.io uses "treatment" terminology instead of "variation." The flag key position varies by language.
Go:
- name: "Split.io Go SDK"
language: "go"
importPattern: "github.com/splitio/go-client"
enabled: true
methods:
- name: "Treatment"
flagKeyIndex: 1 # Note: user key is index 0, flag key is index 1
- name: "Treatments"
flagKeyIndex: 1
- name: "TreatmentWithConfig"
flagKeyIndex: 1
TypeScript:
- name: "Split.io JS SDK"
language: "typescript"
importPattern: "@splitsoftware/splitio"
enabled: true
methods:
- name: "getTreatment"
flagKeyIndex: 0 # Note: JS SDK puts flag key first
- name: "getTreatments"
flagKeyIndex: 0
- name: "getTreatmentWithConfig"
flagKeyIndex: 0
JavaScript:
- name: "Split.io JS SDK"
language: "javascript"
importPattern: "@splitsoftware/splitio"
enabled: true
methods:
- name: "getTreatment"
flagKeyIndex: 0 # Note: JS SDK puts flag key first
- name: "getTreatments"
flagKeyIndex: 0
- name: "getTreatmentWithConfig"
flagKeyIndex: 0
PostHog
PostHog combines analytics and feature flags. The flag methods take a flag key and a distinct user ID.
TypeScript:
- name: "PostHog JS SDK"
language: "typescript"
importPattern: "posthog-node"
enabled: true
methods:
- name: "isFeatureEnabled"
flagKeyIndex: 0
- name: "getFeatureFlag"
flagKeyIndex: 0
- name: "getFeatureFlagPayload"
flagKeyIndex: 0
JavaScript:
- name: "PostHog JS SDK"
language: "javascript"
importPattern: "posthog-node"
enabled: true
methods:
- name: "isFeatureEnabled"
flagKeyIndex: 0
- name: "getFeatureFlag"
flagKeyIndex: 0
- name: "getFeatureFlagPayload"
flagKeyIndex: 0
Python:
- name: "PostHog Python SDK"
language: "python"
importPattern: "posthog"
enabled: true
methods:
- name: "feature_enabled"
flagKeyIndex: 0
- name: "get_feature_flag"
flagKeyIndex: 0
- name: "get_feature_flag_payload"
flagKeyIndex: 0
Flipper (Ruby)
- name: "Flipper Ruby SDK"
language: "ruby"
importPattern: "flipper"
enabled: true
methods:
- name: "enabled?"
flagKeyIndex: 0
- name: "enable"
flagKeyIndex: 0
- name: "disable"
flagKeyIndex: 0
Configuring custom in-house providers
This is where .flagshark.yml becomes essential. If your team has built a custom feature flag library -- whether it is a thin wrapper around an existing provider or a fully custom implementation -- you need to tell FlagShark how to detect it.
Step 1: Identify your SDK's method signatures
Start by finding how your flag library is used in code. Look for the import statement and the method calls.
Example custom Go library:
import "github.com/yourcompany/featureflags"
// Usage:
enabled := featureflags.IsEnabled(ctx, "new-dashboard")
value := featureflags.GetString(ctx, "banner-text", "default")
Example custom TypeScript library:
import { FeatureFlags } from '@yourcompany/feature-flags';
// Usage:
const enabled = await FeatureFlags.check('new-onboarding-flow');
const variant = await FeatureFlags.getVariant('pricing-experiment', 'control');
Step 2: Map the method signatures to YAML
For each method, identify:
- The exact method name (case-sensitive)
- Which argument position contains the flag key
Go example:
- name: "YourCompany Feature Flags"
language: "go"
importPattern: "github.com/yourcompany/featureflags"
enabled: true
methods:
- name: "IsEnabled"
flagKeyIndex: 1 # ctx is index 0, flag key is index 1
- name: "GetString"
flagKeyIndex: 1
- name: "GetInt"
flagKeyIndex: 1
- name: "GetBool"
flagKeyIndex: 1
TypeScript example:
- name: "YourCompany Feature Flags"
language: "typescript"
importPattern: "@yourcompany/feature-flags"
enabled: true
methods:
- name: "check"
flagKeyIndex: 0
- name: "getVariant"
flagKeyIndex: 0
- name: "isEnabled"
flagKeyIndex: 0
Step 3: Handle common custom patterns
Wrapper around an existing SDK:
If your custom library wraps LaunchDarkly or another provider, you likely want to configure both the wrapper and the underlying SDK. This ensures FlagShark detects flags whether they are called through your wrapper or directly through the underlying SDK.
providers:
# Your custom wrapper
- name: "Internal Flag Wrapper"
language: "go"
importPattern: "github.com/yourcompany/flags"
enabled: true
methods:
- name: "IsEnabled"
flagKeyIndex: 0
# The underlying LaunchDarkly SDK (in case anyone uses it directly)
- name: "LaunchDarkly Go SDK"
language: "go"
importPattern: "github.com/launchdarkly/go-server-sdk/v7"
enabled: true
methods:
- name: "BoolVariation"
flagKeyIndex: 0
Environment variable-based flags:
Some teams use environment variables as feature flags. While FlagShark's tree-sitter parsing is optimized for method calls, you can still detect common patterns by configuring the environment variable access pattern:
- name: "Environment Variable Flags"
language: "go"
importPattern: "os"
enabled: true
methods:
- name: "Getenv"
flagKeyIndex: 0
Note: This will detect all os.Getenv calls, not just flag-related ones. Use this only if your team has a clear naming convention for flag-related environment variables (e.g., prefixed with FEATURE_) and you are willing to filter results.
Multi-language internal SDKs:
If your internal flag library has implementations in multiple languages with the same package organization, create a separate provider entry per language. Because language is required and singular, each language gets its own entry:
# Separate entries per language
- name: "Internal Flags (Go)"
language: "go"
importPattern: "github.com/yourcompany/flags"
methods:
- name: "IsEnabled"
flagKeyIndex: 1
- name: "Internal Flags (Python)"
language: "python"
importPattern: "yourcompany.flags"
methods:
- name: "is_enabled"
flagKeyIndex: 0
Separate entries per language are also what you want when your SDKs have language-idiomatic method names (e.g., IsEnabled in Go vs. is_enabled in Python). If the method names happen to be identical across languages, you still create one entry per language -- just repeat the same methods block under each language.
Advanced configuration
Multiple providers in one repository
Most real-world repositories use more than one flag provider. Perhaps you use LaunchDarkly for production flags and a custom library for internal tooling flags. Your .flagshark.yml can include any number of providers:
providers:
- name: "LaunchDarkly Go SDK"
language: "go"
importPattern: "github.com/launchdarkly/go-server-sdk/v7"
enabled: true
methods:
- name: "BoolVariation"
flagKeyIndex: 0
- name: "Internal Feature Flags"
language: "go"
importPattern: "github.com/yourcompany/flags"
enabled: true
methods:
- name: "IsEnabled"
flagKeyIndex: 0
- name: "PostHog JS SDK"
language: "typescript"
importPattern: "posthog-node"
enabled: true
methods:
- name: "isFeatureEnabled"
flagKeyIndex: 0
FlagShark evaluates each provider independently. A single file can match multiple providers if it imports multiple flag SDKs.
Temporarily disabling a provider
If you are migrating from one provider to another and want to stop tracking flags from the old provider without deleting the configuration, set enabled: false:
- name: "Legacy Flag System"
language: "go"
importPattern: "github.com/yourcompany/legacy-flags"
enabled: false # Temporarily disabled during migration
methods:
- name: "Check"
flagKeyIndex: 0
The configuration is preserved for reference, but FlagShark ignores it during detection.
Language-specific overrides
Some providers have different SDKs for different languages with completely different APIs. Configure each as a separate provider entry with the appropriate language field:
# The same provider has different APIs in different languages
- name: "ConfigCat Go SDK"
language: "go"
importPattern: "github.com/configcat/go-sdk/v7"
enabled: true
methods:
- name: "GetBoolValue"
flagKeyIndex: 0
- name: "GetStringValue"
flagKeyIndex: 0
- name: "ConfigCat CSharp SDK"
language: "csharp"
importPattern: "ConfigCat.Client"
enabled: true
methods:
- name: "GetValueAsync" # C# uses async pattern
flagKeyIndex: 0
- name: "GetValueDetailsAsync"
flagKeyIndex: 0
Testing your configuration
After creating or modifying your .flagshark.yml, verify that it works correctly before relying on it for flag detection.
Manual verification
The simplest test is to open a pull request that adds a flag evaluation using your configured provider. FlagShark should detect the flag and comment on the PR with the detected flag key.
Create a test file with a clear flag evaluation:
package test
import "github.com/yourcompany/flags"
func TestFlagDetection() {
// FlagShark should detect this flag
enabled := flags.IsEnabled("test-detection-flag")
_ = enabled
}
Open a PR with this change and verify that FlagShark detects test-detection-flag. Then close the PR without merging.
Common configuration mistakes
| Symptom | Likely Cause | Fix |
|---|---|---|
| No flags detected | importPattern does not match actual import path | Check the exact import path in your code |
| Wrong flag key extracted | flagKeyIndex points to wrong argument | Count arguments starting from 0 |
| False positives | Method name is too generic (e.g., get, check) | Set an importPattern so detection requires the matching import |
| Provider not matching | language field does not match the file's language | Set the correct language, or add a separate provider entry for that language |
| Detection works in Go but not TypeScript | Different SDK has different method names | Create separate provider entries per language |
Verifying importPattern
The importPattern must match exactly what appears in your source code's import statement. Here is how to verify for each language:
Go -- look at the import block:
import (
ld "github.com/launchdarkly/go-server-sdk/v7"
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// This is your importPattern
)
TypeScript/JavaScript -- look at the import or require:
import { LDClient } from '@launchdarkly/node-server-sdk';
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// This is your importPattern
Python -- look at the import:
import ldclient
# ^^^^^^^^
# This is your importPattern
from ldclient import LDClient
# ^^^^^^^^
# This is your importPattern
A complete example configuration
Here is a full .flagshark.yml for a team that uses LaunchDarkly for their Go backend, PostHog for their TypeScript frontend, and a custom in-house library for Python data pipelines:
providers:
# Production flag provider (Go backend)
- name: "LaunchDarkly Go SDK"
language: "go"
importPattern: "github.com/launchdarkly/go-server-sdk/v7"
enabled: true
methods:
- name: "BoolVariation"
flagKeyIndex: 0
- name: "StringVariation"
flagKeyIndex: 0
- name: "IntVariation"
flagKeyIndex: 0
- name: "BoolVariationDetail"
flagKeyIndex: 0
# Frontend flag provider (TypeScript dashboard)
- name: "PostHog JS SDK"
language: "typescript"
importPattern: "posthog-node"
enabled: true
methods:
- name: "isFeatureEnabled"
flagKeyIndex: 0
- name: "getFeatureFlag"
flagKeyIndex: 0
# Internal flag library (Python data pipelines)
- name: "Internal Python Flags"
language: "python"
importPattern: "yourcompany.feature_flags"
enabled: true
methods:
- name: "is_enabled"
flagKeyIndex: 0
- name: "get_value"
flagKeyIndex: 0
This configuration ensures FlagShark detects flags across all three parts of the stack, each with different providers and different method signatures. When a developer opens a PR that touches Go backend code, TypeScript frontend code, and Python pipeline code, FlagShark will detect flags from all three providers and track them in a unified lifecycle view.
Troubleshooting
"FlagShark isn't detecting my flags"
Work through this checklist:
- Is
.flagshark.ymlin the repository root? It must be at the top level of the repository, not in a subdirectory. - Is the provider
enabled? Check thatenabledis not set tofalse. - Does
importPatternmatch exactly? Compare character by character with your actual import statement. - Does the
languagefield match your file's language? A Go file will not match a provider withlanguage: "typescript".
"FlagShark is detecting non-flag method calls"
This happens when method names are too generic. Solutions:
- Set an
importPatternto require the exact import path before matching - Use a more specific method name if your SDK supports it (e.g.,
isFeatureEnabledinstead ofisEnabled)
"I changed .flagshark.yml but nothing changed"
Configuration changes take effect on the next pull request that FlagShark processes. Existing PRs are not re-analyzed. Open a new PR or push a new commit to an existing PR to trigger re-detection.
The .flagshark.yml configuration is what makes FlagShark work with any feature flag provider, not just the popular ones. Whether your team uses LaunchDarkly, Unleash, Split, PostHog, Flipper, ConfigCat, or a completely custom in-house library, a few minutes of YAML configuration gives you complete flag lifecycle tracking across your entire codebase.
Start with the ready-to-use configurations for your provider, add your custom libraries, and let FlagShark handle the rest. Every flag that gets detected is a flag that will not silently accumulate into technical debt.