Common Sense Driven Development

Nowadays every day or week we’ll getting new framework or tool everyone is hyped about. https://dayssincelastjavascriptframework.com/ is a great example of trolling JS people about that. Development is a lot about this new and exciting technologies but day to day life is not as simple as using the cutting edge, shiny things.

The double edged sword of Cargo Cults

For the definition I’ll fall back to good old Wikipedia:

(…) attempt to emulate more successful development houses, either by slavishly following a software development process without understanding the reasoning behind it, or by attempting to emulate a commitment-oriented development approach (in which software developers devote large amounts of time and energy toward seeing their projects succeed) by mandating the long hours and unpaid overtime, when in successful companies these are side-effects of high motivation and not requirements.

As managements issues are important, I’d like to focus more on first part of the definition.

There are from time to time new tools and practices released and world is getting crazy. I’d say React.js is one of them. Other may be Netflix Cloud tooling or good old Docker and Kubernetes on the Dev/Ops side.

And don’t get me wrong, I like them all. The difference between what you can use and what to use to make your project successful. It’s context of making decision being more important than decision itself.

Having technology solving your problem is great but you may fail because of very steep learning curve. Tool may be not supported in few months or new version will be released and you’ll have nice and shiny legacy code even before release.

What to look for

  1. Make sure you’re not trying to use the same hammer for every nail – there Is a lot of technologies and some are better in some tasks than other. Like PHP and multithreading or long running processes. You don’t want to do this to yourself. Maybe better solution will be to get people to learn a bit of Java of node.js to make this subsystem?
  2. Support – is the library you want to use “mainstream” enough for you to use it and be sure it will still exist in few years. From other hand ask yourself if you really need to use library for some very simple functionality you can write in about 20 seconds.
  3. Learning curve – Check with your team new solution can be understood and implemented correct way. As an example I can take CQRS and Event Sourcing, which are quite complicated topics and used mostly in enterprise environments. Anyway people often think it’s silver bullet for their problems and going through with it. Often they are right but as it needs time for people to learn about it’s problems it’s better to take middle ground and tart with just emitting events before switching to ultimate solution.
  4. Look at yourself first – There is a lot of companies and a lot of ideas. None of them is a silver bullet. There are also old, “bad” ideas. Like monolith. And those bad ideas are good in some cases. Like when you have quite big application to write in small team.
  5. Take authorities with grain of salt – aka Cargo Cult of the person. It happens when opinion of one person becomes opinion of the community. You know examples of that from global politics. And I’m not saying those people are wrong. They are just preaching one solution which they like. And it doesn’t really matter if it’s correct solution of programatically correct. Their acolytes will quote them in every meeting. Argument of need and correctness of the solution will be pushed back because of argument of well known person having opinion.

There is only one correct answer – it depends

I’d assume there is as many styles of coding and tools as developers in the industry. Some are better than others. Some are evolving and getting better and better. Some are legacy at the idea level but still generating revenue for the company.

Bottom line is that there is no single answer to a problem. Context of the problem changes everything and I think it’s the most important thing to look at when making technical and process decisions. And then choose which hyped tech use in the next project.

Modular monoliths

We all know and we all worked with monolithic application. One big codebase which, in time, is looking more like hairball than real application. Usually at this time we want to rewrite it to microservice architecture. But I think first thing we can take a look is how to write better monoliths.

 

Why do I even think about building monolith?

There is few reasons why monoliths are so common. One is that they are easy to manage and deploy. They are easy to reason about as well. There is one project, one place where things happen. When change is happening you know exactly where it will happen and how to test it. At the beginning of the project, especially in the startup environment, it’s faster to develop when you don’t need to think about issues related with distributed systems.

 

So where it went all wrong?

Usually monoliths are written in a hurry. Features are done quickly and without proper planning. Everything is created in one application as it is one big bounded context.

Dependency tree, as well, is all over the place because of that. If you want to use some model you are just using it. Because it’s in the same codebase, right?

I think this is the sole reason why monoliths are going wrong.

 

How can we do this better?

Alright. So we have microservices and monolith. Let’s think how to get advantage of the best parts of both architectures and keep ourselves in single codebase.

 

Single responsibility principle

One of the best parts of distributed systems is that every service has only one responsibility and it’s doing it right. Similar to Single Responsibility Principle from SOLID. What it means on service level is that every service has it’s own data, contracts and encapsulates bounded context of what needs to be done.

 

We can use this in our monolith with ease. All we need to do is to separate each module/component/bound context into separate package of code. It has it’s own models, it’s own data and contracts in the same way microservice have.

Same way we can do with interface modules representing departments or group of users using our application with adapters for specific modules they need to use and nothing more.

 

Integration

As we have our modules nicely separates we need to let them talk to each other as usually there is a lot of cases where more than one is involved.

As it comes to straight forward calls we can use anti corruption layer (adapter interface) to deal with it. All we know is some method in some service and we don’t really care about implementation. We’re safe when it changes as all we need to do is change the class implementing mentioned interface or create new one.

As it comes to sharing data (what we touch next) we can simply use events, as Event Sourcing do, just on code level. You can even implement or find event bus which will take care of event propagation in your system. It will be synchronous but in monolith everything is.

 

Data

In micro services architecture every service holds it’s own data. Having monolith we have one database to deal with. Even if it may look interesting to split database it’s adding unnecessary complexity to our simple modular application.

Better thing to do is to prefix tables with module name and keep prefixed tables to depend only on each other. What it means is that joins, for example, can be done only inside specific prefix namespace. When there is need for data from different part of the system we need to make in code call to it.

If we really want to join, as we do this pretty often, we can use Event Sourcing pattern of materialised views, where module is reacting to public event of other one. Just like microservice reacting to event from global event bus.

Some may say it’s data duplication. I’d say data depend on context. For authorisation system you need user’s email and password where for billing you need way more. Keeping only one User representation is holding you back when it comes to change in one module or other.

 

Putting it all together

Keeping microservices architecture inside monolith may give you best of both worlds. One codebase, one server, one database from monolith and ease of change and domain relevance from distributed systems. Simply use patterns used by microservices replacing tools and network calls with in-code communication.

It should get you through the mono part of the project and get you simple way to migrate to separate codebases where you can simply extract package after package and change implementation of adapter interfaces from method calls to network.