Inżynieria aplikacji i systemów
Dobrane do zadania, zbudowane z myślą o utrzymaniu.
Budujemy oprogramowanie, na którym stoi firma - platformy internetowe, backendy i integracje, narzędzia wewnętrzne, aplikacje mobilne. Na co dzień pracujemy w technologiach, które znamy najlepiej: Python, TypeScript i Kotlin. Gdy wymaga tego produkt, sięgamy po szerszy warsztat, w tym .NET oraz natywne narzędzia desktopowe i mobilne. A jeśli system już istnieje, rozwijamy Twój kod, zamiast budować obok niego nowy system. Zmienia się technologia, ale nie dyscyplina: testy, spisane specyfikacje API, przegląd kodu, dokumentacja oraz zasada, że język dobiera się do zadania, a nie odwrotnie.
- Python, TypeScript z Next.js i Kotlin jako trzon - za każdym stoją lata wdrożeń. Do tego .NET oraz natywne narzędzia desktopowe i mobilne, gdy wymaga tego istniejące środowisko albo sam produkt.
- Jeśli Twój system powstał w technologii, której tu nie wymieniamy, również w niej pracujemy. Dobieramy język do zadania i dopasowujemy się do Twojego kodu, zamiast naginać Twój problem do naszych przyzwyczajeń.
- Jeśli system komunikuje się z czymkolwiek - przez API, współdzielony katalog z plikami, webhook (automatyczne powiadomienie innego systemu) albo choćby wysyłane wiadomości e-mail - potrafimy go zintegrować.
- Architektura na miarę firmy: najpierw jeden dobrze poukładany backend, osobne usługi i kolejki dopiero wtedy, gdy uzasadnia je skala. Uprzedzimy, kiedy ten moment nadejdzie.
- Dane wchodzące do systemu są sprawdzane w jednym miejscu, na serwerze - niezależnie od tego, czy pochodzą z cudzego API, z pliku, czy zostały wpisane ręcznie. Tak samo dostęp: logowanie przez dostawcę tożsamości, jeśli firma go ma (np. Microsoft Entra ID; protokoły SAML albo OpenID Connect), role sprawdzane na serwerze, dostęp systemów oparty na kluczach.
Zanim zaczniemy rozmawiać o technologiach, odpowiadamy na osiem pytań:
- 01
Co to jest - strona internetowa, platforma, narzędzie wewnętrzne, usługa integrująca inne systemy, aplikacja desktopowa, aplikacja mobilna; każde z nich ma swoją naturalną technologię.
- 02
Co już masz - kod, hosting, języki, które zna Twój zespół; na tym budujemy, zanim zaproponujemy cokolwiek nowego.
- 03
Kto będzie w nim pracował - ile osób, jak często i w jakich warunkach; ekran dla magazyniera w rękawicach to inny projekt niż panel dla dwóch analityków.
- 04
Kto będzie to utrzymywał - Twoi programiści, my albo nikt techniczny; od tego zależy, jak rozbudowane środowisko uruchomieniowe i framework (gotowy szkielet aplikacji) da się rozsądnie utrzymywać.
- 05
Integracje i zgodność - płatności, fakturowanie, tożsamość, rejestry publiczne, RODO; to wyznacza model danych, zanim powstanie pierwszy ekran.
- 06
Wydajność i wzrost - ilu użytkowników, ile danych; to decyduje, jak system rośnie bez przepisywania od nowa.
- 07
Czas do pierwszego wydania - ile czasu jest do pierwszej działającej wersji; to przesądza, co powstaje od razu, a co czeka na kolejne wydanie.
- 08
Żywotność technologii - czy to, na czym budujemy, będzie utrzymywane za pięć lat i kto za tym stoi. Jeśli wybór jest pod tym względem ryzykowny, mówimy to wprost.
Każdy system, który tworzymy lub przejmujemy, otrzymuje ten sam zestaw, niezależnie od języka: repozytorium z testami i ciągłą integracją (automatyczne testy przy każdej zmianie), spisane decyzje oraz dokumentację, z którą następny programista może od razu zacząć pracę.
Aplikacje i strony internetowe
- Next.js - nasz domyślny wybór w projektach internetowych: platformy z rozbudowaną logiką biznesową - sprzedaż, szkolenia, portale klienckie, narzędzia wewnętrzne - oraz strony, na których liczą się widoczność w wyszukiwarkach, wydajność i tempo wdrożeń. Wyświetla to, co rozstrzyga backend, a spisana specyfikacja API, sprawdzana automatycznie, nie pozwala obu warstwom rozejść się niezauważenie. Współpracuje z każdym backendem opisanym na tej stronie.
- Svelte albo sam React - gdy lżejszy frontend lepiej pasuje do produktu albo do zespołu, który go przejmie.
- WordPress - gdy potrzebna jest zwykła strona albo gdy Twoja strona już na nim działa i zależy Ci na solidnym wykonaniu.
Gotowy produkt jest właściwą odpowiedzią tam, gdzie Twój proces wygląda tak jak u wszystkich - płace są tego najlepszym przykładem. Budujemy tam, gdzie to, czym się różnisz, jest dokładnie tym, czego gotowy produkt nie obsługuje. A gdy go kupisz, zostaje zadanie, które i tak wykonujemy: integracja z resztą Twoich systemów.
Aplikacje komputerowe
- WinUI 3 z Windows App SDK - obecny sposób budowania aplikacji na Windowsie; nasz domyślny wybór przy nowych projektach desktopowych.
- WPF - do istniejącego kodu i zespołów, które już w nim pracują; ekosystem jest dojrzały, a kod będzie działał latami.
- .NET MAUI - Windows i macOS z jednego kodu .NET, gdy firma pracuje w .NET, a wspólny kod jest priorytetem.
- C++ z CMake - jeden kod na Windows, macOS i Linux, z wydajnością natywną, gdy liczy się każdy z tych trzech systemów.
- SwiftUI - do aplikacji przeznaczonych na komputery Mac.
Aplikacja desktopowa zwykle przeżywa framework, w którym powstała. Narzędzie dobieramy do tego, jak długo ma działać.
Aplikacje mobilne
- Kotlin Multiplatform - wspólna logika biznesowa dla Androida i iOS, gdy produkt ma szybko trafić na obie platformy, a Twój zespół zna już Kotlina.
- Flutter albo .NET MAUI - Flutter do aplikacji mocno graficznych, które wszędzie mają wyglądać tak samo; MAUI, gdy firma pracuje w .NET i chce jednego kodu.
- Natywnie - SwiftUI na iPhone'a i iPada, Kotlin na Androida - do aplikacji krytycznych, w których niezawodność, wydajność i gwarantowane wsparcie ze strony systemu ważą więcej niż wspólny kod.
Wieloplatformowo najszybciej trafisz do obu sklepów, ale aplikacja zależy od cyklu wydań frameworka i działa o jedną warstwę dalej od sprzętu.
Projektowanie i treść na każdej platformie
- Systemy projektowe na wszystkich platformach - jedna definicja kolorów, typografii i odstępów (tokeny projektowe), użyta w przeglądarce, na komputerze i na telefonie. Do tego sprawdzony kontrast, tabele, które czytnik ekranu potrafi odczytać, i animacja, która wyłącza się sama, gdy użytkownik włączył w systemie ograniczenie ruchu.
- Konwencje platformy - komponenty webowe w przeglądarce, Fluent na Windowsie, wytyczne Apple na iPhonie, iPadzie i Macu, Material na Androidzie - żeby aplikacja wyglądała i zachowywała się tak, jak użytkownik przywykł na swoim urządzeniu.
- Zarządzanie treścią dobrane do potrzeby - CMS, gdy Twój zespół będzie regularnie edytował treść: wbudowany w aplikację albo niezależny od niej (headless). Treść, której i tak nikt nie będzie edytował, trzymamy w kodzie jako dane, dzięki czemu każda cena, obietnica i etykieta przechodzi przegląd, zanim trafi na produkcję.
- Animacja i 3D, gdy są częścią produktu - Three.js i GSAP tam, gdzie to, co dzieje się na ekranie, jest istotą rzeczy, a nie ozdobą.
Animacja, przez którą użytkownik dłużej czeka na pierwszą treść, odstrasza więcej osób, niż zachwyca.
Backendy i usługi
- Python z Django - nasze najbardziej uniwersalne narzędzie: backendy platform z rozbudowaną logiką biznesową, małe systemy działające bez nadzoru, narzędzia do danych, uczenie maszynowe z PyTorch. Pozwala szybko wdrożyć system i łatwo go przekazać.
- Kotlin i Java na Spring Boot - nasz wybór dla backendów klasy korporacyjnej: usługi o wielu integracjach i systemy na lata. Silne typowanie wymusza w nich porządek na danych, które napływają z zewnątrz w niejednolitej postaci. Kotlin z wyboru, Java tam, gdzie już jest - ta sama platforma i te same narzędzia.
- C# i .NET - backendy w C# i usługi natywne dla Azure, gdy firma pracuje już w narzędziach Microsoftu.
- C i C++ - gdy wymogiem jest wydajność albo bezpośredni dostęp do sprzętu; sięgamy po nie tam, gdzie liczy się każda mikrosekunda.
Język dobiera się pod zespół, który będzie ten system utrzymywał, a nie pod ten, który chciałoby się mieć.
Integracje
- Wszystko, czym system się komunikuje - API, webhooki, katalogi na pliki, bazy danych, kolejki, w których wiadomości czekają na odbiór. Po drugiej stronie: systemy płatności i fakturowania, ERP i księgowość, dostawcy tożsamości, komunikatory i narzędzia pracy zespołowej, platformy automatyzacji, rejestry publiczne i usługi państwowe.
- Obejścia, gdy nie ma API - kiedy jedynym wyjściem z systemu jest mail, który wysyła, plik, który eksportuje, albo ekran, który pokazuje, to właśnie ono staje się punktem integracji. Jeśli dane opuszczają system w jakiejkolwiek postaci, da się je odczytać.
- Stare protokoły - usługi SOAP i inne oparte na XML, z poprzedniej dekady, obsługujemy rutynowo.
Każda integracja to zależność od cudzych decyzji. Budujemy je tak, żeby zmiana po drugiej stronie od razu dała o sobie znać, zamiast po cichu psuć dane.
Architektura i skala
- Monolit modularny - jeden backend wdrażany w całości, podzielony na wyraźne moduły: konta, płatności, katalog, administrację. Granic między modułami pilnują narzędzia, więc moduł można wydzielić do osobnej usługi, gdy wymusi to skala albo układ zespołów.
- Mikrousługi, gdy są uzasadnione - niezależne usługi z własnymi danymi, własnym wdrożeniem i orkiestracją opisaną w Chmurze i inżynierii platform, gdy wymaga tego kilka zespołów albo gdy części systemu rosną w zupełnie innym tempie.
- Jedno API dla wszystkich aplikacji - REST albo GraphQL obsługujące aplikacje internetowe, komputerowe i mobilne, żeby wybór platformy nigdy nie dyktował backendu.
- Frontend bez pamięci i buforowanie - warstwa, która sama nie pamięta zalogowanych użytkowników, więc można ją skalować, dodając kolejne kopie. Przed kosztownymi operacjami umieszczamy Redis, czyli szybką pamięć podręczną, z ustalonym z góry czasem ważności zamiast zgadywanego.
- Kolejki i zdarzenia - Kafka albo RabbitMQ, czyli pośrednicy, którzy przyjmują zdarzenie i dostarczają je dalej. Przydają się, gdy coś ma się wykonać już po odpowiedzi dla użytkownika albo trafić naraz do kilku systemów - przy skali, która to uzasadnia.
- Hurtownie i hosting - raportowanie i hurtownie opisujemy w Platformach danych i automatyzacji, a skalowanie samych maszyn w Chmurze i inżynierii platform.
Mikrousługi dla jednego zespołu to rozproszony monolit z rachunkiem za sieć.
Bazy danych
- PostgreSQL - nasz domyślny wybór: relacyjna i transakcyjna. Z rozszerzeniem wektorowym obsłuży też wyszukiwanie po znaczeniu, a nie po dokładnym słowie - w skali, do której dochodzi większość produktów.
- SQL Server i Azure SQL - obok .NET, w środowiskach opartych na Microsofcie, gdy firma już z nich korzysta.
- Bazy dokumentowe - MongoDB i podobne, gdy dane mają kształt dokumentu, a struktura danych zmienia się szybciej, niż nadążają migracje.
- Dedykowane bazy wektorowe - gdy wyszukiwanie jest głównym zadaniem systemu, a zbiór do przeszukania jest większy, niż PostgreSQL obsłuży wygodnie.
Bazę dokumentową wybiera się czasem po to, żeby nie projektować struktury danych. Struktura i tak powstanie - tylko przypadkiem i bez niczyjego nadzoru.
Transakcje, dostęp i dziennik zmian
- Płatności i zamówienia - ceny i sumy liczone na serwerze, zakup kończy się w całości albo wcale, powtórzone żądanie wykonuje się raz - żeby podwójne kliknięcie albo ponowione wywołanie zwrotne nigdy nie skończyło się podwójnym obciążeniem ani podwójną rezerwacją.
- Kontrola dostępu - logowanie przez dostawcę tożsamości, jeśli firma go ma (np. Microsoft Entra ID; protokoły SAML albo OpenID Connect), role i uprawnienia sprawdzane na serwerze przy każdym żądaniu, przełączniki funkcji domyślnie wyłączone.
- Dziennik zmian - zapis, do którego można tylko dopisywać, nigdy w nim poprawiać: kto, co, kiedy i skąd zmienił, na rekordach, które mają znaczenie - zamówieniach, uprawnieniach, konfiguracji.
Przy pomyłce w konfiguracji zostaje stan domyślny. Stanem domyślnym powinien być stan bezpieczny.
Testy, wdrożenia i aktualizacje
- Testy i specyfikacje API - testy regresji, które pilnują, żeby to, co już działa, nie zepsuło się przy następnej zmianie; spisany opis tego, co API przyjmuje i zwraca, sprawdzany automatycznie, żeby frontend i backend się nie rozjechały.
- CI/CD - każda zmiana przejrzana, a potem automatycznie zbudowana i przetestowana, wdrażana na kolejne środowiska, z przećwiczoną drogą powrotu; na GitHub Actions, GitLab CI albo Azure DevOps - tam, gdzie już znajdują się Twoje repozytoria.
- Aktualizacje - biblioteki, z których korzysta system, trzymane na ustalonych wersjach, sprawdzane pod kątem luk i aktualizowane według harmonogramu; większa zmiana wersji przećwiczona na osobnym środowisku, zanim dotknie produkcji, a funkcje, które producent usuwa, śledzone, a nie odkrywane. Szerzej o łataniu i utrzymaniu piszemy w Niezawodności, bezpieczeństwie i utrzymaniu.
- Przeniesienie danych, które już masz - z arkuszy, z papieru, ze starego systemu; z ustaloną zasadą postępowania z danymi, które się nie zgadzają, i bez przestoju w pracy firmy.
- Przejęcie istniejącego kodu - najpierw audyt i zamknięcie luk bezpieczeństwa, potem spisanie tego, co zastaliśmy, i rozwój od tego miejsca. Przepisanie od zera jest ostatnią opcją, nie pierwszą.
„Przepiszmy to od zera” zwykle znaczy „jeszcze tego nie przeczytaliśmy”.
Zawsze w tej samej kolejności:
- 01
Rozpoznanie - co oprogramowanie ma robić, co już masz i kto będzie to utrzymywał.
- 02
Projektowanie - technologie, architektura i integracje uzgodnione z Tobą przed budową; decyzja spisana razem z odrzuconymi wariantami. Jeśli widzimy, że coś nie zadziała, mówimy to tutaj, a nie po wydaniu.
- 03
Budowa - repozytorium, testy i ciągła integracja od pierwszej zmiany w kodzie; specyfikacja API gotowa przed pierwszym ekranem. Rozmawiasz bezpośrednio z inżynierami, którzy piszą Twój system, bez pośredników. Ilekroć o to poprosisz, pokazujemy, na jakim etapie jest praca względem harmonogramu.
- 04
Wydanie - etapami, z przećwiczoną drogą powrotu, w oknach serwisowych, które nie zaburzają Twojej pracy.
- 05
Przekazanie - repozytorium, dokumentacja i dostępy przechodzą na Ciebie.
- 06
Wsparcie - zostajemy na SLA tak długo, jak jest potrzebne: do dnia, w którym Twój dział IT przejmuje system, albo przez kolejne lata. Długość ustalasz Ty - nikogo nie wiążemy na czas, którego sam nie wybrał.
Na koniec projektu przekazujemy Ci:
- Kod w repozytorium, które należy do Ciebie
- Testy i pipeline CI/CD, który je uruchamia i wdraża zmiany
- Specyfikacja API, zapisana w repozytorium i sprawdzana automatycznie - gdy system ją ma
- Zapisy decyzji architektonicznych: co wybraliśmy, co odrzuciliśmy i dlaczego
- Dokumentację i instrukcje operacyjne dla następnego programisty
- Spisane dostępy administracyjne - nic nie zostaje wyłącznie w naszych głowach