Why we did not go all-in on micro-services
It is easy to get carried away by the latest trends. The somewhat recent revival of (micro-)services architectures got m y hands itching while drafting the architecture for an upcoming product. That would certainly be a great way to start off this greenfield project.
As an engineer you have to be able to step back, look at the bigger picture and make rational decisions based on the available information. Once my initial enhuasisme waned off I was seriously doubting my initial design. Here is why.
Commitment to devops
Having multiple services rather than a monolythic application requires extra effort in terms of keeping things running. There is more operational overhead for things like service discovery, logging, multiple application servers and a–potentially–more diverse technology stack.
Your team should be dead set on the devops mentality. We’re still putting together a team for this project and finding good engineers is already hard enough. Adding yet another requirement to our “skills” list wouldn’t make the search any easier. Some of them might be interested in doing devops work, but this certainly can’t be forced.
Do things well or go home
In a service-oriented architectures you should definitely plan on writing asynchronous code to improve throughput. However, most engineers with experience in traditional web backends have limited experience with async programming.
I’m also of the opinion that when write asynchronous code you should opt for a programming language that support this paradigm natively, if possible. Of course you can put an event loop on Ruby or Python, but wouldn’t you much rather use something like Node.js or Go? This would be yet another requirement when hiring new team members.
Services are highly decoupled, allowing teams to work is greater isolation. This is a nice property when working in larger organizations, but certainly not necessairy when working with 3 or 4 people. I’d even argue that having more people (up to a point) on the same codebase will improve the quality of the product.
Additionally, the communication between services has to be defined (I like Thrift as an RPC framework). When you’re still fleshing out the details of a product these interfaces can change quickly, which is hard to coordinate across codebases. Once the applications mature this problem is encountered less frequent and extra consideration could be given to maintaining backward-compatability across services.
No need for web scale (at least for now)
Writing software for scale is fun, but may be counter productive. In a service-oriented architecture you can easily scale each individual service horizontally and thus improving the throughput of the overall system.
"Premature optimization is the root of all evil." – Donald Knuth
Building applications in such a way that it can handle a load it might not ever need to support is wasteful, but there are alternatives. By designing modules just as they were external services you overcome the hardship of a full-on refactor later on. These modules can be put in separate service when time comes.
If you have access to a team of engineers who embrace devops and work on a system that is sure to face scaling issues, sure, go jump on the micro-services bandwagon. If this is not the case certainly keep this architecture in mind, but remember you can’t have your cake and eat it too.