 Mae'n fawr i'n ddweud o'r cyfnodd. Oherwydd, y gallwn gweld i'n meddwl o'r rhaid yn ymddangos cyd-fawr, mae'n ddweud i'n meddwl o'r cyfnodd. Felly mae'n meddwl i'n meddwl i'n meddwl o'r cyfnodd yn Python i Django, i PHP, i .NET i ddim yn llwyth. Mae'n meddwl i'n meddwl o'r cyfnodd, ond nid oedd yn ymddangos cyfnodd yn rhaid, yn ymddangos cyfnodd ymddangos cyfnodd, bydd gan ymddangos cyfnodd yn wirionedd yn ymddangos cyfnodd o ddigonol i wych yn ymddangos cyfnodd, o ddigonol i wych yn ymddangos cyfnodd. Felly mae'n meddwl i'n meddwl i'r reilau, byddwn i'n mynd fel yma'ch cyfnodd i'n meddwl o'r cyfnodd yn joyful o ymddangos cyfnodd yn gweithgawr yn dod. Felly mor yn meddwl i'n meddwl o'r cyfnodd yn galluiych o boblans bydd wedi cael ei со yncyrgyrchu. are the things that are unique to the problem that I'm trying to solve. And I've just never felt like that about mobile. I've spent time building native mobile applications in Java and Android, and while I quite like the languages, I think the tooling is very impressive, I've always felt like I've spent a very long time solving fairly standard mobile development problems, fairly simple things like making API calls like persisting data things that I assume every other mobile developer has also solved. I feel like I've spent an unusually ar gyfer am ddifodol. Ac if you add to that the fact that realistically if you're targeting native apps at iOS and Android and you have a web version, as soon as you start supporting all three platforms, for every feature you build you probably need three developers to build it. And that's something I've always really pushed back on, that means I've always been the guy sitting at the back of the room in sprint planning saying nope I don't want to do native yet because that's going to mean a big hit to our productivity. And because of that I've spent a lot of time over the last few years looking for magic bullets which of course don't exist. I've spent time building HTML5 web applications and deploying them with PhoneGap, and also trying compiled to native solutions such as Accelerator. And while both have a lot going for them, I've never managed to make a HTML5 web application that actually feels like a native application. It always feels like a web application that's been made a bit better on mobile. And the compiled to native frameworks, while impressive, for anything non-trivial, seem to degenerate quite quickly into if Android do this, if iOS do this, which is hard to test, it's hard to maintain, and loses a lot of the supposed benefits. And that's until about 18 months ago. About 18 months ago, at Cadapult, where I work, we started playing with React for our web applications. And we made a lot of mistakes when we first started playing with React, but relatively quickly, over a few months, we got to a stack that we were quite happy with. We started in converting small amounts of our front-end to it, and very quickly we ported everything across. Now, Facebook sells or kind of put React forward, as learn once, write anywhere, as opposed to write once, run anywhere. And I was kind of skeptical of this, because I've just seen so much of this, if Android do this, if iOS do this type code, for platforms that claim the same thing. But we were very pleasantly surprised when we started working in React. We found that almost everything that we learned building React applications for the web was applicable to React applications on mobile. If you try really hard, it's possible to make exactly the same mistakes on both platforms. But not only was what we learned architecturally relevant, we found that the libraries we were using in React Web, in a majority of cases, we could reuse those exact libraries on React Native. And that was brilliant, because that meant from a developer productivity point of view, any developer who could work on our web code base could pretty much instantly get up to speed and start working on our mobile code base. And that was really exciting. And it's not a very big leap to go from, well, libraries are just other people's code, to why can't we then reuse our own code across platforms? The point we started to get to is that a large proportion of our business logic, so the vast majority of our code, we can now share across completely native applications and our web applications. And the outcome of that, the reason I care about it so much, is that if we want to create native applications and a completely separate web interface, rather than that being three times the effort to support three platforms, it's maybe plus 10% for each of the additional platforms that we want to support. And as a result, I'm no longer the guy sitting at the back of the room going, don't want to do native yet because we're going to need three times as many developers to do it. So I'm Ben, by the way. I'm CTO of a company called Catapult, a staffing platform based in London. I've spoken here a couple of times before, but it's always been about Docker or deploying servers. And while I count myself as a full stack developer, I'm generally much more comfortable setting up infrastructure or building APIs or potentially just bootstrap, or at least up until I started working with React. And the reason I wanted to come and give this talk is because this React and React Native stack is the first time I really felt that the tools that I had made me as productive building front-end applications as I can be building Rails applications. So there are kind of three areas I want to cover, which I think we have enough time for. One of them is just at a high level what does a production React application look like? What are the components of that? Then in a little bit more detail, what are the important considerations for that stack if our main aim is the minimum level of friction when sharing code between a web and a native code base? Then finally, at kind of the other end of the detail spectrum, I've just been looking back over the last 18 months where did I waste the most time? What are the very small things that I spent a disproportionately large amount of time trying to work out? And sort of the things I wish people had told me. And I just run through a few almost anecdotal bits that I hope are useful to anyone who tries to pursue this path themselves. So the first and really important thing is React is not a framework. It's not like something like Angular that includes pretty much everything you need in order to build a front-end or a JavaScript application. React is essentially a view layer. React provides us with a declarative model for defining components. We pass props into those components, so we pass data into those components, and React works out what needs to be rendered to display those components based on that input. So if we're building for the web, that will be React DOM, and it will be HTML that it's coming up with for us. And if we're building for native, that will be native components through React Native. But fundamentally, React doesn't provide any of the rest of the stuff. It doesn't provide a way to make API calls. It doesn't provide a way to deal with persistence. And that can be a little bit confusing when people talk about this is built in React. And what of course is meant by this is built in React is that React is being used as a view layer. And a collection of other libraries are being used to provide everything else. And as a Rails developer, I'm quite spoiled. I'm quite used to the fact that generally for any given component of the stack, if it's not provided by Rails, there is probably a community consensus on, oh, it's your first time doing it, just use this. And the JavaScript world, that tends not to be the case. It was you should use this week and probably this next week. And it's all very, very stressful and very tiring. So what I'm going to go over now is a typical React stack. But I mean, we're not suggesting it's the best one or the only one, but it's a stack that we've been very happy with. It's the one that I've probably seen most used across other large React code bases. And it's one that we found is very, very friendly if your main aim is to be able to share your code base across web and mobile with maximum reuse. So a typical React stack is going to need four main components. It's going to need a router. And a router's job in React terms is going to need, for example, slash login in a browser and work out based on this URL which components need to be rendered. Similarly, in a native mobile application it knows that you are on a login screen and it needs to work out which components need to be rendered to provide a login screen. Shockingly, in a talk about React we're probably going to need some implementation of React. For the web we're going to need React DOM and for native we're going to need React native. State is a little bit more complicated. Now, as we said, React is not a framework. So there is no right way of managing global application state in React. There is no React way of doing it. That said, there are certain patterns that have been quite widely adopted across the community and patterns that we found to be very beneficial and very conducive to this code sharing. This code sharing approach. And you may have heard of them referred to with the flux pattern which is I think the original one or what popularized it. You may have heard it referred to by the name of an implementation of that pattern. For example, Redux, which is my personal favorite of those implementations. And these patterns and these implementations are developed almost as a reaction to the fact in a modern JavaScript application. It can be really hard to answer the question what is the current state of my app and how did we get there? If you imagine in a typical JavaScript application now you may well have a selection of models for getters and setters and you may then have callbacks within the DOM which are directly updating those models and you'll probably kick off some API calls which will also, when they return, update those models and very, very quickly we end up with quite complicated race conditions where it's unclear well was this updated by the button that I clicked first or was it by that API call and almost more importantly it feels very nondeterministic. It feels like each time we do it we can't get something different will happen. We can't replicate that state. And so what something like Redux does is say imagine that our state the entire state of application is represented by a single JavaScript object. In a traditional JavaScript application what we might do if we want to change something in that state is just have a callback someone clicks on something and then we directly manipulate the content of that object to reflect the new values that we want. The problem there is that potentially we can't do it once so it's unclear exactly what the value should be at any given time. When using something like Redux your state is immutable. The current application state can only be changed in one way. The only way to change the state is to dispatch what's called an action and an action is a description of the change that you wish to make to the state. When that action is dispatched you pass that action and the current state of the application can work out what should the next state be and return a completely new object with that state in it. The rest of the application can subscribe to updates to this state so that the UI for example can respond accordingly but it can't directly change the state. This means we have a very linear queue of changes. Everything that wants to make a change dispatch is something and they go through this process one by one. There is some amazing tooling around this approach that allows us to step through that so we can at any point get a completely what is the current state of our application and how did it get there and what's more if we want to we can rewind through that we can step backwards through our state and understand what would our application have looked like at each of these states. It's worth looking at a little bit of an example because this is quite core to the architecture that we've adopted. Here you have a very simple example. Imagine an app which is just one button and you press the button and it increments the counter and the aim of the app, it's an amazing game is to get the counter as high as possible. The app is in the app store. Here you can see labeled as an initial state the JavaScript object that represents our global state. It has a property count which begins as one it has the name of our current user and their rank. In a traditional JavaScript application if we want to update this account we might have a callback for the button and when that's clicked it goes in and updates this object. If we're using Redux we don't do this. What we do is dispatch the action at the bottom. The action is a very simple JavaScript object which has a type property. In this case increment and a payload which in this case is increment by but can be any combination of other JavaScript properties and objects. We then pass these two things the application's current state and the action into this reducer this counter reducer. That's a very simple switch. All it's doing is looking at the type of the action and then working out what changes need to be made to the state. But still it doesn't change the state because it's a new object it merges in the existing state and then makes any changes to this new object. It then returns this this overall object which is the new state of our application. The rest of our UI for example will have subscribed to changes to this and then the next action which is in the queue to be applied will be applied to this new state. So we have a very simple, very linear way of modelling what our application state is. Finally we're going to need something to manage side effects and I found side effects as a term quite confusing just because I'd never heard it before and it turns out that when people talk about side effects I'm fairly sure we just mean asynchronous stuff. So in the case of our counter app imagine that because it's become very popular we want to maintain leaderboards we're no longer doing this just in the app when you press that button we want to kick off an API call which will then update the server and maybe get back the current leaderboard rank of our user. So in this case you would have a library when I'm working with Redux I generally use something called Redux Saga for this and this basically listens for actions being dispatched and then it kicks off something asynchronous so in this case it would be an API call. When that asynchronous action returns it will then generally do something to handle it. Most of the time that will be dispatching additional actions. For example in this case we might dispatch an action that updates the rank value in our state to reflect the user's current rank. So those are sort of the four components that we'll end up with and there will be all sorts of other libraries things are wrapping API but architecturally those are the four big components and so we then have the question of what should be shared and what can be shared between a web and a mobile code base. It's quite important at this point that the reason I was interested in this originally wasn't because I wanted a way to make HTML5 versions of native applications I haven't come across very many production applications where the web version is just a scaled up version of the mobile version so one of my assumptions going into this is that the view layer of a keep doing that the view layer of a mobile application will probably be very very different to the view layer of a web application. That can be anything from the fact that forms which are only one screen on a web application may well be three, four, five screens on mobile to the fact that the entire structure of the application may be different. I'd kind of liken this to if we're working in Rails if we build a web application and want to add a JSON API roughly what we expect that to look at is all of our business logic will be sitting in our fat models or possibly these days in our service objects and then we'll have some skinny controllers some routing and some views which take care of the actual display of that data. We would never consider having a single view for a JSON API and a HTML web view because that would be full of if JSON view then this, if web then this and they're just very different things. I've generally found applying the same approach to sharing code here makes a lot of sense. That roughly means that the routing and the actual components, the view layer aren't shared but the management of states so how does the state of application change when particular actions occur and the management of side effects so things like API calls can all be shared very easily and the bottom tend to make up the vast majority of the code base so the benefits and efficiency from sharing them are quite large. It's worth having a look at a practical example so pretty much any application will have some sort of login page. So to begin with we're going to go to a login page our router which will determine what components to display and then we're going to have the view layer that displays them and these won't be shared. They could be very different on web and mobile. However when you click the login button you're going to be happening. We're going to want to dispatch an action to update our application's global state. At the very least we're probably going to want to update some sort of flag which says that we are currently trying to log in. That way whatever UI we have can listen for this flag and if it's true then display some sort of loading or progress spinner and hopefully at the same time we're also going to dispatch something asynchronous, an API call to actually log the user in. So we're going to have a side effect on the API call. This API call is going to go up to the server and come back. So that side effect is going to wait and it's going to listen for a successful response or a failed response and have parts for handling each. And when we get our successful response at the very least we're going to want to update our state to say we are no longer trying to log in so that progress spinner can be hidden. What's more we're probably going to want to store some extra data we're probably going to want to store a login token for example maybe some profile data about the user and then ideally we're not going to leave them on the login page. We're going to redirect them to a dashboard or a homepage or a terrible game involving a counter. And so the simplest way to do this is we then have our side effect issue a redirect to our router and our router then sends them to the page. And this is the bit that looks really simple but it all starts to crumble and fall apart. Cool. It's intentional but it worked really well. So this is where it all starts to crumble and fall apart because what we have here is a side effect which we said is shared doesn't matter what platform we're on we're always going to want to have something that happens after login. But this side effect is now talking to our router which we said is non-shared. So either this side effect is going to have to have some sort of if web do this, if mobile do this in it or we're going to have to stop sharing this side effect so we really don't want to do this. And as is often the case in the JavaScript world it turns out the solution to this is callbacks. And what we actually want to do is if we go all the way back to the start of this login flow when we click that login button the component containing the login button will also define callback functions it will define on success and on failure and recall that that's a non-shared component our view layer is non-shared so it's fine for our component to know about the router to know how the router works and to know where someone should be directed afterwards. All the side effect has to know is that callbacks will be passed in and they will correspond to a given interface i.e. what parameters to pass in and our callbacks are retained and encapsulated within our non-shared logic. Hopefully what this shows is that in the same way we end up with shared business logic including it in models or service objects in Rails, by following something like this approach where we share our actions and reducers and our side effects and we don't share our router and our components we end up with something that's actually a lot like building a regular React application and this has been really important because we found as long as we follow this pattern we're using the same libraries for native and for web and so a developer who can work on one is pretty much instantly productive in the other one. Now the bit of this slide I really don't like but it seems disingenuous not to include it is in my head this is very or ideally this would be very very clean side effect all was shared and view layers all was not shared and as you can see we have this very artfully faded out side effects bit that goes in the non-shared category because actually there's one particular area and there will definitely be side effects that you choose not to share and there's a sample app I'll share at the end which has an example of this and that's generally native functionality of the device I'm talking about things like if you want to interact with a GPS you clearly have asynchronous behaviours there which is the device's GPS returning new coordinates and it takes a lot of contortion to come up with a way that that should be shared between two platforms because you're never going to need that on web and I wasted quite a lot of time talking to a jurist about it and make sure that this was actually done in a component that was passed to this pseudo shared side effect and the only reason this is really important at this point in the talk is that this makes quite a big difference to how you structure the overall application because one approach is to structure it in such a way that all of the code that glues the libraries together and says which side effects and which reducers and which actions are loaded one approach is to have that as standardised and then make exceptions to it and the other approach is to have a web and native and then find that setup behaviour explicitly which makes it very easy to add these exceptions in when you want to and that will probably make a lot more sense if you look at the example code at the end but if you choose one approach changing to the other one is something of a pain so it's worth designing for that from the very beginning and that pretty much covers architecturally what I've found works and we've found that this is generally quite a productive environment to work in, it's quite a simple environment to work in importantly if you're following a tutorial and there are far more of those at the moment because React Web has been popular for much longer generally it will all be completely applicable to React Native as well and this makes the experience of working on this very, very simple you'll find that less so with React Native so anything device specific obviously won't apply to the web but in general this is just like working on a regular React application there's nothing particularly special about it and that kind of covers the sort of architectural side and how we found sharing works and so the rest of this is mainly about the bits that I kind of really messed up early on, the bits where I wasted a lot of time the first one is that we are building native applications here that means that we do need an Xcode project we do need a Gradle file we do need Objective C code we do need Java code and happily React Native includes a CLI which generates all of this for us very, very efficiently now what React Native will generate is quite minimal but it definitely works and because I never want to try and retrofit a React Native into a web project again even if I'm making a web project if I think there's any chance I want to build a native version I'll start by generating the native version just so all that boilerplate is there and then add the web bit afterwards now the built in CLI generator is fine but very minimal it's completely non-opinionated it is the bare minimum code you need for a React Native app so you really, really have a version to writing boilerplate I really don't like it and there's a fantastic generator by the guys at Infinite Red called Ignite now the Ignite generator is just a layer on top of the regular React CLI so you don't need to worry that it's introducing additional dependency it's not something where if it goes away it will cause problems for your app all it's doing is generating a load of boilerplate so you run the Ignite generator and if you remember the four components from earlier on it will take some of the most popular libraries from those and it will set them up and set up all the boilerplate that links it for you this is not a good reason to use something but it also generates an app folder which as a real developer makes me feel far more comfortable because everything is in an app folder that app folder will probably look relatively familiar you have in there subfolders for things like sagas which is for our side effects for redux which is for our state management for components which is for components really and the first thing I'll do is refactor those into three subfolders the only reason that's actually worth its own slide is because something you may want to do quite early on is take your shared code and turn it into an npm package and if you've done this from the very start it's pretty much trivial to pull back shared code out into an npm package because all your file plans will already be relative everything will already be properly namespaced and that process will take a few hours rather than a couple of days if you're having to unwind this from multiple other places the final one which ended up taking an awful lot of time way more time than I'd like to think about really is BabelRC now Babel is a or Babel can be used to transpile more modern dialects of JavaScript like ES6 into forms of JavaScript which are compatible with much older browsers so if you've used React Web before you've almost certainly will have come across Babel for this process and if you're working with React for Web the standard way of configuring Babel is a BabelRC file in the root of the project and in here you define what transformations should be applied to your JavaScript and in what order turns out order is really really important now React Native doesn't use a BabelRC file it does use Babel but it has its definitions baked into it a workflow I discovered in a very painful way is extremely possible is you will then add a BabelRC file to the root of the project to configure the website of the project React Native if it sees a BabelRC file the root of the project will then use that BabelRC file to the exclusion of all its own configuration so far so good there are lots of tutorials that explain how to set up a BabelRC file for use with React Native the problem comes because React Native has a package which caches incredibly aggressively this is to make so that when you make a change to one file you don't have to go through and retransfile everything in node modules essentially now what it's very easy to do is you make a change to your BabelRC file you test it on web, you test it on mobile everything works brilliantly you commit your feature and move on and days or weeks or hours or whatever pass and then something happens which invalidates this cache and you will suddenly get the most unintuitive error messages you will ever encounter my current favourite is type error no other words on the screen just type error in white on a red background it looks like a meme of some sort and so then you do what any responsible developer does or commits to ones that you knew were working so that everything starts working again but of course nothing does start working again because the change to BabelRC occurred a week, two weeks, three weeks ago and eventually you consider scrapping the whole thing of becoming a plumber or a carpenter or something that doesn't involve working with JavaScript and the solution is painfully simple whenever you make a change to BabelRC start the React package manager with hyphen hyphen reset cache then it will break immediately and you will know that your problems occurred BabelRC file true story now hopefully what I've kind of covered here is the higher level architectural staff in terms of what it makes sense to share how you can share it hopefully kind of got across there are some very real productivity gains that we're seeing from this the reason I was so excited to talk about it is this has completely changed at what stage in a project I now think it makes sense to start supporting native applications because I now don't see supporting both web and native as something that is suddenly going to require either a big drop in velocity of features or to hire a lot more developers and to me that's a really big change what I can't really cover in this talk is the kind of detail of how you glue all these libraries together and what that sharing actually looks like so what I've done is put a sample project up at talkingquickly.co.uk forward slash RailsConf 2017 this is a personal project that I've been playing with for a few months it's a little geologa so you work around different countries and it tells you where you've been and shows it on maps and the important thing is it demonstrates what this sharing looks like in practice and both the iOS and web bits are open source as is the Rails API server behind it so you can get a completely functioning app up and running and play around and see which bits of this approach you like and which bits you don't that's pretty much everything before we go on to questions so we're at Catapult, we're based in London we are of course hiring if you think this stuff is interesting if you're in Catapult.com or Grammy here and get a coffee, we're in London but most of the product team is remote and yeah, come and chat thank you so much for listening this was really good fun, are there any questions? yeah absolutely, a question for anyone who didn't get it is when you're structuring actions is there a way that you need to restructure it when they restructure it from a single file they get particularly big yes is a short one so I'm a big fan, so Redux and a library called Redux Source is a nice syntactic sugar on top of Redux and that pretty much provides a framework for having multiple reducer files and then ensuring that so if we go back to that one you can have 10 or 15 or 20 files containing these functions and it takes care of making sure that when an action comes in it passes it to every single one of those in sequence so you can break this code up and make it very modular so if you do actually need a mobile interface for the web version then what should you do there, is that right? so having said I wasn't originally interested in making HTML5 versions of that that's actually the exact problem I have at the moment because it turns out we also do need a HTML5 version of our web application which is upsetting yeah so two approaches and I'm playing with both at the moment so I'm not sure which is the best one we found is because we tend to use bootstrap as the basis for everything it's been very easy to convert our web interface to be a responsive mobile app there is also a new framework from Microsoft from Microsoft doing loads of really really cool react stuff which wasn't something I would have expected a year ago and they're working on a library which is intended to be an abstraction layer on top of react which allows you to define components and then have it work out what component would this be for web and what component would this be for mobile so if that's what you really want to do if you're prepared to lose a little bit of flexibility in terms of what native components and what web components you can use that may be a solution to that I haven't used it and I also can't have the name of it but if you Google Microsoft React cross-platform it would definitely come up so the question was how do you manage when iOS and Android have very different components how do you manage keeping them separate the honest answer for a majority of stuff generally I haven't come across many React has quite a good job of working out given the react component what the native component should be and providing a common interface across it and I'm incredibly sceptical of things doing that because everything else that I've tried that has never really worked so far React has worked quite well so I don't think we have any production code at the moment where we've got switches for display something different for iOS and Android because generally it's just defaults for how to display this particular control type on each platform and how to use the native components on that platform has been good enough for us so not something we've had to deal with yet happily oh that's a great question so what's the productivity of server-side standard sort of in not a bad way old-fashioned server-side rails versus this client-side JavaScript application honestly I've never found anything that I'm as productive in as making a server-side rails app so I want to make something incredibly quickly rails plus hammel I just find that incredibly fast I think what we've found with this is once you get a few months in so it's beyond the simplest application it's actually swung the other way so it's far quicker to build the first version in server-side rails but having this very clean separation of the API server and then the front-end application has actually been far more maintainable and it's been far easier to do in a maintainable fashion so I think for larger projects I would actually I never thought I'd say this I would tend now towards rails as the API server and sort of some variation of this stack as the front-end and that's because after two or three months I'd probably expect to be more productive in this version than in the server-ended version how much iOS and Android you need to know to start with React Native? Absolutely not I was super impressed even without the ignite generator and so on you download React CLI you will have to follow some of the getting started stuff for sort of Xcode projects and Android projects just installing the SDKs but it actually did just work I never believe it when someone says it just works in the context of mobile development but it really did it generated a project, ran the command and it was up and running in a simulator so you don't need to know anything so is there any element of web view to this or is it native UI components? it's all native UI components that are going on anything, any component that can be used in React Native will generally map or does map to native components and so does have the corresponding feel and generally the corresponding performance and of course corresponding performance for the standard applications that are displaying lists of things and taking data in I'm sure it wouldn't be suitable for making games but for most typical applications that are just taking information in and putting information out it will be providing native components at pretty much native performance any pain points working with React Native I want to really glib and say Android but that's really unfair but yes actually Android to an extent not because React Native is any worse on Android but just because the development process is a bit different because pretty much if it works on one iOS device it works everywhere, if it works on one Android device it definitely doesn't work everywhere because they're significantly different so automated testing and that sort of thing we found are very reliable ways of identifying bugs in iOS applications and in the majority of Android applications but you can't get away from the fact that you need that extra layer of testing across Android devices with different capabilities cool so what's the strategy where you want to do an awful lot of customization in the UI I think on almost two levels to that one is the increasingly and when you look at a lot of the very custom components they're actually just quite heavily styled and generally React is quite good at doing that so you can get quite a long way like that if you reach a point where you actually need to write a completely custom control for that platform this is something I've only really done once sort of a long way for an objective COA Java expert but the program the process for developing native plugins is relatively simple and for making sure you do that in a reusable way and making them testable so it's fairly easy to do if you have to do that but it's not something I have a massive amount of experience with sure so any experience working with the native wrappers for turbo links and what was that like working with what how does that compare to working with React I've used them in test projects to try them myself, I've never used them in production so probably take anything I say with a big pinch of soul I was quite impressed with them what bothered me about them and the reason I've shied away from using them in production is they just seemed very very niche there aren't a huge number of people with experience using them so just from a selfish point of view in terms of attracting developers there seem to be an awful lot of developers out there who really want to work with React and React native and far fewer who want to work with turbo links because it's seen as something or turbo links native just because it's seen as something that it's not necessarily clear how in demand that skill is going to be in 2, 3, 4, 5 years time so it's not something I've been particularly keen to put into production cool if that's everything, thank you very much