Objetivo da Aula
Ao concluir esta aula, você será capaz de:
Calcular WSJF (Weighted Shortest Job First) para priorização SAFe
Gerar sugestão de roadmap trimestral via LLM combinando RICE e MoSCoW
Usar LLM para detectar duplicatas e conflitos no backlog
Completar os 2 TODOs: calcularWSJF e sugerirRoadmap
Comparar RICE vs WSJF e quando usar cada um
Por que isso importa
RICE prioriza por impacto e esforço. WSJF (SAFe) adiciona urgência temporal — o “custo do atraso”. Uma feature que perde valor rapidamente (lançamento do concorrente, janela regulatória) deve ter prioridade maior mesmo com RICE similar a outra atemporal.
Roadmap gerado por LLM combina dados de scoring com restrições de capacidade, dependências e estratégia — o que seria horas de trabalho de PM condensado em segundos.
Conceitos Fundamentais
WSJF: Weighted Shortest Job First
WSJF = Cost of Delay / Duration (effort)
Cost of Delay = Valor do Usuário + Criticidade Temporal + Redução de Risco
Cada dimensão em escala 1-10 de Fibonacci (1, 2, 3, 5, 8, 13)WSJF mais alto = fazer primeiro
TODO 1: calcularWSJF(item)
function calcularWSJF(item) {
const { valor_usuario, criticidade_temporal, reducao_risco, esforco } = item
if (!esforco || esforco === 0) {
return { ...item, wsjf: null, erro: 'esforço inválido' }
}
const cost_of_delay = valor_usuario + criticidade_temporal + reducao_risco
const wsjf = cost_of_delay / esforco
return {
...item,
cost_of_delay,
wsjf: Math.round(wsjf * 10) / 10,
interpretacao: wsjf > 10 ? 'urgente' : wsjf > 5 ? 'normal' : 'pode aguardar',
}
}Por que Fibonacci? Em vez de escala linear (1-10), Fibonacci (1, 2, 3, 5, 8, 13) força diferenciação: há uma diferença real entre “3” e “5”, mas não entre “7” e “8”. Reduz debates sobre diferenças marginais.
TODO 2: sugerirRoadmap(historias, client)
async function sugerirRoadmap(historias, client) {
const prioritizados = historias
.map(calcularWSJF)
.filter(h => h.wsjf !== null)
.sort((a, b) => b.wsjf - a.wsjf)
const contexto = prioritizados.map((h, i) =>
`${i+1}. ${h.titulo} [WSJF:${h.wsjf}, MoSCoW:${h.moscow || 'N/A'}]`
).join('\n')
const response = await client.messages.create({
model: 'claude-haiku-4-5-20251001',
max_tokens: 600,
system: `Você é um PM experiente. Dado o backlog priorizado, sugira um roadmap de 3 meses (Q1, Q2, Q3) considerando:
- Must-haves sempre no Q1
- WSJF mais alto = entrega mais cedo
- Máximo de 4 itens por trimestre (capacity)
- Retorne JSON: {"Q1": [...], "Q2": [...], "Q3": [...], "backlog": [...]}`,
messages: [{
role: 'user',
content: `Backlog priorizado:\n${contexto}`,
}],
})
const texto = response.content[0].text
const i = texto.indexOf('{'), f = texto.lastIndexOf('}') + 1
return JSON.parse(texto.slice(i, f))
}Aprofundamento Técnico
RICE vs WSJF: Quando Usar
Contexto | RICE | WSJF |
Startup descobrindo product-market fit | Melhor | OK |
Empresa com ciclos SAFe/trimestrais | OK | Melhor |
Urgência temporal importa muito | Não capta | Capta |
Dados de usuários disponíveis | Melhor (usa Reach) | OK |
Em muitos times: use RICE para conversas internas, WSJF para programação formal de PI (Program Increment).
Detecção de Duplicatas com LLM
async function detectarDuplicatas(historias, client) {
const titulos = historias.map((h, i) => `${i}: ${h.titulo}`).join('\n')
const response = await client.messages.create({
model: 'claude-haiku-4-5-20251001',
max_tokens: 400,
messages: [{
role: 'user',
content: `Analise estas user stories e identifique duplicatas ou conflitos:
${titulos}
Retorne JSON: {"duplicatas": [[0, 3], ...], "conflitos": [[1, 5, "motivo"], ...]}`,
}],
})
const texto = response.content[0].text
const i = texto.indexOf('{'), f = texto.lastIndexOf('}') + 1
return JSON.parse(texto.slice(i, f))
}Um LLM detecta que “Adicionar filtros na lista de produtos” e “Busca avançada no catálogo” se sobrepõem — o que um simples diff de texto não detectaria.
Exemplos Anotados
Exemplo 1: WSJF com itens urgentes vs atemporais
const backlog = [
{ titulo: 'LGPD: relatório de dados do usuário', valor_usuario: 5, criticidade_temporal: 13, reducao_risco: 8, esforco: 3 },
{ titulo: 'Dashboard de analytics', valor_usuario: 8, criticidade_temporal: 2, reducao_risco: 1, esforco: 5 },
{ titulo: 'Integração com API de pagamento', valor_usuario: 13, criticidade_temporal: 5, reducao_risco: 8, esforco: 8 },
]
const scored = backlog.map(calcularWSJF).sort((a, b) => b.wsjf - a.wsjf)
// → LGPD (WSJF 8.7): urgente ← criticidade temporal 13 eleva o score
// → Pagamento (WSJF 3.2): normal
// → Analytics (WSJF 2.2): pode aguardarO LGPD seria prioritário mesmo tendo valor de usuário menor que pagamento — urgência temporal (deadline regulatório) domina.
Padrões e Armadilhas
Padrões
Padrão 1: Comparar dentro do mesmo sprint/PI WSJF 8.7 vs 2.2 faz sentido no mesmo backlog. Não compare WSJF entre projetos diferentes.
Padrão 2: MoSCoW como constraint sobre WSJF WSJF prioriza, MoSCoW determina o mínimo viável. Must-haves entram independente do WSJF.
Armadilhas
⚠️ Armadilha 1: esforco = 0 → divisão por zero
if (!esforco || esforco === 0) return { ...item, wsjf: null, erro: 'esforço inválido' }⚠️ Armadilha 2: Não incluir capacidade no roadmap Um Q1 com 10 itens de alta prioridade e um time de 3 devs é impossível.
Se não for realizar o laboratório, pule para o próximo capítulo.
Ponte para o Lab
TODO 1 — calcularWSJF(item): cost_of_delay = valor_usuario + criticidade_temporal + reducao_risco; wsjf = cost_of_delay / esforco
TODO 2 — sugerirRoadmap(historias, client): ordene por WSJF, passe para LLM com instrução de máximo 4 itens por trimestre, parse JSON resultado.
Agora você está pronto para o lab.