The Cantankerous Coder

Share this post
Micro-app folder/file hierarchy
www.cantankerouscoder.com

Micro-app folder/file hierarchy

The best way to organize your (front end) source code

Chas.
Jul 14, 2022
1
Share this post
Micro-app folder/file hierarchy
www.cantankerouscoder.com

I’ve seen a lot of different ways of organizing source code files and folders over the past two decades or so. Along the way, I picked up a few patterns that have worked very well for me. I continue to improve the system, but, frankly, the improvements have become less frequent and smaller.

I think I might be nearing perfection! Heh.

The file is named “index”

One thing that I adopted very early on—so early that it was back when all my files ended with .html—is to name the default file in the folder “index”.

The immediate benefit of this back in the day, and I can’t believe I’m admitting this publicly, is that if I wanted to add superpowers to the file, I could simply rename index.html to, cough, cough, index.php. And it just worked.

We can just pretend that I never said that. But it worked just as well later when it became index.rb and then index.scala and finally index.ts.

Wait, what? Then how does one name the components if every file is called index?

The name of the component, utility function, whatever goes on the folder, naturally:

App/
  index.ts
  index.module.css
  index.test.ts

Benefit one: none of this nonsense:

import App from "./App/App"

It’s just:

import App from "./App"

The other great benefit of this is that we can colocate all the files that relate to that component/function in the same folder.

That means we don’t have a separate tests or __tests__ folder somewhere far from our component. Or a stories folder.

It means that I can tell at a glance if this component has CSS associated with it, is tested, has Storybook stories, etc. Because the files are right there.

It’s all right there in the folder!

So that’s the key to all this:

Keep things that belong together… together. Who knew?

Everyone wants to belong

Another benefit of this is that I can close up those folders, so instead of seeing this monstrosity:

What a mess!

We can has this:

Sweet!

And that works all the way down:

Sweeter!

Mirror the application structure

It’s the application, stupid.

If your interface is represented by components, and those components are nested, then why wouldn’t you nest them in your source code folder hierarchy?

Consider this: if the BreadcrumbTrail, Logo, and a given Nav component appear only in the Header, then shouldn’t their code be nested there as well?

Proper nesting reduces cognitive load

If I moved the Nav component up to be a sibling of Header and did the same in with the footer’s Nav, then I’d have to rename them HeaderNav and FooterNav. It’s just more noise. Nesting them makes it clear.

Sharing components or functions

But what if it’s the same Nav component? Maybe we’ve abstracted it.

OK, then, we only need one, but where does it go?

In this instance, the folder for that component or function should be raised to the node in the tree where the branches that use the component meet. So in this instance, if we were going to make those two Nav components into one, we’d take this:

These aren’t the Nav components you’re looking for…

And turn it into this:

It’s nice to share

The Nav component is used by both Footer and Header. These two branches meet at the App node, so the shared folder goes directly beneath the App node.

If, for example, FinePrint and Social shared a component, then it would go in a shared folder directly under the Footer folder, as that’s the lowest point still above the two branches where the component is used.

Or if Logo and Social shared a component, then that component would have to go in the shared folder under App, because it is used in both the Footer and Header branches and App is where they meet.

Got it?

It’s pretty simple. And now whenever you see a shared folder, you know:

  1. The items in this folder are used in more than one place.

  2. Wherever those places are, they are guaranteed to be below the shared folder’s parent folder. That is, they are in siblings of the shared folder.

Utilities?

I generally use shared folders for shared components. For utility functions, I have a separate shared folder called utilities. This separation of concerns improves readability in the shared folders. I’m not visually sorting through functions and components trying to find what I want.

Which brings up a thumb rule: If the folder name is PascalCase, then it probably belongs in the shared folder; if the folder name is camelCase, then it probably belongs in the utilities folder.

Utility functions follow the same principle as shared components: they go as low in the folder hierarchy as they can. Which means that if a utility function is used by only one component (or other utility function), then it belongs in the folder of that component:

Main’s utilities folder

The utilities folder under main holds utilities used only by Main and maybe below. If a utility in this folder is needed in, for example, the Header, then that utility’s folder would be moved into a utilities folder immediately under App.

There is almost always a significant number of utility functions that I use pretty much everywhere. If they are not specific to this application, I generally put them into a top-level folder, sibling to App:

Generic utilities

I’m not a fan of adding dependencies willy-nilly. So rather than add, say, Ramda or Lodash (I prefer the former), I simply write my own utilities. It’s not difficult, and then I know not only that they work, but how they work. If it’s something difficult or there is a performance issue, then OK, maybe it’s dependency time, e.g., I’m not writing my own version of React… or am I?

Thus functions such as identity, noOp, and pipe.

But the point of this top-level utilities folder is that I could easily extract it into an npm or Deno module. I’m particularly fond of Deno modules these days. Nothing in this utilities folder is specific to my app. Nothing.

Services

Ditto for the services folder. These are generic services my app uses. Nothing specific to my app lives in the services folder. I inject that information where I use the service in my app (in the App folder).

That means that if I’m using a specific URL to connect to the database (isn’t everyone?), then that URL will be assigned to a constant somewhere in a constants.ts file, and that file will be in a utilities folder at the lowest point practicable in the App hierarchy, where everything specific to this app belongs.

As with the top-level utilities folder, each of these services could be extracted into an npm or Deno module and loaded from the net. Generally, that’s the plan, and they are only colocated here during development.

One benefit of Deno is that we can load them directly from, say, GitHub while keeping them in this folder for easy reference. But then I guess we could also publish them to npm directly from here. It’s just a bit messier.

What are the benefits of this style?

Other than clarity and reduced cognitive load, which are enough to justify the price of admission in my book, the self-contained component/app/function lends itself to easy movement, duplication, or deletion. Consider this:

Easy peasy deletions

If I decide to delete the Sidebar, I just delete its folder. I don’t need to hunt around for tests, stories, CSS specific to the component, or subcomponents. I just delete the Sidebar folder and delete any references to the Sidebar, which are almost certainly only in the Main component.

So easy and clean!

And what if I decide that the Footer and the Header should really be subcomponents of Main? I just drag the folders in there and I’m probably done:

Again, easy as can be. And everything travelled with these components: their authentication, their connections to the database or to some kind of state, other service connections such as pubsub.

Or maybe I want to make the Sidebar a sibling of Footer, Header, and Main:

Hard to beat. Questions? I’ll probably touch on this again soon, maybe when I discuss how I split code up into files and use the module system for easy and clean reuse.

Share this post
Micro-app folder/file hierarchy
www.cantankerouscoder.com
Comments
TopNewCommunity

No posts

Ready for more?

© 2023 Charles F. Munat
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing