Artigo 3 de 12
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.

10 de julho de 2026 · Prompt & Deploy

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.md como base, e regras escopadas por fileMatch para camadas específicas (ex.: a API).
  • Design como artefato vivo: o design.md da spec carrega diagramas mermaid e modelo de dados que ficam sincronizados com o código.
  • Hooks PreToolUse com 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. fileMatch e inclusion: auto existem 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

CampoTipoNota
iduuidPK
product_iduuidFK products
user_iduuidFK users
ratingint1..5
bodytext<= 2000 chars
statusenumpublished | 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:

TriggerBloqueia com exit 2?Uso típico para arquiteto
PreToolUseSimBarrar escrita/execução que viola regra
PreTaskExecSimImpedir tarefa que fere pré-condição
UserPromptSubmitSimRejeitar pedidos fora de escopo/política
PostFileSaveNãoLint, formatar, sincronizar testes
PostToolUseNãoAuditoria, notificação
SessionStartNã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 PreToolUse que 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:

  1. Regra de camada — crie .kiro/steering/api.md com inclusion: fileMatch e fileMatchPattern: "app/api/**/*", ancorando o contrato com #[[file:api/openapi.yaml]].
  2. Padrão por intenção — crie .kiro/steering/api-design.md com inclusion: auto, name: api-design e uma description que descreva “criar ou modificar endpoints”.
  3. 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.
  4. Portão duro — adicione .kiro/hooks/enforce-endpoint-auth.json com trigger PreToolUse e o script que sai com 2.

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: always infla cada requisição, aumenta consumo de créditos e — pior — dilui a atenção do modelo. Se tudo é prioridade, nada é. Use always só para o núcleo inegociável (tech.md, structure.md) e escope o resto com fileMatch ou auto.
  • Regex de matcher frouxa. Um matcher que 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 agent para bloquear. Ela não bloqueia. Governança dura = ação command + trigger Pre* + 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.md para a base; fileMatch para camadas; auto para intenção; #[[file:]] para ancorar contratos vivos.
  • O design.md da 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/UserPromptSubmit com 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; fileMatch e auto existem para você impor sem afogar.

Referências


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.