Blog

24 kwietnia 2015 Wojciech Zawistowski

Pojedynczy i właściwy poziom abstrakcji na poziomie specyfikacji poprzez przykład – 5 reguł jak pisać dobre przykłady

Pojedynczy i właściwy poziom abstrakcji na poziomie specyfikacji poprzez przykład – 5 reguł jak pisać dobre przykłady

W jednym z moich poprzednich postów pisałem o pojedynczym i właściwym poziomie abstrakcji. Skupiłem się w tamtym artykule na poziomie kodu i testów jednostkowych. Jednakże, zasada pojedynczego i właściwego poziomu abstrakcji ma zastosowanie także na dużo wyższym poziomie – na poziomie specyfikacji poprzez przykład (specification by example). Działają tu jednak odmienne czynniki niż w przypadku testów jednostkowych, warto więc poświęcić temu odrębną dyskusję.

1. Pisz specyfikację by zaangażować interesariuszy

Głównym czynnikiem, narzucającym kształt specyfikacji poprzez przykład, jest dyskusja z interesariuszami. Podstawowym powodem formułowania wymagań w postaci przykładów jest to, że taka forma ułatwia dialog. Nie traktujemy specyfikacji jako czegoś zamkniętego, co interesariusze są nam w stanie od razu przekazać w idealnej, ostatecznej formie, ale raczej jako coś, co będzie iteracyjnie, a co ważniejsze wspólnie udoskonalane.

Przykłady świetnie się do tego celu nadają, ponieważ opisują konkretne, wąskie przypadki a nie złożone i abstrakcyjne reguły. Zadają one sprecyzowane pytania, jak system powinien w konkretnej sytuacji się zachowywać, na które dużo łatwiej jest odpowiedzieć niż zweryfikować poprawność całego złożonego algorytmu na raz.

Przykłady zapewniają też wiele innych korzyści, np. doskonale sprawdzają się jako kryteria akceptacji i dobrze poddają się automatyzacji, jednak zalety te są produktem ubocznym. Głównym obszarem zainteresowania specyfikacji poprzez przykład są interesariusze. Należy o tym pamiętać, ponieważ wszystkie pozostałe wskazówki wypływają bezpośrednio z tego faktu.

2. Ukrywaj szczegóły implementacji

By na prawdę zaangażować interesariuszy, musisz pisać przykłady na takim poziomie abstrakcji, który rozumieją i którym są zainteresowani. Przeważnie jest to poziom biznesowych reguł i zachowań a nie szczegółów implementacji czy elementów interfejsu.

Na przykład, taka specyfikacja nie operuje na poziomie abstrakcji odpowiednim dla interesariuszy:

Given I have "some item" in my shopping cart
    And I'm on the "delivery options" screen
When I click a "priority delivery" radio button
    And I click "proceed to the checkout" link
Then I should be redirected to the "checkout" screen
    And the "total" input field should equal "some item" price plus "priority delivery" cost

Interesariuszy nie interesują przyciski, pola tekstowe, linki ani ekrany. Interesariusze myślą w kategoriach terminów biznesowych:

Given I have "some item" in my shopping cart
    And I've choosen priority delivery
When I proceed to checkout
Then my total price should equal "some item" price plus "priority delivery" cost

Często określa się to jako regułę “opisuj co a nie jak” (“describe what not how“) i jest to zasada bardzo podobna do pozostawania na właściwym poziomie abstrakcji w testach jednostkowych. Różnica leży w tym, co determinuje jaki poziom abstrakcji jest właściwy. W przypadku testów jednostkowych wynika to z warstwy systemu, której testowana klasa jest częścią; w przypadku specyfikacji poprzez przykład wynika to z tego, jaki interesariusz ma być odbiorcą specyfikacji.

3. Używaj konkretnych przykładów (nie generalizuj)

Celem specyfikacji poprzez przykład jest, jak sama nazwa wskazuje, wyspecyfikowanie jak system powinien działać poprzez konkretne przykłady jego zachowania zamiast poprzez uogólniony opis. Kluczowe jest tutaj słowo konkretne. Jeśli podasz zbyt ogólny przykład, przestanie on być przykładem a zacznie skłaniać się z powrotem ku “tradycyjnemu” opisowi wymagań.

W związku z tym, poniższe przykłady są zbyt rozmyte:

Given I've bought only digital items
Then I should not be charged for delivery

---

Given I've bought some digital items
    And I've bought at least one physical item
Then I should be charged for delivery

Bardziej konkretne przykłady, takie jak te, są lepsze:

Given I've bought a PDF book and an mp3 song
Then I should not be charged for delivery

---

Given I've bought a PDF book and an mp3 song and a computer mouse
Then I should be charged for delivery

Może się wydawać, że jest to sprzeczne z regułą “opisuj co a nie jak”. Jednakże, skonkretyzowanie przykładów poprzez zawarcie w nich szczegółów biznesowych nie jest tym samym, co wyciek szczegółów implementacji. Szczegóły implementacji powodują, że specyfikacja staje się niezrozumiała i krucha. Konkretne przykłady biznesowe umożliwiają dyskusję z interesariuszami, która jest podstawowym celem specyfikacji poprzez przykład.

Przykład można potraktować jako pytanie, na które chcemy uzyskać od interesariusza odpowiedź: “Zakładając takie warunki początkowe, gdy wykonana zostanie następująca akcja, powinniśmy oczekiwać takich rezultatów – czy to prawda?”. O wiele łatwiej odpowiadać na tego typu pytania, gdy są one konkretne. Mętne, uogólnione algorytmy czy reguły pozostawiają za dużo miejsca na urkyte przypadki brzegowe, których interesariusz może nie zauważyć.

Jak szczegółowe powinny być w takim razie przykłady? Czy w powyższym przykładzie, opisującym klienta kupującego książkę, wystarczy że określimy że jest to książka elektroniczna? Czy musimy być bardziej precyzyjni i powiedzieć, że jest to książka w formacie PDF? Czy powinniśmy podać tytuł, autora i kod ISBN książki?

To prowadzi nas do kolejnego istotnego spostrzeżenia:

4. Używaj takiego poziomu abstrakcji, jaki jest istotny dla danego interesariusza

Projekt prawie zawsze ma wielu interesariuszy, operujących na różnych poziomach abstrakcji, patrzących na system z różnych perspektyw. By zaimplementować system poprawnie, konieczny jest dialog ze wszystkimi tymi interesariuszami.

W związku z tym, będziesz pisał przykłady skierowane do różnych osób. Zgodnie z tym, co omówiliśmy w poprzedniej sekcji, by dyskusja była wartościowa, przykłady muszą być konkretne – muszą zawierać odpowiednie szczegóły biznesowe. Jest tu jednak pewien haczyk: dla różnych osób różny poziom szczegółowości jest odpowiedni. Nie ma jednego poziomu abstrakcji, który odpowiadał by wszystkim.

Rozważmy przedstawiciela działu sprzedaży i prezesa firmy, czytających przykłady dotyczące “zaufanych” klientów. Prezes będzie prawdopodobnie chciał własnoręcznie podejmować decyzje związane z wysoko-poziomową polityką firmy, np. takie jak poniżej:

Given a Trusted Customer
When he delays with payment
Then he should still be able to make new purchases

Given a Untrusted Customer
When he delays with payment
Then he should not be able to make any new purchases

Z drugiej strony, prezes najprawdopodobniej oddeleguje decyzje dotyczące nisko-poziomowych szczegółów, takich jak poniżej, ekspertowi domenowemu z działu sprzedaży:

Given an order with a bank transfer form of payment
When the current date is 7 days from purchase date
    And there is still no payment received
Then the payment should be considered delayed

Given an order with a credit card form of payment
When the current date is 2 days from purchase date
    And the purchase week day is Mon, Tue, Wed or Thu
    And there is still no payment received
Then the payment should be considered delayed

Given an order with a credit card form of payment
When the purchase week day is Fri, Sat or Sun
    And the wire transfer week day is Tue
Then the payment should be considered on time

Zarówno prezes jak i ekspert sprzedażowy potrzebują konkretnych przykładów, by móc na ich temat rozumować, jednak “konkretność” dla każdego z nich oznacza zupełnie inny poziom detali.

Dla każdego przykładu, jaki piszesz, musisz wiedzieć dla kogo piszesz ten przykład i w świadomy sposób dobrać odpowiedni poziom szczegółów.

5. Kieruj dany przykład tylko do jednego interesariusza

Ponieważ, jak przed chwilą omówiliśmy, różni interesariusze potrzebują różnego poziomu szczegółów, wydaje się oczywiste, że próba zaangażowania więcej niż jednego interesariusza w dany przykład może zaowocować niejasnym opisem, miesząjącym wysoko- i nisko-poziomowe szczegóły tak jak poniżej:

Given a Untrusted Customer
    And an order with a credit card form of payment
When the current date is 2 days from purchase date
    And the purchase week day is Mon, Tue, Wed or Thu
    And there is still no payment received
Then the customer should not be able to make any new purchases

Powyższy przykład jest niepotrzebnie skomplikowany i nieczytelny. Jednakże, nie jest to największy problem. Jak wspomniałem na początku tego artykułu, podstawowym powodem opisywania wymagań w postaci przykładów jest ułatwienie dyskusji z interesariuszami. W przypadku powyższego przykładu, taka dyskusja staje się prawie niemożliwa.

Nie możesz zapytać prezesa czy powyższy przykład jest poprawny, ponieważ pogubi się on w nisko-poziomowych szczegółach tego, w jaki sposób przelewy bankowe mogą być opóźnione w weekendy itp. To są operacyjne detale, które powinny być dogłębnie zrozumiane przez wyspecjalizowanego eksperta domenowego, ale są bez znaczenia na poziomie prezesa. Eksperta domenowego z działu sprzedaży także nie możesz zapytać, czy powyższy przykład jest poprawny, ponieważ mimo że będzie on rozumiał wszystkie nisko-poziomowe detale, nie ma kompetencji by zweryfikować czy decyzja dotycząca strategii firmy wobec zaufanych i nie zaufanych klientów jest poprawna.

By w pełni zweryfikować powyższy przykład, musiałbyś zgromadzić obie osoby, prezesa i sprzedażowego eksperta domenowego, w tym samym miejscu i czasie, tak by wspólnie wypracowali odpowiedź, co czyni duskusję mniej efektywną i trudniejszą do zaplanowania.

Podsumowanie

Mimo, że może nie być to oczywiste na pierwszy rzut oka, te same reguły, którymi rządzą się testy jednostkowe (czy w ogóle “czysty” kod) mają zastosowanie także na poziomie specyfikacji poprzez przykład. Różnica leży w tym, co kieruje tymi regułami: łatwość dyskusji z interesariuszami zamiast czytelnośći kodu.

Ważne, żeby pamiętać tą relację pomiędzy zasadami czystego kodu a przykładami. Przykłady są często pisane przez developerów. Zmieniając minimalnie punkt widzenia – myśląc w kategoriach interesariuszy zamiast w kategoriach architektury – developerzy mogą wykorzystać większość wiedzy, którą już posiadają, stosując reguły takie jak Single Level of Abstraction Principle do pisania lepszych, skuteczniejszych przykładów.


Jak Ty piszesz swoje przykłady? Czy zauważyłeś w nich jakieś inne wzorce, podobne do wzorców związanych z kodem? Podziel się swoimi doświadczeniami w komentarzach poniżej!

Zobacz na blogu

09.09.2022
Marcin Jahn
It’s Not Just HTTP It’s Not Just HTTP

In today’s world of cloud-based solutions, distributed systems, and microservices-based architectures, network communication is a...

23.08.2022
Adam Mrowiec
Konferencja IPC 2022 Berlin Konferencja IPC 2022 Berlin

Pandemia wreszcie się kończy, dlatego w tym roku postanowiliśmy wrócić do naszych wyjazdów na konferencje....