Conf42 Golang 2022 - Online

Successful Go for microservices architecture

Video size:

Abstract

Microservices architecture is a formidable pattern and Go is an incredible language. How do these two perform when combined?

The microservices architecture consists of a breakdown of your software into multiple tiny services: flexible, independent, easy to scale up, domain-specific, more granularity, etc… But comes with some issues: complexity of communication between services, E2E tests difficult to write, daunting for new joiners, etc. Having all the services written in Go does not solve all those problems but makes the software development more enjoyable and eases some of the pain points.

The goal of this talk is not to sell the microservices architecture neither Go, but is more about the story/timeline on how we transitioned an existing microservices piece of software into Go.

What will be shared:

  • which features of Go allow us to build a successful microservices system?
  • which blockers did we face?
  • what are the next steps for us?

Summary

  • Jerve will talk about successful Go for successful, successful microservices architecture. Jerve is a senior software engineer at a telecommunication company. Andela has matched thousands of technologists across the globe to their next career adventure. Now the future of work is yours to create.
  • The trend is more into microservices for some reason, for agile and so on. Usually a microservice is like an autonomous, meaningful service representing business unit. It usually translates into a faster release deployment or a better dev experience overall. But obviously I'm here to talk about the caveats of this pattern.
  • We had call start problem with our serverless functions with Scala. The duration, the length of a call start depends on three factors. Some languages take longer than others, these the package size. How do we make the most of go to tackle them?
  • Go is quite a well designed language. All the standard libraries built in when you download go. You don't have to download any external dependency or this kind of stuff. In Go, if you want to test your code straight away, you have a ready two be used library dependencies management.
  • When you're writing microservices you have a lot of common parts used by your different services. The solution for that is to write common library that you can share between your services. A very quick special mention to Golan. This GraphQl server generator. It really smooth the experience, the dev experience and really help us to deliver value quicker.
  • For a person new to go, or maybe his or her first language or second, Golang is quite easy to learn. You have very few keywords and the syntax is quite explicit. It means that for a junior point of view, you don't spend much time on struggling.
  • Go offers advantages but also limitations. The first limitation with go is how to organize our folders and packages. It really help us by organizing those packaging in meaningful context.
  • Go is an efficient language which brings so much high quality to the services. What else could we do with go in order to have even better successful Microservices architecture?

Transcript

This transcript was autogenerated. To make changes, submit a PR.
What if you could work with some of the world's most innovative companies, all from the comfort of a remote workplace? Andela has matched thousands of technologists across the globe to their next career adventure. We're empowering new talent worldwide, from Sao Paulo to Egypt and Lagos to Warsaw. Now the future of work is yours to create. Anytime, anywhere. The world is at your fingertips. This is Andela. Hello, everyone. Thank you for joining my talk. So I'm Jerve. I'm going to talk today about successful. Go for successful, successful microservices architecture. Few years ago, when I joined my current company, the team was working on we had almost monolithic architecture and they were using a programming language called Scala. So then we decided to make the transition using go and building a proper microservices architecture software. So the aim of my talk is not so much about praising go or the microservices architecture, but it's more about talking about the journey and what we learned through that journey. So very briefly, about myself. So I'm Jerve. I'm a senior software engineer. I'm based in London. The company I work for is called smart numbers and I joiners them in 2019. So we are a software company specializing telecommunication. So we deal with things like fraud prevention, call management, telecoms migration or mobile solution and so on. So if you want more detail, you have the website there, the link. Myself, I'm part of the fraud prevention team. So I'm working on fraud prevention products. So as you can imagine, we have quite a weird and strong constraint because we're dealing with telephony, especially latency. So we'll come back to that a few times through the presentation. And if you have any question or any comments or feedback for me, you have on the right for you, I think. Yeah, you have on these right my twitter, and you also have the link to my YouTube channel where I'm trying to teach go the basics, giving you tricks, tips, teaching you design patterns and these kind of things. So that's for me. So now let's dive into the subject. So, microservices, obviously, I said a few minutes ago that I don't want to spend too much time in praising microservices, but I'd like you to understand what's a microservices architecture and also what are the caveats of using one of those. So to set the scene properly, I'm going to have to compare it with a monolith architecture. So no disrespect for a monolith architecture. So it worked very well for a few years it still work in certain eases, but now the trend is more into microservices for some reason, for agile and so on. So the cliche of a monolith, so you have a monolith on the left. So usually a monolith is like a gigantic mono service, years and years of code accumulated. It's using a single database with a horrific schema, difficult to migrate. And this kind of stuff, it's usually in a single version control repo into a single GitHub. So it means that you have to have a strong discipline in using GitHub. Like you have to use branches and you have to do rebase, you have to do commit, pull requests and all this drama. So usually it's quite difficult to extend and maintain, and it's not such a nice dev experience. So as opposed two that we have on the right, successful, successful microservices architecture, the name suggests, instead of having this mono blockers, we have multiple services communicating with each other. So usually a microservice is like an autonomous, meaningful service representing business unit. They can have these own database and they are in their own GitHub. So the advantages of that are that they are easily testable on their own. You can upgrade these on their own. So if you want to upgrade a component, a library, on one service, you can do that and it won't impact the other services. Your schema is simple because you have tinier databases and if you have let's say four colleagues in your team, and it means that they can work independently on four different services in different repo without stepping on each other toes. So you have quite of autonomous. So it usually translates into a faster release deployment or a better dev experience overall. So that's on paper, that's on theory. But obviously I'm here to talk to you about the caveats of this pattern. So I took a screenshot for you about a screenshot of our microservices. So we are using AWS, Amazon, Amazon Services. So this is x ray, which is a monitoring tracing tool. So the dots represent services and databases or any entry points. So you can see visually straight away it's complexity. So here we are introducing a lot of complexity between the services in term of communication. And we are also introducing what we call cognitive load, meaning that it's a lot to take on for one single person or for a team, as in if you have 100 services, it's not humanly possible to remember what each single service is doing or responsible for. Then secondly, you are increasing the overall latency of your those architecture. If you're not careful, it's simply because you can have a service calling another service calling over multiple services. And as I told you, I'm working for a telephony company. So if a person is trying to contact emergency services, you can't wait forever to process the call. And then lastly I mentioned that it was easily extendable, maintainable, yes and no. Because if you have a service sending a request, two another service and you want to change, you want to remove or you want to add another property on those request response. It's quite a bit of gymnastic because you don't want to introduce break and change and you might not aware if you change the response of one service you can think about these service consuming it, but then you might forget others. So it becomes really complex. And it's even more complex if you have multiple environments. Like you have a dev environment, you have a UAT environment, you have a pro environment, you want to propagate this chain smoothly without breaking anything. So it's quite a lot of scheduling. Also maintainable? Yes and no, because I said earlier, they are upgradable on their own. That's true, but let's say you are using a common library for each single of them, and that library requires a security patch. If you have 100 services, you're going to have to propagate those patch security 100 times, unfortunately. So obviously any programming language could help solving mitigating those problems or would have those problems. But the goal of this talk is how do we make the most of go to tackle them? Right, let's see how we can use go the most efficiently possible. So our first challenge was three years ago when we had call start problem with our serverless functions with Scala. So a serverless function is a programmatic function where your cloud provider takes care of hosting, managing and provisioning your infrastructure, and then you just execute your code. The problem with that is when you want to invoke your function, your cloud provider has to download your code and then start your new execution environment as shown on the diagram. So this existing time is your call start and, and then your code executes. And the duration, the length of a call start depends on three factors. So first one is the language. Unfortunately some languages take longer than others, these the package size. So it depends if your language is natively. Once compile is natively more voluminous than others, and also if you're using bigger dependencies or more dependencies. So obviously if you're using more packages or dependency, the longer is going to be your cost because you have to download those dependencies and also doesn't matter here in our case, but it also depends if it's in a private network or not. I'll put a link for you here. It's only about AWS, the Amazon services about call starts. It's a really good article. So the problem we face and what we saw, so we had awful call start with scanner. So Scala is part of the JVM language family, so it compiles to Java byte machine, Java code byte machine, sorry. And so we had call start of one to 2 seconds and then our code executing. So that was not acceptable for our constraint, our product, because you can wait forever for a call to be processed. And you can imagine in that scenario that we had a scala function calling another scalar function. So it means that you could hit a chain of call starts, which is a really awful experience for the customer. So then we makes a trial, we tried a Golemba and it was much much better. So we had a call start of millisecond and then your function execution. So that makes sense because go binaries are really lightweight, so therefore the provisioning, when your cloud provider is downloading your code it's much quicker. So then that's why the consists are much much tinier with go. So that was a first success for our microservices, especially for the latency. So we decided to make the full transition to Golang basically. Second point is Golang is quite a well supported and popular language. So it seems a bit simple and trivial to say that, but it does have an impact. So let me explain because it's quite a well known language now and it was already three years ago. So you often have sdks available. So we use aws, but it's available for Google and for Asia and other probably. And as we had to make the transition we had to write services from scratch. So for instance we had to write HTTP server, graphql server, we had to write test to generate mock mocks, we had to encrypt, to hash, we had to talk to a database, we had to services as request response. So none of us had experience in go and we had plenty of tutorials available, a ton of tutorials available, which made our life really easy. So then yeah, these ecosystem was and still is extremely alive. And we have a supported community and it's a living language, it's really well used. So it means that the services we wrote three years ago still beneficiate from updates, from improvements. It's a bit of a teaser, but we had a few months, a couple of years ago go modules for these dependency management we had generic like a few days ago, so it really does help in building high quality services. So my next points is quite a bit of a continuity of my previous point, but more in detail. So go is quite a well designed language. So here you have all the standard libraries built in when you download go, I think it's worth mentioning because all of that, as I said, is inbuilt. Like when you install go, you have all of that ready to be used and you don't have to download any external dependency or this kind of stuff. So worth mentioning, you have the crypto for hashing or to salt or the secret and this kind of stuff, you have these encoding library for JsON marshalling and marshalling. You have the amazing errors package, very simple but really effective. Have the formatting library, you have the OS library for manipulating files, you have string manipulation, you have the sync library for the existing group, the Mutex and these kind of things. And you also have the really good time library, really well built. So a bit of comparison here you have for instance JavaScript. When you want to use TAM, you have to download external library like moment or momentum or date or date fn or something like that. Also the testing framework. So the testing framework is actually a better example. So when you want to test your code with JavaScript, you have a ton of libraries, can't really remember, but you have mocha, you have Chinese Sinan, you have cypress perpetual js. It's not uniformized, it's not like inbuilt. Whereas in Go, if you want to write go and you want to test your code straight away, you have a ready two be used library dependencies management. So we started with an external tool called depth. But fortunately enough in Go 1.13 the dev introduced Go modules, or Go mod, called Go mod. So it's just fantastic tool which just work. So you can add a dependency, you can download them, you can initiate a file, it's just proper dependency management available to you when you download go. So that's fantastic. All right, some libraries worth mentioning, obviously you could use only standard libraries to build your pieces of software, like you could write from scratch a library on your own to generate graphql server or this kind of stuff. But turns out that you really have good library out there, really well maintained, really well tested, and it really helped us in our journey to build high quality services. So the first two are testing libraries. So Gomega and Ginkgo. So these are matcha assertion library and BDD test framework. And these, the third one is really important because when you're writing microservices you have a lot of common parts used by your different services. So then the solution for that is to write common library that you can share between your services. So those kind of libraries are codec serialization framework or these kind of things. Or they can be models to share between service to ensure that you are using the correct request, the correct contracts between them. So you want to do that smoothly. So you have to have a tool which can properly distribute tag version your library for that. So Gorilla releaser is a really nice binaries builder then. Lastly, I thought it was worth mentioning this GraphQl server generator. So it just works out of the box and from GraphQl schema it's generating a full server ready to deploy. So it really smooth the experience, the dev experience and really help us to deliver value quicker. A very quick special mention to Golan. So Golan is an did. It's part of the Jetbrain family. So you probably know Intellij. I know that ids are quite controversial, but if you're familiar with the Dreadbrain ids, you can go pretty quickly with Golan, with all the shortcuts and stuff. So the learning here about the tooling is because you have so go is well built and you have really good libraries available. So the tooling is really excellent. So that makes the dev experience really solid. And for our microservices it means you build faster and you build with high quality. And overall it's just really enjoyable to write go at the end of the day. All right, so my last point is quite an interesting one. It's about the new joiner experience. So for a person new to go, or maybe his or her first language or second, Golang is quite easy to learn. So you have very few keywords and the syntax is quite explicit. So just in a few weeks you can already understand and you can already contribute massively. Two, the team. So that's really good. And also even after a few years, if you still have appetite for challenges, you can always find subjects, topics to deal with, like garbage collection, or understand how the concurrency model works, all these kind of things. So in our team, just a few months after embracing the Go universe, one junior member of our team did a presentation. Two, the whole engineering department about go. That was a massive success. So at the bottom I put two links, two resources that I like, I'm time to comes go back there to just check some pieces of knowledge. So the first one is a tour of go and the second one is go by example. So Golang is really beginner friendly. So it's immediately rewarding for them. But also from the mentor side, as you can build a solid base and you can consolidate very crucial and important concepts like solid principle, clean code, and you can also build on that eases even further concepts. So for our microservices, it means that for a junior point of view, you don't spend much time on struggling, on learning a new language, but you can also focus on the overall architecture and try to understand the place of a service in the whole fleet of services. All right, so now I'd like to just contrast all those advantages with some limitations. So the very first limitation with go that we face was how to organize our folders and packages. So on the left you have these traditional organization, the old fashioned way, MVC model, view controller. So we started with that. We had a package called controller with all the controllers. Then we had a package called services. Then we had all our services and was gigantic package, not so much meaningful, and so on and so on. You had views, models, and we just agreed on a better organization, which per context. So context here does not mean go context. So context is more like a logical unit, like you have on the right, the order context, where you have everything about order and then everything about customer, and so on and so on. And we also found out that having too much level of nesting didn't work either. So what we tend to do usually is to have only one level of death, like you can see on these, right? Or at most two level of def, of nesting. Sorry. So it really help us by organizing those packaging in meaningful context to identify common packages and extract them as common library, or to do not misidentify them. Do not extract something where it's not necessary. And then the tooling did the rest, as I told you, with that go release library, the second limitation we face. So here, it's not necessarily impacting our microservices, but I thought it was worth mentioning. So the first caveat was the pointer problem. So I'm going two, take a really controversial shortcut here. But usually you use pointer if you want to mutate something or if your struct is too heavy to be passed around. So in that case you use pointer. So then we had a few problems with that. So first is like, it's a bit daunting for someone who has never seen pointer in his or her life. So we just took the decision to avoid punters like the plague, basically. So we try to do it more in a functional programming way. So let's try to avoid mutate things, and let's try to do not use pointer in our struct if it's not necessary, because we had a bit of a drama where we had a pointer, a misuse, and we broke prod once, so that was terrible, so we didn't do that again. And then about interfaces. So sometimes the concept of implicit interfaces are also called the typing. It's quite difficult for a beginner to understand. So about the future, what else could we do with go in order to have even better successful Microservices architecture? Wrote a tiny user story there. So as a Go software engineer, I want to write infrastructure in go, deployment pipeline in go, my task runner in go, and even writing go to generate my documentation. So then I don't write awful yaml. So here it's a bit of a joke, yes and no, because go is such an efficient language which brings so much high quality to the services that you'd like to have that resilience, that kind of quality level for everything around. So that would be the dream. Second point is about protobuff. So protocol buffer. So we are a bit late in the game, so we don't have that implemented at the minute in our architecture. So protobuff is about serialization of structured data. As you can see, you can do it in go. So here it would help, we believe it would help to propagate any changes between our services, because it can do validate schema validation and these kind of fancy things. And it's also very lightweight and very efficient in term of latency. So that's something on our to do list. Another things on our to do list is the generics. So just a few days ago go 1.18 did release these generics, finally. So it's ready to be used. So who knows what we're going to found. So in conclusion, we saw that successful, successful microservices architecture, really good pattern, but unfortunately you have to be careful about two points. So the first one was these complexity between your services, and the second one was the latency that you could introduce in your services. So then we also saw that go was really helping to ease those pain points. So first one was excellent performances, the library binaries, the language speed, and then you have the tooling available because it's quite a popular language, you have a lot of good tooling available, a lot of tutorials and so on. So overall providing a really good developer experience. And then who knows, maybe there will be even more go features in these future to even make the experience better. So thank you for watching. Hi,
...

Herve Ah-Leung

Senior Software Engineer @ Smartnumbers

Herve Ah-Leung's LinkedIn account Herve Ah-Leung's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways