Designing Productive Codebase Structure

When starting a new project, determining the optimal project structure has often been a challenging task for me, especially due to its significance in enhancing the Developer Experience (DX). Over recent years, I’ve experimented with various structuring methods to streamline the project initiation process.
Throughout my years of experience, I’ve consistently noticed that many projects suffer from a lack of effective structure. This issue manifests differently in each project, with a notable problem being the absence of a common language among stakeholders. This lack of uniformity can spread misconceptions throughout the project, affecting even its smallest components. It’s important to understand that a software project’s success extends beyond merely utilizing cutting-edge technologies or tools. However, this is just a brief overview of the challenges — I won’t delve deeper into this topic unless the scope of the article permits further exploration. This discussion is primarily to set the context for the article’s main idea.
Developer Experience
The idea was well defined in the article Developer-experience
Developer experience refers to how easy or difficult it is for a developer to perform essential tasks needed to implement a change. A positive developer experience would mean these tasks are relatively easy for the team.
…
Impact
Developer experience can have a significant impact on the efficiency of the day-to-day execution of the team. A positive experience can pay dividends throughout the lifetime of the project; especially as new developers join the team.
Increased Velocity — Team spends less time on non-value-add activities such as dev/local environment setup, waiting on remote environments to test, and rework (fixing defects).
Improved Quality — When it’s easy to debug and test, developers will do more of it. This will translate to fewer defects being introduced.
Easier Onboarding & Adoption — When dev essential tasks are automated, there is less documentation to write and, subsequently, less to read to get started!
Key aspects such as understanding and conceptualizing the product, finding and fixing bugs efficiently, implementing and estimating new features, onboarding new developers, and enhancing user experience are all directly related to the Developer Experience (DX) provided by the project.
In order to force the structure more productive, we should insist at evolutionary changes regularly because changes start with a basic reaction that comes from somewhere in the system. Moreover every system must be changed at some point and if the system doesn’t evolve enough by that time, there will be consequences. Besides, we shouldn’t forget that mutations might cause cancers and some corruptions in the system which means we should be careful but this shouldn’t discourage us. Evolution doesn’t guarantee to survive; it just increases chance to survive. So as to cultivate efficient products as it is in centuries we still have used artificial selection while engaging natural systems.
The structure of any system designed by an organization is isomorphic to the structure of the organization.[6]
Essentials Of The Project Structure
What the project structure should involve:
1- Comprehensible: Understanding elements of the project easily. 2- Accessible: Reaching the source particular code of a relevant issue or a new feature out quickly. 3- Extendable: Extending or refactoring codebase fast enough using convenient patterns. 4- Scalable: Implementing reusable and loosely coupled components.
In the article, I aim to explain a pattern that simplifies the mapping of codebases without overlooking the aforementioned concepts. We use patterns because they serve as frameworks for understanding irregular information. The patterns are prevalent in everyday life, aiding in tasks such as recognizing faces, composing poetry, and solving problems. By emulating real-life organizations, like natural organisms, we enhance the efficiency of our tools, thereby reducing entropy.
Refactoring is another method of abstraction. It not only aids in maintaining relevance between information and its level of abstraction but also combats entropy in organizations. This is akin to the fundamental principle of survival, which involves adapting and reorganizing to thrive. Similarly, software must evolve to survive. To resist entropy and adapt to new conditions, it should be consistently reorganized.
I will employ programming principles such as Dependency Inversion and SOLID to develop a folder organization. This approach will aid in maintaining loosely coupled relationships between components.
The Abstraction Of Organisation
To categorize and conceptualize an organization, we employ the concept of Abstraction. Abstraction is a significant concept across various fields, enabling us to organize systems by transferring information or knowledge from one context to another. This process is instrumental in the invention of ideas, tools, and technologies. Abstraction is more than just extracting a function or being a principle of Object-Oriented Programming (OOP); it’s a fundamental method of understanding and structuring complex entities.
Abstraction typically organizes concepts in the following ways:
- from high-level to low-level,
- from simple to complex
- from abstract to concrete
- from general to individual
This organization makes irregular phenomena comprehensible, allowing us to perceive relationships between seemingly unrelated systems within a domain. It facilitates the transformation of information from one context to another and aids in sanitizing the codebase. Additionally, abstraction involves concealing specific information behind a certain level of abstraction. In this context, encapsulation is also a method of the abstraction.
Refactoring is another method of the abstraction. It not only aids in maintaining relevance between information and its level of abstraction but also combats entropy in organizations. This is akin to the fundamental principle of survival, which involves adapting and reorganizing to thrive. Similarly, software must evolve to survive. Furthermore, so as to resist against the entropy, it needs to adapt to new conditions and to increase efficiency of its structure.
Elements of The Code Structure
We use so many components, layers, modules, packages etc. throughout the software development. Organisation of the structure should insist to organize informations in implicit modules to protect system against erosions of internal and external forces like developers, stakeholders, users, designers, investors, 3rd parties.
Dependency Inversion principle guide us about how to achieve that. It says a low level component mustn’t share information with the higher level component or inherit behaviours from them. Basically it means unless components are in less abstract level, they can import details from more abstract level components as well. Additionally, components in the same abstraction level shouldn’t share any information among them as well if not, this mostly causes circular dependencies and tight coupling which breaks modularity.
If we want to basically order components by abstraction level in a web application they look like below :
Abstract Level (More General):1. Core Modules: Includes generic interfaces, constants, and utilities. These can also be shared across different applications within the same domain.
2. Database Modules: Covers ORM (Object-Relational Mapping) and entity definitions.
3. Application Modules: Encompasses Application API and configuration settings.
4. Generic Utility Modules: Examples include form managers and task managers.
5. Generic Primitive UI Components: Basic user interface elements like buttons and form elements.
6. Generic Widgets: For more complex UI components.
Feature Modules:
1- Feature-specific components.
2- Feature-centric data modules.
Other feature-related elements.
1- Pages: This includes screens and views that constitute the user interface.Concrete Level (More Specific):According to the above a feature module shouldn’t import details from pages, although pages are allowed to do so. This hierarchy helps in easily identifying the generalization level of a module. Furthermore, it aids in determining the appropriate folder for each module. This is particularly useful when breaking down details into new or existing modules during refactoring, especially as they gradually increase in complexity over time.
The Codebase Structure
As a result, I recommend methodizing codebase for applications in a way that promotes immediate comprehension, inherent scalability, modularity, and enhanced productivity. This approach is similar to the vertical slices architecture, where every feature slices through the layers of a multi-layered application. In this model, each feature encapsulates every detail within its own scope, instead of being distributed across different layers, ensuring that features do not share details unnecessarily.
# Software Project Directory Structure## `/src`
Root directory for source code.### `/core`
Contains generic types and constants.### `/application`
Holds application-specific configurations and context APIs.
- `configuration` (includes environment variables)
- `application context api`
- `getConfiguration`
- `Timezone`
- `Theme`### `/data`
Includes generic data modules and API services.### `/ui`
Contains generic core UI components.### `/shared`
Comprises complex, generic components that utilize UI components and others.### `/modules` or `/features`
Feature-specific directories.
- For each feature module (e.g., `(feature-name)-{module-name}`):
- `views`
- `components`
- `services`Let’s imagine an application in the school domain. it might have been like. Another important point is that should be avoided unnecessary nested folders to enhance the accessibility and ease of navigation :
# Software Project Directory Structure## `/src`
Root directory for source code.### `/core`
Contains high-level, domain-specific details.
- Useful for generalizing common parts across applications in the same domain.
- Can share domain-specific details in a mono-repo using this module.
- Subdirectories:
- `utils`
- `enums`
- `constants`### `/application`
Holds shared details of the application.
- Focuses on application-specific details only.
- Contains:
- `Configuration`
- `constants`### `/ui` Comprises basic UI components. - `Button` - `Input`
### `/utilities`
Includes various utility functions and components.
- `/form`
- `IFormElement`
- `useForm`
- `FormComponent`
- `FormInput`
- `/task-manager`### `/layout`
Contains generic layout components.### `/domain`
Encompasses shared details of the domain.
- Used by various features and does not include feature-specific details.
- Subdirectories for different domains:
- `/auth`
- `useLoginService`
- `useAuthInfo`
- `/user`
- `IUser`
- `IStudent`
- `ITeacher`### `/features`
Feature-specific modules or slices.
- Each feature has its own subdirectory, e.g.,
- `/auth-module`
- `/auth-login`
- `/auth-signup`
- `/student-module`
- `/student-detail`
- `/student-registration`
- `/teacher-module`
- `/teacher-detail`
- `/teacher-management`
- `/school-module`
- `/school-meetings`
- `/school-lunch-menu`
- `/school-information`
- `/school-contact`
- These include routes, views, services, components, utils specific to each feature.Conclusion
Developer Experience is often an overlooked concept in the application lifecycle, especially when dealing with the legacy code. Unless the needs of all stakeholders are equally involved, we cannot enhance the quality because the software is also the organization of things which inherently struggles against high entropy.
We should gradually enhance those annoying aspects that delay the quick identification and resolution of issues. There are no silver bullets for this; therefore, we must observe events within the organization and evolve by avoiding excessive and unnecessary changes.






