React Hooks have introduced a new approach to managing stateful logic; given React components have always been closer to functions than classes, Hooks have embraced this and brought state to the functions, instead of taking function as methods to the state with classes. Based on our experience, Hooks improve reuse of functionality among components and code readability. Given Hooks’ testability improvements, using React Test Renderer and React Testing Library, and their growing community support, we consider them our approach of choice.
Since we first mentioned CSS-in-JS as an emerging technique in 2017, it has become much more popular, a trend we also see in our work. With some solid production experience under our belts, we can now recommend CSS-in-JS as a technique to trial. A good starting point is the styled components framework, which we mentioned in our previous Radar. Next to all the positives, though, there usually is a downside when using CSS-in-JS: the calculation of styles at runtime can cause a noticeable lag for end users. With Linaria we're now seeing a new class of frameworks that were created with this issue in mind. Linaria employs a number of techniques to shift most of the performance overhead to build time. Alas, this does come with its own set of trade-offs, most notably a lack of dynamic style support in IE11.
Through their extended use of Kotlin, our development teams have gained experience with more frameworks designed specifically for Kotlin rather than using Java frameworks with Kotlin. Although it's been around for a while, Exposed has caught our attention as a lightweight object-relational mapper (ORM). Exposed has two flavors of database access: a typesafe internal DSL wrapping SQL and an implementation of the data access object (DAO) pattern. It supports features expected from a mature ORM such as handling of many-to-many references, eager loading, and support for joins across entities. We also like that the implementation works without proxies and doesn't rely on reflection, which is certainly beneficial to performance.
GraphQL Inspector lets you compare changes between two GraphQL schemas. We've cautioned against the use of GraphQL in the past, and we're happy to see some improvements in tooling around GraphQL since. Most of our teams continue to use GraphQL for server-side resource aggregation, and by integrating GraphQL Inspector in their CI pipelines, we've been able to catch potential breaking changes in the GraphQL schema.
Given our experience that tests are the only API specifications that really matter, we're always on the lookout for new tools that might help with testing. Karate is an API testing framework whose unique feature is that tests are written in Gherkin-based syntax without relying on a general-purpose programming language to implement test behavior. Karate uses a domain-specific language for describing HTTP-based API tests. Our teams like the readable specification that they get with this tool and recommend to keep tests with Karate in the upper levels of the testing pyramid and not overload its use by making very detailed assertions.
As Kotlin is used increasingly for both mobile and server-side development, the associated ecosystem continues to evolve. Koin is a Kotlin framework that handles one of the routine problems in software development: dependency injection. Although you can choose from a variety of dependency injection frameworks for Kotlin, our teams have come to prefer the simplicity of Koin. Koin avoids using annotations and injects either through constructors or by mimicking Kotlin's lazy initialization so that objects are injected only when needed. This is in contrast to the statically compiled Dagger injection framework for Android. Our developers like the lightweight nature of this framework and its built-in testability.
Our teams have continued to use and appreciate the PyTorch machine learning framework, and several teams prefer PyTorch over TensorFlow. PyTorch exposes the inner workings of ML that TensorFlow hides, making it easier to debug, and contains constructs that programmers are familiar with such as loops and actions. Recent releases have improved performance of PyTorch, and we've been using it successfully in production projects.
Rust is continuously gaining in popularity. We've had heated discussions about which is better, Rust or C++/Go, without a clear winner. However, we're glad to see Rust has improved significantly, with more built-in APIs being added and stabilized, including advanced async support, since we mentioned it in our previous Radar. In addition, Rust has also inspired the design of new languages. For example, the Move language on Libra borrows Rust's way of managing memory to manage resources, ensuring that digital assets can never be copied or implicitly discarded.
Sarama is a Go client library for Apache Kafka. If you’re developing your APIs in Go, you'll find Sarama quite easy to set up and manage as it doesn't depend on any native libraries. Sarama has two types of APIs — a high-level API for easily producing and consuming messages and a low-level API for controlling bytes on the wire.
Apple has taken a big step forward with their new SwiftUI framework for implementing user interfaces on the macOS and iOS platforms. We like that SwiftUI moves beyond the somewhat kludgy relationship between Interface Builder and Xcode and adopts a coherent, declarative and code-centric approach. You can now view your code and the resulting visual interface side by side in Xcode 11, making for a much better developer experience. The SwiftUI framework also draws inspiration from the React.js world that has dominated web development in recent years. Immutable values in view models and an asynchronous update mechanism make for a unified reactive programming model. This gives developers an entirely native alternative to similar reactive frameworks such as React Native or Flutter. SwiftUI definitely represents the future of Apple UI development, and although new, it has shown its benefits. We've been having great experience with it — and its shallow learning curve. It's worth noting that you should know your customer's use case before jumping into using SwiftUI, given that it doesn't support iOS 12 or below.
With the aim of improving performance in our code, profiling tools are very useful to identify bottlenecks or delays in code which are hard to identify, especially in asynchronous operations. Clinic.js Bubbleprof represents visually the async operations in Node.js processes, drawing a map of delays in the application's flow. We like this tool because it helps developers to easily identify and prioritize what to improve in the code.
There are still some tool gaps when applying good software engineering practices in data engineering. Attempting to automate data quality checks between different steps in a data pipeline, one of our teams was surprised when they found only a few tools in this space. They settled on Deequ, a library for writing tests that resemble unit tests for data sets. Deequ is built on top of Apache Spark, and even though it's published by AWS Labs it can be used in environments other than AWS.
In the previous edition of the Radar we had BERT — which is a key milestone in the NLP landscape. Last year, Baidu released ERNIE 2.0 (Enhanced Representation through kNowledge IntEgration) which outperformed BERT on seven GLUE language understanding tasks and on all nine of the Chinese NLP tasks. ERNIE, like BERT, provides unsupervised pretrained language models, which can be fine-tuned by adding output layers to create state-of-the-art models for a variety of NLP tasks. ERNIE differs from traditional pretraining methods in that it is a continual pretraining framework. Instead of training with a small number of pretraining objectives, it could constantly introduce a large variety of pretraining tasks to help the model efficiently learn language representations. We're pretty excited about the advancements in NLP and are looking forward to experimenting with ERNIE on our projects.
MediaPipe is a framework for building MultiModal (such as video, audio, time series data, etc.), cross-platform (for example, Android, iOS, Web, and edge devices) and applied ML pipelines. It provides multiple capabilities, including face detection, hand tracking, gesture detection and object detection. Although MediaPipe is primarily deployed to mobile devices, it's started to show up in the browser thanks to WebAssembly and XNNPack ML Inference Library. We're exploring MediaPipe for some AR use cases and like what we see so far.
CSS tools and frameworks offer predesigned components for fast results; after a while, however, they can complicate customization. Tailwind CSS proposes an interesting approach by providing lower-level utility CSS classes to create building blocks without opinionated styles and aiming for easy customization. The breadth of the low-level utilities allows you to avoid writing any classes or CSS on your own which leads to a more maintainable codebase in the long term. It seems that Tailwind CSS offers the right balance between reusability and customization to create visual components.
If you need to ingest data from relational databases into a Kafka topic, consider Tamer, which labels itself "a domesticated JDBC source connector for Kafka." Despite being a relatively new framework, we've found Tamer to be more efficient than the Kafka JDBC connector, especially when huge amounts of data are involved.
The Golang community has had its fair share of dependency injection skeptics, partly because they confused the pattern with specific frameworks, and developers with a system-programming background naturally dislike runtime overhead caused by reflection. Then along came Wire, a compile-time dependency injection tool that can generate code and wire components together. Wire has no additional runtime overhead, and the static dependency graph is easier to reason about. Whether you handwrite your code or use frameworks, we recommend using dependency injection to encourage modular and testable designs.