Front End – Modernization Plan

While working at REDACTED-2, I reported the following issue.

The part of the REDACTED-A application that is implemented in AngularJS, has a few points for improvement, some easy, others less, and others practically unthinkable in the current state. The hope is that, by making the easy improvements now, the difficult ones over a period of time, the unthinkable will eventually become achievable.

Among the easy improvements we have the reorganization of the code in many independent files. This will then allow us to use modern JS code that we will later compile and optimize in ways that are now practically forbidden to us. (like tree-shaking)

Among the difficult improvements we have the refactoring to extract the web components from the application. Modern JS frameworks, including AngularJS from version 1.5 (we are currently using 1.4), allow you to structure an application in web components, which are then compiled to standard HTML, CSS, and JS.

Among the unthinkable improvements in the current state we have the migration of AngularJS to a framework that is going to last longer and is more modern. In September 2019 there are several top-tier options: Angular 8.2, React 16.9, and Vue 2.6.

How do we start?

The initial modernization plan that I propose contains many parts, all intended to change the application without causing disruption at the production level, and with minimal disruption at the development level.

  • Disconnect the Angular app from the Java app
  • Extract the Angular app into its separate repository
  • Transform the repository from Mercurial to Git
  • Format the code according to some standard
  • Compile the code to be able to use modern JS
  • Separate each functionality in its own file

How do we continue?

Previous changes viewed existing code as a black box, crafted in a certain way to produce an application, and we were only interested in crafting it in a different way to produce the same application.

So now we have an old AngularJS application inside a modern development environment. What we will do successively is to take advantage of this environment to renew the code as well.

  • Extract reusable code
  • Use Lodash
  • Use modern JS (ES6 +)
  • Use formal JSON validation
  • Extract components and services
  • Manage application settings
  • Manage application status
  • Automate end-to-end tests
  • Use GraphQL

And we will do all this little by little, while we continue to develop and maintain the application.

UPDATE 1

In 2019, I developed the new REDACTED-B and REDACTED-C applications:

  1. each one with VueJS and the Quasar component library
  2. each one in its own repository independent from that of the REDACTED-A application

After integrating it into REDACTED-A following the instructions in the tutorial

Migrating an Angular 1.x app to Vue 2.x
A ridiculously detailed and opinionated attempt to let Angular and Vue peacefully live together (if you wanna rock’n’roll)

the REDACTED-C application will remain as a guiding star for the successive and gradual migration of AngularJS.

UPDATE 2

The integration of REDACTED-C in a tab of the REDACTED-A application was a success, thanks to ocLazyLoad (a tool that we already had installed, but that we used in a sub-optimal way) that allowed us to reduce the boilerplate to unsuspected minimums.

Now, in light of this latest development, I think we can effectively migrate from AngularJS to VueJS after performing the Extract components and services step, for which the tutorial, whose steps I replicated and left “active” in REDACTED-C, marks a sufficiently simple and effective path.

Doing the migration sooner is unthinkable because the current code of the application is not only spaghetti, but also suffers from neglect problems at other levels, first of all the indiscriminate use of the $scope variable and the $timeout function. Because of this, it is very difficult to understand the execution context, and which parts of it are related to each sequential chunk of code.

We cannot introduce substantial changes such as those of migration without first putting order. In this phase we are only interested in partitioning the context into relevant blocks for each functionality (from spaghetti code to single concern). With this we will have a first intermediate version of the code, still in AngularJS, that we can easily compare with the old code. When everything works as before, we will migrate the first intermediate version in AngularJS to a second intermediate version in VueJS. It is very important that in this phase we reduce the changes to a minimum (for example, without also changing the names of things, however much it seems necessary), in order to be able to compare as easily as possible with the old code. When everything works as before, we can get down to work with refactoring and rewriting the code.

Important: we can carry out the migration in parts, starting with Extract components and services included, that is, considering first a controller, then another, then a whole application, taking advantage of the fact that AngularJS and VueJS can coexist without problems, as demonstrated by the tutorial.

Fix how to use RxJS

While working at REDACTED, I reported the following issue.

There is a little bug in our Core module which causes no harm at run time but is nonetheless very annoying because it defeats the purpose of using TypeScript instead of JavaScript, which is type matching. Given that the code editors we use support completion suggestions based on type declarations and type inference, it’s a real pity to loose those benefits because of that mosquito.

All of our access to Back end endpoints passes through copy-paste-adapted TypeScript methods whose return type is always set to something like this:

someEndpoint(params: ...): Observable<Response | OperationFailure> {
    // ...
}

while it should be

someEndpoint(params: ...): Observable<Response> {
    // ...
}

In fact, the omitted body of someEndpoint properly converts an HTTP response into an object of type Observable, but a success response is transformed into a Response type object and that is returned on the next channel of the observable, while a failure response is transformed into an OperationFailure type object and that is returned on the error channel of the observable.

Given that the ReactiveX code we use is programmed like this:

rxjs/src/internal/Observable.ts

export class Observable<T> implements Subscribable<T> {

rxjs/src/internal/types.ts

export interface Subscribable<T> {
  subscribe(observerOrNext?: PartialObserver<T> | ((value: T) => void),
            error?: (error: any) => void,
            complete?: () => void): Unsubscribable;
}

the expression Observable<T> means Observable whose ‘next’ values’ type is T. Hence, the return type of someEndpoint should be Observable<Response> instead of Observable<Response | OperationFailure>.

This fix would allow not only to improve code completion when programming services for accessing endpoints, but it would also avoid having to either cast values to their intended type or use values of type any just to keep the TypeScript compiler happy. Given that using values of type any is easier than casting them to their intended type, we now have mostly unchecked responses.

Speed up copy and paste programming

While working at REDACTED, I reported the following issue.

It’s perfectly valid to program by copy-pasting code from one place to another. This looks like a bold statement, but it’s sometimes very difficult to come up with a cleaner alternative. Of course I’m not talking about breaking the DRY rule. For example,

if (x && y && !z) {
    // ...
}
// later
if (x && y && !z) {
    // ...
}
// later
if (x && y && !z) {
    // ...
}

is a break of the DRY rule, and each programmer should replace that with

const condition = () => x && y && !z; // a function guarantees re-evaluation
if (condition()) {
    // ...
}
// later
if (condition()) {
    // ...
}
// later
if (condition()) {
    // ...
}

What I’m talking about is using a block of many lines of code as a template for generating a similar functionality at another place in the application. For example, I could have already programmed some page using two files: some-page.html and some-page.ts. Later on, when I need to program another-page, which is very similar to some-page, I can copy-and-paste the latter and apply the little changes needed to get the former.

This practice is very popular all around the world and there is no shame in doing it. And nothing stops you from writing a real code generator. However, copy-paste-adapt is all you need if you keep adapt at a minimum. How do you do so? Dead easy: refrain from using specific identifiers in code or move them from the code domain to the data domain.

For example, better names for that pair of files would be some/page.html and some/page.ts. This would allow me to copy the files in the some directory to the new another directory to immediately get another/page.html and another/page.ts files which preserve the structure without requiring any adaptation (like renaming).