Aikido

O que aprendemos com a base de código do Strapi: 20 regras de code review para desenvolvimento escalável

Introdução

Strapi é uma das plataformas CMS headless de código aberto mais populares, mas também é uma enorme base de código com centenas de contribuidores e milhares de pull requests. Manter a alta qualidade de um projeto tão grande não é fácil. Requer regras de revisão de código claras e consistentes para garantir que cada contribuição permaneça confiável, legível e segura.

Neste artigo, compilamos um conjunto de regras de revisão de código baseadas no repositório público do Strapi. Essas regras vêm de trabalho real: problemas, discussões e pull requests que ajudaram o projeto a crescer, mantendo a base de código estável.

Por que manter a qualidade do código em um grande projeto de código aberto é difícil

Manter a qualidade em um grande projeto de código aberto é desafiador devido à vasta escala e diversidade das contribuições. Centenas ou até milhares de desenvolvedores, de voluntários a engenheiros experientes, enviam pull requests, cada um introduzindo novas funcionalidades, correções de bugs ou refatorações. Sem regras claras, a base de código pode rapidamente se tornar inconsistente, frágil ou difícil de navegar.

Alguns dos principais desafios incluem:

  • Colaboradores diversos com diferentes níveis de experiência.
  • Padrões de codificação inconsistentes entre módulos.
  • Bugs ocultos e lógica duplicada surgindo.
  • Riscos de segurança se os processos não forem aplicados.
  • Revisões demoradas para voluntários não familiarizados com a base de código completa.

Para enfrentar esses desafios, projetos bem-sucedidos dependem de processos estruturados: padrões compartilhados, ferramentas automatizadas e diretrizes claras. Essas práticas garantem manutenibilidade, legibilidade e segurança, mesmo à medida que o projeto cresce e atrai mais contribuidores.

Como seguir estas regras melhora a manutenibilidade, segurança e onboarding

Aderir a um conjunto claro de regras de revisão de código tem um impacto direto na saúde do seu projeto:

  • Manutenibilidade: Estruturas de pastas consistentes, convenções de nomenclatura e padrões de codificação facilitam a leitura, navegação e extensão da base de código.
  • Segurança: Validação de entrada, sanitização, verificações de permissão e acesso controlado ao banco de dados reduzem vulnerabilidades e previnem vazamentos acidentais de dados.
  • Onboarding Mais Rápido: Padrões compartilhados, utilitários documentados e exemplos claros ajudam novos contribuidores a entender o projeto rapidamente e a contribuir com confiança.

Ao aplicar essas regras, as equipes podem garantir que a base de código permaneça escalável, confiável e segura, mesmo com o aumento do número de contribuidores.

Conectando o contexto às regras

Antes de analisar as regras, é importante entender que manter a qualidade do código alta em um projeto como o Strapi não se trata apenas de seguir as melhores práticas gerais. É sobre ter padrões e normas claras que ajudam centenas de contribuidores a estarem alinhados. Cada uma das 20 regras abaixo foca em desafios reais que surgem na base de código do Strapi.

Os exemplos fornecidos para cada regra ilustram abordagens não conformes e conformes, oferecendo uma visão clara de como esses princípios se aplicam na prática.

Agora, vamos explorar as regras que tornam a base de código do Strapi escalável, consistente e de alta qualidade, começando pela estrutura do projeto e padrões de configuração.

Regras: Estrutura e consistência do projeto

1. Siga as convenções de pastas estabelecidas da Strapi

Evite espalhar arquivos ou inventar novas estruturas. Mantenha-se fiel ao layout de projeto estabelecido do Strapi para manter a navegação previsível.

Exemplo não em conformidade

1src/
2├── controllers/
3│   └── userController.js
4├── services/
5│   └── userLogic.js
6├── routes/
7│   └── userRoutes.js
8└── utils/
9    └── helper.js

Exemplo em conformidade

1src/
2└── api/
3    └── user/
4        ├── controllers/
5        │   └── user.js
6        ├── services/
7        │   └── user.js
8        ├── routes/
9        │   └── user.js
10        └── content-types/
11            └── user/schema.json

2. Mantenha os arquivos de configuração consistentes

Use as mesmas convenções de estrutura, nomenclatura e formatação em todos os arquivos de configuração para garantir consistência e prevenir erros.

Exemplo não em conformidade

1// config/server.js
2module.exports = {
3    PORT: 1337,
4    host: '0.0.0.0',
5    APP_NAME: 'my-app'
6}
7
8// config/database.js
9export default {
10  connection: {
11    client: 'sqlite',
12    connection: { filename: '.tmp/data.db' }
13  }
14}
15
16// config/plugins.js
17module.exports = ({ env }) => ({
18   upload: { provider: "local" },
19   email: { provider: 'sendgrid' }
20});

Exemplo em conformidade

1// config/server.js
2module.exports = ({ env }) => ({
3  host: env('HOST', '0.0.0.0'),
4  port: env.int('PORT', 1337),
5  app: { keys: env.array('APP_KEYS') },
6});
7
8// config/database.js
9module.exports = ({ env }) => ({
10  connection: {
11    client: 'sqlite',
12    connection: { filename: env('DATABASE_FILENAME', '.tmp/data.db') },
13    useNullAsDefault: true,
14  },
15});
16
17// config/plugins.js
18module.exports = ({ env }) => ({
19  upload: { provider: 'local' },
20  email: { provider: 'sendgrid' },
21});

3. Manter segurança de tipos estrita

Todo código novo ou atualizado deve incluir tipos TypeScript precisos ou definições JSDoc. Evite usar 'any', tipos de retorno ausentes ou inferência de tipo implícita em módulos compartilhados.

Exemplo não em conformidade

1// src/api/user/services/user.ts
2export const createUser = (data) => {
3  return strapi.db.query('api::user.user').create({ data });
4};

Exemplo em conformidade

1// src/api/user/services/user.ts
2import { User } from './types';
3
4export const createUser = async (data: User): Promise<User> => {
5  return await strapi.db.query('api::user.user').create({ data });
6};

4. Nomenclatura consistente para serviços e controladores

Os nomes de controller e service devem corresponder claramente ao seu domínio (ex: user.controller.js com user.service.js).

Exemplo não em conformidade

1src/
2└── api/
3    └── user/
4        ├── controllers/
5        │   └── mainController.js
6        ├── services/
7        │   └── accountService.js
8        ├── routes/
9        │   └── user.js

Exemplo em conformidade

1src/
2└── api/
3    └── user/
4        ├── controllers/
5        │   └── user.js
6        ├── services/
7        │   └── user.js
8        ├── routes/
9        │   └── user.js
10        └── content-types/
11            └── user/schema.json

Regras: Qualidade e manutenibilidade do código

5. Simplifique o fluxo de controle com retornos antecipados

Em vez de aninhamento profundo de if/else, retorne cedo quando as condições falharem.

Exemplo não em conformidade

1// src/api/article/controllers/article.js
2module.exports = {
3  async create(ctx) {
4    const { title, content, author } = ctx.request.body;
5
6    if (title) {
7      if (content) {
8        if (author) {
9          const article = await strapi.db.query('api::article.article').create({
10            data: { title, content, author },
11          });
12          ctx.body = article;
13        } else {
14          ctx.throw(400, 'Missing author');
15        }
16      } else {
17        ctx.throw(400, 'Missing content');
18      }
19    } else {
20      ctx.throw(400, 'Missing title');
21    }
22  },
23};

Exemplo em conformidade

1// src/api/article/controllers/article.js
2module.exports = {
3  async create(ctx) {
4    const { title, content, author } = ctx.request.body;
5
6    if (!title) ctx.throw(400, 'Missing title');
7    if (!content) ctx.throw(400, 'Missing content');
8    if (!author) ctx.throw(400, 'Missing author');
9
10    const article = await strapi.db.query('api::article.article').create({
11      data: { title, content, author },
12    });
13
14    ctx.body = article;
15  },
16};

6. Evite aninhamento excessivo em controllers

Evite grandes blocos de lógica aninhada dentro de controllers ou services. Extraia condições repetidas ou complexas para funções auxiliares ou utilitários bem nomeados.

Exemplo não em conformidade

1// src/api/order/controllers/order.js
2module.exports = {
3  async create(ctx) {
4    const { items, user } = ctx.request.body;
5
6    if (user && user.role === 'customer') {
7      if (items && items.length > 0) {
8        const stock = await strapi.service('api::inventory.inventory').checkStock(items);
9        if (stock.every((i) => i.available)) {
10          const order = await strapi.db.query('api::order.order').create({ data: { items, user } });
11          ctx.body = order;
12        } else {
13          ctx.throw(400, 'Some items are out of stock');
14        }
15      } else {
16        ctx.throw(400, 'No items in order');
17      }
18    } else {
19      ctx.throw(403, 'Unauthorized user');
20    }
21  },
22};

Exemplo em conformidade

1// src/api/order/utils/validation.js
2const isCustomer = (user) => user?.role === 'customer';
3const hasItems = (items) => Array.isArray(items) && items.length > 0;
4
5// src/api/order/controllers/order.js
6module.exports = {
7  async create(ctx) {
8    const { items, user } = ctx.request.body;
9
10    if (!isCustomer(user)) ctx.throw(403, 'Unauthorized user');
11    if (!hasItems(items)) ctx.throw(400, 'No items in order');
12
13    const stock = await strapi.service('api::inventory.inventory').checkStock(items);
14    const allAvailable = stock.every((i) => i.available);
15    if (!allAvailable) ctx.throw(400, 'Some items are out of stock');
16
17    const order = await strapi.db.query('api::order.order').create({ data: { items, user } });
18    ctx.body = order;
19  },
20};

7. Mantenha a lógica de negócios fora dos controllers

Controladores devem permanecer enxutos e apenas orquestrar requisições. Mova a lógica de negócio para os serviços.

Exemplo não em conformidade

1// src/api/article/controllers/article.js
2module.exports = {
3  async create(ctx) {
4    const { title, content, authorId } = ctx.request.body;
5
6    const author = await strapi.db.query('api::author.author').findOne({ where: { id: authorId } });
7    if (!author) ctx.throw(400, 'Author not found');
8
9    const timestamp = new Date().toISOString();
10    const slug = title.toLowerCase().replace(/\s+/g, '-');
11
12    const article = await strapi.db.query('api::article.article').create({
13      data: { title, content, slug, publishedAt: timestamp, author },
14    });
15
16    await strapi.plugins['email'].services.email.send({
17      to: author.email,
18      subject: `New article: ${title}`,
19      html: `<p>${content}</p>`,
20    });
21
22    ctx.body = article;
23  },
24};

Exemplo em conformidade

1// src/api/article/controllers/article.js
2module.exports = {
3  async create(ctx) {
4    const article = await strapi.service('api::article.article').createArticle(ctx.request.body);
5    ctx.body = article;
6  },
7};
// src/api/article/services/article.js
module.exports = ({ strapi }) => ({
  async createArticle(data) {
    const { title, content, authorId } = data;

    const author = await strapi.db.query('api::author.author').findOne({ where: { id: authorId } });
    if (!author) throw new Error('Author not found');

    const slug = title.toLowerCase().replace(/\s+/g, '-');
    const article = await strapi.db.query('api::article.article').create({
      data: { title, content, slug, author },
    });

    await strapi.plugins['email'].services.email.send({
      to: author.email,
      subject: `New article: ${title}`,
      html: `<p>${content}</p>`,
    });

    return article;
  },
});

8. Use funções de utilidade para padrões repetidos

Padrões duplicados (por exemplo, validação, formatação) devem residir em utilitários compartilhados.

Exemplo não em conformidade

// src/api/article/controllers/article.js
module.exports = {
  async create(ctx) {
    const { title } = ctx.request.body;
    const slug = title.toLowerCase().replace(/\s+/g, '-');
    ctx.body = await strapi.db.query('api::article.article').create({ data: { ...ctx.request.body, slug } });
  },
};

// src/api/event/controllers/event.js
module.exports = {
  async create(ctx) {
    const { name } = ctx.request.body;
    const slug = name.toLowerCase().replace(/\s+/g, '-');
    ctx.body = await strapi.db.query('api::event.event').create({ data: { ...ctx.request.body, slug } });
  },
};

Exemplo em conformidade

// src/utils/slugify.js
module.exports = (text) => text.toLowerCase().trim().replace(/\s+/g, '-');
// src/api/article/controllers/article.js
const slugify = require('../../../utils/slugify');

module.exports = {
  async create(ctx) {
    const { title } = ctx.request.body;
    const slug = slugify(title);
    ctx.body = await strapi.db.query('api::article.article').create({ data: { ...ctx.request.body, slug } });
  },
};

9. Remova os logs de depuração antes da produção

Não use console.log, console.warn ou console.error em código de produção. Sempre use strapi.log ou um logger configurado para garantir que os logs respeitem as configurações do ambiente e evitem expor informações sensíveis.

Exemplo não em conformidade

// src/api/user/controllers/user.js
module.exports = {
  async find(ctx) {
    console.log('Request received:', ctx.request.body); // Unsafe in production
    const users = await strapi.db.query('api::user.user').findMany();
    console.log('Users fetched:', users.length);
    ctx.body = users;
  },
};

Exemplo em conformidade

// src/api/user/controllers/user.js
module.exports = {
  async find(ctx) {
    strapi.log.info(`Fetching users for request from ${ctx.state.user?.email || 'anonymous'}`);
    const users = await strapi.db.query('api::user.user').findMany();
    strapi.log.debug(`Number of users fetched: ${users.length}`);
    ctx.body = users;
  },
};
if (process.env.NODE_ENV === 'development') {
  strapi.log.debug('Request body:', ctx.request.body);
}

Regras: Práticas de banco de dados e consultas

10. Evite consultas SQL brutas

Não execute consultas SQL brutas em controllers ou serviços. Sempre use um método de consulta consistente e de alto nível (como um ORM ou query builder) para garantir a manutenibilidade, aplicar regras/hooks e reduzir riscos de segurança.

Exemplo não em conformidade

// src/api/user/services/user.js
module.exports = {
  async findActiveUsers() {
    const knex = strapi.db.connection; 
    const result = await knex.raw('SELECT * FROM users WHERE active = true'); // Raw SQL
    return result.rows;
  },
};

Exemplo em conformidade

// src/api/user/services/user.js
module.exports = {
  async findActiveUsers() {
    return await strapi.db.query('api::user.user').findMany({
      where: { active: true },
    });
  },
};

11. Use o query engine do Strapi consistentemente

Não misture diferentes métodos de acesso a banco de dados (por exemplo, chamadas ORM vs. consultas brutas) dentro da mesma funcionalidade. Use uma abordagem de consulta única e consistente para garantir manutenibilidade, legibilidade e comportamento previsível.

Exemplo não em conformidade

// src/api/order/services/order.js
module.exports = {
  async getPendingOrders() {
    // Using entityService
    const orders = await strapi.entityService.findMany('api::order.order', {
      filters: { status: 'pending' },
    });

    // Mixing with raw db query
    const rawOrders = await strapi.db.connection.raw('SELECT * FROM orders WHERE status = "pending"');

    return { orders, rawOrders };
  },
};

Exemplo em conformidade

// src/api/order/services/order.js
module.exports = {
  async getPendingOrders() {
    return await strapi.db.query('api::order.order').findMany({
      where: { status: 'pending' },
    });
  },
};

12. Otimize as chamadas ao banco de dados

Agrupe consultas de banco de dados relacionadas ou combine-as em uma única operação para evitar gargalos de desempenho e reduzir chamadas sequenciais desnecessárias.

Exemplo não em conformidade

async function getArticlesWithAuthors() {
  const articles = await db.query('articles').findMany();

  // Fetch author for each article sequentially
  for (const article of articles) {
    article.author = await db.query('authors').findOne({ id: article.authorId });
  }

  return articles;
}

Exemplo em conformidade

async function getArticlesWithAuthors() {
  return await db.query('articles').findMany({ populate: ['author'] });
}

Regras: API e Segurança

13. Valide a entrada com os validadores do Strapi

Nunca confie na entrada de clientes ou fontes externas. Valide todos os dados de entrada usando um mecanismo de validação consistente antes de usá-los em controladores, serviços ou operações de banco de dados.

Exemplo não em conformidade

async function createUser(req, res) {
  const { username, email } = req.body;

  // Directly inserting into database without validation
  const user = await db.query('users').create({ username, email });
  res.send(user);
}

Exemplo em conformidade

const Joi = require('joi');

async function createUser(req, res) {
  const schema = Joi.object({
    username: Joi.string().min(3).required(),
    email: Joi.string().email().required(),
  });

  const { error, value } = schema.validate(req.body);
  if (error) return res.status(400).send(error.details);

  const user = await db.query('users').create(value);
  res.send(user);
}

14. Sanitize a entrada do usuário antes de salvar

Sanitize todas as entradas antes de salvá-las no banco de dados ou passá-las para outros sistemas.

Exemplo não em conformidade

async function createComment(req, res) {
  const { text, postId } = req.body;

  // Directly saving data
  const comment = await db.query('comments').create({ text, postId });
  res.send(comment);
}

Exemplo em conformidade

const sanitizeHtml = require('sanitize-html');

async function createComment(req, res) {
  const { text, postId } = req.body;

  const sanitizedText = sanitizeHtml(text, { allowedTags: [], allowedAttributes: {} });

  const comment = await db.query('comments').create({ text: sanitizedText, postId });
  res.send(comment);
}

15. Aplique verificações de permissão

Aplique verificações de permissão em cada rota protegida para garantir que apenas usuários autorizados possam acessá-la.

Exemplo não em conformidade

async function deleteUser(req, res) {
  const { userId } = req.params;

  // No check for admin or owner
  await db.query('users').delete({ id: userId });
  res.send({ success: true });
}

Exemplo em conformidade

async function deleteUser(req, res) {
  const { userId } = req.params;
  const requestingUser = req.user;

  // Allow only admins or the owner
  if (!requestingUser.isAdmin && requestingUser.id !== userId) {
    return res.status(403).send({ error: 'Forbidden' });
  }

  await db.query('users').delete({ id: userId });
  res.send({ success: true });
}

16. Tratamento consistente de erros com Boom

Trate erros de forma consistente em todas as rotas de API usando um mecanismo de tratamento de erros centralizado ou unificado.

Exemplo não em conformidade

async function getUser(req, res) {
  const { id } = req.params;

  try {
    const user = await db.query('users').findOne({ id });
    if (!user) res.status(404).send('User not found'); // raw string error
    else res.send(user);
  } catch (err) {
    res.status(500).send(err.message); // different error format
  }
}

Exemplo em conformidade

const { createError } = require('../utils/errors');

async function getUser(req, res, next) {
  try {
    const { id } = req.params;
    const user = await db.query('users').findOne({ id });

    if (!user) throw createError(404, 'User not found');

    res.send(user);
  } catch (err) {
    next(err); // passes error to centralized error handler
  }
}
// src/utils/errors.js
function createError(status, message) {
  return { status, message };
}

function errorHandler(err, req, res, next) {
  res.status(err.status || 500).json({ error: err.message });
}

module.exports = { createError, errorHandler };

Regras: Testes e documentação

17. Adicione ou atualize testes para cada funcionalidade

Novo código sem testes não será mesclado, testes fazem parte da definição de pronto (definition of done).

Exemplo não em conformidade

// src/api/user/services/user.js
module.exports = {
  async createUser(data) {
    const user = await db.query('users').create(data);
    return user;
  },
};

// No test file exists for this service

Exemplo em conformidade

// tests/user.service.test.js
const { createUser } = require('../../src/api/user/services/user');

describe('User Service', () => {
  it('should create a new user', async () => {
    const mockData = { username: 'testuser', email: 'test@example.com' };
    const result = await createUser(mockData);

    expect(result).toHaveProperty('id');
    expect(result.username).toBe('testuser');
    expect(result.email).toBe('test@example.com');
  });
});

18. Documente novos endpoints

Toda adição de API deve ser documentada nos docs de referência antes do merge.

Exemplo não em conformidade

// src/api/user/controllers/user.js
module.exports = {
  async deactivate(ctx) {
    const { userId } = ctx.request.body;
    await db.query('users').update({ id: userId, active: false });
    ctx.body = { success: true };
  },
};

// No update in API reference or docs

Exemplo em conformidade

// src/api/user/controllers/user.js
module.exports = {
  /**
   * Deactivate a user account.
   * POST /users/deactivate
   * Body: { userId: string }
   * Response: { success: boolean }
   * Errors: 400 if userId missing, 404 if user not found
   */
  async deactivate(ctx) {
    const { userId } = ctx.request.body;
    if (!userId) ctx.throw(400, 'userId is required');

    const user = await db.query('users').findOne({ id: userId });
    if (!user) ctx.throw(404, 'User not found');

    await db.query('users').update({ id: userId, active: false });
    ctx.body = { success: true };
  },
};

Exemplo de Atualização de Documentos de Referência:

### POST /users/deactivate

**Request Body:**
```json
{
  "userId": "string"
}

Resposta:

{
  "success": true
}

Erros:

  • 400: userId é obrigatório
  • 404: Usuário não encontrado
Por que isso funciona:  
- Desenvolvedores e consumidores de API podem descobrir e usar endpoints de forma confiável  
- Garante consistência entre implementação e documentação  
- Facilita a manutenção e o onboarding  

---

Você quer que eu continue com a **Regra #19 (“Use JSDoc para Utilitários Compartilhados”)** no mesmo formato a seguir?

19. Use JSDoc para utilitários compartilhados

Funções compartilhadas devem ser explicadas com JSDoc para facilitar o onboarding e a colaboração.

Exemplo não em conformidade

// src/utils/slugify.js
function slugify(text) {
  return text.toLowerCase().trim().replace(/\s+/g, '-');
}

module.exports = slugify;

Exemplo em conformidade

// src/utils/slugify.js

/**
 * Converts a string into a URL-friendly slug.
 *
 * @param {string} text - The input string to convert.
 * @returns {string} A lowercased, trimmed, dash-separated slug.
 */
function slugify(text) {
  return text.toLowerCase().trim().replace(/\s+/g, '-');
}

module.exports = slugify;

20. Atualizar changelog a cada PR significativo

Atualize o changelog do projeto com cada recurso significativo, correção de bug ou alteração de API antes de fazer merge de um PR.

Exemplo não em conformidade

# CHANGELOG.md

## [1.0.0] - 2025-09-01
- Lançamento inicial

Exemplo em conformidade

# CHANGELOG.md

## [1.1.0] - 2025-10-06
- Adicionado endpoint de desativação de usuário (`POST /users/deactivate`)
- Corrigido bug na geração de slug para títulos de artigos
- Serviço de notificação por e-mail atualizado para lidar com envio em lote

Conclusão

Estudamos o repositório público do Strapi para entender como padrões de código consistentes ajudam grandes projetos de código aberto a crescer sem perder qualidade. Estas 20 regras não são teoria. São lições práticas tiradas diretamente da base de código do Strapi que tornam o projeto mais fácil de manter, mais seguro e mais fácil de ler.

Se seu projeto está crescendo, adote estas lições e aplique-as em suas revisões de código. Elas ajudarão você a gastar menos tempo limpando código bagunçado e mais tempo construindo funcionalidades que realmente importam.

FAQs

Dúvidas?

Por que as regras de revisão de código são importantes em projetos de código aberto como o Strapi?

Como centenas de desenvolvedores contribuem, regras de revisão consistentes evitam o caos. Elas garantem que cada pull request siga a mesma estrutura, nomenclatura e padrões de segurança, mantendo o projeto estável e sustentável ao longo do tempo.

Como o Strapi mantém a qualidade do código em escala?

Strapi utiliza convenções de pastas rigorosas, adoção de TypeScript, testes automatizados e validação padronizada. Cada alteração é revisada quanto à clareza, desempenho e segurança antes da fusão.

Qual a diferença entre linting e regras de revisão de código?

O linting detecta automaticamente problemas de sintaxe e formatação. As regras de revisão de código vão mais fundo — elas abordam arquitetura, legibilidade, clareza de nomenclatura, manutenibilidade e segurança que as ferramentas não conseguem detectar sem contexto humano ou de nível de IA.

Como as equipes podem aplicar essas regras em seus próprios projetos?

- Documente seus padrões de revisão.
- Adicione verificações automatizadas e modelos de PR.
- Use estruturas de nomenclatura e pastas consistentes.
- Exija testes e atualizações de documentação em cada merge.
- Execute ferramentas de revisão de código baseadas em IA como o Aikido Code Quality para identificar problemas de design de alto nível.

Quais ferramentas podem ajudar a aplicar regras de code review automaticamente?

Ferramentas como Aikido Security Code Quality ajudam a detectar problemas arquitetônicos, de manutenibilidade e de segurança durante as revisões de código—indo muito além do linting tradicional.

Qual o maior desafio na manutenção da base de código do Strapi?

Equilibrando inovação com consistência — novos contribuidores trazem ideias novas, mas sem regras claras, o projeto corre o risco de divergir para padrões inconsistentes e código difícil de manter.

Como essas regras melhoram o onboarding para novos contribuidores?

Eles fornecem um roteiro claro de expectativas. Quando a estrutura de pastas, a nomenclatura e a documentação são previsíveis, novos desenvolvedores podem entender rapidamente como contribuir sem quebrar padrões existentes.

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.