Aikido

Why you should use one class per file: improving code organization and navigation

Readability

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.

FAQs

Got Questions?

What about small helper classes or private classes?

Small helper classes used only by one parent class can stay in the same file if they're truly implementation details. However, if helpers are reused or grow beyond 20-30 lines, extract them. Private classes that exist only to support one public class are reasonable exceptions.

Does this apply to TypeScript interfaces and types?

Types and interfaces used by a class can live in the same file. However, shared types used across multiple files belong in their own type definition files. The key is whether the definition is used only by that file or needed elsewhere.

What about languages like Python with multiple classes in standard practice?

Python conventions differ, but the principle still applies: group related classes if they form a cohesive module, but avoid mixing unrelated classes. A models.py with User and UserProfile makes sense. A utils.py with twenty unrelated classes doesn't.

How do I organize related classes like parent and child classes?

Put them in separate files within the same directory. For Animal and Dog, use animals/Animal.js and animals/Dog.js. Directory structure shows relationships while maintaining one class per file. This scales better than bundling related classes in one file.

What if extracting classes creates many small files?

Many small files are preferable to few large files. Modern IDEs handle thousands of files efficiently. File system navigation is faster than searching within large files. The clarity of knowing exactly where each class lives outweighs the perception of "too many files."

Get secure for free

Secure your code, cloud, and runtime in one central system.
Find and fix vulnerabilities fast automatically.

No credit card required | Scan results in 32secs.