
Einleitung
Strapi ist eine der beliebtesten Open-Source Headless-CMS-Plattformen, aber auch eine riesige Codebasis mit Hunderten von Mitwirkenden und Tausenden von Pull Requests. Die Qualität eines so großen Projekts hoch zu halten, ist nicht einfach. Es erfordert klare und konsistente Code-Review-Regeln, um sicherzustellen, dass jeder Beitrag zuverlässig, lesbar und sicher bleibt.
In diesem Artikel haben wir eine Reihe von Code-Review-Regeln zusammengestellt, die auf dem öffentlichen Repository von Strapi basieren. Diese Regeln stammen aus der Praxis: tatsächliche Probleme, Diskussionen und Pull Requests, die dem Projekt geholfen haben, zu wachsen und gleichzeitig die Codebasis stabil zu halten.
Warum die Aufrechterhaltung der Codequalität in einem großen Open-Source-Projekt schwierig ist
Die Aufrechterhaltung der Qualität in einem großen Open-Source-Projekt ist aufgrund des schieren Umfangs und der Vielfalt der Beiträge eine Herausforderung. Hunderte oder sogar Tausende von Entwickelnden, von Freiwilligen bis hin zu erfahrenen Ingenieuren, reichen Pull Requests ein, die jeweils neue Funktionen, Fehlerbehebungen oder Refactorings einführen. Ohne klare Regeln kann die Codebasis schnell inkonsistent, brüchig oder schwer zu navigieren werden.
Einige der größten Herausforderungen sind:
- Vielfältige Mitwirkende mit unterschiedlichen Erfahrungsstufen.
- Inkonsistente Coding Patterns über Module hinweg.
- Versteckte Fehler und duplizierte Logik schleichen sich ein.
- Sicherheitsrisiken, wenn Prozesse nicht durchgesetzt werden.
- Zeitaufwändige Überprüfungen für Freiwillige, die mit der gesamten Codebasis nicht vertraut sind.
Um diesen Herausforderungen zu begegnen, setzen erfolgreiche Projekte auf strukturierte Prozesse: gemeinsame Standards, automatisierte Tools und klare Richtlinien. Diese Praktiken gewährleisten Wartbarkeit, Lesbarkeit und Sicherheit, selbst wenn das Projekt wächst und mehr Mitwirkende anzieht.
Wie die Einhaltung dieser Regeln Wartbarkeit, Sicherheit und Onboarding verbessert
Die Einhaltung klarer Code-Review-Regeln hat einen direkten Einfluss auf die Integrität Ihres Projekts:
- Wartbarkeit: Konsistente Ordnerstrukturen, Namenskonventionen und Codierungsmuster erleichtern das Lesen, Navigieren und Erweitern der Codebasis.
- Sicherheit: Eingabevalidierung, Bereinigung, Berechtigungsprüfungen und kontrollierter Datenbankzugriff reduzieren Schwachstellen und verhindern unbeabsichtigte Datenlecks.
- Schnelleres Onboarding: Gemeinsame Standards, dokumentierte Dienstprogramme und klare Beispiele helfen neuen Mitwirkenden, das Projekt schnell zu verstehen und selbstbewusst beizutragen.
Durch die Anwendung dieser Regeln können Teams sicherstellen, dass die Codebasis skalierbar, zuverlässig und sicher bleibt, selbst wenn die Anzahl der Mitwirkenden wächst.
Kontext mit Regeln verknüpfen
Bevor wir uns die Regeln ansehen, ist es wichtig zu verstehen, dass die Aufrechterhaltung einer hohen Codequalität in einem Projekt wie Strapi nicht nur das Befolgen allgemeiner Best Practices bedeutet. Es geht darum, klare Muster und Standards zu haben, die Hunderten von Mitwirkenden helfen, auf dem gleichen Stand zu bleiben. Jede der 20 Regeln unten konzentriert sich auf reale Herausforderungen, die in der Codebasis von Strapi auftreten.
Die für jede Regel bereitgestellten Beispiele veranschaulichen sowohl nicht-konforme als auch konforme Ansätze und vermitteln ein klares Bild davon, wie diese Prinzipien in der Praxis angewendet werden.
Lassen Sie uns nun die Regeln erkunden, die die Codebasis von Strapi skalierbar, konsistent und hochwertig machen, beginnend mit Projektstruktur und Konfigurationsstandards.
Regeln: Projektstruktur & Konsistenz
1. Befolgen Sie Strapis etablierte Ordnerkonventionen
Vermeiden Sie es, Dateien zu verstreuen oder neue Strukturen zu erfinden. Halten Sie sich an das etablierte Projektlayout von Strapi, um die Navigation vorhersehbar zu halten.
❌ Nicht konformes Beispiel
1src/
2├── controllers/
3│ └── userController.js
4├── services/
5│ └── userLogic.js
6├── routes/
7│ └── userRoutes.js
8└── utils/
9 └── helper.js✅ Compliance-konformes Beispiel
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.json2. Konfigurationsdateien konsistent halten
Verwenden Sie in allen Konfigurationsdateien dieselben Struktur-, Benennungs- und Formatierungskonventionen, um Konsistenz zu gewährleisten und Fehler zu vermeiden.
❌ Nicht konformes Beispiel
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});✅ Compliance-konformes Beispiel
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. Strikte Typsicherheit gewährleisten
Sämtlicher neuer oder aktualisierter Code muss genaue TypeScript-Typen oder JSDoc-Definitionen enthalten. Vermeiden Sie die Verwendung von 'any', fehlende Rückgabetypen oder implizite Typinferenz in gemeinsam genutzten Modulen.
❌ Nicht konformes Beispiel
1// src/api/user/services/user.ts
2export const createUser = (data) => {
3 return strapi.db.query('api::user.user').create({ data });
4};✅ Compliance-konformes Beispiel
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. Konsistente Benennung von Services und Controllern
Controller- und Dienstnamen müssen eindeutig ihrer Domäne entsprechen (z. B. user.controller.js mit user.service.js).
❌ Nicht konformes Beispiel
1src/
2└── api/
3 └── user/
4 ├── controllers/
5 │ └── mainController.js
6 ├── services/
7 │ └── accountService.js
8 ├── routes/
9 │ └── user.js✅ Compliance-konformes Beispiel
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
Regeln: Code-Qualität & Wartbarkeit
5. Kontrollfluss mit Early Returns vereinfachen
Anstatt tiefer if/else-Verschachtelungen frühzeitig zurückkehren, wenn Bedingungen fehlschlagen.
❌ Nicht konformes Beispiel
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};✅ Compliance-konformes Beispiel
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. Vermeiden Sie übermäßige Verschachtelung in Controllern
Große Blöcke verschachtelter Logik in Controllern oder Services vermeiden. Wiederholte oder komplexe Bedingungen in gut benannte Hilfsfunktionen oder Utilities auslagern.
❌ Nicht konformes Beispiel
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};✅ Compliance-konformes Beispiel
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. Geschäftslogik aus Controllern heraushalten
Controller sollten schlank bleiben und nur Anfragen orchestrieren. Verschieben Sie die Geschäftslogik in Services.
❌ Nicht konformes Beispiel
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};✅ Compliance-konformes Beispiel
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. Nutzung von Utility-Funktionen für wiederkehrende Muster
Doppelte Muster (z. B. Validierung, Formatierung) sollten in gemeinsamen Dienstprogrammen untergebracht werden.
❌ Nicht konformes Beispiel
// 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 } });
},
};✅ Compliance-konformes Beispiel
// 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. Debugging-Logs vor der Produktion entfernen
Verwenden Sie keine console.log, console.warn oder console.error im Produktionscode. Verwenden Sie immer strapi.log oder einen konfigurierten Logger, um sicherzustellen, dass Logs Umgebungseinstellungen respektieren und die Offenlegung sensibler Informationen vermieden wird.
❌ Nicht konformes Beispiel
// 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;
},
};✅ Compliance-konformes Beispiel
// 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);
}
Regeln: Datenbank- & Abfragepraktiken
10. Rohe SQL-Abfragen vermeiden
Führen Sie keine rohen SQL-Abfragen in Controllern oder Diensten aus. Verwenden Sie immer eine konsistente, übergeordnete Abfragemethode (wie ein ORM oder Query Builder), um die Wartbarkeit zu gewährleisten, Regeln/Hooks durchzusetzen und Sicherheitsrisiken zu reduzieren.
❌ Nicht konformes Beispiel
// 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;
},
};✅ Compliance-konformes Beispiel
// src/api/user/services/user.js
module.exports = {
async findActiveUsers() {
return await strapi.db.query('api::user.user').findMany({
where: { active: true },
});
},
};11. Strapis Query Engine konsistent verwenden
Mischen Sie keine verschiedenen Datenbankzugriffsmethoden (z. B. ORM-Aufrufe vs. rohe Abfragen) innerhalb desselben Features. Verwenden Sie einen einzigen, konsistenten Abfrageansatz, um Wartbarkeit, Lesbarkeit und vorhersehbares Verhalten zu gewährleisten.
❌ Nicht konformes Beispiel
// 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 };
},
};✅ Compliance-konformes Beispiel
// src/api/order/services/order.js
module.exports = {
async getPendingOrders() {
return await strapi.db.query('api::order.order').findMany({
where: { status: 'pending' },
});
},
};12. Datenbankaufrufe optimieren
Zugehörige Datenbankabfragen bündeln oder zu einem einzigen Vorgang zusammenfassen, um Performance-Engpässe zu vermeiden und unnötige sequentielle Aufrufe zu reduzieren.
❌ Nicht konformes Beispiel
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;
}✅ Compliance-konformes Beispiel
async function getArticlesWithAuthors() {
return await db.query('articles').findMany({ populate: ['author'] });
}
Regeln: API & Security
13. Eingaben mit Strapi-Validatoren validieren
Vertrauen Sie niemals Eingaben von Clients oder externen Quellen.Validieren Sie alle eingehenden Daten mit einem konsistenten Validierungsmechanismus, bevor Sie sie in Controllern, Services oder Datenbankoperationen verwenden.
❌ Nicht konformes Beispiel
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);
}✅ Compliance-konformes Beispiel
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. Benutzereingaben vor dem Speichern bereinigen
Bereinigen Sie alle Eingaben, bevor Sie sie in der Datenbank speichern oder an andere Systeme weitergeben.
❌ Nicht konformes Beispiel
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);
}✅ Compliance-konformes Beispiel
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. Berechtigungsprüfungen erzwingen
Wenden Sie Berechtigungsprüfungen auf jeder geschützten Route an, um sicherzustellen, dass nur autorisierte Benutzer darauf zugreifen können.
❌ Nicht konformes Beispiel
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 });
}✅ Compliance-konformes Beispiel
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. Konsistente Fehlerbehandlung mit Boom
Fehler konsistent über alle API-Routen hinweg behandeln, mithilfe eines zentralisierten oder vereinheitlichten Fehlerbehandlungsmechanismus.
❌ Nicht konformes Beispiel
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
}
}✅ Compliance-konformes Beispiel
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 };
Regeln: Testen & Dokumentation
17. Tests für jede Funktion hinzufügen oder aktualisieren
Neuer Code ohne Tests wird nicht gemergt, Tests sind Teil der Definition von Done.
❌ Nicht konformes Beispiel
// 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✅ Compliance-konformes Beispiel
// 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. Neue Endpunkte dokumentieren
Jede API-Ergänzung muss vor dem Merge in den Referenzdokumenten dokumentiert werden.
❌ Nicht konformes Beispiel
// 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✅ Compliance-konformes Beispiel
// 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 };
},
};Beispiel für die Aktualisierung der Referenzdokumentation:
### POST /users/deactivate
**Request Body:**
```json
{
"userId": "string"
}Reaktion:
{
"success": true
}Fehler:
- 400: userId ist erforderlich
- 404: Benutzer nicht gefunden
Warum das funktioniert:
- Entwickelnde und API-Konsumenten können Endpunkte zuverlässig entdecken und nutzen
- Stellt Konsistenz zwischen Implementierung und Dokumentation sicher
- Erleichtert Wartung und Onboarding
---
Möchten Sie, dass ich mit **Regel #19 („JSDoc für Shared Utilities verwenden“)** im gleichen Format fortfahre?19. JSDoc für gemeinsam genutzte Dienstprogramme verwenden
Geteilte Funktionen sollten mit JSDoc erklärt werden, um Onboarding und Zusammenarbeit zu erleichtern.
❌ Nicht konformes Beispiel
// src/utils/slugify.js
function slugify(text) {
return text.toLowerCase().trim().replace(/\s+/g, '-');
}
module.exports = slugify;✅ Compliance-konformes Beispiel
// 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. Changelog mit jedem signifikanten PR aktualisieren
Aktualisieren Sie das Changelog des Projekts mit jeder wesentlichen Funktion, jedem Bugfix oder jeder API-Änderung, bevor Sie einen PR mergen.
❌ Nicht konformes Beispiel
# CHANGELOG.md
## [1.0.0] - 2025-09-01
- Erstveröffentlichung✅ Compliance-konformes Beispiel
# CHANGELOG.md
## [1.1.0] - 2025-10-06
- Endpunkt zur Benutzerdeaktivierung hinzugefügt (`POST /users/deactivate`)
- Fehler bei der Slug-Generierung für Artikeltitel behoben
- E-Mail-Benachrichtigungsdienst für den Stapelversand aktualisiertFazit
Wir haben Strapi’s öffentliches Repository untersucht, um zu verstehen, wie konsistente Code-Muster großen Open-Source-Projekten helfen, ohne Qualitätsverlust zu wachsen. Diese 20 Regeln sind keine Theorie. Es sind praktische Lektionen, die direkt aus Strapi’s Codebasis stammen und das Projekt wartbarer, sicherer und lesbarer machen.
Wenn Ihr Projekt wächst, übernehmen Sie diese Lektionen und wenden Sie sie auf Ihre Code-Reviews an. Sie helfen Ihnen, weniger Zeit mit dem Aufräumen von unordentlichem Code zu verbringen und mehr Zeit mit dem Erstellen von Features, die wirklich wichtig sind.

