Jak przeszliśmy od Java do Javascript

Nokia to nie tylko BTSy i C++. Tworzymy również oprogramowanie, które usprawnia naszą wewnętrzną pracę i wspomaga nas w automatyzacji i wykonywaniu codziennych zadań. W dziale Big Data & Network Engineering budujemy narzędzia, które pomagają przetwarzać i analizować duże ilości danych telekomunikacyjnych.

Etap 1: Offline

Kiedy dołączyłem do naszego działu zajmowaliśmy się kilkoma narzędziami, które tworzone były jako aplikacje desktopowe. Pracowaliśmy nad projektem pisanym w Javie (rozwijanym do dzisiaj), który używany jest podczas analizy pracy sieci oraz planowania nowych stacji bazowych.

Aplikacja desktopowa jest oczywiście instalowana na komputerze użytkownika i dzięki temu można ją uruchomić, nawet kiedy nie ma on dostępu do Internetu. Problem pojawia się, kiedy aplikacja jest intensywnie rozwijana i chcemy, żeby wszyscy użytkownicy zawsze korzystali z najnowszej wersji. Dzień, w którym wychodziła nowa wersja naszej aplikacji i okres, który poprzedzał release był najbardziej intensywnym i stresującym okresem - w momencie, kiedy przygotowaliśmy instalator i wysłaliśmy wiadomość do użytkowników o nowej wersji wszystko musiało być dopięte na ostatni guzik. Kiedy użytkownicy pobiorą instalator nie będzie już odwrotu i możliwości wysłania im poprawek.

Kolejną bolączką było tworzenie GUI. Stworzenie spójnego interfejsu wymagało od nas napisania wielu abstrakcyjnych komponentów, co było bardzo czasochłonne. Interfejs aplikacji desktopowej jest też ograniczony kontrolkami, które dostępne są w danym systemie operacyjnym. Nasze GUI było po prostu brzydkie i nie było mowy o wprowadzeniu żadnych animacji czy wizualnych efektów.

Z tych powodów zaczęliśmy szukać alternatywnego podejścia do tworzenia naszych narzędzi.

Etap 2: Going Online

Pierwszym podejściem do rozwiązania tych problemów była próba stworzenia aplikacji webowej, a właściwie strony internetowej. Idea jest prosta - zamiast uruchamiać aplikację na komputerze użytkownika, uruchamiamy ją na naszym serwerze, a użytkownikowi dajemy tylko adres, pod którym może ją znaleźć. Dzięki takiemu podejściu to my kontrolujemy wersję aplikacji, z której korzystają wszyscy użytkownicy, ale niestety dostęp do Internetu (czy przynajmniej tej samej sieci, w której znajduje się serwer) staje się niezbędny.

Na tym etapie nie wiedzieliśmy prawie nic o pisaniu aplikacji internetowych. Kilku z nas miało pewne doświadczenie w tworzeniu stron z wykorzystaniem HTML i jQuery, ale na tym nasza wiedza się kończyła.

Pierwszy prototyp stworzyliśmy korzystając z PHP w oparciu o framework Symfony. Najważniejszą zaletą aplikacji było to, że nie musieliśmy już prosić użytkowników o instalację programu (lub uaktualnienia) i generowanie licencji - wystarczyło wysłać im link i od razu mogli z niej korzystać!

Korzystanie z technologi webowych pozwoliło nam także na szybsze tworzenie funkcjonalnego interfejsu. Stworzenie GUI było też o wiele prostsze niż w Javie i mogliśmy wykorzystać otwarte biblioteki oraz zasoby, których w Internecie jest dużo więcej dla technologi webowych niż dla Javy.

Sposoby tworzenia interfejsu również zmieniały się wraz z zagłębianiem sie w to, co możliwe jest do zrobienia z użyciem HTML. Rozpoczynaliśmy od stylowania komponentów za pomocą CSS i problemów z nazewnictwem klas, specyficznością i kompatybilnością między przeglądarkami. Bardzo szybko zaczęliśmy korzystać z preprocesorów _CSS (np. LESS), a ostatecznie użyliśmy frameworku Twitter Bootstrap, który zaadaptowaliśmy do naszych potrzeb. Dzięki takiemu podejściu mogliśmy łatwo tworzyć spójny interfejs i bardzo szybko prototypować różne układy strony. Większość z nas jest typowymi programistami, a nie designerami, więc pozwala nam to skupić się na tworzeniu logiki naszej aplikacji, która przy okazji wygląda całkiem nieźle.

Aplikacja w postaci strony internetowej ma oczywiście swoje wady. Każda akcja użytkownika na naszej stronie była wysyłana do serwera, który generował nowy kod do wyświetlenia użytkownikowi. Powodowało to spore opóźnienia, ponieważ za każdym razem nasza strona musiała się odświeżyć. Użytkownicy oczekiwali od naszego narzędzia dużo więcej - trzeba było coś z tym zrobić.

Etap 3: JavaScript

Jedną z niewielu dostępnych opcji wprowadzenia dynamicznej zawartości na stronę jest użycie JavaScriptu. Kojarzył się nam wtedy z prostym językiem skryptowym, który nie nadaje się do tworzenia bardziej zaawansowanych aplikacji, a pisanie w nim jest największą karą, która może spotkać “prawdziwych programistów”.

Początkowo traktowaliśmy wstawki JS jako dodatek do statycznie wygenerowanej strony, które wprowadzały dynamiczne zachowanie w niektórych miejscach aplikacji: proste obliczenia, walidację itd. W miarę zwiększania się ilości kodu pisanego w JS, zaczęliśmy szukać informacji o tym jak pisać kod dobrze i jak o niego dbać. Coraz bardziej przekonywaliśmy się do JavaScriptu jako “prawdziwego” języka programowania.

Z obowiązkowych narzędzi każdego programisty JS należy tutaj wymienić JSLint (lub JSHint), który wykonuje statyczną analizę kodu, znajdując najczęściej popełniane błędy i pozwala także kontrolować, czy zachowane są reguły, według których formatujemy nasz kod. Warto skorzystać też z biblioteki lodash (lub underscore), zawierającej wiele pomocniczych funkcji.

Dynamiczne elementy strony wymagane były coraz częściej, a nasza wiedza na temat JS cały czas rosła. Wtedy padła szalona propozycja: “Zróbmy SPA!”.

Etap 4: SPA

Idea Single Page Application (SPA) polega na tym, że użytkownik ładuje początkową stronę, która zawiera całą aplikację. Kolejne odwołania do serwera robione są nie po to, aby wygenerować kolejny widok, ale w celu pobrania od serwera wyłącznie danych. Cała logika ładowana jest w przeglądarce użytkownika i pisana w języku JavaScript!

Pisanie SPA wymaga zupełnie innego podejścia niż pisanie zwykłej strony internetowej. Rozdzielamy naszą aplikacje na dwie części: pierwsza działa na naszym serwerze i wysyła dane (back end), natomiast druga to widok i logika aplikacji (front end). Back end odpowiedzialny jest za komunikację z bazą danych, autoryzację i ekspozycję danych, najczęściej w formie RESTowego API. Jako format wymiany danych używany jest JSON, który jest łatwo parsowalny po stronie klienta za pomocą JS. Frontend zawiera wszystko to, co widzi użytkownik i jest odpowiedzialny za obsługę interakcji z nim.

Przygotowania rozpoczęliśmy od wyboru frameworku JavaScriptowego, na którym miała być oparta aplikacja. Nasz pierwszy wybór padł na Backbone.js, którego krzywa wejścia jest dość łagodna (łatwo rozpocząć w nim pisać, jeżeli zna się trochę JS), a dzięki podziałowi na Model, Widok i Kontroler wymusza dobrą strukturę aplikacji. Dodatkowo, w celu rozwiązania problemu paczkowania wielu plików JS po stronie klienta, skorzystaliśmy z RequireJS, który dba o ładowanie wymaganych modułów. Przez wprowadzenie podziału na front end i back end nasz kod JavaScriptowy stał się de facto niezależną aplikacją, która podlega takim samym procesom projektowym jak np. kod w Javie.

Tworzenie wielu plików JS wymagało od nas także wprowadzenia dodatkowego kroku poprzedzającego deploy nowego kodu na produkcję. Uruchomienie naszej aplikacji wymaga od przeglądarki użytkownika pobrania wszystkich plików JS z serwera. Z tego powodu ładowanie strony trwałoby bardzo długo (przeglądarki mają limit równolegle ściąganych plików z danej domeny). Przed uruchomieniem aplikacji na serwerze produkcyjnym łączymy wszystkie pliki JS w jeden, który następnie minimalizujemy, korzystając z narzędzi takich, jak UglifyJS czy Closure Compiler.

W celu automatyzacji procesu budowania aplikacji używamy Grunta - narzędzia, które rozszerzone o odpowiednie pluginy, pozwala na uruchamianie wszystkich zadań związanych z przygotowaniem aplikacji do przesłania na serwer produkcyjny. Grunt generuje raport z JSLinta i uruchamia testy, natomiast UglifyJS tworzy pojedynczy plik zawierający kod całej aplikacji.

Dodatkowo, tak jak podczas pisania aplikacji desktopowych, nasz kod po trafieniu do repozytorium pobierany jest przez Jenkinsa - serwer Continuous Integration. Jenkins uruchamia Grunta i przygotowuje najnowszą wersję aplikacji, która jest zawsze gotowa do testowania.

Proces tworzenia aplikacji webowych jest tak samo wymagający, jak proces tworzenia programów innego typu (desktop, mobile, serwer). W przypadku SPA dodatkowy problem może stanowić sam język JS. Pomimo tego, że łatwo zacząć w nim pisać, to jego opanowanie jest nie lada wyzwaniem.

Etap 5: Learning

W naszym przypadku, zarówno jako metodę uczenia nowych osób dołączających do projektu, jak i doskonalenia oraz rozwijania się pozostałych członków zespołu, świetnie sprawdziło się Code Review. Każda zmiana w kodzie była analizowana przez co najmniej 3 dodatkowe osoby, których zadaniem nie było wytykanie błędów czy niewiedzy, ale wspólna nauka i dbanie o dobrą jakość kodu całego projektu.

Podczas Code Review wymieniamy się i dyskutujemy nad sposobami rozwiązania danego problemu i poznajemy nowe punkty widzenia. Obecnie nie wyobrażam sobie pracy w projekcie, w którym Code Review nie jest stosowane.

Kolejnym ważnym elementem pisania w JS (a właściwie we wszystkich językach dynamicznych) jest testowanie. W związku z tym, że kod jest interpretowany, najbardziej trywialny błąd może wyjść na jaw dopiero w momencie, gdy faktycznie ten fragment zostanie uruchomiony! Dzięki testom jednostkowym możemy zweryfikować, czy nasze poszczególne funkcje robią dokładnie to, czego chcemy. Duża liczba testów daje nam też pewność, że dodając nowy kod lub refaktorując istniejący, niczego nie zepsuliśmy. Praktyka pisania testów oraz metodyka TDD pozwalają także poprawić jakość i architekturę kodu.

W przypadku JS ciągłe doskonalenie się jest niezwykle ważne. Całe środowisko bardzo dynamicznie się zmienia, co chwila powstają nowe biblioteki i rozwiązania, a przeglądarki zyskują kolejne możliwości w postaci API dostępnych z poziomu JSa.
Do naszego procesu oprócz Continuous Integration, Continuous Deployment i Continuous Improvement dołączyliśmy także Continuous Learning.

? + NoSQL + Node.js + AngularJS

Obecnie wszystkie projekty, które rozpoczynamy, tworzone są z front endem pisanym w AngularJS, który pozwolił nam znacznie przyspieszyć i uprzyjemnić pracę nad aplikacjami SPA. Bardzo dobra znajomość JavaScriptu pozwala nam pisać programy, które nie tylko działają w przeglądarce, ponieważ JS jest językiem obecnie dostępnym już wszędzie. JS używamy również po stronie serwera, korzystając z node.js. W ramach Continuous Learning organizujemy jednodniowe Hackathony, podczas których mieliśmy okazję spróbować pisania aplikacji mobilnych w JS (PhoneGap, Ionic) lub innych technologii (Go). Pierwszy serwer w node.js rownież powstał podczas jednego z Hackathonów - wszyscy w ekspresowym tempie mieli okazję poznać dobre i złe strony tej technologii.

Przejście od pisania desktopowych aplikacji do tworzenia SPA w AngularJS nie było dla nas proste. Podczas tej drogi wiele razy musieliśmy wyjść poza naszą strefę komfortu i pokonać wiele przeszkód. Ale czy właśnie nie na tym polega bycie progamistą? Zdecydowanie było warto!