LaunchedMotionShot- Make informative screen guides for your products, tools, and SOPs!
Published on

How I structure my project

Authors

Coding is not the same as building a project. If you are solving some problems for fun, or for competitive programming, you don’t have to give importance to the structure of the program you write. You can call the variable names whatever you want. Your job is to get the tests to pass.

Whereas building a project which has to be handled by multiple people, extendable easily, decently manageable, and debuggable. These are more engineering concepts than the ability to solve the problems.

I would like to talk about a few important qualities. Most of the time while reviewing the pull requests, my major portion of time goes on these things than the logic itself.

Make sure even a non-programmer can also be able to read and understand your code

Naming

It sounds like it is not so important but believe me it is! Even when I write code, I spend a lot of time deciding what to name the variable, function, class, etc. I remember moments where I used to fight with my peers either to change the name of a function or just rewrite the function in some other way.

If you can’t name the function/variable/class properly, you did not understand the feature completely yet

There is no straight way to name things correctly. It comes with practice and I personally feel it is an art. Nonetheless, some basic thumb rules I follow are

  1. Variables and classes should always be nouns
  2. Functions should always be verbs or represent an action; should not be a noun
  3. Enums should always be singular
  4. Lists should always be plural
  5. Groups of constants can have a common prefixes. Ex: PRODUCT_TYPE_A, PRODUCT_TYPE_B

On top of all these, try to understand what this particular variable holds? Make use of the English dictionary if you have doubts about any word. Ex: Invoice vs Bill, where do you name bill? and where the invoice?

I will try to write a dedicated post with even more steps under this section.

Package by feature

You might have heard this debate “Package by feature vs. package by layer”. I still remember I used to fight with my CTO 3year back and I defended package by layer. I quickly learned why package by feature is better than the other.

Let me explain what these are first. Let’s say you are building an application that has few controllers, services, and models.

Package by feature vs by layer

There are two ways to package them. One you just group by the common technical aspects, ex. all controllers in one package(directory) and services in another, etc. Another better way is to group by the feature. For example, group all authentication related stuff in one package, profile related stuff in another package, orders related stuff in another, etc.

The advantage of Package by feature is that it will be more readable. Even a non-programmer will be able to land on almost the correct file to look into. Not just that, even if you want to move some part of the application to another service, it makes more sense to just move the whole “order” folder to a new service than moving controller, service from different folders. Even the dependency graph will be more meaningful if you pack by feature.

Function-ification

Functions are the ones that perform any type of action in our code. If it is OOP based programming language then it is some other variant but the logic is the same. There is no hard rule for splitting a function into multiple functions, but it is always best to split the functions with an appropriate degree. I will explain it with an example

Monolithic function

Functionified version

In the above example, the order function can be split into other small functions as shown above. Reasons are

  1. Readability. When I go to the order function, I can clearly understand that first, we are validating the payment, then getting the amount, and finally placing the order. In the monolithic function, the steps are not clear.
  2. Blackboxing. When I come to see what is the implementation of order(), I don’t have to know what is the logic of validating the payment, how the order amount is getting calculated unless I want to know. This comes handy while debugging the code.
  3. Test friendly. Because now we have small individual functions, we can test the functions by mocking other functions if we want.
  4. Clear definition. You have a clear set of inputs and outputs with types. So as soon as you see the definition, you know what it is doing

Bonus

There are two other minor practices I would like to mention.

Style consistency. Keep your code style consistent across the project. If it is 4 spaces, keep it that way everywhere. Even minor things like the way you put { and }, indentation, etc. makes the code look beautiful.

Line breaks. These are just empty lines. Use them wisely. I personally use them where there is some logical division. Ex: if I want to separate variable declaration and the actual logic. I remember asking to remove empty lines in a lot of PRs. It should be used where it has to be used.

Summary

All of these topics are more philosophical than logical. There can be other ways to make the project structure better but these are the things that I follow subconsciously. If you feel something is misleading, inappropriate, or wrong, I am more than happy to listen. Cheers!