Initially making a decision to use Flow vs Typescript we decided to go with flow as we wanted our code to run in a way we wrote it, because when using Flow types are simply removed from the code without modifying the code itself. Sadly, the type system of Flow was in some cases very hard to understand and declare the types correctly, especially in cases when the structure is very dynamic (e.g. object keys and values are created dynamically). Another reason was bad integration with IDE and frequent crashes which made DX very poor. Therefore, we made another evaluation of Typescript and decided to move towards it. As our code base was pretty big when we decided to migrate to TS we couldn't just stop and re-write everything, that's why we started writing new modules in Typescript as well as transforming old components. To make that possible we had to configure Webpack loader to support simultaneous bundling of Flow&JS and Typescript. After around 2 months of the transformation we have around 40% of code being written in Typescript and we are more than happy with integration TS has with IDE, as well as ease of declaring types for dynamic modules and functions.
Simple but flexible API for web, what allows us to have a very quick start of using this tool and doesn't cause any headaches exploring the specification. Flexible API allows to easily override some default properties in the context. Issues with inconsistent data can be resolved very easy and fast with real-time debugging.
As we work in the area of smart TVs, performance optimization is a very important aspect for us. That's why we want to have a full control of the data we are receiving over the network. Graphql allows to solve exactly this problem, so you will receive only the data you need, this way you can prevent overfetching, which does not only influence network bandwidth, but also computation resources needed to parse the JSON. Another good reasons to go with Graphql are data schema and analysis of your queries which prevents many errors (like property name mismatch, invalid nesting, etc.) already during the development time. Last but not least, it's very easy to understand what data the component is using just by looking at the declarative structure of the query which is used for this component, so no more need to run the application in the debug mode to see what data is received in the component as an input.
As we have to build the application for many different TV platforms we want to split the application logic from the device/platform specific code. Previously we had different repositories and it was very hard to keep the development process when changes were done in multiple repositories, as we had to synchronize code reviews as well as merging and then updating the dependencies of projects. This issues would be even more critical when building the project from scratch what we did at Joyn. Therefor to keep all code in one place, at the same time keeping in separated in different modules we decided to give a try to monorepo. First we tried out lerna which was fine at the beginning, but later along the way we had issues with adding new dependencies which came out of the blue and were not easy to fix. Next round of evolution was yarn workspaces, we are still using it and are pretty happy with dev experience it provides. And one more advantage we got when switched to yarn workspaces that we also switched from npm to yarn what improved the state of the lock file a lot, because with npm package-lock file was updated every time you run
npm install, frequent updates of package-lock file were causing very often merge conflicts. So right now we not just having faster dependencies installation time but also no conflicts coming from lock file.
Typescript incorporated a lot of features from ES6 with its very useful syntax sugar what can make the code more concise and expressive. Main ES6 features we are currently using are: * destructuring, spread and rest * async/await * arrow functions * const, let * classes (but still following more functional approach) * default values * string interpolation
We think that it's extremely important that developers learn and become experts in ES6, as it has a lot of new syntax structures which have hidden edge cases and this might sometimes create unexpected bugs in the system. Good example of such bugs can be default values which are not replacing falsy values (what || would do), but it only provides a default when the value is undefined.
At Joyn we use AWS infrastructures extensively, so we need a wide range of different tools starting from file storage until the health monitoring of our running services. For Smart TV stack we use S3 to mostly store static files (like scripts, styles and html), CloudFront to keep good response times using the cache, Lambda to recognize the TV platform and serve appropriate bundle (except Samsung Tizen platform where application is packaged), CloudWatch to receive alerts in case we have any issues inside of our lambda function.
Main argument for us to use Sentry was that we can report handled exceptions, so we can keep track of how many problems are happening inside of the code base. This is very important for us, as we cannot leave the user with the unhandled exception, because there will be no chance to reload the application, there might no focused element on the page what will make impossible to interact with the application with the remote control. This way we try to write safe code with captureException/captureMessage in places where something wrong happened, at the same time we provide user a fallback (degrading experience) or at least an error message where it's possible to reload the application.
The best bundler on the market which has a lot of documentation and plugins available. Another good reason to go with webpack if you need to have a very granular configuration of your project building process. In our case we needed to be able to build application for different TV platforms, as well as somehow keep running together Typescript and Flow codebase.
As we are using GraphQL we also need to have a wrapping library which will take care of sending requests, handling errors, caching. Apollo currently is one of the best libraries on the market, which is flexible enough and has a very good integration with React. This way we make our code perform well as well as have a good structure. It also provides a very good visibility of the lifecycle of the requests which helps a lot to keep the components in the correct state. Worth mentioning that heavy lifting with the data caching is also taken from our shoulders with the help of Apollo InMemory Cache. Apollo InMemory cache allows you not to worry about it at all and utilize the cached data stored in the cache in the best possible way, as it can inspect for the query you're sending and check if all needed data is already available. This being said we are more than happy using Apollo (apollo-client, react-apollo, apollo-cache-inmemory) in our project and see a big potential in everything it provides.
One of the best UI frameworks nowadays. We also had very good expertise using it in previous project. One of the advantages over Angular or Vue, it doesn't carry over with it many additional dependencies which we won't need, as our main task is to render and update the UI in the most performant way. We didn't go with Preact as we want to stay on the cutting-edge with most modern ways of developing with React and bundle size it not very critical for us, we can live with additional second on the initial load, but it's important for us to have the best possible performance during the runtime.