Nasza strona używa cookies. Dowiedz się więcej o celu ich używania i zmianie ustawień w przeglądarce. Korzystając ze strony, wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki. Rozumiem

Jak zapewnić jakość kodu w projektach JavaScript

Mateusz Dobrowolski Frontend Developer / Globe Group S.A.
Poznaj procesy i narzędzia, które pomogą Ci zadbać o dobrą jakość kodu w JavaScript.
Jak zapewnić jakość kodu w projektach JavaScript

Javascript jest w 2019 roku wciąż zdecydowanie najbardziej popularnym językiem programowania. Jako skryptowy język z dynamicznym typowaniem pozwala on programistom na wiele. Ogromna swoboda ma jednak swoje konsekwencje. Tworząc projekt bez narzuconych standardów łatwo stworzyć bałagan, a rosnący z czasem chaos w kodzie skutecznie obniża jakość całego projektu i przyjemność płynącą z jego dalszego rozwijania.

W poniższym artykule przedstawione zostaną proste i skuteczne sposoby na wprowadzenie porządku w javascriptowych projektach (frontendowych i nie tylko). Ich wdrożenie możliwe jest w nowych, jak i już istniejących projektach. Proces ten można wprowadzać etapami, lub też od razu w pełni korzystać z możliwości dostępnych dziś na rynku narzędzi.


Procesy zapewniające jakość

W trakcie wytwarzania oprogramowania można wyróżnić co najmniej kilka procesów, które pomogą nam zadbać o jakość kodu (rozumianą tutaj jako m.in. spójność w zakresie nazewnictwa, formatowania, pełną zgodność ze składnią języka i jego standardami). Zysk płynący z wdrożenia takich rozwiązań to m.in::

  • wyłapanie wielu drobnych błędów w kodzie na wczesnym etapie (znaną prawdą jest, że im później błędy są znalezione, tym większy jest koszt ich usunięcia),
  • wyeliminowanie występowania konfliktów wywołanych przez narzędzia autoformatujące kod,
  • możliwość wyeliminowania przestarzałej składni kodu na rzecz najnowszych standardów,
  • utrzymanie jednolitego standardu programowania w projekcie (lub wielu projektach) przy których pracują nawet setki programistów, 
  • skrócenie czasu wykonywania code review (jak wspomniano część błędów jest wyłapywana na wcześniejszym etapie),


Pośrednio niesie to ze sobą również korzyści w warstwie biznesowej. Łatwiej jest “trzymać projekt w ryzach”, koszty rozbudowy i utrzymania maleją, maleje też prawdopodobieństwo doprowadzenia do sytuacji, w której, ze względu na trudność nowego zespołu w odnalezieniu się w starym kodzie koniecznym stanie się przepisanie całej aplikacji.

Dobra wiadomość jest taka, że powyższe profity jesteśmy w stanie zapewnić wprowadzając do naszych projektów narzędzia, które w zautomatyzowany sposób wesprą naszą codzienną pracę. Poniżej wymienione są 3 rzeczy, które, o ile jeszcze oczywiście już z nich nie korzystamy, warto wprowadzić w naszych projektach:

  1. Korzystanie z wtyczek do edytorów kodu zajmujących się formatowaniem i statyczną analizą kodu (wraz z umieszczeniem dla nich plików konfiguracyjnych wewnątrz repozytorium projektu)
  2. Wprowadzenie narzędzi CLI, które automatycznie zweryfikują/poprawią kod przed wysłaniem go do zdalnego repozytorium.
  3. Wprowadzenie procesu CI (ciągła integracja), który zweryfikuje kod po wysłaniu go do zdalnego repozytorium.


Prettier - automatyczne formatowanie kodu

Taby czy spacje, klamra w tej samej, czy nowej linii, średnik, czy brak średnika, przejście do nowej linii, czy dodanie kodu do tej samej linii? Zapomnij o tych problemach i skup się na rozwiązywaniu poważnych programistycznych problemów. Prettier Ci w tym pomoże.

To przykład narzędzia, które (na podstawie zdefiniowanych reguł) automatycznie formatuje kod (HTML,CSS, SCSS, JSON, TypeScript, JSX itd.). Funkcjonuje zarówno jako wtyczka do wszystkich popularnych IDE oraz jako narzędzie uruchamiane za pomocą komendy z wiersza poleceń (przykładowa konfiguracja znajduje się w dalszej części artykuły).

Ustalone w ramach zespołu reguły (plik .prettierrc) umieszczamy w repozytorium projektu, przez co kod formatowany jest w ten sam sposób u wszystkich programistów w zespole.

Zalecane jest, by skonfigurować swoje IDE w taki sposób, by formatowanie pliku odbywało się automatyczne przy każdym zapisie (np. w Visual Studio Code jest to opcja Format On Save). Dla już istniejących projektów istnieje możliwość przeformatowania wszystkich plików (nie tylko tych aktualnie edytowanych).

Pomocne linki:

Prettier
NPM prettier 
NPM pretty-quick 
Visual Studio Code prettier plugin 


Linter - statyczna analiza kodu

Statyczna analiza kodu to automatyczny proces badania struktury kodu źródłowego (np. wykrywanie nieużywanego kodu, analiza poprawności składni, weryfikacja norm nazewnictwa, zgodności z dobrymi praktykami).

W przypadku Javascriptu Linter jest w stanie zaproponować nam na przykład skorzystanie z const zamiast let, usunięcie zadeklarowanych, ale niewykorzystywanych zmiennych, wymusić zastosowanie wyłącznie silnych operatorów porównania (np. === zamiast ==).

Wartą uwagi funkcjonalnością Lintera jest to, że może on również służyć do automatycznego naprawiania wykrytych błędów (dotyczy tylko najprostszych błędów, których sposób naprawy jest oczywisty z punktu widzenia narzędzia).

Przykładowe Lintery dla poszczególnych języków:

Javascript:

ESLint jest przykładowym statycznym analizatorem kodu Javascript. Reguły do sprawdzania przez lintera deklarujemy (domyślnie) w pliku .eslintrc.json.

ESLint 
Visual Studio Code eslint plugin 
NPM eslint

TypeScript:

Dla TypeScriptu warto skorzystać z TSLint. Wprowadza on szereg dodatkowych reguł uwzględniających składnię TSa. Reguły domyślnie w pliku tslint.json

TSlint 
Visual Studio Code tslint plugin 
NPM tslint

CSS/SCSS:

Przed statyczną analizą nie ukryją się nawet nasze pliki ze stylami! Reguły domyślnie w pliku .stylelintrc.json

Stylelint 
Visual Studio Code stylelint plugin 
NPM stylelint 
NPM stylelint-scss 

Wszystkie powyższe Lintery są w stanie (jako pluginy) podpowiadać nam w czasie rzeczywistym wykryte błędy już podczas pisania kodu. Uruchomione jako komendy z poziomu terminala (przykładowa konfiguracja znajduje się w dalszej części artykułu) są w stanie weryfikować poprawność plików oraz automatycznie naprawić część wykrytych nieprawidłowości (a o reszcie odpowiednio poinformować programistę).


Lokalna automatyzacja dla zapominalskich

Nawet najznakomitsze wtyczki i najlepiej zdefiniowane reguły dla autoformatowania i statycznej analizy kodu nie uchronią żadnego projektu przed tym, że ktoś zignoruje błędy wskazane przez Lintera lub nie uruchomi formatowania kodu z poziomu wiersza poleceń.

Na szczęście to nic straconego! Wyobraź sobie, że można skonfigurować te narzędzia w taki sposób, by wykonywały swoją pracę automatycznie przed każdym commitem. How cool is that?

Z pomocą przybiega nam Husky. Przytaczając jedne z pierwszych słów z jego README:

“Husky can prevent bad git commit, git push and more 🐶 woof!”

Husky to narzędzie, które pozwala nam w łatwy sposób zdefiniować skrypty, które mają uruchamiać się w przypadku konkretnych git’owych akcji (tzw. git hooks). Tym samym możemy automatycznie uruchomić autoformatowanie i statyczną analizę kodu przed każdym commitem. Co więcej, w przypadku, gdy Linter nie będzie w stanie sam naprawić wszystkich błędów commit zostanie przerwany, a programista będzie zmuszony poprawić wszystkie wykryte błędy.

NPM husky

Przykład pełnej konfiguracji Prettiera, ESLinta, Stylelinta oraz Husky’iego: 

.stylelintrc

{
  "extends": "stylelint-config-standard"
}


.eslintrc

{
  "extends": "standard"
}


package.json

{
  ...
  "scripts": {
    "format:check": "pretty-quick --check",
    "format:fix": "pretty-quick",
    "linter:js:check": "eslint src/**/*.js",
    "linter:js:fix": "eslint src/**/*.js --fix",
    "linter:css:check": "stylelint src/**/*.css",
    "linter:css:fix": "stylelint src/**/*.css --fix"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged && pretty-quick --staged"
    }
  },
  "lint-staged": {
    "src/**/*.js": [
      "eslint --fix",
      "git add"
    ],
    "src/**/*.css": [
      "stylelint --fix",
      "git add"
    ]
  },
  ...
  "devDependencies": {
    "eslint": "~6.6.0",
    "eslint-config-standard": "~14.1.0",
    "eslint-plugin-import": "~2.18.2",
    "eslint-plugin-node": "~10.0.0",
    "eslint-plugin-promise": "~4.2.1",
    "eslint-plugin-standard": "~4.0.1",
    "husky": "~3.1.0",
    "lint-staged": "~9.4.3",
    "prettier": "~1.19.1",
    "pretty-quick": "~2.0.1",
    "stylelint": "~12.0.0",
    "stylelint-config-standard": "~19.0.0"
  }
}


Ciągła integracja dla świętego spokoju

Z powyższymi narzędziami komfort pracy wyraźnie wzrasta (co może być wystarczające dla sporej części projektów), ale wciąż istnieją pewne sposoby na obejście naszych automatycznych mechanizmów (np. tymczasowa zmiana skryptów w package.json tuż przed commitem, albo edycja plików bezpośrednio z poziomu gitlaba/githuba itd.).

Chcąc być w 100% pewnymi, że nasz kod jest w pełni zgodny z zadeklarowanymi przez nas regułami dla formatowania i statycznej analizy kodu należy skonfigurować proces ciągłej integracji (ang. continuous integration. w skrócie CI), w ramach którego wszystkie te kroki będą weryfikowane również po stronie serwera.

Implementacja takiego rozwiązania opiera się na wykorzystaniu tych samych skryptów, które uruchamiane są za pomocą Husky’iego przed każdym commitem. Ciągła integracja sama w sobie jest jednak obszernym tematem, który wykracza daleko poza ramy tego artykułu.


Na zakończenie

Powyższe sposoby zdecydowanie polepszają jakość kodu. Zwiększają jego poprawność i pozwalają dużo szybciej wyłapać proste błędy. Należy jednak zwrócić uwagę na to, że dotyczy to głównie kwestii jakości kodu na poziomie pojedynczych linijek (głównie ich formatowania i składni). Zwiększamy prawdopodobieństwo, że nasz program wykonuje “coś” w poprawny sposób. Do weryfikacji tego “co” wykonuje nasz program służą m.in. testy, które również są tematem na osobny artykuł (lub serię artykułów, a nawet książkę, czy też całą serię książek).

Lubisz dzielić się wiedzą i chcesz zostać autorem?

Podziel się wiedzą z 130 tysiącami naszych czytelników

Dowiedz się więcej