Dlaczego React zwyciężył w obszarze frontendu, podczas gdy backend pozostał wielojęzyczny.
Dlaczego duże firmy łączą React z usługami w Java, Go i Python, a do warstw BFF używają Node.js, wraz z wskazówkami dla początkujących programistów na temat tego, czego powinni się nauczyć dalej.
Źródła: Why Your Favorite Apps Use React on the Frontend, but Not Node.js on the Backend
React zdecydowanie zdominował stronę klienta. Strona serwerowa nigdy nie dostarczyła równie skutecznego rozwiązania, a osoby przechodzące z kursów opartych na MERN-stack do firm z rzeczywistym ruchem użytkowników szybko to zauważają.
Bootcamps często przedstawiają Mongo, Express, React i Node jako kompletny zestaw: jeden język do całego stacku oraz aplikacje typu full-stack w zasięgu ręki. Projekty poboczne i małe implementacje typu CRUD zdają się to potwierdzać. Jednak pierwsza praca w firmie z milionami użytkowników pokazuje zupełnie inny obraz. Interfejs użytkownika jest oczywiście napisany w React, ale usługi podstawowe działają w Javie, mikrosługi o wysokiej wydajności są napisane w Go, a pipeline sztucznej inteligencji wykorzystuje Python. Node.js pojawia się głównie w cienkiej warstwie, która agreguje API dla strony klienta. Na dużą skalę pełni rolę pomocnego asystenta dla bardziej złożonych backendów, a powody tego są konkretne.
Pierwszy powód: jeden wątek, wiele zadań
Node.js uruchamia JavaScript na pojedynczej nitce. Doskonale radzi sobie z operacjami wejścia/wyjścia, ale jednocześnie może wykonywać tylko jedno zadanie wymagające dużych zasobów CPU. Wyobraź sobie restaurację z jednym niezwykle szybkim kelnerem – przyjmowanie zamówień, co odpowiada obsłudze baz danych i sieci, nie stanowi żadnego problemu. Klient, który zamawia pięciodaniowy posiłek wymagający trzydziestu minut przygotowania w kuchni, co symbolizuje pracę CPU, powoduje zatrzymanie tego kelnera, podczas gdy wszyscy inni muszą czekać.
Platforma wideo pokazuje, co to oznacza w warunkach rzeczywistych. Użytkownik przesyła plik 4K, a serwer musi:
- Otrzymać plik.
- Przekodować go na kilka rozdzielczości, takich jak 1080p, 720p i 480p.
- Stworzyć miniatury.
- Wyekstrahować metadane.
- Zachować wyniki w odpowiednich miejscach.
Wykonywanie tego wszystkiego wewnątrz procesu Node blokuje jego pętlę zdarzeń. Inne przesyłanie plików czeka, inne żądania API trafiają do kolejki, a aplikacja przestaje odpowiadać. Java i Go wykorzystują wiele wątków, dzięki czemu jeden może przetwarzać wideo, podczas gdy inne obsługują ruch API i procesy autoryzacji. Node doskonale nadaje się do zadań zintensyfikowanych operacjami wejścia/wyjścia, takich jak API, czat w czasie rzeczywistym i przepływ danych. Słabym punktem jest natomiast praca zintensyfikowana obliczeniami CPU, a duże aplikacje zawierają wiele takich zadań.
Drugi powód: mikrosługi pozwalają każdej zespołowi na samodzielny wybór
Wielkie firmy w większości przestały budować monolity. Monolit łączy w jednej bazie kodu funkcje autoryzacji, płatności, powiadamień i rekomendacji. Architektura mikrosług dzieli je na niezależne usługi, które komunikują się ze sobą przez sieć.
To rozwiązanie daje każdej zespołowi swobodę wyboru języka. Zespół zajmujący się płatnościami może wybrać Java, ponieważ sprawdziło się ono w transakcjach finansowych. Zespół odpowiedzialny za czat w czasie rzeczywistym może wybrać Go ze względu na jego zdolność do utrzymywania tysięcy jednoczesnych połączeń. Zespół tworzący rekomendacje może wybrać Python dzięki jego bibliotekom do uczenia maszynowego. Node pozostaje jedną z opcji wśród wielu i rzadko jest standardem.
Dlaczego Go i Java dominują
Go powstało w Google i zostało zaprojektowane, aby rozwiązać problemy wcześniejszych języków. Jego zalety to:
- Jednoczesność. Goroutines mogą utrzymywać ogromną liczbę jednoczesnych połączeń przy bardzo małej ilości pamięci.
- Rozruch. Programy kompilują się do kodu maszynowego i uruchamiają się niemal natychmiast.
- Rozwój. Wystarczy jeden plik binarny bez zależności w czasie wykonywania, aby wszystko zadziałało.
Uber polega na nim dla niektórych ze swoich najbardziej intensywnie używanych usług, Twitch do czatu w czasie rzeczywistym, a Dropbox zgłosił znaczne poprawy wydajności po przeniesieniu części swojego oprogramowania z Pythona.
Java jest standardem w środowiskach korporacyjnych od ponad 25 lat. Choć nie jest modny, jest niezawodny:
- Jest sprawdzony w bankowości, przewozach lotniczych i dużych sklepach internetowych.
- Jego ekosystem obejmuje Spring Boot, Hibernate oraz tysiące bibliotek.
- Typowanie statyczne pomaga wykryć wiele błędów już na etapie kompilacji, zanim usługa trafi do produkcji.
- Narzędzia do profilowania, monitorowania i debugowania są doskonalone od dziesięcioleci.
Netflix, Amazon i Airbnb wykorzystują ten język do obsługi kluczowych elementów backendu. Te organizacje potrzebują niezawodności w ogromnych skali i nie podążają za trendami.
Trzeci powód: dziedzictwo i inercja
Wielkie firmy mają długą historię. Amazon powstał w 1994 roku, Netflix w 1997, a Google w 1998. Posiadają miliony linijek kodu napisanego w językach Java, C++ i Python, infrastrukturę budowaną przez dziesięciolecia oraz inżynierów, którzy są ekspertami w tych językach. Przepisanie kodu na Node tylko dlatego, że jest to modne, nie jest brane pod uwagę. CTO w dużej korporacji nie zaryzykuje systemu płatności, który bezpośrednio przynosi przychody, na rzecz nowego języka tylko dlatego, że jest on popularny wśród programistów w Internecie.
Dlaczego React jest wyjątkiem
To te same firmy przyjęły React, ponieważ rozwiązał on rzeczywisty problem. Przed nim skomplikowane interfejsy użytkownika były trudne w obsłudze: splątany jQuery, mylące wzorce Backbone, cykl przetwarzania w Angular 1.x. React wprowadził architekturę komponentów, wirtualny DOM dla lepszej wydajności, deklaratywny interfejs użytkownika oraz ogromny ekosystem obejmujący Next.js i React Native. Zwyciężył, ponieważ był lepszy od alternatyw.
Node nigdy nie miał odpowiedniego, niezbędnego momentu we wersji serwerowej. Jest dobrym i solidnym wyborem do określonych zastosowań, ale przy większości poważnych zadań backendowych nie jest wyraźnie lepszy od Java, Go czy Pythona, więc nie jest automatycznym wyborem.
Jak jest zorganizowana duża aplikacja
Typowy schemat pokazuje frontend oparty na React, który łączy się z bramą API, a router kieruje każde żądanie do odpowiedniej usługi backendowej. Za nią znajdują się:
- Backend-for-frontend w Node.js obsługuje logikę specyficzną dla frontendu, agregację API oraz wydarzenia w czasie rzeczywistym. To tutaj Node sprawdza się najlepiej.
- Usługi bazowe w Java obsługują płatności, zamówienia, autoryzację użytkowników oraz zarządzanie zapasami – te elementy, które nie mogą zawieść.
- Usługi w Go zajmują się czatem w czasie rzeczywistym, transmisją strumieniową oraz wszelkimi zadaniami wymagającymi dużej równoczesności.
- Usługi w Pythonie obejmują rekomendacje, przetwarzanie obrazów oraz analizę danych.
Każda usługa posiada własne bazy danych, a usługi komunikują się za pomocą gRPC, WebSockets lub kolejków wiadomości takich jak Kafka. W przypadku interfejsu użytkownika jest jeden oczywisty zwycięzca. Strona backendowa jest wielojęzyczna, więc wybierany jest odpowiedni narzędzie do danego zadania.
Szybkie porównanie języków
- Node.js nadaje się dla startupów, wersji MVP oraz warstw BFF. Rozwój w tym środowisku jest szybki, ekosystem npm jest ogromny, a programiści JavaScriptu są wszędzie. Nie nadaje się jednak do przetwarzania wideo ani transakcji finansowych.
- Java to niezawodny narzędzie do pracy ciężkiej. Wymaga długiego czasu na naukę i powolnego uruchamiania, ale jest idealny, gdy awaria o 3 rano w Black Friday jest nie do przyjęcia.
- Go to przykład języka backendowego zaprojektowanego od nowa dzisiaj: szybki, prosty i zdolny do pracy równoległej. Jego wadą jest młodszy ekosystem w porównaniu z Javą.
- Python dominuje w dziedzinie nauki o danych i uczenia maszynowego. Jednak w przypadku API o wysokiej konkurencji Global Interpreter Lock stanowi prawdziwy wąskie gardło.
- C++ nadaje się do zadań wymagających maksymalnej szybkości, takich jak silniki gier, systemy wbudowane i handel wysokoczęstotliwościowy. Nie jest przeznaczony do API internetowych.
Co to oznacza dla początkujących programistów
To wszystko nie czyni Node złym wyborem. Pozostaje doskonałą opcją dla startupów i MVP, gdzie ważna jest szybkość wprowadzenia produktu na rynek, dla warstw backendowych obsługujących frontend, dla funkcji w czasie rzeczywistym, takich jak czaty i aktualizacje na żywo, dla funkcji serverless w AWS Lambda lub Vercel Edge, oraz dla prototypów służących do testowania pomysłów przed zastosowaniem bardziej zaawansowanych rozwiązań.
Każdy, kto dąży do pracy w dużej firmie, nie powinien polegać wyłącznie na technologii MERN. Bardziej przydatnym nawykiem jest nauka tego, jak działają systemy, aby móc dobrać odpowiedni narzędzie do każdego zadań. Błędnym podejściem jest przekonanie, że znajomość JavaScript oznacza umiejętność robienia wszystkiego.
Najlepsi programiści nie są przywiązani do jednego języka. Jeden z nich może pisać kod w JavaScript dla interfejsu, korzystać z usług w Javie podczas ich analizy, zrozumieć, dlaczego zespół wybrał Go do mikrousług, oraz rozpoznać moment, gdy Python jest lepszym rozwiązaniem. Są to osoby biegłe w wielu językach, ponieważ rozumieją, że różne problemy wymagają różnych rozwiązań, a nie dlatego, że chcą się popisać.
Odpowiednie narzędzie do każdego zadań
React zwyciężył w obszarze interfejsu użytkownika, a każda duża firma używa go z uzasadnionych powodów. Tło systemowe lepiej opisać jako ekosystem niż jako „wojnę”. Java, Go, Python, C++, Rust i Node mają swoje miejsce, a firmy tworzące codzienne aplikacje nie zmuszają do używania tylko jednego języka.
Celem programisty jest rozwiązywanie problemów, a nie używanie tego języka, który jest już mu znany; czasami oznacza to konieczność nauki czegoś nowego. Juniorzy posiadający certyfikat MERN nie powinni panikować – Node to doskonały punkt wyjścia. Kontynuuj dalej: przejrzyj projekt Java Spring Boot, napisz mały mikrosłużbę w Go i dowiedz się, dlaczego usługa płatności w firmie jest napisana w Java, a nie w JavaScript. Najlepsi inżynierowie to ci, którzy wiedzą, dlaczego istnieje każdy język i kiedy należy go używać, a nie ci, którzy znają najwięcej języków.
Literatura pokrewna
- Konfiguracja frontendu React przy użyciu Node, npm i Vite — Przyporządkowanie narzędzi w stylu Pythona do stacku React plus Vite: instalacja Node, ignorowanie folderu node_modules, tworzenie szkieletu aplikacji za pomocą Vite oraz uruchamianie lokalnego serwera rozwojowego.
- Poza CRUD: dziesięć nawyków architektonicznych, które utrzymują aplikacje MERN w stanie nadającym się do utrzymania — Poznaj nawyki na poziomie systemu, które zapewniają zdrowy rozwój aplikacji MERN w miarę jej rozwoju: odpowiedzialność za dane, umowy API, stan pochodny, kod warstwowy, lekkie dane przekazywane i spójne komunikaty błędów.