Wersje semantyczne przy naprawianiu ważnego błędu

18

Obecnie zarządzam biblioteką, która ma wiele zastosowań publicznych, i miałem pytanie dotyczące wersji semantycznej . Chcę refaktoryzować jedną dość ważną część biblioteki, która jest niepoprawnie zaimplementowana - i zawsze była niepoprawnie zaimplementowana. Ale zrobienie tego oznaczałoby zmiany w publicznym interfejsie API, co jest poważną decyzją.

Zmiana, którą chcę wprowadzić, dotyczy sposobu używania iteratorów. Obecnie użytkownicy muszą to zrobić:

while ($element = $iterator->next()) {
   // ...
}

Co jest niepoprawne, przynajmniej w natywnym interfejsie Iteratora PHP . Chcę to zastąpić:

while ($iterator->valid()) {
   $element = $iterator->current();
   // ...
   $iterator->next();
}

który jest analogiczny do:

foreach ($iterator as $element) {
    // ...
}

Jeśli spojrzysz na przewodnik Toma dotyczący wersjonowania semantycznego, wyraźnie stwierdza, że ​​wszelkie zmiany w publicznym interfejsie API (tj. Te, które nie są kompatybilne z poprzednimi wersjami), powinny uzasadniać ważną wersję. Tak więc biblioteka przeskakuje z wersji 1.7.3 do 2.0.0, co dla mnie jest krokiem za daleko. Mówimy tylko o naprawieniu jednej funkcji.

Mam plany, aby ostatecznie wydać 2.0.0, ale myślałem, że to wtedy, gdy całkowicie przepisałeś bibliotekę i zaimplementowałeś wiele publicznych zmian API. Czy wprowadzenie tego refaktoryzacji wymaga wydania ważnej wersji? Naprawdę nie widzę, jak to działa - czuję się bardziej komfortowo, wypuszczając go jako 1.8.0 lub 1.7.4. Czy ktoś ma jakieś rady?

Hohner
źródło
Co uniemożliwia zachowanie zgodności wstecznej?
mouviciel
W tej chwili next()metoda służy do pobrania bieżącego elementu ORAZ przesunięcia wewnętrznego wskaźnika do przodu. Co jest złe. next()powinien przesunąć wskaźnik i current()służy do wyszukiwania ...
hohner,
6
więc w nowej wersji ludzie nie powinni przejmować się wartością zwracaną next()tylko za to, że przesuwa wskaźnik, to naprawdę nie psuje kompatybilności
maniak zapadkowy

Odpowiedzi:

29

Wahasz się, ponieważ nie chcesz tworzyć wersjonowania semantycznego, chcesz zrobić „reklamy obsługujące wersjonowanie”. Oczekujesz, że numer wersji „2.0” poinformuje świat, że masz teraz w bibliotece wiele nowych fajnych funkcji, a nie to, że zmieniłeś interfejs API. Zgadza się (robi to wiele firm i / lub programistów). IMHO masz następujące opcje:

  • trzymaj się wersji semantycznej i żyj z faktem, że musisz zmienić numer wersji na 2.0.0
  • zmień schemat wersji na 4 liczby. „1.1.7.3” to Twoja wersja, „1.2.0.0” następna po zmianie interfejsu API, a „2.0.0.0” pierwsza z „całkowicie nowej rodziny produktów 2.x”
  • uczyń swoją poprawkę wstecznie kompatybilną (więc nie zmieniaj funkcjonalności next, po prostu dodaj funkcje validi current). Następnie możesz użyć „1.8.0” jako kolejnego numeru wersji. Jeśli uważasz, że zmiana zachowania nextjest naprawdę ważna, zrób to w 2.0.0.
Doktor Brown
źródło
O ile ostatnia opcja byłaby idealnym rozwiązaniem: nie możesz prosić next()o robienie dalej. Aby poprawnie zaimplementować tę funkcję, musi zrobić coś inaczej. Więc jeśli sprawię, że będzie on kompatybilny wstecz - nowa funkcjonalność / poprawka również będzie błędna i podważy cały sens zmiany.
hohner
2
Warto rozważyć szerszą sugestię zawartą w trzeciej kuli (dostosowanie poprawki do poprzedniej wersji). Może nie działać w tym konkretnym przypadku, ale ogólna technika jest warta rozważenia. Ta funkcja jest bardziej złożona, ale może to być wykonalna droga.
Dziękujemy wszystkim: gdybym mógł zaakceptować dwa, zgodziłbym się. Skończyło się na tym, że włamałem się do nowej next()metody, aby wykonać wszystkie nowe funkcje, a także to, co było potrzebne, aby zapewnić kompatybilność wsteczną. To trochę okropne, że muszę niszczyć nowe funkcje w ten sposób, ale hej, ho.
hohner
10
@hohner: Teraz nadszedł czas, aby udokumentować stare zachowanie jako przestarzałe, aby można je było usunąć w wersji 2.0.0.
Jan Fabry
7

Trzymaj się przewodnika Toma po wersjach semantycznych.

Wszelkie istotne zmiany w publicznym interfejsie API muszą być wykonane w jednym z dwóch punktów:

  1. Nigdy
  2. W głównej aktualizacji wydania

Nawiasem mówiąc, mój głos jest pierwszy. Ale potwierdzam, że jest odpowiedni tylko do drobiazgów.

Problemem jest utrzymanie kompatybilności wstecznej i upewnienie się, że nie psujesz rzeczy poprzednim użytkownikom interfejsu API.

Zasadniczo tworzysz błąd indeksowania dla użytkowników, którzy nie są świadomi zmiany. Wymuszenie takiej zmiany zmusza wszystkich użytkowników do wykonania następujących czynności:

  1. Kod poprawki, aby użyć nowego podejścia
  2. Sprawdź poprawkę i upewnij się, że niczego nie zepsuła
  3. Wysyłaj nowe wersje swojego produktu do użytkowników końcowych

Może to potencjalnie wymagać dużego wysiłku, zwłaszcza gdy weźmie się pod uwagę, jak niewiele projektów ma wdrożone przypadki testowe, aby zweryfikować takie zmiany. Nakład pracy jest większy, gdy weźmie się pod uwagę liczbę dalszych użytkowników, którzy będą musieli również zaktualizować swoje instalacje.

W przypadku czegoś tak małego pozwoliłbym temu odejść i nie zawracałem sobie tym głowy.
Jeśli naprawdę Ci to przeszkadza (co najwyraźniej tak robi lub nie zapytałbyś), zrobiłbym następujące.

  1. Utwórz gałąź v2.0.0 w drzewie kodu
  2. Dokonaj pierwszego wkładu do gałęzi v2.0.0, która jest tą zmianą
  3. Wyślij podgląd z Release Noteswyprzedzeniem, że nadchodzi zmiana

A potem uzbrój się w cierpliwość, ponieważ zgromadzenie innych rzeczy uzasadniających uaktualnienie numeru wersji do nowej wersji głównej zajmie trochę czasu. Zaawansowane powiadomienie (część 3) daje czas na otrzymanie informacji zwrotnych od użytkowników końcowych, aby dowiedzieć się, jaki wpływ będzie miała ta zmiana.


Alternatywnym rozwiązaniem jest dodanie nowej funkcji, która działa tak, jak chcesz.

Jeśli tak, należy foo()utworzyć fooCorrect(), aby zapewnić poprawkę, ale także w pełni zachować zgodność z poprzednimi wersjami. W pewnym momencie możesz foo()przestać działać, aby inni mogli wiedzieć, że go nie używasz.

Wyzwaniem jest, że znajdziesz coś innego, w fooCorrect()co wymaga To aktualizacji i skończy się fooCorrectedCorrect()albo jakaś inna głupie bzdury.

Jeśli naprawdę chcesz to teraz naprawić, to alternatywne podejście jest prawdopodobnie najlepszą trasą. Należy pamiętać o tworzeniu wielu dodatkowych funkcji w ten sposób, ponieważ utrudnia to pracę z interfejsem API. I ta świadomość może wystarczyć, aby zapobiec najgorszemu z tego rodzaju problemów.

Ale może to być „najmniej złe” podejście do rozważenia w przypadku czegoś małego.


źródło
Zgadzam się z Tobą. Problem, z którym się spotykam, polega na tym, że chcę całkowicie przepisać bibliotekę dla wersji 2.0.0 (ponieważ jest wiele tych problemów, które wymagają naprawy); więc nie chcę, aby tak mała zmiana, jak iteratory, była podstawą tej dużej zmiany. Więc mam następujące opcje: zignorować ten błąd lub naprawić błąd i umieścić go w nowej głównej wersji?
hohner
@hohner - zaktualizowano odpowiedź, aby zapewnić alternatywne podejście do tworzenia nowych funkcji. Należy pamiętać, że wiele nowych, podobnie nazwanych funkcji jest prawie tak samo złe, jak zmiana samego interfejsu API.
3
@hohner: Konsekwentnie źle> niekonsekwentnie słusznie w tym przypadku. Zachowanie nadal działa, po prostu nie jest idiomatyczne. Weź pod uwagę, że wprowadzając tę ​​zmianę, łamiesz kod klienta. Zrobienie tego bez ostrzeżenia nie będzie mile widziane.
Phoshi
@ GlenH7 W tym przypadku użycie alternatywnie nazwanej metody nie będzie działać. Natywny iterator PHP opiera się na tych metodach (tj. next()Nie nextCorrect()). Zobaczę, czy mogę zmodyfikować next (), aby był kompatybilny wstecz ORAZ działa podczas implementacji Iteratorinterfejsu.
hohner
1
@ Phoshi Jesteś na miejscu - teraz całkowicie się zgadzam. Czas spróbować zakodować niemożliwe: D
hohner