 There we are. Okay, so hello. My name is Patrick. On GitHub, you know me as Raghvip. I'm one of the maintainers of the Backstage project, and... Yeah, I'm Copenhagen, and I'm working a lot on Backstage. Yeah, and also maintain. And we started the same day. Yes, we did. So today we're going to talk about how we are evolving the Backstage backend system. So we're going to look at what it looks like today for building backends. We're going to look at where we're headed and why we're headed in that direction. And we'll also dive a bit... That's better. That is much nicer. We'll also dive a bit deeper into how the new system works and end with a bit of a comparison. So first I want to give you a bit of history of the current back-end system and how it came about. So when open sourcing Backstage, we had a pretty well-built out plug-in system for the front-end. But on the back-end, there really wasn't much at all for plug-ins. And we had also taken the decision to rewrite all of our back-end systems into Node.js. So we basically started from a completely clean slate. And we started very simple, just with a pattern for how to set up an HTTP router to serve the back-end APIs. Then over time, we added more plugins. Things grew. We added more utility services for things like logging and configuration, database access. And as back-end plug-ins got even more elaborate, we started seeing extension patterns for plug-ins. So you can now add things like entity providers in your catalog or custom actions in the scaffolder. Now, all of these new things grew out while we maintainers were kind of frantically trying to keep things a bit consistent across the project. So that's led us to where we are today, where we're quite powerful tools for building elaborate back-end plug-ins. So what does a back-end plug-in look like today? At the heart of it is the create router. Every plug-in exports this function or something like it. And it's essentially a plug-in that gives you an HTTP router to serve external traffic. And then through the options for the create router function, the plug-in can accept any kind of options or dependencies that it needs to get started. Now, this design is extremely flexible because it's just a function. You can do anything with a function, really. But with this flexibility, there's a lot of cost. So when you have something that is this flexible, it's very hard to provide any utility. So from the framework's point of view, we can't really hook in and help out in any way because there's no points for us to do that. So let's zoom out and see how this create router method is used. So this code handles the setup of the plug-in, right, where you supply your dependencies from the environment and any other extra options. And it lives in a separate file in each back-end installation. And to be clear, there's a copy of this file in every single backstage instance that uses this plug-in. So this file is just copied over and over again. Well, at least it's pretty simple, right? So what's the problem? It's kind of what we thought in the beginning, too. So, yeah, as Patrick's leading on to here, it does bring a couple of headaches. So let's look at the installation of an existing back-end plug-in today. So these instructions seem to be very plug-in-specific. And in most cases, they're always available to read me. I'm sure this looks pretty familiar to people today. As you might be able to see from the instructions, we first need to install the package from NPM. Then we need to create a file which is named after the plug-in and then import that into the back-end package and then wire it all together in the HTTP router. Now, we're about evolving an existing plug-in. So let's say I'm a developer and I want to add access control to my plug-in. Now, luckily, we've got a permission service that's super simple. So to use it, I need to update my plug-in to accept it as an option to create a router. Now, requiring permissions is a breaking change, and that line that you can see on the slide needs to be added for every installation of this plug-in. This will continue to happen for every capability we add that requires a new service to be passed from the outside. What plug-in authors ended up doing is basically just making this optional. Now, that's kind of problematic because it means we need to deal with optional code from, like, as a producer of a plug-in, like as a developer and also as a client that's going to use the plug-in itself. So let's look at the catalog installation. So this is code that you will recognize, I think, today, because this is basically adding the scaffolder processor to the catalog. So the processor we're adding here is what we now call a module, which extends the catalog with new features. It's essentially a plug-in for plug-ins. The way that modules and plug-ins interact is completely up to each plug-in, and there's no real standard pattern for it. So let's imagine we want to evolve any part of this setup. So let's look at the API surface. So changes to how the builder is constructed can be breaking, changes to how our processor method and the processor interface that can also be breaking, changes to how the entity processor is constructed, that can also be breaking, you get the point. There's a lot of breaking changes that we could make. The API surface is huge. So with all this in mind, we tried to make the situation better for both end users and plug-in developers. So based on learnings like what we've just seen, we basically set out with the design for these higher-level lists of what we wanted to do. So first and foremost, we wanted to simplify the installation of a plug-in. So having to create multiple files and managing imports is just too much of a hurdle, and it's a hassle for people trying to integrate with Backstage. We wanted a much simpler process, both for plug-ins and modules. So secondly, we also wanted a few breaking changes. So by reducing the API surface exposed by these plug-ins, we want to get rid of the situation where everyone needs to update their back-end code when we introduce new features or new services for plug-ins. We wanted less code in the back-end setup and move all of this back-end wiring into the back-end system itself. Like we're going to build a framework, so let's hide away all the hurdles that people have got to jump over. Let's move a lot of that stuff to the framework. And we wanted the system to feel recognizable. So to anybody that's been working with front-end plug-ins, it should kind of just be a really simple switch to writing back-end plug-ins, right? We've got a lot of that framework already built in the front-end, so let's take advantage of that. And we also wanted to make it easy to evolve and extend. So users shouldn't have to invent or learn new pattern for every plug-in. Now, how did we go about building this new system? So we started sessions and design sessions and prototyping at the beginning of this year, using a top-down approach and focused really heavily on the back-end setup and installing new plug-ins and slimming that down to what you need to get up and running. We settled that and found a design that we were happy with and published it as an RFC for some time in the middle of May. And we're all pretty happy with our approach, so we got straight into building the new system. And that was around summertime, and then we've been shipping early versions of the systems for the last couple of months. Now, let's explore what this system looks like with a high-level overview. The base of the new system is the back-end instance. You can think of this as the unit of deployment. The back-end itself really has no functionality at all. It's responsible for wiring things together. Next, of course, we add plug-ins, which house the actual features just like in the existing system. So plug-ins operate completely independently. If they want to communicate with each other, they have to do so over the wire. So there can't be any direct access between plug-ins. Now, because of this constraint, a plug-in is very much like a microservice. It allows you to decide the way you structure your deployment in your back-end installation. So you can take all of your plug-ins and put it in a single back-end, or you can go the other way and other extreme and split every single plug-in into its own back-end or something in between, all depending on your need to scale and isolate individual plug-ins. Next up are services. So we don't want plug-ins to have to implement everything from scratch, right? It wouldn't be a very good plug-in system. We provide access to services like the current ones, the database, task scheduling and configuration. Now, those are the built-in services, and there are many more, but you can also import existing services, and you can easily create your own custom services as well. Now, like the other building blocks we've seen so far here, services is nothing new. What is different, though, is that they're now provided from the back-end through dependency injection. So this means you no longer manually need to wire up your back-end to pass services to each plug-in. And a shout-out to Jussi from Rode for helping out with that earlier. We also wanted services to be a customization point. So just like in the front-end, where you can replace services. So you can completely replace a service, or you can also take existing service and customize it, like changing the middlewares of the HTTP service. We've also seen these plugins for extensions, or extending plugins themselves, like the processors in the catalog and the actions for the scaffolder. So we have now created a common pattern for these types of extensions that we call extension points. So they function a little bit like services, where you depend on them and they're registered into the back-end. But rather than being provided by the back-end, it's the plugins that provide the extension points. Based on what each individual plug-in wants to expose, what kind of customization they want to expose. And extension points are also decoupled from the plugins themselves and exported separately. So it's much easier to evolve and deprecate individual extension points, rather than dealing with one single huge API service. Now, the last piece of the puzzle are modules, which use the plug-in extension points to add the actual features. So a module might, for example, add a catalog processor or a scaffolder action. And modules are quite similar to plugins. They also have access to services. But one key difference is that they must be deployed together with the plug-in that they extend, and each module can only extend a single plug-in. So in many ways, a module is a plug-in for plug-ins. So let's take a look at what this new back-end system actually looks like in practice. So all of that set-up code that you used to have in the back-end has now been distilled down to a single function call, create back-end. By moving all of that set-up code into the system itself, it's a lot easier for all of you to maintain the back-end, and it's also a lot easier to evolve and extend over time. There's plenty happening under the hood, of course, like wiring up all the built-in services that everyone has to manually configure before. We also choose to split out the startup into a separate function call, so that the API is a little bit more flexible, and it also makes it easier to add custom logic. This code, obviously, is not going to get you much more than just like a bare-bones back-end. So let's add the catalog plug-in. So rather than having to manually create and copy the contents from the readme that we saw before, we now have everything in one single call. So note that there's no arguments being passed into this catalog plug-in, and all of its required services have been provided from the back-end through dependency injection. This means there's much less code needed if you stick with the defaults, but it's also possible to make decustomizations. So you might notice that the catalog plug-in is a function call. That's because we give you the ability to pass in additional options for making lightweight customizations. So now let's look at the example of installing a module. So this is probably conceptually the biggest change that we've made from the previous system. As we mentioned, modules now are first-class citizens that you can create and install rather than some loose pattern invented by plugins themselves. So in this case, you can see that the scaffolder module extends the catalog by adding a processor for software templates. So let's see how this thing works under the hood. So I just want to make this clear that this is not code that you have to write to integrate with plugins. This is something that you would make as a plug-in author. So to create modules, for instance. So this is how it's implemented in the scaffolder, for instance. So we'll start with the scaffolder module because it's a bit simpler. You can see that we can create a new module. We give a plug-in ID and a module ID. And as implied, that only means that a module can extend a single plug-in. So in register, we add the init function for this module. We declare a dependency on the log of service and the process and extension point from the catalog. If you're familiar with the front-end plugins in Backstage, this is the same system that we use for utility APIs. As all of our declared dependencies then get passed to the init callback. Finally, in that callback, you can see how we use the catalog extension point to add a new processor. Modules are always initialized before plugins, so therefore there's no race condition between each other. Now, looking at the catalog plugin, you can see there's a lot of similarities from the previous module example. Again, this code is housed in the catalog plugin itself. So the main difference between... The main difference compared to the scaffolder module is that we register an extension point to be used by the modules. The reason we settled for this particular sort of nested in callbacks is so the extension point implementations can be accessed directly from the closure and the init closure itself. This makes it much easier to build extension points since you don't need to juggle internal and external services and dependencies and interfaces. This is a slimmed-down version of the catalog implementation, but in reality, the catalog plugin is configured exactly like this, today. The upside is that this code can evolve without having to get users to update their code themselves. And lastly, for people that want to go a little bit more extreme and do a little bit more customizations that we talked about, you can see how we can replace service APIs just like we do in the front-end. So, for instance, here we're replacing the logo with a cloud logger instead. We also added the ability to supply default implementations for new custom services, which means that you'll be able to import and use them right away without having to wire them up beforehand. All right. Let's look at some comparisons between the current system and this new one. So, in this example, I have a Kubernetes client service in an external package that I want to import and use in my plugin. So, if you want to do this today in the current system, there are a lot of hoops that you need to jump through. So, obviously, you need to import it into your own plugin and wire it up and use it. But there are also many changes that you need to ask users of your plugins to make to their backend installations. So, first off, you need to ask them to do what you see on this slide and add the packages to dependency. Then they also need to update the plugin environment type to include this new service. And they also need to update the backend wiring to create a manager and a plugin-specific instance of this service and forward it to the environment. Lastly, they also need to forward the service from the environment to your plugin. Now, that's a lot of wiring that everyone has to do for something that really should be simple. So, in the new backend system, this is a lot simpler. Thanks to the introduction of default service factories, all you need to do is import and declare dependency on the service. As long as the service has a default factory, that factory will be used to create an instance of the implementation for the service at runtime. Now, this means that users of your plugin won't need to make any changes to their backends at all, unless they explicitly want to override that service implementation. One of the more visible changes between the new system and the old one is the reduction in code size of a typical backend installation. So, comparing the setup of a newly created project, there's a quite massive difference. So, rather than nine separate files with 348 lines of code, we now just have a single file with 26 lines of code. That's more than order of magnitude difference. So, overall, this gives an indication of how much easier it will be to manage a backend installation once we've rolled this new system out. I'm sure something a lot of you are thinking is, oh, crap, another migration. And... Yeah, there will be some paint upgrade. But, as we saw on the last slide, it's mostly about deleting a bunch of code, especially if you have a somewhat standard setup. So, you delete the plugin-specific file and you replace it with a one-liner in the index file. Now, any customizations through the backend wiring, if you've done custom services, that would need to be replaced with a custom service implementation. And any plugin customizations would be either options or migrated over to be modules instead. Now, you'll be able to find both general and plugin-specific guides for this in our upcoming migration documentation. So, overall, this is going to lead to a much slimmer code base and that is going to be a lot easier to keep up to date. So, what about migrating the plugins themselves to the new system? It might get quite complicated, right? Nope. So, we've done a bunch of these already and usually what you need to do is you take the creator out there and you wrap it up in the create backend plugin instance. You depend on whatever services the plugin needed before and you also depend on the new HTTP service where you register the router and then you're good to go. Now, the plugin can accept options for any of the more lightweight customizations but if you do have a plugin with more complex patterns, that probably needs to be migrated over to extension points instead. So, in summary, the backend system simplifies the installation and it will be a lot less code for you to manage and there's going to be way fewer breaking changes. If you use to the frontend APIs and even the current backend system, these patterns are going to be recognizable and it is going to be a lot easier to evolve and extend this new system compared to the current one. All right, so what we've shown today is available for testing so if you like what you've seen, you can give it a spin. We'd love for you to share any findings or feedback either directly with us here or in the backend system channel on Discord which I just created an hour ago. Now, you can also read more in-depth documentation that will be evolving as we build the system out at the link at the top and we don't have a specific date in mind for a stable release. We really want to give this time to settle and respond to feedback but we're hoping for some time early next year. So, hopefully we have time for some questions but you can also find us here, of course, and later on at CUBE, come. Thank you. Martina will run around. So, if there's any questions, raise your hand. Objections about migrating again. What are you expecting that to be available for us to expect? Available. Can't promise anything. So, we're hoping next year but it really takes time, especially from the experience with the frontend building out the frontend APIs as well. We discovered things that we didn't think about that need to be addressed so it does take time to build this out but the more feedback we get and the more people try it out the quicker it's going to be and also the question was when do we expect this to be available? So, question is what about running this system in parallel with our current one going to be deprecated? I hope so. It's basically the answer. So, we haven't talked a lot about running them in parallel in the same backend. So, as mentioned, plugins, they are quite a lot like microservices. You can really split them apart and move them over one by one from two different installations. If you have a big and complex setup sort of a related question if you're running multiple replicas for the same deployment for pods, for example how how friendly is this migration going to be for not killing your three other pods as one of them updates? So, I guess how much testing of that has gone on? So, maybe struggling for the answer. So, I guess if you mean you want to replace the migration for running a new system. I guess the APIs have the contract. The APIs of your public-facing APIs like the catalog API, whatever you're using that's the contract, that's not going to change. It's just the wiring together of the backend system that surfaces these plugins that's going to change. So, I guess in terms of downtime I don't think you should have any I don't think there's any migration from an end-user point of view it's just integration point of view a plugin or a point of view. Does that answer your question? Sorry, I'm unsure if that was the answer. I guess for clarifications' sake database migrations I'm assuming this comes with database changes. No, I don't think it does. No, it's nothing like that. So, this is basically just all JavaScript code that does all the wiring of the backend together. It's been 19 much happier. The plugins are all the same still. It's just the actual export of things from the plugins. So, the database migrations are contract somewhere else. This is just what the plugin exports and how we wire everything together. We got there in the end. Thanks. I'm curious if that's the right forum. What's the roadmap of data model evolution for Backstage? So, data model evolution and Backstage. I think the catalog SIG is the best place to show up and discuss that. Yeah, I would say so too. In terms of roadmap and what we have planned kind of hard to say. I don't really know yet. I'm going to defer this to product at some point. But yeah, not sure. I think SIG is probably the best place to start. We can start driving that roadmap and driving our next priorities, I guess. It's a catalog SIG. So, you can find information about the catalog SIG in Backstage slash community, I believe? Yeah, good. Charles has a question. This is for Charles. So, this is Charles's question. What are your thoughts on being able to unmount a module once it's been activated? Like at runtime? Dynamic plugins. I remember that discussion. We're looking at most likely introducing some form of lifecycle service to kind of hook into events of start-up and tear-down. Just especially for start-up where you want to get HTTP serving listening kind of at some specific point when the backend is ready. No concrete plans yet. Work with us to implement it. Sounds good. I think that we have no more question. Well, one last question is how do guides and as well as some video to show, because if I'm kind of a little bit looking at how much effort we need to put in migrating the current one to the new. Or maybe what I was asking automated tool or maybe you can build some automated plug and you can discover like, okay, you know this is the change within this backstage I guess maybe it's possible the pattern of code mods is what you're essentially talking about. So the question is can we make migration easier? I think it's hard because there's quite a lot of hundred and code already that's kind of very specific. When we mentioned in the presentation that there's no real standard for it already so we're going to set these patterns and make it easier. There's going to be some effort but mostly hopefully going to be deleting code like Patrick said, which is always good. We always want to do that. Of course with our documentation of course. Do we have some documentation already? Some documentation is up. Migration documentation is not up. So also where the backend system is kind of coming from is the pain to migrate or upgrade. So that's a good address as well with this new system. Great. So give it up.