Download as pdf or txt
Download as pdf or txt
You are on page 1of 113

Paweł 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

Dla kogo jest ta książka


Każdy, kto chciałby poznać różne aspekty wytwarzania
oprogramowania, może wynieść z książki coś użytecznego.
Przedstawię w niej różne zadania w pracy z i nad
oprogramowaniem, które mogą dotyczyć rozmaitych
obszarów codziennej pracy. Czasem zdarza się, że jesteśmy
zaangażowani w projekt, który zobowiązuje nas do
przekazania swoich wymagań zespołowi programistów.
Innym razem jesteśmy poproszeni o przetestowanie, czy
oprogramowanie zostało naprawione lub czy spełnia
oczekiwania. Zdarzają się sytuacje, kiedy zgłaszamy
problem z oprogramowaniem i jesteśmy przełączani
pomiędzy kolejnymi grupami techników i inżynierów. Warto
przeczytać tę książkę, aby usprawnić komunikację w takich
momentach.
Pisząc tę książkę, postawiłem sobie następujące cele:
Przybliżenie cyklu życia oprogramowania osobom spoza
środowiska IT, włączając w to aspekty ludzkie,
biznesowe i technologiczne.
Zaznajomienie czytelników z terminologią (blisko 500
terminów) związaną z wytwarzaniem oprogramowania.
Terminy opisane są w języku polskim, ale zawierają
także odpowiedniki w języku angielskim.
Zilustrowanie skomplikowanej terminologii za pomocą
przykładów z życia codziennego (czasem w
humorystycznym podejściu do tematu).
Każdy z rozdziałów stanowi osobny opis konkretnej fazy
cyklu życia oprogramowania (terminy mogą być
powtórzone w różnych kontekstach celem pokazania
powiązań).
Liczę, że Czytelnicy tej książki znajdą odpowiedzi na
pytania i przydatne wskazówki, jak komunikować się
językiem tworzenia oprogramowania.

Dla kogo nie jest ta książka


Odradzam lekturę tej książki osobom, które mają formalne
wykształcenie lub doświadczenie w wytwarzaniu
oprogramowania. Przykłady, jakimi operuję, mogą wydawać
się banalne. Celowo upraszczam wiele tematów i posługuję
się przykładami z życia codziennego.
Głównym założeniem dla tej książki jest bycie
przewodnikiem dla początkujących w środowisku IT.
O autorze
Autor ma ponad dziesięć lat doświadczenia w wytwarzaniu
oprogramowania. Pracował przy wielu projektach, m.in. dla
Microsoftu, Credit Suisse czy Goldman Sachs. Na jego
karierę zawodową złożył się szereg ról w projektach
informatycznych, począwszy od programisty,
administratora, testera, architekta, analityka biznesowego
po menedżera zespołów.
Autor prowadził również zajęcia ze studentami na
Uniwersytecie im. Adama Mickiewicza i Uniwersytecie
Ekonomicznym w Poznaniu.
Prywatnie miłośnik włoskiej motoryzacji, właściciel dwóch
kotów i mąż żony, która rok temu nie znała połowy
zagadnień opisanych w tej książce.
Przedmowa
Rozmowy z ludźmi z IT często przypominają rozmowy z
przybyszami z innej galaktyki. Powszechnie znane są
gwiazdy pokroju Billa Gatesa czy Steve’a Jobsa, które
stworzyły cały układ firm i rozwiązań technologicznych i
wpłynęły na życie milionów ludzi. Za każdym z tych
rozwiązań zazwyczaj stoi sztab specjalistów. Ludzi
pełniących różne funkcje, mocno skoncentrowanych na
aspektach technicznych, a także posługujących się
specjalistycznym żargonem. Osobom spoza światka IT
trudno jest znaleźć wspólny język z przedstawicielami
środowiska, a powszechnie znane terminy nabierają
nowego znaczenia.
Książka ma na celu przedstawienie Czytelnikom
podstawowych terminów z tematyki IT oraz wsparcie ich w
zrozumieniu przedstawicieli świata IT i we współpracy z
nimi. Nie jest to kompendium naukowe czy kurs
technologicznego savoir-vivre’u. Książka stanowi zbiór
historii z życia codziennego okraszonych terminami
technologicznymi z zamiarem przedstawienia szerszego
kontekstu problemów.
Komputery
i oprogramowanie
Książka powstawała w 2018 roku. Opis technologii jest
adekwatny do tego czasu. Być może w momencie, kiedy
książka będzie czytana, część treści zdezaktualizuje się.
Niemniej omówmy stan oprogramowania, z którego
korzystamy w życiu prywatnym i w pracy.

Komputer w życiu codziennym


Zacznijmy od rzeczy w zasięgu ręki, rozpakujmy kieszenie,
portfele i torebki i rzućmy okiem na to, co w nich
znajdziemy. Dla zdecydowanej większości naszych
czytelników pierwszą rzeczą kojarzącą się z
oprogramowaniem będzie telefon komórkowy, smartfon.
Telefony komórkowe od początku miały oprogramowanie.
Pierwotnie oprogramowanie było proste, bo telefony służyły
głównie do rozmów głosowych. Później pojawiły się funkcje
przesyłania krótkich wiadomości tekstowych (SMS). Z
czasem zaistniała możliwość uruchamiania dodatkowych
programów (aplikacji lub gier). Nowe funkcje w telefonach
dodawane są z każdą kolejną generacją urządzeń.
Oprogramowanie w pierwszych telefonach było
klasyfikowane jako oprogramowanie wbudowane lub
oprogramowanie sprzętowe. Oprogramowanie wbudowane
zawiera podstawowe funkcje związane z obsługą sprzętu.
Często oprogramowanie wbudowane jest trudne do
aktualizacji. Bardziej skomplikowane urządzania są
wyposażone w oprogramowanie systemowe. Rolą
oprogramowania systemowego jest zarządzanie
komputerem (w tym wypadku komputerem w postaci
telefonu) i często kryje się pod postacią systemu
operacyjnego. System operacyjny to specjalne
oprogramowanie pozwalające na zarządzanie komputerem.
Jako zarządzanie komputerem rozumiemy zarządzanie
wszystkimi zasobami, w które komputer jest wyposażony.
Przez zasoby rozumiemy: procesor, pamięć operacyjną,
dyskową, ekran, klawiaturę i inne urządzenia służące do
wprowadzania i wyprowadzania danych. Urządzeń może
być o wiele więcej, ale na razie skupmy się na niezbyt
skomplikowanym smartfonie.
Każdy smartfon ma procesor, czyli centralną jednostkę
przetwarzania (ang. central processing unit, CPU), która
służy do realizacji obliczeń. Myśląc o obliczeniach, możemy
mieć w wyobraźni skomplikowane matematyczne formuły.
Nic bardziej mylnego. Procesor komputera to pod tym
względem prosta elektroniczna maszyna. Maszyna, którą
Richard Feynman opisał obrazowo jako potężnego
archiwistę1. Archiwista potrafi przekładać plik kartek z
jednej szufladki do drugiej. Potrafi odczytać kartkę,
wykonać prostą instrukcję zapisaną na niej (np. dodawanie)
i odłożyć do kolejnej szufladki. Zdecydowana większość
procesorów realizuje najprostsze operacje logiczne
(logiczna koniunkcja, alternatywa, negacja), arytmetyczne
(dodawanie, odejmowanie, mnożenie, dzielenie),
matematyczne (np. sinus lub pierwiastek), operacje
manipulacji danymi (zapisz dane lub odczytaj je z pamięci).
Swoją moc obliczeniową procesory zawdzięczają nie tylko
liczbie realizowanych operacji, ale także szybkości ich
realizacji i liczbie operacji wykonanych w tym samym
czasie (operacje równoległe).
Szybkość realizacji operacji jest związana z częstotliwością
taktowania zegara. Cyfrowe urządzenia elektroniczne
muszą być zmuszane do pracy. Każdy takt zegara wywołuje
impuls elektryczny, który zmusza procesor do pracy. Liczba
wystąpień tych impulsów na sekundę to miara
częstotliwości. Częstotliwość mierzymy w hercach, stąd
wielokrotności jednostki herc w oznaczeniach procesora —
megaherc lub gigaherc. Zazwyczaj im wyższa częstotliwość,
tym szybciej procesor realizuje operacje, dlatego
częstotliwość pracy procesora jest jednym z kluczowych
parametrów przy wyborze komputera. Kolejnym z nich jest
liczba operacji wykonywanych w tym samym czasie. Liczba
operacji wykonywanych równolegle jest uzależniona od
liczby rdzeni w procesorze, które odpowiadają za
przetwarzanie oprogramowania. Liczba rdzeni jest zawsze
większa od zera, więc zawsze mamy przynajmniej jeden
rdzeń.
Czasami zdarza się, że korzystając z telefonu, jednocześnie
słuchamy muzyki i rozmawiamy na komunikatorze. Jest to
możliwe nawet na procesorze jednordzeniowym. Wtedy
nasz jedyny rdzeń jest współdzielony przez aktualnie
działające oprogramowanie. Rolą systemu operacyjnego
jest przydzielanie odpowiednim programom kwantu czasu
(bardzo krótkiej chwili), który jest wykorzystywany do
obliczeń dla jednego programu, a następnie przełączanie
pomiędzy jednym i drugim programem. W zdecydowanej
większości przypadków dzieje się to tak szybko, że jako
użytkownicy nie słyszymy przerw w odtwarzaniu muzyki i
nie zauważamy opóźnień we wprowadzeniu tekstu w
komunikatorze. Inaczej jest, gdy mamy zbyt wiele
uruchomionych programów. Wtedy możemy zacząć
odczuwać spowolnienie pracy. Z jednej strony może to być
spowolnienie spowodowane ograniczeniami procesora (i
czasu poświęconego na samo przełączanie), a z drugiej —
ograniczeniami pamięci operacyjnej.
Pamięć operacyjna mieści w sobie wszystkie aktualnie
pracujące programy oraz aktualnie używane dane.
Wszystko, co działa, zabiera nam pamięć operacyjną,
włączając w to wspomniany program komunikatora (z
historią naszej komunikacji widoczną nie tylko na ekranie),
program do odtwarzania muzyki (i te kilka sekund
aktualnie odtwarzane), oprogramowanie systemu
operacyjnego. Jeżeli uruchomimy zbyt wiele programów,
system operacyjny może zgłosić błąd braku wolnej pamięci
(ang. out of memory). Pamięć operacyjna jest ulotna, czyli
po restarcie urządzenia program startuje na nowo. Aby
utrwalić dane, korzystamy z pamięci nieulotnej, czyli
pamięci dyskowej (w telefonie to będzie pamięć
wbudowana lub karta pamięci, w tradycyjnych
komputerach dysk talerzowy lub dysk SSD). Pokrótce
omówimy inne urządzenia wbudowane w smartfon, czyli
ekran i urządzenia wprowadzania danych (dotyk i
przyciski). Bardziej ogólnie nazywamy takie urządzenia
urządzeniami wejścia (wprowadzania) i wyjścia
(wyprowadzania danych), w języku angielskim odpowiednio
input i output.
Rolą systemu operacyjnego jest kontrola, które
oprogramowanie aktualnie może korzystać z danego
urządzenia, np. który program może coś narysować na
ekranie lub odebrać sygnał naciśnięcia przycisku włączenia
aparatu. Tworzenie oprogramowania systemowego jest
skomplikowane i wymaga znajomości wielu zagadnień
(sprzętowych, planowania pracy), stąd na rynku jest
relatywnie niewielu przedstawicieli tej kategorii
oprogramowania. Typowi przedstawiciele oprogramowania
systemowego wywodzą się pośrednio lub bezpośrednio z
systemu Unix. Zarówno główni gracze na rynku, czyli
Google Android i Apple iOS, jak również pomniejsi, jak
Tizen, Ubuntu czy Microsoft Windows, czerpią pełnymi
garściami z rozwiązań spopularyzowanych przez system
Unix. Pomiędzy smartfonami i tradycyjnymi komputerami
osobistymi wyrosło wiele różnych kategorii sprzętu
przenośnego: fablety (połączenie telefonu i tabletu), tablety
(przenośne komputery z dotykowym ekranem o wielkości
większego lub mniejszego zeszytu), netbooki (mniejsze i
lżejsze komputery przenośne), laptopy (komputery
przenośne), stacje robocze (przenośne lub stacjonarne
komputery). Kategorie przenośnych komputerów zaczynają
być bardziej kategoriami marketingowymi niż niosącymi za
sobą istotne różnice technologiczne.
Wróćmy do mniej oczywistych komputerów, z których
często nieświadomie korzystamy. Skupmy się na dwóch:
kartach płatniczych i sprzęcie zaszytym w odzieży.
Większość aktualnie używanych kart płatniczych to kawałek
płaskiego plastiku. Karta płatnicza zawiera mikroprocesor i
ma zatopioną antenę. Karty płatnicze (zarówno debetowe,
jak i kredytowe) nadal posiadają pasek magnetyczny do
pracy ze starszymi terminalami, ale do codziennych
transakcji możemy używać terminali stykowych lub
zbliżeniowych. Terminal stykowy to nic innego jak
komputer, który potrafi połączyć się z procesorem na karcie
kredytowej (ten chropowaty układ o kolorze złocistym) i
zweryfikować naszą transakcję. Tutaj także mamy
oprogramowanie realizowane przez naszą kartę celem
potwierdzenia transakcji. Terminale zbliżeniowe działają na
analogicznej zasadzie, ale komunikacja następuje przez
zbliżenie karty. Prosta zasada działania kryje za sobą
obsługę standardów komunikacji bezprzewodowej NFC
(ang. Near Field Communication) i RFID (ang. Radio
Frequency Identificcation). Z tych samych standardów
nieświadomie korzystamy, kupując wartościowe produkty
(ubrania, perfumy). Jako zabezpieczenie antykradzieżowe
wykorzystuje się minikomputery, które umożliwiają
śledzenie, czy produkt nie wychodzi poza strefę wykreśloną
antenami. Kiedy dokonujemy zakupu, sprzedawca fizycznie
usuwa taki komputer (zdejmuje klips) lub dezaktywuje go
(niszczy jego pamięć za pomocą silnego magnesu). W tym
drugim przypadku nieaktywny element pozostaje z nami na
czas posiadania sprzętu, np. jest wszyty w podszewkę lub
pozostaje wewnątrz podeszwy buta.
Czasem zdarza się, że te same technologie i
oprogramowanie, które służą do zabezpieczenia naszych
płatności czy chronią sklep przed kradzieżą, pozwalają nam
na szybkie dostanie się na teren nieruchomości. Także w
celu zwolnienia elektrycznych zamków i wejścia do domu
lub biura możemy korzystać z kart i telefonów. W tych
rozwiązaniach również spotkamy standardy takie jak RFID
czy NFC. Oczywiście wachlarz rozwiązań jest szeroki,
często zawiera rozwiązania o mniej standardowych
protokołach wymiany danych.
Wróćmy do przeglądu komputerów znajdujących się w
zasięgu naszego wzroku. Po wejściu do domu rozejrzyjmy
się po pomieszczeniach: pokoju dziennym i kuchni. W
pokoju może stać komputer (stacjonarny, laptop, tablet), ale
także telewizor, sprzęt multimedialny czy system
inteligentnego domu. Pomińmy komputer i skupmy się na
pozostałych elementach wyposażenia.
Być może widzimy telewizor. Przedmiot, który podobnie jak
telefon zaczął pełnić inne funkcje niż tylko emisja dźwięku i
obrazu. W zależności od stopnia skomplikowania telewizor
także ma wbudowany jeden lub więcej procesorów.
Podstawowe modele mogą mieć dedykowaną elektronikę z
oprogramowaniem służącym do dekodowania sygnału
(telewizji naziemnej, kablowej czy satelitarnej). W tych
bardziej skomplikowanych mamy małe centrum rozrywki i
funkcje oprogramowania korzystające z Internetu,
określane jako smart (odtwarzanie filmów z Internetu,
udział w wideokonferencjach, instalowanie dodatkowego
oprogramowania czy przeglądanie Internetu). Do
telewizora może być podłączona konsola do gier.
Konsola to zdecydowanie bardzo skomplikowany komputer,
który służy do uruchamiania gier. Gry to specyficzny rodzaj
oprogramowania. Podczas tworzenia gier szczególny nacisk
kładzie się na zanurzenie gracza w wirtualnej
rzeczywistości, do której budowy służy zaawansowana
grafika, dźwięk czy szybka reakcja na wprowadzone dane
(np. komunikaty płynące z kontrolera).
Obok konsoli do gier może znajdować się system
sterowania domem. Jest to elektroniczne urządzenie, które
w odpowiedzi na wypowiadane słowa klucze potrafi
wyłączyć światło w określonym pomieszczeniu, włączyć
określony rodzaj muzyki lub obudzić nas i powiadomić o
prognozie pogody na zewnątrz. Aby działać efektywnie,
urządzenie sterujące musi korzystać z innych urządzeń.
Jednym z nich może być programowalna świetlówka LED.
Przez sieć lokalną (naszą domową) możemy podłączyć się
do określonej żarówki i wydać jej komendę włączenia,
wyłączenia lub zmiany barwy.
Również w kuchni możemy dostrzec programowalne
urządzenie wielofunkcyjne, które po wybraniu
odpowiedniego programu wyda użytkownikowi tylko
komendy wrzucenia odpowiednich składników, a w
rezultacie udostępni gotową potrawę (upieczony chleb,
ugotowaną zupę czy gotowy drink).
Poza zaawansowanymi podzespołami przedstawione
powyżej urządzenia mają jeden wspólny element —
oprogramowanie. Za chwilę zastanowimy się nad
oprogramowaniem w firmie, a później omówimy proces
tworzenia oprogramowania.

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

IBMVER EQU FALSE


IBM EQU IBMVER
IBMJAPVER EQU FALSE ; If TRUE set KANJI true
also
MSVER EQU TRUE
ALTVECT EQU FALSE ; Switch to build ALTVECT
version
HIGHMEM EQU FALSE
KANJI EQU FALSE

IF IBMVER OR IBMJAPVER
NOEXEC EQU TRUE
ELSE
NOEXEC EQU FALSE
ENDIF

; Set to agree with those in DOST:MSHEAD.ASM, ALTVECT


version only
MAJOR_VERSION EQU 2
MINOR_VERSION EQU 0B ;2.11
Kolejny fragment przedstawia kod źródłowy
oprogramowania NodeJS. To fragment pliku node_api.cc z
kodem w języku programowania C++. Kod dostępny jest
pod adresem https://github.com/nodejs/node/.
#include <node_buffer.h>
#include “env.h”
#define NAPI_EXPERIMENTAL
#include “js_native_api_v8.h”
#include “node_api.h”
#include “node_binding.h”
#include “node_errors.h”
#include “node_internals.h”

struct node_napi_env__ : public napi_env__ {


explicit node_napi_env__(v8::Local<v8::Context>
context):
napi_env__(context) {
CHECK_NOT_NULL(node_env());
}
inline node::Environment* node_env() const {
return node::Environment::GetCurrent(context());
}
};
Poniżej zamieszczono kod źródłowy oprogramowania
Apache Tomcat. To fragment pliku HostRuleSet.java z
kodem w języku programowania Java. Kod dostępny jest
pod adresem https://github.com/apache/tomcat/.
package org.apache.catalina.startup;

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”.

1 Richard Feynman, Computer Heuristics Lecture, wykład


Esalen Institute, Big Sur, California, 26 września 1985.
Nagranie w języku angielskim dostępne w serwisie
YouTube pod adresem https://www.youtube.com/watch?
v=EKWGGDXe5MA.
Projekty informatyczne
Wyobraźmy sobie bezę, a konkretnie owocowy deser o
nazwie beza Pavlova. Delikatny deser został nazwany na
cześć rosyjskiej primabaleriny Anny Pavlovej. Kunszt
przygotowania polega na umiejętnym wypieczeniu (a
właściwie wysuszeniu) bezy, a następnie udekorowaniu jej
owocami. Całość słodkiej kompozycji zawitała do wielu
restauracji na całym świecie, jest także dostępna w wielu
restauracjach w Polsce. Istnieje wiele różnych przepisów na
najlepszą bezę. Poniżej zastanowimy się nad procesem
przygotowania bezy. Zaczynając od powstania, do
konsumpcji, musimy wybrać przepis, przygotować
składniki, wymieszać je, upiec, udekorować, a następnie
podać i skonsumować.
Zanim zaczniemy wybierać przepis, musimy zidentyfikować
potrzebę konsumpcji słodyczy. Podczas wizyty w restauracji
możemy wybrać pozycję z karty deserów, a przed
przygotowaniem domowej kolacji zaplanować serwowane
posiłki. Nie zawsze beza będzie komponować się z
pozostałymi elementami menu, ale jeśli docelowy
konsument wyrazi ochotę jej spożycia, to wymaganie dla
szefa kuchni jest jasne.
Kiedy mamy jasno zdefiniowane wymaganie dotyczące
przygotowania bezy, pozostaje wybranie przepisu. Zarówno
w tradycyjnych książkach kucharskich, jak i w Internecie
jest ich wiele. Na podstawie własnych doświadczeń lub
otrzymanych rekomendacji możemy wybrać właściwy na tę
okazję. Możemy dokonać dowolnej wariacji przepisu na
bazie doświadczeń i posiadanych składników. Po wyborze
przepisu2 możemy zabrać się do jego realizacji.
Bazując na wybranym przepisie, musimy przygotować
(czasem udać się na dodatkowe zakupy) składniki. To
pierwsza praca do wykonania. Kiedy w przepisie
potrzebujemy białek jajek, musimy zadbać o ich jakość.
Wybierzemy jajka z zaufanego źródła, a nie
wyeksponowane na słońce w czterdziestostopniowym
upale. Dodatkowo, aby zminimalizować ryzyko zatrucia
pokarmowego naszych gości, dokładnie umyjemy jajka, a
nawet sparzymy je wrzątkiem. Przed przystąpieniem do
właściwej realizacji rozdzielimy jajka na białka i żółtka,
przesiejemy skrobię i umyjemy owoce.
Przystępując do realizacji bezy, w mikserze ubijamy białka
ze szczyptą soli na pianę. Następnie dodajemy cukier,
mieszamy. Powtarzamy ostatni krok do osiągnięcia sztywnej
piany. Później dodajemy ocet winny i skrobię, a następnie
delikatnie mieszamy. Tak powstałą masę wykładamy na
blachę i formujemy odpowiedni kształt. Masę pieczemy na
blasze przez półtorej godziny, a następnie chłodzimy. Tutaj
także musimy weryfikować zarówno czas, jak i postęp (np.
czy beza nie nabiera ciemnożółtego zabarwienia w
przypadku nietrzymania temperatury).
Tak upieczoną bezę dekorujemy owocami na docelowym
naczyniu i podajemy gościom. Często przed podaniem
próbujemy, czy beza ma smak zgodny z zakładanym.
Przygotowaną bezę serwujemy gościom.
Podsumowując, przeszliśmy przez kolejne kroki pieczenia
bezy, czyli:
Po pierwsze zidentyfikowaliśmy zapotrzebowanie
na słodki deser.
Po drugie wybraliśmy przepis i zabezpieczyliśmy
potrzebne składniki.
Po trzecie zapewniliśmy jakość na kolejnych etapach
realizacji.
Po czwarte wymieszaliśmy składniki i upiekliśmy
powstałą masę.
Po piąte zaserwowaliśmy deser gościom.
Powyższe pięć kroków jest analogiczne w wytwarzaniu
oprogramowania. Poniżej przejdziemy przez kolejne kroki,
a także opiszemy jedno z powszechnych podejść do
zarządzania projektami wytwarzania oprogramowania. W
kolejnych rozdziałach będziemy omawiać każdy z kroków z
osobna.

Cykl życia i role projektu


oprogramowania
Cykl życia oprogramowania (ang. software development life
cycle, SDLC) przebiega przez kolejne fazy od identyfikacji
wymagań oraz dokumentacji ich, przez zaprojektowanie,
implementację, testy, wdrożenie i utrzymanie. Poniżej
pokrótce opiszemy kolejne fazy, a w następnych rozdziałach
dokładniej scharakteryzujemy wybrane.
Identyfikacja oraz analiza polega na udokumentowaniu
wymagań różnych interesariuszy projektu (osób
zainteresowanych powstaniem projektu). W tej fazie na
bazie różnych ćwiczeń (warsztatów, wywiadów, syntezy
tekstów) powstają wymagania dla pozostałych członków
zespołu. Wymagania przygotowywane są przez analityków
biznesowych i systemowych (czasem nazywanych
inżynierami wymagań). Analityk biznesowy (ang. business
analyst) to rola dla osoby, która potrafi przeprowadzić
analizę biznesową, czyli zgromadzić istotne czynniki
biznesowe (np. poprawa konkurencyjności lub efektywności
biznesowej). Analityk systemowy (ang. system analyst) to
rola dla osoby, która prowadzi analizę systemową,
techniczną, czyli zbieranie informacji na temat posiadanej
lub planowanej infrastruktury programowej i sprzętowej.
Analityk systemowy potrafi rozwiązać skomplikowane
problemy techniczne. Aby skutecznie przygotowywać
wymagania, analitycy muszą korzystać z wiedzy i
doświadczenia innych osób zaangażowanych w projekt. Do
grona takich osób można zaliczyć użytkowników końcowych
(osoby korzystające z systemu) i ekspertów dziedzinowych
(ang. subject matter expert, SME).
Projektowanie to faza, w której powstaje techniczny projekt
systemu. W tej fazie podejmowane są decyzje, co i jak
będzie zrealizowane. Oznacza to zarówno decyzje na temat
wyglądu interfejsu użytkownika, jak również wybór
technologii i architektury realizacji. W tym etapie mamy do
czynienia z architektami (biznesowymi i technicznymi) i
projektantami (specjalistami od budowania doświadczenia
użytkownika, projektantami interfejsów użytkownika i baz
danych). W tym momencie także zapadają decyzje o
dzieleniu oprogramowania na konkretne komponenty oraz
o wyborze sposobu komunikacji pomiędzy nimi.
Implementacja to faza, podczas której tworzone jest
rozwiązanie. Przez tworzenie oprogramowania rozumiemy
wszystkie aspekty związane z dostarczeniem rozwiązania
dla problemu zdefiniowanego przez wymagania.
Rozwiązaniem może być rekonfiguracja istniejącego
komponentu lub napisanie kodu źródłowego (potocznie
kodowanie, ang. coding) nowego. Zazwyczaj mamy tutaj do
czynienia z programistami różnych technologii oraz innymi
rolami wspomagającymi wytwarzanie oprogramowania.
Programistom poświęcimy osobny rozdział, a w tym miejscu
scharakteryzujemy wszystkich tych, którzy sprawiają, że
programiści mogą pracować efektywnie. Do takiego grona
osób można zaliczyć każdego, kto dostarcza infrastrukturę
służącą do wytwarzania oprogramowania. Przez
infrastrukturę wytwarzania oprogramowania rozumiemy
wszystkie aktywności, od konfiguracji środowisk
programistycznych, przez konfigurację środowiska budowy
kodu i wdrożenia. Czasem takie role łączone są z rolami
programistów — w postaci kultury pracy DevOps. Rola
DevOps to połączenie programisty, dewelopera ze
specjalistą od operacji oprogramowania. Zautomatyzowane
procesy wokół oprogramowania (systemy CD i CI)
pozwalają na łatwiejsze dokonywanie zmian w
oprogramowaniu w dłuższym przedziale czasu z
zachowaniem wysokiej jakości oprogramowania.
Testowanie polega na weryfikacji i walidacji
oprogramowania pod kątem spełniania wymagań oraz
poszukiwania w nim błędów. Poza testowaniem często
zapewnienie jakości obejmuje dodatkowe zadania, takie jak
weryfikacja samego procesu wytwarzania oprogramowania.
Osoby zaangażowane w testowanie mogą sprawdzać, czy
proces wytwarzania jest zgodny z zasadami sztuki.
Dodatkowo zadania wchodzące w zakres testowania
przewidują prowadzenie różnego rodzaju metryk
odzwierciedlających jakość oprogramowania. Testowanie
jest realizowane przez role testerów, specjalistów QA (w
zależności od kultury pracy QA może oznaczać zapewnienie
jakości procesu wytwarzania, Quality Assurance, lub
zapewnienie jakości oprogramowania, Quality Assistance)
lub inżynierów jakości.
Wdrożenie (ang. deployment lub release) to etap, w którym
oprogramowanie zostaje udostępnione szerszemu gronu
odbiorców. Przez szersze grono odbiorców rozumiemy
publiczne testy, kiedy oprogramowanie jest udostępnione
użytkownikom końcowym. Publiczne testy mogą przybierać
wiele postaci. Prawie gotowe (większość funkcji przeszła
wewnętrzne testy) oprogramowanie może podlegać
testowaniu przez końcowych użytkowników (ang. beta
testy). Wdrożenie może polegać na korzystaniu z wersji
kandydackiej wydania (ang. release candidate), gdzie
oprogramowanie zdało publiczne testy, ale jeszcze nie
zostało publicznie zaoferowane. Finalna wersja została już
„wygrzana”, czyli została przetestowana przez różne grupy
użytkowników.
Utrzymanie to faza, podczas której oprogramowanie jest
udostępnione użytkownikom końcowym, używane i
konserwowane. Utrzymanie polega na rozwiązywaniu
bieżących problemów przez kolejne grupy inżynierów
wsparcia. Zgodnie z wieloma standardami (np. ITIL)
rozróżnia się trzy linie wsparcia. Pierwsza linia inżynierów
wsparcia jest odpowiedzialna za kontakt z użytkownikiem
końcowym i rozwiązanie najbardziej podstawowych
problemów (np. sugerowanie restartu oprogramowania).
Druga linia polega na rozwiązywaniu bardziej
skomplikowanych problemów, na przykład wymagających
rekonfiguracji lub weryfikacji poprawności działania po
aktualizacji oprogramowania do nowej wersji. Trzecia linia
zajmuje się najbardziej skomplikowanymi problemami,
które wymagają często modyfikacji oprogramowania. Obok
inżynierów wsparcia są także administratorzy.
Rysunek 1. Porównanie procesu pieczenia ciasta z projektem
wytwarzania oprogramowania

W dużych organizacjach każdy większy komponent


oprogramowania (np. baza danych) ma „opiekuna”,
administratora. Rolą administratora jest utrzymanie
ciągłości działania konkretnego systemu oraz
wprowadzanie w nim zmian i aktualizacji.
Całość procesu wytwarzania oprogramowania wymaga
koordynacji. W zależności od wielkości przygotowywanego
oprogramowania rozróżniamy projekt (np. pojedynczego
komponentu oprogramowania) i portfolio (np. zbiór
projektów komponentów tworzących większy system
oprogramowania). Zdarza się, że z perspektywy zarządczej
portfolio projektów nazywa się programem (nie mylić z
programem realizowanym przez komputer). Nie będziemy
jednak wchodzić w zawiłości terminologii projektu i
programu, jest to zależne od przyjętej metodologii
prowadzenia projektów. Bez względu na konwencję
nazewniczą ról potrzebujemy koordynatorów, menedżerów.
Mamy do czynienia z program i projekt menedżerem
(odpowiednio w języku angielskim: program manager,
project manager), obie role mają akronim PM. Poza
koordynacją zadań PM prowadzi rejestry i informuje
wszystkich interesariuszy o aktualnym stanie procesu
wytwarzania oprogramowania. Typowym prowadzonym
rejestrem jest rejestr ograniczeń (co ogranicza proces
wytwarzania oprogramowania), rejestr ryzyka (co może się
nie wydarzyć i jak temu zaradzić), rejestr przypuszczeń
(czego nie wiemy, ale przypuszczamy, że tak jest), rejestr
problemów (co istotnego wydarzyło się) i rejestr zależności
(od jakich innych rzeczy zależy powodzenie procesu
tworzenia oprogramowania). Taki rejestr w języku
angielskim nazywany jest CRAID log (skrót od ang.
Constraints, Risks, Assumptions, Issues, Dependencies).
Każdy z elementów tego rejestru może mieć swój stan (np.
otwarte, zamknięte), priorytet (np. wysoki, niski) i status w
odniesieniu do zakładanych terminów (np. kodowanie
kolorami świateł ulicznych, RAG, czyli status od nazw
kolorów w języku angielskim: czerwony — red,
pomarańczowy — amber, zielony — green).

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.

2 W tym przykładzie będziemy bazować na przepisie


opublikowanym w serwisie Moje Wypieki, o nazwie „Biała
Pavlova”, dostępnym pod adresem
https://www.mojewypieki.com/przepis/biala-pavlova.
3 W dużych organizacjach dziś także zdarzają się takie
sytuacje (administracja publiczna, międzynarodowe
korporacje, firmy outsourcingowe).
4 Tekst pochodzi ze strony Agile Manifesto w tłumaczeniu
Pawła Lipińskiego, Piotra Żołnierki, Karoliny Polak, Macieja
Nadolskiego, Romana Słocińskiego i Polish Agile
Community; dostępny jest pod adresem
http://agilemanifesto.org/iso/pl/manifesto.html.
5 Technical Debt is a Systemic Problem, Agile Alliance,
dostępny w języku angielskim pod adresem
https://www.agilealliance.org/technical-debt-systemic-
problem/, oraz Introduction to the Technical Debt Concept,
Agile Alliance et al.,
https://www.agilealliance.org/introduction-to-the-technical-
debt-concept/ (ostatni dostęp do obu — październik 2018).
6 Ze względu na brak jednego, niepodważalnego terminu
określającego rolę Scrum Master w języku polskim poniżej
będę konsekwentnie używał terminu „mistrz ceremonii”.
Analiza i wymagania
Decydując się na zakup samochodu, kierujemy się różnymi
czynnikami. Poza ustalonymi widełkami ceny analizujemy
także ofertę konkretnych aut na rynku. Przyjmijmy, że pod
uwagę bierzemy przeznaczenie, wielkość auta, napęd i
zawieszenie oraz roczny koszt posiadania. Jeżeli planujemy
wyprawy w słoneczne weekendy i tylko we dwoje, to
naszym wyborem może być kabriolet lub zgrabne coupe.
Dla sześcioosobowej rodziny z kotem będzie to raczej
miniwan lub auto z dodatkowymi rzędem siedzeń. Auto
służące do dojazdu do pracy w pojedynkę lub na małe
zakupy zapewne może być miejskim maluchem. Podobnie
jest z napędem i zawieszeniem, tutaj także możemy
wybierać pomiędzy napędami na przednią (uniwersalny
charakter i ekonomia), tylną (sportowe zacięcie) lub obie
osie (trudne warunki pogodowe, a w połączeniu z wyższym
zawieszeniem także wymagający teren). Oczekiwania co do
napędu mogą być też związane ze skrzynią biegów. Ktoś
może preferować manualne lub automatyczne, a nawet
konkretny system pracy. Łatwo możemy wyliczyć roczny
koszt paliwa. Wystarczy podzielić roczną, oczekiwaną liczbę
przejechanych kilometrów przez zużycie paliwa, a
następnie pomnożyć przez szacunkowy koszt paliwa.
Przejdźmy do oszacowania pozostałych kosztów
eksploatacji i napraw. Zawsze możemy zasięgnąć opinii
znajomych lub opinii w Internecie, jak bardzo awaryjny jest
dany model oraz jak wysokie są koszty napraw. Innym
aspektem byłoby sprawdzenie rankingów najczęściej
kradzionych aut — czołowa pozycja w tym rankingu może
powodować wyższe składki na ubezpieczenie AC.
Oczywiście na końcu mamy — dla wielu najważniejszy —
czynnik związany z wyglądem i kolorem auta. Przeszliśmy
zaledwie przez kilka kryteriów, jakie możemy wziąć pod
uwagę przy zakupie auta, a i tak nazbierało się tego trochę.
Tego typu analiz dokonujemy na co dzień i w dodatku
często nieświadomie. Myśląc o oprogramowaniu, jakie
będzie potrzebne do realizacji określonych zadań,
powinniśmy zrobić podobną analizę.
Istnieje wiele metod i technik zbierania wymagań. Tutaj nie
będziemy wchodzić w szczegóły warsztatu, raczej omówimy
podstawowe zagadnienia z nimi związane. Spróbujemy
zidentyfikować źródła wymagań, podzielić je na różne
kategorie, a następnie zweryfikować, czy dobrze je
udokumentowaliśmy.

Źródła i podział wymagań


Istnieje wiele różnych źródeł wymagań. W zależności od
przedsięwzięcia, w jakim bierzemy udział, różne czynniki
mogą grać rolę. I tak: w małym przedsięwzięciu, gdzie
chcemy zbadać, czy nasza idea zachwyci naszych
użytkowników, raczej będziemy koncentrowali się na
dostarczeniu wiadomości, co chcemy zaoferować, oraz
wyeksponowaniu wartości tego oprogramowania dla
użytkownika. Głównym celem pewnie nie będzie przychód z
tego oprogramowania, ale raczej opinie i sprzężenie
zwrotne (ang. feedback) informacji, co można zrobić lepiej.
Do opisu takiego podejścia stosuje się termin „minimalny
wartościowy produkt” (ang. Minimal Viable Product, MVP).
W takich okolicznościach koncentrujemy się na szybkim
testowaniu nowych podejść do oferowanego
oprogramowania i nauce. Zazwyczaj mniej czasu spędzamy
na skrupulatnej dokumentacji, a wymagania są
przekazywane w sposób nieformalny zespołowi
technicznemu.
Źródłem wymagań jest właściciel produktu (ang. Product
Owner), który opiera swoje wymagania na informacjach
zaczerpniętych od użytkowników lub interesariuszy
projektu. Przekładając to na rynek samochodowy,
moglibyśmy wyobrazić sobie wypuszczanie najbardziej
podstawowej wersji samochodu (np. z nadwoziem sedan), a
następnie ewolucję w kolejnych wersjach (np. przejście
przez nadwozie kombi do auta z nadwoziem typu pickup i
napędem na cztery koła). Takie podejście w
skomplikowanych systemach jest trudne lub nawet
niemożliwe do realizacji (ciężko sobie wyobrazić ewolucję
rowerka trzykołowego do auta wyścigówki), więc może być
użyte do tworzenia jedynie małych komponentów lub
aspektów pracy aplikacji.
Im większe przedsięwzięcie, im bardziej złożone
oprogramowanie, tym więcej czynników gra rolę. Tak jak na
każdą organizację, tak na oprogramowanie wpływają
czynniki zewnętrzne i czynniki wewnętrzne.
Czynniki zewnętrzne to może być zachowanie konkurencji,
sytuacja rynkowa, omawiani wyżej klienci, dostawcy,
regulator rynku czy technologia. Konkurencja firmy, dla
której pracujemy, może wprowadzić na rynek całkowicie
nowe produkty lub nowe cechy (ang. feature) w
istniejących produktach. Każde przedsięwzięcie może być
przeanalizowane pod kątem rentowności inwestycji, jaki
będzie oczekiwany zysk w porównaniu z nakładem
poniesionym na inwestycję (ang. Return on Investment,
ROI). W przypadku oprogramowania analizowanego na
potrzeby własne przedsięwzięcia możemy próbować
obliczyć całkowity koszt posiadania (ang. Total Cost of
Ownership, TCO). W omawianym powyżej przykładzie TCO
to byłyby wszystkie koszty związane z posiadaniem auta.
Innym aspektem są czynniki związane z możliwościami
dostawy. Przykładowo przygotujemy wymagania dla
samochodu, którego nie ma w ofercie żaden wiodący
producent. Innym aspektem są równie istotne wymagania
regulacyjne (w przypadku oprogramowania mamy
szczególnie istotne zasady przetwarzania danych
osobowych). Wracając do naszego przykładu, nie możemy
poruszać się po drogach publicznych autem bez
działającego oświetlenia. Kiedy planujemy zakup auta na
kolejne lata, to często szacujemy wartość nowatorskich
rozwiązań w stosunku do tych wdrażanych przez
konkurencję (np. czy kupowane przez nas auto ma
określony układ bezpieczeństwa).
Wewnętrzne czynniki wynikają z tego, co mamy i co
możemy zrobić. Jeżeli jesteśmy fanami restaurowania
zabytkowych aut konkretnego producenta, to będziemy
kierowali się już posiadanym instrumentarium technicznym
(np. już posiadanymi częściami, wiedzą oraz narzędziami).
Jeżeli dysponujemy ograniczonym budżetem i jednocześnie
potrafimy dokonać drobnych napraw samodzielnie, to
możemy zdecydować się na zakup używanego auta. Takie
same czynniki są istotne w procesie przygotowywania
wymagań biznesowych dla oprogramowania. W organizacji
możemy mieć umiejętność wytwarzania oprogramowania
na potrzeby własne (ang. in-house development) lub
politykę zakupów u podmiotów trzecich (ang. 3rd parties).
W tym ostatnim przypadku należy zwracać szczególną
uwagę na uzależnienie oprogramowania lub technologii od
jednego dostawcy, który może ograniczyć dołączenie
komponentów od innych dostawców (ang. Vendor lock-in).
Wymagania rozwiązania to będą szczegółowe wymagania
dla określonych funkcji oprogramowania i oczekiwanych
rezultatów. Wymagania techniczne opisują konkretne
aspekty techniczne rozwiązania.
Tak jak mamy różne źródła wymagań, tak rozróżniamy
różne rodzaje wymagań. Mamy wymagania pochodzące od
regulatora, wymagania biznesowe, wymagania rozwiązania
czy wymagania techniczne. Nie ma jednej, uniwersalnej
metody przygotowywania wymagań. Omówimy trzy metody
gromadzenia wymagań: od ogółu do szczegółu (metoda
zstępująca), metoda od szczegółu do ogółu (metoda
wstępująca) i metoda mieszana. Kiedy dokumentujemy
wymagania, idąc od ogółu do szczegółu, zaczynamy od
wymagań najbardziej ogólnych i odległych od docelowego
rozwiązania (np. wymagania regulatora, dokumentacja
scenariuszy użycia). Przechodzimy przez kolejne warstwy
wymagań, które coraz bardziej konkretyzują docelowy
produkt (np. wymagania biznesowe i wymagania
rozwiązania). Kończymy na nie mniej ważnych
wymaganiach technicznych. Metoda wstępująca polega na
pracy nad szczegółami najpierw, a następnie łączeniu
powstałych elementów w jedną całość. Zdarza się, że obie
metody łączy się w jedną metodę mieszaną — tworzy się
zręby wymagań całości, a jednocześnie pracuje się nad
szczegółowymi wymaganiami konkretnego komponentu.
Przechodząc przez różne wymagania, warto z zespołem
projektowym ustalić jeden sposób dokumentacji wymagań.
Na sposób dokumentacji składa się zarówno kategoryzacja
wymagań (omawiane powyżej), jak i forma dokumentacji.
Skrupulatnie przestrzegana umowa zawarta na początku
projektu pozwala uniknąć niespodzianek i umożliwia
łatwiejszą nawigację w dokumentacji. Zanim przejdziemy
do formy dokumentacji konkretnych rodzajów wymagań,
zastanówmy się nad elementami wspólnymi wszystkich
wymagań. Każdy dokument musi mieć swoją nazwę, autora
i wersję, połączenia z innymi dokumentami oraz zawartość.
Nazwa powinna być łatwa do zapamiętania, a jednocześnie
w kilku słowach opisywać, czego wymaganie dotyczy. Każdy
dokument ma swojego autora, a następnie kolejnych
współautorów, którzy modyfikują wymaganie (wymagania
często ewoluują w czasie). Każda zmiana wymagań
powinna być odnotowana i wersjonowana. W dowolnym
momencie powinno być możliwe powrócenie do dowolnej
wersji konkretnego wymagania w czasie (często jest to
wspierane przez narzędzia przechowywania dokumentacji
projektowej). Zdarza się, że zapisanie kolejnej wersji wiąże
się z koniecznością wprowadzenia komentarzy na jej temat
(co i dlaczego zostało zmienione). Warto wprowadzać tego
typu informacje ze względu na dokumentowanie powodów
zmian (np. konsultacja z ekspertem z danej dziedziny).
Ludzka pamięć bywa ulotna, a nowa wersja z odpowiednim
komentarzem zostaje dla kolejnych odbiorców. Powiązanie z
innymi dokumentami wymagań (ang. traceability) to
wskazanie na powiązane wymaganie (np. na takie o
większym lub mniejszym poziomie szczegółowości).
Zawartość dokumentu zawierającego wymagania zależy od
konkretnego rodzaju wymagania. Tutaj także nie mamy
jednej, uniwersalnej metody, a raczej zbiór metod i technik
dostosowanych do potrzeb danego projektu
oprogramowania. Często spotyka się mieszane podejście i
wiele różnych rodzajów dokumentów wymagań
występujących jednocześnie.

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).

7 Lean Change Managment: Innovative Practices For


Managing Organizational Change, JASON LITTLE, 3
października 2014, HMEXPRESS.
Projektowanie
Fiński architekt Eliel Saarinen wskazywał, aby zawsze
projektować rzeczy w odniesieniu do większego kontekstu
— krzesło w pokoju, pokój w domu, budynek w otoczeniu, a
otoczenie na planie miasta8. Tak samo jest z
projektowaniem oprogramowania. Projektuje się w oparciu
o wymagania oraz otoczenie. Otoczeniem będzie inne
oprogramowanie lub sprzęt. Inne oprogramowanie to takie,
z którym projektowane oprogramowanie wymienia dane.
Projektowanie to często podział projektowanego
oprogramowania na mniejsze komponenty i pokazanie
interakcji pomiędzy nimi w konkretnym kontekście. Tak też
jest ze sprzętem, kiedy chcemy pokazać, który komponent
oprogramowania jest realizowany przez wybrany sprzęt.
Podobnie jak w sztukach wizualnych, mamy tu do czynienia
z perspektywami architektonicznymi. Każda perspektywa
pokazuje układ komponentów w konkretnym kontekście lub
interakcji.
W wielu projektach informatycznych nie wyróżnia się
oficjalnej fazy projektowania. Im większy projekt
dostarczenia oprogramowania i im więcej osób
zainteresowanych (interesariusz, ang. stakeholder), tym
większe prawdopodobieństwo formalnej fazy projektowej.
Kiedy oficjalnie przystępujemy do projektowania, to
zazwyczaj znamy już zręby wymagań dla całego
oprogramowania. Często przygotowane są przynajmniej
wysokopoziomowe wymagania dla większości
komponentów (w tym także wymagania niefunkcjonalne).
Podczas projektowania oprogramowania mamy
zaangażowanego projektanta i architekta. Jeżeli oficjalnie
nie ma takich osób w organizacji, to ich rolę przejmuje
najbardziej doświadczony programista. Wyprodukowany
„projekt” to plan prac dla zespołu technicznego, czyli jakie
komponenty należy zbudować oraz jakich technologii do
tego użyć. Istotne jest także zaplanowanie zależności, czyli
systemów, od których zależy sukces powstającego projektu
oprogramowania.

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).

Rysunek 2. Przykład procesu zakupu auta z perspektywy kupującego.


Proces został zamodelowany w BPMN. Adnotacje kolejno opisują:

1. Tor basenowy reprezentujący innego aktora w procesie.


2. Zdarzenie początkowe procesu.
3. Prostą czynność (zadanie).
4. Zwinięty podproces w procesie.
5. Bramkę decyzyjną.
6. Sekwencję przebiegu procesu.
7. Przepływ komunikatu pomiędzy aktorami (np. wysłanie wiadomości e-
mail, telefon).
8. Zdarzenie końcowe projektu

W wielu obszarach dzieli się klasy w systemie na byty


prymitywne (dane takie jak pojedyncza liczba) i złożone
(rekordy z wieloma składowymi, które mogą zawierać
logikę działania). Podział jest zależny od docelowego
środowiska oprogramowania. Poniżej opiszemy typowy
przykład oprogramowania biznesowego, korzystając z
języka Java.
W wielu projektach wprowadza się dodatkowe kategorie
bytów — byty proste i złożone (usługi). Byty proste (w
implementacji używany jest akronim POJO, od
historycznego określenia Plain Old Java Object) mają tylko
pola oraz podstawowe operacje na tych polach (zapis i
odczyt pola). Zaś byty złożone pełnią funkcję komponentów-
usług posiadających logikę. Nie posiadają danych, a w
polach są inne komponenty-usługi (np. klasa
odpowiedzialna za zapis danych nie posiada danych, ale
może mieć pole z komponentem-usługą dostępu do
składnicy danych).
Aby bardziej zobrazować klasy i obiekty, możemy użyć
przykładu z początku tego paragrafu. Możemy mieć
uproszczony model danych, złożony z dwóch klas: krzesło i
stół. Obie klasy mogą mieć cechy wspólne (np. kolor) i
charakterystyczne (np. stół może być okrągły lub
kwadratowy). Jeżeli chcielibyśmy zobrazować pokój, gdzie
stoi jeden stół i cztery krzesła, to powołalibyśmy do życia
pięć obiektów: jeden obiekt klasy stół i cztery obiekty klasy
krzesło. Każdy z obiektów ma mieć swój kolor. Może on być
taki sam, ale równie dobrze każdy mebel może mieć inny
kolor. Malowanie (ustawianie koloru) to operacja
dostarczana przez klasę.
Operacje mogą być wyekstrahowane w interfejsy. Interfejs
zawiera zbiór sygnatur operacji, ale nie ma informacji o ich
realizacji. Realizacja jest znana w klasach realizujących
(implementujących) interfejs.
Podążając za przykładem meblarskim, interfejs opisujący
mebel może mieć operację malowania. Operacja malowania
może być realizowana przez każdą z klas w osobny sposób.
Operacja malowania stołu może sprowadzać się do
malowania drewnianego blatu i pozostawienia
chromowanych nóg nietkniętych. Zaś operacja malowania
krzesła może obejmować zarówno malowanie nóg, jak i
siedziska.
Klasy mogą mieć między sobą relacje, np. korzystania lub
dziedziczenia (rozszerzenia). Mówimy, że jedna z klas
korzysta z drugiej, gdy ma składową typu drugiej klasy lub
jedna z operacji przyjmuje albo zwraca obiekt drugiej klasy.
Z kolei dziedziczenie (rozszerzenie) to zabieg polegający na
zbudowaniu hierarchii klas. Każda klasa w dół hierarchii
zachowuje składowe dane i operacje klasy bazowej,
jednocześnie może dokładać swoje.
Specjalnym rodzajem klas bazowych są klasy abstrakcyjne.
To są klasy, których obiektów nie można powołać do życia, a
jednocześnie pomagają w budowaniu struktury. Klasy
abstrakcyjne mogą deklarować konkretne operacje, jakie
potomne klasy nieabstrakcyjne mają obowiązek dostarczyć
(analogicznie do implementacji interfejsów).
Omówmy przykład krok po kroku:
dodatkowa klasa pokój może korzystać z dwóch klas
krzesła i stołu (rysunek 3);
Rysunek 3. Diagram klas UML. Klasa Pokoj używa klas Stol i Krzeslo

klasa stół i klasa krzesło mogą mieć wspólną klasę


bazową w postaci mebla abstrakcyjnego;
mebel abstrakcyjny może deklarować implementację
interfejsu mebel, z abstrakcyjną operacją malowania
(ustawienia koloru);
osobno stół i osobno krzesło, dziedzicząc po
abstrakcyjnym meblu, muszą dostarczyć sposób
realizacji operacji malowania (rysunek 4);

Rysunek 4. Diagram klas UML z ukazanym dziedziczeniem i


implementacją interfejsu. Klasy Stol i Krzeslo dziedziczą po klasie
MebelAbstrakcyjny. Klasa MebelAbstrakcyjny implementuje interfejs
Mebel
jednocześnie składowa kolor może być zdefiniowana w
klasie bazowej mebla abstrakcyjnego;
krzesło lub stół jest meblem abstrakcyjnym;
może być konkretny obiekt klasy krzesła lub klasy stołu,
ale nie może być konkretnego obiektu mebla
abstrakcyjnego;
każde krzesło jest meblem abstrakcyjnym, ale nie każdy
mebel abstrakcyjny jest krzesłem;
każdy stół jest meblem abstrakcyjnym, ale nie każdy
mebel abstrakcyjny jest stołem;
krzesło nie jest stołem, choć każde z nich jest meblem
abstrakcyjnym, a ponadto krzesło i stół mogą być
dostarczone przez całkowicie inne zespoły.
Moglibyśmy więc uprościć klasę pokoju, tak aby miała
składową tylko w postaci kolekcji mebli. Dzięki takiemu
zabiegowi nie bylibyśmy ograniczeni liczbą krzeseł lub
mebli mogących znaleźć się w pokoju. Moglibyśmy powołać
do życia obiekt pokoju, którego składowymi byłyby same
obiekty klasy krzesło, same obiekty klasy stół lub dowolna
kombinacja obiektów klasy stół lub klasy krzesło (rysunek
5).

Rysunek 5. Diagram klas UML z rozbudowanym modelem klas. Klasa


Pokoj posiada składową typu kolekcji bytów implementujących interfejs
Mebel. W kolekcji mogą znajdować się zarówno obiekty klasy Stol, jak i
Krzeslo
W niektórych wypadkach zasadne jest ograniczenie danego
bytu tylko do ściśle określonych i znanych obiektów. Takie
obiekty będą jednocześnie składowymi tego bytu. W
zależności od języka programowania mówimy wtedy o
enumeracjach lub typach wyliczeniowych. Przykładowym
typem wyliczeniowym może być kolor (mebla). Bardziej
skomplikowany przykład z dziedziczeniem i użyciem typów
wyliczeniowych został pokazany na kolejnym diagramie
(rysunek 6).
Rysunek 6. Przykładowy diagram klas w notacji UML. Po lewej stronie
są klasy z polami. Po prawej stronie są pokazane typy wyliczeniowe
(enumeracje) możliwych wartości, jakie pola w klasach mogą
przyjmować. Warto zwrócić uwagę na relację pomiędzy klasami
Samochód i SamochódOsobowy (relacja dziedziczenia, rozszerzająca).
Nazwa klasy Samochód jest pisana kursywą, co wskazuje na to, że ta
klasa jest klasą abstrakcyjną

Kiedy wiadomo, jakie byty są w systemie oraz w jakie


operacje wchodzą, można zacząć grupować je w większe
komponenty. Komponenty mogą być aktywne lub pasywne.
Komponenty aktywne to takie, które „żyją” w systemie (tzn.
zachowują się jak samodzielne programy, które potrafią
wystartować i kontrolować swój przebieg). Komponenty
pasywne są częścią większych komponentów aktywnych.
Istotny byt, który jest zdefiniowany w wymaganiach
biznesowych, może być realizowany przez jeden lub wiele
komponentów technicznych i podobnie wiele bytów
biznesowych może być realizowanych przez jeden aktywny
komponent techniczny.
Komponenty mogą wymieniać między sobą wiadomości
(analogicznie do wiadomości przekazywanych pomiędzy
różnymi organizacjami w notacji BPMN). Wymiana
wiadomości jest zdefiniowana w kontrakcie. Kontrakt
komponentu opisuje zbiór operacji i obsługiwanych
wiadomości. Kontrakt może definiować sposób
przetwarzania operacji, może być synchroniczny lub
asynchroniczny. Operacje synchroniczne to takie, kiedy
wykonanie operacji powoduje zablokowanie wołającego i
konieczność oczekiwania na odpowiedź. Operacje
asynchroniczne to wykonanie operacji „w tle” i
powiadomienie wołającego o jej zakończeniu. Wiadomość to
zbiór danych, jakie komponent odbiera lub wysyła. Proste
komponenty mogą być realizowane przez jedną klasę
złożoną, usługę.
W przypadku, gdy komponent będzie używany przez
większą liczbę (często nieznanych) programistów,
udostępnia się interfejs programistyczny (ang. application
progamming interface, API). Interfejs programistyczny
pozwala na łatwiejszą integrację skomplikowanych
komponentów z innym oprogramowaniem. API, a także całe
komponenty udostępnione innym programistom często
grupuje się w pakiety (ang. packages) lub biblioteki (ang.
libraries). Narzędzia do modelowania UML pozwalają —
analogicznie jak BPMN — wygenerować gotowy kod, który
programista musi zintegrować i uzupełnić.
W zależności od preferencji danej organizacji projektant lub
architekt może przyjąć założenia co do realizacji projektu.
Założenia przyjmowane są na różnych poziomach
szczegółowości. W zależności od zdefiniowanych wymagań
biznesowych, funkcjonalnych i niefunkcjonalnych architekt,
projektant może przygotować rozwiązanie korzystające z
infrastruktury technicznej będącej w firmie (np. serwery
stoją fizycznie na terenie firmy, w jej serwerowni) lub na
zewnątrz (firma trzecia troszczy się o infrastrukturę
techniczną w ramach kupowanej usługi).
Definicja wymagań niefunkcjonalnych ma szczególne
znaczenie dla architektury całej aplikacji. Jeżeli ilość
przetwarzanych danych jest stała, mierzalna i da się
określić (przynajmniej w określonym przedziale czasu),
pozwala to na najbardziej precyzyjne zaplanowanie
infrastruktury technicznej. Dla znanych obciążeń można
zaplanować zakup lub wynajęcie konkretnej liczby
serwerów. Jeżeli nie jest znane obciążenie lub tym bardziej
jest ono zależne od zainteresowania docelowych
użytkowników, to bardziej korzystne jest dynamiczne
alokowanie infrastruktury technicznej. Dynamiczne
alokowanie infrastruktury technicznej polega na
korzystaniu z infrastruktury jak z usługi (usługi
przetwarzania lub składowania danych, usługi korzystania z
gotowego oprogramowania). Usługa ma określone warunki
pracy (np. dostępność przez 99% czasu) w umowie (ang.
Service Level Agreement, SLA), w dodatku płaci się za czas
korzystania. Taki model dostarczenia infrastruktury
sprzętowej lub programowej ma swoją kategorię „jako
usługa” (odpowiednio ang. Platform as a Service, PaaS,
Software as a Service, SaaS, itd.). Model korzystania z
czegoś w postaci usługi w połączeniu z nieistotnością
informacji o geograficznym położeniu konkretnego serwera
(oraz jego kopii) pozwala nam na nieprzywiązywanie do
tego wagi. Jeżeli jesteśmy w Europie Północnej, to dla nas
bez różnicy jest to, czy serwer znajduje się w Polsce, czy w
Niemczech, Szwecji, Finlandii lub Norwegii. Równie dobrze
taki serwer, którego czas kupujemy jako usługę, mógłby być
w chmurach. I tak dochodzimy do coraz popularniejszego
terminu w ostatnich latach — chmur obliczeniowych (ang.
cloud computing). Chmury obliczeniowe to sposób na
dostarczenie infrastruktury technicznej do celów realizacji
projektów informatycznych z uwypukleniem dynamicznej
alokacji zasobów oraz płatności jak za usługę. Można
korzystać z publicznych chmur dostarczanych przez
wiodące firmy (w momencie pisania tej książki to jest
Amazon Web Services, Microsoft Azure, Google Cloud), ale
wiele dużych organizacji (np. międzynarodowe korporacje
lub administracja publiczna) posiada prywatne chmury
obliczeniowe. W zależności od rodzaju przyjętych założeń i
określonych wymagań można wybrać odpowiedni model
korzystania z infrastruktury (własna infrastruktura,
dzierżawa infrastruktury, prywatna chmura obliczeniowa,
publiczna chmura obliczeniowa lub podejście hybrydowe
łączące dwa lub więcej modeli).
Schodząc na kolejną warstwę projektu, można wybrać
technologię wytwarzania oprogramowania. Na ten wybór
wpływa wiele czynników — wymagania biznesowe,
posiadane doświadczenie w firmie czy też perspektywy na
przyszłość konkretnej technologii i jej dostawcy. Każda
technologia oferuje pewien zakres możliwości, a zarazem
niesie ze sobą zbiór ograniczeń. Jeżeli posiadany przez
organizację zespół techniczny nie korzystał nigdy wcześniej
z jakiejś technologii, to wytworzenie oprogramowania może
zająć więcej czasu, niż gdyby zastosował znaną już
technologię. Coraz to nowe wymagania biznesowe
wymuszają nowe podejście do już znanych rozwiązań, zaś
ewolucja podejścia do wytwarzania oprogramowania także
gra istotną rolę w projektowaniu.
Przed laty korzystano z podejścia strukturalnego (ścisłego
podziału na struktury danych oraz funkcje i procedury na
nich operujące), później obiektowego (podział na
wspomniane klasy i obiekty), przez podejście (funkcje jako
podstawowe byty w oprogramowaniu) i podejście
reaktywne (oprogramowanie działa w reakcji na jakieś
zdarzenie, podobnie jak arkusz kalkulacyjny automatycznie
wylicza sumę dwóch liczb, gdy zmienimy którąkolwiek ze
składowych). Bez względu na modę na konkretną
technologię warto brać pod uwagę jasno sprecyzowane
kontrakty (jaki komponent za co jest odpowiedzialny, co
otrzymuje na wejściu oraz jakimi danymi odpowiada) oraz
protokoły (rozumiane jako ustalony sposób i kolejność
wymiany komunikatów pomiędzy komponentami).
Protokoły zazwyczaj kojarzymy z protokołami sieciowymi,
które służą do wymiany danych pomiędzy komputerami.
Przeciętny użytkownik komputera zapewne dostrzega
różnicę pomiędzy protokołem HTTP (ang. Hypertext
Transport Protocol) i HTTPS (ang. Hypertext Transport
Protocol Secure). W przeglądarce internetowej, w pasku
adresu, drugi protokół jest pokazany z ikoną kłódki
(oznaczenie „bezpiecznej” witryny), a pierwszy bez. Na
bazie jednego protokołu można zbudować kolejny. I tak
możemy wspomnieć o dwóch protokołach do wymiany
danych pomiędzy maszynami, służących do konstrukcji
usług sieciowych, REST i SOAP.
REST (ang. Representational State Transfer) jest
nadbudową nad standardowe polecenia protokołu HTTP,
która umożliwia dostęp i modyfikację odpowiednich
zasobów po stronie serwera (np. danych o samochodach)
przez kombinację odpowiedniego adresu i rodzaju
komunikatu (metody) przesyłanych danych. Protokół REST
jest niezwykle popularny wśród programistów aplikacji
internetowych. Projektant lub architekt musi także podjąć
decyzję na temat sposobu reprezentacji przesyłanych
(czasem także składowanych) danych. W parze z REST
często przesyłane są dane w formatach JSON lub XML.
JSON to akronim od ang. JavaScript Object Notation, czyli
notacji obiektów zapożyczonej z języka JavaScript. Format
pozwala na tworzenie hierarchicznych danych w postaci
tekstowej. Podstawą hierarchii jest obiekt z kolekcją
wpisów w postaci klucz-wartość. Klucz jest unikalną nazwą
w ramach struktury tego obiektu. Wartością może być
tekst, liczba, inny obiekt lub tablica obiektów. Ten inny
obiekt stanowi samodzielny byt, który ma swoją niezależną
hierarchię. JSON ma istotną cechę w postaci zwięzłości
zapisu (każdy obiekt jest przedstawiony w nawiasach
klamrowych, każda tablica w nawiasach kwadratowych,
wpisy są odseparowane przecinkiem, klucz od wartości
dwukropkiem, a tekst jest w apostrofach), co wpływa na
szybkość jego przetwarzania.
Bardziej skomplikowanym formatem reprezentacji danych
jest XML (akronim od eXtensive Markup Language). XML,
podobnie jak JSON, pozwala na reprezentację danych w
postaci hierarchicznej. Przy czym składnia formatu XML
jest bardziej skomplikowana, zawiera podział na elementy i
atrybuty. Każdy element jest obiektem i może mieć atrybuty
oraz inne elementy wewnątrz. Każdy element jest
przedstawiony w nawiasach ostrych. Każdy otwarty
element musi być zamknięty. Kiedy element nie zwiera
innych elementów, można użyć postaci skrótowej
zamknięcia (ukośnik zamknięcia w definicji elementu).
Atrybuty są przypięte do samego elementu (nigdy do jego
zamknięcia). Każdy atrybut to para klucz-wartość. Klucz to
unikalny wpis w definicji elementu. Wartość zawsze jest
odwzorowana w apostrofach. Separatorem pomiędzy
kluczem i wartością atrybutu jest znak równości, zaś
pomiędzy kolejnymi atrybutami są znaki białe (spacje).
Wewnątrz elementu mogą być kolejne składowe — tekst lub
kolejne elementy. Format XML ma standardy opisu danych,
które pozwalają na łatwiejsze przetwarzanie przez maszyny.
Jednym z powszechnie używanych standardów jest schemat
XML (ang. XML Schema, XSD). XML Schema opisuje, jakie
elementy i atrybuty są dozwolone, jaki jest typ danych
zawartości (np. czy jest to liczba, czy wartość liczbowa).
XML Schema powinien być związany z wymaganiami
danych. Z perspektywy implementacji dzięki XML Schema
można łatwo wygenerować klasy typu POJO, przydatne do
integracji z resztą kodu. Format XML jest często używanym
formatem do zapisu danych dla formatu SOAP.
Poniżej przedstawiam przykład dokumentu z danymi w
formacie XML. Pierwsza linia zawiera informacje o
kodowaniu znaków i wersji XML. Zawartość elementu
SamochódOsobowy zawiera kolekcję elementów opisujących
obiekt. Cena ma dodatkowy atrybut o nazwie waluta i
wartości PLN. Pojemność silnika, w kolejnej linii, nie jest
aplikowalna dla samochodów elektrycznych, stąd zamknięty
znacznik bez wartości.
<?xml version=”1.0” encoding=”utf-8”?>
<SamochodOsobowy>
<Marka>BMW</Marka>
<RokProdukcji>2018</RokProdukcji>
<RodzajPaliwa>Elektryczny</RodzajPaliwa>
<Przebieg>0</Przebieg>
<Cena waluta=”PLN”>220000</Cena>
<PojemnoscSilnika />
<Moc>184</Moc>

<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.

8 Wolne tłumaczenie zdania „Always desing a thing by


considering it in its next larger context — a chair in a room,
a room in a house, a house in a en environment, an
environment in a city plan” Eliel Saarinen, z 101 Things
I Learned in Architecture School Matthew Frederick, The
MIT Press, Cambridge. Massachusetts, 2007, strona 92.
9 Opisywany przykład jest inspirowany nadzorem nad
transportem odpadów w Holandii, za Dutch and Belgian
authorities to streamline European waste transportation on
Blockchain, Jonathan Stolk, 26 kwietnia 2018 r., źródło w
języki angielskim, https://medium.com/capptions/dutch-
and-belgian-authorities-to-streamline-european-waste-
transportation-on-blockchain-5e44522d3eb.
Programowanie i programiści
Galaktyka to duży układ gwiazd, pyłu, gazu i ciemnej
materii, który orbituje wokół wspólnego środka masy i jest
związany siłami grawitacji. Gwiazdy emitują
promieniowanie elektromagnetyczne. Ich liczba jest
względnie duża w galaktyce. Ciemna materia nie emituje i
nie odbija promieniowania elektromagnetycznego, ale jej
istnienie udowadniają zawirowania pola grawitacyjnego.
Zdarzają się „zderzenia galaktyk”, które powodują
połączenia orbitujących gwiazd we wspólny zbiór oraz
wyłonienie nowych gwiazd. W momencie pisania tej książki
ludzkość nie dokonała jeszcze intergalaktycznej wyprawy
ani tym bardziej nie odnalazła inteligentnych istot we
Wszechświecie, co nie zmienia faktu, że niektórzy
specjaliści IT postrzegają swoich kolegów jak przybyszy z
innej galaktyki. Ciemną materią jest wszystko to, co
orbituje wokół gwiazd i absorbuje ich promieniowanie
(czytaj: konsumenci technologii, czyli użytkownicy
końcowi). Poniżej przedstawiono kilka galaktyk IT dla
lepszego zrozumienia różnic dzielących ich specjalistów.
Niektórzy przedstawiciele przybyszów z jednej galaktyki IT
odnoszą się do przybyszów z drugiej w taki sam sposób jak
kibice zwaśnionych klubów piłkarskich. Na przełomie lat
osiemdziesiątych i dziewięćdziesiątych dwudziestego wieku
fani komputerów Atari i Amiga (a później fani Nintendo i
Microsoft Xbox z fanami Sony PlayStation) toczyli równie
ogniste spory jak kibice Realu Madryt i FC Barcelony. Dziś
spory w ramach środowiska IT ogniskują się nadal wokół
wielkich firm technologicznych, ale osie tych sporów mają
więcej niuansów. Poniższa charakterystyka nie ma na celu
wskazywania lepszych i gorszych, ale raczej pokazanie
różnych perspektyw w środowisku programistów.

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.

10 Skryptowy język oprogramowania to taki, którego kod


źródłowy (napisany przez programistę) jest przekazywany
w niezmienionej postaci do oprogramowania interpretera
celem wykonania.
Testowanie
Ćmy i motyle to owady bardzo podobne do siebie.
Polowanie na motyle kojarzymy z chwytaniem ich w siatkę
na wiosennej polanie. Dla kontrastu łapanie ćmy wiążemy z
mniej finezyjnym wymachiwaniem klapkiem. Ćmy mają to
do siebie, że czasem, wiedzione świetlnym instynktem,
podejmują się samobójczych misji, celując w rozgrzane
przedmioty. Jednak dzięki temu pewnego dnia 1947 roku
programistka Grace Hopper odnotowała wadliwe działanie
komputera Mark II. Wadliwe działanie było spowodowane
przez znalezioną w elektromechanicznych podzespołach
ćmę. Hopper oraz członkowie jej zespołu zaczęli używać
terminu „bug” (pluskwa, owad) do opisu niewłaściwego
działania komputera. W latach czterdziestych dwudziestego
wieku na całym świecie było zaledwie kilka takich
urządzeń, a informatyka i programowanie dopiero
raczkowały. Podobnie było z językiem opisującym różne
zjawiska związane z komputerami, stąd termin „bug” został
spopularyzowany do opisu dowolnego, nieoczekiwanego
zachowania komputera (niekoniecznie związanego z
insektami). Jak wskazują historycy domeny11, sam termin
został po raz pierwszy użyty w 1873 roku przez Thomasa
Edisona przy okazji pracy nad urządzeniem do przesyłania
telegramów, jednak „bug” na stałe zagościł w żargonie
programistycznym do opisu błędów.
Błędy w oprogramowaniu mają różne pochodzenie,
począwszy od niedociągnięć programistów, przez
nieprzewidziane sytuacje, po błędy wynikające z wymagań.
Liczba błędów w oprogramowaniu jest skorelowana z
szeroko rozumianą jakością oprogramowania.
Niedociągnięcia programistów wynikają z braku wiedzy lub
doświadczenia konkretnego programisty, jego pośpiechu
lub nieuwagi. Takie błędy są zazwyczaj relatywnie łatwe do
naprawienia lub wyłapania w trakcie profesjonalnego
procesu wytwarzania (faza testów).
Nieprzewidziane sytuacje, takie jak nieoczekiwane
działanie sprzętu (np. brak odpowiedzi lub długotrwałe
przetwarzanie), nieoczekiwany ciąg zdarzeń użytkownika
(np. wybieranie opcji na ekranie w kolejności innej niż
zakładana) lub kombinacja obu, są trudniejsze do
naprawienia. Zanim programista będzie mógł skutecznie
naprawić błąd, musi najpierw zidentyfikować przyczynę i
powtórzyć ciąg zdarzeń wiodących do wystąpienia błędu.
Dlatego istotne jest dokładne udokumentowanie zgłoszenia
błędu lub defektu. Na poziomie technicznym analizę
zachowania komputera podczas pracy programu nazywamy
debugowaniem (ang. debugging, inne polskie tłumaczenie
to odpluskwianie). Programiści, pracując nad złożonymi
problemami, często prowadzą analizy typu post mortem,
czyli analizy po fakcie wystąpienia buga. Aby skutecznie
przeprowadzić tego typu analizę, potrzebne są różne
artefakty techniczne, takie jak pliki logów, zrzuty pamięci i
pliki symboli.
Pliki logów to ciąg notowań różnych akcji realizowanych
przez działający program. Ze względu na poziom
skomplikowania współcześnie używanego oprogramowania
nie jest możliwe odnotowanie każdej potencjalnej instrukcji
(względy wydajności; czasem także ze względu na dostępną
powierzchnię dyskową), stąd domyślne logowanie zdarzeń
jest wyłączone. Niektóre aplikacje po wykryciu błędu
automatycznie aktywują logowanie, w innych przypadkach
użytkownik może zostać poproszony o zmianę konfiguracji
programu. Zaawansowany użytkownik może być także
poproszony o zrobienie i udostępnienie zrzutu pamięci
programu.
Zrzut pamięci (ang. memory dump) zawiera stan pracy
programu wraz z wczytanymi bibliotekami, stanem
używanych urządzeń oraz aktualnie przetwarzanymi
danymi użytkownika. Zrobienie zrzutu pamięci działającego
programu umożliwione jest przez system operacyjny
(powstały plik może mieć duży rozmiar). Za pomocą
narzędzi programistycznych można otworzyć plik zrzutu
pamięci i przeanalizować ciąg aktualnie przetwarzanych
instrukcji oraz zawartości pamięci. Pliki symboli pozwalają
programiście połączyć zrzut pamięci z kodem źródłowym
programu (wiele instrukcji wykonywanego programu
przekłada się na jedną instrukcję w kodzie źródłowym) .
Najtrudniejszymi błędami są błędy wynikające z wadliwych,
nieprecyzyjnych lub sprzecznych wymagań. Takie błędy
wymagają ponownej analizy wymagań, poprawienia ich
oraz zaprojektowania, zaimplementowania i przetestowania
nowego rozwiązania. Ze względu na mnogość zadań i czas
poświęcony na dostarczenie poprawionej wersji jest to
najbardziej kosztowny bug.

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).

11 Did You Know? Edison Coined the Term „Bug”, Bugs


have plagued technologists for centuries, Alexander B.
Magoun, Paul Israel, IEEE, 23 sierpnia 2013, w języku
angielskim dostępny pod adresem
http://theinstitute.ieee.org/tech-history/technology-
history/did-you-know-edison-coined-the-term-bug.
12 Wpis w Wikipedii w języku angielskim dostępny pod
adresem
https://en.wikipedia.org/wiki/Knuth_reward_check.
13 Facebook hands out White Hat debit cards to hackers,
ELINOR MILLS, CNET, 31 grudnia 2011, w języku
angielskim dostępny pod adresem
https://www.cnet.com/news/facebook-hands-out-white-hat-
debit-cards-to-hackers/.
14 Strona Microsoft Windows Insider Program, dostępna
pod adresem: https://insider.windows.com/pl-pl/.
Wdrożenie i utrzymanie
Wdrożenie i utrzymanie to dwie osobne fazy cyklu życia
oprogramowania. W zależności od rodzaju organizacji (a
także charakteru zespołu technicznego) nacisk jest
kładziony na wybraną z nich lub obie. Wdrożenie to
zazwyczaj relatywnie krótki moment w cyklu życia
oprogramowania, czasem powtarzany wielokrotnie.
Utrzymanie to istotny proces, od którego jakości zależy
komfort i bezpieczeństwo pracy wielu użytkowników
końcowych. W tym rozdziale krótko przybliżymy wdrożenie,
a następnie skoncentrujemy się na utrzymaniu.

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.

15 Eksperyment Mojave, serwis Wikipedia, opis w języku


angielskim dostępny pod adresem
https://en.wikipedia.org/wiki/Mojave_Experiment.
16 Updated version of Windows 10 October 2018 Update
released to Windows Insiders, wpis na blogu Microsoft
Windows, datowany na 8 października 2018, w języku
angielskim dostępny pod adresem
https://blogs.windows.com/windowsexperience/2018/10/09/
updated-version-of-windows-10-october-2018-update-
released-to-windows-insiders/ (ostatni dostęp październik
2018).
Spis treści
Wstęp
Dla kogo jest ta książka
Dla kogo nie jest ta książka
O autorze
Przedmowa
Komputery i oprogramowanie
Komputer w życiu codziennym
Komputer i oprogramowanie w firmie
Komputer w wytwarzaniu oprogramowania
Działanie oprogramowania
Projekty informatyczne
Cykl życia i role projektu oprogramowania
Zwinne podejście
Analiza i wymagania
Źródła i podział wymagań
Dokumenty wymagań
Praca nad nie swoimi wymaganiami
Projektowanie
Krok po kroku
Przykład wymagań kształtujących architekturę
Programowanie i programiści
Podział programistów
Programista programiście nierówny
Nieprogramiści tworzący oprogramowanie
Testowanie
Zgłoszenia błędów
Rodzaje testów
Środowisko testowe
Wdrożenie i utrzymanie
Wdrożenie
Utrzymanie

You might also like