Migrating from Android to React Native, is it worth it? – Part 2 Code Analysis

Overview

(This is a reproduction of a post I wrote for my consulting company.  The original can be found here).

This is the second part of our 4 part React Native (RN) evaluation. In this post, we delve in software metrics comparing the RN and Android applications.

There are many metrics that could be used, and whichever metric you care about (all or some), always remember that you only get a partial picture of the nature of the code. As they say “The map is not the territory”, so take this data with a grain of salt.

Other parts of this analysis can be found here:

Metrics collection

To do the analysis of the code, we used SonarQube, our preferred analysis tool. It’s a wonderful tool and we recommend it to all of our clients. It supports a large number of languages. Java and Javascript (including Flow) are supported.

Note that this is not a “paid” advertisement, we just like the tool.

The metrics that we use are:

  • Lines of code
  • Maintainability
  • Cognitive complexity.

The choice of these metrics is debatable as the LOCs will have an impact on maintainability and complexity. Same thing can be said between the relationships between maintainability and complexity. That being said, it is our opinion that we still gain by the analysis of these.

React Native modularization

The RN application is split in modules. This is not something we initially planned as it makes our analysis more complex. The reason for this modularization is explained in a later section. For the moment, keep in mind that the RN application is composed of 5 modules (4 libraries and the main app). The following diagram describes this:

The top level module (reacticity-montreal) is the application itself and is not offered as open source software (OSS). The other modules are available as OSS and can found on both GitHub and NPM.

Analysis Results

Lines of Code

As stated, Urbanoe Mobile is one project. Here’s a summary of its analysis:

The application is composed of 33KLOCs. Most of it is Java but there’s also quite a bit of XML files. In any case, this is a substantial amount of code. Though, it’s far from being an outlier when it comes to Android projects. We have worked on a number of Android apps that had more code than this.

(With regards to the code smells, a large portion of those are due to deviations from the standard Java coding standard, which we adopted late. None of the deviations are important).

As to the RN modules and application we have:

The whole application stands at roughly 5KLOCs. This was quite a surprise considering that with these lines of code we have an app that works on both Android AND and iOS.

Obviously this does not tell the whole story as the Android app implements a lot more features than the RN app. We need to normalize this data to compare apples to apples.

The normalization was done as follows. Each package of the Android app was reviewed to see where it fit (or not) in the RN application. Practically, each package was categorized as either mobile-ui, communication, common, model, app or “not replicated”.

We debated as to the coarseness of this categorization. Class or method categorization were also valid choices. There are 80 packages while there are over over 800 classes and close to 3000 methods. Categorizing using packages is more efficient in our eyes. Since our code has good cohesion, we would gain little to do with a lower coarseness. In other words we won’t have functionality that is badly categorized (but may get some level of non replicated code making its way in the categorization).

Doing this categorization exercise yields the following results:

Let’s dive in those numbers and justify them (or not):

urbanoe-model

In the Android app, the model is defined using POJOs with accessors and some domain specific methods. In the RN app, we use Flow to define our types. Furthermore, all model instances are stored in Redux and made immutable (so no accessor is required). Here’s a comparison of the code: Android and RN. As you can see, there are no accessors, no domain methods. The “+” sign is used indicate immutability.

urbanoe-common

The “urbanoe-common” code is a grab bag of formatting code used throughout the application. It contains a lot of presently unused code and for this reason, we consider this module to be an outlier.

This module will probably be re-written if the RN project is kept. It has the lowest cohesion in the system.

urbanoe-communications

This is one of the most interesting parts of our analysis. The 6KLOCs Android communication system was replaced by 800 LOCs of Redux and Redux Thunk. We lost a little bit in terms of performance as the Android system is multithreaded but this difference in performance is not noticeable practically. We also gain through the use Redux middlewares which offers us time travelling and data persistency for free.

urbanoe-mobile-ui

Again, a non negligible difference. Looking at the code, it appears that Javascript is simply more concise to express UI constructs. Using pure functions made the code even simpler and more testable. When implementing Android activities and Fragments, you have to conform to class APIS and therefore need to override a lot of methods for each one. This is not something you have in the Javascript world.

reacticity-montreal

This is the code for the app itself. Again, Javascript is more concise. There’s a lot of Android methods being overridden here too. One discriminating factor here is the fact that the “upper” classes are more difficult to split according to supported/unsupported features and therefore some code bubbles up.

Maintainability

SonarQube defines a maintainability rating (also known as the SQALE value). This value is an indication of the effort required to fix technical debt on a file (or project).

For the Android app, maintainability is as follows:

For the RN app, we have the following:

Though not shown in the preceding diagrams, the worst Android code (ratings C, D and E) can be divided as:

  • the login/logout functionality (C rating)
  • the communication mechanism (D and E rating)
  • some of the complex screens (C rating)

The login/logout maintainability can be discounted as this functionality is not implemented in the RN application.

With regards to the communication mechanism, again we believe that the complexity difference can be explained through the use of Redux. Redux code is easier to maintain and test. It is our impression, looking at the Redux code, that complexity of the RN communication package will increase linearly for the coming months.

As to the complex screens, they exist in both RN and Android, so we can’t truly explain the difference through the complexity of the screens themselves. From the analysis of the code, we believe that the difference here can be explained through:

  • the Android asynchronous event handling used by Activities and Fragments (which is more complex and error prone than the equivalent in React/Redux).
  • the split between rendering code and “connected code” promoted in React/Redux. It makes for a larger number of components but of much greater simplicity.

Cognitive Complexity

Cyclomatic complexity is not a good measure of overall complexity. It does a good job in determining the number of test cases a method should have but it’s pretty useless to determine how understandable a given method is. Furthermore, it becomes totally useless on a class or project basis as the modularity of your code will actually worsen your score.

We will use cognitive complexity instead. It does a better job of capturing code clarity and elegance. You can find an explanation of cognitive complexity here.

Sadly, there are no fancy graphs in this section. What we’ll do is characterize the cognitive complexity for each module and for the worst 10% of the code (as complex code is usually not spread out equally throughout a codebase).

For the Android code, we have:

The total complexity value is 1815. It’s quite interesting to note that we have a score of score of 1252 for the worse 10% of the code. Basically this means that 10% of the codebase accounts for 70% of the complexity.

For the React Native code, we have:

The RN codebase is far less complex than the Android codebase. The average complexity is 0.75 while 10% complexity is around 6. In other words, the RN code is more understandable than the Android code. That being said in the RN case too, 10% of the codebase accounts for close to 70% of the complexity. All of this complexity is in the UI. In other words, when writing a standard RN app, you can expect your UI code to be where complexity will be found (though it will be lower than what you have in Android).

We don’t know if all of the elements that go in the calculation of the cognitive complexity are exponential in nature but since some of them definitively are, we can safely say that cognitive complexity is exponential (the only question is about its “sloping”… ).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s