About this talk
So you've thought about building a central component library to use across your projects, but will it last? Will it hurt developer productivity? Can you still iterate quickly on your UI? We'll be exploring how you can build a component library which can be used across teams and projects with no friction by using a nice semantic API, and also survive the inevitable ad hoc hacks by other team members.
Yeah, hi everyone, I'm Jamie, I have been here before, but many years ago. So, a few familiar faces, who I've worked with before, working with currently. But yeah, I'm a full staff developer at the moment, contracting at a couple of places, one of which is in Bristol and I'm gonna be using some of the stuff that I've been doing there as an example today. But yeah, I've been using React for several years now, been building all kinds of cool things from websites, apps, using React Native and even light shows for nightclubs, all kinds of cool things. Unfortunately today, I'm talking about the very boring topic of component libraries and building and testing them. So just so I get an idea, can I have a show of hands, who's using React or has played around with it? Okay, a lot of people. Who's using React professionally, like in their day-to-day work? Still a lot of people. I'm just thinking of more questions I can ask now. Who's, yeah, well, no. Who's got a kind of component library that they use at their organisation and maybe share across multiple projects? Even if it's just two? Less hands. Cool. Well, yeah, so at places I've been at before and currently as well, the fairly common theme is wanting to centralise components which are being used on the front ends. I'm not talking about across platforms, so I'm just using web for this example here. But yeah, you know, there might be multiple products, whatever, and they want to use the kind of visual style guide that applies to that organisation. And not only the branding and the style of it, but also the API, they want a consistent API across the products that they're using. And it's worth noting all the stuff I'm talking about today isn't specific to React. It can be applied to any component-based library, so Angular 2 Plus, Preact, View, whatever. I don't really use those, but sure. So quick agenda. Quick history of HTML, so I'm just really back to the start very briefly because there's a few interesting things there which I think are just worth recapping. And then we'll go through how to split up components effectively, iterating quickly on your component library without breaking things, testing and just briefly at the end versioning and releasing. And yeah, it's worth mentioning with the splitting up of components and pretty much everything I'm saying today, it's very much a matter of opinion and it's just really what works well for me. So I'm hoping that you'll all be able to take away something from this. I'm not saying, here's how you should do it, but hopefully it will just spawn a few ideas or give you some pointers. Yeah, if we just rewind a bit and go all the way back to the first website. So you know, back then the World Wide Web was just documents pointing to other documents with links going between them. Very basic, no styling, nothing like that, and it was created as a means just to share information. So the markup around it was purely semantic, it didn't contain any kind of presentational configuration, it was all about what was on the page rather than what it looked like. Here's a quote from Wikipedia. So sematic HTML is the use of HTML markup to reinforce the semantics. So you hear a lot about this nowadays, there's a lot of UI frameworks out there for, claiming to be semantic, providing semantic APIs. So you've got Bootstrap, which technically isn't really semantic on its own, but you can use it in a way which is semantic. You've got things like semantic UI. A lot of these tools and frameworks in my opinion though, don't do a very good job of getting the semantics right though. So yeah, it's all very well at the start being just documents, but today we've got very complex web applications which we're all building which have many different UI states and elements. Branding, they're very visual, lots of custom design. But if we just take, just step back from the design and think of what's on our pages and the semantics, and if we can describe our pages with a semantic markup and semantic API, it makes it easier for a computer to read. So I'm thinking screen readers. So a lot of you have probably done this where you've littered your markup with area tags or you've been using the correct HTML5 elements so that screen readers can better understand your pages. That's all a push to try and improve the semantics of the page. But also in my opinion, if you kind of abstract away HTML for a minute, imagine that you're not working directly with it and you're, you've got your own component interface. If there was an interface where you were just purely describing what was on the page rather than how it looked, in my opinion that would be easier to work with. Because when it comes to actually building features in my application, you know, if I'm working on a story, I've got specs, something to build, I can just smash out what should be on the page rather than having to worry about what it looks like. I do a lot of front end work, so I'll be doing that look and feel at some point. But when I'm actually working on a feature, I just care about what's on the page. So yeah, getting true semantic markup with HTML, CSS is very difficult, sometimes impossible. So I mentioned you can wrap things like Bootstrap to make them more semantic. So you might've read a blog post or seen online that the kind of API that Bootstrap gives you with all of its columns and, yeah, all kinds of class names, is not very semantic and instead you should be wrapping those with your own class names so that the markup that you're writing describes what's on the page rather than what it looks like. But a lot of the time, you still have to write HTML just for HTML's sake because the look and feel of the page can't be represented with true CSS. It's only there as an add-on, essentially, to style certain elements on the page. Sometimes you need extra elements on the page in order to get what you're looking for. So imagine some kind of complex select box where it had a dropdown and all kinds of things going on. You couldn't just go, I want a select box with these items, you'd have to create all of that complex HTML to complement the CSS that you've got. So you're left with not very semantic markup and a very tightly coupled HTML and CSS. So if, well, a lot of you are using React so you've probably heard about all of this CSS and JS. And the idea there is that the separation of concerns should be at the component level rather than at the technology level. So that kind of solves the problem of having a tight coupling between HTML and CSS because all of the stuff needed for just a single component is in one place. But the HTML that you're writing will still be quite verbose in some cases. Yeah, so React to the rescue. You can move all of this markup, so in my select box example, all of that extra markup you need. You can move all of that into a React component and then your API to that can just be a select box with these items, you know, you could specify items as strings, labels, strings for labels or maybe further React elements, whatever you want. Yeah, components can be composed of other components, you already know this if you're using React so I'm not gonna spend too much time talking about that. But yeah, how do you split up your UI components? And are they really isolated? It's all very well using React having your UI components, but a lot of the time, and you see this a lot with React UI libraries online, they're not truly isolated and they often have, a lot of the components themselves have a lot of responsibilities, responsibilities that they shouldn't probably have. I don't wanna get too ahead of myself here. Yeah so, make sure your components have a single responsibility and are isolated. So I like to think of the components that I'm building as a black box where I only know the API, the props that are going into it, and the output, because that's what I care about and I want to render a select box because I know that's gonna give me a select box on the screen I can click on and those items. And actually, the implementation of that really doesn't matter. Instead of worrying about choosing a CSS and JS framework or how you're gonna do styling for your components, if everything's truly isolated then it doesn't actually matter because you can just swap out the implementation and it shouldn't affect anything. That sounds like I'm making it out to be really easy but often a lot of the time you will spend a lot of time choosing how you're doing your styling and trying to apply something throughout the project. And when, if you wanted to change that, it could be quite a big task and that's probably because you don't have a truly locked down API. So in this example here, I get a pointer now. So my form component here has a class name, which I'm passing in as a subscribe form. So imagine this is like a subscribe to newsletter kind of form. So I need some layout for this page. I might need some margins just to move it away from other things on the screen. So I've got the subscribe form class name to apply that margin that I need and then I've got field component with a label and a span element and a text box for the email address and then I've got a primary button with a class name there as well because when I put that button in, it didn't end up quite where I wanted it so I had to put a class name on so I could move it down the screen a bit more. And then I've got my label for the subscribe to newsletter. Now the problem with this is we've kind of, we've accidentally increased our API surface area massively by accepting this prop here. So by accepting the class name, we've allowed for people to just come along, render our form component and apply whatever styling they want as in, when they want to. Whereas actually, it would be really nice if we could lock this down and make sure that people can't just move this form element around the screen if they want to. And if we look at our field component as well, we've got the label and a span there and a text box, but actually this label is quite freeform, it's just there as a child of the field component. It's not very explicit that that is the label. But obviously, you know, you can read that and think, okay, that's the label of the text box. But the API doesn't indicate that. You know, I could order the input box to be before the label all kinds of weird things. And then the button as well, I've put a class name on there so you know, you could do all kinds of hats to change what that looks like. So what about something like this? What about passing through all of the parts of our form explicitly through named props? So my submit button has been passed in as a primary action prop, I'm no longer having to hang off a prop on the button saying it's a primary button because it just knows it's a primary button because it's the primary action to the form. Yeah, we've got the label to our button still. And then our form field, the field component is now nested in the form component. This is just a style I like to do because it just shows the hierarchy of components a bit better. And the label's also passed in as a named prop, and as a string as well. So I'm not just passing in the label as a span element, as just a freeform child on that component. So I mentioned when I put the form theoretically, I put the form component on my page and it wasn't quite where I wanted it. So now that I don't have those class name hooks, I can't move it around, right? But this actually brings us on to separation of concerns and not between languages, not between HTML and CSS, let's forget about that. I'm talking about between components, right? So if we think about the responsibilities of a simple button component, we want to render some kind of surface with a label, we want to indicate that the user can touch it so that they would see it and they think, oh yeah, I can press on that. They can press on it, display a meaningful label, and we also want to fire a callback when that button is pressed and we can do something when they press it, right? But we don't wanna do any layout. We don't want to dictate where the button is or move it around, apply external margins inside that button component because then it's just not portable. That button would then move things around just based on where it's placed. Layout instead should be kept separate. Yeah, keep layout separate into dedicated layout components. So now you've got my attempts at trying to represent this. I have got some screenshots as well. But what we commonly see in UI libraries nowadays is kind of like a flow of components where you're expected to use components in a certain way. So in Bootstrap's example, you expect it to render a form label, an input box, a form label and input box all inside form group components. And the problem with this is then when you actually go to use it, these components then have margins, often on the bottom, sometimes on the sides. Which then means that if you want to render that in isolation, so let's say I wanted to render an input box on a navigation bar or something, there's then just some random margin just hanging off the end of it. Now the Bootstrap CSS accommodates for this because there's a lot of CSS in there to go when I place an input box in this navigation bar, then there shouldn't be any margin hanging off the bottom or when I place this button in a list view, then the margin should be applied appropriately. Whereas actually, we could just avoid all of this just not have the margin hanging off the form fields and do the layout another way. If we look at material design, this is even worse. It's got margins just all over the place. I don't really know what's going on there. I've tried to put in material design in isolation on a project where I was using my own components. I just wanted a few of the components and yeah, the layout was just crazy. I expected the components to be put in all kinds of places, yeah, just about. So wouldn't it be better if the margins weren't there in the first place? So I know this doesn't really mean much, but imagine something where you have a layout component which kind of sits at the top and everything gets passed in through these slots, these kind of hooks, right? So in this example here, we've got this kind of containing component here. And we'd somehow pass in input boxes, our form controls, and they would just lay themselves out. And the form component, the containing component would dictate how to do that. By shifting, yeah. So by shifting the layout to these dedicated components, our normal components are now far more portable. They can be put wherever without moving things around. But yeah, let's have a look at some code for this. So we've got a page here, oh, my wires are all tangled. Yes, we've got a page with a title, very simple stuff here. And we've got a form like I was kind of showing in the other slide here with button, primary action, two input fields. And then we've just got some content here at the top, some paragraphs. Now, this doesn't actually have, by looking at this, it's quite semantic. There's no kind of indicators on what this might look like and how the layout might work. Because if we're not hanging the margins off our form fields, how will there be spacing in between them? How is there gonna be spacing in between our content and the form? Where's the content gonna be on the screen? This is actually dictated by the page component in this example and the form component. So obviously in the real world, these components will be far more complicated in how they actually do the layout because you'll have your template for the page. You might have sidebar, navigation bar, all kinds of things. But just for this, for the example of this page here, I just wanted to render a title and then stuff underneath it. So it's something I quite like to do, I tend to have these layout helper components, so something called stacked here. Where all this is doing, is it's gonna put space in between all of the children. So the children themselves, the header and the form fields, the form, they don't have any margins. That's all handled by the stacked component that actually does the spacing, actually puts the spacing in between those. Yeah, we're just rendering header, some children. Same with the form actually, we just want to render all of the form fields that we had over here. So form fields, form field. And then we're just gonna take that primary action and just slap it on the bottom as well. So you might think, what's the point of doing this? Like, all you're doing here is just rendering the form fields and the submit button. But actually by doing this and by having that clean API, it makes it a lot easier to work with in the future and you can change completely how this component works behind the scenes where the fields are, where the buttons are, primary action, whatever, and you don't actually have to change the core site, you're only changing the component itself. Whereas if everything was passed in through children, yeah, good luck. You'd have to change all over your application. Form field as well, stacked, I want to render a label. And then just a single child, so we're just enforcing a single child here. So back over here. Yeah, so passing label is a string and then we're passing in a single component for the actual input so here we've just got an input text, which, it's just an input text box. And yeah, we're using stacked there as well. But you know, you can create these layout components to help you with all kinds of things. You might have a row component which will help you with a responsive grid. So mobile, tablet views, whatever. You'll want some kind of horizontal equivalent of this. Something common you see is like a header and then some buttons pushed over to the right, you might want something to abstract that way. But yeah. Content is the only exception to this though. Exception to the layout hanging off the components, I mean. So if we look at the content block we had on that example, we've wrapped it in this content component, and then we're just using HTML here. So didn't really see any point in abstracting away HTML for content purposes just because you may as well use those HTML tags directly. And here what we're doing is we're actually, we're actually going to put just regular CSS to put margins in between paragraphs, just like you would do on like, the default styling for a webpage. So there will be margins on headers and paragraphs, but that's isolated to those kind of typography related components because that flow of the layout when it comes to typography is actually quite nice. But it's just not very desirable for the application level in my opinion. And yeah, so the style cascade there is isolated and scoped to the content block. So if you're using something like style components, you can just cascade those CSS rules down. But yeah, so our isolated components are more portable. I can put them wherever, it's not gonna break layout. But the reality of it is, even with our locked down API, even with our isolation, you still might not have the confidence to change your components. Especially if your component library is used in multiple projects and it's the centralised place for your visual style guide, everyone's using it and you want to change the button component or your form component and, oh my god, I'm gonna break everything. So what tends to happen is, people will pull down, so the project's using your component library, you'll pull down the library and then you'll tend to just stick it on a certain version, do all of your kind of styling that you need, maybe not even contributing those changes upstream. You'll just do them there and then on the project. You'll then lock down the component library to a certain version and then yeah, upgrading it becomes this massive technical debt task. So this is really common when you're using something like Bootstrap, I've seen so many times people using like, Bootstrap 3. You know, they've done all the CSS on top of it and now I want to upgrade to Bootstrap 4. That's the next week of my time. So you know, how did we get here? We want to be able to work quickly on our components, we want to be able to change things without breaking everything. So regression testing, right? This is what we do for just software delivery in general, right? You know, we're using things like Selenium already, Webdriver, we might be doing API testing, all of these things to prevent regressions and to give you the confidence to make changes quickly, especially if you're doing continuous delivery. But how do we do this for a component library? How do we test our components without just increasing the workflow complexity and making everyone's lives hell? The solution isn't to use Selenium, Webdriver, in it's traditional way, so yeah. So Webdriver is always used for kind of automating user actions, what they would do on the page. And then you assert some kind of result. So normally that's asserting I've landed on this page, I want this data in this table, something, some expectation so that you know that those actions have been performed end-to-end. But the visuals can completely change of that page and the test will still pass, which is actually what we want because otherwise, we'd change just a few borders, some margins, whatever, and all our tests would break. We don't want that. But for a component library, we do want that because we want to be able to see every time something changes visually, which could break in our feature level code. Another tool that I've used before is something called Galen. So I don't know, has anyone heard of Galen or used it? Okay. So it's basically a tool where you write these special files using the Galen syntax and you describe the layout of your page in quite a high level. You say you want this element to be next to this element, this should be above this, those kind of things. And then it renders your page and then asserts that that layout matches what you have described. But this really does start to break down because every time you change something small, your tests are broken and to actually update those files to reflect changes, valid changes that you've made, it just takes, takes ages. So it slows down the workflow, it's just a pain. So what about screenshot testing? What if we could take screenshots of all of our components, have some kind of baseline for what it should look like and then be notified if it doesn't look like that, if it's changed? Now this can be a really bad idea for testing feature and app code because like I mentioned, like I mentioned earlier, if you're constantly throwing those errors when anything visual changes at the feature level, your test will always be broken. And you just want to test that your feature actually works functionally in your application. But actually, we want this behaviour in the component library as long as it's surfaced in an easy way to review the changes and it's really easy just to mark differences to say, yeah, this is actually a valid change, this is a valid change. Now if we're applying all the stuff I mentioned earlier, so locking down our API, preventing kind of ad hoc styling, it means that if we're testing our component library in theory, all of the visuals throughout our application should be tested as well. I mean sure, the markup could be changed, things could be shifted around the page, and that would change, that would all go through without any tests failing. But if anything actually breaks in the components themselves you would be notified of it. Yeah, so screenshot testing is not new. There's plenty of tools out there and frameworks to deal with it. They tend to do it though, with an approach where it will take a screenshot of what's in production and then screenshot what you're working on now and then compare those. So maybe your branch versus production, and then it'll surface those. They also tend to be quite these bulky pieces of software that you have to use or they're just hosted in the cloud, they're not tools which you can just instal on your machine. But yeah, instead of comparing screenshots against what's in production, a simpler way of doing it I think, and this is also not new, but it's just what we're gonna be using here, instead we're gonna take a baseline screenshot and we're gonna store it in our source control. So that means it's versioned, it'll be in branches whenever it's changed or updated. That could just be there in your pull requests along with all of your other changes to the code. And yeah, we just take the screenshot and compare it to what's stored in source control as the baseline, and if it's different, we'll then throw that as an error. So I'm gonna give a quick demo of this. So one of the places I'm working at the moment called Parmenion, they've been building a component library which is gonna be used on a lot of their projects, a lot of their front ends. Mainly just to centralise all of the presentation layer so that people can develop features without having to worry about CSS and all of that. So this is a tool that I've developed there. It's called Screenie, it's a bit of a meh name. But basically all it does is exactly what I've just been talking about. It will go through pages that you've defined, it will take screenshots and then it will surface those differences to you, and it's just a command line tool. You can just instal it, just like Mocker or something like that. I didn't write this as a library on top of something like Mocker or Jest even, just because it's something that has quite a lot of moving parts for how it actually orchestrates the test run. So I needed quite granular control over that, so it is a runner in itself. But yeah, we've got quite a few tests, well, not many, but we've got some tests here and I just ran them and they've all passed. And the tests themselves, if I just pull those up. Let me just zoom in here. So the tests themselves are just very simple, you just give it a name and a path, which will be, have a screenshot taken of it. So I want to screenshot /containers/accordion, /containers/modal, and then all of these screenshots are then stored in under store screenshots. And then, there. So yeah, just in the source control like I said. Now this is very similar to, does anyone use Jest, at all for testing? One brave soul. There's something, so there's Jest snapshots, which can be used to, it works with React really well. It basically, you shallow render a component and then the next layer down, the next level of components down gets serialised and then stored in your source code. And then every time that, every time that changes and it doesn't match the expected snapshot that's stored in your source code, it will throw an error and you are given the chance to update it if it's a valid change. And this is something very similar, it's just that our snapshots aren't serialised React renders, they're screenshots. Now these pages, which, they're going to here /containers/accordion, markup just looks very much like this. So I can just change something here. Oh, that's not how. And if I run my test, that should surface a failure. Now like I said, there's a lot of tools that do this already, they all tend to use things like PhantomJS. Now the problem with that is it's a browser which no one uses, right? PhantomJS, even though it's used in web kit, there are slight differences and I'd rather render my components in an actual browser which people will be using. So you can see here that the test has failed, so it gives you the choice now to open a browser and actually see the dif, so I'm gonna do that. And I can see all my tests here, let me just zoom in. So I can actually zoom in, see my tests. And if I go here, I can see that the dif is actually brought right out, highlighted in red. I get some kind of like, overlaps dif over here, sometimes that's easier to see. And then I get the side-by-side here, the reference what it should look like, and the screenshot that it's just taken. And yeah, that's just completely broken. You can update that so that's a valid change, and then it will rerun it and it should pass. Yeah, excellent. Now this is great for testing all of our components, but we can actually use this to now test across browsers, across view ports, and for the component library here at Parmenion, we actually have quite a lot of different themes for all of our components so depending on what site it's being used on, what project, the components can look completely different. They don't at the moment, but the place it hopefully will end up, they will look completely different. So we can test all of those components for how they might look across all of those different possibilities. So what you can do with this is you can pass in, like, multiple test matrices here. So at the moment I am just gonna stick to testing in Chrome because that's all I have on my machine at the moment. But I can say here that I want mobile, I also want to test tablet as well. I'm also gonna test some different themes here. Now I haven't just, it doesn't just read mobile and know what to do suddenly. There is some config here where those different view ports are then mapped onto actual widths and heights. And you build, this is all using Webdriver and Selenium, just in case I haven't mentioned. So here we're just building up our normal Selenium desired capabilities if anyone's used Selenium before. And then the view port size just comes in from this object map up here. So yeah, if I run this, I should suddenly get quite a few more tests, but that's kind of spanning all of those different view port sizes now and those different themes. Now my Mac is quite dated now, so I can only run so many Chrome instances on it. But on the machines that we have at Parmenion, 16 gigs of memory could be considered like a standard benchmark now for machines that you might be working on for development work. You can easily spin up 20, 25 Chrome instances and run 25 tests in parallel and it all finishes in just 20 seconds, 25 seconds. This integrates really nicely in CI, so like I mentioned in pool requests, so we use gitlab for our CI stuff and we actually have it so that when the tests run on gitlab, it's spinning up a pre-emptible Google cloud instance which has, I think it's 32 cores, 64 gig of memory and we can just go all out, test them all in just a few seconds. So yeah, you really do have possibilities here for how you want to do this. And because it is just Selenium, you can run Chrome on your machine. This is actually, what it's doing here is it's using an out of the box package, which comes with Screenie where it runs these Chrome instances in parallel using Docker, so it's all behind the scenes. And then yeah, you can see the difs and suddenly there's quite a few more tests here that have been run. And you can go into all of these and just see all of the different screenshots. So this is really great for if we want to iterate quickly on our component library. I mentioned that it's being used on multiple projects, it could be really easy to break something. But fortunately, every little change that we make gets surfaced using the screenshot testing and we can be notified of it and say, no, I didn't intend to do this. So it's quite common for someone to be tweaking the theme variables, maybe changing some border sizes, and they think, you know, when they're testing it on their machine it all looks okay, but then for this theme over here on this view port it's just completely broken. You just get notified of it straightaway. Yeah, that's pretty much it from the screenshot testing side of it. So it's definitely helped our workflow. And this is open source, the link should come up on the, there we go. So it's open source, you can use it today. But like I said, there are plenty of other tools to do this, it's just that this is something which I've put together just because it's quite lightweight, it's very modular, so in my usage of this I'm probably gonna put in something where it can test mobile apps as well. So React Native kind of stuff. Yeah, so how you take the screenshots is completely separate from the actual test runner itself. So you can just swap out the implementation for that. So now that we're iterating quickly, I know I've only got a couple of minutes left. Now that we're iterating quickly, now that we're changing our component library with confidence, how do we actually release this? How do we version it? So I highly recommend use semver if you're not already. Who's using semver for their internal packages? Nice, a few people. So what this allows you to do is mark your breaking changes to the consumer by just the version string. So the first digit in a version, so like 1.0.0, the one is the major version, the second digit is the minor. So whenever there's a breaking change, it would go up to version two from version one. And then your consumers can then say, okay, I want anything that's in version one because any updates there are guaranteed to be safe, they're not gonna break anything. But this is okay for just libraries with simple APIs. Because if the API changes in a breaking way, that's clearly a new version. But with the component library, the API might change but it also might not change, but it looks completely different. And is that a breaking change? Don't know. So the approach that I've taken to this is kind of just have an internal discussion for what works well for your team. For us we started off where every small visual change was a breaking change. But that just, yeah. Colours were being changed ever so slightly and we're on version 50 already. It just got a bit out of hand. So we're kind of in the works now with thinking, you know, any major visual changes. So layout, things moving around, that should be a breaking change. But if we're just changing a few colours, then nothing's actually broken there. If nothing looks different, then that could just be a minor, a minor version bump. But it's always best to have a clear change log so that all of your consumers can see if there are breaking changes, how does this affect me? And because when your component library is central to your organisation and everyone's using it, you still could be having a lot of new major versions coming out and you just want to make sure that the upgrade path is very clear so people know whether it's safe to upgrade. So this is something, just a quick screenshot from what we have. We just highlight those breaking changes really obviously, so here, version two, we increased some spacing between character inputs, which at the time, we considered that to be a breaking change, but probably not so much now. But yeah, and then we're changing APIs and all kinds, so that should be breaking. But then yeah, what you can do when your component library gets a bit more sizable and you kind of know what components you need because you're using it in production. You can try splitting out groups of your components into different modules. So maybe layout can be its own module. And then these can be versioned independently and you could have some rules in place even and say, we don't want you to use any layout components in feature code because that should all be wrapped with something that's in the component library. And then yeah, the dependencies between these modules can then be defined as version ranges. So if you had some components in one project, you could then say this depended on between version one and three of the layout library because you know it's guaranteed to work with those versions but you haven't tested version four onwards kind of thing. So it just gives you that flexibility. But I don't recommend you do this from the start because it's always good to start off with a monolith and just gain confidence in what you're building first and then split it out if necessary. Cool, yeah that's it.