Escrevendo Regras
Regras são funções TypeScript que verificam seu codebase quanto à conformidade com ADRs. Elas ficam em arquivos .rules.ts complementares ao lado dos arquivos markdown dos ADRs e são executadas quando você roda archgate check.
.archgate/adrs/ ARCH-001-command-structure.md # The decision ARCH-001-command-structure.rules.ts # The automated checksConfiguração básica
Seção intitulada “Configuração básica”Todo arquivo de regras exporta por padrão um objeto simples tipado com satisfies RuleSet. Cada chave no objeto rules se torna um ID de regra, e cada regra tem uma description e uma função async check que recebe um objeto de contexto.
/// <reference path="../rules.d.ts" />
export default { rules: { "my-rule-id": { description: "What this rule checks", async check(ctx) { // Your check logic here }, }, },} satisfies RuleSet;Um único arquivo de regras pode definir múltiplas regras:
/// <reference path="../rules.d.ts" />
export default { rules: { "first-rule": { description: "Checks one thing", async check(ctx) { // ... }, }, "second-rule": { description: "Checks another thing", async check(ctx) { // ... }, }, },} satisfies RuleSet;A API de Contexto
Seção intitulada “A API de Contexto”O objeto ctx passado para toda função check fornece capacidades de leitura de arquivos, busca e relatório. Aqui está uma referência detalhada com exemplos.
ctx.scopedFiles
Seção intitulada “ctx.scopedFiles”Um array de caminhos de arquivo que correspondem ao glob files do frontmatter do ADR. Se o ADR não tiver o campo files, isso inclui todos os arquivos do projeto.
for (const file of ctx.scopedFiles) { const content = await ctx.readFile(file); // Check content...}Use ctx.scopedFiles quando sua regra deve se aplicar apenas aos arquivos que o ADR governa. Por exemplo, uma regra de estrutura de comandos com escopo src/commands/**/*.ts receberá apenas arquivos de comandos.
ctx.changedFiles
Seção intitulada “ctx.changedFiles”Um array de caminhos de arquivo que diferem da branch base, incluindo alterações não commitadas da árvore de trabalho (arquivos staged, não staged e não rastreados que não sejam ignorados). Auto-detectado por padrão, ou preenchido via --staged / --base <ref>. Útil para verificação incremental e regras de dependência entre arquivos.
// Verificação incremental -- validar apenas arquivos alteradosconst filesToCheck = ctx.scopedFiles.filter((f) => ctx.changedFiles.includes(f));
// Dependência entre arquivos -- se o arquivo A mudou, o arquivo B também deve mudarif (ctx.changedFiles.includes("config/database.yml")) { if (!ctx.changedFiles.includes("deploy/manifest.yml")) { ctx.report.violation({ message: "config changed but manifest was not bumped", file: "config/database.yml", }); }}ctx.readFile(path)
Seção intitulada “ctx.readFile(path)”Lê o conteúdo de um arquivo como string. O caminho é relativo à raiz do projeto.
const content = await ctx.readFile("src/cli.ts");ctx.readJSON(path)
Seção intitulada “ctx.readJSON(path)”Lê e faz o parse de um arquivo JSON. Retorna unknown — faça cast para o formato esperado.
const pkg = (await ctx.readJSON("package.json")) as { dependencies?: Record<string, string>;};ctx.grep(file, pattern)
Seção intitulada “ctx.grep(file, pattern)”Busca em um único arquivo com uma expressão regular. Retorna um array de objetos GrepMatch, cada um com as propriedades file, line, column e content.
const matches = await ctx.grep(file, /console\.error\(/);for (const match of matches) { ctx.report.violation({ message: "Use logError() instead of console.error()", file: match.file, line: match.line, });}ctx.grepFiles(pattern, fileGlob)
Seção intitulada “ctx.grepFiles(pattern, fileGlob)”Busca em múltiplos arquivos que correspondem a um padrão glob. Retorna um array plano de objetos GrepMatch de todos os arquivos correspondentes. Arquivos ignorados pelo .gitignore são excluídos por padrão. Defina respectGitignore: false no frontmatter do ADR para incluí-los.
const matches = await ctx.grepFiles(/TODO:/, "src/**/*.ts");for (const match of matches) { ctx.report.warning({ message: "TODO comment found", file: match.file, line: match.line, });}ctx.glob(pattern)
Seção intitulada “ctx.glob(pattern)”Encontra arquivos por padrão glob. Retorna um array de caminhos de arquivo relativos à raiz do projeto. Arquivos ignorados pelo .gitignore são excluídos por padrão. Defina respectGitignore: false no frontmatter do ADR para incluí-los.
const testFiles = await ctx.glob("tests/**/*.test.ts");ctx.ast(path, language, opts?)
Seção intitulada “ctx.ast(path, language, opts?)”Faz o parse de um arquivo-fonte para sua AST nativa da linguagem. Linguagens suportadas: "typescript", "javascript", "python" e "ruby". TypeScript e JavaScript são parseados in-process para um Program ESTree; Python e Ruby invocam como subprocesso o parser da biblioteca padrão do interpretador do sistema. Lança uma exceção em caso de falha de parse ou interpretador ausente — nunca retorna null.
const program = await ctx.ast("src/cli.ts", "typescript");Um terceiro argumento opcional cobre mais duas necessidades:
{ rev: "base" }faz o parse do arquivo na revisão base do git em vez da árvore de trabalho — combine com um parse da árvore de trabalho para detectar se a estrutura executável mudou (veja abaixo).{ comments: true }anexa um arraycommentsestruturado à árvore (typescript/javascript/python), para que regras de governança de comentários trabalhem com tokens de comentário estruturados em vez de regex.
Veja Verificações estruturais com ctx.ast() abaixo para exemplos completos, e a Referência da API de Regras para o formato retornado por linguagem e as opções completas.
ctx.fileAtBase(path)
Seção intitulada “ctx.fileAtBase(path)”Lê o código-fonte de um arquivo na revisão base do git — a base de merge entre a ref --base e HEAD. Retorna null quando nenhuma base é resolvida (sem --base) ou o arquivo não existia na base (um arquivo adicionado), então uma única verificação de null cobre ambos os casos. Para uma comparação estrutural, prefira ctx.ast(path, language, { rev: "base" }).
const before = await ctx.fileAtBase("src/config.ts");if (before !== null && before !== (await ctx.readFile("src/config.ts"))) { // o arquivo mudou desde a base}ctx.report
Seção intitulada “ctx.report”A interface de relatório com três métodos de severidade:
ctx.report.violation(detail)— severidade error (código de saída 1, bloqueia CI)ctx.report.warning(detail)— severidade warning (registrado, mas não bloqueia)ctx.report.info(detail)— informacional (registrado para visibilidade)
Cada método aceita um objeto com:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
message | string | Sim | Qual é a violação |
file | string | Não | Caminho do arquivo com a violação |
line | number | Não | Número da linha da violação |
fix | string | Não | Correção sugerida (exibida ao desenvolvedor) |
ctx.report.violation({ message: "Command file must export a register*Command function", file: "src/commands/check.ts", line: 5, fix: "Add: export function registerCheckCommand(program: Command) { ... }",});ctx.projectRoot
Seção intitulada “ctx.projectRoot”O caminho absoluto para o diretório raiz do projeto. Útil quando você precisa construir caminhos absolutos.
Verificações estruturais com ctx.ast()
Seção intitulada “Verificações estruturais com ctx.ast()”Regex funciona para padrões de superfície, mas falha em perguntas estruturais como “este arquivo contém apenas re-exports?” ou “isto é uma cláusula except: vazia?” — declarações multilinha, comentários e conteúdo de strings derrotam a correspondência baseada em linhas. ctx.ast() faz o parse de um arquivo para uma árvore de sintaxe real, permitindo que sua regra verifique a estrutura diretamente.
A árvore retornada é nativa da linguagem, não unificada entre linguagens:
- TypeScript / JavaScript — um
ProgramESTree parseado in-process pelo meriyah. O TypeScript é transpilado primeiro, então sintaxe exclusivamente de tipos (interface, type aliases,export type { ... } from) é apagada da árvore; um arquivo contendo apenas declarações de nível de tipo parseia para umProgramcombodyvazio. - Python — a árvore do módulo
astda biblioteca padrão serializada em JSON. Cada nó é um objeto com um campo_typemais os campos do próprio nó e as posiçõeslineno/col_offset. - Ruby — a saída de
Ripper.sexpdoRipperda biblioteca padrão: arrays aninhados como["program", [["command", ...]]]com pares de posição[linha, coluna].
ctx.ast() lança uma exceção em caso de falha de parse ou de interpretador ausente — nunca retorna null. A exceção fica isolada à regra que falhou e aparece como um erro de execução de regra (código de saída 2), então um ambiente quebrado se manifesta como uma falha visível em vez de um falso sucesso.
TypeScript: sem barrel files
Seção intitulada “TypeScript: sem barrel files”Um barrel file é um index.ts cujas declarações de nível superior são todas re-exports. Com o Program ESTree, essa pergunta se torna uma verificação direta sobre os tipos das declarações, em vez de uma heurística de correspondência de linhas:
/// <reference path="../rules.d.ts" />
export default { rules: { "no-barrel-files": { description: "index.ts files must not be pure re-export barrels", async check(ctx) { const indexFiles = ctx.scopedFiles.filter((f) => f.endsWith("/index.ts") );
const checks = indexFiles.map(async (file) => { const program = await ctx.ast(file, "typescript");
const isBarrel = program.body.length > 0 && program.body.every( (node) => node.type === "ExportAllDeclaration" || (node.type === "ExportNamedDeclaration" && node.source !== null) );
if (isBarrel) { ctx.report.violation({ message: `Barrel file detected: ${file} contains only re-exports`, file, fix: "Delete the barrel and import directly from the source modules", }); } });
await Promise.all(checks); }, }, },} satisfies RuleSet;Como o TypeScript é transpilado antes do parse, re-exports export type { ... } from são apagados — um barrel contendo apenas re-exports de tipos parseia para um body vazio, que a guarda body.length > 0 ignora. Se você também precisa sinalizar barrels exclusivamente de tipos, combine a verificação de AST com uma verificação textual. A mesma transpilação também desloca posições: os números de linha em loc referem-se ao texto transpilado, não ao seu arquivo .ts original, então uma regra "typescript" precisa relocalizar a construção no código-fonte original (por exemplo com ctx.readFile() e indexOf) antes de reportar um line — ou omitir line.
JavaScript: sem require() em módulos ES
Seção intitulada “JavaScript: sem require() em módulos ES”Percorra a árvore ESTree em busca de nós CallExpression cujo callee é o identificador require. Um percurso recursivo sobre valores de objetos e arrays cobre todos os tipos de nó sem enumerá-los:
/// <reference path="../rules.d.ts" />
function findRequireCalls(node: unknown, lines: number[]): void { if (Array.isArray(node)) { for (const item of node) findRequireCalls(item, lines); return; } if (node === null || typeof node !== "object") return; const n = node as EsTreeNode; const callee = n.callee as EsTreeNode | undefined; if ( n.type === "CallExpression" && callee?.type === "Identifier" && callee.name === "require" ) { if (n.loc) lines.push(n.loc.start.line); } for (const value of Object.values(n)) findRequireCalls(value, lines);}
export default { rules: { "no-require-in-esm": { description: ".mjs files must not call CommonJS require()", async check(ctx) { const files = await ctx.glob("src/**/*.mjs");
const checks = files.map(async (file) => { const program = await ctx.ast(file, "javascript"); const lines: number[] = []; findRequireCalls(program, lines); for (const line of lines) { ctx.report.violation({ message: "CommonJS require() call in an ES module", file, line, fix: "Use a static import or await import() instead", }); } });
await Promise.all(checks); }, }, },} satisfies RuleSet;O reporte via loc.start.line aqui é válido apenas porque "javascript" parseia o código-fonte original sem transpilação — uma regra "typescript" precisa localizar a linha no código-fonte original, já que seu loc refere-se à saída transpilada.
Python: sem cláusulas except vazias
Seção intitulada “Python: sem cláusulas except vazias”No módulo ast do Python, uma cláusula except: é um nó ExceptHandler cujo campo type guarda a expressão da exceção capturada — um except: vazio tem "type": null. Percorra a árvore JSON em busca desse formato:
/// <reference path="../rules.d.ts" />
function findBareExcepts(node: unknown, lines: number[]): void { if (Array.isArray(node)) { for (const item of node) findBareExcepts(item, lines); return; } if (node === null || typeof node !== "object") return; const n = node as PythonAstNode; if (n._type === "ExceptHandler" && n.type === null) { if (n.lineno !== undefined) lines.push(n.lineno); } for (const value of Object.values(n)) findBareExcepts(value, lines);}
export default { rules: { "no-bare-except": { description: "Python code must not use bare except: clauses", async check(ctx) { const files = await ctx.glob("**/*.py");
const checks = files.map(async (file) => { const tree = await ctx.ast(file, "python"); const lines: number[] = []; findBareExcepts(tree, lines); for (const line of lines) { ctx.report.violation({ message: "Bare except: catches every exception, including SystemExit", file, line, fix: "Catch a specific exception class, e.g. except ValueError:", }); } });
await Promise.all(checks); }, }, },} satisfies RuleSet;Ruby: sem chamadas puts
Seção intitulada “Ruby: sem chamadas puts”O Ripper.sexp representa puts "hello" como ["command", ["@ident", "puts", [1, 0]], [...args]] — o par [linha, coluna] fica dentro do token @ident. Percorra os arrays aninhados em busca desse formato:
/// <reference path="../rules.d.ts" />
function findPutsCalls(node: unknown, lines: number[]): void { if (!Array.isArray(node)) return; const [kind, first] = node; if ( kind === "command" && Array.isArray(first) && first[0] === "@ident" && first[1] === "puts" ) { const [line] = first[2] as [number, number]; lines.push(line); } for (const child of node) findPutsCalls(child, lines);}
export default { rules: { "no-puts": { description: "Ruby code must use the application logger, not puts", async check(ctx) { const files = await ctx.glob("app/**/*.rb");
const checks = files.map(async (file) => { const sexp = await ctx.ast(file, "ruby"); const lines: number[] = []; findPutsCalls(sexp, lines); for (const line of lines) { ctx.report.violation({ message: "puts writes to stdout directly", file, line, fix: "Replace puts with logger.info", }); } });
await Promise.all(checks); }, }, },} satisfies RuleSet;O Ripper usa um formato de nó diferente para a forma com parênteses — puts("hello") aparece sob um par method_add_arg / fcall, em vez de command — então uma regra de produção casaria o token fcall da mesma maneira.
Comparando com a revisão base
Seção intitulada “Comparando com a revisão base”Algumas regras não se importam com o estado atual de um arquivo, mas com o que mudou. Um caso comum: dispensar uma exigência (um bump de versão, uma entrada no changelog) quando uma alteração é apenas de documentação — a estrutura executável permanece intocada e só comentários ou formatação se moveram. ctx.ast(path, language, { rev: "base" }) faz o parse do arquivo na revisão base do git, para que você possa compará-lo com o parse da árvore de trabalho. Comentários estão ausentes dos formatos ESTree e ast do Python — mas as posições dos nós não estão: os campos loc / lineno se deslocam quando um comentário ou linha em branco move as linhas abaixo dele, então uma serialização crua difere mesmo em uma edição apenas de documentação. Compare uma projeção sem posições das duas árvores. (Docstrings do Python são nós de string comuns na árvore ast, então uma docstring editada é uma mudança real da árvore — remova-as também se sua regra deve tratar edições de documentação como neutras.)
export default { rules: { "bump-required-on-behavior-change": { description: "A .py change that alters behavior must bump the version", async check(ctx) { // Remove metadados de posição para comparar só a estrutura executável: // um comentário ou linha em branco desloca lineno/col_offset sem mudar o // comportamento. (Remova docstrings aqui também se edições de doc forem neutras.) const POS = ["lineno", "col_offset", "end_lineno", "end_col_offset"]; const structure = (node: unknown): unknown => { if (Array.isArray(node)) return node.map(structure); if (node && typeof node === "object") { return Object.fromEntries( Object.entries(node) .filter(([k]) => !POS.includes(k)) .map(([k, v]) => [k, structure(v)]) ); } return node; };
const changed = ctx.changedFiles.filter((f) => f.endsWith(".py")); for (const file of changed) { // Sem contraparte na base (ex.: um arquivo adicionado) -- pular. if ((await ctx.fileAtBase(file)) === null) continue;
const before = await ctx.ast(file, "python", { rev: "base" }); const after = await ctx.ast(file, "python"); if ( JSON.stringify(structure(before)) !== JSON.stringify(structure(after)) ) { ctx.report.violation({ message: `${file} changed behavior -- bump the version`, file, }); } } }, }, },} satisfies RuleSet;O archgate check resolve uma base automaticamente (ou aceita um --base <ref> explícito), e ctx.changedFiles é preenchido a partir dela — incluindo trabalho não committado. A guarda por arquivo é o que torna a regra segura: quando nenhuma base é resolvida, ou para um arquivo ausente na base, ctx.fileAtBase() retorna null e o laço pula esse arquivo. Como o parse da base passa por ctx.ast() e seu acesso ao git é embutido no Archgate, a regra nunca executa git ou um interpretador por conta própria — o que o sandbox de regras bloquearia de qualquer forma.
Governando comentários
Seção intitulada “Governando comentários”ctx.ast(path, language, { comments: true }) anexa um array comments à árvore, para que regras de política de comentários trabalhem com tokens estruturados (type, value, loc) em vez de regex linha a linha. As posições dos comentários são precisas em relação ao código-fonte original mesmo para TypeScript. Suportado para typescript, javascript e python.
const tree = await ctx.ast(file, "typescript", { comments: true });for (const comment of tree.comments ?? []) { if (comment.value.split("\n").length > 10) { ctx.report.warning({ message: "Comment block too long -- move rationale to an ADR", file, line: comment.loc.start.line, }); }}Níveis de severidade
Seção intitulada “Níveis de severidade”Cada regra pode definir uma severidade padrão em sua configuração. A severidade determina como as violações são tratadas:
| Severidade | Código de saída | Comportamento |
|---|---|---|
error | 1 | Bloqueia CI, deve ser corrigido |
warning | 0 | Registrado, mas não bloqueia |
info | 0 | Informacional, registrado para visibilidade |
Defina a severidade na definição da regra:
export default { rules: { "my-rule": { description: "...", severity: "warning", async check(ctx) { // Violations from this rule are warnings, not errors ctx.report.violation({ message: "..." }); }, }, },} satisfies RuleSet;Se severity for omitido, o padrão é error.
Você também pode reportar com diferentes severidades dentro da mesma regra usando ctx.report.violation(), ctx.report.warning() e ctx.report.info() diretamente.
Timeout de regras
Seção intitulada “Timeout de regras”Cada regra tem um timeout de execução de 30 segundos. Se uma regra exceder esse limite, ela é tratada como erro. Isso impede que verificações descontroladas bloqueiem o pipeline.
Mantenha as regras rápidas:
- Usando
ctx.grepFiles()ao invés de ler cada arquivo manualmente - Usando
Promise.all()para verificar arquivos em paralelo - Delimitando regras com o campo
filesdo frontmatter para limitar o número de arquivos processados
O campo fix
Seção intitulada “O campo fix”O campo fix é uma string opcional exibida ao desenvolvedor junto com a mensagem de violação. Ele descreve qual ação tomar para resolver o problema. Correções não são aplicadas automaticamente — são orientações.
ctx.report.violation({ message: `Unapproved dependency: "chalk"`, file: "package.json", fix: "Use styleText() from node:util instead of chalk",});Quando exibido, o fix aparece abaixo da mensagem de violação:
ARCH-006/no-unapproved-deps package.json Unapproved dependency: "chalk" Fix: Use styleText() from node:util instead of chalkDicas para escrever regras
Seção intitulada “Dicas para escrever regras”-
Use
Promise.all()para verificações de arquivo em paralelo. Ao verificar múltiplos arquivos independentes, processe-os em paralelo ao invés de sequencialmente.// Good: parallelconst checks = files.map(async (file) => {const content = await ctx.readFile(file);// ...});await Promise.all(checks);// Avoid: sequentialfor (const file of files) {const content = await ctx.readFile(file);// ...} -
Use
ctx.changedFilespara verificação incremental.ctx.changedFilesé auto-preenchido com o diff da branch mais as alterações não commitadas da árvore de trabalho (ou arquivos staged com--staged). Filtrectx.scopedFilescom ele para verificar apenas o que mudou, ou use-o diretamente para regras de dependência entre arquivos. -
Mantenha as regras focadas em uma única preocupação. Uma regra que verifica tanto convenções de nomenclatura quanto padrões de import deve ser dividida em duas regras com IDs separados.
-
Use
ctx.grepFiles()ao invés de iteração manual. Ao buscar um padrão em muitos arquivos,ctx.grepFiles()é mais eficiente do que ler cada arquivo e executar uma regex. -
Forneça mensagens de
fixacionáveis. Um fix como “Não faça isso” não é útil. Diga ao desenvolvedor exatamente o que fazer. -
Filtre arquivos não-aplicáveis cedo. Se sua regra se aplica apenas a certos arquivos dentro do escopo, filtre
ctx.scopedFilesantes de processar:const commandFiles = ctx.scopedFiles.filter((f) => !f.endsWith("index.ts")); -
Trate arquivos ausentes com elegância. Se sua regra lê um arquivo específico como
package.json, envolva a leitura em um try/catch e retorne antecipadamente se o arquivo não existir.
Diretivas de opt-out
Seção intitulada “Diretivas de opt-out”Existem duas formas de lidar com exceções no Archgate: supressão no nível do engine (funciona com qualquer regra automaticamente) e diretivas customizadas no nível da regra (implementadas pelo autor da regra para opt-outs específicos do domínio).
Supressão no nível do engine
Seção intitulada “Supressão no nível do engine”O Archgate suporta comentários inline archgate-ignore que suprimem violações sem modificar a regra em si. O engine analisa esses comentários e filtra as violações correspondentes antes de reportar.
Supressão da próxima linha: suprime a violação na linha imediatamente seguinte:
// archgate-ignore ARCH-006/no-unapproved-deps dep legada, migração planejada para Q3import chalk from "chalk";Supressão no nível do arquivo: suprime todas as violações correspondentes em qualquer lugar do arquivo:
// archgate-ignore-file ARCH-005/test-mirrors-src arquivo gerado, sem teste manualMúltiplas regras: empilhe comentários para suprimir mais de uma regra na mesma linha:
// archgate-ignore ARCH-006/no-unapproved-deps dep legada// archgate-ignore ARCH-003/use-style-text lib de terceiros lida com coresimport chalk from "chalk";Comentários de supressão consecutivos todos visam a primeira linha que não é uma supressão após o bloco.
O formato é ADR-ID/rule-id seguido de um motivo. O motivo é obrigatório: uma supressão sem motivo é ignorada e produz um aviso:
[suppression] Suppression for ARCH-006/no-unapproved-deps is missing a reason src/foo.ts:1Ambos os estilos de comentário // e # são suportados, então as supressões funcionam em TypeScript, JavaScript, YAML, Python, shell scripts e outros tipos de arquivo que suas regras podem analisar.
Diretivas customizadas no nível da regra
Seção intitulada “Diretivas customizadas no nível da regra”Para opt-outs específicos do domínio, autores de regras podem implementar suas próprias diretivas baseadas em comentários dentro da função check. Esse padrão dá à regra controle total sobre a sintaxe, posicionamento e validação da diretiva.
// No seu arquivo .rules.ts:async check(ctx) { const files = await ctx.glob("src/components/**/*Connected.tsx");
for (const file of files) { const content = await ctx.readFile(file);
// Suportar diretiva de opt-out no topo do arquivo if (/^\/\/\s*@no-presentational:/u.test(content.trimStart())) continue;
// ... lógica da regra que pode reportar uma violação ... ctx.report.violation({ message: "Componente presentational ausente", file, fix: 'Adicione "// @no-presentational: <motivo>" no topo do arquivo para fazer opt-out', }); }}O desenvolvedor faz opt-out adicionando a diretiva ao seu arquivo:
// @no-presentational: este componente apenas redireciona, sem UI para renderizarimport { useNavigate } from "react-router";Quando usar qual
Seção intitulada “Quando usar qual”| Abordagem | Melhor para | Quem controla |
|---|---|---|
archgate-ignore | Exceções ad-hoc para qualquer regra | Desenvolvedor usando a regra |
| Diretiva customizada | Opt-outs específicos do domínio com motivos estruturados | Autor da regra |
Use archgate-ignore quando um desenvolvedor precisa suprimir uma violação pontual. Use diretivas customizadas quando o opt-out é um conceito de primeira classe no domínio da sua regra. Por exemplo, marcar um componente como intencionalmente sem par, ou um arquivo como auto-gerado.
Próximos passos
Seção intitulada “Próximos passos”- Padrões Comuns de Regras: padrões prontos para copiar e colar, organizados por categoria: gerenciamento de dependências, restrições de import, estrutura de arquivos, qualidade de código, esquema de banco de dados e limites de arquitetura.
- Referência da API de Regras: referência completa de todos os tipos e funções da API de regras.
- Integração com CI: integre
archgate checkao seu pipeline para aplicar regras em cada PR.