Design + Testy + Development koszyka e-commerce

Design + Testy + Development koszyka e-commerce
jgf j
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 13
0

Jeśli można - prośba o feedback ad. mój proces developmentu. Przepraszam jeśli to nieodpowiedni dział.

Cel: feedback pod uczenie się -> poprawienie swojego zrozumienia.

Mój ogólny workflow: PRD feature -> design/mock ui/ux -> Techniczny Design Rozwiązania -> Plan testów -> Testy -> Implementacja -> Green/Refactor jeśli potrzebny. Jest w tym coś niepotrzebnego/coś słabego?

  1. PRD. Na ile jest profesjonalny/solidnie zrobiony? 1-10
  2. Organizacja testów (data layer -> unit testy, view layer -> integration test, + happy path e2e). Na ile profesjonalnie/dobrze? 1-10
  3. Chciałbym wrzucić całe testy pod feedback ale nie chce spamować. Na ile to wygląda profesjonalnie/solidnie z 2 wyrywków, na ile taki pattern jest dobry/solidny? 1-10

Zakres: globalny koszyk. (stronę koszyka oddzieliłem na osobny produkt feature, tak samo jak checkout queue/checkout flow/adres/shipping/stripe/return page)

PRD:
Goal: capture accurate minimalest requirements for coherent, intuitive, simple user see -> actions -> goal experience
Criteria: 0 unnecessary verbiage, 0 unnecessary characters

Feature: Non-Local Basket

Perspective: User interacting with basket controls across any product page

1. Objective

Users add, modify, and remove basket items from any page with persistent state across sessions and tabs.

2. Scope Boundaries

In Scope:

  • User perspective - what user sees, can do and experiences
  • Global basket state management
  • Product controls (add, increment, decrement, remove)
  • Persistence across page refreshes
  • Cross-tab synchronization
  • Header badge count display
  • localStorage storage

Out of Scope:

  • Anything outside user perspective - what happens under the hood, how it's implemented, etc.
  • Basket page rendering
  • CMS inventory sync
  • Stock availability display
  • Checkout process
  • Payment processing

3. Requirements & Definition of Done (DoD)

[ ] DoD [1]: When I add a product, I want it in my basket

  • When I go to any product page, I see add to basket button
  • I click add button
  • Product appears in basket with quantity 1
  • Header badge (basket button) count increments

[ ] DoD [2]: When I increment quantity, I want count updated up to available stock

  • When I go to any product page with item in basket, I see increment button
  • I click increment button
  • Item quantity increases by 1
  • Header badge count updates by 1 increment
  • When I try to see how far I can increment, I see that at some point increment button is disabled - I reached available stock limit

[ ] DoD [3]: When I decrement quantity, I want count updated

  • When I go to any product page with item in basket, I see decrement button
  • I click decrement button
  • Item quantity decreases by 1
  • Header badge count updates by 1 decrement

[ ] DoD [4]: When I decrement to zero, I want item removed

  • When I go to any product page with item quantity 1, I see decrement button
  • I click decrement button
  • Item removes from basket
  • Header badge count updates

[ ] DoD [5]: When I refresh page, I want basket preserved

  • When I go to any page with items in basket
  • I refresh page
  • Basket items remain with same quantities
  • Header badge count remains correct

[ ] DoD [6]: When I open new tab, I want basket synced

  • When I go to any page with items in basket in tab A
  • I open same site in new tab B
  • I see that basket count badge is up to date on tab B
  • I add/remove/increment/decrement a few products in tab B
  • I go back to tab A and see that basket count badge is up to date on tab A after my changes in tab B

[ ] DoD [7]: When I click basket button, I want to go to basket page

  • When I go to any page, I see basket button in header
  • I click basket button
  • I navigate to /basket page

Screenshot drzewka plików i folderów:
screenshot-20260505192723.png
Diagram:
screenshot-20260505193228.png

Wyrywek z unit testów:

Kopiuj
describe('when decrementing quantity to 0', () => {
  it('removes item from basket items array', () => {
    // ARRANGE - setup test state with product quantity = 1
    const productId = 'product-1'
    useBasketStore.getState().addProduct(productId, 100, 10)

    // ACT - call decrementQuantity with productId
    useBasketStore.getState().decrementQuantity(productId)

    // ASSERT - verify item removed from items array
    const state = useBasketStore.getState()
    expect(state.items).toHaveLength(0)
  })
})

Wyrywek z integration testów:

Kopiuj
describe('when user clicks add button (product page)', () => {
  it('hides add button and renders increment/decrement buttons', () => {
    // ARRANGE - setup test state with rendered BasketControls, product not in basket
    const productId = 'product-1'
    const displayPriceAtAdd = 100
    const availableStockAtAdd = 10

    render(
      <BasketControls
        productId={productId}
        displayPriceAtAdd={displayPriceAtAdd}
        availableStockAtAdd={availableStockAtAdd}
        isBasketPage={false}
      />
    )

    // ACT - trigger user click on add button
    act(() => {
      screen.getByTestId('add-to-basket-product-1').click()
    })

    // ASSERT - verify add button no longer renders
    expect(screen.queryByTestId('add-to-basket-product-1')).not.toBeInTheDocument()
    // ASSERT - verify increment/decrement buttons render
    expect(screen.getByTestId('increment-product-1')).toBeInTheDocument()
    expect(screen.getByTestId('decrement-product-1')).toBeInTheDocument()
  })
});
Charles_Ray
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1935
0

Nie bardzo wiem, co chcesz uzyskać. Generalnie tak w uproszczeniu wygląda niepełny proces SDLC.

Brakuje między innymi:

  1. wdrożenia, monitoringu, alertów, etc.
  2. makiet/mockupów UI (Figma)
  3. Części backendowej (API, aplikacja, baza danych)

Zwykle w zespole produktowym PRD pisze PM, a makiety UI robi UX Designer.

Ponadto, czasami pisze się testy przed kodem produkcyjnym. A w świecie AI, kodu czasem w ogóle się nie dotyka :)

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10295
1

Perspective: User interacting with basket controls across any product page
Objective
Users add, modify, and remove basket items from any page with persistent state across sessions and tabs.

Nie potrzebnie zawiera techniczne szczegóły.

Kopiuj
In Scope:

User perspective - what user sees, can do and experiences
Global basket state management
Product controls (add, increment, decrement, remove)
Persistence across page refreshes
Cross-tab synchronization
Header badge count display
localStorage storage

Out of Scope:

Anything outside user perspective - what happens under the hood, how it's implemented, etc.
Basket page rendering
CMS inventory sync
Stock availability display
Checkout process
Payment processing

Te dwie listy się ze sobą kłócą. Niby masz napisane że "what happens under the hood, how it's implemented" jest out of scope, ale w "In scope" masz takie rzeczy jak localStorage i taby. Praktycznie wszystko co napisałeś w "in scope" to są szczegóły implementacjne.

  1. Requirements & Definition of Done (DoD)

Niby dobrze, ale mają za dużo technicznych szczegółów, to spowalnia i upośledza development. Niepotrzebnie zawiera szczegóły nt interfejsu użytkownika. DoD powinny zawierać informacje nt tego co użytkownik chce zrobić, a nie to jak to zrobi. Twoja lista zawiera praktycznie samą listę jak zrobić, a nie co i po co. Taka DoD jest mało pomocna, bo prawie każdy refaktor aplikacji ją zinwaliduje. Szczegóły nt interfejsu użytkownika powinny powstać podczas dewelopmentu, a nie wyspecyfikowane zanim się zacznie.

Takie coś: "When I click basket button, I want to go to basket page" to w żaden sposób nie jest DoD, tylko specyfikowanie interfejsu użytkownika. Dużo za wcześnie na to.

Wyrywek z unit testów:
[...]
Wyrywek z integration testów:

Noooo, nie wiem czy to co tam pokazałeś to unit testy 🤔 Tylko pod warunkiem że useBasketStore nie dotyka nic z UI, w co wątpię. Jeśli useBasketStore dotyka UI to jest tak samo integracyjnym testem jak to z komponentem.

Co do samych testów, idę o zakład że każdy sensowny refaktor sprawi że te testy zaczną failować, więc ja bym się zastanowił czy te testy dają Ci coś sensownego? 😕

Jak moim zdaniem powinno być to zrobione i poważniejsze problemy

Według mnie główny problem Twojego DoD to to, że one tak na prawdę nie precyzują żadnej wartości dla użytkownika. Owszem, dodawanie i usuwanie rzeczy z koszyka, ale po co? Nikt normalny nie korzysta z koszyka dla samego koszyka, najpewniej korzysta z tego po coś więcej - np. że jak przejdzie do płatności to są tam rzeczy z tego koszyka. Po co ktoś miałby wkładać rzeczy do koszyka skoro nie może nic więcej zrobić?

Drugim problemem jest to że te DoD bardzo pachną jakby ktoś chciał wyspecyfikować UI i ukrył to w DoD. Jeśli te DoD napisał jakiś PO, to bardzo to wali micromanagmentem. Jeśli ja miałbym napisać DoD pod koszyk to wyglądałyby mniej więcej tak:

  • Dodałem przedmiot A do koszyka, jak przejdę do kasy, to przedmiot A jest na paragonie
  • Dodałem i usunąłem przedmiot A z koszyka, jak przejdę do kasy, to paragon jest pusty
  • Jak dodałem przedmiot A do koszyka dwa razy, jak przejdę do kasy, to przedmiot A jest w paragonie z mnożnikiem x2
  • Dodałem przedmiot A do koszyka dwa razy, usunę przedmiot A z koszyka, jak przejdę do kasy, to przedmiot A jest w paragonie z mnożnikiem x1
  • Jeśli dodam przedmiot A do koszyka, to przedmiot jest w koszyku.
  • Dodałem przedmiot A do koszyka, jeśli usunę przedmiot A z koszyka, to koszyk jest pusty.
  • Dodałem przedmiot do koszyka, jeśli zamknę i otworzę sesję, koszyk zawiera dodany przedmiot.

Nie powiedziałem nic o stronah, przyciskach, adresach url ani klikaniu, i tak powinno być.

A co do testów - żeby były sensowne też najlepiej gdyby nie zawierały szczegółów implementacyjnych.

Zamiast:

PRD feature -> design/mock ui/ux -> Techniczny Design Rozwiązania -> Plan testów -> Testy -> Implementacja -> Green/Refactor jeśli potrzebny.

lepiej byłoby

Określenie spodziewanego benefitu -> hipoteza feature'a który ma dosatrczyć ten benefit -> Testy akceptacyjne -> Implementacja high-levelowa -> TDD i unit testy -> implementacja low-levelowa -> Indykator czy dostarczony feature przyniósł określony benefit -> Jeśli tak, zostawić, jeśli nie, revert.

Z całą pewnością nie powinieneś zaczynać od UI, tylko od dostarczenia funkcjonanosci. Ten "design/mock ui/ux" to powinien być jeden z ostanich kroków.

jgf j
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 13
0

Dzięki za feedback!

Nauka:

  • nie dodawać JAK w PRD ad. wartość usera, dawać samo CO
  • nie specyfikować JAK w PRD DoDs; JAK może się zmienić

Zmiana scope:
"In Scope:

  • Global basket state and logic (add, update quantity, remove).
  • State persistence across sessions, page reloads, and parallel tabs.
  • Header indicator reflecting current basket total.

Out of Scope:

  • Implementation details (storage mechanisms, state managers).
  • Checkout, payments, backend syncing, stock validation logic (beyond UI limits)."

Zmiana DoDs:
"### [ ] DoD [1]: When I request to add a product from a product discovery location, it appears in the basket with quantity 1, and the global header count increments."
"### [ ] DoD [2]: When I request to increment a basket item's quantity, the count increases up to the available stock limit, updating the global header count. The increment action is blocked at the stock limit."
itd. (7 DoDs w takim formacie)

Tego punktu: "Z całą pewnością nie powinieneś zaczynać od UI, tylko od dostarczenia funkcjonanosci. Ten "design/mock ui/ux" to powinien być jeden z ostanich kroków" - dlaczego?

To jest dla mnie nieklarowne. W moim obecnym rozumieniu:

  • najpierw 1. PRD
  • potem 2. JAK ad. ui/ux (mocki/wireframes)
  • wydaje mi się, że ui/ux to kontrakt, który funkcjonalność ma obsłużyć, a nie na odwrót
  • t.j. najpierw robię ui/ux kontrakt, a potem się zastanawiam ad. baza danych/queries/frontendowe serwer/klient, render tree store typy interfejsy itd.
  • czemu mi się tak wydaje? bo jak zrobię na odwrót - to wtedy mam funkcjonalność i muszę się zastanawiać jak to ubrać - wydaje mi się, że znacznie lepsze ui powstają jeżeli najpierw mam design system -> definuję ui/ux kontrakty -> dostosowuję funkcjonalność/backend/frontend pod potrzeby ui/ux kontraktów, a nie na odwrót

Czy jestem w błędzie?

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10295
0
jgf j napisał(a):
  • czemu mi się tak wydaje? bo jak zrobię na odwrót - to wtedy mam funkcjonalność i muszę się zastanawiać jak to ubrać - wydaje mi się, że znacznie lepsze ui powstają jeżeli najpierw mam design system -> definuję ui/ux kontrakty -> dostosowuję funkcjonalność/backend/frontend pod potrzeby ui/ux kontraktów, a nie na odwrót

Czy jestem w błędzie?

To jest trochę poprawne, ale nie do końca. Mówisz tutaj o "wpinaniu zachowaniania pod potrzeby UI". Można więc powiedzieć że kontrolki UI sa endpointami - miejscami które wchodzą/wychodzą na świat z Twojej aplikacji. I to faktycznie jest prawda, że cokolwiek się dzieje w Twojej aplikacji, logika, persystencja, zachowanie, sieć, finalnie muszą wejść i wyjść przez UI do użytkownika. Ale to nie koniecznie oznacza że musisz od nich zaczać. UI to jest taki sam szczegół implementacyjny jak baza danych, query, serwer, klient i inne takie rzecz.

Najważniejsze jest żeby aplikacja spełniała jakieś zadanie dla użytkownika - i tym zadaniem jest:

  • jeśli dodam item do koszyka to w nim jest
  • jeśli usunę item z koszyka to go nie ma
  • jeśli dodam kilka takich samych itemów do koszyka to jest dostępna ich ilość
  • jesli przedmioty są w koszyku, to mogę dostać listę ich wszystkich
  • jeśli przejdę do kasy to przedmioty z koszyka będą w paragonie

Zauważ, te rzeczy są niezmienne i praktycznie definiują ten koszyk. Designer może sobie zmieniać UI koszyka do woli (ikonki, cyfry, ułozenie, header, nie header) a Twoje testy pozostają poprawne.

Z punktu widzenia DoD, to czy ilość przedmiotów się pojawi w górnej belce, headerze czy gdziekolwiek indziej to jest szczegół implementacyjny.

Na usprawiedliwienie powiem że trafił Ci się dosyć mały feature do zrobienia, tzn. jest dosyć płytki. Sama logika koszyka mogłaby się z powodzeniem zmieścić w 3-4 linijkach kodu, ale cała infrastruktura dookoła tego (baza, api, query, ui) znacznie przewyższają objętością całą logikę.

Ja bym się zastanowił czy w ogóle trzeba od razu dodawać backend do tego koszyka. Na 99% pierwsza wersja mogłaby z powodzeniem siedzieć w localStorage; mógłbyś zrobić realse i zobaczyć czy ktokolwiek z tego korzysta oraz jak korzysta - czy wymaga to jakiś zmian, poprawek, czy wychodzą nowe fakty na światło dzienne które dobrze byłoby poznać teraz, raczej niż później. Jeśli masz odpowiednio spisane DoD i przerobisz to na testy akceptacyjne, to potem przerobienie localStorage na call do backendu, a potem do bazę to kwestia zwykłego refaktoru który nie psuje testów.

jgf j napisał(a):

definuję ui/ux kontrakty

To jest bardzo ciekawe, specyfikacja UI dostarczona przez designera miałaby być kontraktem. Czyli rozumiem że się nie zmieni? 🤔 Słabe podejście do wytwarzania oprogramowania.

Po pierwsze to bardzo nieprawdopodobne, bo UI się zmienia ciągle. Po drugie, na prawdę chcesz budować aplikacje przy założeniu że jej elementy się nie będą zmieniać? Moim zdaniem to jest przepis na porażkę.

Jeśli miałbym nazwać po imieniu to co robisz, to po prostu chcesz żeby ktoś dostarczył Ci specyfikację UI, tak żebyś nie musiał projektować systemu samemu i mógł nałożyć ciasne powiązanie (tight-coupling) na UI. Ma to spore wady, bo oczywiście oznacza to że jak UI się zmieni (a zmieni się) to Twoje DoD i testy są do kosza - albo przynajmniej dużej poprawy. Alternatywa to taka że pracujecie w waterfall, i raz podjęte decyzje się nie zmieniają; co jest jeszcze gorsze bo zakłada ze wszystkie wasze pomysły są poprawne od początku i nie podlegają korektom - co znaczy mniej więcej tyle że jak się zdarzy że pierwsza decyzja będzie niepoprawna, to ciężko ją usprawnić. Pracujecie wtedy w systemie w którym macie jedną szansę żeby coś zrobić dobrze; bo jeśli pierwsza decyzja jest niepoprawna to DoD i testy są przywiązane do niej, więc jej zmiana jest pradoksalnie trudniejsza niż jakbyś nie miał ani DoD ani testów.

PS: Oczywiście nie musisz projektować UI; ale jako programista projektujesz system.

jgf j
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 13
0

Cenne uwagi. Dzięki.

"Zauważ, te rzeczy są niezmienne i praktycznie definiują ten koszyk" - no właśnie zauważam to i mi to dało sporo do myślenia. Tak samo jak motyw zmienności. Łatwe do zmiany > trudne do zmiany, #1 kryterium.

Wyciągnąłem takie wnioski, na tą chwilę:

  • PRD = invariants. Punkty, który spełniają zadanie dla użytkownika.
  • Kontrakt ui - wyrzuciłem to z kroków workflow.
  • Testy - powinny być pod PRD. UI się zmienił? Testy nadal powinny przechodzić. Jak nie, to znaczy, że robię błąd - np white box testy albo tight coupling, mieszam warstwy, albo testuje szczegóły implementacji.

Update kroków workflow:

  1. PRD.
  2. Kontrakt systemu. (Typy, kontrakty operacji, minimal viable).
  3. Minimal viable design rozwiązania.
    -> idea = to ma być łatwe do zmiany. Zakładam, że błędne. Iterując RGR, poprawiam błędy i uczę się, składam całokształt -> w końcu system się spina w spójną całość.
  4. Logic RGR.
  5. View/UI RGR.

Hipoteza: nie brać dosłownie kroków workflow, dopasować do potrzeb sytuacji. Dokumentacja/pierwsze 3 kroki to powinno być małe 10 % czasu. Resztę - skuteczniej i wydajniej realizować od razu w RGR. Wraz z pisaniem testów wychodzą realne braki/błędy -> uczenie się. Jak coś się zaczęło plątać - wracam do minimal viable design rozwiązania, poprawiam błąd i z powrotem do RGR.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.