A look at the open source tooling created by the UI Engineering group at JP Morgan to streamline the developer workflow of building and deploying apps in isolation while still delivering a single unifying micro-frontend based portal to the end user, leveraging the use of native support for ES module imports in the browser.
Using ES Modules Based Micro-Frontends to Enable Distributed Development
AI Generated Video Summary
JP Morgan is using modern web technologies like the UITK and Modular to address challenges in their large-scale development environment. They employ micro front-ends with ES modules for dynamic loading and sharing theming using CSS variables. The applications are built using modular, which allows for scaffolding and creating new applications from templates. They rely on an opinionated approach to app creation and use a CDN for efficient deployment and caching. The host application handles user authentication and communication between micro front-ends, while platform services like authorization and authentication are provided by the digital platform.
1. Introduction to JP Morgan's Web Technologies
My name is Steve and I'm here to talk about how we're using modern web technologies at JP Morgan. We have two things to discuss: the UITK, a React component library, and Modular, an opinionated monorepo management tool. We have a large-scale development environment with thousands of applications and developers. Each development team faces the same challenges of continuous integration, continuous deployment, and quality assurance.
Good morning everyone. With that amount of videos about JPMorgan, no pressure doing this today. So, as I said, my name is Steve, and I'm here to talk a little bit about how we're using modern web technologies, JP, things like ES modules where we can use them to solve age-old problems, problems like how do we run multiple applications that have all been built and deployed independently of each other in single container portal-like applications.
So a little bit about me. I've been working in and around the fintech space for about 20 years now, and I've had the privilege to see UI technologies grow during that time. And now that I'm with JP Morgan, I'm actually no longer a core developer. But now I get to work with developers to put together our open source providing. We have two things I'm going to talk about today. One is the UITK, which is a React component library that we use to standardize the way their applications look and feel. And Modular, which is an opinionated monorepo management tool. And they're actually both on our public Github, if you want to check them out. And we have we appreciate pull requests.
So the kind of places I've worked at before, I said it's always in Fintech. Some of those organizations are really small. When I came to JP Morgan last year, I had no real idea of a sense of scale that I was going to be facing. We have across the bank as a whole is 50,000 developers, I think. 18,000 of which are in our division. And we're deploying about 5,800 applications into one of our container types, which is a desktop container. 2,000 of those are in production. They service about 200,000 daily users. There are hundreds of deployments going on every day. We have this really distributed development problem. Or joy, depending on how you look at it. Distributed development means we have loads of development teams. They all work independently. They're empowered to make their own choices about how they're going to build their applications, how they're going to deploy them, and more recently, how often do they deploy? Why are they actually sending code out? But looking at it from an organizational standpoint, what is the developer experience of that distributed development model? Well, every development team has to solve the same problems. They're all going to need to have some form of continuous integration. They're all going to need continuous deployment, and they're all going to have the same need to apply quality assurance. So they're going to need test tooling. They're going to need to choose the same products for doing their testing.
2. Consistency and Solutions in UI Engineering
And then because we're all under the same brand umbrella, we're going to need to have the same design language. So what was our solution to that? So our solution is in UI engineering. We have what we call the Digital Platform, which is a bit like a Jamstack provider inside the bank. We have a design system that is an accessibility first design system that has a companion react-based component library. We have a new version of the React library being developed at the moment. And more finally, the modular, which is an opinionated monorepo management tool. It has a few features in it that I'll be showing today, but it's similar to create-react-app and then an opinionation on developer tooling that goes in with building the applications themselves. So we have a desktop container. When we come to deliver a web portal, users still want to have a single way of launching applications. They want to have a consistent look and feel on the apps that they're loading, and they want to be able to use their apps in all modern browsers.
And then because we're all under the same brand umbrella, we're going to need to have the same design language. So how do we add that consistency to the applications that we're deploying? Bearing in mind, there's thousands of them being pushed out. So when the applications go out as well, they're going to need to solve the same kind of problems. We need to know how they're going to authenticate their users, how they authorize them to use the apps. And because we want to have the same rich user experiences, we're thinking how are you going to be able to share the layouts you've created, how you're going to share access to your applications. Which all kind of implies that you've got the same kind of understanding of having a preferences system. How you're going to store and persist the user data.
So what was our solution to that? So our solution is in UI engineering. We have what we call the Digital Platform, which is a bit like a Jamstack provider inside the bank. So I've already mentioned we have a desktop container, but we have ways that we distribute our mobile and web apps as well. We call it Omnichannel, but we also provide more of a DevOps providing. So we have things like continuous integration, actually it's based on Jenkins. We have continuous deployment which is based on Amazon S3. And we also provide like a centralized application registry and that's where we have our developers push the versions of their code so we can determine which are going to be made available in production. And more importantly, when we need to roll things back, it simplifies our processes. And then probably more relevant to today and what I'm going to be talking about later is we have a design system. So that is actually, it's an accessibility first design system that has a companion react-based component library. We have been embracing web components and allowed for the use of Angular. There was a decision made a couple of years ago that we were going to be react focused. So we actually have a new version of the React library being developed at the moment. It will be released fairly soon. Feel free to go and have a look at it on our GitHub. And then more finally, the modular, which is an opinionated monorepo management tool. It has a few features in it that I'll be showing today, but it's similar to create-react-app and then an opinionation on developer tooling that goes in with building the applications themselves.
So we have a desktop container. The developers, when they create applications, push it through to our continuous deployment infrastructure, and tell the desktop container, which is an Electron-based app, where to fetch the URLs. When we come to deliver a web portal, what is the user experience expectation? It's actually it's almost the same as on desktop. Users still want to have a single way of launching applications. They want to have a consistent look and feel on the apps that they're loading, and they want to be able to use their apps in all modern browsers. So historically, that could have meant using iframes to load applications together, which would come with the usual drawbacks of you've got many, potentially many, instances of frameworks being loaded into one application, into one web browser window.
3. Micro Front-Ends and Application Container
The use of memory, things get janky. You can't share or readily share CSS and theming between the various apps that you've loaded. So what was our solution? Well, I mentioned that we're using modern technologies, so now we are focusing on having micro front-ends. The micro front-ends that we're looking at are relying on ES modules for splitting the applications and loading them dynamically. And the React library that we've created uses CSS variables to share theming across the various apps that you might be loading.
4. Building Micro Frontend and Application Templates
Building a micro frontend. It's live demo time. I've mentioned modular, which has a way of scaffolding applications. We have a minimal monorepo with an express server and an example of creating a host app. We switched to Webpack 5 and added support for ES build. Modular also allows creating new applications from templates, either from NPM or repositories.
Okay, so, building a micro frontend. That was lots of words from me. It's live demo time, which obviously never goes wrong, so please bear with me.
So, I've mentioned, oh, good. I've mentioned modular. It does have a way of scaffolding applications. It's based very similar on your create React app, however, what we're looking at here, I'm going to use a, this is a minimal monorepo. It has a couple of packages in so far. One package is simply an express server that, mocking out the application registry that I talked about before, is gonna serve up our built content, so you can see the demo. And we also have an example of how you would create a host app.
Now, create React app would be able to serve these components, so it looks very similar to normal, using Webpack and Webpack dev server. Recently, we switched up to Webpack 5 and added support for ES build. With ES build, instead of generating a commons.js bundle, we're now creating an ES module bundle. So, what I'm gonna do in here is run a quick command to, there we go, to start up as an express server. It's also gonna start a Webpack dev server that builds this host application. It should, there we go, good. It's gonna very slowly load on localhost. My application, we'll just assume that that's the case. I'll come back to it.
So, thereafter, other things that modular has in it. Now, I mentioned this delivering of development at scale. At scale, we often have the need to create new applications and those new applications are created from templates. So, modular has a feature that can either use templates based on, that come directly from NPM or from within your repositories. So, I'm gonna use them, they're coming from my repository. So, I'm gonna add a new module into my monorepo. Gonna look at the card. Ah, I am, there we go, I can't type with people watching me. So, bringing modular commands to add a new package into my template, I've created some templates already, but just so you can see how they get added in. Why am I running through this? The templates that we add are actually, they can all be run as independent applications. So in the code view, I've now got a new application.
5. Running and Building Applications
It doesn't do an awful lot. Fortunately it doesn't use useReact, useEffect rather. I'm gonna start using a new port. So what this is proxying is if a developer is creating the app for themselves, then, did the old one load? Good. Good. Anyway, assume that you can render those. 3000, 2001. I'll show you the video and it'll be amazing. Gonna add another one. Does it start? Good. Good, good. Who would want it to just work straight out of the box, eh? So, never mind. The idea is, we have this card view. They actually get rendered as independent applications that you can yarn. The next step is, having created a couple of packages, that I can show them running together. So, next, I would need to build them. Now, the difference in the build process is that by running through esbuild, we actually create an ES module, which is a single file that can then be pushed onto a CDN.
It doesn't do an awful lot. Fortunately it doesn't use useReact, useEffect rather. Oh, it does, is that an effective one? Maybe, okay, so pretend you haven't seen my useEffects, I did say that I don't do code for a living anymore, and now that that's created a new package, I can actually start this for myself. I'm gonna start using a new port.
So let's say that I'm gonna just run this application. So what this is proxying is if a developer is creating the app for themselves, then, did the old one load? Hello? Hello? There we go. These do load. Oh well. If it doesn't load, I'm just gonna talk for a little bit longer and then show you the video because live demos do that. Good. Good. Anyway, assume that you can render those. Nevermind. 3000, 2001. You know, I've never seen create React app just stop. Good, I knew I shouldn't have tried to do a live demo.
Okay, so I'll carry on for now, and then I'll show you the video and it'll be amazing. Gonna add another one. Okay, so trying again. Then next, I'm gonna have to just talk. Does it start? Good. Good, good. Who would want it to just work straight out of the box, eh? So, never mind. Okay, so what am I gonna talk about? So the idea is, we have this card view. They actually get rendered as independent applications that you can yarn. I'm sorry, you can start with Webpack DevServer. The next step is, having created a couple of packages, that I can show them running together. So, next, I would need to build them. Now, the difference in the build process is that by running through esbuild, we are, ah. By running through esbuild, we actually create an ES module, which is a single file that can then be pushed onto a CDN.
6. Building and Loading Dynamic Modules with ES Build
In the example I have here, it actually runs as a local server, and let's just see if it's magicked its way it's magicked its way through the internet. Well, turns out I'm not online. That's a shame. Because I've styled it since I've created this video. Disappointing. Oh well, you get to see what I was doing yesterday. So assume that this is running live.
I'm gonna see why my modules aren't fetching from the internet and maybe if you come find me, I can show you a real running version of this as you can get from all the videos. We have a stand out the front. I'll be there later with a working version. But I'm gonna show you the mocked up version instead.
7. Dynamic Import and Module Sharing
If you are running this through Webpack, you'll need a little escape hatch, because Webpack will try and evaluate the import statement for you. But knowing that we've done a dynamic import in the browser, that actually gives us the evaluated default content of the package itself. So we know that those are going to be views for us. So we simply use that view as a component constructor and can include it in the TypeScript TSX. By loading the modules into the same React tree, we can benefit from things like sharing of modules, sharing of dependencies, especially if you've got stateful dependencies. They actually share the same instance. Our builds are faster because we only have to actually build and transpile our own application code. They reduce the file size that we're storing on our CDN. They load faster in the browser because they share dependencies between them. I'm really excited that we get to use modern technology to solve problems that have been around for a long time.
If you are running this through Webpack, you'll need a little escape hatch, because Webpack will try and evaluate the import statement for you. But knowing that we've done a dynamic import in the browser, that actually gives us the evaluated default content of the package itself. So we know that those are going to be views for us. So we simply use that view as a component constructor and can include it in the TypeScript TSX.
I'm presuming I still haven't gone online. OK. So the next bit is I can add more. I can add more components into this, independently building them from each other. And then we can use the portal, simply because it does dynamic loading. The portal is deployed independently of the applications that it contains. And then they can, actually, in my real working demo, I was so proud, I have the grid and the card talking to each other. But there we go. There is an example that I can share that shows this running. It is a very simple thing to get up and running. I think that that was me approaching time. So there was left for me to say that we have, by loading the modules into the same React tree, we can benefit from things like sharing of modules, sharing of dependencies, especially if you've got stateful dependencies. They actually share the same instance. So we do have a system of recommending that our app developers pin at least React to the same version, which simply just uses the resolutions object in a package.json that we then use the same React version across all of our applications. And yeah, so our builds are faster because we only have to actually build and transpile our own application code. They reduce the file size that we're storing on our CDN. They load faster in the browser because they share dependencies between them. And yeah, I'm really excited that we get to use modern technology to solve problems that have been around for a long time. So apologies if the demo didn't work quite well. But honestly, come see me later and it does actually work. Cool. Thank you. Thank you. Thank you. Oh, what a treat. Please, my darling, step into the hot seat.
Managing Deployments and Opinions of Modular
Thank you. We're going to talk about modular a little, which is a great joy for me. We have a couple of questions in the Slido already. How do you manage deployments of these distributed apps? It depends on the team. Some are containerized applications, while others are totally independent. The CDN we use treats each version as immutable, allowing for continuous deployment. We have loads of apps that depend on each other, so communication is key. I have a question about the opinions of Modular. It was designed to be very opinionated.
Thank you. We're going to talk about modular a little, which is a great joy for me. First of all, good job having a backup video that's a pro speaker tip because conference Wi-Fi is usually not great because there's hundreds of you. So good job. Phew. I know.
We have a couple of questions in the Slido already. Remember, slide.do and the code is 2124. Let's go to the live. How do you manage deployments of these distributed apps? Do you just deploy ESM bundles? What happens when you need to synchronize deployments of multiple apps? Ah, fun. It depends on the team. We have got, as a mentioning of scale, not all of those development teams know each other. Sometimes where we have things that are interlinked, we actually use a variation on a theme of Scrum of Scrums, so we can plan together. There are large programs of work. Those programs of work usually involve like the tech leads from the applications that are going to be deployed, their product managers, so we can shape actually how they're going to be deployed. The deployments themselves, we use a range of technologies. I was talking about the shiniest and the newest. Some of them are containerized applications. Some of them, they are totally independent of other apps. The CDN that I was mentioning is fairly new. That one, actually, we use, we treat each version as being immutable, so even in our continuous deployment infrastructure, each build that comes off CI gets pushed into the CDN and is, therefore, available for use as a production application. So yeah, we've got loads of apps that all depend on each other. It's down to human contact. We have to talk to each other to say when we can deploy them, really. What a concept.
I know, right? I have a question about the opinions of Modular. So I was there when Modular got created some time ago. And so I don't know what it looks like now. But I remember that the concept was it was going to be very opinionated. You mentioned that.
Benefits of Opinionated App Creator and CDN Usage
Having a very opinionated app creator has worked for us in terms of bootstrapping new applications and providing a consistent way of setting up configuration. While modular is becoming less opinionated, it helps us scale up development and add new features to large-scale applications. We rely on ES modules, a consistent CDN, and modern tech like HTTP2 to keep micro front-ends snappy and avoid unnecessary reloading or extra downloads. Our specific CDN is heavily cached, and we pin versions for stateful components. The difference between our approach and Webpack's Module Federation or Nexus is that we don't build everything together.
What part of having a very opinionated app creator has worked for you, has worked for you at scale, and may or may not work for someone with not quite so many apps? So it has worked in some ways. When it was used to start with, it really was about bootstrapping new applications. So in the ever-changing face of UI development, it was convenient to have a consistent way of either picking tooling or setting up configuration for your tooling. The UI development space moves very quickly. So what was confusing and difficult then isn't necessarily confusing and difficult now. So while modular exists as a way of standardizing how we interact with applications, it is becoming ever less opinionated. So there's more and more escape patches to come with it. And actually, it's giving us a really unique way of helping scale up development. So we now don't just have like we aren't there at the origin of applications. We're now being included into large scale applications that are wanting to add new features. So things like the new releases of, I think we called it ghost testing, for similar to running tests on only the things that have changed in your pull request or things that depend on you. And soon we're going to have the incremental build of just building selectively. So it gets added in.
That actually relates to another question which we have, which is, how do you manage to keep it snappy between the micro front-ends, avoiding unnecessary reloading maybe, extra downloads, state management, et cetera. So there, actually, one of the things we rely on is by using ES modules and a consistent CDN. So I mean, it's not quite as elegant and seamless as I made it look. We do recommend a specific CDN. So it's always, we all use the same CDN. We pin versions for things that we know that are stateful. But the CDN that we use, we actually have an implementation of it internally so we can share our proprietary code with internal users. But the CDN itself is heavily cached. So the modules themselves do load really quickly. We also rely on HTTP2 and newer standards. So it doesn't matter having lots of connections needed, needing to fetch lots of dependencies. We'd like, that was old school. We would think that was a problem, but modern tech, it isn't really. So yeah, we try and rely on modern tech. There's a couple of questions here that I'm going to bundle, which is people asking, why not this other thing, right? Which is kind of always comes up. How is this different from Webpack's Module Federation or Nexus approach? Mm-hmm. So Module Federation, the version of using micro frontends in this way means you don't actually build everything together.
Building Host Application and Platform Services
To build the host application, you don't need to know what modules you loaded. Being opinionated means making choices and evolving over time. User authentication and communication between micro front-ends are handled by the host application. Platform services like authorization and authentication are provided by the digital platform. Steve is available for further discussion in the speaker Q&A room.
So you don't need to know what modules you loaded in order to build your host application. I know there are a few of, why have you chosen's coming up. Because I think to be opinionated, you have to make a choice. Those choices have come around over time, and often people push back on, why did you do a thing? And now we take it case by case, and we have to evolve.
And then in the similar vein, it's How do you do specific things, such as user authentication across multiple modules, or communication between one micro front end in the deployed app, and the other if it exists? So in the version that does work, please come see me later. In the speaker room Q&A down by the lobby. I actually showed the grid talking to that card view, and they talk to each other. The answer that we've got internally is part of that digital platform is we provide platform services. So things like the authorization authentication that's done on launching the host application. There's a arcane system for authorization, but it is handled by the host application. So as an app is loaded, it can define which credentials the user must have. And then the host is responsible for ensuring that those have been fetched.
That's great. We have a ton more questions, but unfortunately not a ton more time. So let's thank Steve. And you can find him later in the speaker Q&A room. Thank you for joining us. Thank you so much. Free to go.