The Broad Way é o kinho.dev: meu site pessoal e meu blog de engenharia, em português e inglês. O nome vem de uma frase de Miyamoto Musashi que o site imprime no console do navegador: "When you know the way broadly you see it in all things."
Escrevo sobre agentes de LLM, arquitetura e trabalho em produção, em três seções: logs, rants e nonsense. O site também reúne carreira e contato (/about), projetos, consultoria, as ferramentas que uso no dia a dia (/loadout) e meu currículo como ficha de personagem de D&D 5e (/sheet).
Comecei em 20 de novembro de 2025, a partir do create-next-app, e o site segue no ar em kinho.dev.
>Arquitetura (HLD)
Roda inteiro na Vercel, em Next.js 16 com App Router, React 19 e Tailwind v4. Cada pull request no GitHub ganha um preview, e o merge na main publica em produção.
Uma requisição sem idioma passa pelo middleware, que lê o Accept-Language e redireciona para /pt-BR ou /en-US. Dali ela cai num de dois route groups com layouts raiz separados: (site), com a navegação de terminal, ou (sheet), a ficha de D&D com HTML, fontes e estilos próprios.
Não há banco: cada post é um módulo TypeScript por idioma, e um único catálogo esconde rascunhos e alimenta páginas, RSS e sitemap. O único caminho com estado externo é o contato: o navegador posta JSON em /api/contact, que valida, descarta bots, limita por IP e entrega em paralelo para uma issue no GitHub e, se configurado, um e-mail pelo Resend.
Passe o mouse ou foque uma peça: ela acende com as ligações.
10 peças9 ligações4 áreas
Componentes
Middleware de idioma :: Redireciona URLs sem idioma para /en-US ou /pt-BR pelo Accept-Language, com en-US como padrão.
Catálogo de posts :: Um módulo TypeScript por post e idioma; uma função única tira os rascunhos das páginas, do RSS e do sitemap.
Feeds :: RSS em /en-US/rss.xml, /pt-BR/rss.xml e /rss.xml, e sitemap gerado a partir do mesmo catálogo.
Ficha de personagem (/sheet) :: Route group com layout raiz próprio, quatro fontes próprias e um d20 desenhado em canvas que respeita reduced motion.
API de contato :: Valida os campos, descarta bots por honeypot, limita por IP e entrega por issue num repositório privado e por e-mail opcional.
Testes :: Testes unitários com node:test para catálogo, rascunhos, feeds, contato e ficha, mais suítes de navegador com Playwright.
>Trade-offs
A ficha de D&D vive num route group próprio, com HTML e body independentes.
Alternativa descartada: Estender a página com a moldura do site ou esconder essa moldura com CSS.
Por quê: Route groups separados dão HTML e body independentes e evitam renderizar uma navegação escondida e inacessível.
Rascunho é uma flag no post, e um único catálogo decide o que aparece.
Alternativa descartada: Filtrar rascunhos em cada lugar: home, listas, páginas de post, RSS e sitemap.
Por quê: Um filtro só cobre todos esses lugares, e os testes verificam que título, URL e texto de rascunho não aparecem no RSS nem no sitemap. A flag de prévia não é controle de acesso, e o README diz isso.
Slug explícito quando um título é corrigido.
Alternativa descartada: Sempre gerar o slug a partir do título.
Por quê: Corrigir um título mudaria a URL publicada. Com o slug fixado no slug original, os links que já circulam continuam funcionando.
Contato entregue por issue no GitHub como canal principal, com e-mail opcional. Conta como entregue se um canal aceitar.
Alternativa descartada: Depender só de um envio de e-mail.
Por quê: Cada mensagem vira uma issue e chega como notificação do GitHub. Se nenhum canal aceitar, a API devolve 502 e a página mostra o e-mail e o LinkedIn, então a mensagem não some em silêncio.
>Destaques
>62 posts publicados em cada idioma (27 logs, 29 rants, 6 nonsense), de 20/11/2025 a 25/09/2026, mais 5 rascunhos fora do ar.
>O RSS de /en-US tinha 62 itens em 22/10/2026.
>Testes unitários com node:test, mais 4 arquivos de testes de navegador com Playwright.
>Contato com honeypot, limite de 5 mensagens por IP a cada 10 minutos e mensagens de até 5.000 caracteres.
>O d20 da ficha são 385 linhas de TypeScript com canvas 2D: icosaedro, quatérnios e um único loop de animação para todos os dados.
>Curiosidades
*Abra o console do navegador no kinho.dev: aparece uma katana em ASCII e a frase de Musashi que deu nome ao site.
*No d20 da ficha, as faces são numeradas para que faces opostas somem 21, como num dado de verdade.