Tuesday 29 May 2018

Spring I/O 2018 recap


Last week I had a chance to attend Spring I/O 2018. It was my first time at Spring I/O conference ever, and it was an honor to share the stage with so many fantastic speakers. The number of participants was doubled comparing to the last year. 1000 people from 46 countries gathered in the wonderful city of Barcelona to get the Spring knowledge from over 50 speakers.

The agenda was full of interesting topics and workshops from numerous different areas, and it was hard to choose where to go. Unfortunately, the quality level was very diverse - from the very poor talks to great ones. Below I tried to summarize my thoughts.

First of all, I am very happy to see that DDD concepts are being spread around the world, because this is the way we should make our business software. Michael Plöd gave a good talk about implementing DDD in Spring ecosystem. He made an overview of DDD concepts, and mentioned the initiative of Jakub Pilimon and Michał Michaluk, called ddd-by-examples, which is a solid proof that all DDD enthusiasts collaborate with each other in teaching people what DDD is all about. What's more, Michael gave an example of Archunit library, which I had never used before (but I surely will). Archunit is a tool that enables us to enforce the architectural conventions within our application by writing tests that stand guard.

In most of my projects I used Swagger as a way of documenting my APIs. The problem with it is that it can very easily contribute to technology debt, as there are no mechanisms that would enforce the consistency between the documentation and the actual implementation. Alternative solution was proposed by Mathias Düsterhöft in his talk about Documenting RESTful APIs with Spring REST Docs and RAML. With the help of Spring REST Docs we are able to produce an accurate documentation from our Spring MVC tests. I will surely give it a try.

As I am pretty up to date with Spring 5.0.x and Spring Boot 2.0.x, I didn't get any new information but I'm sure that all attendees that were new to these releases could get an insight into the roadmap and features presented by Juergen Hoeller (Roadmap, Features' highlights),  Andy Wilkinson (Actuator) or Madhura Bhave (Spring Boot 2.0).

Unfortunately I had my flight back to Poland too early to attend talks of Spencer Gibb (Introducing Spring Cloud Gateway), Marcin Grzejszczak (Continuous Deployment of your Application), and Jakub Pilimon (Testing your Message-Driven Application), but as I already had a possibility to see those presentations during polish conferences and meetups I'm sure they rocked the place!

Now something about myself. First day of conference, at 5:30 PM, I started my talk about Dynamic Configuration Management in Microservice Architecture with Spring Cloud, and... I had a full room. If you don't believe me, look at this photo:


The best thing about the talk is that I got fantastic feedback from the audience. People were approaching me, shaking my hand, asking questions, and from many of them I heard that it was the best talk of the day. This is exactly what keeps me going! Thank you all for your precious feedback!

From this place I wanted to thank Sergi Almar for giving me a chance to speak in front of such a fantastic audience and for organizing this great event. Well done, Sergi! Many thanks to HL Tech as well for handling all the logistics and supporting my speaker's career all the way through.

I hope to join the next edition of Spring I/O. See you next year! Cheers! 


Monday 28 May 2018

Microservices - what you give and what you get

If you read my article about semantic diffusion in microservices you might recognize the title. This article is kind of a continuation, and its aim is to emphasize that having microservices and benefitting from them is possible only when we put enough effort to handle both organizational and distributed computing issues we are about to face. In subsequent paragraphs you will find what we get from the real microservices and what they take from us, as well. You won't find any concrete solutions here, but rather a high level overview of how many different, and complex problems we need to solve before we go for microservices. Read on!

Multi-dimensional autonomy


One of main advantages of microservice architecture is that each microservice is an autonomous unit. What does the autonomy mean? Well, we can analyze it from numerous perspectives.

Deployment


First of all, an autonomous service can be deployed at will, independently from other services. The basic way to provide autonomy is to clearly separate business capabilities, so that our bounded contexts don't leak to other services and create tight coupling. If a microservice is autonomous it can be characterized with high cohesion. Now cohesion is a term adapted from physics and in this context high cohesion means that all parts of the application (expressed in source code) are very strongly connected and it requires a lot of energy to separate a thing from it.

Technology


Autonomy is also about technology. Of course while developing our applications you should choose technologies wisely so that they are aligned with company's competences and strategy. However, you should be able to create applications with technology stack that differs from other services. It means that you cannot bind your interfaces with particular technology - your API should be technology agnostic.

Data management


Another thing worth mentioning is that microservices are not only about the code - these are databases as well. Firstly, each microservice needs to manage data it uses on its own. Secondly, even if you identify bounded contexts perfectly, but some of your services use the same database (schema) your applications will still be coupled, and you won't be able deploy them independently, and in case of a database failure all of them will be unavailable.

Scalability


There are two main reasons to scale our application. The first one is because we want to serve a bigger load. The second one is about resilience that we want to provide in case of failure. In a monolithic architecture we need to scale the whole system with all its components. It is often a pain that the only way to scale a monolith is vertical scaling by adding more CPU or memory. With microservices, in contrary, we have autonomy, which give us the ability to scale applications  horizontally, by adding new nodes independently and even automatically.

Resilience


I already mentioned resilience in the previous paragraph, but I need to add that apart from providing resilience in the scope of a single service, autonomy gives us the possibility to isolate problems and errors within particular services, so that other ones are not affected, and thus, the system can still work.

Organization culture


Last but not least, autonomy is also about organization culture. Each microservice should be managed and developed by a single agile team, that takes full responsibility for its whole lifecycle. In order to deliver the best software possible, the team needs to have the power to decide about how people within the team will work.


You don't get it for free

Tradeoffs - they are everywhere. Microservices can give us a lot of benefits. Both business and tech people can drink from the chalice as long as they make the sacrifice. Below you will find things, you need to provide in order to call your architecture a microservice one.

Service discovery


In a traditional environment, where applications run on physical hardware, and their locations are relatively static, services may communicate each other using simple, file based configuration with predefined URLs. However in a cloud-based microservices infrastructure, where a service can have multiple instances with dynamically assigned resources (especially hosts) relying on a static configuration won't be sufficient. In such situations, services should discover each other dynamically using their identifiers (codes/names). And that is what service discovery is all about. In this area you have two options - server-side service discovery and client-side service discovery. In both cases each service instance registers itself in service registry. In the former case calls are directed through a load balancer that queries the registry and forwards requests to the target instances that are currently available. Functionality like this is provided by platforms like Amazon or Kubernetes. In the latter case, though, each application queries the registry itself and it is up to this application to choose the proper instance to call (client-side load balancing). A good example of this is Eureka. If you don't use any of the mentioned options, you neither have scalability nor autonomy, as adding new instance of dependent service or deploying it on some other host implies the necessity of configuration changes in every application that communicates with it.

Scalability and load balancing


Horizontal scaling is not only about increasing the number of service instances. It must be also transparent to service consumers (you shouldn't do anything when the instance count changes), so you need to use either client-side or server-side load balancing, so that the traffic can get to new instances without any extra manual work. When we use cloud platforms like Amazon, Kubernetes, we can scale our applications automatically with the respect to configured policies.


Design for failure


We cannot avoid failures. It is not always about our code, it might be network or hardware failures as well or it might be too many requests that saturated CPU or memory of our components. A failure in monolithic application usually means total unavailability. In enterprise systems that I worked with resilience was improved by horizontal scaling but if some component was erroneous - the error eventually occurred in all instances and couldn't be easily isolated. In general, prevention is not as vital as coping with failure when it occurs and this is what changes our mindset, especially in the context of microservice architecture, where the number of components that may fail is much higher.

Topics that we have already covered should give you some idea of how to handle the failures, but let's  systematize it. Regardless the reason, if our the application that we are trying to communicate with is not responsive, we can always scale it up. Then we will be able to both serve bigger traffic and stay resilient in case of a failure. Sometimes, though, we have limited resources and it is impossible to scale our application. What then? In order not to slow down our system as a whole we should set up timeouts. Random backoff policies would also be a good idea to choose, so that we won't overload the target application with retries. We should also think about isolating failures through mechanisms like circuit breaker, so that we prevent the failure to cascade up to clients. My intention is not to describe all possibilities but rather emphasize how difficult it is to deal with failures, especially when they are unavoidable.

Almost every article about microservices, especially in terms of databases, contains a few words about the CAP Theorem. It couldn't be any different here. Like we all know, software development is about tradeoffs. In our every day work we make decisions that sacrifice one thing to give us another - the more valuable one for us in a given context. The same choice we face with Consistency, Availability and Partition tolerance. If for example due to some network failure we cannot perform data replication among database nodes, we need to make a decision about what to sacrifice - availability or consistency. If we want to handle requests (keep availability) despite failure we must know that we are working in non consistent (eventually consistent) environment until the problem is solved. If we cannot accept inconsistency at any moment, we need to reject all requests - resign from availability. The vital thing about CAP theorem is that some parts of our system might AP and some CP - we can make the decision on a component level.

Monitoring


When our system consists of a single application residing on one or just few nodes then you can pretty easily locate log files and look into them in case of any problems. If there are no errors, but your users experience very long response times, then you can probably profile your application, and look for bottlenecks in one place. In the distributed environment things get complicated. First of all you have dozens of applications running with several instances each on different nodes, which are very often assigned dynamically, and finding the place where something went wrong is a very tough task. Secondly, if it is not about an error that you can find in logs but about the responsiveness, then finding the guilty is even worse. So with microservice architecture we must accept and handle the complexity in terms of system monitoring.

Now the first thing you should provide is a central system that would aggregate logs from all applications' instances and that would enable you to analyze them in one place. A good example would be the ELK stack (Elasticsearch, Logstash, Kibana). Secondly, you should also provide a tracing solution so that you could find out which request exactly caused a problem. In this field an example of a fantastic solution is Spring Cloud Sleuth, which can be easily enhanced with Zipkin, that helps you analyze and visualize dependencies among services in your infrastructure and latencies. With this set of tools you can easily find out which part of your system creates the bottleneck. When we are talking about application logs, we think about finding the source of an error that already occurred. In microservice architecture real-time monitoring of hosts, CPUs, memory, network latencies, response times, etc. is priceless as well. Using tools like Grafana + Graphite and properly configuring your applications you can easily visualize all those metrics. Setting proper threshold values you can trigger alarm and react to it before something really bad happens.

This paragraph might seem to be an option in microservice environment. One may say "I can search for logs in each instance. It takes some time but I can deal with it". If you have 3 applications then it might work, but if you have dozens of them the amount of time that you spend on looking for problems will eventually discredit all other benefits you gain with microservice architecture as it would be completely not maintainable. We need to agree, that microservices bring a lot of complexity in the context of system monitoring, but this is something we literally need to provide.

Continuous delivery


Yet another characteristic of microservice architecture is that when you have small, independent applications, you can provide changes much quicker, and they have much smaller impact on the whole system comparing to monolithic approach. That means that you should be ready to deploy the features as quickly as you develop them. The first step to achieve this is to use so called Continuous Integration so that every change you provide into your codebase is automatically verified in terms of integrity with already existing version - does your code compile? do your tests pass successfully? is static code analysis result positive? These and maybe more conditions are checked within your build pipeline. This is the basis of continuous delivery. By delivery I mean producing some artifact, that is a potential release candidate and can be safely deployed on production. It might be a .jar file or even more platform specific creature like docker image for example. The reason for this is that in microservice architecture we need to respond to changes quickly and be ready to deploy them right after pushing the code to repository.

Of course sometimes it is not so easy to deploy our changes to production. There might be some regulations and processes containing some manual work, like user acceptance testing, or just clicking the "deploy" button by someone in charge, but this is not how it should look like, as development teams should be a part of company that is responsible for the services throughout their whole lifecycle. Drawing a line between developers, testers, and people in charge is not healthy, but in terms of delivery, we - developers should be ready for call.


Conclusions


Microservices has been very popular for a couple of years, and there are strong reasons for that. The autonomy that concerns organization culture, technology, deployment, data management, scalability and resilience brings a lot of value for both technical and business people, but at the same time it requires a lot effort to reach it. Service discovery, load balancing, design for failure, monitoring, continuous delivery are the very base we need to have, and it is not that cheap after all. Before we go for microservices we need to be aware of all these things. I hope that after reading this article, you will be confident to say if your infrastructure provides you the full microservice autonomy, or if you have just another distributed system. And please, care more for the words, and be pragmatic in your day to day work.


Saturday 26 May 2018

Semantic diffusion of microservices

It has been a couple of years already that we can hear microservices buzzword being mentioned in the IT world. "We have microservices doing this", "Oh, let's write a microservice doing that". If you are completely new to this term you might event think that these guys are smart, experienced, and they are up to date with the latest architectural trends in software development. But if you are really carrying about the words and their meaning, you will quickly get pissed. Right now it is really annoying to hear that the word microservice has become a replacement for the name of almost every application that has been developed. At the same time, programmers often seem to treat microservices just as a set of relatively small applications forgetting both that there are people behind that concept and that they are entering the distributed world, and committing so called fallacies of distributed computing. I might seem to be overreacting because the definition of a microservice is still floating, but for all those years numerous design patterns, good practices and heuristics have been well established, and clearly manifest what microservices are all about.

Do we really need to keep up with times only by using some buzzwords? Or maybe we are trying to find an excuse for not giving enough effort to think of what microservices really are? Or maybe we just want to feel that we are trendy? Regardless the motivation, it is not the first time in the history when we can observe such phenomenon. Even in the late 80s when there was a hype about Object Oriented Programming, developers called every piece of their software an object although they were usually nowhere near the OOP principles. Martin Fowler in one of his articles came up with the term of semantic diffusion:

Semantic diffusion occurs when you have a word that is coined a person or group, often with a pretty good definition, but then gets spread through the wider community in a way that weakens that definition. This weakening risks losing the definition entirely - and with it any usefulness to the term.

...and this is exactly what I'm talking about. The aim of this article is to shortly tell you what microservices are, how many different areas they cover, and how many things they require and imply. Then I hope you will revise your organization and software architecture and think carefully if you are really doing microservices. I would like to encourage everyone to care for the words and to defend their true meaning.


Does size matter?


The first thing that we should talk about is what micro means? How little is little enough and how little is too little? If you are waiting for a concrete line count range that would classify your application as microservices within a given technology stack (language, platform, framework, etc.) - then sorry - there is none.

There is a couple of heuristics, though. The first one is that if you feel that you start loosing the idea of how things in your code work - then it is probably too big (assuming you wrote it cleanly). Another rule of thumb was given by Jon Eaves, who said that if your application is a microservice it should be rewritable in two weeks. We might also go in another direction - when communication and maintenance costs of your application are much bigger than gains from its usability. This happens very often when we split our domain to separate services too eagerly, and we end up in having too granulated applications doing almost nothing. This is what we call nanoservice - yep, nano is not micro - it is considered an anti-pattern. Of course a very small application is nothing wrong as long as it really brings you enough value that you are ready to accept the costs of distributed computing which we already mentioned.

All these heuristics might make sense in some specific contexts - when we know our business domain well, we have years of experience in the field of microservices, we learn what micro means for us.  In general, however, DDD is what we should follow. If we implement an application for our Core Domain - we might not know how complex it is from the very beginning and it is the complexity that may determine the service size. It is the business functionalities that we care for - not the size. Such application must gather things related to exactly one bounded context so that we can provide high cohesion, and decoupling/autonomy. However, if we implement applications from Supporting or Generic Subdomains should we care about the size? Not really, as this is not what we get money for.

Summing up, service size from any range won't make our application a microservice, so if you try to call you service a micro one and justify that with the number of your code lines you are just doing it wrong. Similarly, if you mix business responsibilities among numerous applications, breaking your core domain's bounded contexts, you loose cohesion and autonomy, which means you are far away from real microservices.


Conway's Law


Microservices are people. We are not developing software for fun - someone is paying us for bringing value. Now, how many times were you angry at the way applications in your company are communicating? It didn't happen just like that, and it didn't need to be developers' fault. According to  Conway's Law, every organization that creates a system will eventually create one that will mirror the communication structure from within this company. Developers often tend to ignore business people, they seem to know what's best and how to solve all their problems. We know how to code, but we might not know the business. Let's talk with people who will use our systems, discover their processes, help them in locating bottlenecks, and... maybe go for DDD - if both developers and business are on the same page, our services will surely reflect that. Developers will be organized in teams corresponding to bounded contexts, and business will know exactly of what's going on in the company.

If you underestimate human factor and ignore the voice of the business you will keep chaos in your services as well. If you don't discover the domain well enough, you won't be able to keep your applications cohesive, and thus you will lose their autonomy - yeah, it means there won't be any microservices any more.


What you give and what you get


Microservices wouldn't have been such a popular architecture style if it hadn't been for the concrete benefits that they give us. What benefits I am talking about? Autonomy.

High cohesion, which is the main microservices characteristics, implies applications’ autonomy. Now the autonomy is multidimensional. It can refer to agile teams’ structure, independent deployments, technology stack, data management, scalability. However, we don’t get it for free. Service discovery, load balancing, design for failure, monitoring, continuous delivery - these are the things we need to provide to deal with the problems of distributed computing, and fully utilize microservice architecture benefits. As this is out of the scope of this article, details on this topic you will find in a separate article.

Each vector of microservice autonomy space must be addressed within your infrastructure, and on the organizational level. There are undeniable benefits, but they are hard to deliver if you don’t have the culture in your company, or don’t solve issues of distributed systems first. Microservices give, but they take as well.


Be pragmatic


As it was already said, in our day to day work we are supposed to bring value, not to be rock stars. If microservices is what you find useful in your case, go for it, but be pragmatic and don't forget that the value is what you are paid for. Remember that microservices are about distributed computing, and before you go into development, think carefully how you will manage all the problems of distributed environment, because they will occur for sure. Make small steps, providing solutions one by another and you will quickly learn a lot, and at the same time you will know if this is what you expected from microservices. Don't worry if you start with monolith, it is not a shame. We, the developers, are adults (mostly), and we are to make conscious decisions as software development is about tradeoffs.  Don't pursue buzzwords, study them carefully instead, experiment, use well defined patterns, choose solutions that fit you best, and believe me, you will finally feel confident with them and they won't be buzzwords for you anymore. If the definition won't be crystal clear, the anti-definition will. And for me it is better to tell "I have a distributed system where I use microservice architecture patterns" than call everything a "microservice", as the second one is really hard to defend.


Conclusions


Summing up, microservice architecture has become a popular buzzword that every developer would like to use to keep up with the latest architectural trends but microservices are not about nomenclature - these are concrete rules, tradeoffs, decisions, patterns, problems and specific philosophy. Semantic diffusion that we observe nowadays make this term devaluated. In this article you got a brief overview of what stands behind the definition of microservices, and what does not. I will end this article with one more Martin Fowler's quote:

"(...) a good term is worth fighting for - particularly since the only bullets you need are words"