mozak.tech Engenharia de IA Corporativa 68%

Parte IV — Gestão de Projetos e Produto com Suporte de IA

4.3 — Modelagem de Cronograma e Capacidade com Análise de Cenários (Planejamento)

Objetivo da Aula

Ao concluir esta aula, você será capaz de:

Implementar análise what-if para cenários adversos (dev saindo, feature maior que estimado)

Implementar balanceamento automático de carga quando dev tem >120% da carga média

Calcular caminho crítico em dependências de tasks

Completar os 2 TODOs: analisarWhatIf e balancearCarga

Usar LLM para sugestões de paralelização de tasks

Por que isso importa

Cronogramas falham por dois motivos: estimativas erradas (endereçado por RICE/WSJF) e eventos imprevistos. What-if automático responde “e se um dev sair no meio do sprint?” sem horas de reunião de replanejamento.

Balanceamento de carga é onde gerentes de projeto gastam mais tempo manualmente: João está em 150%, Maria em 80%. LLM pode sugerir redistribuição em segundos.

Conceitos Fundamentais

TODO 1: analisarWhatIf(cenario, sprint, client)

async function analisarWhatIf(cenario, sprint, client) {

  // cenarios: 'dev_saiu', 'feature_maior', 'bug_critico', 'dependencia_atrasou'

  

  const contextoSprint = {

    tasks_pendentes: sprint.tasks.filter(t => t.status !== 'done').length,

    dias_restantes: sprint.dias_restantes,

    capacidade_total: sprint.devs.reduce((acc, d) => acc + d.horas_disponiveis, 0),

  }

  

  const response = await client.messages.create({

    model: 'claude-haiku-4-5-20251001',

    max_tokens: 500,

    system: 'Você é um scrum master experiente. Analise o impacto do cenário e sugira ações de mitigação.',

    messages: [{

      role: 'user',

      content: `Cenário: ${cenario}



Estado atual do sprint:

${JSON.stringify(contextoSprint, null, 2)}



Forneça:

1. Impacto estimado no prazo (dias de atraso)

2. Tarefas que devem ser descartadas do sprint se necessário (por prioridade)

3. Ações imediatas de mitigação (top 3)

4. Comunicação sugerida para stakeholders`,

    }],

  })

  

  return response.content[0].text

}

TODO 2: balancearCarga(sprint)

function balancearCarga(sprint) {

  const devs = sprint.devs

  

  // Calcular carga média

  const total_pontos = sprint.tasks.reduce((acc, t) => acc + (t.pontos || 0), 0)

  const media_por_dev = total_pontos / devs.length

  

  const redistribuicoes = []

  

  // Identificar devs sobrecarregados (>120% da média)

  for (const dev of devs) {

    const carga_dev = sprint.tasks

      .filter(t => t.responsavel === dev.id)

      .reduce((acc, t) => acc + (t.pontos || 0), 0)

    

    const pct_carga = carga_dev / media_por_dev

    

    if (pct_carga > 1.2) {

      // Sobrecarregado: mover tasks menores para devs com menor carga

      const tarefas_realocar = sprint.tasks

        .filter(t => t.responsavel === dev.id && (t.pontos || 0) <= 2)

        .slice(0, Math.ceil((pct_carga - 1.0) * media_por_dev / 2))

      

      // Encontrar dev com menor carga

      const dev_livres = devs

        .filter(d => d.id !== dev.id)

        .map(d => ({

          dev: d,

          carga: sprint.tasks.filter(t => t.responsavel === d.id).reduce((acc, t) => acc + (t.pontos || 0), 0),

        }))

        .sort((a, b) => a.carga - b.carga)

      

      for (const tarefa of tarefas_realocar) {

        const destino = dev_livres[0].dev

        redistribuicoes.push({

          task: tarefa.titulo,

          de: dev.nome,

          para: destino.nome,

          pontos: tarefa.pontos,

        })

      }

    }

  }

  

  return {

    media_pontos_por_dev: Math.round(media_por_dev),

    redistribuicoes,

    sprint_balanceado: redistribuicoes.length > 0,

  }

}

Aprofundamento Técnico

Caminho Crítico

function calcularCaminhoCritico(tasks) {

  // Tasks sem dependências começam em t=0

  const sem_deps = tasks.filter(t => !t.dependencias || t.dependencias.length === 0)

  

  // Para cada task, calcular o tempo mínimo de início

  const tempo_inicio = {}

  

  function calcularInicio(taskId) {

    if (tempo_inicio[taskId] !== undefined) return tempo_inicio[taskId]

    const task = tasks.find(t => t.id === taskId)

    if (!task.dependencias || task.dependencias.length === 0) {

      tempo_inicio[taskId] = 0

      return 0

    }

    const max_dep = Math.max(...task.dependencias.map(depId =>

      calcularInicio(depId) + (tasks.find(t => t.id === depId)?.duracao || 0)

    ))

    tempo_inicio[taskId] = max_dep

    return max_dep

  }

  

  tasks.forEach(t => calcularInicio(t.id))

  

  // Caminho crítico = task com maior tempo_inicio + duracao

  const critico = tasks.reduce((max, t) => {

    const fim = (tempo_inicio[t.id] || 0) + (t.duracao || 0)

    return fim > max.fim ? { task: t, fim } : max

  }, { task: null, fim: 0 })

  

  return { tempo_inicio, duracao_total: critico.fim, task_critica: critico.task }

}

Exemplos Anotados

Exemplo 1: What-If Dev Saiu

Cenário: "dev_saiu" (Pedro saiu no meio do sprint)



Análise LLM:

1. Impacto: +5-7 dias de atraso (Pedro tinha 40% das tasks restantes)

2. Descartar: "Dark mode" (baixa prioridade), "Analytics refinements" (não comprometido)

3. Ações imediatas:

   - Reatribuir tasks de Pedro por RICE (mais importantes primeiro)

   - Comunicar PM sobre risco de data de entrega

   - Verificar se alguma task pode ir para próximo sprint

4. Comunicação: "Sprint em risco por redução de 25% da capacidade..."

Exemplo 2: Balanceamento

const sprint = {

  devs: [

    { id: 'd1', nome: 'Ana', horas_disponiveis: 40 },

    { id: 'd2', nome: 'João', horas_disponiveis: 40 },

    { id: 'd3', nome: 'Maria', horas_disponiveis: 40 },

  ],

  tasks: [

    { titulo: 'Auth', pontos: 8, responsavel: 'd1' },

    { titulo: 'Dashboard', pontos: 5, responsavel: 'd1' },

    { titulo: 'API', pontos: 3, responsavel: 'd1' },  // Ana: 16 pontos!

    { titulo: 'Testes', pontos: 5, responsavel: 'd2' }, // João: 5 pontos

    { titulo: 'Deploy', pontos: 3, responsavel: 'd3' }, // Maria: 3 pontos

  ]

}



balancearCarga(sprint)

// → redistribuicoes: [{task: 'API', de: 'Ana', para: 'Maria', pontos: 3}]

// Ana: 13, João: 5, Maria: 6 → mais balanceado

Padrões e Armadilhas

Padrões

Padrão 1: Threshold 120% configurável

const THRESHOLD_SOBRECARGA = process.env.THRESHOLD_CARGA || 1.2

if (pct_carga > THRESHOLD_SOBRECARGA) { ... }

Padrão 2: What-if antes do sprint começar O melhor momento para análise what-if é no sprint planning, não quando o problema acontece.

Armadilhas

⚠️ Armadilha 1: Redistribuir tasks especializadas

// João é o único que conhece o sistema de pagamentos

// Redistribuir "Integrar gateway de pagamento" para Ana pode não funcionar

// Adicionar campo 'especialidade' nas tasks

⚠️ Armadilha 2: LLM não conhece o contexto técnico

What-if: "feature_maior" 

LLM sugere: "remova feature X e Y"

Realidade: X é bloqueante para Y e Z, que são committed para o cliente

Sempre revisar com o tech lead antes de aplicar sugestão de LLM.

⚗ Laboratório prático — mozak.tech
Se não for realizar o laboratório, pule para o próximo capítulo.

Ponte para o Lab

TODO 1 — analisarWhatIf(cenario, sprint, client): Monte o contexto do sprint e passe para o LLM com estrutura clara de output (impacto, descartes, ações, comunicação).

TODO 2 — balancearCarga(sprint): Calcule média de pontos, identifique devs com >120%, mova tasks pequenas para devs com menor carga.

Agora você está pronto para o lab.