Code that changes together should be moved closer together.
It may remind you of the Common Closure Principle, or single responsibility principle from SOLID. It's not the same thing and it better than both of them.
Common Closure Principle is the proximity principle—backwards. It's hard to apply, and often leads to overcopmlicated code. Instead of planning what is going to chang with what it's better to watch it after some some passes and refactor. I wouldn't say it's false, but it's better to focus on the Proximity Principle.
Single Responsibility Principle is a nebulous construct. Maybe it can apply to pure functions, but not to anything else—especially not classes. Think about it—your class can be instantiated and handles some business logic. Those are two things. If you want to argue that that it doesn't count then think of a clear list of things that do count. Logging. Metrics. Annotations to prevent unauthorized requests. What's worst it leads to code that violates The Proximity Principle. With your misaligned quest for objects to have one responsibility you end up with code that single business change has to be made all over the place and you argue that's a good thing.
The principle of proximity focuses on how well organized your code is with respect to readability and change. Proximity implies that functions that are changed together are moved closer together. Proximity is both a design principle and a heuristic for refactoring hotspots toward code that's easier to understand.
— Software Design X-Rays by Adam Tornhill
Let's undarstand why this is not just an opinion.
Humans learn (and get mastery) thanks to the process called Chunking.
In cognitive psychology, chunking is a process by which individual pieces of an information set are broken down and then grouped together. A chunk is a collection of basic familiar units that have been grouped together and stored in a person's memory.
— Software Design X-Rays by Adam Tornhill
So good groups can help you familiarize yourself with big codebase. Project structure makes a difference, and most of programmers know about. That's the reason why often quote:
There are only two hard things in Computer Science: cache invalidation and naming things.
— Phil Karlton
What about proximity? It's one of Principles of grouping
The Gestalt law of proximity states that "objects or shapes that are close to one another appear to form groups". Even if the shapes, sizes, and objects are radically different, they will appear as a group if they are close.
— Wikipedia: Principles of grouping
If you're not convinced that graphic design principles are important for code organization then you have to think about biological interpreters of code—humans.
Keep the code that changes together close to each other #
- Keep code that changes together in one file
- Keep files that change together in the same directory
Let's see an example of
Button React Component:
📁/Button/ 📄 Button.js 📄 Button.test.js 📄 Button.css 📄 Button.story.js
Common criticism #
I've looked for the criticism of keeping tests in the same directory as source files.
Best practice #
Best I could find are people calling it a
best practice without any other reasoning other than most projects do this.
Even when I think it's a terrible layout I can see a value in common practice. It can help get new contributors for an open source project. Onboarding can be easier if people are used to it.
But, I would still sacrifice that for the ease of working with tests that colocation provides.
Test 'pollute' real code #
Your source code is not 'polluted' with code that is not related to actual business logic. Mind that it is not so uncommon to have more than one test file for the same class/module.
— source: Should a test file be placed in the same folder as the source file? [closed]
It already has a good rebuttals on StackOverflow, but I want to emphasise it. Tests are your code. If you think your tests are polluting list of your files then it means that there is something wrong with your tests. Solution is to either delete them or rewrite so they add value to the project. Placing them in a separate directory is like sweeping them under the rug.
Builds are faster or easier #
Mixing test and source filer makes it harder to build and deploy code. But, is it true?
In Java you have to duplicate the directory structure between your code and tests to keep package access to your classes. Every time you want to move something you have to make the same change in two places. It would be easier to keep to refactor code if they were kept in one directory. Yes, your IDE is doing that for you, but IDEs are Code Smell
More reading: #
- Why I Recommend Unit Tests in the Src Folder by Jim Lynch
- Java unit tests, directory layout [closed]