Pular para o conteúdo principal

Avaliação

Endpoints para avaliação de feature flags em tempo real.

Endpoints

MétodoEndpointDescriçãoPermissão
POST/api/v1/feature-flags/evaluate/:flagKeyAvaliar uma flagFEATURE_FLAGS_EVALUATE
POST/api/v1/feature-flags/evaluate-batchAvaliar multiplas flagsFEATURE_FLAGS_EVALUATE
POST/api/v1/feature-flags/evaluate-allAvaliar todas as flags ativasFEATURE_FLAGS_EVALUATE

Contexto de Avaliação

O contexto e enviado no body de todas as requisições de avaliação:

CampoTipoObrigatórioDescrição
context.userIdstringNãoID do usuário (usado para rollout percentual e overrides)
context.attributesobjectNãoAtributos do usuário para matching de segmentos

Resposta de Avaliação

CampoTipoDescrição
flagKeystringKey da flag avaliada
valueanyValor da variação retornada
variationKeystringKey da variação retornada
reasonenumMotivo da decisão: DISABLED, DEFAULT, OVERRIDE, RULE_MATCH
matchedRuleIdstringID da targeting rule que fez match (se aplicável)
matchedSegmentIdsarrayIDs dos segmentos que fizeram match (se aplicável)

Avaliar Uma Flag

POST /api/v1/feature-flags/evaluate/:flagKey

Avalia uma unica feature flag para o contexto fornecido.

Request

curl -X POST 'https://feature-flags.stg.catalisa.app/api/v1/feature-flags/evaluate/new-checkout-flow' \
-H 'Authorization: Bearer SEU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"context": {
"userId": "user-123",
"attributes": {
"plan": "premium",
"country": "BR",
"age": 25
}
}
}'

Response (200 OK)

{
"data": {
"flagKey": "new-checkout-flow",
"value": true,
"variationKey": "on",
"reason": "RULE_MATCH",
"matchedRuleId": "550e8400-e29b-41d4-a716-446655440002",
"matchedSegmentIds": ["550e8400-e29b-41d4-a716-446655440001"]
}
}

Avaliar Multiplas Flags (Batch)

POST /api/v1/feature-flags/evaluate-batch

Avalia multiplas flags de uma vez para o mesmo contexto. Máximo de 50 flags por requisição.

Request

curl -X POST 'https://feature-flags.stg.catalisa.app/api/v1/feature-flags/evaluate-batch' \
-H 'Authorization: Bearer SEU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"flagKeys": ["new-checkout-flow", "dark-mode", "premium-features"],
"context": {
"userId": "user-123",
"attributes": {
"plan": "premium"
}
}
}'

Response (200 OK)

{
"data": [
{
"flagKey": "new-checkout-flow",
"value": true,
"variationKey": "on",
"reason": "RULE_MATCH",
"matchedRuleId": "550e8400-e29b-41d4-a716-446655440002"
},
{
"flagKey": "dark-mode",
"value": false,
"variationKey": "off",
"reason": "DEFAULT"
},
{
"flagKey": "premium-features",
"value": { "discount": 0.15, "freeShipping": true },
"variationKey": "premium",
"reason": "RULE_MATCH"
}
]
}

Avaliar Todas as Flags

POST /api/v1/feature-flags/evaluate-all

Avalia todas as flags ativas da organização para o contexto fornecido.

Request

curl -X POST 'https://feature-flags.stg.catalisa.app/api/v1/feature-flags/evaluate-all' \
-H 'Authorization: Bearer SEU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"context": {
"userId": "user-123",
"attributes": {
"plan": "premium"
}
}
}'

Response (200 OK)

{
"data": [
{
"flagKey": "new-checkout-flow",
"value": true,
"variationKey": "on",
"reason": "RULE_MATCH"
},
{
"flagKey": "dark-mode",
"value": false,
"variationKey": "off",
"reason": "DEFAULT"
},
{
"flagKey": "premium-features",
"value": { "discount": 0.15, "freeShipping": true },
"variationKey": "premium",
"reason": "RULE_MATCH"
}
]
}

Reasons (Motivos de Decisão)

ReasonDescrição
DISABLEDFlag esta desativada, retornou default variation
DEFAULTNenhuma targeting rule fez match, retornou default variation
OVERRIDEOverride configurado para este usuário
RULE_MATCHUma targeting rule fez match

Lógica de Avaliação

Fluxo de Decisão da Avaliação

A avaliação de uma feature flag segue este fluxo sequencial:

1. Verificar se a flag está desativada

  • Se SIM: Retornar defaultVariation com reason: DISABLED
  • Se NÃO: Continuar para próximo passo

2. Verificar se existe override para o usuário

  • Se SIM: Retornar variação do override com reason: OVERRIDE
  • Se NÃO: Continuar para próximo passo

3. Avaliar targeting rules

  • Iterar pelas targeting rules por ordem de prioridade (menor primeiro)
  • Para cada rule:
    • 3.1. Verificar se a rule faz match

      • Avaliar se usuário pertence aos segmentos da rule
      • Se NÃO faz match: Continuar para próxima rule
      • Se faz match: Continuar para passo 3.2
    • 3.2. Aplicar rollout percentual

      • Calcular hash consistente do userId
      • Verificar se hash está dentro do percentual definido
      • Se passou no rollout: Retornar variação da rule com reason: RULE_MATCH
      • Se não passou: Continuar para próxima rule

4. Nenhuma rule fez match

  • Retornar defaultVariation com reason: DEFAULT

Resumo dos Reasons:

  • DISABLED: Flag desativada
  • OVERRIDE: Override configurado para este usuário
  • RULE_MATCH: Targeting rule fez match e passou no rollout
  • DEFAULT: Nenhuma rule fez match ou flag não tem rules

Cache e Performance

Cache de Avaliação

  • Resultados são cacheados por 5 minutos (300 segundos)
  • Cache key: ff:eval:{organizationId}:{flagKey}:{hash(context)}
  • Cache invalidado automaticamente ao atualizar flag, rules ou overrides

Cache de Segment Membership

  • Membership de segmentos cacheado por 15 minutos (900 segundos)
  • Cache key: ff:membership:{organizationId}:{segmentId}:{userId}
  • Cache invalidado ao atualizar segmento

Melhores Praticas

// ✅ BOM: Buscar todas as flags de uma vez no inicio da sessao
const flags = await evaluateAll({ context });

// ❌ RUIM: Avaliar flag a flag durante a sessao
if (await evaluateFlag('flag-1', context)) { ... }
if (await evaluateFlag('flag-2', context)) { ... }
if (await evaluateFlag('flag-3', context)) { ... }

// ✅ BOM: Usar evaluate-batch para um conjunto conhecido de flags
const flags = await evaluateBatch({
flagKeys: ['feature-a', 'feature-b', 'feature-c'],
context,
});

// ✅ BOM: Incluir userId para rollout consistente
const flags = await evaluateAll({
context: {
userId: currentUser.id, // Garante consistencia
attributes: { plan: currentUser.plan },
},
});

Exemplos de Uso

Bootstrap de Aplicacao Frontend

// No carregamento da aplicacao
async function loadFeatureFlags(userId, userPlan) {
const response = await fetch('/feature-flags/api/v1/feature-flags/evaluate-all', {
method: 'POST',
headers: {
'Authorization': `Bearer ${getToken()}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
context: {
userId,
attributes: { plan: userPlan },
},
}),
});

const { data } = await response.json();

// Armazenar em estado global
return data.reduce((flags, item) => {
flags[item.flagKey] = item.value;
return flags;
}, {});
}

// Uso no componente
function CheckoutButton() {
const flags = useFeatureFlags();

if (flags['new-checkout-flow']) {
return <NewCheckout />;
}
return <LegacyCheckout />;
}

Backend Feature Toggle

// Service layer
async function processPayment(userId, order) {
const { data } = await evaluateFlag('pix-payment-enabled', {
context: {
userId,
attributes: {
orderValue: order.total,
country: order.shippingCountry,
},
},
});

if (data.value === true) {
return processWithPix(order);
}
return processWithCreditCard(order);
}

A/B Testing

// Avaliar variante do experimento
const { data } = await evaluateFlag('checkout-button-experiment', {
context: {
userId: 'user-123',
},
});

// data.value pode ser "green", "blue", ou "red"
// data.variationKey indica qual variante foi atribuida

// Registrar em analytics
analytics.track('experiment_viewed', {
experimentId: 'checkout-button-experiment',
variant: data.variationKey,
userId: 'user-123',
});

Erros Comuns

CódigoErroDescrição
404NOT_FOUNDFlag nao encontrada (evaluate single)
400VALIDATIONflagKeys vazio ou excede limite de 50 (batch)