Professional Documents
Culture Documents
U mnie dziala. Jezyk branzy IT - Pawel Baszuro
U mnie dziala. Jezyk branzy IT - Pawel Baszuro
U mnie działa
Język branży IT
Wszelkie prawa zastrzeżone. Nieautoryzowane
rozpowszechnianie całości lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione.
Wykonywanie kopii metodą kserograficzną, fotograficzną, a
także kopiowanie książki na nośniku filmowym,
magnetycznym lub innym powoduje naruszenie praw
autorskich niniejszej publikacji.
Wszystkie znaki występujące w tekście są zastrzeżonymi
znakami firmowymi bądź towarowymi ich właścicieli.
Autor oraz Wydawnictwo HELION dołożyli wszelkich
starań, by zawarte w tej książce informacje były kompletne
i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za
ich wykorzystanie, ani za związane z tym ewentualne
naruszenie praw patentowych lub autorskich. Autor oraz
Wydawnictwo HELION nie ponoszą również żadnej
odpowiedzialności za ewentualne szkody wynikłe z
wykorzystania informacji zawartych w książce.
Redaktor prowadzący: Małgorzata Kulik
Grafika na okładce została wykorzystana za zgodą
Shutterstock.com
Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog
książek)
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/umnied_ebook
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
ISBN: 978-83-283-5914-7
Copyright © Helion 2019
Poleć książkę
Kup w wersji papierowej
Oceń książkę
Księgarnia internetowa
Lubię to! » nasza społeczność
Mojej żonie Aleksandrze
Wstęp
Komputer i oprogramowanie w
firmie
Zastanówmy się nad oprogramowaniem, z jakim spotykamy
się w firmie, i nad rozwiązaniami stosowanymi w firmach o
różnej wielkości zatrudnienia. Nie koncentrujmy się tu na
tym, co jest przedmiotem działania, a poszukajmy
elementów wspólnych. Szczególnym przykładem są firmy
tworzące programy; ich oprogramowanie omówimy osobno.
Wyobraźmy sobie firmę jedno- lub kilkuosobową. W takiej
organizacji pracownicy korzystają z oprogramowania do
obsługi bieżącej działalności firmy i wymieniają dane
pomiędzy sobą lub z kontrahentami. Oprogramowanie może
służyć do sprzedaży produktów, komunikacji czy
prowadzenia dokumentacji firmowej. Obecnie wiele firm ma
swoją stronę internetową. Gdy firma działa na rynku e-
commerce, strona jest na ogół pierwszym i głównym
kontaktem z klientem. Ze stroną internetową prawie
zawsze idzie w parze poczta elektroniczna. Czytanie i
odpisywanie na elektroniczne listy jest codziennym
zadaniem wielu pracowników. Oprócz poczty pracownicy
często używają komunikatorów internetowych (np.
Microsoft Skype, Cisco Jabber, Slack). Do prowadzenia
podstawowych zadań, takich jak wymiana
sformalizowanych dokumentów czy kalkulowanie
rozmaitych rozrachunków, przydatny jest pakiet biurowy
oprogramowania (np. Microsoft Office, Google Docs lub
LibreOffice). Należy jeszcze dodać oprogramowanie
księgowe czy specyficzne dla danego biznesu (np. do
obsługi specjalistycznych maszyn).
Kiedy firma rozrasta się do kilkunastu lub kilkudziesięciu
osób, pojawiają się dodatkowe problemy związane z
zarządzaniem dostępem do określonych informacji,
przepływem wiedzy i koordynacją zadań. Zarządzanie
dostępem może być realizowane za pomocą połączenia
usług domenowej i katalogowej. Usługa katalogowa zbiera
informacje o wszystkich istotnych bytach w firmie,
użytkownikach (każdy ma swój login i hasło do systemu),
komputerach, drukarkach, grupach dystrybucyjnych (kto
dostaje informacje wysłane na dany adres), grupach
bezpieczeństwa. Grupy bezpieczeństwa to szczególny byt,
który musi być oferowany przez system operacyjny (usługa
domenowa lub domena, nie mylić z domeną WWW) i
jednocześnie powinien być zintegrowany z systemem
zarządzania (usługa katalogowa). Scentralizowane
zarządzanie dostępem ułatwia pracę wielu osobom.
Typowym oprogramowaniem do zarządzania domeną jest
wbudowany w Microsoft Windows Active Directory (istotne
protokoły techniczne to Kerberos i LDAP).
Mając scentralizowany system zarządzania dostępem do
informacji w firmie, przejdźmy do miejsc gromadzenia
informacji. Informacje zazwyczaj gromadzi się za pomocą
oprogramowania lub platform do przetwarzania
dokumentów i stron. W zależności od potrzeb firmy wdraża
się rozwiązania integrujące obieg dokumentów (np. oparte
na Microsoft SharePoint) lub podobne do Wikipedii (np.
Atlassian Confluence). Do zarządzania zadaniami może
także służyć dedykowane oprogramowanie (np. Atlassian
JIRA lub Trello).
Wraz z rozwojem firmy pojawiają się kolejne zadania
i konieczne jest wdrożenie dodatkowego oprogramowania
(obok wcześniej omówionego dla mniejszych firm, z którego
korzysta się dalej). W przypadku kilkusetosobowej
organizacji mamy do czynienia z nowymi zagadnieniami,
takimi jak zarządzanie ryzykiem lub poszukiwanie
efektywności działania firmy na różnych polach.
Zarządzanie ryzykiem musi odbywać się na wielu polach.
Firmy mogą podlegać regulacjom specyficznym dla danej
branży, czasem będącym wynikiem starań o certyfikację
zarządzania jakością (np. certyfikacja ISO). Aby przełożyć
zadania biznesowe na oprogramowanie, potrzebne są
wiedza i zdolność zarządzania nią. Tutaj pojawia się miejsce
na zarządzanie procesami biznesowymi (ang. Business
process management, BPM). W bardziej zaawansowanych
scenariuszach procesy biznesowe są przełożone na
konkretne komponenty oprogramowania lub sprzętu. Na
różnych poziomach organizacji pojawiają się systemy
zarządzania procesami biznesowymi (oprogramowanie
zgodne ze standardem opisu procesów biznesowych
BPMN), silniki reguł biznesowych lub kompleksowe
systemy mapujące jeden świat na drugi (np. ServiceNow).
Przy takiej wielkości organizacji pojawiają się systemy
planowania zasobów przedsiębiorstwa (ang. enterprise
resource planning, ERP). Skomplikowane systemy ERP
zazwyczaj są rozbite na moduły (np. moduł księgowości,
magazynu, planowania produkcji) i pozwalają na
kompleksowe zarządzanie przedsiębiorstwem (np. SAP ERP
lub Microsoft Dynamics AX).
Gdy firma rozrasta się, a kolejne biura rozlokowane są w
różnych krajach, wzrasta poziom skomplikowania
infrastruktury biznesowej i informatycznej. Różne działy
biznesowe podlegają różnym reżimom prawnym i pojawiają
się problemy w wymianie danych. Dane nie zawsze mogą
być dostępne na bieżąco (ang. on-line), co powoduje
problemy związane z integracją systemów i jakością
samych danych. Istotnym aspektem kontroli jakości danych
stają się procesy mające zapewnić kompletność, dokładność
i aktualność danych (odpowiednio w języku angielskim
completeness, accuracy i timeliness, często występuje w
postaci akronimu CAT). Kompletność ustala, czy taka sama
liczba rekordów jest w obu komponentach. Dokładność
danych oznacza zweryfikowanie, czy dane są
przetransportowane w niezmienionej postaci pomiędzy
różnymi komponentami oprogramowania. Aktualność
danych to sprawdzenie, czy odpowiedni zbiór danych
spłynął na czas. Analiza danych wymaga dedykowanych
zespołów technicznych (dostarczenie infrastruktury
programowej) oraz biznesowych (rozwiązanie problemów z
jakością danych).
Komputer w wytwarzaniu
oprogramowania
Aby wyprodukować oprogramowanie, potrzebujemy
zespołu ludzi, wiedzy na temat samego wytwarzania oraz
infrastruktury sprzętowej i programowej. Zasoby ludzkie i
bazę wiedzy na temat procesu omówimy w kolejnych
rozdziałach. Tutaj skupmy się na infrastrukturze sprzętowej
i programowej.
Infrastruktura sprzętowa w wytwarzaniu oprogramowania
to każdy sprzęt, który jest potrzebny do wytworzenia i
przetestowania oprogramowania. Wytworzenie
oprogramowania zazwyczaj odbywa się na komputerach
osobistych lub stacjach roboczych zbliżonych do
regularnych komputerów, używanych przez zwykłych
użytkowników. Różnice zaczynają się w możliwościach
technicznych sprzętu. Z racji konieczności uruchamiania
różnych komponentów zespół techniczny dysponuje
większymi zasobami od przeciętnego użytkownika (szybszy,
z większą liczbą wątków procesor, więcej pamięci
operacyjnej lub dyskowej), czasem zwielokrotnionymi.
Zespół techniczny spędza większość dnia roboczego przed
komputerem, stąd szczególnie istotne są komfort i
ergonomia pracy. Jeżeli oprogramowanie wymaga do
działania szczególnego sprzętu, to także powinien się on
znaleźć na wyposażeniu. Jeżeli oprogramowanie działa na
urządzeniach mobilnych (tablety i telefony), to różne wersje
tych urządzeń znajdują się one na biurkach zespołu. Jeżeli
oprogramowanie operuje w określonym środowisku lub
branży (np. sektorze automotive), to w biurze firmy
wytwarzającej oprogramowanie możemy zobaczyć motor
lub część pojazdu. Poza tym bardzo często oprogramowanie
wymaga (zwykle niewidocznych przy biurkach) serwerów,
na których poszczególne komponenty działają. Z racji
różnic w posiadanych zasobach mogą zdarzyć się różnice w
zachowaniu wytwarzanego oprogramowania.
Oprogramowanie powstaje przez pisanie plików kodu
źródłowego i następnie przetworzenie ich do postaci
„rozumianej” przez komputer. Kod źródłowy powstaje w
dedykowanych edytorach, a czasem całych środowiskach
wytwarzania oprogramowania (np. Microsoft Visual Studio,
Eclipse lub JetBrains WebStorm). Zintegrowane środowisko
wytwarzania (ang. Integrated Development Environment,
IDE) oprogramowania ma wbudowany nie tylko edytor, ale
także narzędzia do budowania (przetwarzania do postaci
„rozumianej” przez maszynę), testowania, uruchamiania i
instalowania oprogramowania. Edytor kodu źródłowego ma
szereg przewag nad popularnymi narzędziami do edycji
tekstu. Przeciętny użytkownik może korzystać z notatnika
lub edytora z pakietu oprogramowania biurowego. Od
edytora kodu źródłowego oczekuje się innych cech. Jednym
z atutów jest kolorowanie odpowiednich symboli zgodnie z
zasadami danego języka programowania. Stąd na ekranach
zespołu technicznego możemy zobaczyć ciąg znaków
pokolorowanych wedle preferencji (np. w stylu znanym z
filmu „Matrix”). Budowanie kodu składa się z przetworzenia
kodu źródłowego i plików konfiguracji oraz
wyprodukowania „paczki” pozwalającej na uruchomienie i
przetestowanie. Na przetwarzanie może składać się
automatyczne pobranie innych komponentów
oprogramowania, kompilacja i przygotowanie wersji
instalacyjnych. Przez kompilację rozumiemy przetworzenie
tekstu wyrażonego w języku programowania na język
rozumiany przez komputery. Testowanie pozwala na
uruchomienie części programów niezależnie i sprawdzenie
różnych scenariuszy, często niedostępnych dla końcowego
użytkownika.
Kod napisany przez zespół techniczny musi być utrwalany.
Aby zespół techniczny mógł wymieniać się zmianami
wprowadzonymi we wspólnym kodzie, kod źródłowy jest
składowany w repozytorium kodu. Repozytorium kodu
pozwala na śledzenie najdrobniejszych zmian, ich czasu
oraz autora. Repozytoria mogą być scentralizowane (np.
SVN lub CVS) lub rozproszone (np. Git). Istotnym zadaniem
w pracy nad wspólnym repozytorium jest łączenie zmian
(ang. merge). Scentralizowane składowanie kodu
źródłowego pozwala na sprawdzenie go po każdej zmianie
(czy nikt nie popsuł oprogramowania swoją zmianą). Do
sprawdzenia oprogramowania w repozytorium służą
zautomatyzowane narzędzia, które pobierają kod źródłowy,
a następnie próbują zbudować i przetestować
oprogramowanie. Istnieje oprogramowanie pozwalające na
ciągłą pracę rozwojową (ang. continuous development, CD)
i ciągłą weryfikację integracji pomiędzy komponentami
(ang. continuous integration, CI). Istnienie takich narzędzi
pozwala na wspólną pracę rozproszonych zespołów
programistów. Ktoś z zespołu może pracować w biurze, ktoś
inny z domu, a ktoś jeszcze inny z odległego, egzotycznego
kraju. Zapewnienie jakości oprogramowania polega na
częstej wymianie kodu źródłowego (pobieranie zmian
innych i udostępnianie swoich zmian), tworzeniu testów i
regularnym procesie budowania i testowania. Typowymi
środowiskami CD i CI są JetBrains TeamCity, Bamboo,
Jenkins czy Travis CI. Omawiane oprogramowanie CD i CI
działa zazwyczaj na dedykowanych serwerach, często
uruchamia się automatycznie po każdej dokonanej zmianie
lub na żądanie członka zespołu technicznego.
Działanie oprogramowania
Kiedy chcemy przedostać się z jednego miejsca w mieście
do drugiego, mamy do wyboru komunikację publiczną,
taksówkę, własne auto, rower lub możemy iść pieszo. Przy
założeniu istnienia rozbudowanej sieci komunikacji
publicznej mamy wybór pomiędzy autobusem, metrem,
tramwajem lub kolejką miejską. Kiedy wybierzemy autobus
konkretnej linii, możemy udać się na przystanek i dojechać
na miejsce. W większości wypadków nie zastanawiamy się
nad rodzajem paliwa w autobusie. Kierowca zazwyczaj wie,
czy jedzie pojazdem spalinowym na benzynę lub diesel,
LNG, czy jest to pojazd elektryczny. Zapewne ma także
świadomość mocy i ogólnych parametrów pracy silnika. Do
codziennej jazdy raczej nie jest mu potrzebna wiedza na
temat konkretnych aspektów dotyczących procesów
spalania mieszanki w silniku (w przypadku silników
spalinowych). Mechanik silnikowy zazwyczaj potrzebuje już
przynajmniej podstawowej wiedzy na temat zasad pracy
konkretnego silnika. Idąc dalej, inżynier pracujący nad
danym silnikiem nie tylko zna ogólne zasady, ale także
konkretne parametry, jak choćby wielkość zaworu ssącego.
Inny inżynier z kolei może wiedzieć, na podstawie jakich
obliczeń wyznaczono taką, a nie inną wielkość tego zaworu.
Począwszy od kierowcy, każdy wspominany wie więcej o
danym pojeździe i jego silniku. Kierowca nie wie tego, co
wie inżynier odpowiedzialny za zawory, jednocześnie nie
przeszkadza to mu w poprawnym realizowaniu swoich
zadań związanych z przewozem osób i korzystaniem z
maszyny. Patrząc z perspektywy posiadania wiedzy na
temat funkcjonowania autobusu, kierowca ma wiedzę
bardziej ogólną i szeroką, a inżynier odpowiedzialny za
omawiane zawory bardziej specyficzną i w węższym
zakresie.
Przykład transportu obrazuje zjawisko generalizacji i
abstrakcji. Im dalej jesteśmy od szczegółów realizacji
konkretnego aspektu pracy naszego obiektu analizy, tym
mniej wiedzy potrzebujemy na temat jego działania. Przy
czym nieprawidłowe działanie jednego z elementów może
mieć negatywny wpływ na pracę całego oprogramowania i
naszą pracę. Przenosząc transportową dyskusję na grunt
komputerów, zauważymy różne aspekty funkcjonowania
sprzętu i oprogramowania. Możemy znaleźć inżynierów,
którzy są specjalistami w konstruowaniu procesorów.
Równie dobrze możemy znaleźć inżynierów elektroników
odpowiedzialnych za integrację procesora z resztą
komponentów projektowanej platformy pracy (np.
elektroniczne aspekty projektowania elektronicznego
układu z procesorem). Podobnie jest z oprogramowaniem.
Poniżej omówimy warstwy szczegółowości w
oprogramowaniu.
Pomiędzy realizacją sprzętową i programową musimy
gdzieś postawić granicę. Zazwyczaj dla komputerów i
procesorów ogólnego przeznaczenia tę granicę ustawiamy
na poziomie asemblera. Jest to bardzo prosty język, z
relatywnie prostą gramatyką, który zawiera listę instrukcji i
ich operandy. Każda instrukcja dla procesora ma swój
identyfikator (tzw. mnemonik), który reprezentuje
konkretną instrukcję w kodzie binarnym. Rola narzędzia o
nazwie kompilator polega na przetłumaczeniu ciągów
znaków na odpowiedniki w postaci binarnej, którą rozumie
odpowiedni procesor. Operand to parametry dla danej
instrukcji (analogicznie do argumentów dla funkcji w
matematyce). Istotnym aspektem jest tutaj rozdział na
dane, które są dostępne w ramach rejestrów procesora
(„przeniesione do procesora”), i te dostępne w pamięci
operacyjnej. Rejestry to specjalne komórki pamięci w
procesorze, które są dostępne do bardzo szybkich operacji.
Przykładowo: dodanie dwóch liczb będzie przebiegało
szybciej, gdy obie liczby będą zapamiętane w rejestrach
procesora, zaś wolniej, gdy jedna z nich będzie w pamięci
operacyjnej. Już sam opis brzmi skomplikowanie, a kontrola
tego w rozbudowanym oprogramowaniu także nie należy do
prostych zadań. Dlatego asembler w oprogramowaniu
został zastąpiony językami programowania wyższego
poziomu.
Dzięki temu programista, pisząc kod dodający dwie liczby,
może skorzystać z narzędzi, które za niego wygenerują kod
w asemblerze. Po dalszym przetwarzaniu kod ten będzie
mógł być uruchomiony przez procesor. Przykładowymi
językami programowania, które podlegają pod ten proces,
są języki C i C++. Oba języki są bardziej zaawansowane i
pozwalają na odseparowanie od aspektów pracy asemblera.
Programista nadal posiada pełną kontrolę nad pracą
komputera, ale nie jest zależny od asemblera konkretnego
typu procesora. Jeden kod źródłowy może być
skompilowany zarówno dla procesora Intel Pentium, jak i
Qualcomm Snapdragon.
Pełna kontrola to duże możliwości, ale także duża
odpowiedzialność. Szczególna odpowiedzialność ciąży na
programiście, aby efektywnie i bezpiecznie zarządzał
pamięcią swojego programu. Aspekty zarządzania sprzętem
możemy przerzucić na odpowiedzialność systemu
operacyjnego. W związku ze skomplikowaniem tworzenia
oprogramowania w językach C i C++ powstały bardziej
złożone narzędzia, które pomagają programiście w
zarządzaniu pamięcią i bezpieczeństwem programu.
Przykładowym narzędziem jest maszyna wirtualna (ang.
virtual machine). W odniesieniu do pamięci programu
wirtualna maszyna posiada wbudowany mechanizm
„wyrzucenia śmieci” (ang. garbage collector). Mechanizm
odśmiecania przegląda obszary pamięci zaalokowane przez
programistę, a następnie zwalnia je, gdy są nieużywane. W
celu zapewnienia bezpieczeństwa na wielu poziomach
maszyna wirtualna wprowadza szereg sprawdzeń za
programistę (np. czy dla danej operacji typ danych jest
odpowiedni). Maszyna wirtualna jest zależna od systemu
operacyjnego i z perspektywy programisty opakowuje go,
tym samym przesłaniając bardziej skomplikowane aspekty.
Poniżej zamieszczony został kod źródłowy systemu
operacyjnego Microsoft DOS. To fragment pliku
SYSINIT.ASM z kodem w języku programowania asembler.
Kod dostępny jest pod adresem
https://github.com/Microsoft/MS-DOS/.
TITLE BIOS SYSTEM INITIALIZATION
FALSE EQU 0
TRUE EQU NOT FALSE
IF IBMVER OR IBMJAPVER
NOEXEC EQU TRUE
ELSE
NOEXEC EQU FALSE
ENDIF
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.RuleSet;
/∗∗
∗ <p><strong>RuleSet</strong> for processing the
contents of a
∗ Host definition element. This <code>RuleSet</code>
does NOT include
∗ any rules for nested Context which should be added via
instances of
∗ <code>ContextRuleSet</code>.</p>
∗
∗ @author Craig R. McClanahan
∗/
public class HostRuleSet implements RuleSet {
// --------------------------------------------------
--- Instance Variables
/∗∗
∗ The matching pattern prefix to use for recognizing
our elements.
∗/
protected final String prefix;
// --------------------------------------------------
---------- Constructor
/∗∗
∗ Construct an instance of this <code>RuleSet</code>
with the default
∗ matching pattern prefix.
∗/
public HostRuleSet() {
this(“”);
}
A teraz kod źródłowy biblioteki GGPlot2. To fragment pliku
margins.R z kodem w języku programowania R. Kod
dostępny jest pod adresem
https://github.com/tidyverse/ggplot2/.
#’ @param t,r,b,l Dimensions of each margin. (To remember
order, think trouble).
#’ @param unit Default units of dimensions. Defaults to
“pt” so it
#’ can be most easily scaled with the text.
#’ @rdname element
#’ @export
margin <- function(t = 0, r = 0, b = 0, l = 0, unit =
“pt”) {
structure(unit(c(t, r, b, l), unit), class =
c(“margin”, ”unit”))
}
Z biegiem lat powstało wiele maszyn wirtualnych, np. Java
Virtual Machine lub Microsoft .NET (.NET czytamy jako
„dot net”). Oczywiście można skompilować niemal dowolny
język do postaci, którą rozumie wybrana wirtualna
maszyna. Niemniej dla obu popularnych maszyn powstały
nowe języki programowania, odpowiednio Java dla Java
Virtual Machine i C# (wymawiamy jako „si szarp”) dla
Microsoft .NET. Innym rodzajem wirtualnych maszyn są
silniki języka programowania JavaScript wbudowane w
przeglądarki internetowe. Silnik JavaScript (np. V8) oferuje
analogiczne możliwości w postaci bezpiecznego wykonania
kodu w wyizolowanym środowisku. Zarówno silnik
JavaScript, jak i wirtualne maszyny dla Javy lub C# są
napisane w językach C i C++. Warto pamiętać, że nie ma
konieczności uczenia się języka C, żeby efektywnie
programować w JavaScripcie. Co więcej, kod źródłowy
napisany przez początkującego programistę w C może być
mniej efektywny niż analogiczny kod napisany w
JavaScripcie. Zjawisko to jest spowodowane potężnym
zaawansowaniem wirtualnych maszyn, także w obszarze
szeroko rozumianej wydajności. Im dalej oddalamy się od
sprzętowej realizacji poszczególnych instrukcji, tym
bardziej wchodzimy na kolejne poziomy lub generacje
języków programowania. Możemy znaleźć analogię do
pięter w budynku. Na potrzeby tej książki możemy przyjąć,
że parter to asembler z reprezentacją kodu w postaci
binarnej, piętro pierwsze to język C, drugie to Java, trzecie
to język R. Kolejne języki programowania zbudowane w
oparciu o nie stanowiłyby kolejne piętra, tym samym będąc
językami kolejnych generacji.
Programiści języków wyższych poziomów operują w innym
świecie bytów niż programiści niższych poziomów. Dla nich
nie ma znaczenia, czy wartość jest w pamięci operacyjnej,
czy w rejestrze procesora. Istotniejsze jest, czy wartość jest
stała, czy zmienna i, ewentualnie, czy operacja jest możliwa
na różnych typach danych. Co więcej, każdy ze
wspominanych języków programowania przez lata doczekał
się wielu wersji i całego ekosystemu narzędzi oraz
rozszerzeń. Na przykład język JavaScript ma nadbudowę w
postaci języków TypeScript i CoffeeScript, które podczas
„kompilacji” są przetwarzane do postaci JavaScript. Z kolei
kod źródłowy JavaScript jest uruchomiany przez silnik,
który jest kontrolowany przez system operacyjny. Odległość
pomiędzy TypeScript i CoffeeScript a asemblerem jest
skomplikowana i niejednoznaczna. Transformacje do
JavaScriptu wykonywane są przez środowisko napisane w
języku C, które opiera się na systemie operacyjnym także
napisanym w języku C i asemblerze. Programiści obu
światów mogą operować na różnych bytach, mieć inne cele
oraz całkowicie inną efektywność pracy. Za miarę
efektywności możemy przyjąć liczbę napisanych linijek
kodu źródłowego na godzinę czy wartość biznesową dodaną
dla końcowego użytkownika za pomocą zbioru linijek kodu
źródłowego. Co nie znaczy, że którykolwiek z programistów
jest gorszy. Szerzej różne aspekty programowania
omówimy w rozdziale „Programowanie i programiści”.
Zwinne podejście
Zanim przejdziemy do opisania zwinnego podejścia,
skupmy się na chwilę na historii wytwarzania
oprogramowania. Kilkadziesiąt lat temu, gdy powstawały
pierwsze duże systemy oprogramowania, nie było aż tylu
osób zaangażowanych w proces wytwarzania
oprogramowania. Fazy następowały kolejno, a wiedza
przepływała niczym woda w wodospadzie (stąd nazwa w
języku angielskim waterfall). Co więcej, każda z faz trwała
od kilku tygodni do kilku lat oraz mogła być realizowana
przez inny zespół3. Odległość w czasie pomiędzy etapem
zbierania wymagań a faktycznym ich dostarczeniem była
duża, mijały miesiące lub nawet lata. W tym czasie
zmieniało się wiele: ludzie, ich potrzeby, otoczenie
organizacji oraz sama technologia. Nowo dostarczone
oprogramowanie nie dotrzymywało tempa zmieniającej się
rzeczywistości. Ze względu na duże nakłady środków
wymaganych na zakup technologii i wytworzenie
oprogramowania problem był dostrzegany przez wielu
badaczy i praktyków. Na przestrzeni lat jako usprawnienie
zaproponowano wiele modeli wytwarzania
oprogramowania, które mają na celu usprawnić cały
proces. Z uwagi na treść tego rozdziału najbardziej istotny
jest model iteracyjny. W modelu iteracyjnym
oprogramowanie powstaje w iteracjach, z których każda ma
osobny zbiór wymagań, czas implementacji, testów i
wdrożenia. Mając ten kontekst, możemy przeskoczyć kilka
dekad do czasów bardziej współczesnych.
W 2011 roku grupa wielu utalentowanych praktyków
sformułowała zbiór zasad wytwarzania oprogramowania w
postaci „Manifestu programowania zwinnego”. Manifest,
początkowo adresowany głównie do środowiska
programistów, został sformalizowany i przyjęty przez
szersze grono odbiorców oprogramowania. Coraz częściej
można spotkać stosowanie technik wywodzących się ze
zwinnego podejścia w projektach i procesach
nieskomputeryzowanych. W tym rozdziale przybliżymy sam
manifest oraz praktyczne dwa przykłady zastosowania
zwinnego podejścia.
Manifest zwinnego oprogramowania (ang. Agile Manifesto)
w polskim tłumaczeniu brzmi następująco (pisownia i
łamanie linii jak w oryginale)4:
„Odkrywamy nowe metody programowania dzięki
praktyce w programowaniu
i wspieraniu w nim innych.
W wyniku naszej pracy zaczęliśmy bardziej cenić:
Ludzi i interakcje od procesów i narzędzi
Działające oprogramowanie od szczegółowej
dokumentacji
Współpracę z klientem od negocjacji umów
Reagowanie na zmiany od realizacji założonego planu.
Oznacza to, że elementy wypisane po prawej są
wartościowe,
ale większą wartość mają dla nas te, które wypisano po
lewej”.
Omówmy kilka linijek tekstu i spróbujmy zastanowić się
nad ich sensem. „Ludzi i interakcje od procesów i narzędzi”
— w centrum tworzenia oprogramowania są ludzie oraz ich
interakcje (spotkania, rozmowy, wspólne warsztaty).
Rozmowa (najlepiej twarzą w twarz) pozwala na lepsze
zrozumienie intencji niż najlepsza dokumentacja.
„Działające oprogramowanie od szczegółowej
dokumentacji” mówi nam, że żadna dokumentacja nie
zastąpi działającego oprogramowania. Jak głosi
powiedzenie, papier przyjmie wszystko. Nie każde
doskonale udokumentowane wymaganie można zrealizować
za pomocą oprogramowania, a przynajmniej
oprogramowania, które działa poprawnie. Jednocześnie ten
fragment nie zwalnia nas z prowadzenia dokumentacji,
nigdzie nie jest zaproponowane odrzucenie czy też brak
dokumentacji. „Współpracę z klientem od negocjacji umów”
— tutaj autorzy odwołują się do problemów z innymi
podejściami wytwarzania oprogramowania. Ścisłe
przestrzeganie dokumentacji często wiedzie do
niepoprawnego działania oprogramowania. W przypadku
modelu finansowania zespołu implementacyjnego za
pomocą określonej ceny (ang. fixed-price) czas zespołu
może być przeznaczony na poprawę działania lub tworzenie
nowych funkcji. Brakujący czas należałoby dokupić i tutaj
mamy wspominane „negocjowanie umów”. „Reagowanie na
zmiany od realizacji założonego planu” polega na
dostosowaniu procesu wytwarzania oprogramowania do
zmieniającego się otoczenia i priorytetów. Zdarza się, że
zamówione oprogramowanie musi być dostosowane do
zmieniających się okoliczności (np. reakcja konkurencji w
biznesie), więc nie ma sensu brnąć w nierealne plany, a
bardziej korzystne jest zaakceptowanie zmian i
dostosowanie oprogramowania do aktualnych potrzeb.
Dostosowywanie oprogramowania do aktualnych potrzeb
nie powinno odbywać się przez obniżanie jakości
oprogramowania. Często zdarza się tak, że presja
wywierana przez interesariuszy (np. użytkowników
końcowych, menedżerów i decydentów) powoduje
koncentrowanie prac zespołu technicznego wokół nowych
funkcji oprogramowania zamiast wokół usprawnień i
zapewnienia jakości istniejących. Zaniedbanie jakości i
nierozwiązywanie problemów powoduje narastanie pracy
koniecznej do realizacji w przyszłości. Zjawisko tworzenia
nowych funkcji kosztem jakości oprogramowania nazywamy
długiem technicznym (ang. technical dept). Jak każdy dług,
tak i dług techniczny należy kiedyś spłacić. Akumulowanie
długu technicznego w nieskończoność może opóźnić lub
całkowicie uniemożliwić wprowadzenie nowych funkcji w
przyszłości. To z kolei przyśpiesza proces starzenia się
oprogramowania (przestarzałe oprogramowanie, ang.
legacy software). Zamiast zaciągać dług, najlepiej działać
prewencyjnie i zawczasu monitorować jakość
oprogramowania. Jeżeli nowe funkcje wymagają dużej
ingerencji w istniejące oprogramowanie, należy zaplanować
dodatkowe zasoby na zmiany w istniejących obszarach
oprogramowania. Często spotykanym zadaniem w takich
okolicznościach jest refaktoryzacja (ang. refactoring), czyli
zmiana mająca na celu utrzymanie jakości istniejącego
oprogramowania. Ze względu na pojawianie się takiego
zjawiska w wielu projektach oprogramowania rozpoczęto
dyskusję nad aktualizacją manifestu agile o punkty
dotyczące długu technicznego5.
Przejdźmy do sposobów wdrożenia manifestu w praktyce.
Poniżej przedstawię dwa z wielu dostępnych podejść.
Codzienna praktyka prowadzenia projektu wytwarzania
oprogramowania polega na doborze zbioru technik
(narzędzi) odpowiednich do danego otoczenia. Część
technik sprawdza się lepiej w jednych okolicznościach,
część w innych. Omówmy dwa podejścia mające
zastosowanie w różnych sytuacjach: Scrum i Kanban.
Mając zespół techniczny skupiający się głównie na fazie
implementacji i wstępnie wyspecyfikowane zadania dla
tego zespołu, możemy spróbować zastosować podejście
Scrum. Scrum (czytaj „skram”) to zbiór narzędzi do
realizacji podejścia zwinnego w środowisku wytwarzania
oprogramowania. Scrum wyróżnia trzy role aktywnie
uczestniczące w procesie wytwarzania. Pierwszą z ról jest
właściciel produktu (ang. Product Owner), który ma na celu
prowadzenie produktu oprogramowania, począwszy od
aspektów komunikacji z interesariuszami, przez
komunikację i uzgodnienia z zespołem technicznym,
negocjowanie zakresu, planu i priorytetu pracy, po
planowanie pracy na przyszłość (przygotowywanie zadań).
Drugą rolą jest zespół techniczny, który odpowiada za
wykonanie zadań. W wyidealizowanym podejściu cały
zespół techniczny stanowią programiści, z których każdy
może zastąpić każdego w dowolnym momencie. W praktyce
w zespole technicznym są różne specjalizacje, np.
programiści lub testerzy. Trzecią rolą jest rola mistrza
ceremonii (ang. Scrum Master)6. Jak nazwa wskazuje, ma
on za zadanie opiekę nad poprawnością procesu i
ceremonii, musi pomagać właścicielowi produktu
dostarczyć produkt, pomagać zespołowi w aspektach
organizacyjnych i komunikacyjnych. W praktyce zdarza się,
że role często są łączone. Właścicielem produktu często jest
doświadczony analityk projektu, a mistrzem ceremonii —
PM lub tester.
Przejdźmy do samych ceremonii i opiszmy te najważniejsze.
Do najpopularniejszych możemy zaliczyć planowanie,
codzienne spotkania, demonstracje i retrospekcje. Przed
przystąpieniem do pracy ustala się czas trwania sprintu.
Sprint to ograniczona czasem iteracja wytwarzania
oprogramowania. Sprinty zazwyczaj trwają od jednego do
czterech tygodni. W ramach zespołu, czyli z udziałem
trzech wspomnianych ról, planuje się zakres zadań na
nadchodzący czas. Planowanie polega na przejściu przez
zadania gotowe do realizacji (ang. definition of ready) przez
zespół i wycenę, jak bardzo skomplikowane jest zadanie.
Zadania muszą być na tyle małe, aby mogły być
zrealizowane przez jedną osobę w jednym sprincie, większe
zadania muszą być podzielone na mniejsze. Co istotne,
mierzy się poziom skomplikowania zadania, a nie czas
poświęcony na jego realizację. Czas realizacji zadania jest
zależny od poziomu wiedzy i doświadczenia danego członka
zespołu technicznego (im bardziej doświadczona osoba, tym
szybciej zrealizuje dane zadanie). Proces mierzenia określa
się jako estymację, czyli szacowanie poziomu
skompilowania zadania. Na wynik szacowania mają wpływ
wszystkie czynności potrzebne do realizacji zadania
(wytworzenie nowych funkcji, konieczna modyfikacja
istniejących, testowanie, przygotowanie do wdrożenia).
Miary są różne, punktowe lub analogiczne do rozmiaru
koszulek (S, M, L, XL). Po kilku sprintach zakończonych
sukcesem można próbować przeliczyć statystycznie, ile
zadanie z określoną miarą zajmuje czasu. W trakcie wyceny
właściciel produktu odpowiada na wszystkie pytania
dotyczące zadań. W praktyce często zdarza się tak, że
właściciel produktu lub interesariusze wymagają od zespołu
technicznego konkretnej wyceny w czasie (np. czy da się
zrealizować w dwa tygodnie?). Takie podejście stoi w
konflikcie z dobrymi praktykami wycen.
Kiedy zespół techniczny wyceni zadania, następuje
uzgodnienie zakresu sprintu (jednego lub kilku kolejnych) .
Przez zakres rozumiemy zbiór zadań wraz z określonymi
priorytetami, nad którymi zespół techniczny będzie
pracował. Zespół techniczny powinien pracować tylko nad
uzgodnionymi zadaniami, a gdy skończy przedwcześnie,
powinien pracować nad kolejnymi przygotowanymi
zadaniami z kolejki oczekujących (ang. backlog). Jeżeli
okazałoby się, że podczas trwania sprintu zmieniają się
wymagania dla zadania, to wtedy zostaje ono usunięte ze
sprintu. Gdy więcej zadań jest zmienionych, wtedy sprint
należy przerwać i uzgodnić zadania z właścicielem
produktu. Kiedy zbiór zadań zostaje uzgodniony, następuje
start sprintu. W trakcie sprintu zespół ma za zadanie
zrealizować wszystkie przypisane zadania. Panuje prosta
zasada, że każdy, kto skończy swoje zadanie, bierze kolejne
z kolejki oczekujących w danym sprincie.
Przez skończenie (ang. definition of done) rozumiemy
zrealizowanie i przetestowanie wszystkich aspektów
technicznych zadania (w zależności od ustaleń z
właścicielem produktu — napisanie kodu źródłowego,
napisanie dokumentacji w postaci komentarzy w kodzie
źródłowym, zmianę konfiguracji, przetestowanie,
wdrożenie).
Kolejną ceremonią są codzienne spotkania zespołu
technicznego i mistrza ceremonii (opcjonalnie z
właścicielem produktu). Codzienne spotkanie powinno być
krótkie. W trakcie spotkania każdy odpowiada na trzy
pytania: nad jakim zadaniem pracował poprzedniego dnia,
nad czym będzie pracował kolejnego dnia i czy doświadcza
jakichś blokad (ang. blocker) dla danego zadania (np.
niedostępność jednego z zależnych systemów). Rolą mistrza
ceremonii jest monitorowanie procesu (czy „małe” zadanie
nie zajmuje komuś zbyt wielu dni w porównaniu z innymi
zadaniami) i odnotowywanie blokad. Nad oboma
problemami mistrz ceremonii pracuje po spotkaniu.
Spotkania dzienne określane są z języka angielskiego jako
dosłownie „daily” lub „stand-up”. Spotkania typu „stand-
up” powinny odbywać się na stojąco. Jak wskazuje wielu
instruktorów podejścia Scrum, jest to pozycja mniej
komfortowa od pozycji siedzącej, stąd osoby zaangażowane
powinny mieć naturalną tendencję do skracania
wypowiedzi i dzielenia się jedynie istotnymi aspektami
pracy. Zaoszczędzony czas można poświęcić na rozwój
oprogramowania. Spotkania odbywają się w każdy dzień
roboczy zespołu technicznego podczas trwania sprintu.
Sprint kończy się demonstracją powstałego produktu,
oprogramowania dla interesariuszy. Sesja demonstracyjna
powinna kończyć się przyjęciem lub odrzuceniem pracy
zespołu (np. gdy podczas demonstracji oprogramowania
zostaną odkryte kompromitujące błędy). Jednocześnie
podczas demonstracji może zostać opisane, co wymaga
dalszych prac i jakie zadania w związku z tym muszą trafić
do kolejki oczekujących na realizację. Po jednym lub kilku
sprintach zespół może mieć spotkanie retrospektywne.
Podczas takiego spotkania omawiane są zrealizowane
sprinty z naciskiem na ocenę zespołu. Zostaje opisane, co
poszczególnym członkom zespołu technicznego podobało
się, co się nie podobało, co należy zmienić, a co utrzymać w
procesie wytwarzania oprogramowania. W podsumowaniu
spotkania retrospektywnego musi brać udział właściciel
produktu oraz mistrz ceremonii celem zaadresowania
punktów zgłoszonych przez zespół.
W przypadku dużej zmienności w zakresie zadań, gdy
trudno jest przewidzieć liczbę, rodzaj i priorytet zadań,
warto spróbować pracy w sposób ciągły. Zamiast
ograniczonych czasowo sprintów można mieć priorytety
uzgadniane z dnia na dzień. Same zadania można podzielić
na trzy grupy: oczekujące na realizację, w trakcie realizacji
i skończone. Takie podejście jest tożsame z podejściem
Kanban w tworzeniu oprogramowania. Zadania
zwizualizowane jako karteczki (fizyczne na ścianie lub w
elektronicznym systemie) łatwo pokazują, w jakiej grupie
jest duża liczba zadań. Celem tego podejścia jest
minimalizacja liczby zadań w grupie „w trakcie realizacji”
(ang. work-in-progress, WIP) i budowanie efektywności w
przejściu zadań z grupy „oczekujących na realizację” do
grupy „skończonych”. Podejście Kanban może mieć
zastosowanie w trakcie fazy utrzymania oprogramowania,
gdzie kolejne zadania spływają w nieokreślonych
momentach czasu.
Nic nie stoi na przeszkodzie, aby przy podejściu Kanban
stosować formułę codziennych spotkań, kiedy przechodzi
się przez zadania kolejnych członków zespołu. Warto także
spróbować zbadać barometr odczuć zespołu za pomocą
spotkania retrospektywnego. Zastosowanie konkretnej
techniki czy podejścia zależy od charakteru pracy oraz
sposobu organizacji.
Dokumenty wymagań
Kilka przykładów, jakie omówimy, to wymagania w postaci
dokumentu wymagań biznesowych, dokumentu specyfikacji
funkcjonalnej, dokumentu pól i atrybutów, dokumentu ról i
uprawnień, opisującego interfejs użytkownika czy
dokumentów projektów prowadzonych metodami zwinnymi
(podejście agile). Dokument wymagań biznesowych (ang.
Business Requirements Document, BRD) to zazwyczaj
dokument opisujący wysokopoziomowe wymagania dla
danego systemu czy komponentu oprogramowania. Jest to
dokument pisany tekstem zrozumiałym dla szerokiego
grona odbiorców (także, a nawet głównie ludzi
„nietechnicznych”). W zależności od stopnia
skomplikowania danego komponentu objętość takiego
dokumentu może być różna. Dokument powinien zawierać
powód powstania konkretnych wymagań (np. spadek
konkurencyjności firmy), cel (co chcemy osiągnąć),
scenariusze użycia lub scenariusze biznesowe, wszystkich
interesariuszy projektu (kto jest zainteresowany
powstaniem projektu oraz kto pośrednio lub bezpośrednio
bierze udział w tworzeniu rozwiązania), opis
przetwarzanych danych lub dokumentów, logikę i formuły
przetwarzania. Dokument wymagań biznesowych zazwyczaj
nie rozstrzyga o konkretnej technologii wykonania ani nie
zawiera wyczerpującej listy wszystkich wymagań,
natomiast może opisywać środowisko (np. inne komponenty
oprogramowania, które muszą być zintegrowane w ramach
rozwiązania). Tak jak dokument wymagań biznesowych
może być łatwy w odbiorze dla dowolnej osoby w
organizacji, tak może być niewystarczający do zbudowania
konkretnego rozwiązania (napisania oprogramowania przez
programistów). Aby uszczegółowić dokument wymagań
biznesowych, często tworzy się dokument specyfikacji
funkcjonalnej. Dokumentacja funkcjonalna (ang. Functional
Specification Document, FSD) dotyczy konkretnych
wymagań dla oczekiwanego rozwiązania. To także jest
dokument tekstowy, ale często zawiera załączniki. W
tekście istotne jest każde zdanie, stąd szczególnie
przydatna jest tutaj klasyfikacja MoSCoW. MoSCoW to
skrót powstały od kolejnych czasowników w języku
angielskim: must have (musi mieć), should (powinien mieć),
could (może mieć), won’t have (nie będzie mieć).
Klasyfikacja nadaje priorytety kolejnym opisywanym
wymaganiom, a jednocześnie je wymusza. Poza opisem
samego tekstu pomocne są załączniki, np. diagramy
przepływu prac (ang. workflow), wzory matematyczne dla
konkretnych wyliczeń, opis sytuacji wyjątkowych czy szkice
dla konkretnego rozwiązania. Dokument specyfikacji
funkcjonalnej może zawierać wymagania funkcjonalne i
niefunkcjonalne. Wymagania funkcjonalne odpowiadają na
pytanie, co oprogramowanie robi (np. oprogramowanie
musi mieć funkcję wyszukiwania danych). Wymagania
niefunkcjonalne odpowiadają na pytanie, jak
oprogramowanie coś robi (np. jaki jest dopuszczalny czas
czekania na pojawienie się wyników wyszukiwania na
ekranie lub ile maksymalnie dokumentów pojazdów
oprogramowanie ma trzymać przez cały okres jego użycia).
Jednym z samodzielnych dokumentów (często będącym
załącznikiem do dokumentu specyfikacji funkcjonalnej) jest
dokument opisujący pola i atrybuty. Dokument pól i
atrybutów jest zaprezentowany w wielkiej tabeli opisującej
konkretne dane występujące w oprogramowaniu, gdzie
wiersz jest konkretną informacją, a kolumny kolejnymi
atrybutami. Dane mają nazwę (np. rok produkcji), typ (np.
liczba, tekst, data), ograniczenia (np. liczba z przedziału
1900 – 2100), źródło pochodzenia (np. wprowadzane przez
człowieka lub pobierane z innego systemu), model (np.
model danych dla obiektu pojazd), komunikaty błędów dla
braku lub nieprawidłowych danych. W przypadku
skomplikowanych systemów operujących na danych
poszczególne pola można rozbić na osobne dokumenty
wymagań danych (ang. Data Requirements). Dokument ról i
uprawnień (ang. Roles and Entitlements) to dokument
opisujący wszystkie role w systemie oraz kto i do czego ma
dostęp. Jako rolę rozumiemy grupy użytkowników (np.
odbiorca danych, autor danych, inny system czy też
administrator danych). Uprawnienia to lista dostępnych
funkcji (np. ekran wprowadzania danych) — w zależności
od systemu może być to lista funkcji lub funkcje wraz z
zakresem uprawnień (np. uprawnienia do odczytu danych,
uprawnienia do utworzenia nowych danych, uprawnienia
do modyfikowania istniejących danych). Dokumenty
opisujące interfejs użytkownika to najczęściej dokumenty
graficzne reprezentujące różne poziomy szczegółowości
interfejsu użytkownika. Zaczynając od tych najbardziej
ogólnych, które mogą być połączeniem komiksowej
reprezentacji graficznej wizualizacji scenariuszy użycia
(ang. storyboard), przez różne prototypy, po mapy powiązań
nawigacji pomiędzy elementami (ang. site map). Skupmy
się na prototypach, które mogą mieć wiele postaci.
Począwszy od najbardziej ogólnych zdigitalizowanych
prototypów papierowych, przez bardziej lub mniej
szczegółowe szkielety (ang. wireframe), wygenerowane
obrazy gotowego interfejsu użytkownika (ang. mock-up), po
dokumentację wideo przejść pomiędzy elementami
interfejsu.
Osobną kategorię stanowi dokumentacja przygotowywana
dla projektów oprogramowania realizowanych w podejściu
zwinnym (ang. agile software development). Zamiast
„klasycznych” dokumentów wymagań biznesowych lub
funkcjonalnych przygotowuje się zbiór mniejszych
dokumentów wymagań. Często możemy napotkać
wymagania w postaci „historyjki użytkownika” lub
kryteriów akceptacyjnych. Warto nadmienić, że istnieją
także alternatywne rozwiązania. Poniżej omówimy pokrótce
po jednym przykładzie. „Historyjka użytkownika” (ang.
User story) to wymaganie wyrażone w postaci krótkiego
zdania ze stałymi elementami: Jako [rola użytkownika] chcę
[funkcja systemu], bo [wartość lub zaspokojona potrzeba]
(w języku angielskim szablon wygląda „As a [role], I want
[features] so that [reason]”). W każde ze zmiennych pól
należy wpisać konkretny element, np. „Jako autor danych
chcę wprowadzić dane samochodu, bo chciałbym je
utrwalić”. Takie podejście wymusza wcześniejsze
zaplanowanie ról w systemie (role i uprawnienia) oraz
wartości (utrwalenie danych), jaką wnosi dana funkcja
(wprowadzenie danych samochodu). Każdy zaangażowany
w przedsięwzięcie może tworzyć własne historyjki.
Historyjki mogą wnosić wartość zarówno biznesową
(wymaganie biznesowe) , jak również techniczną
(wymaganie techniczne). Na podstawie jednego zdania
trudno jednoznacznie określić, kiedy można uznać, że dana
historyjka jest zrealizowana (np. czy utrwalenie to znaczy
zapisanie w pamięci lokalnego urządzenia, czy może po
drugiej stronie internetowych łączy?), stąd dodatkowo
korzysta się z wymagań w postaci kryteriów
akceptacyjnych. Kryteria akceptacyjne (ang. Acceptance
Criteria) pozwalają skonkretyzować wymagania przez
opisanie kolejnych kroków, takich jak stan wejściowy,
podejmowana akcja i oczekiwany rezultat. Kryteria
akceptacyjne dokumentuje się analogicznie do historyjek —
za pomocą wzorca zdania: „Dla [stan wejściowy], kiedy
[podejmowana akcja], wtedy [oczekiwany rezultat]” (w
języku angielskim „Given [state] when [action is performed]
then [something happens]”). Dla jednej historyjki istnieje
jedno lub wiele kryteriów akceptacyjnych. Powinny być
napisane jednoznacznie zarówno dla użytkowników, jak i
zespołu technicznego. Wymaganie uznaje się za
zrealizowane, kiedy wszystkie kryteria akceptacyjne z nim
związane są spełnione. Jednym z alternatywnych podejść do
historyjek, utrzymanym w duchu „szczupłości” (ang. lean),
jest podejście zaczerpnięte z książki Jasona Little’a7, gdzie
wymagania są sformułowane w postaci zdania „Stawiamy
hipotezę, że dzięki [zrealizowaniu zmiany], uda nam się
[rozwiązać problem], co zapewni [korzyści] mierzone przez
[miarę]” (w języku angielskim „We hypothyse by
[implementing this change] we will [solve this problem]
which will have [those benefits] as measured by [this
measurement]”). Zaproponowana metoda jest nieco
bardziej złożona niż historyjki i kryteria akceptacyjne, ale
jednocześnie wymusza stosowanie mierzalnych miar
osiągnięcia sukcesu.
Praca nad nie swoimi
wymaganiami
Nie zawsze jest tak, że zaczynamy gromadzenie wymagań
od zera. Często przychodzi nam pracować z wymaganiami
przygotowanymi przez inne osoby (osoby wcześniej
pracujące nad oprogramowaniem albo firmy trzecie
zaangażowane w realizację). Przed przystąpieniem do
pracy warto przeanalizować umowę, jaka obowiązuje w
danym projekcie, oraz zapoznać się z rodzajami
gromadzonych dokumentów. Kiedy wiemy, z jakim
projektem mamy do czynienia, zawsze możemy (a w
niektórych okolicznościach powinniśmy) zbadać jakość
gromadzonych wymagań. Jakość można mierzyć na wiele
sposobów, poniżej przejdziemy przez kilka technik, które
mogą okazać się przydatne w codziennej pracy. Omówimy
terminologię, rodzaje dokumentów oraz ich zawartość.
Kiedy czytamy wymagania, to warto zacząć od tych
najbardziej ogólnych, a następnie schodzić do bardziej
szczegółowych. Posiadanie szerszego i bardziej ogólnego
obrazu pozwala na lepsze zrozumienie potrzeb
oprogramowania oraz jego otoczenia. W dokumentach
wymagań wszystkie terminy powinny być zdefiniowane. Nie
można zakładać, że każda osoba bez przygotowania
rozwikła skomplikowane skróty i terminy specyficzne dla
danego problemu. Każdy niejednoznaczny, niezdefiniowany
termin wymaga doprecyzowania (np. dopytania podczas
warsztatów). To wydłuża czas pełnego zrozumienia projektu
i wdrożenia się w niego, zwłaszcza gdy autorzy mogą
pracować już nad innym przedsięwzięciem. Sklasyfikowane
dokumenty wymagań powinny zawierać odpowiednią i
wcześniej zdefiniowaną nomenklaturę. W dokumencie
wymagań biznesowych nie powinno poruszać się zawiłości
rozwiązania, zaś każdy dokument powinien przestrzegać
powszechnie akceptowanej struktury (np. odpowiednio
sformułowane zdania historyjek użytkownika lub kryteriów
akceptacyjnych). Kiedy dochodzi do weryfikacji treści
wymagań, będzie to sprawdzenie, czy wszystkie elementy
są na swoim miejscu i w poprawnej formie. Innym
zagadnieniem jest walidacja wymagań od strony
poprawności. Wracając do naszego samochodowego
przykładu, możemy sprawdzić, czy wymagania nie są
wzajemnie sprzeczne (np. czy w jednym miejscu nie
piszemy o wymaganym napędzie na jedną, a w innym na
dwie osie) oraz czy są wykonalne (np. auto z nadwoziem
typu kabriolet dla siedmiu osób i w dodatku wiodącego
producenta, który nie ma tego w ofercie).
Krok po kroku
Podczas projektowania przechodzimy przez wszystkie
wymagania. Często są one reorganizowane w formie
zbliżonej do tego, jak przetwarza to komputer. Na przykład
wymagania danych przekształcane są w modele danych
(model to nadal uproszczenie rzeczywistości, ale bliższe
technicznej realizacji), wymagania biznesowe i wymagania
rozwiązania opisują przepływy tych danych. Podsumowując,
wymagania opisują dane i algorytmy ich przetwarzania.
Algorytm rozumiany jest jako ciąg czynności prowadzących
do realizacji celu. Algorytmy dla znanych problemów (np.
sortowania) są udokumentowane i powszechnie stosowane.
W zależności od postawionego celu (np. minimalizacja
czasu wykonania) można skorzystać z najlepszego
rozwiązania dostępnego w danym momencie. Czasem
problemy z wydajnością rozwiązuje się za pomocą innych
metod niż zmiana algorytmów. W niektórych obszarach
należy zmienić organizację przetwarzanych danych (model)
lub miejsce ich składowania. Składowanie w pamięci
trwałej, dyskowej może być zreplikowane do pamięci
ulotnej. Taki mechanizm nazywamy korzystaniem z pamięci
podręcznej (ang. cache). Pamięć podręczna pozwala na
zmniejszenie czasu dostępu do konkretnych danych. Kiedy
nie ma jasnych wymagań niefunkcjonalnych, a nie
zdiagnozowano nieefektywnego problemu (np. programu
zbyt długo wykonującego przeliczenia), to zazwyczaj nie
przystępuje się do optymalizacji konkretnego algorytmu.
Poniżej przedstawię standardy dokumentacji projektu
systemu i różne aspekty organizacji oprogramowania.
Omówię też standardy dokumentacji procesów biznesowych
i język opisu UML.
Projektowanie procesów biznesowych nie należy do
najłatwiejszych zadań. Co więcej, o powodzeniu ich
decydują coraz częściej komponenty oprogramowania.
Notacja procesów biznesowych (ang. Business Process
Model and Notation, BPMN) to wizualna reprezentacja
procesów biznesowych. Procesy biznesowe są
odwzorowane w postaci elementów i połączeń pomiędzy
nimi.
Elementy dzielimy na zdarzenia, czynności i bramki. Każdy
proces ma swój początek i koniec (końców może być wiele).
Każdy element wzbudzający działanie w procesie to
zdarzenie (np. otrzymanie wiadomości e-mail, zdarzenie
początku lub zdarzenie końca). Czynność to dowolna
aktywność wykonana przez maszynę lub człowieka (np.
wprowadzenie danych do innego systemu lub wysłanie
powiadomienia). W postaci czynności możemy odwzorować
inny proces (zwijamy inny proces do pojedynczej
czynności). Każda czynność powinna mieć jedno wejście i
jedno wyjście. Jeżeli chcielibyśmy, aby przepływ wykonania
procesu rozgałęział się lub łączył, to wtedy musimy
skorzystać z bramki. Na przykład możemy mieć bramki
decyzyjne (np. czy udało się uzgodnić wszystkie warunki
umowy?) lub bramki rozdzielające przepływ sterowania (np.
dwa lub więcej ciągów czynności wykonywanych jest
równolegle). Proces może mieć skomplikowany przebieg.
Kiedy proces przechodzi przez wiele komórek w jednej
organizacji, wtedy każda z komórek organizacyjnych jest
odwzorowana w postaci osobnych torów (analogicznie do
torów w basenie). Kiedy proces przechodzi przez wiele
organizacji, to można to pokazać za pomocą wielu basenów
(z jednym lub wieloma torami wewnątrz).
Połączenia pomiędzy czynnościami w tej samej organizacji
wizualizujemy za pomocą strzałek z ciągłą linią. Kontakty
pomiędzy wieloma organizacjami zazwyczaj wymagają
bardziej skomplikowanej komunikacji, więc przesłanie
komunikatu wymaga strzałki z linią przerywaną. Bardziej
skomplikowane procesy można, a wręcz należy rozbić na
mniejsze. Im mniej skomplikowany proces (mniej
aktywności i połączeń na diagramie), tym łatwiej go
przeczytać i przeanalizować. Kiedy brakuje nam
specyficznych elementów na diagramie, to możemy użyć
dodatkowych artefaktów (np. ikony kartki papieru z
zagiętym rogiem symbolizującym specyficzny dokument).
Czynności operujące na specyficznych danych łączymy za
pomocą linii kropkowanej łączącej czynność z konkretnymi
danymi lub dokumentem. Istnieje wiele narzędzi, które
potrafią przenieść procesy w notacji BPMN do postaci kodu
programu. Rolą programisty jest zintegrowanie takiego
kodu w ramach większego komponentu rozwiązania.
Jednym z najpopularniejszych sposobów dokumentacji
projektów oprogramowania jest język UML (ang. Unified
Modelling Language). UML standaryzuje opisy dostępnych
funkcji dla użytkownika, klasyfikuje byty w systemie,
opisuje obiekty powołane do życia, charakteryzuje
współpracę i strukturę systemów oraz wdrożeń.
Za pomocą diagramu można opisać klasę bytów, czyli zbiór
pól i operacji, jakie będzie posiadał obiekt, przedstawiciel
danej klasy. Pola klasy, ich typ oraz krotność (ile razy dane
pole występuje) wynikają z dokumentu wymagań
opisującego pola i atrybuty. Operacje w klasie to wszystko
to, co podlega obliczeniu przez komputer (np. weryfikacja
roku, czy jest w przedziale 1900 – 2100). Klasy mogą
korzystać z innych klas (np. pole może być typu wcześniej
znanej klasy).
<SkrzyniaBiegow>AutomatycznaBezstopniowa</SkrzyniaBiegow>
<TypAuta>Miejskie</TypAuta>
<Naped>TylneKoła</Naped>
</SamochodOsobowy>
Przykład dokumentu w formacie JSON. Obiekt jest
nienazwany, a jego zawartość jest w nawiasach
klamrowych. Zawartość stanowi kolekcja wartości w
postaci klucz-wartość. Kluczem jest nazwa (w tym wypadku
zawsze w podwójnych apostrofach), wartość to tekst (np.
dla klucza Marka), liczba (np. klucz RokProdukcji) lub brak
(wartość null dla klucza PojemnoscSilnika).
{
“Marka”: “BMW”,
“RokProdukcji”: 2018,
“RodzajPaliwa”: “Elektryczny”,
“Przebieg”: 0,
“Cena”: 220000,
“PojemnoscSilnika”: null,
“Moc”: 184,
“SkrzyniaBiegów”: “AutomatycznaBezstopniowa”,
“TypAuta”: “Miejskie”,
“Napęd”: “TylneKoła”
}
SOAP (ang. Simple Object Access Protocol) to protokół,
który powstał do realizacji usług sieciowych. SOAP jest
bardziej skomplikowany i trudniejszy do implementacji niż
REST, ale dzięki wsparciu wielu narzędzi
programistycznych ma zastosowanie w środowisku
korporacyjnym. SOAP bazuje na formacie XML i pozwala na
szybkie wygenerowanie kodu po drugiej stronie kontraktu.
Takie podejście umożliwia szybszą integrację pomiędzy
komponentami (dostawcy i konsumentów usługi).
Dzięki otwartym i standardowym protokołom wymiany
danych możliwa jest współpraca urządzeń i
oprogramowania wielu różnych firm (np. sieć WWW
możemy przeglądać zarówno z komputera z systemem
Windows lub Linux, jak i z telefonu komórkowego). Taka
cecha oprogramowania nazywa się interoperacyjnością
(ang. interoperability).
Przykład wymagań
kształtujących architekturę
Zastanówmy się nad przykładem firmy logistycznej, która
chciałaby śledzić swoje ładunki z dokładnością co do
minuty oraz mieć elektroniczne potwierdzenie każdego
przeładunku. W przypadku dużej firmy logistycznej mamy
do czynienia z dużą liczbą ładunków, które dziennie
przemierzają setki lub nawet tysiące kilometrów. Pobieranie
dokładnego położenia danego ładunku z dokładnością co do
minuty powoduje powstanie blisko półtora tysiąca wpisów
tylko w ciągu jednej doby i to dla jednego ładunku. Kiedy
głębiej zastanowimy się nad tym rozwiązaniem, okaże się,
że problem dotyczy nie tylko oprogramowania zbierającego
dane, ale również sposobu przekazania tych danych.
Oprogramowanie zbierające dane musi przetworzyć
znaczącą liczbę rekordów danych zależną od aktualnie
transportowanych ładunków. W ciągu jednej doby mogą być
tysiące lub nawet miliony wpisów rekordów, z czym nie
poradzi sobie tradycyjne oprogramowanie. Taka liczba
danych pozwala nam na skategoryzowanie powstałego
problemu jako problemu big data. Aby zdobyć,
przetworzyć, przeanalizować i udostępnić tak duży zbiór
danych, potrzeba specjalistycznego oprogramowania oraz
innego podejścia do projektowania aplikacji. Takie
podejście kryje się właśnie pod terminem big data. W
idealnym przypadku można byłoby założyć, że każdy
komponent oprogramowania w wyżej opisanym procesie
żyje własnym życiem. Kolejne kroki są realizowane przez
kolejne komponenty zależne od zastosowania danych przez
konkretny zespół. Podejście typu Data Lake (z ang. jezioro
danych) zakłada, że surowe dane wpływają, a następnie są
przetwarzane przez kolejne warstwy w zależności od
docelowego odbiorcy (dane z kolejnych, pośrednich kroków
mogą być współdzielone pomiędzy różnych odbiorców).
Przy okazji przetwarzania dużych wolumenów danych
wraca popularność przetwarzania wsadowego (ang. batch
processing), które pozwala na zaplanowanie dużej ilości
obliczeń. W przeciwieństwie do przetwarzania
interaktywnego (np. wchodzimy na stronę internetową, a
serwer generuje nam stronę w odpowiedzi na nasze
żądanie) raz przygotowany wsad jest automatycznie
przeliczany przez oprogramowanie w postaci ciągłej (np.
raport powstaje na podstawie danych wszystkich ładunków
z jednego dnia).
Niemniej dane muszą jakoś wpłynąć do tego jeziora danych.
W tym konkretnym przykładzie musimy założyć pracę
urządzenia (trudno zakładać precyzję człowieka przy
wprowadzaniu danych dokładnie co minutę). Takie
zautomatyzowane podejście do kolekcjonowania danych,
gdzie urządzenie komunikuje się z innym systemem przez
Internet, nazywamy Internetem Rzeczy (ang. Internet of
Things, IoT). W wymaganiach dodatkowo wspomniano o
elektronicznym potwierdzeniu dotarcia danego ładunku.
Zazwyczaj ładunki przeładowywane są w portach, a finalne
potwierdzenie będzie pochodzić od docelowego klienta.
Trudno, aby wszystkie takie podmioty korzystały z jednego,
centralnego systemu wymiany danych logistycznych i
zawsze na czas wymieniały dane na temat ładunków. W
takim wypadku korzysta się z rozwiązań
zdecentralizowanych, gdzie każdy kolejny wpis jest
potwierdzony przez całą sieć współpracujących
komputerów (komputerów będących w posiadaniu
zaangażowanych instytucji). Rozproszone rozwiązanie
polegające na księgowaniu każdej transakcji (np.
przekazania dobra od jednego do drugiego podmiotu) jest
realizowane w postaci rozwiązania o nazwie blockchain9.
System typu blockchain jest rozproszoną bazą danych,
gdzie każdy rekord powstaje przez dodanie kolejnego
bloczku z wpisem do łańcucha wszystkich zdarzeń.
Komputery (węzły w sieci blockchain) uzgadniają między
sobą aktualną wersję danych.
Podział programistów
W ramach grupy specjalistów IT rozróżniamy różne
specjalizacje, a także pracę skoncentrowaną na różnych
elementach oprogramowania. Architekturę aplikacji
możemy pokazać jako stos różnych technologii. Stos
możemy podzielić na warstwę prezentacji danych (ang.
frontend) i warstwę dostępu do danych (ang. backend). I
rozróżniamy też specjalistów pracujących nad
specyficznymi warstwami, o czym poniżej. Wyłomem od tej
reguły są programiści całego stosu aplikacji (ang. full
stack), którzy w swojej pracy łączą oba światy i pracują nad
wszystkimi warstwami.
Specjaliści odpowiedzialni za prezentację danych często
pracują blisko z użytkownikiem. To oni odpowiadają za
finalny kształt i funkcje oprogramowania. To do nich często
użytkownicy zwracają się z pytaniami o działanie aplikacji
w przypadku zaobserwowanych anomalii. Stąd często
specjaliści od frontendu poza umiejętnościami technicznymi
mają także wiedzę z budowania doświadczenia korzystania
z aplikacji (ang. user experience), ergonomii czy
kompozycji graficznych. Typowymi komponentami
oprogramowania, jakie specjaliści od prezentacji
dostarczają, są aplikacje internetowe, aplikacje na telefony
komórkowe i urządzenia mobilne, tradycyjne aplikacje
okienkowe oraz gry jako osobna kategoria. Choć potrzebują
serwera do działania, aplikacje internetowe zaliczane są do
warstwy prezentacji danych (część serwerową
odpowiedzialną za generowanie stron zalicza się do
warstwy prezentacji).
Począwszy od lat dziewięćdziesiątych, aplikacje
internetowe podlegają ciągłym procesom ewolucji.
Ewolucja została zapoczątkowana przez proste, statyczne
(niezmienne) strony internetowe (często mechanicznie
generowane przez specjalistów od danych). Kolejnym
etapem były aplikacje z elementami dynamicznymi
(animacje, obsługa formularzy do wprowadzania danych) i
szereg standardów, takich jak bogate aplikacje internetowe
(ang. Rich Internet Applications, RIA). Obecnie najbardziej
aktualny zbiór standardów to ten pod parasolem
progresywnych aplikacji internetowych (ang. Progressive
Web Apps, PWA). Do tworzenia aplikacji internetowych
historycznie były używane takie technologie jak Adobe
Shockwave, Adobe Flash, Microsoft Silverlight czy aplety
Java.
Otwartym i szeroko akceptowanym zbiorem standardów
aplikacji internetowych jest zbiór oparty na akronimach
HTML, CSS i JS. HTML (ang. Hypertext Markup Language)
to język opisu stron i aplikacji internetowych. Elementy
języka HTML zawierają tekst, linki do obrazów oraz inne,
interaktywne obiekty (np. formularze wprowadzania
danych). CSS to akronim pochodzący od kaskadowych
arkuszy stylów (ang. Cascading Style Sheets), które służą
do opisu formy prezentacji (np. koloru tła, koloru czcionek,
obramowań, animacji, przejść). JS to akronim pochodzący
od języka JavaScript, skryptowego języka programowania
działającego w warstwie prezentacji danych. JavaScript
odpowiada za weryfikację, transformację i przekazanie
danych. Jest to pełnoprawny język programowania, w
którym powstają skomplikowane aplikacje o funkcjach
nieustępujących aplikacjom dedykowanym na urządzenia
mobilne czy tradycyjnym aplikacjom okienkowym.
Aplikacje na telefony komórkowe (w tym terminie
uwzględniamy także smartfony) oraz urządzenia mobilne
(tablety i wszystkie inne przenośne urządzenia, w tym
inteligentne zegarki) mogą być tworzone na uniwersalnych
platformach lub za pomocą narzędzi dedykowanych dla
konkretnego systemu telefonu (np. osobno dla Apple iOS i
Google Android). Tworzenie aplikacji na uniwersalnej
platformie polega na napisaniu jednego trzonu aplikacji w
jednym języku, a następnie zbudowaniu odpowiednich
wersji dedykowanych na konkretne systemy. Każde z
urządzeń ma swoją charakterystykę (np. wielkość ekranu)
oraz zbiór funkcji dla niego specyficznych (np. część
urządzeń może nie mieć urządzeń transmisji danych w
podczerwieni albo czytników biometrycznych), stąd wiele
aspektów musi być zaadresowanych osobno. Typowe
uniwersalne platformy to Microsoft Xamarin lub Apache
Cordova.
Aplikacje dedykowane na konkretne platformy (lub wręcz
urządzenia) pozwalają na dowolność w korzystaniu ze
specyficznych funkcji urządzenia, ale wiążą specjalistów z
konkretnymi narzędziami, językami i technologiami. W
momencie pisania tej książki aplikacje dla systemu Android
często powstają w takich językach i technologiach jak C++,
Kotlin i Java (środowisko Dalvik). Dla systemu Apple iOS
typowe języki i technologie to Objective-C lub Swift.
Tradycyjne aplikacje okienkowe, do których jesteśmy
przyzwyczajeni podczas pracy komputera PC, to także
oprogramowanie zależne od docelowego systemu.
Przykładowo oprogramowanie prezentacji danych dla
Windows korzysta z szeregu funkcji obsługiwanych przez
ten system. Microsoft dostarcza szereg różnych standardów
do tworzenia aplikacji, począwszy od formularzy Windows
(Windows Forms), przez pakiet bibliotek prezentacji
aplikacji (Windows Presentation Foundation), po zbiór
uniwersalnych funkcji (Universal Windows Platform) dla
wszystkich systemów opartych na Windows (Windows 10,
Microsoft Xbox, Microsoft HoloLens). Na platformie
Windows można pisać aplikacje w wielu językach
programowania, m.in. C++, C#, Visual Basic czy
JavaScript. Analogicznie w świecie Apple aplikacje dla
macOS można tworzyć w Objective-C i korzystając z
biblioteki Cocoa.
Alternatywą dla technologii specyficznych dla konkretnego
producenta systemu są technologie uniwersalne,
wspierające wiele systemów. Ich cechy są analogiczne do
uniwersalnych technologii dla telefonów i urządzeń
mobilnych. Godną odnotowania tego typu technologią jest
Qt, czyli technologia budowania aplikacji interfejsu
użytkownika dla wielu systemów operacyjnych (Windows,
macOS, Linux, Android).
Osobną kategorią w wytwarzaniu oprogramowania
interfejsu użytkownika są gry i aplikacje multimedialne.
Gry ze względu na oczekiwane doświadczenie zanurzenia w
wirtualnej rzeczywistości wymagają innego podejścia do
tworzenia oprogramowania. W tym obszarze tworzenie
oprogramowania ma więcej wspólnych cech, bez względu
na rodzaj urządzenia (telefon/tablet/komputer/konsola do
gier) , ale możliwości konkretnego urządzenia mają większe
znaczenie (np. moce obliczeniowe układu graficznego na
poziomie sprzętu). Coraz częściej do tworzenia tego typu
oprogramowania korzysta się z rozwiązań dostarczanych
przez specjalistyczne podmioty. Rozwiązania mają na celu
unifikację wielu różnych standardów (DirectX w świecie
Microsoft i OpenGL poza nim) i pozwalanie na łatwiejsze
przenoszenie oprogramowania na różne urządzenia (np.
Unity).
Specjaliści od logiki i danych mają na celu zbudowanie
efektywnego rozwiązania (zwłaszcza od strony technicznej)
przetwarzania i składowania danych. W mniejszym stopniu
wchodzą w interakcję z użytkownikami, ale bardziej
ingerują w funkcjonalne aspekty (wydajność, optymalizacja
i bezpieczeństwo). Specjaliści odpowiedzialni za dostęp do
danych mają analogiczny podział na grupy języków i
technologii jak specjaliści prezentacji danych.
Warstwę dostępu do danych można podzielić na elementy
usług i logiki aplikacji, komunikacji oraz składowania
danych.
Usługi i logika aplikacji to wszystko to, co składa się na
otrzymanie zapytania od innego komponentu
oprogramowania, przetworzenie go i przygotowanie
odpowiedzi. W ramach wykonania usługi odbywa się
weryfikacja bezpieczeństwa, pobieranie danych z różnych
źródeł, poddanie ich obróbce zgodnie z logiką
przetwarzania oraz transformacja do oczekiwanego
formatu. Istotne aspekty tej warstwy to bezpieczeństwo i
skalowalność. Bezpieczeństwo to różne aspekty związane z
uwierzytelnieniem (potwierdzenie tożsamości podmiotu
stojącego za żądaniem) i autoryzacją (weryfikacja dostępu
do określonych danych czy funkcji).
Technologie wykonania usług i implementacji logiki
biznesowej możemy próbować skategoryzować na oparte
na językach skryptowych i kompilowalnych (w tym
wirtualnych środowiskach wykonania kodu). Popularne
języki skryptowe10 i oparte na nich technologie wykonania
aplikacji podane w nawiasie to: PHP (Zend), Ruby (Rails),
Python (Django), JavaScript (NodeJS). Technologie oparte
na wirtualnych środowiskach wykonania kodu to takie, w
których istnieje pośrednik (wirtualne środowisko) pomiędzy
oprogramowaniem a systemem. Przykładami wirtualnych
środowisk mogą być Java Virtual Machine (przykładowe
języki programowania to Java i Scala) oraz Microsoft .NET
(języki to C# i Visual Basic).
Komunikacja w warstwie przetwarzania danych to stos
technologii służący do wymiany komunikatów pomiędzy
komponentami (zazwyczaj w tej samej warstwie) złożonych
aplikacji biznesowych. Przykładowe rozwiązania
technologiczne to kolejki wiadomości (ang. message
queue), np. Microsoft Message Queue, Tibco czy Solace.
Składowanie danych to utrwalanie danych w
scentralizowanym miejscu i jest zależne od charakteru
oprogramowania. Składowanie danych może mieć
charakter płaskich plików lub baz danych. Płaskie pliki to
najprostsza forma składowania danych (kolejnych wpisów)
w postaci tekstowej (zrozumiałej dla człowieka) lub
binarnej (zrozumiałej tylko dla oprogramowania). Płaskie
pliki w wielu zastosowaniach wychodzą z użycia na rzecz
baz danych.
Bazy danych możemy podzielić na relacyjne i nierelacyjne
(NoSQL). W relacyjnych bazach danych dane trzymane są
w tabelach oraz istnieją relacje pomiędzy nimi.
Najpopularniejsze rozwiązania w tej kategorii to MySQL,
PostgreSQL, Microsoft SQL Server, bazy danych Oracle lub
linia produktów IBM DB2.
W ostatnich latach rośnie popularność nierelacyjnych baz
danych, często skrywanych pod akronimem NoSQL. NoSQL
wziął się z tego, że pierwotnie ten rodzaj baz danych nie
wspierał standardowego języka zapytań baz danych SQL
(Structured Query Language) i relacji pomiędzy danymi.
Jest wiele różnych rodzajów baz danych NoSQL: bazy
dokumentowe (przykładowi przedstawiciele to Elastic
Search i MongoDB), bazy oparte na parach klucz-wartość
(BerkleyDB, Redis), bazy oparte na kolumnach (Cassandra,
HBase) lub grafowe bazy danych, dedykowane do
trzymania danych o strukturze grafu (Neo4J, Apache
Giraph).
Innym podziałem programistów baz danych jest podział na
programistów transakcyjnych baz danych i hurtowni
danych. Transakcyjne bazy danych mają na celu zapewnić
ciągłe działanie funkcji biznesowych (ang. Online
transaction processing, OLTP). Przykładem może być
przetwarzanie zamówienia w sklepie od momentu
utworzenia wirtualnego koszyka, przez zarządzanie
produktami do niego włożonymi, realizację zamówienia,
przejście przez różne stany jego realizacji (oczekiwanie na
płatność, zatwierdzenie płatności, zlecenie
skompletowania, aktualizacja stanów magazynowych), po
obsługę wysyłki. W transakcyjnych bazach danych często
istotnym obszarem zainteresowania jest szybkość działania
i wydajność (np. liczba transakcji przetworzonych w
zakresie czasu).
Często pojawia się potrzeba analizy zebranych danych (np.
odpowiedzi na pytanie, jaka była sprzedaż w kolejnym
rejonie kraju w danym kwartale). Tworzenie
skomplikowanych raportów danych, często
niestandardowych (np. dotyczących wpływu konkretnych
działań marketingowych), może wpłynąć negatywnie na
wydajność systemu transakcyjnego, stąd istnieje potrzeba
oddzielenia obsługi bieżącej działalności i analizy
zebranych danych w oprogramowaniu. W warstwie
składowania danych pojawia się pojęcie hurtowni danych.
Hurtownia danych (ang. data warehouse) to specjalna baza
danych zbudowana pod kątem umożliwienia użytkownikom
wglądu w konkretny obszar działalności (ang. Online
analytical processing, OLAP). Często dane zawarte w
hurtowni pochodzą z wielu niezależnych transakcyjnych
baz danych (np. baz danych pochodzących z różnych
działów firmy). Proces ekstrakcji danych z systemu
źródłowego, transformacji do wspólnego formatu i
ładowania do docelowej bazy danych (ang. process ETL, od
kolejnych słów Extract, Transform, Load) także często
wymaga dedykowanego oprogramowania. Dlatego bez
względu na użyte technologie (bazy danych relacyjne lub
NoSQL) programistów baz danych możemy podzielić na
programistów baz danych i programistów hurtowni danych.
Programista programiście
nierówny
W poprzednim rozdziale wymieniono szereg technologii
składających się na stos oprogramowania. Za tymi
elementami stoją ludzie tworzący te technologie i
korzystający z ich dobrodziejstw. Czasem zdarza się tak, że
ludzie pracujący w jednej galaktyce różnych technologii nie
wiedzą, co dzieje się w sąsiedniej, posługują się innym
językiem. A do rewelacji pochodzących ze zwaśnionego
świata podchodzą wręcz z niechęcią lub sarkastycznym
humorem. Kiedy rozmawiamy z konkretnym specjalistą,
warto zainteresować się, z jakiej galaktyki technologicznej
pochodzi, aby uniknąć pewnych terminologicznych
nieporozumień (np. słowo „atrybut” ma inne znaczenie dla
programisty C# niż dla programisty języka Java) lub
technologicznego faux pas (pomylenie języków o podobnej
nazwie Java i JavaScript może prowadzić do
kompromitacji). Dodatkowo warto mieć na uwadze szybką
ewolucję w (wszech)świecie programistów. Technologie są
relatywnie szybko zmienianie, co wpływa na popularność
konkretnych rozwiązań i języków.
Poza programistami klasycznych języków programowania
coraz częściej mamy role związane z programowaniem, ale
niekoniecznie wykonawców tych czynności nazywamy
programistami. W kolejnym rozdziale przyjrzyjmy się bliżej
takim rolom.
Nieprogramiści tworzący
oprogramowanie
W wielu obszarach tworzenia rozwiązań oprogramowania
pojawiają się role, które wymagają umiejętności
programowania, ale nie są ściśle związane z braniem
udziału w procesie programowania. Omówmy kolejne z
nich, które mogą być pomocnicze w procesie wytwarzania
danych.
Najliczniej reprezentowaną grupą w tym gronie są
specjaliści od Business Intelligence (BI). W różnych
kontekstach i organizacjach nazywani: analitykami lub
specjalistami BI, analitykami danych (ang. data analyst) lub
badaczami danych (ang. data scientist). Zadania wchodzące
w skład BI to analiza danych, eksploracja danych, analiza
predykcji czy wizualizacja danych. Podstawowym
narzędziem dla wszystkich analityków może być arkusz
kalkulacyjny (np. Microsoft Excel lub LibreOffice Calc).
Analiza danych może mieć różne postacie: analizy inspekcji
danych, statystycznej analizy, czyszczenia i transformacji
danych. Do tego celu można użyć języków programowania,
np. języka R, języka Python lub wizualnych środowisk
pozwalających wymodelować dane, jak Alteryx Designer
lub Microsoft Power BI. Stosując bardziej skomplikowane
metody eksploracji danych (ang. data mining), można
odkrywać wzorce w danych, klasyfikować je lub w
usystematyzowany sposób szukać zależności, a także
budować modele przewidywania przyszłości. Do tego celu
można użyć rozszerzeń lub bibliotek narzędzi do analizy
danych. Większość dostawców oferuje stosowne narzędzia.
Do wizualizacji danych także można użyć rozszerzeń lub
dedykowanych rozwiązań, jak Tableu lub Prezi dla biznesu.
Zgłoszenia błędów
„Okrągłe” liczby w świecie IT, jak 64 czy 1024, mogą
wydawać się trudne do zapamiętania dla przeciętnego
użytkownika. Są to jednak liczby nieprzypadkowe, to potęgi
liczby dwa. Liczba dwa jest związana z dwoma poziomami
napięcia — napięcie jest (poziom jeden) lub go nie ma
(poziom zero). Ciąg zmian poziomu napięcia tworzy ciąg zer
i jedynek. Jedynka na kolejnej pozycji oznacza potęgę
dwójki. I tak liczba 1000 w zapisie binarnym oznacza liczbę
8. W pamięci komputera wszystkie dane są reprezentowane
właśnie w postaci binarnej (kod binarny). Ze względu na
łatwiejsze zarządzanie tymi ciągami liczb bity przyjęło się
grupować po osiem i nazywać bajtami. Osiem bitów to 256
możliwości. Profesor uniwersytetu Stanforda Donald Knuth
zwykł wystawiać czeki na okrągłą liczbę 2,56 dolara dla
każdej osoby (okrągłe 256 centów), która znajdzie błąd lub
będzie miała znaczący wkład w jego pracę12. Donald Knuth
być może zapoczątkował program nagradzania za
wyszukiwanie błędów w oprogramowaniu (ang. bug bounty
program). Tego typu program polega na wynagradzaniu
każdego, kto znajdzie bug w oprogramowaniu, w podzięce
za zgłoszenie. Zazwyczaj im większa firma, im groźniejsze
ryzyko związane z błędem, tym wyższa wartość materialna
wynagrodzenia. Firma Facebook, stojąca za największą
siecią społecznościową (w momencie pisania tej książki
serwis Facebook miał ponad dwa miliardy użytkowników),
w podzięce za zgłoszenie błędu związanego z
bezpieczeństwem zwykła rozsyłać czarne karty
kredytowe13. Zaś firma Microsoft za pomocą programu
Windows Insider14 zbiera opinie (w tym także zgłoszenia o
błędach) na temat kolejnych wersji systemu Windows,
zanim nowe wydania staną się publicznie dostępne dla
szerokiego grona odbiorców. Tym samym każdy uczestnik
programu może wpłynąć na kształt jednego z
najpopularniejszych systemów operacyjnych.
Warto zatem wiedzieć, jak napisać dobre zgłoszenie błędu.
Bug wiąże się z konkretnym oprogramowaniem, akcją i
reakcją systemu. Przygotowując zgłoszenie, należy
sprawdzić wersję oprogramowania (lub jego komponentu),
które podlega zgłoszeniu. Jeśli oprogramowanie działa w
wielu systemach i na wielu platformach, także je warto
scharakteryzować (np. jaki system operacyjny, w jakiej
wersji, jaka przeglądarka internetowa i w jakiej wersji). W
zależności od typu zgłoszenia charakterystyka urządzenia
może być istotna (np. nazwa i model telefonu
komórkowego). Im więcej tego typu szczegółów umieścimy,
tym szybciej programiści będą mogli zbadać problem.
W ramach zgłoszenia należy precyzyjnie przedstawić ciąg
kolejnych kroków, które wiodły do odnalezienia błędu. Taki
ciąg można spisać w punktach, np. „Wybranie z menu Plik
opcji Otwórz; w nowym oknie zaznaczenie pliku; wybranie z
akcji Anuluj”. Powinniśmy również napisać, co
otrzymaliśmy w tym momencie, np. „oprogramowanie
otwiera zaznaczony plik”. Warto także wyartykułować nasze
oczekiwania po takiej akcji, np. „oprogramowanie zamknie
okno oraz nie otworzy zaznaczonego pliku”. Dobrą praktyką
jest sprawdzenie przed zgłoszeniem, czy błąd miał
charakter jednostkowy, czy może powtarza się za każdym
razem (także po restarcie urządzenia). Inne „znaleziska”
warto odnotować w komentarzach do zgłoszenia.
Do zarządzania stanem zgłoszeń często używa się
dedykowanych systemów zarządzania zgłoszeniami (ang.
bug tracking system), np. JIRA lub Redmine.
Rodzaje testów
Ludzkość przez tysiąclecia traktowała upuszczanie krwi
jako ważny element terapii różnych chorób. Nietrudno
sobie wyobrazić kapłana starożytnej religii, który źródła
gorszego stanu zdrowia upatruje w „złej” krwi, więc należy
się jej pozbyć. Tak postawiona hipoteza wymagała
natychmiastowej weryfikacji. Uzdrowiony chory utwierdzał
kapłana w pozytywnym działaniu terapii (test pozytywny).
Co ciekawe, chory bez jakiejkolwiek innej kuracji zapewne
nie odczuwał poprawy, co także mogło służyć do
potwierdzenia tej tezy (test negatywny). Takie obserwacje
zapisały upuszczanie krwi jako istotny element terapii
medycznej na kolejne stulecia. W XIX wieku zaczęto
weryfikować skuteczność tej metody i dziś jest ona
stosowana w nielicznych schorzeniach. Przetestowanie tego
rozwiązania zajęło jednak kilka tysięcy lat. Oczekiwaniem
względem przedstawionej metody była poprawa stanu
zdrowia chorego, co udowadniało prawdziwość tej
(pozytywnej) hipotezy. Nieprecyzyjna ocena stanu zdrowia
przed i po zastosowaniu metody leczniczej pozwalała na
zakończenie jej testów z sukcesem.
Powyższy przykład pozwala na wyprowadzenie bardziej
ogólnych wskazówek na temat testowania (w tym także
oprogramowania). Po pierwsze w testach należy być
precyzyjnym w określaniu stanu wejściowego (w testach
„aranżujemy” stan wejściowy, ang. arrange),
podejmowanych kroków (ang. act) i stanu oczekiwanego
(ang. assert). Po drugie należy zweryfikować nie tylko
pozytywne scenariusze, ale również negatywne (np. jak
powyższa metoda zadziała na zdrowego człowieka?) i
przypadki skrajne (np. jak pacjent będący na granicy progu
wyleczenia innymi metodami zachowa się pod wpływem
wyżej wymienionej metody?). Weryfikując wyniki testu,
należy skategoryzować wyniki — co to znaczy, że test
zakończył się sukcesem? Kiedy mówimy o porażce?
Sukcesem (ang. test success lub test pass) jest każdy wynik
testu, który wskazuje, że oprogramowanie zachowuje się
zgodnie z oczekiwaniami (zarówno testy pozytywne, jak i
negatywne mogą, a wręcz powinny kończyć się sukcesem).
Porażką (ang. test fail) jest każdy test, w wyniku którego
otrzymany stan jest inny od oczekiwanego.
Mówiąc o precyzji, należy jasno określić metodę testowania
oraz jej zakres (najlepiej zanim zlecimy zespołowi
technicznemu pracę nad oprogramowaniem). Wyróżnia się
testy jednostkowe, komponentowe, integracyjne oraz
systemowe. Testy jednostkowe polegają na testowaniu
bliżej niezdefiniowanej „jednostki”. Tutaj pojawia się
pytanie, czym jest ta mityczna „jednostka”? Jednostka to
najmniejszy moduł w oprogramowaniu, który da się
przetestować. W praktyce oznacza to zwykle pojedynczą
linijkę kodu i odpowiedzenie na pytanie, czy aby na pewno
funkcjonuje ona prawidłowo i czy jest to udowodnione
(pokryte) testami. Sprawy nieco komplikują się, kiedy
testujemy decyzję. W takim wypadku należy przetestować
wszystkie możliwe rozgałęzienia danej decyzji (ang. branch
testing; np. badając warunek, czy liczba jest podzielna
przez dwa, powinniśmy przetestować zarówno przypadki,
kiedy testowana liczba jest liczbą parzystą, jak i
nieparzystą). Często spotykamy się z sytuacją, kiedy
prowadzone są metryki testów, w których gromadzone są
informacje na temat stosunku przetestowanych linijek kodu
do liczby wszystkich linijek kodu, stosunku
przetestowanych rozgałęzień do wszystkich rozgałęzień w
kodzie, stosunku przetestowanych plików, bytów do
wszystkich plików, bytów w kodzie itd. Takie testy
realizowane są przez programistów. Jedną z miar oceny
jakości oprogramowania jest procent pokrycia kodu
testami.
Przechodząc do testów komponentu, zazwyczaj testujemy
zespół lub ciąg linijek kodu, które składają się na większy,
samodzielny obszar realizujący określoną funkcjonalność.
Testy komponentu zazwyczaj nie mają aż tak dużej
granulacji jak testy jednostkowe, pokrywają przepływ
danych i sterowania przez komponent, od wejścia do
wyjścia danych. W praktyce oznacza to testowanie
komponentu w odizolowaniu od pozostałych (np. testowanie
komponentu generowania raportu w odizolowaniu od
komponentu wprowadzania danych). Istnieją przypadki,
kiedy testy komponentu nie mogą być przeprowadzone
samodzielnie przez użytkownika i wymagana jest pomoc
zespołu programistów. Takie wsparcie może być potrzebne
w budowaniu zaślepek dla komponentów wchodzących w
interakcję z testowanym komponentem (ang. stub) albo
symulowaniu zachowania tychże komponentów (ang.
mock). Innym przypadkiem testów, które także wymagają
nakładu prac zespołu technicznego, są testy integracyjne.
Testy integracyjne mają na celu sprawdzenie, czy
komponenty poprawnie współpracują ze sobą. Często
testuje się komponenty parami. Podczas testów
integracyjnych sprawdza się, czy dane z jednego
komponentu trafiają do drugiego w poprawnej i
niezniekształconej postaci oraz w dobrej kolejności (np. czy
moduł generowania raportu przekaże poprawny raport do
modułu wydruku raportu).
Testy systemowe z kolei polegają na testowaniu systemu
całościowo. Testy systemowe mogą być przekrojowe i
przechodzić przez wszystkie komponenty składające się na
system (np. definiujemy dwie pozycje, jakie mają być
wprowadzone do systemu; wprowadzamy je do systemu;
wybieramy opcję generowania raportu dla nich; następnie
wybieramy opcję wydruku wygenerowanego raportu i
weryfikujemy, czy wydruk zawiera wprowadzone wcześniej
pozycje).
Kiedy podczas testów realizowane są typowe scenariusze
użycia oprogramowania, naśladujące zachowanie realnego
użytkownika, to wtedy mówimy o testach akceptacyjnych
(ang. user acceptance test, UAT).
Oprogramowanie możemy testować manualnie albo ułatwić
sobie pracę i testować automatycznie. Testy manualne
(innymi słowy ręczne) wymagają interwencji użytkownika
(potocznie „przeklikania się” przez system) i zazwyczaj są
czasochłonne. Testy manualne pozwalają na
wygenerowanie przypadkowych zdarzeń w systemie, które
mogą prowadzić do znalezienia nieoczywistych błędów.
Takie testowanie nazywamy testowaniem eksploracyjnym.
Częste testowanie systemu wymaga dużego nakładu czasu i
środków na przetestowanie całości systemu. Automatyzacja
znanych i typowych testów przyczyni się do minimalizacji
kosztów. W takich przypadkach to komputer przetestuje
oprogramowanie (podejmując takie same akcje jak człowiek
siedzący przed komputerem) oraz zdecyduje, czy wynik
zachowania oprogramowania jest zgodny z oczekiwanym
(wprowadzonym wcześniej przez człowieka do testu
automatycznego).
Zbiór wszystkich testów, które są powtarzane przy każdej
sesji testowania (np. z okazji wydania nowej wersji),
nazywa się testami regresyjnymi. Na testy regresyjne mogą
składać się zarówno testy manualne, jak i testy
automatyczne. Celem tych testów jest zweryfikowanie, czy
podstawowe funkcje oprogramowania działają w
niezmienionej i oczekiwanej formie. Wiele zespołów
technicznych realizuje testy regresyjne przy każdej
większej zmianie dowolnego z elementów oprogramowania
(np. zmian w kodzie źródłowym, zmian w zbiorze
używanych bibliotek lub znaczących zmian w konfiguracji
komponentu).
Bez względu na to, jakie rodzaje testów zostały
przeprowadzone, należy każdorazowo zgromadzić artefakty
(dokumenty, raporty, zrzuty ekranu i wideo) potwierdzające
fakt przeprowadzenia testów i ich rezultaty. Takie artefakty
nazywa się ewidencją testów (ang. test evidence).
Środowisko testowe
Woda wrze w temperaturze stu, a zamarza poniżej zera
stopni Celsjusza. Na podstawie tych faktów można byłoby
przetestować, czy termometr pokazuje właściwą
temperaturę. Jednak kiedy z tym samym termometrem
udałoby nam się wspiąć na wysoką górę (np. Mount
Everest), wtedy wskazałby znacząco inną temperaturę dla
tych samych zjawisk. Zmiana środowiska w tym wypadku
powoduje inny punkt odniesienia (względem przemian
termodynamicznych) i musi to być uwzględnione w testach.
W idealnym przypadku test powinien odbywać się w takim
samym środowisku (a w opisanym przypadku pod takim
samym ciśnieniem), aby uzyskać takie same wyniki. Nie
inaczej jest z testami oprogramowania. Środowisko, w
którym odbywają się testy, ma równie istotne znaczenie.
Każdy z wyżej wymienionych rodzajów testów realizowany
jest (w najbardziej optymistycznym scenariuszu) w
osobnych środowiskach testowych. Testy jednostkowe
zazwyczaj wykonywane są w środowisku programistycznym
(ang. development environment, w skrócie dev
environment), testy komponentów w środowisku
zapewnienia jakości (ang. quality assurance environment,
QA environment), testy integracyjne w środowisku
integracyjnym (ang. integration environment, INT
environment), zaś testy całego systemu z perspektywy
użytkownika odbywają się w środowisku akceptacyjnym
(ang. User Acceptance Test environment, UAT
environment). Środowisko nietestowe i z realnymi danymi
nazywamy produkcją (ang. Production environment, PROD
environment). Podczas wytwarzania oprogramowania
czasem używa się dodatkowych środowisk albo całkowicie
innej terminologii nazewniczej. Istnieją także przypadki
ograniczenia liczby środowisk testowych (np. ze względu na
koszty zarządzania lub zakupu licencji). W ostatnich latach
istnieje trend automatyzacji procesu tworzenia, zarządzania
i konfigurowania różnych środowisk, począwszy od
automatyzacji opartej na prostych skryptach systemowych,
po rozwiązania oparte na wirtualnych maszynach (np.
Docker i zarządzanie konfiguracją za pomocą narzędzia o
nazwie Kubernetes).
Wdrożenie
Wyobraźmy sobie, że postawiono nam zadanie zjedzenia
słonia (lub bardziej sensownie — ilości żywności
odpowiadającej wielkości słonia) . Zjedzenie słonia na raz
może być wyzwaniem nawet dla największego drapieżcy,
więc realizację takiego zadania trzeba byłoby rozbić na
kawałki. Kawałek po kawałku można skonsumować słonia.
Jak nietrudno sobie wyobrazić, zjedzenie takiej ilości
pożywienia musi być rozłożone w czasie.
Wdrożenie produkcyjne (ang. PROD release) dużych
systemów składających się z wielu komponentów
oprogramowania przypomina jedzenie słonia. Także musi
być realizowane kawałek po kawałku. Stopniowo
przygotowywane są kolejne komponenty do „wdrożenia”.
Przez wdrożenie rozumiemy ciąg akcji: przygotowanie
wydania, przetestowanie go (wdrożenie testowe na
przykład w środowisku produkcyjnym równoległym),
przygotowanie procedur i kolejności instalacji, a następnie
sposobu weryfikacji, czy oprogramowanie działa, jak to
zostało zaplanowane. Im bardziej skomplikowane
oprogramowanie, tym bardziej etap wdrożenia jest
rozciągnięty w czasie. Zdarzają się momenty, kiedy
wdrożenie kończy się w momencie połączenia wszystkich
komponentów w jedną całość. Taki moment przypomina
start rakiety. Niczym na filmach z siedziby NASA zespół
techniczny na ekranach obserwuje stan pracy kolejnych
komponentów. Kiedy dane zaczynają wypełniać cyfrowy
„krwiobieg” i poprawnie docierać do wszystkich
komponentów, często w zespole technicznym panuje
euforia. Im bardziej skomplikowany system powiązań
pomiędzy komponentami, tym bardziej precyzyjne
uruchomienie „krwiobiegu”. Rzadko zdarzają się sytuacje,
gdy dane przepływają bez problemów od pierwszego razu.
Często wymagane są restarty, ponowne instalacje lub
poprawki w kodzie na gorąco (ang. hot fixes). Każde duże
wdrożenie pociąga za sobą zaangażowanie wielu zasobów.
Zdarza się, że zespół techniczny pracuje w dodatkowym
dniu lub przez weekend.
W 2007 roku firma Microsoft udostępniła system Windows
Vista — oprogramowanie niewystarczająco przetestowane i
wymagające silnego komputera. Instalacja na zbyt słabym
sprzęcie lub korzystanie z wymagającego oprogramowania
powodowały niestabilność pracy systemu. Dyskomfort
użytkowników stawiał pod znakiem zapytania przyszłość
całej marki Windows na rynku konsumenckim. Równolegle
z udostępnieniem kolejnych poprawek ruszyła kampania
marketingowa „experiment Mojave” mająca poprawić
wizerunek systemu Windows15. To pokazuje, jak istotne jest
pierwsze wdrożenie i udostępnienie szerokiemu gronu
odbiorców. Wdrożenie „niewygrzanego” oprogramowania
powoduje znaczący nakład kosztów.
Utrzymanie
Wyobraźmy sobie zabytkowy kredens. Drewniany mebel,
który jest ostoją niejednej rodzinnej historii. Przez lata
mógł stać w różnych miejscach i mogło być w nim
przechowywanych wiele przedmiotów (ubrań, naczyń itp.).
Dobrze utrzymany zabytek może być wkomponowany w
zmieniające się otoczenie i jest ozdobą każdego
pomieszczenia. Aby na co dzień cieszyć się z charakteru
mebla, musimy dbać o jego konserwację. Przez
konserwację rozumiemy wszystkie zabiegi mające na celu
zachowanie jego funkcji i wyglądu. Zaczynając od
systematycznego ścierania kurzu z powierzchni płaskich,
przez okresowe polerowanie zdobień, po reakcję na
nieoczekiwane zdarzenia. Takim zdarzeniem może być
niesforne dziecko z brudnymi rączkami zostawiające ich
ślady na meblu. Do poważniejszych zdarzeń możemy
zaliczyć różnego rodzaju czynniki losowe, jak zalania czy
uszkodzenia fizyczne. W przypadku poważniejszych
wypadków zabytkowego mebla zazwyczaj udajemy się do
specjalisty, który oceni rozmiar i charakter zniszczeń oraz
przygotuje plan i wyceni koszty naprawy. W przypadku
zalania to może być osuszenie i konserwacja dedykowanymi
środkami chemicznymi, zaś w przypadku uszkodzeń
mechanicznych to może wiązać się z wymianą konkretnego
elementu. Posiadanie zabytkowego mebla to nie tylko
przyjemność, ale także szereg podejmowanych działań
(często nieobecnych w przypadku ogólnodostępnych,
budżetowych mebli). W analogiczny sposób musimy
traktować procesy i zadania związane z konserwacją i
utrzymaniem oprogramowania. Poniżej opiszemy kilka
działań, które są częścią fazy utrzymania oprogramowania.
Na utrzymanie oprogramowania składa się szereg działań
w postaci monitorowania, diagnostyki, konfiguracji i
aktualizacji oprogramowania.
Monitorowanie oprogramowania to systematyczny proces
analizy stanu oprogramowania. W zależności od rodzaj
oprogramowania monitorowanie może być realizowane na
wielu poziomach, najczęściej biznesowym i technicznym.
Przykładowo na poziomie biznesowym mogą być zbierane
informacje na temat liczby przetworzonych wiadomości w
konkretnym czasie czy też o procencie zaakceptowanych
wiadomości względem wszystkich wiadomości. Na poziomie
technicznym jest to na przykład liczba otwartych połączeń
sieciowych, liczba otwartych plików, rozmiar
uruchomionego oprogramowania w pamięci operacyjnej.
Do monitorowania oprogramowania przydatne są pliki
logów oraz narzędzia diagnostyczne (często dostarczone
przez system operacyjny). Monitorowanie może odbywać
się przez okresowe zadania realizowane przez człowieka
(np. analiza plików logów) albo zautomatyzowane
narzędzia. Narzędzia monitorowania zależą od docelowej
grupy odbiorców (użytkownicy biznesowi lub techniczni),
mogą przedstawiać stosowne informacje. Na przykład
użytkownicy biznesowi mogą mieć dedykowane
oprogramowanie przypominające tablicę rozdzielczą (ang.
dashboard) z informacjami na temat przepływu wiadomości
przez system (aktualnie lub za wybrany okres) wraz z
odniesieniem do średniej lub trendu. Z kolei użytkownicy
techniczni mogą korzystać z narzędzi do automatycznego
zbierania wybranych informacji z plików logów wielu
komponentów oprogramowania wraz ze wskazaniem na
konkretne błędy techniczne (np. błąd braku pamięci).
W codziennej pracy oprogramowania zdarzają się sytuacje
mające negatywny wpływ na jego działanie. Do takich
sytuacji może należeć istotna usterka mająca wpływ na
użytkowników (np. dane są niedostępne lub dostępne ze
znaczącym opóźnieniem) lub na aspekty techniczne (np.
poziom zajętości pamięci uniemożliwia pracę innych
komponentów oprogramowania). Kiedy takie sytuacje
zostają dostrzeżone w procesie monitorowania pracy
oprogramowania, należy przeprowadzić ich diagnostykę.
Przez diagnostykę rozumiemy określenie stanu
oprogramowania i identyfikację źródeł usterek. Na przykład
użytkownicy mogą zgłaszać problem z długim
oczekiwaniem na dane. Na bazie danych z monitorowania
pracy oprogramowania może okazać się, że
oprogramowanie przetwarza dane szybko, ale samo dostaje
je z opóźnieniem. W takim przypadku usterka nie leży w
badanym oprogramowaniu (choć użytkownicy tak to
postrzegają), ale w czasie otrzymania danych z innych
systemów. Dalsza analiza może wskazać inne usterki,
niekoniecznie związane z oprogramowaniem (np. zbyt
wolne łącze internetowe). Tego typu problemy nie zawsze
wymagają analizy przez zespół programistów i z
powodzeniem mogą być zdiagnozowane przez inżynierów
wsparcia.
Inny problem, jaki można napotkać podczas utrzymania
oprogramowania, to kończąca się przestrzeń dyskowa.
Jeżeli oprogramowanie utrwali zbyt dużo danych, może
okazać się, że skończy się dostępna przestrzeń dyskowa.
Inżynier wsparcia może spróbować tymczasowych
rozwiązań w postaci usunięcia nieużywanych plików, jednak
czasem trzeba zaangażować poważniejsze środki i zmienić
miejsce składowania danych. Miejsce składowania danych
to element konfiguracji oprogramowania. Zmiana miejsca
składowania danych (np. folderu) może podlegać
rozbudowanej procedurze — począwszy od upewnienia się,
że o określonej porze nikt nie korzysta ze wskazanego
oprogramowania, przez zaplanowanie i poinformowanie o
zmianie, przez jej wykonanie i powiadomienie o jej
wynikach. Samo wykonanie może zawierać szereg innych
kroków, począwszy od zamknięcia jednego lub wielu
komponentów oprogramowania, przez zmianę konfiguracji i
ponowne włączenie wcześniej wyłączonych komponentów.
Zmiana konfiguracji to znaczy zmiana parametrów pracy
oprogramowania. Parametry powinny być przygotowane w
procesie tworzenia oprogramowania i służą do zmiany
konkretnych aspektów jego pracy. Zmiana parametrów
powinna odbywać się bez konieczności odwoływania się do
kodu źródłowego i jego modyfikacji. Konfiguracja może być
zarówno w plikach, rejestrach systemu, jak również w
scentralizowanych miejscach (czasem osobnych serwerach
konfiguracji).
Kolejnym ważnym aspektem jest aktualizacja
oprogramowania. W wielu przypadkach nawet przez kilka
lat po pierwszym wdrożeniu oprogramowanie otrzymuje
nowe wersje. Na wersję składają się poprawki błędów i
nowe funkcje oprogramowania. Poprawki błędów to zmiany
w oprogramowaniu wynikające ze zidentyfikowanych
błędów. Błędy mogą dotyczyć funkcji oprogramowania i
technicznych aspektów ich realizacji. Istotnymi rodzajami
aktualizacji są te związane z bezpieczeństwem. System
przetwarzający dane powinien zapewnić zarówno
bezpieczeństwo samym danym, jak również procesowi ich
transmisji i przetwarzania. Kiedy błąd bezpieczeństwa
zostanie zidentyfikowany, może być użyty do kradzieży lub
nieautoryzowanej modyfikacji danych. Aby zapobiec tego
typu ryzyku, należy niezwłocznie instalować poprawki
związane z bezpieczeństwem.
Kiedy dochodzimy do instalacji poprawek oprogramowania,
należy zwrócić uwagę na to, że poprawki mogą zawierać
błędy. Przed instalacją poprawki na „żywym” systemie
powinno się sprawdzić działanie poprawki w środowisku
bardzo zbliżonym do produkcyjnego (najczęściej
klasyfikowanym jako środowisko produkcyjne równoległe,
ang. PROD-PARALLEL environment). Kiedy poprawka tam
zainstalowana przechodzi testy akceptacyjne
oprogramowania, wtedy może być zainstalowana
(wdrożona) w środowisku produkcyjnym. W przypadku
oprogramowania, z którego korzystają setki tysięcy lub
miliony użytkowników, nawet wdrożenie zmian w
środowisku produkcyjnym podlega skomplikowanym
procedurom. Na przykład zmiany są tak wdrażane, aby
początkowo objęły niewielką grupę odbiorców, która rośnie
z czasem (np. kolejno 1%, 5%, 15%, 50% i 100%). Podczas
kolejnych wdrożeń obejmujących kolejne grupy
użytkowników środowisko produkcyjne jest ściśle
monitorowane pod kątem występowania błędów.
W przypadku wystąpienia istotnych błędów w wyniku
instalacji poprawki w środowisku produkcyjnym (np. kiedy
milion użytkowników nie może zlecić płatności
elektronicznych) należy wykonać procedurę odwrócenia
zmian (ang. rollback) i powrotu do poprzedniej wersji
oprogramowania. Przykład procedury odwrócenia zmian
był zastosowany na szeroką skalę przez zespół techniczny
odpowiedzialny za Microsoft Windows. Po udostępnieniu
wersji Windows 10 datowanej na październik 2018 część
użytkowników (0,01%) doświadczyła utraty plików. Ze
względu na naturę problemu wstrzymano udostępnianie
nowej wersji do czasu naprawienia błędu16.
Podsumowując, utrzymanie oprogramowania polega w
dużej mierze na realizacji procedur i zapewnieniu pracy
oprogramowania. Proces utrzymania jest mocno
uzależniony od profesjonalizmu osób utrzymujących
system, czyli inżynierów wsparcia oraz administratorów
różnych komponentów.