Proper software architecture for a new project
I got an email recently asking about my advice on how to approach the architecture on new projects. In particular, looking at typical architectural patterns, they are full of things like repositories, interfaces, components and multiple moving pieces. Usually they are marketed as items that will aid future extensibility or promote separation of concerns.
In fact, if you’ll look at the image on the right, you’ll see a typical timeline for a non trivial project. The amount of time that will be spent on the project architecture before we actually start writing any real code is obscene, in my eyes. This is the stage where we know the least about the project, and we are already spending so much time on it to get it moving. By the time we actually start getting real work done, the inertia and the amount of investment that we put into the infrastructure means that you usually can’t change it.
My approach for this is different. My goal at the beginning of the project is to get, as soon as possible, to the point where we have something that we can show to the user. That means that we don’t have time to do complex setups. Instead, I tend to follow the following architecture rule:
When I start a project, I write the minimal amount of infrastructure that I think that I can get away with. It is important to note that I’m writing this infrastructure with the explicit intent to throw it away. This is scaffolding, not permanent structure. Do you see the orange wheel in the picture?
That is meant to represent the abstraction layer between the infrastructure and the rest of the code. Once I have something that I can get away with, I can go ahead and solve real problems. If and when I’ll realize that I can’t really go on the way I did, I can change the infrastructure and not touch any of the application code.
For example, let’s say that I’m building a backend service. There are a lot of decisions that I need to make when building these:
- What is the transport mechanism? REST? gRPC? SOAP?
- How do you handle authentication?
- How do you handle auditing?
- How do you… ?
I can spend weeks and months on making these decisions. And they are important, but they aren’t useful at the beginning of the project. What is worse, they are likely to change. Six months down the road, we might realize that we need to meet the requirements of a particular regulation, so we can’t use a particular transport. If we are wedded to it, we are done.
So my infrastructure would be the minimal amount of code required to get something done. Here is an example of how it can work:
I’m now able to write code that inherit from AbstractMessageHandler and does stuff, using strongly typed code, without really caring how I go the messages or how I send them back. This is probably now what we’ll end up using, but it is small, easily handled / replaced and I can change it without touching the application code.
I have also ensured that I’m able to lean on the compiler and use the proper types, avoiding dealing with strings and implicitly typed values.
It took me about 15 minutes to create the above code, and my limit for setup time for a new project is one to three days. Afterward, we have to be able to write actual code, not infrastructure.
I’ll admit that it does take some thinking upfront. In the code above, I’ve decided to use a message handler pattern. In some cases, that wouldn’t be appropriate and I would need something else (for example, having an explicit channel to communicate between actors). Usually, however, these theme of the project is easier to decide on than the actual infrastructure.