Blog

A tale of the last 10 years in web development

A tale of the last 10 years in web development

August 28, 2018
Blog Hero Image
This is a work of fiction. Names, characters, businesses, places, events, locales, and incidents depicted in this story are either the products of my imagination or used in a fictitious manner. Any resemblance to actual persons, living or dead, or actual events is purely coincidental.

‍

An Introduction

This story is based on a true story of each of us. Over the past 10 years, many web developers have experienced such a journey. Starting from jQuery, passing through Angular, using React and streak Vue.

‍

When reading, “darker areas” of the beginnings of single page applications (SPA) are undergone. This can cause unwanted nervous muscle twitches from the past.

‍

For risks and side effects read the package leaflet (there is none) and consult your doctor or pharmacist (I don’t know if they could understand).

‍

Once upon a time, in spring 2008 in a small company in Sheffield, there was a software developer called Dorian. He was working on a desktop application built with Microsoft .NET.

‍
Not far from there, in Rotherham, Shaun worked in a similar company as web developer.

‍
Both companies were very similar and sometimes even had the same customers. So it happened that the top management of both companies met and they merged.

‍
Patty, who had been promoted to development manager, had the task to assemble a new web development team. After she had already inducted Shaun into her team, she also found Dorian and noticed a possible team change.

‍
She beat him an offer and Dorian joined straight the team in Rotherham.

‍
For Dorian, professional development with Javascript was something new. He’d learned Javascript during his studies, but he’d used it rather than animate a website.

‍
Shaun, on the other hand, even showed him how to handle node.js and linux and mac. Dorian had always only worked with Windows so far. And from Javascript in the backend he was quite astonished and surprised.
Patty saw the first weeks relaxed and felt that she was uniting these two, the right thing.

Soon it was time to productively implement a new web application. Together, they decided to try to use node.js in the backend and jQuery in the frontend.

‍
They did choose jQuery because the support of the community was great, it made DOM manipulation painless, played well with AJAX, made basic animation a piece of cake, had a lot of plug-ins, etc


‍
Since the web application was not only for English speaking users, they used jquery-i18next as an internationalization (i18n) library.

‍

1<html>
2  <head>
3    <meta charset="utf-8">
4    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
5    <meta name="theme-color" content="#000000">
6    <title>jQuery App</title>
7    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js" ></script>
8    <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next/11.6.0/i18next.min.js" ></script>
9    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-i18next/1.2.1/jquery-i18next.min.js" ></script>
10    <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next-browser-languagedetector/2.2.3/i18nextBrowserLanguageDetector.min.js"></script>
11    <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next-xhr-backend/1.5.1/i18nextXHRBackend.min.js"></script>
12  </head>
13
14  <body>
15
16    <div id="root">
17      <div class="App">
18        <div class="App-header">
19          <img src="https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/spaces%2F-L9iS6Wm2hynS5H9Gj7j%2Favatar.png?generation=1523462254548780&alt=media" class="App-logo" alt="logo">
20          <h2 data-i18n="title" data-i18n="title" data-i18n-options='{ "tech": "jquery", "lib": "jquery-i18next" }'>Welcome to jquery using jquery-i18next</h2>
21        </div>
22        <button onclick="javascript: window.location.href = '?lng=de'">de</button>
23        <button onclick="javascript: window.location.href = '?lng=en'">en</button>
24        <div class="App-intro" data-i18n="description">Switch language between english and german using buttons above.</div>
25      </div>
26    </div>
27
28    <script>
29      i18next
30        .use(i18nextXHRBackend)
31        .use(i18nextBrowserLanguageDetector)
32        .init({
33          fallbackLng: 'en',
34          backend: {
35            loadPath: '../locales/{{lng}}/{{ns}}.json'
36          },
37          ns: ['translations'],
38          defaultNS: 'translations'
39        }, function(err, t) {
40          jqueryI18next.init(i18next, $, {
41            useOptionsAttr: true
42          });
43          $('body').localize();
44        }
45      );
46    </script>
47
48  </body>
49
50</html>

‍

To see how this could look like click here.

‍

After about 3 to 4 years, the first signs of weakness of the web application began.

‍
There were overusing big/clever plugins, had big/complex files and polluted the global namespace.

‍
The heavy use of long chains of selectors (“ul#leftnav li p a.current“) made the code brittle.

‍
They started to lose track of what’s where because of the neat idea to use .data() to attach data to the DOM elements, to track the page state.

‍
Everything started being slow


‍
Patty had new major features in the pipeline. But the team, which had problems to grow, recommended a refactoring phase.

‍
Quickly the team got support from 2 freelancers, Serge and Martina. Patty introduced the new ones as “the experts”.

‍
Serge and Martina had Angular know-how and persuaded everyone to replace the jQuery solution with Angular.

‍
Their arguments sounded promising:

‍
Instead of unobtrusive Javascript with selectors, now declarative templates.

‍
From semantic HTML, to semantic models. Instead of classic separation of concerns (HTML, CSS, JS), the usage of MVC pattern. No plug-ins but directives. $scope instead of closure.

‍
Instead of manual DOM manipulation and binding, modern data binding. Less “spaghetti” code and more dependency injection. From unorganized, to modular service architecture.

‍

Because i18next was not only built for jQuery, they could use ng-i18next and at least keep the same configuration and use the same localization files as before!

‍

1if (window.i18next) {
2	window.i18next
3		.use(window.i18nextXHRBackend)
4		.use(window.i18nextBrowserLanguageDetector)
5		.init({
6			debug: false,
7			fallbackLng: 'en',
8			backend: {
9				loadPath: '../locales/{{lng}}/{{ns}}.json'
10			},
11			ns: ['translations'],
12			defaultNS: 'translations'
13		}, function (err, t) {
14			console.log('resources loaded');
15		}
16	);
17}
18
19angular.module('MyApp', ['jm.i18next']).controller('MyDirectiveCtrl', function ($rootScope, $scope, $timeout, $i18next, $filter) {
20
21	'use strict';
22
23	$scope.i18nextReady = false;
24
25	$scope.$on('i18nextLanguageChange', function () {
26		console.log('Language has changed!');
27		if (!$scope.i18nextReady) {
28			$timeout(function () {
29				$scope.i18nextReady = true;
30			}, 500);
31		}
32	});
33
34	$scope.changeLng = function (lng) {
35		$i18next.changeLanguage(lng);
36	};
37
38});

‍

1<!doctype html>
2<html ng-app="MyApp">
3<head>
4  <meta charset="utf-8">
5  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
6  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7  <meta name="theme-color" content="#000000">
8  <title>Angular.js App</title>
9  <script src="https://code.angularjs.org/1.7.3/angular.min.js"></script>
10  <script src="https://code.angularjs.org/1.7.3/angular-sanitize.min.js"></script>
11  <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next/11.6.0/i18next.min.js" ></script>
12  <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next-xhr-backend/1.5.1/i18nextXHRBackend.min.js"></script>
13  <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next-browser-languagedetector/2.2.3/i18nextBrowserLanguageDetector.min.js"></script>
14  <script src="https://cdnjs.cloudflare.com/ajax/libs/ng-i18next/1.0.4/ng-i18next.min.js" ></script>
15  <script src="directive.js"></script>
16</head>
17
18<body>
19  <div ng-controller="MyDirectiveCtrl">
20    <div id="root">
21      <div class="App">
22        <div class="App-header">
23          <img src="https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/spaces%2F-L9iS6Wm2hynS5H9Gj7j%2Favatar.png?generation=1523462254548780&alt=media" class="App-logo" alt="logo">
24          <h2 ng-i18next="[i18next]({ 'tech': 'angular', 'lib': 'ng-i18next' })title">Welcome to angular using ng-i18next</h2>
25        </div>
26        <button ng-click="changeLng('de')">de</button>
27        <button ng-click="changeLng('en')">en</button>
28        <div class="App-intro" ng-i18next="description">Switch language between english and german using buttons above.</div>
29      </div>
30    </div>
31  </div>
32</body>
33</html>

‍

To see how this could look like click here.

‍

During the next 3 to 4 years, Patty added more freelancers and contractors to the team.
Shaun and Dorian started to recognize that making the simplest features work seems a struggle.
There were more and more performance and complexity issues.
Directives, services and filters theoretically were available, but ultimately, everything was built around controllers and their two-way bound $scope.
Angular seemed to be fine for the beginning, when it was a simple application, but as the frontend application grew in complexity, this led to the scope soup problem amongst other issues.
Additionally, the fear of Angular 2’s release was on the horizon. By trying some samples, Shaun said:

‍

“Is this still Angular?”

In the meantime, the backend had turned into a solid foundation based on DDD, CQRS and event sourcing.

‍
During a late-night beer, Dorian told Shaun about React and Redux. He said that when he read about React, Redux and FLUX he immediately felt that this was a natural fit to the existing backend.

‍
Finally, they convinced Patty to rewrite the whole frontend with these arguments:

‍

react
react

‍

  • Angular was a framework vs. React was a library
  • the more flexible state management with Redux
  • virtual DOM, one-way data flow, PropTypes and a well-defined component lifecycle
  • the obvious natural fit with their backend
  • single source of truth
  • JSX, a natural evolution of Javascript
  • React has faster learning curve. It feels like learning faster.

‍

As with the last technology change, this time there was an i18next option. Just used the new react-i18next library and still the same localization files!

‍

1import React, { Component } from 'react';
2import { translate } from 'react-i18next';
3import './App.css';
4
5class App extends Component {
6  render() {
7    const { t, i18n } = this.props;
8
9    const changeLanguage = (lng) => {
10      i18n.changeLanguage(lng);
11    }
12
13    return (
14      <div className="App">
15        <div className="App-header">
16          <img src='https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/spaces%2F-L9iS6Wm2hynS5H9Gj7j%2Favatar.png?generation=1523462254548780&alt=media' className="App-logo" alt="logo" />
17          <h2>{t('title', { tech: 'react', lib: 'react-i18next' })}</h2>
18        </div>
19        <button onClick={() => changeLanguage('de')}>de</button>
20        <button onClick={() => changeLanguage('en')}>en</button>
21        <div className="App-intro">
22          {t('description')}
23        </div>
24      </div>
25    );
26  }
27}
28
29export default translate('translations')(App);

‍

1import i18n from 'i18next';
2import Backend from 'i18next-xhr-backend';
3import LanguageDetector from 'i18next-browser-languagedetector';
4import { reactI18nextModule } from 'react-i18next';
5
6i18n
7  .use(Backend)
8  .use(LanguageDetector)
9  .use(reactI18nextModule)
10  .init({
11    fallbackLng: 'en',
12    backend: {
13      loadPath: '../../locales/{{lng}}/{{ns}}.json'
14    },
15    ns: ['translations'],
16    defaultNS: 'translations',
17
18    interpolation: {
19      escapeValue: false // not needed for react!!
20    },
21
22    react: {
23      wait: true
24    }
25  });
26
27export default i18n;

‍

1import React from 'react';
2import ReactDOM from 'react-dom';
3import App from './App';
4
5import './i18n';
6
7ReactDOM.render(<App />, document.getElementById('root'));

‍To see how this could look like click here.

‍

Around the year 2018, many freelancers and contractors were no longer in the team. Instead of them now younger developers started to ask for something new


“React is ok, but what’s about Vue?”

Shaun and Dorian organized a little technical session and they explained that React and Vue had more similarities than differences:

  • both were fast and lightweight
  • both had a component based architecture
  • both used a virtual DOM
  • both could be dropped into a single HTML file or be a module in a more sophisticated webpack setup
  • both had separate router and state management libraries

Long talk short, the session ended with the following sentence by Shaun:“Ok, let’s try to write our web app in Vue and create a PoC
”Whether they’ve really switched to Vue, we do not know, but what we know is that if that’s the case, then they’re sure to use vue-i18next or a similar library.

‍

1/* eslint-disable no-undef, new-cap */
2i18next
3  .use(i18nextXHRBackend)
4  .use(i18nextBrowserLanguageDetector)
5  .init({
6    fallbackLng: 'en',
7    backend: {
8      loadPath: '../locales/{{lng}}/{{ns}}.json'
9    },
10    ns: ['translations'],
11    defaultNS: 'translations'
12  }
13);
14
15const i18n = new VueI18next(i18next);
16
17Vue.component('app', {
18  template: `
19  <div id="root">
20    <div class="App">
21      <div class="App-header">
22        <img src="https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/spaces%2F-L9iS6Wm2hynS5H9Gj7j%2Favatar.png?generation=1523462254548780&alt=media" class="App-logo" alt="logo">
23        <h2>{{ $t("title", { "tech": "vue", "lib": "vue-i18next" }) }}</h2>
24      </div>
25      <button v-on:click="changeLanguage('de')">de</button>
26      <button v-on:click="changeLanguage('en')">en</button>
27      <div class="App-intro">{{ $t("description") }}</div>
28    </div>
29  </div>`,
30  methods: {
31    changeLanguage(lang) {
32      this.$i18n.i18next.changeLanguage(lang);
33    },
34  },
35});
36
37new Vue({
38  i18n,
39}).$mount('#app');

‍

1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="utf-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6    <meta name="theme-color" content="#000000">
7    <title>Vue.js App</title>
8    <script src="https://unpkg.com/vue@2.2.2/dist/vue.js"></script>
9    <script src="https://unpkg.com/i18next@11.6.0/i18next.js"></script>
10    <script src="https://panter.github.io/vue-i18next/dist/vue-i18next.js"></script>
11    <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next-browser-languagedetector/2.2.3/i18nextBrowserLanguageDetector.min.js"></script>
12    <script src="https://cdnjs.cloudflare.com/ajax/libs/i18next-xhr-backend/1.5.1/i18nextXHRBackend.min.js"></script>
13  </head>
14  <body>
15    <div id="app">
16      <app />
17    </div>
18    <script src="app.js"></script>
19  </body>
20</html>

‍

To see how this could look like click here.

‍

i18next was right:

“learn once — translate everywhere”!

Technologies and libraries come and go, but i18next remains!

‍

Post-credits scene

There are voices who say that they have also replaced their i18next-xhr-backend with that of locize.

To see how this could look like look at this video.

‍

‍

‍