Join Sentry’s Evan Purkhiser and David Wang as they walk through Sentry’s 230k LoC Typescript/React codebase. They’ll tell war stories of the good, the bad, and the ugly. Gaze in wonder at their modern usage of Typescript, react hooks, and styled components. Recoil in disgust at their legacy Reflux usage and perplexing Webpack configuration. Step away from the workshop with a working knowledge of techniques to keep a large-scale frontend codebase modern and maintainable.
Building a Sentry: 7 years of open-source React
AI Generated Video Summary
Sentry is a valuable tool for handling errors and optimizing performance in applications. The Core UI Team at Sentry ensures a high-quality product and fills in any gaps. The workshop covered topics such as the tech stack, front-end compilation, styling with Emotion and React Router, state management with Reflux, the benefits of TypeScript, performance optimization, code reviews, and measuring page load. The workshop concluded with the recommendation to join the Sentry Discord for further discussion and exploration of the open-source code.
1. Introduction to Sentry and the Core UI Team
Thank you for joining the Building a Century workshop. I'm Evan, a full stack engineer on the Core UI Team at Sentry. Our team fills in the gaps and ensures a high-quality product. Now, let me introduce David, another engineer on the team. He will share more about Sentry and why it's valuable for your application. We'll use a simple example to explain how Sentry works.
Thank you, everyone, for joining. This is the Building a Century, seven years of open source React. You're going to be hearing from me, Evan Perkizer, and this guy, David Wang. I guess we can start here with some quick intros. So, yeah, who are we? My name is Evan. I've been here at Century for four years now as a full stack engineer. I'm on this team called the Core UI Team, and I guess I can kind of explain in a little bit just what that is, because I think the name isn't super descriptive, but kind of the idea here is that we've kind of noticed as almost any kind of engineering organization kind of natural thing happens where you know you initially when you're small kind of everyone is just working on everything kind of covering all the quarter cases of everything, but as you grow as an organization, you start to have teams that specialize more in like specific things, so the thing that happens when teams specialize is you have kind of these cracks that form within the things that are getting worked on, right? So you know, maybe you start having all these specialized teams, and then little things just kind of start getting forgotten about or like just no one owns them, right? Like at some point, for example, you might have like, oh, no one owns the webpack build. And then it's just like anytime someone needs to do that, they kind of have to like find time to make that happen kind of thing. So the idea of this Courtyard IT team that we have here at Sentry is that we're sort of the team that fills in those gaps, that is tasked with the things that you know, are almost like not thought about. And we think that those are like kind of the really important things to, you know, make the product really good, right? It's it takes it from being like a product that works really well to a product that feels really good. So yeah, so like I said, my name is Evan, you can read about my likes, dislikes and and dislikes. So if you want to like nested conditionals, please only early returns only. And then I'll put it over here to David. Yep. So Hey, everyone, I'm David. And you can see we have this little setup in the corner where whoever's talking is like the bigger, I guess, floating head. So this is really easy to see who's talking. But yeah, I've been here at Sentry for now two years. I'm a Fullstack engineer, and I'm the second engineer on the core UI team. And we only I really have two engineers. So it's a relatively small team. I also have my likes and dislikes here. I didn't really know what to put for dislikes. So I guess I don't really dislike anything specifically. Actually, I like everything. Yeah, I thought pretty hard about this, so I couldn't really put anything down. But you know, that's what I like. So yeah, we're gonna get right into this. So we've introduced ourselves. I think it's due time that we introduce the product that we work on. So this is Sentry. Perhaps you've heard about us. Maybe use us at your own company or maybe on your own projects. But I'm going to go ahead and take some time to explain the justification over why you might want something like Sentry in your application and then go through a quick demo just so you have more context about all the things that we're talking about here today. You have to be clear. It's not an ad. We just want to get some context so that it makes all sense. Yeah. Just to inform the audience. So we have Sentry. The way I like to explain it is typically through a simple example. So we're probably all developers here. Say you have a side project going up and it's a simple checkout page, maybe like an online store, right? Say this online store has a couple of pages, like I said, checkout page, login, authentication, all the basic things that you have, a full stack web app. And you go ahead and you deploy your online store out to the public. And inevitably, you're gonna get some users that run into issues. And initially, if you're just handling a very small user base, what might happen is they'll send you emails and one user might be like, oh, my gosh, the checkout page doesn't work. I have all these products in my cart. I'm trying to get these products, but they just don't check out. I get this big red error on my screen. And then you have to engage in this back and forth with this customer.
2. Introduction to Sentry Features
Sentry is a valuable tool for handling errors in your application. By integrating Sentry, you can easily monitor errors and view them in a dashboard. Additionally, Sentry offers performance monitoring, allowing you to diagnose and optimize the performance of your application. You can also set up alerts to stay informed about critical issues. Visit the Sentry website to explore a sandbox demo and see how it can benefit your development process.
There's probably some better channels for it. But say you're just doing it over email, there's a lot of back and forth. And if they're a nice customer, they might send you some more information like what browser they're using, the version of the browser if they can even get that for you. Maybe they can even go into the logs, like the console, if they're really good, and they can send you all these things and it's really nice. You can have this back and forth. But obviously, this isn't very scalable. Say you have thousands of customers. You can't be having these day-to-day interactions when you're trying to build out new features.
So this is where something like Sentry can come into play. So if you put Sentry into your application, anytime users run into errors, we're going to float up an error into a dashboard, and you can view that basically whenever you have time, or you can set up alerts on that. So that's what we're going over right now. So I'm going to flip over to Sentry. We have a test organization. This is actually the sandbox feature that we recently created. And you can get to this yourself if you go to the Sentry webpage. There you go, sentry.io.com. There's a sandbox demo, and you can just register for that. And you can poke around Sentry yourself. So I encourage you to do that if you want to check out more afterwards.
And we have this organization. It's called Peaceful Rhino. This is just like a test name. You know, don't worry about it too much. And also on the right-hand side here, there's my projects. And, you know, say, for example, this is like a full-stack web app. And we also have like mobile application, so you have Android, iOS. You know, we support a variety of different languages through our multitude of SDKs. But because this is a React conference, I'm going to go ahead and select React. And these are examples of issues that users can actually run into. So this reference error, for example, I'm going to click into this, and this is an actual issue. You can see that scrolling down, we have certain contexts that we want. So the version of the browser they're on, for example. Maybe the actual user that you have, their email. So you can look into more logs in the database. You have even the OS. And scrolling down even further, we can see that there is the Strap Trace, which is arguably some of the most important information you can have here. And even you can track breadcrumbs. So if you want to see specifically what happened leading up to this issue, you can look at this through the list of breadcrumbs that we have here. So I'll just scroll through that real fast.
So this is super useful already, and I think typically when people use Sentry, they think of it as primarily like an error monitoring service, but we also offer things in the form of performance, too. So we have performance monitoring. So we're on the React project, so it's going to show us Web Vital data. This is the, I guess, frontend performance. This is like Google Web Vitals. They have FTP LCP, first input delay, bunch of other things that you might find useful for diagnosing the performance of your application. And you can see because it's an online store, we have the checkout and product store transactions that are going on here. And then finally, you can view this as kind of more of like a passive way to consume Sentry. You see issues, you might log in once a day to look at some issues and look at some performance things, make sure the app is working as planned. But if you want to set up some alerts, you can also do that. So I'm going to go over to alerts now.
3. Proactive Alert Rules and Value of Sentry
You can set up alert rules in Sentry based on issues, volume, errors, or performance metrics. It provides valuable insights into potential issues, such as slow checkout pages, performance regressions, or high error volumes. Sentry is essential for side projects, offering visibility into user experiences and ensuring safer deployments.
And this is like a proactive way to you Sentry, I guess, in the sense that you can set up an alert rule. And this can be based on issues or volume, errors, or even some performance metrics. And you can see, oh, maybe the checkout page is taking too long, maybe there's been a performance regression, or there's a high volume of errors on the login, you just can't get into their accounts. So this is all super useful. And maybe now that you've seen this, you have an idea of you know, the next side project you build, you might want to use this. And yeah, ever since I've, you know, worked at, I use Sentry on a lot of my side projects. I can't live without it these days. I honestly can't live without it. It just feels so much safer to deploy things when you have visibility into what's going on, on the user end. So yeah, that's Sentry.
4. Overview of Talk and Tech Stack
In this talk, you'll learn about a large scale React TypeScript codebase, the front-end developer experience at Sentry, and techniques used to keep the code base modern. You'll also see interesting parts of the Sentry codebase and have the opportunity to ask questions. Sentry is powered by Django on the back-end, and the React front-end app is almost a single page application. We primarily use classic REST patterns for API requests. Despite starting as classical Django rendered views, Sentry has evolved over the years.
And now that we've introduced ourselves, the product, we're going to give a quick outline of what the talk is going to be going over, right? So what does this talk about? You're going to be learning a couple things. First off, you're going to see firsthand what a large scale React TypeScript codebase looks like. And you're going to hear directly from us two engineers who work pretty closely with it, how we build it, how we maintain it. And you're also going to be exposed to the front-end developer experience here at Sentry. You're going to see all the tooling that we use, the decisions that we've made along the way, what works best for us and why we've kind of made these decisions.
And then you're also going to see a couple interesting, or rather growth, parts of the Sentry codebase. You're going to see some stuff that probably you shouldn't replicate. And we'll talk about it in more detail. And feel free to ask any questions about that. And finally, hopefully, after all that, you can step away with a working knowledge of techniques that we've used in Sentry to keep the code base modern and attainable, despite how long we've had a history with React here. And you will be looking at actual code in our editors, not going to look at it a lot, but we'll show off some cool things that we like. So you're encouraged to follow along here at github.dev. You don't know about this feature. It's super cool. You can see a web editor for VS Code in here. And you can just kind of follow along. And you're encouraged to ask any questions in Discord along the way.
So, basically, what's going on is only one of us is going to be talking at a time. So the other person will primarily be looking at the Discord, make sure, you know, any questions are flagged up and we'll pause every now and then. And, you know, we'll get these questions answered. So definitely don't save them for the end. We want to interact with y'all as much as possible. And with that being said, I think it's time to hand it back over to Evan. He's going to go over our tech stuff. Thanks, David. Yeah, like David said, definitely ask questions whenever. We kind of wanted just to sort of be in and maybe a little bit more of the form of the discussion. Just, you know, ask about decisions we made, why we made them, stuff like that.
Okay, so I want to talk a little bit about just kind of like stuff that we use, why we use it. Things that we like, just within our tech stack. And, you know, like I said, a lot of the stuff you guys have probably seen. It might just be kind of nice to hear why we use it. So obviously this isn't a back-end talk in any way. This is definitely all React front end. This is a React conference. But I did want to mention that Sentry is powered by Django, the back-end side of things. Mostly these days it's Django is the back-end API. And the React front-end app is more or less almost a single page application at this point. So most of when you're using Sentry you'll be in React land where it's making API requests to Django. We're not real fancy. We're not using GraphQL or anything like that. We're kind of still using kind of the classic REST patterns for things. But we found this works pretty well. That said, it's not always true. Like I said, we've been using React for quite a long time, seven years now. Actually it was like trolling back through the commit log just to like validate that we've been using it that long because React was introduced I think by Facebook in 2013. So we have been using it for a pretty long time. The point I wanted to make here is that, it's obviously not always true that it was a single page application or at least it wasn't born that way. For a pretty long time, it was kind of the classical Django rendered views. I put Jinja here and I feel like some people in the Discord are going to be like Django views aren't Jinja but to me they're very similar.
5. Sentry Front-end Compilation and Configuration
We still have some views that render in the back-end, like the login and authentication pages. We recently migrated to TypeScript and found it highly beneficial. We use webpack and babel for building the application, with Babel doing most of the work. Our webpack configuration is unique due to the existence of Sentry.IO. We compile both the open source and closed source code with Webpack, allowing them to interoperate seamlessly.
6. Styling with Emotion and React Router
We found that using styled components, like Emotion, has been valuable for maintaining and understanding our large codebase. Although some criticize not using the cascading part of CSS, we can still use cascading with styled components by using CSS variables. We chose Emotion over styled components because it was more performant at the time. We're still using React Router 3 because it works well for us, even though it has a longer routes file. We're considering migrating to React Router 6 when it's released. We've also replaced jQuery's AJAX with fetch in recent updates.
It was really bad. Yeah, don't do that. All right. So, yeah, so I mentioned, we don't use too much Lust anymore, we use Emotion. I know that the kind of CSS and JS thing can be a little bit of a hot topic. Maybe not so much these days. I'm not sure. But I've definitely talked to people that are like, why are you doing that?
We're using Emotion. We're not using styled components. As far as I can tell they're more or less the same thing these days. Maybe there's some subtle differences that I don't know about. At the time when we chose to use Emotion, two years ago, styled components were not as performant. That's kind of why we ended up with Emotion. I think Kent Dodds, the author of React Testing Library and a number of other things also had this tweet. I found this when I was trying to validate our reasoning for using Emotion. Other important people say it's good. Yeah. We use React Router. This is kind of like a little tip I wanted to point here. This is a very kind of classical tip, don't fix what's not broken. We're still on the third version of React Router. If you're familiar with React Router, you probably know it has undergone a bit of churn in terms of structure and how to use it. With React Router 3, you would put all of your routes in one place. With React Router 4, you put your routes into your components, and then other components that are rendered have their routes inside of them. It's kind of like this... You put your routes kind of everywhere in your app. We certainly considered migrating over to React Router 4, but we kind of determined that it would be harder to maintain, probably harder to navigate, and a lot of this kind of came from our own experience using React Router 4 for side projects or other projects. And I think what's almost maybe kind of funny to point out here is that, because we've waited so long to migrate, and because we made this consistent decision not to change anything, because it works fine, it works great for us, I think maybe the biggest problem might be that our routes file is like 2000 lines long. Though I did recently reorganize this, so it's quite a bit easier to navigate. This is where all of our routes are is in routes.tsx which is really nice when you're an engineer coming in and you're saying oh, I need to figure out what is rendering this page to be able to just come in here and say where is the discover stuff. Here's the discover routes right here and then you can easily navigate to the thing, oh, here's the landing. You'd have to traverse through a tree of like okay, where does the organization tree, where does the discover tree render, where's the landing. What I was going to say is because we've waited so long React 6 is going to be coming out pretty soon and they've actually returned to the all routes in one place kind of structure of creating your routing setup. Because we managed to wait so long, hopefully, we can migrate from 3 to 6. And we'll probably have to fix a bunch of like types. I think we use the with router stuff heavily and I think there's no longer an HOC. It's only hooks, which is something else that we're kind of moving towards. Yeah. A few other kind of like little things here. I'll talk about the bottom one first is we just use fetch these days, so this is actually kind of recent but we killed off jQuery's AJAX along with maybe like three or four other usages of jQuery that we still sort of had floating around. I think I introduced like a old jQuery compatibility thing where I just reimplemented.
Reflux for State Management and Handling URLs
We use jQuery for simple tasks and highly recommend it. We primarily use reflux for global state management, but it has some limitations, especially with testing and functional components. We're working on replacing it with a newer solution like ReactQuery. We also use reflux for global state management and have a sidebar with open and close functionality. We don't have helpers for generating URLs in our components, as we handle it on the backend with Django. It hasn't been a problem for us so far.
It was like two things that we were using jQuery for and they were really simple but I don't remember what they were. But that works great, I highly recommend it. You honestly almost don't need other fetching libraries other than fetch, as far as I can tell from our usage.
The other thing that we use is reflux, which is a very old flux state management library. It's not redux, don't get confused with that. A lot of people do tend to get confused with that, but we use that as our global state management. And when I say global state management, I mean like asynchronous states, or like server states, like when, for example, if you have two components on the same page, and they're both, you know, fetching the same thing, you want to use some kind of global state thing so that you can de-duplicate those requests. We use it for that. We're looking to replace it with something else, something, for example, like React, ReactQuery, which kind of does that, but is newer. So the fact that it's kind of older kind of inherently has some problems. A lot of it has to do with, like, testing. So it's actually kind of hard to use reflux with, like, things like React testing library when you're using functional components. Mostly because it happens all outside of the React rendering cycles. So like, you know, reflux, it's basically, like, I said, it's like the flux thing. So you have a store and then you can listen to that store and then you can have, like, actions that cause updates, and then all the subscribers will, you know, that are listening will re-render, or, well, they won't re-render. They will be notified and you have to implement the re-rendering logic yourself. Which is sort of what we've done. In fact, I think we recently managed to turn it into, like, a pretty simple hook, kind of recently. So you can pass in one of these stores into this hook. And because of how nice hooks have been, you can simply listen to the store and have it basically set state. But the annoying thing here is because this happens outside of React's lifecycle, you have to make sure when you're running tests that you're telling React that you're acting when you update your store. Otherwise the set state will happen without any sort of act. And it will get confused, or really, it will yell at you. I'm sure we've all seen the missing act warnings. Or maybe you haven't, if you're just using a bunch of newer stuff that handles all that for you. But yeah, this is something we're trying to get rid of. We also use it for global state for things like, I don't know, we have a sidebar that you can open and close. It probably shouldn't be used for that. Something like React Contacts would probably make a lot more sense. Yeah, and obviously we use Sentry. Hopefully, at this point in the talk, you know what that is. David gave a really great demo earlier. But it works really well to be able to, you know, see if we've broken anything. Yeah.
I'll pause you right here. We got a question from the Discord. We got M. Hayes. He asks, for your reps. Do you have any kind of helpers for generating URLs within your components? We don't actually. That's a good question. That was actually something that I thought about when I joined. I was like, hey, why don't we have this? And I actually thought it was going to be kind of like a problem. Because we do it on the backend. We use Django. So we have all the Django helper stuff for generating routes based on route names. So we don't do anything on the frontend. I think it hasn't been a problem for us, honestly. Because... Well, we haven't moved routes around quite at all.
Benefits of TypeScript in Front-end Development
TypeScript is valuable for larger apps and helps eliminate bugs. It offers static checking and ensures correct typing, leading to fewer front-end bugs. TypeScript also enhances productivity and facilitates quick onboarding of new contributors. It provides features like near-always correct auto imports, making coding more efficient.
If we do move routes around, we're pretty careful to make sure we have redirects. We haven't run into that scenario, where we need to migrate where a route lives, and it's frustrating to go back through the app and try to find all the hard-coded paths. Which is what we do. We do more or less just hard-code the paths. But it hasn't really been a problem, I guess. I think it's been okay for now. Yeah. Perhaps in the future. Something to revisit. Yeah, we definitely have a number of kind of like... Where are they? The legacy redirects routes. So we have this whole route tree of just things that are redirecting to other paths, that are just because we move things around. But I don't think... I mean, when we change stuff in code, usually it's not... We don't use these redirects anymore. I think this is typically for people who have bookmarks. Yeah. So I think we're pretty good about refactoring. So everything uses the new routes. So yeah, this is what we've been doing. Yeah, that's a good question though, thank you.
Cool, okay, let's keep going. So we kind of talked about kind of what we use to build Sentry, like, some of the tooling. I did want to talk about kind of a few things that we particularly like at Sentry, in terms of just like... You know, being a front end engineer, and like thinking about how do we... How can we scale front end engineering at Sentry, how can we make sure that as we've grown as an organization, how do we, you know, keep being productive. So I mentioned earlier that we really like TypeScript. I personally also really like TypeScript. If you read my likes earlier, I think the first thing I wrote was TypeScript. I'm a big convert. You know, before we migrated over, I hadn't actually used TypeScript, and we started using it, and I'm like, seems like so much work, but at this point in my life, my development career, I don't know, I feel like I'm just like writing TypeScript all the time. Older and wiser. No, so, let's see, yeah, I don't know, why do we love TypeScript so much? So TypeScript is really valuable for when you have, it's very valuable for when you have like a larger app, right? And, you know, Sentry, obviously it's like, you know, we've grown our code base quite a bit, but it's like, not only have we grown like how much code we're writing, but you know, we have more and more people writing code, right? And I mentioned earlier, like, you know, people are specializing more in like front end and, you know, the teams are kind of like doing specific things. And so maintainability becomes like a very, very real kind of priority. And so the idea that we can use something like TypeScript to have like static checking and to be able to basically eliminate whole classes of bugs, you know, no more, you know, objects does not have a property on undefined or whatever type things, it's really, really valuable and it's very much true that we have eliminated those bugs, like, you know, obviously And so it was very easy to see like, the kinds of bugs were introducing before, versus the kinds of bugs we introduce now. Where, honestly, it's, you know, we don't have too many front end bugs these days. I think we used to, we used to have things like that, where it would be like, weird object types floating around and stuff like that. But ever kind of, since we had kind of completed our migration or TypeScript, where things are correctly typed, just like, 100% of the time, it's, it's kind of eliminate all that stuff. But the other part of it is kind of like, the kind of very appealing part of TypeScript is that not only does it kind of stop you from writing code that is buggy, but you also can end up being like, more productive. And especially, you know, like making, it's really important to, you know, make new contributors productive quickly. So, yeah. So it's more than just like a correctness checking thing, right? It's more than just, just like, oh, like, we know that the code is not going to break type thing. It's also that, like, TypeScript offers these really, really great features that you almost might not even think about if you're just like, you know, in VSCode all day and already using it. But things like, you know, for example, like near always correct auto imports where we'll almost always know how to auto import like something that you need. Right? So if I'm just like floating over here in some file and it's like, I need, this thing and we'll see if my LSP works here or not. Yes. There we go. So it's like, if I needed to use projects, right? I can click this hit enter and it correctly imports it. Right. So before we use TypeScript.
Advantages of TypeScript and API Checking
TypeScript's productivity advantages include easy navigation, go-to definition, finding references, and safe refactoring. It ensures changes are applied consistently and identifies unused code. The Language Server Protocol powers editors, providing semantic navigation and importing. While some at Sentry use NeoVim, most prefer VS Code. As for checking API requests and responses with TypeScript, it's a topic under discussion, but not yet implemented.
So to be able to like do go-to definition to be able to find all references of a user of a used like export. Right. And like all these things are like really, really incredibly valuable and like things that like very much you take kind of like grant for it to be able to just jump around code really fast and like do that correctly. Right. Like to be able to just be in here and be like there's nothing to understand here, but like, Oh, like, where's this common store interface thing used? And it's like, Oh, here, here's the definition for it. Right. And then to be able to say like, Oh, well what's, what's using it. Right. And to be able to just like hit a keyboard, like keyboard shortcut and see like, oh, here's all of the, all of the the usage of this common store interface. This is related to our reflex thing. This is like how how this hook has like its own interface for using, using, using the stores. Yeah. I don't know. So that's, that's like a very, very valuable thing that like, I think that people kind of like almost take for granted. The other thing that I wanted to mention this way I put it in bold here is that it makes refactoring things really, really safe.
Do you have a question, David? Yeah, I saw some steps falling on the screen there. We do, we do. We have M. Hayes back at it. He's saying, do you use TypeScript to check your API requests and responses? That's another great question. Oh man, him as with the hard ones. I actually wish I could say yes to that. This is something that we were kind of like talking about and it's sort of maybe like up in discussion a little bit. But no, we don't yet. We do in some places, but we don't do it in a sort of like automated way.
Discussion on Using TypeScript and REST APIs
We don't use GraphQL or auto-generate APIs. We still use old-school REST APIs and sometimes type the responses. Although it's a topic we've discussed, we haven't taken action yet. TypeScript is worth it on any project, even small ones. It's valuable in Sentry because we're big, and I use it on all my side projects now.
Like I said, we don't use GraphQL or anything like that where a lot of times it will auto generate all that stuff. We are kind of still just in kind of the old school rec style APIs. We do type them sometimes where we'll type the responses. That said, obviously it'd be very easy for those to go out of sync. Yeah. It's definitely a topic that we've talked about, but we haven't particularly taken any action on it quite yet. We would definitely like to. If you have the opportunity to do that, if you're starting fresh on a new project and it's a project that you anticipate becoming a little bit larger, do you think it would be worth it? I think TypeScript is worth it on any project, even if it's small. I mentioned it's really valuable in Sendry cause we're big, but I use it on all my side brushes now. Same here. Yeah. No, that's a good question though. Yeah. Thank you.
Deploy Previews for Frontend Verification
One cool thing that we built recently is Deploy Previews. It allows frontend developers to easily verify code changes by automatically deploying pull requests to a server. This is especially useful for automatic PRs like the Panda bot, as it allows easy verification of package updates and ensures everything is functioning correctly.
Building and Deploying the Application
Building the application required untangling the back-end rendered HTML and introducing new APIs for boot configuration, authentication pages, and Webpack. Vercel proved to be a valuable tool for deploying and testing the application, providing automatic deployment and permission controls. We also explored the possibility of running the front end locally and introduced the WI command for local development. Hack weeks occur once a year, allowing for the creation of innovative tools like a visual snapshotting tool.
Using Century for Performance Optimization
We use Century for Century and have seen performance wins. The organization details endpoint became very expensive, with some customers experiencing 50-second load times. To address this, we used our own performance and distributed tracing product to collect metrics and reduce transaction duration. By analyzing the data, we identified that the project summary serializer operation was the main contributor to the long load times, as it serialized all the teams and projects for the organization. This resulted in a heavy payload and costly network transfer.
But our own version. Yeah. Yeah. We build a lot of cool things. But back to this story time. So this is how we use Century for Century and basically describing how we've been able to get some performance wins and how we use Century Performance products specifically.
So in order to understand the context here, I'm going to flashback to 2019. This is, you know, when the app basically had a couple of performance issues that we're going to talk about, and one of them is caused because on App Startup, the organization details is fed and this is all happening in one end point.
So the organization details is composed of a couple of different pieces of information. So we have the organization information that's like the slug, the avatar and the plan, maybe some features that you had for like early access organization that you can turn on. And then we also have set of all your teams in the organization and the set of all your projects and the organization. And that is stored as, you know, normalized field within this organization object. So it's organization.teams and organization.projects and this stored, this is stored in reflux. Like Evan talked about, we have reflux. So it's not the greatest right now, but we have this to store API data since typically you don't want to be fetching this multiple times. It's a rather heavy payload. So we don't touch it multiple times. And the way it's accessed as we store it in this store and then downstream components will go ahead and directly access teams and projects from the organization, you know, so now bigger and bigger customers started using Century in 2019. And obviously at any company you'll start to get bigger and bigger customers. And we started to see that the organization details endpoint is becoming very expensive.
So expensive. In fact that some customers are subjected to 50 seconds a little time, which is a lot. It's like really a lot. And this is nowhere near acceptable. I feel like typically they would just see this loading spinner for 15 whole seconds. So obviously this is huge problem. It's not so bad. Like it's not every single customer, but it is a select few larger customers that obviously would care a lot about. So this is something that we definitely need to get to the root of and how did exactly did we do that? So, as I mentioned before, we have our own performance and distributed tracing product, and it collects a variety of metrics like throughput latency and transaction duration, which is something we'll be looking at today, which is how long a transaction takes maybe like an end point or like a page load transaction. And then we've also recently introduced web vitals like you saw. So LCP, FCP, which are also pretty related to this case because ultimately we're trying to reduce those numbers as well. So let's go ahead and click in the century and this is some screenshots of actual data that we have. I blurred out the customer name. So that's why you see like a little bit of white out here. It's not like a visual glitch. So we have a page load transaction, and this is specifically for like any page load that we have. This, I think this is for the issues page though, this is the most typical page load, and you can immediately see a very long bar.
This is the span view. So this is showing kind of the operations that take place in the front end. And all of these are like HTTP get requests or like, you know, rendering something or getting, you know, a script. You can see we have a seven seconds end point. So it's not as bad as I talked about, but there are some very bad cases. This is probably not the worst seven seconds, just loading this, having this loading indicator and loading this end point, get API site zero slash organizations, right. And this is the specific organization that they're requesting details from. So because this is distributed tracing, we can connect the front end to the backend so we can click into this and see that the backend transaction and Django looks a little different. And we're gonna open that up right now. And we have basically the culprit right here, right? So we click into the backend transaction and we see that part of the reason why it takes seven seconds is because most of it is constituted by the 6.6 seconds project summary serializer operation. So as you heard before in this organization end points we basically have all the teams and all the projects. And for this specific organization, they have a lot of projects. So serializing all of them and sending that over the network is going to be very, very costly.
Optimizing Organization Details
We're splitting the organization details and point into multiple parts to improve performance. By separating the heavy endpoint into lightweight organization details and the all projects and all teams API call, we can render the app faster. Many views in Century don't require all projects and all teams, so we're optimizing the loading process.
So sweet. We've kind of found out that this project summary serializer is a huge bottleneck performance. So what is the solution here? We're going to go ahead and try to split the organization details and point into multiple parts. And even though we're doing the same amount of work, the important thing to note here is that in the previous, I guess, so we saw that the whole application was blocking on that one, really heavy end point. But if we split it up into multiple pieces, we can block on just the one lightweight organization details, which only composed of like strings and numbers and ideas. So it's super lightweight, as the name implies, and then the all projects and all teams API call. It can still happen in the background, but it shouldn't be blocking any of the app from loading, right. We want to render as much as we can with as little information as possible to make it feel, you know, a snappy as you can. I think the thing is also that Century, like a lot of the views don't actually require all projects and all teams to render, which is something that we're currently tackling. We're going to talk more about later, but we really don't need all this information just to load, you know, simple issues page. Right.
Migration of Organization Projects and Teams
The migration to separate the organization of projects and teams into their own stores was a complex process that took about a year. TypeScript greatly facilitated this migration. The views had to be updated to use the new pattern and be eligible for migration.
So in order to do this migration, took quite a bit of work. So you have this incremental migration in place. We have tons of files that use the organization of projects and organization teams directly. So first we have to separate those two constructs into their own stores, right? We have to fetch them separately. And then we have to ensure before we can migrate these views to use this new pattern, it has to use these new stores and they have to be eligible for migration. And this is something that TypeScript made a lot easier. But this migration is also happening in the midst of the TypeScript migration. So this did end up taking like a year long.
Performance Optimization and Hook Usage
We introduced a lightweight and heavyweight routing tree in our application to improve performance. After migrating high traffic views to the lightweight organization pattern, we saw significant performance gains. However, there were still some inefficient endpoints, such as the ones fetching all projects and all teams. We aim to further decompose the codebase and move away from fetching unnecessary data. To achieve this, we have started using hooks more extensively, even in our old class components. We have examples of using hooks like 'useTeams' to abstract away fetching and interaction with the store. Additionally, we utilize the render props pattern to pass hook data as arguments to children components.
And especially because we didn't have TypeScript for some of these verifications, but the way this works is we had two routing trees. We had a lightweight and a heavyweight routing tree, and I'll go ahead and switch over to an actual pull request that this one that I did a while ago. And you can see that we have this lightweight organization details. So this was the very introduction of this lightweight pattern where we had a component that was responsible for fetching this lightweight organization, all projects and all teams, and then put those all into the stores so that components could actually interact with them, right? So that's just the history right there. It's really real. We had this lightweight and heavyweight thing. And so after we were able to get some of those high traffic views in, we started seeing performance gains.
So again, this is another example, obviously really bad 13 second, load time. You can see we have the same thing over here, the same get requests and it's pushing all these spans over to the right, right? Nothing else can happen until this huge endpoint is done loading. So this is super bad. And then we have the new world where the equivalent request is quite a bit faster. So it goes from 13,000 milliseconds to 281 milliseconds and you can see everything else is not blocked anymore. Though you will notice that there are some very bad examples of these called to all projects and all teams. These two long bars are those endpoints. Obviously not great that we have those. But it's unfortunate for now, but at least you can see that the app is able to render, which is a huge improvement. And then what did we do after we performed like some of these migrations? Well, we need to continue the process, right? So we got this initial gains, but we need to migrate everything over into the lightweight organization. And during this cleanup phase, I feel like this is kind of like a two phase thing. We no longer had to maintain this construct of heavyweight and lightweight, which was really nice. Obviously having this in, you know, the code is not the clearest because engineers would come on and be like, why are there two versions of this organization? I didn't even know what lightweight was like. You did it. And I was just like, why don't we have two organizations now? Yeah, there's definitely the issue of education. I remember people would ping me like they'd get blame. And they'd see this lightweight thing. They're like, what the heck is going on here? Which one do I use? So having that in for a year, not the greatest, but we're super glad to be able to get rid of it. And Evan can talk more about how he's TypeScript there if you have any questions, but yeah, we use TypeScript. We got rid of all these organization dot projects, organization dot teams. And now we're focused on even further composition or further decomposition. Really. You're probably thinking why the heck are you even fetching all projects and all teams is probably not necessary to a super big organization. And you're exactly correct. We definitely shouldn't be doing that. So we're trying to even move away from that. A lot of the time we have these components that just get the user teams, for example, or just get the users projects, but they ended up filtering that from the side of all projects. So this is not that efficient. So what are some of the tools that we can use to make this a little bit nicer? And this is where we introduced the idea of hooks more readily in our application. So we have a really old react code base, right? So a lot of our components are in the old style, like just class components. We even had like three react class that we recently got rid of. So super old stuff. How can we even make use of this hooks, which can only be used in functional components? Well, we have some examples here. So I'm going to fire up my editor real quick in this VS code. Um, you can see, we have this use teams hook, and this is right here, and it's supports a number of options, which is, you know, a limit for the number of teams that you want to paginate to maybe specific slugs that you want and whether or not you just want the user teams. And this is where we abstract away all the fetching and interaction with the store. They scroll through. This is a bunch of code for interrupting with a store and providing the right stuff. And how do we end up using this in a class component? Well, we have this kind of thing, this render props, teams utility, and it really has no logic within it, which I think is really nice. So we have all the logic separated into the stuff. All it does is it just forwards whatever you get from use teams or a hook and it gives it to the children as an argument. And so this is a way that you can use it as a render prop style, which is a little bit older. Obviously if you can just use hooks, it's great if you're using a new project.
Integrating Hooks and the Front End TSC
We integrate hooks slowly and have a new path forward. We can still use old class components with new logic. The migration of the settings view from heavyweight to lightweight organization resulted in a 61% speed improvement. The performance trends feature provided clear insights without specific searching. The front end TSC, or technical steering committee, ensures a cohesive approach to building Century. It is the birthplace of initiatives like the TypeScript migration.
But I thought this was a cool way that we were able to kind of integrate hooks slowly and have this new path forward. So we're kind of on a, you know, like a more modern path and we can still have all the old class components using our new logic. And of course there's stuff that we've already highlighted before, like use legacy store, which has been super helpful and kind of abstracting away all the reflux stuff. So eventually when we make the switch over, it is not as jarring, right? We can kind of put this all onto a little box and you know, abstract away the logic.
So yeah, that's what's going on there. And of course tools that made it easier. TypeScript, that's a given. And following up with the actual, you know, some more performance gains, more taps, pats on the back. We have recently migrated over the settings view from the heavyweight to the lightweight organization. And this was kind of the last step. This is not a very heavily trafficked view. Not a lot of people are like going into this page from the start. So we kind of de-prioritize this to the very end, but as you can see, we're able to get 61% speed improvement is using the performance trends feature, which takes a time period, splits it in half, looks at the before and after does some statistical methods and basically tells you whether or not there's a real trend here. And as you can see there is a pretty clear trend for faster load times. And it's pretty cool too, cause this is like, we didn't have to like go in specifically look for this. This just was one of those things where we did it and then we looked at the performances trends page and it was just there and it was like, Oh look, there's that thing we did. Right. Yeah, it's actually pretty, I was, I was like it was one of those moments where I was like, man, our product's really good. Oh yeah. You know, it's, it's quite useful, I'd say. Definitely be able to go in here and just view at a glance what's changing is very nice. So yeah. That's how we do performance here at Century. I think bring it all together. I'll just talk about one last thing. So I mentioned at the very start that we're two engineers on, you know, a team of maybe like what? 80 engineers total. And we're not the only people who interact with the front end obviously. So how do we actually go in and do all these changes that are kind of like wide sweeping, cross team, cross organization we have something called the front end TSC. And what does that even stand for? Well TSC stands for technical steering committee. And it's a bi-weekly meeting that anyone can join if they're interested in kind of influencing the direction of the front end code base. So if you have opinions, you can come here and talk all about them. You can just represent your product team and you can talk about the specific issues that you've been facing and then propose debate and approve initiatives. And it just ensures that we're building Century and basically, you know, in a cohesive manner, right. We have, we're we have some harmony here in the front end and we don't have like really clashing patterns everywhere. And this is actually the birthplace of multiple initiatives, right? So we have the TypeScript thing. This migration was something that we gathered all the TSC individuals and we really had like some good spirit about it. We're all participating in this. People didn't want to do that for us. I was like one of the people I was like, Yeah, we don't know if we really need that. Right. It was like, Oh, we use it in the SDK cause those are like consumer things. But and then I don't remember, I think someone did it. I think like TypeScript literally came out as a hackery project and then like someone just started doing it. And then like they, then they came back to the TSC and they were like, Hey, look, we converted this whole like party app over. I think it was like the initial version of discover. Yeah. They can, they can bring that all the way over to like TypeScript. And then like they came back to the TSC and they're like, all right guys, check this out kind of thing. Right. Yeah.
The TSC and Valuable Discussions
At the TSC, we have initiatives like react testing library and hooks. We remove old code, such as rack bootstrap, and discuss ESLint rules. The front end TSC has been valuable, addressing small decisions that take up time. We recommend having such meetings for organizations with a growing code base. Thank you to everyone for attending.
And I think that's how a lot of it goes at the TSC. We kind of have these initiatives. I feel like, Evan, you're hearing someone who's like, Oh, I got to see it to believe it. But we'll end up doing things that we're like, you know, have a floor quest up that's like, this is how good it could look, you know? And that's how things like react testing library got in, hooks was also something that we talked about in the TSC. We removed a lot of old code as well. We have initiatives for that. So removing rack bootstrap, that's out of their reflex box. We're done with that. And then the ESLint rules many are born and they also die here. And many, many more. We have a whole like notion board. That's what we use internally. They can see the whole list of everything that we've approved, rejected. It's not really the whole list cause you can scroll and there's like tons more. But you know, this is everything that we're working on here.
Yeah. Yeah, no, the front end TSC has definitely been like super valuable. I think I remember, I remember like when we just started the TSC, I think it was, so when did we do this? It was like, maybe three and a half years ago. It was pretty early on in my tenure here that we like formed the TSC. I remember like one of the first ESLint rules that we added was just like always use const or it was like use const over let kind of thing. Where it's like only use let if you have to. And even that I remember people were like, Oh, it's like another extra character. But then it was once you managed to get the ESLint rule and everyone just stops complaining, it's the same thing with like Prettier where it's like, as long as everything's consistent, like no one's gonna complain kind of thing. So it's actually been really nice to have a formal working group for that, where we meet and talk about it. Sometimes it really does get into the nitty-gritty details of like, um, you know what, I don't, I don't know. Like we've talked about color is pretty in-depth there too. There's a lot of things, very small decisions that take up a lot of time, but I think they're super useful and I, I think we both recommend having this kind of meeting, you know, at any organization that has like a, you know, a growing code base like this.
Yeah. So, you know, I had to bring it back to the people here right at the end. Of course, yeah. Thank you to all the people. Yeah. And I think, I think that's pretty much it, what we had in this talk when we were putting this together. We kind of, you might, you might notice that kind of intro was like, are we going to talk about like so much code stuff? And then we realized, Oh God, we only have an hour here, so we kind of reduced it down to just like, you know, kind of how we, how we build things. And, you know, here's kind of a pretty interesting story. But yeah, if anyone had any more questions or wanted to ask the same thing, we're, I think we're here for another five minutes and we can certainly, you know, talk about stuff, you know, feel free to type things into discord and we can answer them. Yeah. We can unmute on zoom. Yeah. Maybe, maybe. I don't know. I don't know if that's like allowed or not. You're brave enough, right? I don't know. Otherwise we'll just like, you know, hang out here and talk, talk more about, I don't know. What are, what are some, someone's typing something. No, let's see. We got no pressure, no pressure. Yeah. Yeah. Thanks so much for coming to the talk.
Coding Practices and Testing
Hopefully you learned something about how we do coding here and maybe took away some, some good lessons, right? We don't do everything perfectly, but we're always trying to evolve and maintain things properly. Definitely like, you know, being proactive about about maintainability has been like super valuable here. Like I've certainly seen places before where maintainability becomes more of like a reactive thing where things just start to become like really hard to like to.
Front End Code Reviews and Production Deploys
Front end code reviews are conducted through pull requests on GitHub. GitHub owners are assigned as reviewers for files owned by other teams. For production deploys, the front end and backend were previously deployed together, but now only the front end needs to be deployed for front end changes, making the process quicker. The visual snapshot and preview builds are currently being internally tested.
But so how do front end code reviews where I don't know he wants, and you and you want to take this one day, I mean, front end code review. It's like it's, it's, it's, it's the same as any code review that we have. We just put up a pull request on GitHub and then we have, we actually have GitHub owners. So we have like the, basically whenever you do a pull request, which if you modify any files that are owned by another team, then it'll put them on as a reviewer. and then you just do the, I guess the review that way you just, get the comments and revise. and then for the front of production deploys, we actually have something we're recently trying to split up and deploys into the front and backend. So typically if you make friend and co-changes, you actually have to deploy both the front and the backend. but now we're changing it so that you only have to deploy the front end if it's just front end changes. And that makes it like a lot quicker. and that's something that that's the person who is responsible for a lot of like the visual snapshot and the preview builds is working on right now. And we have that, I think that we're internally testing it right now.
Measuring Page Load and Google Web Vitals
To measure page load consistently with Sentry in a single page app, you can manually instrument the application by marking the point when all API endpoints have returned and the page is rendered. Alternatively, you can rely on Google web vitals like FCP and LCP, which determine when the largest contentful paint has occurred. This approach provides a semantic representation of performance, considering the loading of the most significant element as the completion of page load. The FCP and LCP metrics are crucial, especially with Google's page rank algorithm update.
All right. I've opened up the zoom chat. I see that we've got a few questions over here. How do you measure page load consistently? It's from Giorgio. How do you measure a page load consistently with sentry in a single page app, IE making sure all the requests you consider to part of the page load or waiting for to be part of the page load or whatever. I can, I can take this. So I think this is a problem that I encountered when I was doing some of the performance work before. And this is something that you can manually instrument, which is something that I did initially. so you just manually instruments so you say, okay, once the application has reached this state, so once it's got a return from all these API endpoints and you know, it's rendered either in like the component did update or like something like that, that's like a bit older. But once you got all the information, you can put like a mark and connect that up with Sentry and you can do it that way. you can also just rely on like the Google web vitals, which I think is kind of what we're trying to lean towards. We're trying to make it more automated like that. So things like FCP and LCP, I don't know if you're familiar with those web vitals, but you can see the strategy that, you know, the browsers used to actually determine when that point has been reached. I think for LCP, which is largest contentful paint, typically it's correct for at least things that we're trying to measure. So it won't say that the loading spinner is like the largest content, full pain. It will usually say like the biggest block of text, which happens once you load the issues page and that kind of correlates strongly with user experience. So we try to lead more towards that because we think that's like kind of like the, like a semantic way to represent performance. I'm saying that once the largest thing has loaded, then consider it loaded and usually the largest thing is the actual thing that we want these just to see. Yeah, that's like kind of the way that we've tackled that. Yeah. And it is like a little bit. A lot of that kind of stuff happens over in the SDK. So yeah, definitely the FCP LCP stuff. Especially now it sounds like, I think, I don't remember when this happened or if it's, if it has already happened, but I think Google had, was changing their page rank algorithm where you'll get ranked lower if your LCP is, yeah. So that's part of the reason that this has become like kind of important, Yeah. If it actually takes a long time for your your large pain, I can't remember if it's LCP or FCP. It might be, it might be a combination of both. I can see that. Probably FCP actually, since you know, the first pane is probably the more important one.
React Usage and Template Mixing
We only use React on the client side and do not perform server-side rendering. While server-side rendering can improve performance, integrating it with our separate tech stack would be a significant undertaking. We primarily use Django to render a bare-bones layout.html file, delegating most rendering and functionality to the front-end app. There are a few instances where we mix templates with React, such as the password strength indicator on the registration page for the open-source Sentry. However, this is minimal and self-contained.
Sure. Let's see, we got another question over here in the chat. Do you only use React with the client side or do you, do you only use React with client side rendering? Do you mix old templates with React? So yeah, so right now we do only use React on the client side. Like I said, we, we have kind of a Python Django back-end. There probably is actually, I can imagine there might be some like crazy like integration thing where you can like run node to like render you know, like render the server-side stuff through Django with like some node instance or something. I imagine that might be a thing. We don't do any sort of server-side rendering. It definitely does have performance benefits to do that. It certainly could make things a lot faster. That said, because of the fact that we're well the, you know, the tech sacks are two separate things. I think it would be probably a lot of work to integrate something like that. Yeah. I'd be right, honestly. I might be right. I don't know. Maybe, maybe, maybe it is kind of easy. Maybe chat knows but, but yeah, so right now it's, it's pretty much all just client-side. So kind of as I sort of implied where it's like we have this kind of front-end single page app and then we have our back-end thing do we mix kind of template abuse with React stuff? Mostly no. So the way that it works right now is that Django renders this, this I think the file is just called layout.html where it's like really pretty bare bone. It used to have a bit more stuff in it. It used to like render like things like alert bars at the top of the page. It doesn't do that anymore. That's all delegated to the front-end app now. But it is the thing that, that loads the, that loads the app and actually runs the app. I think we literally export like one or two things to the global window now. And I think one of them is like render app. Though even some of that might've changed kind of with some of this front-end deploy stuff that you mentioned that we're doing. But we don't, we don't mix things for the most part. There is, I think there's like maybe one or two places where we have, I think we have like that. There's one thing that I can think of where we have the registration page, which is the, this is, this is Sentry on-premise only by the way, the registration page for sentry.io is completely different because it's on the marketing website. But the registration page for the open source for this entry has a, it has a password strength indicator, which is a rack component that we export onto the window and then is rendered in that HTML view. So there is a little teeny bit of mixing like that, but it's you know, it's pretty self-contained.
Internal Client Libraries and Webpack Version
Let's see. Got a few more questions over here. I'm like internet client libraries. What method and requirements were you considering to create it now? So internal client libraries. I guess I'm still not exactly sure what internal client libraries means. Can you clarify a little bit more Rusky? We are using webpack five. It was maybe a little bit of work to migrate over to it, but it wasn't too bad. We're one, the latest versions of most things. Yeah. We try to keep it latest except for select things like react router and reflux. If we were to rebuild Sentry today, we probably wouldn't use Redux. I personally like React context and have been interested in React query as an asynchronous data fetching library. We have mobX, React Popper, React Select, and e-charts in our package.js.
Let's see. Got a few more questions over here. I'm like internet client libraries. What method and requirements were you considering to create it now? So internal client libraries. I guess I'm still not exactly sure what internal client libraries means. Can you clarify a little bit more Rusky?
See, we have a clarification. What version of my webpack are using? Oh, I see there is, there is a clarification. What libraries? Well, he's typing right now. Let's wait for it. We are, we are using webpack five. It was maybe a little bit of work to migrate over to it, but it wasn't too bad. We're one, the latest versions of most things. Yeah. We try to keep it latest except for select things like react router and reflux.
What is the UT template code? Maybe I'm still confused. I mean, someone said that, that, what client libraries would we use now? Like say, for example, like we talked about how reflux was bad. Oh, Oh, I see. I see what you're saying. Sorry. I think that's what, I mean, we could talk about that, you know, we can talk about that. I guess. Yeah. I mean, I feel like I can speak kind of just like to my own experience if, if we were to rebuild Sentry today, I guess it's, it was a little bit more difficult. I mean, I, I, I don't know if that's a bad thing, but I think it's, I mean, I rebuild Sentry today, I guess it's, it would certainly be kind of all of the sort of mindset. So, so one of the things I guess I can specifically talk about is like, you know, we want to get rid of this, this reflux old thing. We probably wouldn't use Redux. I, I, my, my personal feeling of, of Redux is that there's a lot of boilerplate code there. And it's kind of a little bit too much. We would probably use a good bit of React context I think. Though I think there's also maybe some performance implications of using too much React context. But one of the libraries I've been super interested in recently that I, that we've started to kind of like experiment with is React query, which is kind of like a asynchronous data fetching library, though it doesn't actually do any of the fetching. I think you probably just need to search for that. But it kind of handles the deduplication requests. It handles the caching, it handles reloading of data. There's a few more things, I actually don't know too, too much about it, but it looks very interesting to me. In terms of like other client libraries, I mean, I think like React's great, what other, what other stuff? I personally still like React Writer 3. I mean, I don't know, there's a whole bunch of cool stuff and we can even just like kind of look at our like package.js, we made it, we made a joke earlier how it's like, oh, we just walk through the whole package.js with everyone. You can just switch over to your. Oh, you're right, you're right, you're right. But there is, there is some, there is some kind of neat stuff in here that I think that we like, we have mobX in here. I think actually mobX is kind of like, it's a pretty nice like library for, for kind of reactive things. I've actually used it in some of my side projects and I quite like it, but I will say it's a little bit harder to document than it is something like Reflux, which I think, or Redux, sorry, which is, I think pretty good at document itself despite being like tons of boilerplate code. One of the other things in here that we use this kind of cool is like React Popper is a really cool library for doing like pop-over and things like that. I also really like React Select. That's a really nice library. We use e-charts for all of our chart visualizations. And I think, I think that's actually been impressively good considering how hard it is to do charts. Yeah, I think, pretty solid. Yeah. Oh no.
Wrapping Up and Final Remarks
We're wrapping up the workshop now. If you have more questions, join the Sentry Discord where David and I can provide further information. Sentry is open source, so feel free to explore the source code. Adding comments to core pages can be helpful for understanding how the app works. Remember to document your code to aid others in understanding it. Thank you to Alicia for the props. Goodbye and thanks to everyone for joining!
We're just like talking about, talking about cool libraries these days or now. Um, let's see, do we have any more questions over here? No. Yeah. I think we'll probably wrap it up. Um, I think we're yeah, a little, like 10 minutes over time here. Um, cool. Um, like I said, definitely hop in. There's a Sentry discord. Um, me and David are also in there. We can certainly answer more questions about front-end, how we build front-end. Sentry's totally open source. Go, you know, read the source code. Um, I've actually been adding quite a bit more like kind of comments recently to like, kind of big core pages. So like, for example, I added a, um, I added this whole big comment to the app index file, which explains just how the app even boots. Um, even though like literally this is like all it does. Um, but this is actually kind of nice if you're just like, if you're just like looking at Sentry code or like browsing through it, this is kind of like a nice introduction to just the way it works. Um, and yeah, same thing with like the routes file, I added like a whole Comment in here about like how the routes file is structured. This is kind of like another pro tip is like, you know, add lots of like, kind of read me documentation and deer and your code because it's really, you know, people will read that stuff sometimes not, but a lot of times they will, and we got some props. Thank you. Alicia. Bye. API. Cool. All right. Um, thanks everyone for joining. We really appreciate it.