Diversity w polskim IT
Arkadiusz Pachucy
Arkadiusz PachucySenior Software Developer

Jak stworzyć bibliotekę w 8 krokach

Sprawdź, co jest najważniejsze w budowaniu nowoczesnej biblioteki i poznaj konkretne wskazówki.
23.01.20198 min
Jak stworzyć bibliotekę w 8 krokach

Implementacja biblioteki, z której będą korzystać różni programiści może być dużym wyzwaniem. Całym zespołem przekonaliśmy się o tym na własnej skórze, implementując i utrzymując SDK do płatności mobilnych, które opisywałem w serii trzech artykułów (powstanie, utrzymanie, support). Wyciągnęliśmy z tego doświadczenia mnóstwo wniosków, które pomogły nam stworzyć nową, lżejszą wersję biblioteki. Pozwoliło nam to też spojrzeć szerzej na tworzenie, potrzeby klientów oraz rozwój SDK przeznaczonych do zewnętrznego użytku.

W tym artykule przedstawię listę kroków, która sprawdzi się przy tworzeniu tego typu rozwiązań. Dodatkowo w każdym punkcie opiszę, jak to wyglądało w naszym przypadku.

Cele stworzenia biblioteki

Biblioteki ze współdzielonym kodem to nie tylko sposób na oszczędzanie czasu programistów. Stron, które są zainteresowane korzyściami z powstania SDK, jest więcej, szczególnie jeżeli jest to produkt, który pomaga pozyskać nowych klientów. Kto może być taką stroną? Osoby pracujące po stronie front-endu, back-endu, dział sprzedaży, utrzymania, czy  sami klienci. Każdy chce, by jego potrzeby zostały zaspokojone, jednak czasem potrzeby jednej grupy mogą przeszkadzać w celach innej.

W naszym przypadku...

Postanowiliśmy stworzyć nowe rozwiązanie, bardzo podobne do już istniejącego, ze względu na zmieniające się potrzeby zaangażowanych stron związanych z biblioteką:

- Biznesu - klienci chcieliby zmieniać kolory, style elementów graficznych. Dobrze by było, gdyby mogli dodać swoje logo (zastąpienie brandingu operatora płatności dowolnym innym), zmieniać również grafiki oraz żeby mieli większy wpływ na przepływ płatności. Jednocześnie chcielibyśmy, aby proces integracji był prostszy
- Backend - chcielibyśmy, aby requesty związane z procesem płatności (o ile to możliwe) wykorzystywały ogólnodostępne RESTowe API
Security  - przenieśmy większą odpowiedzialność za proces zakupowy na klientów
- Mobile - odświeżmy nieco stos technologiczny biblioteki oraz odchudźmy ją

Priorytetyzacja

Jak widać, każda ze stron miała inne cele, które nieraz były ze sobą sprzeczne. Tak samo może być w Twoim projekcie. Musisz zdecydować, które potrzeby są priorytetowe. W naszym projekcie uszeregowaliśmy je następująco:

Biznes

To on zgłasza zapotrzebowanie na produkt przez naszych klientów. Postanowiliśmy położyć bardzo duży nacisk na większą możliwość konfiguracji dla klienta. Wpłynęło to na dużo większą modularność nowego rozwiązania - każdy z modułów SDK mógł zostać wykorzystywany indywidualnie. Więcej opcji i możliwości jednak nie przełoży się na prostszą dokumentację

Backend/Security

Kompromis polegał na zredukowaniu liczby zapytań do backendu przez SDK do niezbędnego minimum. Większość danych do biblioteki powinien dostarczyć nam merchant.

Mobile

Jesteśmy w stanie wymienić biblioteki zależne, ale czy na pewno warto je wymienić? Może warto zastanowić się nad rezygnacja z kilku z nich w celu zmniejszenia zależności od komponentów firm trzecich. Musimy mieć również z tyłu głowy obecnie istniejące aplikacje, które są zintegrowane ze starszym SDK - chcemy zapewnić jak najszybsze/najprostsze przejście do nowej biblioteki pamiętając ciągle o ramach czasowych jakie otrzymaliśmy od biznesu.

Plan działania

Tworzenie biblioteki nie różni się aż tak bardzo od stworzenia aplikacji mobilnej. Zasady są podobne: wybiera się metodologię wytwarzania oprogramowania oraz przygotowuje odpowiednie historyjki pozwalające na zrozumienie tego, co należy zrobić. Jest jednak kilka punktów, w których biblioteka powinna różnić się od aplikacji:

- Im mniej wykorzystania bibliotek zewnętrznych podczas tworzenia rozwiązania, tym lepiej
- Biblioteki nie powinny ograniczać programistów, więc warto dawać im wybór, jak zaimplementować dane rozwiązanie w aplikacji - czy chcą wykorzystać warstwę logiczną, a może również i UI ?
- Nazwa zmiennych oraz zasobów w bibliotekach powinna być na tyle unikalna, żeby nie gryzła się ona z aplikacjami - np wprowadzenia prefixów
- Jednoznaczne punkty wejścia, które pozwolą na łatwą integrację
Biblioteka po prostu powinna działać i to zawsze, jak nie to programista powinien otrzymać informację np w logcacie

W naszym przypadku...

Ze względu na to, że nie tworzyliśmy pierwszej biblioteki oraz nie mieliśmy dostępu do historyjek/informacji, na podstawie jakich działała postanowiliśmy dokonać “reverse-engineeringu”. Takie podejście pozwoliło nam otrzymać w szybkim tempie odpowiedzi na zadane pytania.

W przypadku wykorzystania tego typu podejścia nie mieliśmy świadomości, że obie platformy różnią się, aż tak bardzo między sobą, w związku z tym musieliśmy wybrać jedno z podejść.

Mimo wspólnych historii oraz ogólnego planu działania nowa implementacja bibliotek również posiada rozjazdy pomiędzy wersją Androida, oraz iOS’a. Być może bardziej szczegółowy refinement pomógłby zaadresować problem wyboru „właściwej” ścieżki. W takich sytuacjach pytanie od programistów i testerów (którzy to wyłapali kłaczki) może być tylko jedno. Które zachowanie jest właściwe?

Dziel i rządź

Biblioteki na różne sposoby mogą adresować problemy z różnym poziomem zaawansowania. Udostępniając tylko logikę, kontrolki UI, czy też łącząc oba podejścia. Biblioteka może być cienkim klientem, której główną funkcjonalnością jest tylko prezentowanie danych, jak również i elementem stand alone zawierając całą logikę po swojej stronie. Bibliotekę można udostępniać w postaci fatliba - czyli jednego pliku zawierającego wszystkie funkcjonalności, jak i w postaci wielu plików odpowiedzialnych za  konkretne moduły, zachowania. Biblioteki, które są utrzymywane, po wypuszczeniu ich w świat, nieustannie ewoluują za sprawą społeczności, która je wykorzystuje oraz w głównej mierze dzięki zespołom, które je tworzą oraz wspierają.

W naszym przypadku...

Założenie architektury oraz funkcjonalności nowego rozwiązania wspierającego płatności mobilne było odejście od jednej biblioteki - czyli kombajnu, który ma w sobie wszystko. Po wewnętrznych rozmowach oraz rozmowach z użytkownikami naszego rozwiązania okazało się, że są funkcjonalności, które nie są w ogóle wykorzystywane.

Architektura rozwiązania wyglądała podobnie jak poprzedniczka. Postanowiliśmy podzielić projekt na moduły - każdy z tych modułów odpowiada za określoną funkcjonalność lub warstwę. Będą one częścią bibliotek końcowych przeznaczonych dla klientów.

W obecnym podejściu osoby implementujące mają możliwość wyboru metod płatności, z jakich chcą skorzystać. Metody płatnicze mogą być wykorzystywane na różne sposoby:

- Indywidualnie z minimalizmem UI, który można konfigurować
- Bez UI,  gdzie programista ma możliwość podpięcia metod pod swoją kontrolkę
- Metody płatności wraz z przepływem. Poza bibliotekami, które są dedykowane dla konkretnych metod płatności, dajemy możliwość wykorzystania biblioteki spinającej wszystkie metody płatności, która pozwala użytkownikowi końcowemu na wybór metody płatności z dostępnej listy.  

Dzięki jawnemu wydzieleniu z jednej biblioteki kilku głównych funkcjonalności w postaci pomniejszych części SDK klient ma możliwość stworzenia idealnego przepływu dla swojego biznesu. Każda z bibliotek posiada poza możliwością konfiguracji kolorów i styli również możliwość konfiguracji elementów logicznych/zachowania kontrolek.

Ponadto postanowiliśmy scedować odpowiedzialność komunikacji sieciowej na klienta SDK, przez co SDK nie bierze udziału w procesie opłacenia zakupu (SDK przestało bezpośrednio komunikować się z backendem dostawcy płatności). Wadą takiego podejścia jest wydłużenie drogi komunikacji oraz nowe „punkty zapalne”, które mogą spowodować problem.

Dokumentacja, czyli zrozumienie  

Żeby dobrze zapoznać się z bardziej skomplikowaną biblioteką, programista potrzebuje zapoznać się z  dwoma kluczowych rzeczami:

- Dokumentacją
- Przykładową aplikacją

Dokumentacja jest “instrukcją obsługi”, która mówi jak wykorzystać dany element tak, aby programista nie stracił za dużo czasu oraz zyskał nowe możliwości dzięki integracji z rozwiązaniem firmy trzeciej.

Co powinna zawierać dobra dokumentacja:

- Opis biblioteki
- Listę funkcji oraz ich jednoznaczny opis
- Diagram przepływu informacji
- Opis punktu wejścia (jakie informacje należy przekazać, jakie można przekazać)
- Opis informacji jakie otrzymamy na wyjściu
- Wymagania/Ograniczenia związane z wykorzystaniem danej biblioteki
- Opis jak dodać bibliotekę do swojego projektu
- Przykład kodu implementującego daną funkcję

Niestety, ale taka dokumentacja nie powstaje podczas jednej iteracji. Jest ona dziełem wysiłków developerów, testerów oraz analityków. Najprostszą weryfikacją pierwszej dokumentacji jest poproszenie kolegi z zespołu o napisaniu prostej aplikacji z wykorzystaniem nowo-powstałej biblioteki oraz notatka, co było niezrozumiałe w dokumencie, jaki otrzymał.

Technologie

Decyzja o wyborze technologii to indywidualna sprawa każdego zespołu. Jednak zalecam mocne zastanowienie się, czy warto wykorzystywać biblioteki, które generują kod jak lombok czy dagger, o czym pisałem już we wcześniejszym artykule.

Biblioteka nie powinna być również polem do sprawdzenia, jak działa nowy pattern, czy też nowa biblioteka. Podczas jej tworzenia powinniśmy wykorzystywać narzędzia, które jak sama biblioteka będą utrzymywane oraz rozwijane.

W naszym przypadku...

Chciałoby się zacząć od cytatu: Miało być tak pięknie, a wyszło jak zawsze. Jednak w tym przypadku udało nam się zrealizować kilka założeń, które postawiliśmy sobie jako programiści.

Jednym z kluczowych założeń, „aby było lepiej” jest zrezygnowanie z daggera oraz ograniczenie wykorzystywania niektórych bibliotek (np Guava) w czasie, jaki posiadaliśmy.

Postawiliśmy na prostotę oraz wykorzystanie możliwości, jakie daje nam Android wraz z pakietem bibliotek architecture components, przy czym wykorzystywaliśmy głównie klasy:

- LiveData
- Transformations
- Observer

Gdyż pozwalały one na dużą swobodę. Często biblioteka udostępnia klientom możliwość wykorzystania customowej kontrolki odpowiedzialnej za konkretną funkcjonalność z wykorzystaniem patternu MVP. Tylko kilka z naszych funkcjonalności - tych związanych z przepływem wymaga pełnej kontroli nad Aktywnością/Fragmentem.

Wdrożenie

Z wdrożeniami nowych aplikacji/bibliotek powinniśmy postępować powoli,  z rozmysłem. W momencie, kiedy produkt jest gotowy, powinien zostać on wypuszczony do wykorzystania wewnętrznego w postaci wdrożenia alpha, a następnie w postaci wdrożenia do kilku wybranych i zaufanych odbiorców w postaci bety. Następnie po zgromadzeniu uwag oraz wdrożeniu poprawek produkt powinien nadawać się dla wszystkich potencjalnych odbiorców i powinien on być dystrybuowany za pomocą odpowiednich narzędzi pozwalających na proste pobranie bibliotek.

W naszym przypadku...

Na chwilę obecną biblioteka jest w dość zaawansowanej becie, która jest integrowana w dość ścisłej współpracy z chętnymi klientami. Dzięki takiej współpracy jesteśmy w stanie poprawić zarówno zaistniałe problemy jak i poprawić istniejącą dokumentację.

Podczas wdrożenia udało nam się również w ramach pętli zwrotnej (feedback od zespołów wdrażających rozwiązanie) dodać nowe funkcjonalności do biblioteki, gdyż  początkowo nie były one brane pod uwagę. Dzięki temu poznaliśmy dokładnie oczekiwania osób, które integrują się z SDK. Komponentami/funkcjonalnościami, jakimi są najbardziej zainteresowani, są płatności kartą oraz płatność za pomocą przelewu bankowego przy minimalizmie integracji z ich strony (brak ustawiania customowych styli). Klienci, mając dowolność, postanowili stworzyć w aplikacjach własne przepływy płatności.  

Dzięki obu SDK programiści będą mieli możliwość podjęcia decyzji, które rozwiązanie wybrać - prostsze z predefiniowanym przepływem, czy jednak wybrać rozwiązanie trudniejsze, z większymi możliwościami integracyjnymi.

Podsumowanie

Z punktu widzenia użytkownika biblioteki jej celem jest ułatwienie życia, pozwalając na ponowne używanie jednego rozwiązania w różnych miejscach. Z mojego punktu widzenia, jako twórcy mobilnego SDK, napisanie biblioteki niesie ze sobą konsekwencje w postaci wsparcia rozwiązania. W komercyjnych SDK konieczna jest dobra dokumentacja i zespół supportowy. Proces nauki potrzeb klientów zawsze trwa pewien czas, co powoduje zmiany w dokumentacji oraz przyswajanie nowej wiedzy przez team supportu.

To szczególnie ważne, gdy decydujemy czy budujemy rozwiązanie pudełkowe, czy szereg małych bibliotek, z których każdy komponuje najbardziej odpowiadające mu rozwiązanie. My zdecydowaliśmy się na przejście na modularne podejście, które daje najwięcej możliwości, ale stwarza też duże wyzwanie dla zespołu integrującego, który musi mieć wiedzę domenową oraz musi poświęcić więcej czasu na integracje.

Stworzenie biblioteki jest niczym napisanie aplikacji, z tą różnicą, że staje się ona częścią różnych innych aplikacji, dzięki czemu możemy czuć się jako współtwórcy innych rozwiązań i pomysłów. Dziękuję, że przebrnęliście przez cykl i mam nadzieję, że przybliżył on niego tworzenie oraz wspieranie bibliotek od podszewki.

<p>Loading...</p>