Abandon tailored flows and adopt reusable flows

While working at REDACTED, I reported the following issue.

Our apps are based on Flow classes that define which pages they manage and how those pages are interconnected according to pressed buttons and input data.

Unfortunately,

  • it’s needlessly complicated, basically due to a misunderstanding of how a highly configurable system is usually implemented nowadays;
  • it’s a layer on top of Angular, but it’s not Angular, which means that an expert Angular programmer must learn it before being productive;

Additional drawbacks of our flow implementation:

  • They are meant to reuse pages but then, for example, a page which is at the same time used for creating and editing a resource is plagued by continuous branching to account for all the little differences between creating a resource and editing it afterwards. (what about components inheritance?)
  • They are meant to reuse pages but not flows themselves, so, given that a flow is associated to a set of URLs which change from creating to editing (continuing with the example above), those two practically identical flows for creating and editng must be represented by two separate classes.

These flows force us to

  1. show page 1 to the user and wait for her to press a navigation button,
  2. after she presses a navigation button, move captured data from page 1 to its flow,
  3. make the flow decide that page 2 is the page following page 1, according to the current state,
  4. move those captured data (and possibly some more) from the flow to page 2,
  5. show page 2 to the user and wait for her to press a navigation button,

which means that these flows force us to write lots of code which is hard to follow, because it’s spread in two different classes and the logic used to determine the next page strongly depends on the state of the current page but the configuration of that state is much more richly documented by the page class (that defines it) than the flow class (that uses it).

Additionally, many flows of ours are fake flows because they only are a means to capture data of complex entities with a very big form split into many pages, connected to one another in a linear fashion. The user can only move back and forth, from start to end. And the only exceptions to linearity are conditional skips.

We could just use a store to preserve state between pages and then

  1. tell pages that they belong to the sequence: page 1, page 2, …
  2. show page 1 to the user and wait for her to press the back or next button,
  3. show page 2 to the user and wait for her to press the back or next button,

While fake flows are justly tailored and not reusable, there are also a few flows like the confirmation flows that are real and should be addressed with reusable flows. In our app, confirmation flows work like this:

  1. On an ORIGIN page, the user selects some documents to act upon, e.g. to delete them.
  2. After the user presses the Delete button, the confirmation flow starts.
    1. The confirmation page opens and lists the selected documents.
    2. The user can unselect some of them. (to not delete them anymore)
    3. The user can open and read some of them. (to make sure she really wants to delete them)
    4. The user can cancel the operation altogether, and get back to the ORIGIN page.
    5. The user can accept the operation on the remaining documents.
    6. If they could all be deleted, the ORIGIN page will show a success message.
    7. If they couldn’t all be deleted, the ORIGIN page will show a failure message.
    8. When the ORIGIN page is loaded again, all documents that the user had selected are still selected (if they appear in the list).

That is a small flow which could benefit from a reusable architecture. Instead of deleting, it could be about accepting or rejecting documents, copying them, archiving them, … whatever.

It could work like this:

originPage.deleteSelectedDocuments = () => {
    ConfirmationFlow.show({
        selected: originPage.getSelectedArticles(),
        onAccept: [Article, 'moveToTrash'],
    });
}

but our current implementation of flows doesn’t allow anything like that.

Automate as much as possible

While working at REDACTED, I reported the following issue.

There are so many places where automation can make you save time and mistakes. One, the first I found at REDACTED, was related to how the application architecture was set up.

The application I worked on used a couple of separate modules, also made by us: Core and UI Components.

  • Core collected in one place all the app’s services. (in Angular terms, i.e. classes/methods to access all the endpoints of the Back end)
  • UI Components collected in one place all the app’s UI elements, like easier to use tables and inputs.

While it made sense to separate UI elements (because they changed slowly, there was a special team dedicated to maintaining them, they could be used by other apps, …), it was a mistake to separate the Back end library. (because it changed very frequently, the same programmer making a page had to change it too, they could only be used by the same app, …)

On a daily basis, after adding a new endpoint or changing a previous one in your custom branch, you had to

  1. compile the Core module
  2. remove the currently used Core module from the app
  3. add the just built Core module to the app
  4. compile and run the server of the app

Did I mention you had to do those steps manually? But there was a twist, too. If you wanted to add a new module from the Internet, the standard command $ npm install --save <module> had been hooked into by our team leader to automatically build and install again both the Core and the UI Components modules. Neat, right?

Unfortunately though, his cool thing did build and install the development branch of the Core module, not your custom branch!! In the end, each time the collection of third party modules changed (a change introduced by you or others) you had to run the $ npm install command again to install all the modules of the collection but you also had to remember the twist and manually run the steps above again afterwards. In fact, the command replaced your perfectly good, previously compiled, custom branch of the Core module with its unrequested, freshly compiled, development branch, and you had to undo that.

Why did he program that that way? I guess because that suited his own needs as a team leader. In fact, probably to test if additional internet modules would work fine in the app, he had to be sure to install them into the most up-to-date version of the app, so he needed to install the development branches of the Core and UI Components modules.

There was absolutely no need to keep the steps above manual so, two weeks after being hired and many times after forgetting one of the steps or the twist and wasting time to understand why suddenly the app didn’t work anymore, I wrote a very simple script to run all the right steps for me.

It worked very nicely but it wasn’t cool enough according to the team leader standards. It faced his frontal opposition immediately, and I was forced not to share my improvement with my colleagues.

Shorten the time needed to reload context

While working at reported, I reported the following issue.

Javascript is a privileged language because its programmers can immediately see in a browser how a Javascript program works. Thus the interaction with the browser must be as fast as possible for a Front end engineer to be productive.

Instead, in the 15 people Front end team I was assigned to, this interaction was very slow and cumbersome.

  • When I joinded the team, each reload of the page they were working on forced them to close the browser’s currently loaded tab, open a new empty tab, go to the login page of the app, identify with their credentials, navigate through many intermediate pages, and eventually load again the page they were working on.
  • Some hours after I voiced my surprise that we had to program that way, the team leader came up with an automatic solution, where the running environment would detect a file change and reload that page only. Notice that he decided to make the reload automatic because he knew how to, not because it was needed (zero effort at doing it manually by pressing the F5 key).
  • However this cool thing never worked very well, because his implementation of HMR (Hot Module Reload) always left the navigation links broken, making it impossible to load a different page after the current one had been reloaded. Thus, in the end, a reload from login is still used a lot.
    • How much money the company wastes is left as an exercise. Consider 1 minute per reload from login, performed 100 times a day by a team of 15 programmers.

Why is the Front end team forced to program like that? Essentially because of the inferiority complex of many designers of that configuration. Instead of building what their team needed, they built what they needed to show how good they were at making cool things. (like optimizing the JavaScript transpiled from TypeScript before showing the application in the browser, which is only useful in production, not in development)