
Giriş
Strapi, en popüler açık kaynaklı headless CMS platformlarından biridir, ancak aynı zamanda yüzlerce katkıda bulunanı ve binlerce çekme isteği (pull request) barındıran devasa bir kod tabanıdır. Böyle büyük bir projenin kalitesini yüksek tutmak kolay değildir. Her katkının güvenilir, okunabilir ve güvenli kalmasını sağlamak için net ve tutarlı kod inceleme (code review) kuralları gerektirir.
Bu makalede, Strapi'nin genel (public) repository'sine dayanarak bir dizi kod inceleme kuralı derledik. Bu kurallar gerçek çalışmalardan doğmuştur: projenin büyümesine yardımcı olan ve kod tabanını kararlı tutan gerçek sorunlar, tartışmalar ve pull request'ler.
Büyük bir açık kaynaklı projede kod kalitesini korumak neden zordur?
Büyük bir açık kaynak projesinde kaliteyi korumak; katkıların muazzam ölçeği ve çeşitliliği nedeniyle zorlayıcıdır. Gönüllülerden kıdemli mühendislere kadar yüzlerce hatta binlerce geliştirici, her biri yeni özellikler, hata düzeltmeleri veya yeniden yapılandırmalar (refactors) içeren pull request'ler gönderir. Net kurallar olmadığında kod tabanı hızla tutarsız, kırılgan veya üzerinde çalışılması güç bir hale gelebilir.
Başlıca zorluklardan bazıları şunlardır:
- Farklı deneyim seviyelerine sahip katılımcılar.
- Modüller arasında tutarsız kodlama kalıpları (patterns).
- Sinsi hatalar ve kod tabanına sızan mükerrer mantık (duplicated logic).
- Güvenlik riskleri: Süreçlerin uygulanmaması durumunda ortaya çıkar.
- Zaman alıcı incelemeler: Kod tabanının tamamına aşina olmayan gönüllüler için geçerlidir.
Bu zorlukların üstesinden gelmek için başarılı projeler yapılandırılmış süreçlere güvenir: paylaşılan standartlar, otomasyon araçları ve net yönergeler. Bu uygulamalar, proje büyüse ve daha fazla katkıda bulunan insanı kendine çekse bile sürdürülebilirliği, okunabilirliği ve güvenliği garanti eder.
Bu kurallara uymanın sürdürülebilirliği, güvenliği ve uyum sürecini nasıl iyileştirdiği
Net bir kod inceleme kuralları bütününe bağlı kalmak, projenizin sağlığı üzerinde doğrudan bir etkiye sahiptir:
- Sürdürülebilirlik: Tutarlı klasör yapısı, adlandırma kuralları ve kodlama kalıpları, kod tabanının okunmasını, incelenmesini ve genişletilmesini kolaylaştırır.
- Güvenlik: Giriş doğrulama, sanitizasyon, yetki kontrolleri ve denetimli veritabanı erişimi, güvenlik açıkları azaltır ve kazara veri sızıntılarını önler.
- Daha Hızlı Uyum Süreci: Paylaşılan standartlar, belgelenmiş araçlar ve net örnekler, yeni katkıda bulunanların projeyi hızla kavramasına ve güvenle katkı sağlamasına yardımcı olur.
Ekipler bu kuralları uygulayarak, katkıda bulunanların sayısı artsa bile kod tabanının ölçeklenebilir, güvenilir ve güvenli kalmasını sağlayabilir.
Bağlamı kurallarla buluşturmak
Kurallara geçmeden önce, Strapi gibi bir projede kod kalitesini yüksek tutmanın yalnızca genel en iyi uygulamaları takip etmekten ibaret olmadığını anlamak önemlidir. Bu durum, yüzlerce katılımcının aynı sayfada kalmasını sağlayan net kalıplara ve standartlara sahip olmakla ilgilidir. Aşağıdaki 20 kuralın her biri, Strapi'nin kod tabanında ortaya çıkan gerçek zorluklara odaklanmaktadır.
Her kural için sağlanan örnekler, uyumlu olan ve olmayan yaklaşımları göstererek bu ilkelerin pratikte nasıl uygulandığına dair net bir tablo sunar.
Şimdi, proje yapısı ve yapılandırma standartlarından başlayarak Strapi'nin kod tabanını ölçeklenebilir, tutarlı ve yüksek kaliteli kural setini inceleyelim.
Kurallar: Proje yapısı ve tutarlılık
1. Strapi'nin belirlenmiş klasör kurallarına uyun
Dosyaları rastgele dağıtmaktan veya yeni yapılar icat etmekten kaçının. Gezinmeyi öngörülebilir tutmak için Strapi'nin belirlenmiş proje düzenine bağlı kalın.
❌ Uyumsuz örnek
1src/
2├── controllers/
3│ └── userController.js
4├── services/
5│ └── userLogic.js
6├── routes/
7│ └── userRoutes.js
8└── utils/
9 └── helper.js✅ Kurala Uygun Örnek
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. Yapılandırma dosyalarını tutarlı tutun
Tutarlılığı sağlamak ve hataları önlemek için tüm yapılandırma dosyalarında aynı yapıyı, adlandırmayı ve biçimlendirme kurallarını kullanın.
❌ Uyumsuz örnek
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});✅ Kurala Uygun Örnek
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. Katı tip güvenliğini koruyun
Tüm yeni veya güncellenmiş kodlar doğru TypeScript tipleri veya JSDoc tanımlamaları içermelidir. Paylaşılan modüllerde any kullanmaktan, eksik dönüş tiplerinden veya örtük tip çıkarımından kaçının.
❌ Uyumsuz örnek
1// src/api/user/services/user.ts
2export const createUser = (data) => {
3 return strapi.db.query('api::user.user').create({ data });
4};✅ Kurala Uygun Örnek
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. Servisler ve controller'lar için tutarlı adlandırma
Controller ve servis adları, alan adlarıyla açık bir şekilde eşleşmelidir (ör. user.controller.js ve user.service.js).
❌ Uyumsuz örnek
1src/
2└── api/
3 └── user/
4 ├── controllers/
5 │ └── mainController.js
6 ├── services/
7 │ └── accountService.js
8 ├── routes/
9 │ └── user.js✅ Kurala Uygun Örnek
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.jsonKurallar: Kod kalitesi ve sürdürülebilirlik
5. Erken return (early return) kullanarak kontrol akışını sadeleştirin
Derin if/else iç içe yapıları (nesting) kullanmak yerine, koşullar sağlanmadığında işlemi erken sonlandırarak return edin.
❌ Uyumsuz örnek
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};✅ Kurala Uygun Örnek
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. Controller'larda aşırı iç içe yapılardan kaçının
Controller veya service katmanlarında büyük iç içe mantık blokları bulundurmaktan kaçının. Tekrar eden veya karmaşık koşulları, anlamlı isimlere sahip yardımcı fonksiyonlara (helper) ya da araçlara taşıyın.
❌ Uyumsuz örnek
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};✅ Kurala Uygun Örnek
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. İş mantığını (business logic) controller'ların dışında tutun
Controller'lar yalın kalmalı ve yalnızca istekleri yönetmelidir. İş mantığını service'lere taşıyın.
❌ Uyumsuz örnek
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};✅ Kurala Uygun Örnek
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. Tekrar eden kalıplar için utility fonksiyonları kullanın
Tekrar eden kalıplar (ör. doğrulama, biçimlendirme) paylaşılan utility'lerde yer almalıdır.
❌ Uyumsuz örnek
// 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 } });
},
};✅ Kurala Uygun Örnek
// 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. Prod ortamına geçmeden önce hata ayıklama (debugging) log'larını kaldırın
Canlı (production) kodunda console.log, console.warn veya console.error kullanmayın. Log'ların ortam ayarlarına uymasını ve hassas bilgilerin sızmasını önlemek için her zaman strapi.log veya yapılandırılmış bir logger kullanın.
❌ Uyumsuz örnek
// 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;
},
};✅ Kurala Uygun Örnek
// 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);
}Kurallar: Veritabanı ve sorgu pratikleri
10. Ham (raw) SQL sorgularından kaçının
Controller veya service'lerde ham SQL sorguları çalıştırmayın. Sürdürülebilirliği sağlamak, kuralları/hook'ları uygulamak ve güvenlik risklerini azaltmak için her zaman tutarlı, üst düzey bir sorgu yöntemi (ORM veya query builder gibi) kullanın.
❌ Uyumsuz örnek
// 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;
},
};✅ Kurala Uygun Örnek
// src/api/user/services/user.js
module.exports = {
async findActiveUsers() {
return await strapi.db.query('api::user.user').findMany({
where: { active: true },
});
},
};11. Strapi'nin sorgu motorunu tutarlı bir şekilde kullanın
Aynı özellik içerisinde farklı veritabanı erişim yöntemlerini (ör. ORM çağrıları ile ham sorgular) bir arada kullanmayın. Sürdürülebilirliği, okunabilirliği ve öngörülebilir davranışları garanti etmek için tek ve tutarlı bir sorgu yaklaşımı benimseyin.
❌ Uyumsuz örnek
// 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 };
},
};✅ Kurala Uygun Örnek
// src/api/order/services/order.js
module.exports = {
async getPendingOrders() {
return await strapi.db.query('api::order.order').findMany({
where: { status: 'pending' },
});
},
};12. Veritabanı çağrılarını optimize edin
Performans darboğazlarını önlemek ve gereksiz ardışık çağrıları azaltmak için ilgili veritabanı sorgularını toplu halde (batch) çalıştırın veya tek bir işlemde birleştirin.
❌ Uyumsuz örnek
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;
}✅ Kurala Uygun Örnek
async function getArticlesWithAuthors() {
return await db.query('articles').findMany({ populate: ['author'] });
}Kurallar: API ve Güvenlik
13. Girdileri Strapi validatörleri ile doğrulayın
İstemcilerden veya harici kaynaklardan gelen hiçbir girdiye körü körüne güvenmeyin. Controller, service veya veritabanı işlemlerinde kullanmadan önce tüm gelen verileri tutarlı bir doğrulama mekanizmasından geçirin.
❌ Uyumsuz örnek
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);
}✅ Kurala Uygun Örnek
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. Kullanıcı girdilerini kaydetmeden önce sanitize edin
Veritabanına kaydetmeden veya diğer sistemlere iletmeden önce tüm girdileri sanitize edin.
❌ Uyumsuz örnek
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);
}✅ Kurala Uygun Örnek
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. Yetki kontrollerini zorunlu kılın
Yalnızca yetkili kullanıcıların erişebilmesini sağlamak için korumalı her rotaya yetki kontrolü uygulayın.
❌ Uyumsuz örnek
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 });
}✅ Kurala Uygun Örnek
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. Boom ile tutarlı hata yönetimi
Merkezi veya birleştirilmiş bir hata yönetimi mekanizması kullanarak tüm API rotalarında hataları tutarlı bir şekilde ele alın.
❌ Uyumsuz örnek
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
}
}✅ Kurala Uygun Örnek
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 };Kurallar: Test ve dokümantasyon
17. Her özellik için test ekleyin veya güncelleyin
Test edilmeyen yeni kodlar merge edilmez; testler "tamamlandı" tanımının bir parçasıdır.
❌ Uyumsuz örnek
// 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✅ Kurala Uygun Örnek
// 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. Yeni uç noktaları (endpoint) belgelendirin
Her API eklemesi, merge işleminden önce referans dokümanlarında belgelendirilmelidir.
❌ Uyumsuz örnek
// 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✅ Kurala Uygun Örnek
// 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 };
},
};Referans Dokümanı Güncelleme Örneği:
### POST /users/deactivate
**Request Body:**
```json
{
"userId": "string"
}Yanıt:
{
"success": true
}Hatalar:
- 400: userId alanı zorunludur
- 404: Kullanıcı bulunamadı
Neden işe yarıyor:
- Geliştiriciler ve API tüketicileri uç noktaları güvenilir şekilde keşfedebilir ve kullanabilir
- Uygulama ile dokümantasyon arasında tutarlılık sağlar
- Bakım ve ekibe adaptasyon (onboarding) süreçlerini kolaylaştırır
---
Bir sonraki aşamada **#19 numaralı kural (“Paylaşılan Yardımcı Araçlar İçin JSDoc Kullanın”)** ile aynı formatta devam etmemi ister misiniz?19. Paylaşılan yardımcı araçlar için JSDoc kullanın
Ekibe adaptasyonu ve iş birliğini kolaylaştırmak için paylaşılan fonksiyonlar JSDoc ile açıklanmalıdır.
❌ Uyumsuz örnek
// src/utils/slugify.js
function slugify(text) {
return text.toLowerCase().trim().replace(/\s+/g, '-');
}
module.exports = slugify;✅ Kurala Uygun Örnek
// 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. Önemli her PR ile birlikte değişiklik günlüğünü (changelog) güncelleyin
Bir PR merge edilmeden önce projenin değişiklik günlüğünü (changelog) her önemli özellik, hata düzeltmesi veya API değişikliği ile güncelleyin.
❌ Uyumsuz örnek
# CHANGELOG.md
## [1.0.0] - 2025-09-01
- İlk sürüm✅ Kurala Uygun Örnek
# CHANGELOG.md
## [1.1.0] - 2025-10-06
- Kullanıcı devre dışı bırakma uç noktası eklendi (`POST /users/deactivate`)
- Makale başlıkları için slug oluşturmadaki bir hata giderildi
- Toplu gönderimleri yönetecek şekilde e-posta bildirim servisi güncellendiSonuç
Tutarlı kod kalıplarının büyük açık kaynaklı projelerin kaliteden ödün vermeden büyümesine nasıl yardımcı olduğunu anlamak için Strapi'nin resmi deposunu inceledik. Bu 20 kural birer teoriden ibaret değil. Doğrudan Strapi'nin kod tabanından alınan ve projeyi bakımı daha kolay, daha güvenli ve okunabilir kılan pratik derslerdir.
Projeniz büyüyorsa bu dersleri alın ve kod incelemelerinize (code review) uygulayın. Düzensiz kodları temizlemekle daha az, gerçekten önem taşıyan özellikler geliştirmekle daha çok zaman geçirmenize yardımcı olacaktır.

