Rule
One class per file.
Multiple classes in a single file make code
organization unclear and harder to navigate.
Supported languages: 45+Introduction
Putting multiple classes in a single file makes it difficult to locate specific classes when navigating a codebase. Developers searching for UserRepository won't find it quickly if it's buried in a file named database.js alongside five other classes. This violates the principle of least surprise and slows down development as team members waste time hunting for class definitions.
Why it matters
Code maintainability: Multiple classes per file create unclear boundaries between responsibilities. When one class needs modification, developers must open a file containing unrelated classes, increasing cognitive load and the risk of accidentally modifying wrong code.
Navigation and discoverability: IDEs and text editors struggle to provide accurate "go to definition" when multiple classes share a file. Developers spend time searching within files rather than jumping directly to the class they need. This compounds in large codebases with hundreds of classes.
Version control conflicts: When multiple classes share a file, changes to different classes by different developers create merge conflicts. Separate files enable parallel development without coordination overhead, as each developer works in their own file.
Code examples
❌ Non-compliant:
// database.js
class UserRepository {
async findById(id) {
return db.users.findOne({ id });
}
}
class OrderRepository {
async findByUser(userId) {
return db.orders.find({ userId });
}
}
class ProductRepository {
async findInStock() {
return db.products.find({ stock: { $gt: 0 } });
}
}
module.exports = { UserRepository, OrderRepository, ProductRepository };
Why it's wrong: Three unrelated repository classes in one file named database.js. Searching for OrderRepository requires knowing it's in database.js rather than OrderRepository.js. File changes affect multiple classes, creating unnecessary merge conflicts.
✅ Compliant:
// UserRepository.js
class UserRepository {
async findById(id) {
return db.users.findOne({ id });
}
}
module.exports = UserRepository;
// OrderRepository.js
class OrderRepository {
async findByUser(userId) {
return db.orders.find({ userId });
}
}
module.exports = OrderRepository;
// ProductRepository.js
class ProductRepository {
async findInStock() {
return db.products.find({ stock: { $gt: 0 } });
}
}
module.exports = ProductRepository;
Why this matters: Each class in its own file makes navigation predictable. IDEs can jump directly to OrderRepository.js when searching for the class. Changes to one repository don't affect others, eliminating unnecessary merge conflicts.
Conclusion
Name files after the class they contain for predictable navigation. This convention scales to large codebases where finding specific classes quickly matters. The extra files are worth the organizational clarity they provide.
.avif)
