1. Definir a decisão antes de desenhar a consulta
Um comité fictício de APS quer decidir se pode transferir uma pessoa de suporte para uma migração. O dashboard mostra tickets por responsável, mas ninguém registou se os valores representam hoje ou o fecho do mês anterior. Antes de ajustar filtros, escreve a pergunta: que itens estavam abertos, em que serviços, sob que responsabilidade e em que instante? Regista os estados considerados abertos pelo processo local, sem pressupor que todos os projetos usam os mesmos nomes. Identifica ainda exclusões, como pedidos cancelados ou trabalho de outra equipa. Depois pergunta se a contagem é suficiente para a decisão. Dois tickets podem exigir esforços e competências muito diferentes. O relatório deve apoiar uma conversa sobre capacidade, prazos e cobertura operacional. Não transforma quantidade de itens numa medida automática de produtividade ou de pessoas dispensáveis.
2. Distinguir estado atual, histórico e passagem por um responsável
Considera um item atribuído a Rita no início de setembro, a Luís no fecho e novamente a Rita em outubro. O estado atual responde a uma pergunta diferente do estado no fecho. Uma pesquisa sobre ter passado por Rita também pode ser verdadeira sem que ela fosse responsável no instante pedido. Em WIQL, ASOF permite aplicar condições aos valores históricos; EVER identifica passagem pelo valor pesquisado. Não são substitutos entre si. Desenha a linha temporal do exemplo antes de escrever a expressão. Um filtro sobre ChangedDate no presente também não reconstrói a revisão antiga: pode simplesmente excluir itens que entretanto mudaram. No handover, explica que um ticket alterado depois do fecho continua a poder pertencer à população histórica. A sua alteração posterior não é motivo suficiente para o remover dessa análise.
3. Separar a seleção dos IDs da leitura dos campos
O exportador tem duas responsabilidades. Primeiro, encontra os itens que satisfazem a pergunta. Depois, obtém os campos que vão aparecer no relatório. A API Query By Wiql devolve referências de work items e metadados, incluindo as colunas; listar Title no SELECT não significa receber o título de cada item nessa resposta. A API Work Items List pode obter os campos dos IDs encontrados e aceita asOf em UTC. Para um fecho histórico, mantém o mesmo instante nessa leitura e na seleção. Guarda o asOf devolvido pela consulta e confirma a sua coerência com o pedido. Compara os IDs pedidos com os realmente obtidos. Uma falha parcial não deve transformar-se silenciosamente em redução da carga. O exercício assume que existem revisões acessíveis; um erro de leitura exige investigação e uma limitação explícita no relatório.
4. Fixar o instante e a identidade numa equipa internacional
A expressão 01/10/2026 sem convenção pode ser interpretada de maneiras diferentes. Mesmo uma data ISO sem fuso não identifica necessariamente o mesmo instante em duas máquinas. No exercício, o comité aprova 2026-09-30T23:00:00Z como referência. Conserva esse valor, a data de geração e a identidade executante em campos separados. Uma macro como @Me refere-se a quem executa a consulta, não a quem a guardou. Se Ana e Bruno têm o mesmo acesso mas pesquisam a sua própria atribuição, podem obter listas distintas sem existir defeito. Para um relatório sobre Ana, exprime esse âmbito explicitamente. Para uma vista pessoal, mantém a macro e identifica a finalidade. Não elimines todos os parâmetros por conveniência: torna visíveis os que influenciam o significado da comparação e valida o contexto do cliente usado.
5. Escolher uma forma de consulta que suporte a pergunta
Uma lista de itens e uma lista de relações não representam o mesmo objeto. Para procurar uma Feature ligada a um Bug, distingue critérios de origem, destino e tipo ou direção da ligação. Uma condição plana que selecione Features ou Bugs não prova que estejam relacionados. Por outro lado, uma consulta em árvore com MODE(Recursive) não suporta a combinação com ASOF ou ORDER BY. Se precisas da hierarquia num instante passado, retirar ASOF apenas para obter uma resposta muda o requisito. Define outra estratégia de evidência histórica e valida-a antes de apresentar resultados. O âmbito desta aula não inclui implementar um reconstrutor de relações históricas. Num projeto real, essa limitação deve entrar no planeamento da solução de reporting, com responsável, prova de conceito e critérios de aceitação, em vez de ficar escondida num gráfico aparentemente convincente.
6. Separar leitura, edição e administração do acesso
No exemplo privado desta aula, os utilizadores têm Basic e as permissões sobre os itens já foram confirmadas. Uma pessoa que vê o dashboard pode continuar sem Read na consulta subjacente; o widget não fica acessível apenas porque a página abre. Para permitir editar consultas numa pasta, considera Contribute. Para administrar as permissões da pasta, existe Manage Permissions. O pedido para corrigir um filtro não implica conceder administração do acesso. Analisa as permissões efetivas e eventuais negações antes de aplicar a mudança através do processo aprovado. Na passagem a RUN, documenta quem mantém a definição, quem aprova alterações ao âmbito e quem pode resolver falhas de acesso. Uma exportação estática autorizada pode servir uma reunião, mas deve ser identificada como snapshot, com data e limitações, e não como reparação do widget.
7. Ensaiar o erro com um histórico pequeno e verificável
Executa o modelo Python desta aula sem credenciais. Os dados são inventados e a função escolhe a última revisão fornecida até ao instante indicado. Dois itens satisfazem Active no fecho. Depois, um deles muda de estado e responsável. Observa que reutilizar os mesmos IDs com campos atuais mantém o número de linhas, mas muda a distribuição e o significado do relatório. Há também um item criado depois do fecho e outro cuja mudança ocorre um segundo depois; ambos ajudam a verificar a fronteira temporal. Altera uma revisão e antecipa o resultado antes de executar novamente. O modelo não interpreta WIQL nem aplica permissões Azure. Demonstra coerência temporal num conjunto local completo. Uma integração real ainda precisa de tratar autenticação, limites da API, erros, dados omitidos e comportamento do serviço.
8. Entregar um relatório que outra equipa consiga explicar
Prepara um pequeno contrato do relatório: pergunta, população, estados, exclusões, instante UTC, identidade, definição da consulta, campos lidos e regra para dados em falta. Para um gráfico de consulta no dashboard, confirma que a fonte é plana e está em Shared Queries; testa o âmbito após converter uma árvore. Verifica a visualização com um destinatário representativo, através de acesso autorizado. No comité, distingue factos, limitações e decisão proposta. Se a base histórica estiver errada, corrige-a ou apresenta a limitação e adia a decisão que depende dela. No resumo do handover, regista quem mantém o relatório e como reconhecer um resultado incompleto. A conclusão central é simples: uma contagem credível exige saber que itens foram selecionados e de que momento vêm os valores apresentados, além de compreender o que essa contagem não mede.
# Fictional local revision model. Not a WIQL parser or an Azure permission/API test.
from datetime import datetime
from collections import Counter
def instant(value):
if not value.endswith("Z"):
raise ValueError("This exercise requires an explicit UTC Z timestamp")
return datetime.fromisoformat(value.replace("Z", "+00:00"))
history = {
501: [("2026-09-10T09:00:00Z", "Active", "Rita"),
("2026-10-02T09:00:00Z", "Resolved", "Luis")],
502: [("2026-09-12T09:00:00Z", "Active", "Rita")],
503: [("2026-10-01T09:00:00Z", "Active", "Luis")],
504: [("2026-09-10T09:00:00Z", "New", "Luis"),
("2026-09-30T18:00:01Z", "Active", "Luis")],
}
def snapshot(when):
result = {}
for item_id, revisions in history.items():
eligible = [r for r in revisions if instant(r[0]) <= instant(when)]
if eligible:
revision = max(eligible, key=lambda r: instant(r[0]))
result[item_id] = {"state": revision[1], "owner": revision[2]}
return result
def hydrate(ids, when):
data = snapshot(when)
return {item_id: data[item_id] for item_id in ids} # Missing IDs raise, never silently omitted.
def owners(data):
return Counter(row["owner"] for row in data.values())
cutoff = "2026-09-30T18:00:00Z"
now = "2026-10-05T09:00:00Z"
at_close = snapshot(cutoff)
selected = [i for i, row in at_close.items() if row["state"] == "Active"]
correct = hydrate(selected, cutoff)
mixed = hydrate(selected, now)
assert selected == [501, 502]
assert 503 not in at_close
assert at_close[504]["state"] == "New"
assert snapshot("2026-09-30T18:00:01Z")[504]["state"] == "Active"
assert correct[501]["state"] == "Active"
assert mixed[501]["state"] == "Resolved"
assert correct[501]["owner"] == "Rita"
assert mixed[501]["owner"] == "Luis"
assert len(correct) == len(mixed) == 2
assert set(correct) == set(mixed)
assert owners(correct) == {"Rita": 2}
assert owners(mixed) == {"Rita": 1, "Luis": 1}
assert correct != mixed
assert hydrate(selected, cutoff) == correct
try:
hydrate([503], cutoff)
except KeyError:
missing_rejected = True
else:
missing_rejected = False
assert missing_rejected
try:
instant("2026-09-30T18:00:00")
except ValueError:
ambiguous_rejected = True
else:
ambiguous_rejected = False
assert ambiguous_rejected
print("16 fictional historical-report checks passed")
O modelo seleciona dois tickets no fecho. A leitura dos mesmos IDs em outubro mantém duas linhas, mas apresenta outro estado e outra distribuição de responsáveis.
Armadilhas comuns
Filtrar ChangedDate em vez de reconstruir o estado; misturar IDs históricos com campos atuais; confundir @Me com o autor; assumir que acesso ao dashboard resolve acesso à consulta.
Tópicos relacionados: Rastreabilidade e métricas de fluxo · Permissões e segregação de funções · Planeamento de capacidade · Transição para suporte operacional
Preserva âmbito, identidade e instante entre seleção, leitura e apresentação. Reconcilia linhas em falta e explica as limitações antes de usar o relatório para decidir.
Referência: Work Item Query Language syntax reference · AZ-400 objectives 2026-07-27