For the complete documentation index, see llms.txt. This page is also available as Markdown.

Folder structure

The context

According to our first principle, the codebase should primarily reflect the business domain. Frameworks and libraries are secondary considerations - implementation details.

Folders and files are organized first around the entities that are manipulated. Proper names convey the purpose of the application: what can I do with it?

One common problem is to structure code according to a framework or library's requirements. It is almost always a mistake. Such solutions mask the real purpose of the module or application, while also couple the whole codebase to a specific technology.

Another mistake is to organize source files by type, for example, to have a dedicated folder for all images, another for CSS, components, resolvers, etc.

Every module should reflect the entities it works with as top-level folders - this is the work context. When there is a hierarchy, we use subfolders. When there is no more domain-driven context to talk of, a technology-driven grouping can happen.

The above also means that context-free group names like "utils" or "helpers" are pointless and are strongly discouraged. They are not domain-driven, not technology-driven and don't convey meaning. There is always a better name than these.

Another consequence of context-driven grouping is that the test, mock and all supporting files live next to the source files (co-location). Co-locating makes clear if there is a test for a module and avoids creating a shadow folder-tree.

More importantly, all resources are at hand when working in a specific context. The developer can see the source, the tests and any relevant assets. There is no need to open files from multiple, far-away directories, and there is no guessing. It is obvious where to implement changes.

A regular question is where to store UI components that are shared across all domain types, for example, buttons, typography. Since these don't have a domain context, they can live in a group named base, baseComponents. components has the danger of luring every component in, even context-specific ones, so it's not the best choice.

Index files

Do not add index files to folders. This practice has several drawbacks:

  • It needs to be done manually, which is a drain on attention and energy (no matter how small it appears)

  • It's just another thing to remember

  • Makes refactoring harder, as you'll need to edit the re-exports

  • Makes moving modules around harder

  • It seems like a cosmetic change

  • It is a code smell

Making refactoring harder usually means it never happens, which is far from ideal.

In any case, when using correct grouping, the full path to a module is usually pretty short.

The only exception to this rule is when creating a local or public npm package: the module's top-level exports are best exposed using an index file so clients can import without mentioning a path (which is an implementation detail to be hidden). For this reason, our domain, service etc. modules will have a top-level index file, but only one and it is used only from the outside.

Example

Here is an example for confusing and a clear grouping of source files:

A good example

Hint: you can use the "1 or more" rule to decide if something needs to go into a subfolder. If there is only one "thing", the file can live where it is (e.g. sortMarbles.ts above). If there is two or more, put them in a subfolder (e.g. marble/sortMarbles.ts)

Last updated