Four lessons learned when working with Microservices

Read about what our Dortmund team has learned on the road to implementing Microservices.

photo of Malte Pickhan
Malte Pickhan

Backend Engineer – Dortmund Hub

Posted on Apr 27, 2016

For a couple of weeks now, our team at Zalando has been implementing Microservices for a new feature, which we’re looking forward to sharing with you. Our team is relatively young and has been working together for roughly six months. This is our first big project as a team and there are a lot of learnings to share.

On top of successes, we’ve also encountered failures. With this blog post I’d like to address the lessons we’ve taken on when working with Microservices.

Mistakes are mandatory

Looking back, I’d say some mistakes could have been avoided in our project. On the other hand, these mistakes were also important to ensure we learned from the experience.

You have to pull yourself together and get on with the job.

Our first challenge was that Zalando “switched” from its monolith architecture to creating all applications as Microservices. You might have thought that this would be an easy task, as we initially did: You just split your applications into smaller ones and continue on.

Well, not quite.

Defining Microservices

As soon as we started designing our new components and digging deeper into the Microservices approach, we realised how hard it was to define the boundaries of our services.

We started with having two “Microservices”: One for the core business logic and one for calling third party applications.

After a few days of re-thinking and designing, we knew we had to sit down together and discuss our approach. We took a detailed look at our ER-Diagram and started splitting up the services by the context that they’re serving, making sure they would only serve one purpose.

Breaking it down this way, we ended up with eight Microservices. Each of these would have their own API, which then needed to be designed.

Lesson #1: Define your Microservices properly and know your boundaries.

Not only were we a completely new team and also new to the whole “Microservices thing”, we also made a decision to switch to new technologies. We replaced Maven with Gradle in order to create cleaner build-files and switched from PostgresSQL to Apache Cassandra in order to avoid downtimes introduced by the database.

Structural apocalypse

Since our team was implementing our first instance of Microservices, we started structuring our projects as we’d been used to. For example, we created multiple modules, one for the database layer, one for the domain objects, one for the API, and so on.

After some time, we recognised that this method introduces a lot of overhead, such as maintaining dependencies for each module and uploading jar files to Nexus in order to share libs between modules. Most of the time, each module contains only a few classes.

Lesson #2: Don’t create modules in your Microservices.

We soon removed all modules and used packages to separate the classes. With this we’re truly on our way to implementing our new feature.

From Waterfall to early integration

We divided our team into sub-teams consisting of pairs, with each starting to define the APIs required to implement a Microservice. Thanks to Zalando’s internal API Guild, the API design went really well. Guilds are modern-day groups with a concentrated purpose of sharing knowledge and spreading new practices. The API Guild is a community of API engineers at Zalando that contribute to overarching API quality and share knowledge around API design and implementation.

An issue that arose from this was that we tried to implement a lot of use cases in one Microservice at the same time. As soon as we started integrating the services, we figured out that something wasn’t working as expected. From here, we either had to change an API, or worse still, change the complete implementation of a use case.

After some weeks, we changed the process so that we implemented one use case through all Microservices and integrated them early.

Lesson #3: Integrate early, integrate often.

This change in the development process made it possible for us to detect design failures at an early stage, resulting in throwing away less code.

I love it when a plan comes together

In order to serve a product’s need for a “go-live-date”, we created a relatively detailed plan of tasks we had to complete in order to finish the project.

Unfortunately, it turned out that this plan was designed to fail. Due to the organisational changes made to the project and the tweaks we made to find our place in the new environment, a delay had occurred in our project.

Lesson #4: Focus on weekly goals!

We soon discovered that including weekly goals to focus on, like you would in a Scrum situation when setting up your Sprint targets, was a better way to prioritise and focus our efforts. Weekly goals lead to an improved workflow and also tie into the lesson we learned about integrating early and often. We wanted to make sure we could stick to the “go-live-date”, so structuring our work via weekly deliverables was a manageable way of achieving this.

I hope you enjoyed reading this article about our learnings and that it saves you time when taking on the task of developing new Microservices. Avoid our mistakes! If you have any questions, you can contact me on Twitter @Coderebelll.