Kiro & AWS
Como arquitetos de software usam o Kiro para impor decisões arquiteturais
Steering como architecture-as-code, design vivo, hooks que bloqueiam e rastreabilidade real — como transformar decisões que vivem na cabeça de poucos em regras que o agente respeita.
TL;DR
- Decisão arquitetural que só existe no Confluence ou na cabeça do tech lead não sobrevive ao primeiro sprint apertado. Com o Kiro, ela vira arquivo versionado que o agente lê a cada geração de código.
- Steering (
.kiro/steering/*.md) é architecture-as-code:product.md,tech.md,structure.mdcomo base, e regras escopadas porfileMatchpara camadas específicas (ex.: a API). - Design como artefato vivo: o
design.mdda spec carrega diagramas mermaid e modelo de dados que ficam sincronizados com o código. - Hooks
PreToolUsecom exit code 2 bloqueiam ações que violam a arquitetura — antes de acontecerem. - Trade-off honesto: steering demais polui o contexto e degrada as respostas.
fileMatcheinclusion: autoexistem justamente para escopar.
O problema: a arquitetura que ninguém lê
Todo time de médio porte tem aquele documento. O ADR que decidiu que autenticação passa por um middleware central. A página de wiki que diz “toda chamada externa tem timeout e retry”. O diagrama de camadas que o arquiteto desenhou no onboarding e ninguém abriu de novo.
Essas decisões vivem em dois lugares igualmente frágeis: na cabeça de duas ou três pessoas e em documentos mortos que ficam desatualizados no dia seguinte ao merge. Quando um dev novo — ou pior, um agente de IA gerando código — precisa criar um endpoint, ninguém consulta o ADR. O resultado é a erosão silenciosa: cada PR afasta um pouco o código da arquitetura pretendida, e seis meses depois você tem um sistema que ninguém projetou.
O Kiro não resolve isso porque é “mais inteligente”. Resolve porque move a decisão arquitetural para dentro do repositório, num formato que o agente lê antes de escrever qualquer linha. A arquitetura deixa de ser documentação e vira restrição executável.
A arquitetura que não está no repositório não existe para quem gera o código — seja humano júnior ou agente de IA.
Steering como architecture-as-code
Steering são arquivos Markdown em .kiro/steering/ (workspace) ou ~/.kiro/steering/ (global) que o Kiro injeta no contexto do agente. Cada arquivo tem um front matter YAML que deve ser a primeira coisa do arquivo, sem linha em branco antes. Três arquivos fundacionais formam a espinha dorsal: product.md, tech.md e structure.md.
O tech.md codifica as decisões de stack e as convenções inegociáveis:
---
inclusion: always
---
# Stack e convenções
- Backend: Node.js 20 + TypeScript strict. Sem `any`.
- Framework HTTP: Fastify. Validação com zod.
- Banco: PostgreSQL via Prisma. Migrations versionadas.
- Testes: Vitest. Cobertura mínima 80% em `src/domain`.
- Erros: nunca engolir exceção; sempre logar com correlação (requestId).
Com inclusion: always, essas regras entram em todo contexto. Quando você pede ao agente “crie o serviço de pedidos”, ele já sabe que não pode usar any, que valida com zod e que erros precisam de requestId. Isso é architecture-as-code: a decisão de “usar Prisma, não query bruta” não é mais uma conversa de corredor, é uma linha que o agente obedece.
O structure.md faz o mesmo para a organização de pastas e as fronteiras entre camadas; o product.md carrega o contexto de negócio e as personas. Juntos, os três dão ao agente o mesmo modelo mental que o arquiteto tem — sem precisar repetir em cada prompt.
Escopando regras por camada com fileMatch
inclusion: always é poderoso, mas caro: tudo entra sempre no contexto. Para regras que só valem numa camada específica, use inclusion: fileMatch. O steering só é carregado quando o agente toca arquivos que casam com o padrão.
Para governar a camada de API, por exemplo:
---
inclusion: fileMatch
fileMatchPattern: "app/api/**/*"
---
# Convenções da camada de API
- Todo endpoint novo declara o contrato ANTES da implementação. Contrato vivo:
#[[file:api/openapi.yaml]]
- Autenticação é obrigatória por padrão. Rota pública exige comentário
`// PUBLIC:` justificando e aprovação em ADR.
- Toda resposta de erro segue RFC 7807 (application/problem+json).
- Rate limiting no gateway, nunca no handler.
- Nenhum handler acessa o banco direto: sempre via repository do domínio.
Repare no #[[file:api/openapi.yaml]]. Essa sintaxe ancora o steering no contrato vivo — o Kiro injeta o conteúdo do openapi.yaml no contexto sempre que essa regra é carregada. O agente não trabalha com uma descrição do contrato: ele lê o contrato de verdade. Se o spec OpenAPI mudar, o steering acompanha automaticamente, sem edição manual.
O fileMatchPattern aceita um padrão único ou uma lista:
---
inclusion: fileMatch
fileMatchPattern: ["**/*.ts", "**/*.tsx"]
---
Steering auto para padrões acionados por intenção
Existe um quarto modo além de always, fileMatch e manual: o inclusion: auto, que carrega o steering com base em name e description — o agente decide incluí-lo quando a tarefa casa com a descrição.
---
inclusion: auto
name: api-design
description: Padrões de design de API REST. Use ao criar ou modificar endpoints.
---
# Padrões de design de API REST
- Recursos no plural, kebab-case: /purchase-orders, não /purchaseOrder.
- Versionamento por path: /v1/... . Nunca quebrar contrato dentro de uma versão.
- Paginação por cursor (opaco), nunca por offset em coleções grandes.
- 201 devolve Location; 200 nunca cria recurso.
- Idempotência: POST que cria recurso aceita header Idempotency-Key.
A diferença prática: fileMatch liga por caminho de arquivo tocado; auto liga por intenção da tarefa. “Crie um endpoint de faturas” aciona o api-design mesmo antes de qualquer arquivo existir, porque a descrição casa com a intenção. É o mecanismo ideal para regras transversais que não se prendem a um diretório.
Steering é a diferença entre “espero que o dev tenha lido o ADR” e “o agente não consegue gerar código que ignore o ADR”.
Design como artefato vivo
Steering governa o como. Mas decisões arquiteturais também precisam do o quê e do porquê — e é aí que entram as specs. Cada spec vive em .kiro/specs/<nome>/ e gera três arquivos: requirements.md (user stories em notação EARS), design.md (arquitetura) e tasks.md (plano de implementação).
Para o arquiteto, o design.md é o artefato central. Ele não é um documento que você escreve uma vez e arquiva — o Kiro o mantém sincronizado com o código conforme o sistema evolui. Um diagrama de sequência dentro do design.md deixa de ser um PNG desatualizado no Confluence e passa a ser a fonte que o agente consulta para implementar:
## Arquitetura
```mermaid
sequenceDiagram
participant U as Cliente
participant API as Review API
participant DB as PostgreSQL
U->>API: POST /products/{id}/reviews
API->>DB: verifica compra (orders)
DB-->>API: compra confirmada
API->>DB: INSERT review (status=published)
API-->>U: 201 Created
Modelo de dados
| Campo | Tipo | Nota |
|---|---|---|
| id | uuid | PK |
| product_id | uuid | FK products |
| user_id | uuid | FK users |
| rating | int | 1..5 |
| body | text | <= 2000 chars |
| status | enum | published | pending | rejected |
Esse trecho é decisão arquitetural pura: a ordem das operações (verificar compra **antes** de inserir), a fronteira entre API e banco, o modelo de dados com suas constraints. Quando o agente implementa a tarefa, ele lê esse design e gera código coerente com ele. Quando um requisito muda, você atualiza o design e as tasks derivadas acompanham.
O fluxo de três fases — **Requirements → Design → Tasks** — dá ao arquiteto pontos de revisão explícitos. Você aprova o design antes que qualquer código seja escrito. É o oposto do vibe coding: a intenção estruturada mora no repositório, não só na conversa efêmera com o agente.
## Governança com hooks que bloqueiam
Steering e specs *orientam*. Mas orientação não é imposição — um agente pode, em teoria, gerar código que viola a regra. Para governança de verdade, o Kiro tem **hooks**: automações por evento, em `.kiro/hooks/*.json` (workspace) ou `~/.kiro/hooks/` (usuário).
O que torna hooks uma ferramenta de arquiteto é que **alguns triggers bloqueiam**. Os triggers `PreToolUse`, `PreTaskExec` e `UserPromptSubmit` interrompem a execução quando o hook (ação do tipo `command`) sai com **exit code 2** — o STDERR volta ao agente como motivo. Os demais exit codes: `0` é sucesso; qualquer outro vira aviso e a execução segue. Ou seja, só o `2` em um trigger `Pre*` é uma barreira dura.
Um caso clássico para arquitetos: garantir que nenhum endpoint novo seja escrito sem autenticação. O hook intercepta a escrita de arquivo na camada de API e roda um validador:
```json
{
"version": "v1",
"hooks": [
{
"name": "enforce-endpoint-auth",
"description": "Bloqueia gravação de handler de API sem declaração de auth",
"trigger": "PreToolUse",
"matcher": "app/api/.*route\\.ts$",
"action": {
"type": "command",
"command": "./scripts/check-endpoint-auth.sh"
},
"timeout": 20,
"enabled": true
}
]
}
O matcher é uma regex que filtra por nome de tool ou caminho — aqui, só arquivos de rota da API disparam o hook. O trabalho pesado fica no script, que sai com 2 para barrar:
#!/usr/bin/env bash
# check-endpoint-auth.sh
# Recebe o caminho do arquivo prestes a ser escrito. Bloqueia se
# o handler não declarar auth e não tiver a marcação // PUBLIC:.
target_file="$1"
if grep -qE "requireAuth|withAuth|authGuard" "$target_file" 2>/dev/null; then
exit 0 # tem auth declarada — libera
fi
if grep -q "// PUBLIC:" "$target_file" 2>/dev/null; then
exit 0 # rota pública justificada — libera
fi
# Sem auth e sem justificativa → bloqueia (exit 2) e explica no STDERR.
echo "BLOQUEADO: endpoint em '$target_file' não declara autenticação." >&2
echo "Use requireAuth()/withAuth() ou marque a rota com '// PUBLIC:' + ADR." >&2
exit 2
Como o trigger é PreToolUse, o bloqueio acontece antes de o arquivo ser gravado. O agente recebe o STDERR, entende o motivo e — na maioria dos casos — corrige o próprio código adicionando o guard de autenticação. A regra arquitetural “todo endpoint autentica por padrão” deixa de depender de code review humano e vira um portão que o próprio pipeline do agente respeita.
Compare os triggers pela capacidade de bloquear:
| Trigger | Bloqueia com exit 2? | Uso típico para arquiteto |
|---|---|---|
PreToolUse | Sim | Barrar escrita/execução que viola regra |
PreTaskExec | Sim | Impedir tarefa que fere pré-condição |
UserPromptSubmit | Sim | Rejeitar pedidos fora de escopo/política |
PostFileSave | Não | Lint, formatar, sincronizar testes |
PostToolUse | Não | Auditoria, notificação |
SessionStart | Não (STDOUT vira contexto) | Injetar estado inicial |
Um detalhe importante: hooks de ação agent (que rodam um prompt em vez de um comando) não usam exit code para bloquear e ignoram timeout. Para governança dura, use sempre ação command em um trigger Pre*.
Um hook
PreToolUseque sai com código 2 é a única forma de o agente literalmente não conseguir violar uma regra arquitetural. O resto é orientação — isto é imposição.
Rastreabilidade e conhecimento institucional
A parte mais subestimada, para quem pensa em arquitetura de longo prazo, é a rastreabilidade. No tasks.md, cada tarefa aponta para o requisito que a originou:
# Plano de implementação
- [ ] 1. Criar migration da tabela reviews (Req 1)
- [ ] 2. Implementar ReviewRepository com verificação de compra (Req 1)
- [ ] 2.1 Testes unitários do repositório
- [ ] 3. Endpoint POST /products/{id}/reviews (Req 1)
- [ ] 3.1 Teste de integração (403 sem compra)
- [ ] 4. Filtro de moderação (Req 1 - WHERE)
Essa cadeia — requirement (EARS) → design → task → código — é ouro para governança. Seis meses depois, quando alguém pergunta “por que esse endpoint devolve 403 se o usuário não comprou?”, a resposta não está na memória de ninguém: está no critério de aceitação IF o usuário não comprou o produto, THEN THE SYSTEM SHALL rejeitar com HTTP 403, ligado à task 3.1, ligado ao commit. O conhecimento institucional para de evaporar quando as pessoas trocam de time.
E porque as specs ficam sincronizadas com o código conforme ele evolui, você não tem o problema clássico de documentação que mente. Ao rodar “Run all Tasks”, o Kiro ainda constrói um grafo de dependências e agrupa tarefas independentes em waves (a Wave 1 são as que não têm dependências) — o que também é uma leitura arquitetural: se duas tarefas caem em waves diferentes, há acoplamento; se caem na mesma, são de fato independentes.
Mão na massa: blindando a camada de API em 4 passos
Junte as peças num fluxo que um arquiteto pode montar numa tarde:
- Regra de camada — crie
.kiro/steering/api.mdcominclusion: fileMatchefileMatchPattern: "app/api/**/*", ancorando o contrato com#[[file:api/openapi.yaml]]. - Padrão por intenção — crie
.kiro/steering/api-design.mdcominclusion: auto,name: api-designe umadescriptionque descreva “criar ou modificar endpoints”. - Design vivo — para a próxima feature de API, gere uma spec e aprove o
design.md(com o diagrama mermaid de sequência) antes de liberar as tasks. - Portão duro — adicione
.kiro/hooks/enforce-endpoint-auth.jsoncom triggerPreToolUsee o script que sai com2.
O efeito combinado: o agente conhece o contrato (steering + #[[file:]]), segue os padrões REST por intenção (auto), implementa conforme o design aprovado (spec) e é fisicamente impedido de gravar um endpoint sem auth (hook). Nenhuma dessas quatro camadas depende de um humano lembrar da regra no momento do PR.
Armadilhas comuns
- Steering demais polui o contexto. Empilhar dez arquivos com
inclusion: alwaysinfla cada requisição, aumenta consumo de créditos e — pior — dilui a atenção do modelo. Se tudo é prioridade, nada é. Usealwayssó para o núcleo inegociável (tech.md,structure.md) e escope o resto comfileMatchouauto. - Regex de
matcherfrouxa. Ummatcherque casa arquivos demais transforma um hook de governança num gargalo que dispara em toda gravação. Seja específico (app/api/.*route\\.ts$, não.*\\.ts$). - Confiar em ação
agentpara bloquear. Ela não bloqueia. Governança dura = açãocommand+ triggerPre*+ exit code 2. - Design que vira PNG morto. Se você exporta o mermaid e edita o diagrama fora da spec, perdeu a sincronização. Mantenha a decisão dentro do
design.md. - Steering global vs. workspace. Regra que só vale num projeto não deve ir para
~/.kiro/steering/(global), senão vaza para todos os seus repositórios.
Principais aprendizados
- Steering é architecture-as-code.
product.md/tech.md/structure.mdpara a base;fileMatchpara camadas;autopara intenção;#[[file:]]para ancorar contratos vivos. - O
design.mdda spec é o artefato arquitetural vivo — diagrama mermaid e modelo de dados que o agente consulta e que fica sincronizado com o código. - Só hooks
PreToolUse/PreTaskExec/UserPromptSubmitcom exit code 2 bloqueiam. É a diferença entre orientar e impor. - A cadeia EARS → design → task → código dá rastreabilidade que preserva o conhecimento institucional além das pessoas.
- Escopar é uma disciplina de arquiteto: steering demais degrada o contexto;
fileMatcheautoexistem para você impor sem afogar.
Referências
- Steering — documentação: https://kiro.dev/docs/steering/
- Specs (design docs) — documentação: https://kiro.dev/docs/specs/
- Hooks — documentação: https://kiro.dev/docs/hooks/
- Hooks: tipos e triggers: https://kiro.dev/docs/hooks/types/
- Padrão AGENTS.md: https://agents.md/
- Introducing Kiro (blog oficial): https://kiro.dev/blog/introducing-kiro/
Este artigo foi publicado originalmente no Medium. Preços, nomes de modelos e detalhes de produto refletem o estado do Kiro em meados de 2026 e podem mudar — confirme em kiro.dev antes de tomar decisões. Este é um conteúdo educacional independente, sem vínculo oficial com a AWS.
Tags: kiro, arquitetura-de-software, aws, architecture-as-code, governanca
Publicado originalmente em prompt.victorbatistax.com. Se você chegou por outra plataforma, esta é a versão canônica.