How to organize your code base
Besides scaling the application itself for user demand there is another very important aspect to scaling which is organizing the source code itself. We've all heard (or seen) the horror stories about very big unmaintainable code bases. In this post I will elaborate on what I think is a good practice on maintaining your source code as the project grows.
Over the (short) period of my career I've encountered relatively simple systems and more complex systems. With these projects at some point the dicussion arises on how the folder structure should be laid out. Most of the time people start out with what they know, based on experience from previous systems. While Go embraces simplicity I feel like this subject is not in any way simple.
A sensible directory structure is important since it is something you will work with every day.
In the community there is no clear consensus on how to organize your repository. This is because there is no right or wrong way, it really depends on your situation. What I will share with you in this blog post is my perspective on organizing source code so you will (hopefully) be able to make these decisions a bit better. Don't take this as a silver bullet because as with a lot of things in software development, it depends.
There are several blog posts/videos about directory layout and ports & adapaters etc. While I think these way of doing things can be good I don't think it is the right way when you are just starting out on building your application.
The main goal should always be keep it simple
Making things complex by adding a lot of packages initially doesn't make sense, these packages should organically grow.
As an example. Let's say you are building an
api. There are a lot of things necessary for building an
api: middleware, authentication, logging, routers etc. You can split all these up in a separate package which would give you a bunch of packages.
This overcomplicates from the beginning and is a form of premature optimisation. A single package would be sufficient where every logical piece is in a separate file or even in 1 file (if the logic itself is small enough). This approach works perfectly well and it scales quite good.
At some point this one package model starts to grow too big when you are adding more features. This is the moment when you start moving code out into separate packages.
You might be thinking, isn't this a lot of work? Well it might be, but most of this can be accomplished by tooling making it essentially free and thus, saving a lot of other developers the hassle of understanding the directory structure beforehand.
How do you know when something is "too big"? This is based on your own experience, whenever you have trouble finding something or navigation the code base that is an indication that the packages are growing to big or not adhering to having a single responsbility.
The nice thing about this approach is that you are not trying to think beforehand what package names logically make sense, which most of the time is very hard to predict. The names for your packages will organically arise, when you feel like some things start to grow too big or things should be isolated in a separate package (like logging for example).
This approach especially works well in a microservice architecture, since these services are quite scoped and not too big. At my current employment we have services which have only one package and as a result are very easy to maintain. This minimalistic approach makes sure that you postpone these significant decisions to when the time is right.
Postpone these significant decisions to when the time is right.
As a result I always start with a main package and with the first abstraction which is to split everything logically into separate files. Whenever I feel the need for moving these to a package (for instance the single package starts to grow too big) I create a new package and move the files to there.
Thanks for reading and happy coding 🚀
Thanks to Edward Poot for proofreading my post.