
Giriş
Strapi, en popüler açık kaynaklı başsız CMS platformlarından biridir; ancak aynı zamanda yüzlerce katkıcıya ve binlerce çekme isteğine sahip devasa bir kod tabanıdır. Böylesine büyük bir projenin kalitesini yüksek tutmak kolay değildir. Her katkının güvenilir, okunabilir ve güvenli olmasını sağlamak için net ve tutarlı kod inceleme kuralları gereklidir.
Bu makalede, Strapi’nin herkese açık deposunu temel alarak bir dizi kod inceleme kuralı derledik. Bu kurallar, kod tabanını istikrarlı tutarken projenin büyümesine katkıda bulunan gerçek sorunlar, tartışmalar ve çekme isteklerinden elde edilmiştir.
Büyük bir açık kaynak projesinde kod kalitesini korumak neden zordur?
Büyük bir açık kaynak projesinde kaliteyi korumak, projenin muazzam boyutu ve katkıların çeşitliliği nedeniyle zorlu bir iştir. Gönüllülerden deneyimli mühendislere kadar yüzlerce, hatta binlerce geliştirici, her biri yeni özellikler, hata düzeltmeleri veya yeniden yapılandırmalar içeren çekme istekleri gönderir. Net kurallar olmadan kod tabanı hızla tutarsız, kırılgan veya gezinmesi zor bir hale gelebilir.
Başlıca zorluklardan bazıları şunlardır:
- Farklı deneyim düzeylerine sahip çeşitli katılımcılar.
- Modüller arasında tutarsız kodlama kalıpları.
- Gizli hatalar ve tekrarlanan mantıklar yavaş yavaş ortaya çıkıyor.
- Süreçlerin uygulanmaması durumunda ortaya çıkabilecek güvenlik riskleri.
- Kod tabanının tamamına aşina olmayan gönüllüler için zaman alıcı inceleme süreçleri.
Bu zorlukların üstesinden gelmek için başarılı projeler, ortak standartlar, otomatikleştirilmiş araçlar ve net kılavuzlar gibi yapılandırılmış süreçlere dayanır. Bu uygulamalar, proje büyüdükçe ve daha fazla katılımcıyı kendine çektiğinde bile bakım kolaylığı, okunabilirlik ve güvenliği garanti eder.
Bu kurallara uymak, bakım kolaylığını, güvenliği ve yeni kullanıcıların sisteme alışma sürecini nasıl iyileştirir?
Net bir kod inceleme kuralları dizisine uymak, projenizin sağlığı üzerinde doğrudan bir etkiye sahiptir:
- Bakım Kolaylığı: Tutarlı klasör yapıları, adlandırma kuralları ve kodlama kalıpları, kod tabanının okunmasını, içinde gezinilmesini ve genişletilmesini kolaylaştırır.
- Güvenlik: Giriş doğrulama, verilerin temizlenmesi, izin kontrolleri ve kontrollü veritabanı erişimi, güvenlik açıklarını azaltır ve kazara veri sızıntılarını önler.
- Daha Hızlı Uyum Süreci: Ortak standartlar, belgelenmiş yardımcı araçlar ve net örnekler, yeni katkıcıların projeyi hızlı bir şekilde anlamasına ve kendinden emin bir şekilde katkı sağlamasına yardımcı olur.
Bu kuralları uygulayarak, ekipler katkıda bulunanların sayısı arttıkça bile kod tabanının ölçeklenebilir, güvenilir ve güvenli kalmasını sağlayabilirler.
Bağlam ile kurallar arasında köprü kurmak
Kurallara geçmeden önce, Strapi gibi bir projede kod kalitesini yüksek tutmanın sadece genel en iyi uygulamalara uymaktan ibaret olmadığını anlamak önemlidir. Bu, yüzlerce katkıcıya aynı çizgide kalmalarına yardımcı olacak 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 verilen örnekler, hem kurallara uymayan hem de uyan yaklaşımları göstererek, bu ilkelerin pratikte nasıl uygulandığını net bir şekilde ortaya koymaktadır.
Şimdi, proje yapısı ve yapılandırma standartlarından başlayarak, Strapi’nin kod tabanını ölçeklenebilir, tutarlı ve yüksek kaliteli kılan kuralları inceleyelim.
Kurallar: Proje yapısı ve tutarlılık
1. Strapi’nin belirlenmiş klasör kurallarına uyun
Dosyaları dağınık hale getirmekten veya yeni yapılar oluşturmaktan kaçının. Gezinmeyi öngörülebilir tutmak için Strapi’nin yerleşik proje düzenine sadık kalın.
❌ Kurallara uymayan örnek
1src/
2├──denetleyiciler/
3│ └── userController.js
4├──hizmetler/
5│ └── userLogic.js
6├──rotalar/
7│ └── userRoutes.js
8└──utils/
9 └── helper.js✅ Kurallara uygun örnek
1src/
2└──api/
3 └── user/
4 ├── denetleyiciler/
5 │ └── user.js
6 ├── hizmetler/
7 │ └── user.js
8 ├── rotalar/
9 │ └── user.js
10 └── içerik-türleri/
11 └── user/schema.json2. Yapılandırma dosyalarının tutarlılığını koruyun
Tutarlılığı sağlamak ve hataları önlemek için tüm yapılandırma dosyalarında aynı yapı, adlandırma ve biçimlendirme kurallarını kullanın.
❌ Kurallara uymayan ö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});✅ Kurallara 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. Sıkı tip güvenliği sağlamak
Tüm yeni veya güncellenen kodlar, doğru TypeScript türlerini veya JSDoc tanımlarını içermelidir. Paylaşılan modüllerde tür belirtmemekten, eksik dönüş türlerinden veya örtük tür çıkarımından kaçının.
❌ Kurallara uymayan örnek
1// src/api/user/services/user.ts
2export const createUser = (data) => {
3 return strapi.db.query('api::user.user').create({ data });
4};✅ Kurallara 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. Hizmetler ve denetleyiciler için tutarlı adlandırma
Denetleyici ve hizmet adları, ilgili etki alanlarıyla açıkça eşleşmelidir (örneğin, user.controller.js ile user.service.js).
❌ Kurallara uymayan örnek
1src/
2└── api/
3 └── kullanıcı/
4 ├── denetleyiciler/
5 │ └── anaDenetleyici.js
6 ├── hizmetler/
7 │ └── hesapHizmeti.js
8 ├── yollar/
9 │ └── kullanıcı.js✅ Kurallara uygun örnek
1src/
2└── api/
3 └── kullanıcı/
4 ├── denetleyiciler/
5 │ └── kullanıcı.js
6 ├── hizmetler/
7 │ └── kullanıcı.js
8 ├── yollar/
9 │ └── kullanıcı.js
10 └── içerik-türler/
11 └── kullanıcı/şema.jsonKurallar: Kod kalitesi ve bakım kolaylığı
5. Erken dönüşlerle kontrol akışını basitleştirin
Derin if/else iç içe geçme yerine, koşullar karşılanmadığında erken dönüş yapın.
❌ Kurallara uymayan ö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};✅ Kurallara 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. Denetleyicilerde aşırı iç içe geçme durumlarından kaçının
Denetleyiciler veya hizmetler içinde iç içe geçmiş büyük mantık bloklarından kaçının. Tekrarlanan veya karmaşık koşulları, uygun şekilde adlandırılmış yardımcı işlevlere veya yardımcı programlara ayırın.
❌ Kurallara uymayan ö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};✅ Kurallara 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ı denetleyicilerden uzak tutun
Denetleyiciler yalın kalmalı ve yalnızca istekleri koordine etmelidir. İş mantığını hizmetlere taşıyın.
❌ Kurallara uymayan ö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};✅ Kurallara 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. Tekrarlanan desenler için yardımcı fonksiyonları kullanın
Tekrarlanan kalıplar (örneğin, doğrulama, biçimlendirme) paylaşılan yardımcı programlarda yer almalıdır.
❌ Kurallara uymayan ö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 } });
},
};✅ Kurallara uygun örnek
// src/utils/slugify.js
modül.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. Üretim ortamına geçmeden önce hata ayıklama günlüklerini silin
Üretim ortamındaki kodda console.log, console.warn veya console.error kullanmayın. Günlük kayıtlarının ortam ayarlarına uygun olmasını sağlamak ve hassas bilgilerin ifşa edilmesini önlemek için her zaman strapi.log’u veya yapılandırılmış bir günlük kaydediciyi kullanın.
❌ Kurallara uymayan ö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;
},
};✅ Kurallara 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 uygulamaları
10. Ham SQL sorgularından kaçının
Denetleyicilerde veya hizmetlerde ham SQL sorguları çalıştırmayın. Bakım kolaylığını sağlamak, kuralları/kancaları uygulamak ve güvenlik risklerini azaltmak için her zaman tutarlı, üst düzey bir sorgu yöntemi (örneğin bir ORM veya sorgu oluşturucu) kullanın.
❌ Kurallara uymayan ö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;
},
};✅ Kurallara 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çinde farklı veritabanı erişim yöntemlerini (örneğin, ORM çağrıları ile ham sorgular) birbirine karıştırmayın. Bakım kolaylığı, okunabilirlik ve öngörülebilir davranış sağlamak için tek ve tutarlı bir sorgu yaklaşımı kullanın.
❌ Kurallara uymayan ö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 };
},
};✅ Kurallara 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 sıralı çağrıları azaltmak için veritabanı sorgularını gruplandırın veya tek bir işlemde birleştirin.
❌ Kurallara uymayan ö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;
}✅ Kurallara uygun örnek
async function getArticlesWithAuthors() {
return await db.query('articles').findMany({ populate: ['author'] });
}Kurallar: API ve Güvenlik
13. Strapi doğrulayıcılarıyla girdileri doğrulayın
Müşterilerden veya harici kaynaklardan gelen girdilere asla güvenmeyin. Gelen tüm verileri, denetleyicilerde, hizmetlerde veya veritabanı işlemlerinde kullanmadan önce tutarlı bir doğrulama mekanizması kullanarak doğrulayın.
❌ Kurallara uymayan ö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);
}✅ Kurallara 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. Kaydetmeden önce kullanıcı girdisini temizleyin
Verileri veritabanına kaydetmeden veya diğer sistemlere aktarmadan önce tüm giriş verilerini temizleyin.
❌ Kurallara uymayan ö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);
}✅ Kurallara 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. İzin denetimlerini uygulayın
Yalnızca yetkili kullanıcıların erişebilmesini sağlamak için, korunan her rotada izin denetimleri uygulayın.
❌ Kurallara uymayan ö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 });
}✅ Kurallara 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 işleme
Merkezi veya birleştirilmiş bir hata işleme mekanizması kullanarak tüm API rotalarında hataları tutarlı bir şekilde işleyin.
❌ Kurallara uymayan ö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
}
}✅ Kurallara 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 belgeleme
17. Her özellik için testler ekleyin veya güncelleyin
Testleri olmayan yeni kod birleştirilmeyecektir; testler, “tamamlanma” tanımının bir parçasıdır.
❌ Kurallara uymayan ö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✅ Kurallara 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ı belgelendirin
Her API eklemesi, birleştirme işleminden önce referans belgelerinde belgelenmelidir.
❌ Kurallara uymayan ö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✅ Kurallara 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 Belgeleri Güncelleme Örneği:
### POST /users/deactivate
**Request Body:**
```json
{
"userId": "string"
}Yanıt:
{
"success": true
}Hatalar:
- 400: userId bilgisi zorunludur
- 404: Kullanıcı bulunamadı
Bunun neden işe yaradığı:
- Geliştiriciler ve API kullanıcıları, uç noktaları güvenilir bir şekilde bulabilir ve kullanabilir
- Uygulama ile belgeler arasında tutarlılık sağlar
- Bakım ve yeni kullanıcıların sisteme alışmasını kolaylaştırır
---
**Kural #19 (“Paylaşılan Yardımcı Programlar için JSDoc Kullanın”)** ile aynı formatta devam etmemi ister misiniz?19. Ortak yardımcı programlar için JSDoc'u kullanın
Ortak işlevler, yeni başlayanların işlere alışmasını ve işbirliğini kolaylaştırmak amacıyla JSDoc ile açıklanmalıdır.
❌ Kurallara uymayan örnek
// src/utils/slugify.js
function slugify(text) {
return text.toLowerCase().trim().replace(/\s+/g, '-');
}
module.exports = slugify;✅ Kurallara 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. Her önemli PR ile değişiklik günlüğünü güncelleyin
Bir PR’yi birleştirmeden önce, projenin değişiklik günlüğünü her önemli özellik, hata düzeltmesi veya API değişikliğiyle güncelleyin.
❌ Kurallara uymayan örnek
# CHANGELOG.md
## [1.0.0] - 2025-09-01
- İlk sürüm✅ Kurallara 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ının kısaltma oluşturulmasında bulunan hata düzeltildi
- Toplu gönderimi destekleyecek şekilde e-posta bildirim hizmeti güncellendiSonuç
Kaliteden ödün vermeden büyük açık kaynaklı projelerin nasıl büyüyebileceğini anlamak için Strapi’nin halka açık kod deposunu inceledik. Bu 20 kural teorik değildir. Bunlar, projenin bakımını kolaylaştıran, güvenliğini artıran ve okunabilirliğini geliştiren, doğrudan Strapi’nin kod tabanından çıkarılan pratik derslerdir.
Projeniz büyüyor ise, bu derslerden yararlanarak kod incelemelerinize uygulayın. Bu sayede, dağınık kodları düzeltmek için daha az zaman harcayıp, gerçekten önemli olan özellikleri geliştirmek için daha fazla zaman ayırabilirsiniz.

