Jak backendowcy mogą ułatwić pracę frontendowcom. Devdebata
Czy potrafiłbyś przekazać na front detale sytuacji wyjątkowych? Częstym problemem jest brak dokumentacji backendu, przez co frontend ma problem z odnalezieniem wymagań do flow, nad którym aktualnie pracuje. Skąd się to bierze i jak radzić sobie w takiej sytuacji? Jak rozstrzygasz kwestie sporne na linii backend-frontend? Te pytania zadaliśmy zaproszonym seniorom w naszym autorskim cyklu devdebat. Zobaczcie, jak na nie odpowiedzieli.
Odpowiedzi na pytania udzielili:
Piotr Kosek. Senior Software Developer w Fujitsu Technology Solutions. Związany z branżą gier video, obecnie przekwalifikowany na zabezpieczenia biometryczne, interfejsy użytkownika, a także low-level C++, wyniesiony prosto z czasów pisania własnych silników. Kiedy nie wysyła bugów w /dev/null pije kawę cynamonową, czyta książki bądź świadomie śni (złośliwi pytają czemu podczas spotkań…)
Michał Zimnicki. Java Tech Lead w ALTEN Polska. Java Technical Leader z wieloletnim doświadczeniem w programowaniu w języku Java oraz projektowaniu systemów rozproszonych. Specjalność: szeroko pojęty “performance”. Przygodę z programowaniem zaczynał od Pascala, będąc w pierwszej klasie gimnazjum. Żaden problem go nie przeraża.
Jakub Król. CEO w IT.focus. Full-stack JavaScript Developer z pasji i profesji. Zdobywca licznych ogólnopolskich i międzynarodowych nagród związanych z branżą IT. Zdobył wiele medali na Światowych Targach w Brukseli, Paryżu, Seulu, Moskwie, laureat Olimpiady Innowacji. Właściciel małego software house – IT.focus, z siedzibą w Jastrzębiu-Zdroju.
Patryk Pawłowski. Senior Full Stack JavaScript Developer w Toptal oraz product designer w jednym. Od ponad 6 lat pracuje zdalnie – głównie dla firm i startupów z USA i Szwajcarii. W ostatnim czasie zajął się również szerzeniem pracy zdalnej. Ma doświadczenie w prowadzeniu własnego biznesu. W wolnym czasie lubi podróżować, pływać na kitesurfingu, a zimą uprawiać ski-touring.
Piotr Bogusz. Lead Java Developer w Altkom Software & Consulting. Programista z wieloletnim doświadczeniem w różnych obszarach biznesowych. Zwolennik idei Software Craftsmanship starający się stosować jej założenia podczas pracy w projektach. Aktualnie wiodący programista w firmie Altkom Software & Consulting. Prywatnie mąż i ojciec.
1. Czy potrafiłbyś przekazać na front detale sytuacji wyjątkowych? Internal Error to nie jest opisowy komunikat o błędzie; frontendowiec nie będzie w stanie przekazać użytkownikowi co powinien zrobić żeby rozwiązać dany problem.
Piotr Kosek: Przede wszystkim zdałbym sobie sprawę z celu, dla którego dany soft powstaje – zbyt łatwo zgubić się w rozważaniu użytkownika końcowego i popaść w tworzenie sztuki dla sztuki (bądź dla managementu albo innego PRu – niekoniecznie zewnętrznego). Wbrew pozorom ten grzech zdarza się nie tylko po stronie backendu, pojawia się masa interfejsów, które są piękne, ale niefunkcjonalne. Dlatego te dwa – całkowicie różne podejścia w tworzeniu softu – powinniśmy alignować za pomocą usera końcowego. I front, i back służy jednej rzeczy: żeby klient chciał korzystać z naszej aplikacji: I tu jest pole do nawiązania „wspólnego języka” i współpracy.
Michał Zimnicki: Tutaj sprawa nie jest taka oczywista jak się wydaje. Oczywiście, że da się przekazać (i nawet nie jest to zwykle szczególnie trudne) detale wyjątku. Jest jednak pewien haczyk: bezpieczeństwo. Frontend jest po stronie użytkownika, który dostając szczegółowe informacje o błędzie może wykorzystać je w „niecny” sposób. Dodatkowo pamiętajmy, że takie informacje zwykłemu użytkownikowi nie są do niczego potrzebne, nie jest też w stanie nic z nimi zrobić oprócz podania ich w zgłoszeniu serwisowym, co przy odpowiednim logowaniu po stronie backendu nie jest potrzebne. A więc udostępniamy informacje, które dla 99,9% użytkowników nie są przydatne, a 0,1% może wykorzystać do zrobienia krzywdy systemowi. Oczywiście można zawsze prosić jakiegoś eksperta do spraw bezpieczeństwa aplikacji o odpowiednią filtrację informacji, ale w praktyce człowiek odbije się od ściany albo usłyszy to co napisałem wyżej.
Jakub Król: Zgadzam się z Michałem co do zadania sobie nieco szerszego kontekstem pytania: co oznacza dla użytkownika dany błąd i co może z nim zrobić? Pokazane błędy – z puli 500 – mogą zazwyczaj świadczyć o dwóch problemach z back-endem. Pierwszym z nich jest nieodpowiednia walidacja danych – tzn. dopuszczanie takich informacji od użytkownika, jakich się nie spodziewamy (bo np. ufamy walidacji na front-endzie – mam nadzieję, że nie). Taki przypadek powinien prowadzić do jasnych komunikatów błędów, a nie nic nie mówiący „Internal Server Error”. Mówiąc krótko – trzeba wprowadzić lepszą walidację i filtrowanie danych.
Drugą możliwością są nieoczekiwane błędy – nie jesteśmy w stanie przewidzieć wszystkiego, a tym bardziej jak w każdym scenariuszu ma zareagować użytkownik. Patrzmy zatem na program jako całość: co zrobić gdy wystąpi nieoczekiwany wyjątek? Wiele autorów poleca radykalne podejście – restart całego programu. Powód jest prozaiczny: nie wiemy gdzie program został przerwany, czy nie zepsuł się jakiś flow albo nie brakuje teraz danych. Z punktu widzenia back-endu może to być realizowalne lub nie (jedna architektura to umożliwi, inna nie, a jeszcze inna zrobi to sama).
Co do frontu to dałbym użytkownikowi następujące informacje:
- Przeprosiny, że wystąpił nieoczekiwany błąd. Bez szczegółów, bez detali, bez stacktrace (chyba każdy pamięta słynne błędy z PHP-a, gdy leży baza i są wyświetlane szczegóły połączenia).
- Dodałbym identyfikator błędu – to on powinien nas prowadzić do szczegółów błędu zapisanych w logach.
- Dałbym przycisk, który przeładowuje cały front. Po prostu – nie wiemy w jakim momencie błąd wystąpił i jakie flow mógł zepsuć.
Patryk Pawłowski: Podpisuję się pod wszystkim, co reszta powiedziała nt. bezpieczeństwa. Jeżeli mówimy o błędach 5xx, to one nie niosą żadnej przydatnej informacji dla użytkownika. Z perspektywy front-endu istotny jest kontekst, który spowodował wystąpienie błędu (np. kliknięcie przycisku „Dodaj do koszyka”), a nie to co wydarzyło się na back-endzie.
Przeważnie wystarczy użytkownikowi pokazać komunikat, np. „Ups, coś poszło nie tak. Nie udało nam się dodać produktu do koszyka. [Spróbuj ponownie] lub [skontaktuj się z Działem Wsparcia].”
Dzięki temu zostawiamy użytkownik wie, że chwilowo coś nie działa i ma wybór – albo spróbuje jeszcze raz później, albo skontaktuje się z działem wsparcia (lub np. sprzedaży) i dokona zakupu bezpośrednio.
Piotr Bogusz: Moim zdaniem backend powinien wystawiać uzgodniony z frontendem kontrakt. Tak jak w komunikacji pomiędzy systemami rozproszonymi tak i pomiędzy frontendem i backendem powinna istnieć udokumentowana umowa, mówiąca o tym jakiego rodzaju błędy backend zwraca i sugestię z czego może on wynikać oraz jak można go obsłużyć.
Przy tego typu założeniach frontend zawsze wie czego się spodziewać i sam decyduje jak określony kod błędu powinien być obsłużony po jego stronie. Stosując taką konwencję w przypadku pojawienia się kolejnego klienta, np. aplikacji mobilnej, nowy klient może wcześniej ustalone błędy prezentować na swój sposób. W przypadku błędów zwracanych przez serwer warto przekazywać unikalny identyfikator, który będzie powiązany ze szczegółami błędu w logach. Dla frontendu szczegóły błędów backendowych nie będą istotne, oni tylko potrzebują obsłużyć tą sytuację wyjątkową w sposób jak najbardziej przystępny dla użytkownika końcowego i zasygnalizować do backendu, że jest tam błąd powiązany z takim, a takim identyfikatorem błędu.
2. Częstym problemem jest brak dokumentacji backendu, przez co frontend ma problem z odnalezieniem wymagań do flow, nad którym aktualnie pracuje. Skąd się to bierze i jak radzić sobie w takiej sytuacji?
Piotr Kosek: Osobiście jestem zwolennikiem mało popularnego w Polsce podejścia, dokumentacja – ta techniczna – powinna powstać PRZED napisaniem kodu. Dlaczego tak? Z kilku powodów:
- dokument Worda na 5 stron powstanie dużo szybciej – w czasie rzędu kilkudziesięciu minut – niż 20 000 linii kodu. Nasza ludzka wyobraźnia jest zawodna; gdy zobaczymy tekst na ekranie możemy nagle odkryć, że moduł XYZ w sumie to nie potrzebuje informacji z ZYX, natomiast dostęp do ABC znacząco ulepszy flow,
- zakładając alternatywę – napisaliśmy już te 20 000 linii i odkryliśmy to samo… ile osób (nawet nie mówię o managemencie, tylko o samych programistach) będzie skore refaktorować, przebudowywać, ba – nawet wyrzucić do kosza rozwiązanie, które się zwyczajnie nie sprawdza? Większość znanych mi ludzi rozłoży ręce i powie, że mleko się rozlało i stworzone już średniej jakości rozwiązanie jest lepsze od dowolnego nienapisanego.
- dokument Worda znacząco ułatwia indukcję nowej osoby; pisanie kodu jest dużo łatwiejsze niż jego czytanie. Oczywiście nie możemy rozpisać go ultra precyzyjnie – z dokładnością do pojedynczej funkcji API, gdyż wtedy zarzut o szybką dezaktualizację jest bardzo zasadny. Do takiej precyzji najlepiej wykorzystać automatyczną dokumentację. Natomiast flow danych, operacji, co się dzieje pod maską, powinien być spisany i dostępny dla każdego.
- jeżeli trzymamy dokumenty na czymś w rodzaju wiki – najlepiej takiej udostępniającej kolaborację w realtime – możemy nad nimi pracować w formie brainstormingu, także z front endowcami, nie zmuszając ich do mailowania w sprawie API
Btw. Joel Spolsky, współzałożyciel Stackoverflow, zaproponował kiedyś – całkowicie na serio, że jeżeli twoi programiści nie lubią/umieją Worda, żeby wysłać ich na bootcamp… literacki. Żeby się oswoili z pisaniem czegoś, co nie zawiera za dużo średników i zobaczyli, że narzędzia typu Word wcale nie gryzą
Michał Zimnicki: Brak dokumentacji to problem całego developmentu. Na każde pytanie o dokumentację do API, usłyszy się od części osób, że: „kod powinien być samo-dokumentowalny”, albo „przecież wszystko jest tu jasne”. Ogromnym problemem z dokumentacją jest jej utrzymywanie.
Aplikacje/charakterystyki/systemy/środowiska/konfiguracje ciągle się zmieniają, tak samo ludzie, którzy nad nimi pracują. Bardzo ciężko jest utrzymać dokumentację, która odzwierciedla stan faktyczny. Wymaga to dyscypliny od każdego developera, a z tym bywa różnie. Często też bywa, że dokumentacja istnieje, ale dokopanie się do niej okazuje się bardzo problematyczne, lub sama w sobie jest niejasna. Dla mnie jest to problem nierozwiązywalny praktycznie (bo w teorii to się pięknie da to zrobić).
Bardzo dużym ułatwieniem jest stosowanie zasad zaczerpniętych z: czystego kodu, DDD i BDD (świetnym opisem flow będzie właśnie test, albo chociaż scenariusz testowy). Jednak im większy system tym trudniej. Przy tworzeniu nowej funkcjonalności zwykle jest dostęp do jakiegoś BA/PO/QA lub deva co temat ogarnia. Dla legacy kodu bywa lipa i niestety trzeba sobie radzić. Nie spotkałem się z firmą, gdzie dokumentacja byłaby należycie zrobiona (paradoksalnie w banku było najlepiej, ale dalej słabo), co nie znaczy, że takowa nie istnieje. Jeśli tak to chętnie poznam.
Jakub Król: Rozwiązaniem braku dokumentacji jest… pisanie dokumentacji. Serio! Istnieje sporo narzędzi – od pisania w Wordzie, przez Postmana, po automatycznie generowaną dokumentację – są one przydatne, łatwe w użyciu, a w perspektywie nawet tygodni oszczędzają sporo czasu. Oczywiście, że taka dokumentacja może nie być zawsze najświeższa i czasem wymaga poświęcenia większej ilości czasu, żeby odzwierciedlić rzeczywistość. Ale z doświadczenia wiemy, że im bardziej dbamy o dokumentację, czyli w aplikacjach, gdzie jest ona zgodna z kodem – dodawanie nowych funkcjonalności, czy tropienie bugów jest po prostu o niebo łatwiejsze.
Patryk Pawłowski: Faktycznie z dokumentacją jest spory problem. Jeżeli oprogramowanie jest ciągle rozwijane, to dokumentacja też powinna być na bieżąco uaktualniana. Większość programistów nie lubi albo nie potrafi jej pisać. Niektóre firmy mają osobne stanowisko, którego jedynym zadaniem jest pisanie dokumentacji do powstającego kodu.
Nawet gigantom jak Google zdarza się nieposiadanie aktualnej dokumentacji, a często jej zawartość jest wybrakowana lub niewiele mówiąca… Wszystko zależy od procesów w projekcie. Jeżeli powiemy, że dokumentacja jest kluczowym elementem developmentu i będziemy pilnowali, aby była częścią releasów, to będzie on aktualna i kompletna. Jeżeli stwierdzimy, że jest ona tylko miłym dodatkiem, ale w sumie jest niepotrzebnym kosztem (bo lepiej wypychać nowe funkcjonalności), to pewnie nigdy ona nie powstanie.
Podobnie jest z testami – one też są (dalej) często traktowane jako niepotrzebna strata czasu i ucina się je kosztem budowania kolejnych funkcjonalności. Ale jeżeli od początku rozwijamy oprogramowanie w myśl np. TDD, to okazuje się, że testy stają się kluczowym elementem procesu. Tak też może być z dokumentacją, ale jako branża musimy do tego dojrzeć.
Piotr Bogusz: W danej chwili przychodzą mi na myśl dwie opcje:
- wymagać dokumentacji z przykładowymi wywołaniami i odpowiedziami, wtedy flow może być bardziej elastyczne i może decydować o tym co i w jakiej kolejności chce wywoływać,
- wymagać testów API, które będą pokazywały jak uzyskać przewidziany flow dla przykładowych wywołań. Czyli właściwe nadal dokumentacja ale w innej formie.
3. Jak rozstrzygasz kwestie sporne na linii backend-frontend?
Piotr Kosek: Przychodzi front-endowiec do back-endowca z pytaniem:
– Dlaczego usunęliście tę funkcję z API? Była nam potrzebna!
– Bo używaliście jej za często i cały system był zbyt obciążony.
– No, ale teraz to my musimy robić z siedmiu osobnych query, żeby zdobyć to samo!
– Tak, ale teraz to wasza wina.
A teraz na poważnie. Kod powstaje po coś; użytkownik końcowy chce rozwiązać problem. Front-end powinien poprowadzić go za rękę by go rozwiązać. Back-end natomiast powinien udostępnić wszystko to, czego front potrzebuje. I tak powinna wyglądać hierarchia. Widziałem stanowczo za dużo softu, w których „ogon merdał psem” i back-endowcy uważali się za bogów, od których wszystko zależy, a użytkownik końcowy był… well, na szarym końcu. (A teraz mnie zlinczują…)
Michał Zimnicki: Kwestie sporne backend-frontend staram się rozwiązywać tak jak wszystkie inne, czyli według listy priorytetowej.
- Internet – co mówi ulica na ten temat. Możliwe, że to nie pierwszy raz, kiedy dany problem wystąpił i rozwiązanie jest już dobrze opisane.
- Specyfikacja – rozwiązanie, które nie działa zgodnie z oczekiwaniami biznesowymi, lub wcale – od razu odpada.
- Standard przyjęty na rynku lub w firmie/dziale – rozwiązanie niezgodne z ogólnie przyjętym standardem lub implementujące go w błędny sposób (jak np w REST endpoint „/account/{id}/delete”) też jest odrzucane.
- Performance – wygrywa rozwiązanie, które znacząco mniej obciąża server, a w szczególności bazę danych, ale różnica musi być widoczna gołym okiem. No chyba, że liczy się każda milisekunda. Nie ma co optymalizować na siłę jak endpoint może być używany raz na godzinę.
- Łatwość rozbudowy – czy struktura zapytania/odpowiedzi jest taka, że można w łatwy sposób ją rozbudować bez utraty kompatybilności.
- Użyteczność – czy rozwiązanie może być łatwo zastosowane w innych miejscach. Nie ma co tworzyć 5 endpointów, które w sumie zwracają to samo, kiedy można stworzyć jeden, który będzie miał np. dodatkowy parametr (ale tutaj nie można przesadzać bo endpoint z 10 parametrami jest trudny w użyciu).
- Szeroko pojęta elegancja – tutaj już podejście czysto subiektywne, albo jakieś rozwiązanie wygląda dobrze albo nie.
To podejście jest oczywiście do sporów na podłożu technicznym. Bo spory typu: „frontend jest lepszy od backendu” omijam szerokim łukiem.
Jakub Król: Wierzę w to, że odpowiednie zrozumienie specyfiki pracy i frontu, i backu jest kluczowe dla braku takich sytuacji. Pomaga także jasny podział – np. rzeczy funkcjonalne wychodzą z BE, rzeczy związane z usability od FE. Jeżeli nie ma jasności między frontem a backendem, to od tego jest Scrum Master, żeby w tym pomóc, a PM/PO, żeby zdecydować. Trzeba mieć świadomość, że taki brak porozumienia często doprowadza do znalezienia lepszych rozwiązań, z drugiej strony nie może trwać zbyt długo, by nie marnować czasu.
Patryk Pawłowski: Rozwój JavaScriptu i całego ekosystemu webowego sprawił, że nie mówimy już o stronkach, które wyświetlają tylko HTML, a cała logika siedzi po stronie back-endu. Na co dzień korzystamy raczej ze skomplikowanych aplikacji webowych, które często są serverless (eg. Google Firebase), a ich logika biznesowa głównie jest osadzona we front-endzie. Dlatego w szczególności ważna jest obustronna komunikacja i chęć współpracy. Wielokrotnie zdarzało mi się, że po chwili dyskusji i wymiany myśli stwierdzaliśmy, że efektywniej będzie obsłużyć część logiki właśnie po stronie front-endu.
Piotr Bogusz: Biznesowo. To Product Owner decyduje o funkcjonalnościach i to on określa jak dana funkcjonalność ma działać. W takim przypadku osoba pracująca na frontendzie powinna zweryfikować czy jest w stanie dostarczyć wymaganą funkcjonalność w optymalny sposób wykorzystując dostępne API. Jeżeli nie, to backend powinien się zmobilizować i zrobić wszystko co w jego mocy żeby API było kompletne. Jeśli jednak z jakiegoś powodu nie będzie to możliwe, to cały zespół: Product Owner, Frontend i Backend powinni przeanalizować docelowe rozwiązanie i jeżeli zajdzie taka potrzeba zgodzić się świadomie na kompromisy.
Pracujemy nad devdebatę o tym, jak frontendowcy mogą ułatwić pracę backendowcą. Jeśli chciałbyś wziąć w niej udział – napisz na adam@justjoin.it.
Zdjęcie główne artykułu pochodzi z unsplash.com.