About a year ago, our team decided to undertake a massive rewrite of much of our application. We knew we wanted to add a ton of new features and functionality to help our users fix bugs as painlessly as possible, but part of this meant rethinking the way our Dashboard’s front-end was built.
Recently, we released a new Bugsnag Dashboard to the world, which has been incredibly exciting. You can read more about it here in case you didn’t hear about it. Throughout the process, we learned a lot, and we thought sharing our experiences might help other product teams who are thinking of undertaking similar projects.
One of the main reasons we wanted to use React was so we could build components that would be reusable in multiple areas of our application. As it turns out, building components is one of the main use cases for React. Since components keep their code so encapsulated, they make reusing code, testing, and separation of concerns easy.
We chose a few technologies that would help us integrate React:
We also decided to use Reflux instead of traditional Flux. Reflux is a refactored version of Flux that makes everything more dynamic and adheres more closely to the principles of functional reactive programming.
For numerous reasons, including the fact that the Rails asset pipeline does not allow us to harness sourcemaps, we decided to remove the asset pipeline and use Gulp for our asset compilation needs. Gulp is a Node.js streaming build system, strictly providing streams and a basic task system. We use Gulp to compile all of our assets, including everything we need to run React and related technology.
React was a great library for us to work with, and although the rewrite was a huge project, we definitely learned a lot. The library is unique and allowed us to do a lot of really cool things, but we had to learn how to structure and use it properly over time. Along the way we made some mistakes, but ultimately learned how to use it and get us to the Dashboard we have today.
One of the early mistakes we made was not using mixins correctly. At first, when we had a pretty reusable piece of code, we would decide that it would make a great mixin. After making quite a few mixins and developing on top of them, we realized they ended up being either extremely brittle or extremely generalized. This made our code harder to maintain, which defeated the whole purpose of code reuse. In the end, we pulled out these mixins into components. We learned not to use mixins for view code, but instead for small and very specific logic. For example, we have a mixin for managing our API requests, and another mixin for creating and managing application hotkeys. Every mixin we have now is very small and very specific.
A helpful thing we added to React was the ability to request data from Reflux stores while a component is on screen. It allows you to “mount” a store, which automatically #connects to that store, and while on screen can ask the store for certain data. When that data is fetched, it will automatically update the state. On #unmount, it automatically removes that hook. We developed this workflow before Facebook released Relay, which handles approximately the same use case. In the future, we will probably switch over to using Relay so we don’t have to maintain our own system.
Another thing we found handy was to use the PureRenderMixin in pretty much every component. If your React component’s render function is “pure” (in other words, it renders the same result given the same props and state), you can sometimes get a performance boost by using this mixin. We did run into a pitfall though: this mixin works by comparing your component’s data to avoid unnecessary re-renders during changes, but it only shallowly compares the data objects. That means if a component contains complex data structures, it may produce false-negatives for deeper differences.
One future improvement we are considering is using Immutable-js, which would allow us to use the PureRenderMixin in more components. Immutable-js makes it so that we can create immutable data, which cannot be changed once created. This means much simpler application development, no defensive copying, and enabling advanced memoization and change detection techniques with simple logic. Because we wouldn’t be modifying original objects, Immutable-js will allow us to use the PureRenderMixin on components with complex data structures without producing false-negatives.
At Bugsnag, we are really excited about the architectural changes we’ve made to the product for the new Dashboard. We hope these changes helped enhance your experience as they allowed us to make more meaningful representations of the data, helping you to debug easier. From an engineering perspective, these changes allow easier maintenance of the code, and the component-based system enables us to create new features faster. We’re hopeful that in the future we can open source our components and contribute to the React ecosystem. We hope you love the new dashboard, and we would love your feedback! Reach us on Twitter.