W zespole Narzędzi deweloperskich jesteśmy wielkimi fanami TypeScript. Nasz nowy kod w Narzędziach deweloperskich jest tworzony w tym języku, a obecnie jesteśmy w trakcie dużej migracji całej bazy kodu, aby sprawdzać typy za pomocą TypeScript. Więcej informacji na ten temat znajdziesz w naszej prezentacji Chrome Dev Summit 2020. Dlatego postanowiliśmy przenieść kod Puppeteer do TypeScript.
Planowanie migracji
Podczas planowania migracji chcieliśmy robić postępy małymi krokami. Dzięki temu zmniejszysz nakłady związane z migracją (w każdej chwili pracujesz tylko nad małą częścią kodu) i ograniczysz ryzyko. Jeśli coś pójdzie nie tak podczas wykonywania któregoś z tych kroków, możesz łatwo cofnąć zmiany. Puppeteer ma wielu użytkowników, a nieudana wersja może spowodować problemy dla wielu z nich, dlatego bardzo ważne było dla nas zminimalizowanie ryzyka wprowadzenia zmian, które mogłyby spowodować nieprawidłowe działanie.
Mieliśmy też szczęście, że Puppeteer ma solidny zestaw testów jednostkowych obejmujących wszystkie jego funkcje. Dzięki temu mogliśmy mieć pewność, że podczas migracji nie uszkodziliśmy kodu, a także że nie wprowadziliśmy zmian w naszym interfejsie API. Celem migracji było jej przeprowadzenie bez udziału użytkowników Puppeteer, którzy nawet nie zdawali sobie sprawy, że nastąpiła migracja. Testy były kluczowym elementem tej strategii. Gdyby nie mieliśmy dobrych wyników w testach, dodaliśmy je przed rozpoczęciem migracji.
Wprowadzanie zmian w kodzie bez testów jest ryzykowne, ale zmiany, które dotyczą całych plików lub całej bazy kodu, są szczególnie ryzykowne. Podczas wprowadzania zmian mechanicznych łatwo pominąć jakiś krok, a testy wielokrotnie wykrywały problemy, które umykały zarówno implementatorowi, jak i sprawdzającemu.
Na początku poświęciliśmy dużo czasu na konfigurowanie ciągłej integracji (CI). Zauważyliśmy, że procesy CI dotyczące żądań pull były niestabilne i często kończyły się niepowodzeniem. Zdarzało się to tak często, że zaczęliśmy ignorować nasz system CI i i tak łączyć żądania pull, zakładając, że błąd był jednorazowym problemem w systemie CI, a nie w Puppeteer.
Po przeprowadzeniu ogólnej konserwacji i poświęceniu czasu na naprawienie niektórych testów, które nie działały prawidłowo, udało nam się uzyskać znacznie bardziej stabilne wyniki. Dzięki temu mogliśmy zwracać uwagę na CI i wiedzieć, że błąd wskazuje na rzeczywisty problem. To nie jest praca dla gwiazd i nie jest przyjemnością obserwowanie niekończących się procesów CI, ale ze względu na liczbę żądań pull, które migracja wysyłała do naszego kompletnego zestawu testów, konieczne było zapewnienie jego niezawodnego działania.
Wybierz i zapisz jeden plik
W tym momencie mieliśmy już gotową migrację i solidny serwer CI z wieloma testami, które nas chroniły. Zamiast zająć się dowolnym plikiem, celowo wybraliśmy mały plik do przeniesienia. To przydatne ćwiczenie, ponieważ pozwala sprawdzić zaplanowany proces. Jeśli to zadziała, Twój sposób jest prawidłowy. Jeśli nie, musisz wrócić do rysowania.
Dodatkowo ryzyko było mniejsze, ponieważ sprawdzaliśmy poszczególne pliki (i wykorzystywaliśmy regularne wersje Puppeteer, więc nie wszystkie zmiany były wprowadzane w tej samej wersji npm). Pierwszym wybranym przez nas plikiem był DeviceDescriptors.js
, ponieważ był to jeden z najprostszych plików w kodeksie źródłowym. Może się wydawać, że to nie jest warte całego tego wstępnego wysiłku, ale nie chodzi o to, aby wprowadzić duże zmiany od razu, tylko metodycznie i ostrożnie modyfikować poszczególne pliki. Czas potrzebny na weryfikację tej metody z pewnością pozwala zaoszczędzić czas potrzebny na późniejszą migrację, gdy będziesz zajmować się bardziej skomplikowanymi plikami.
Sprawdź wzór i powtórz
Na szczęście zmiana na DeviceDescriptors.js
została wprowadzona do kodu źródłowego, a plan działał zgodnie z nadziejami. W tym momencie możesz zacząć działać, czyli dokładnie tak, jak my. Używanie etykiety GitHub to bardzo dobry sposób na grupowanie wszystkich żądań pull. Okazało się to przydatne do śledzenia postępów.
Przeprowadź migrację i ulepszaj aplikację później
W przypadku każdego pojedynczego pliku JavaScript proces wyglądał tak:
- Zmień nazwę pliku z
.js
na.ts
. - Uruchom kompilator TypeScript.
- Rozwiąż wszelkie problemy.
- Utwórz prośbę o przechwycenie.
Większość pracy w tych początkowych żądaniach pull polegała na wyodrębnianiu interfejsów TypeScript dla istniejących struktur danych. W przypadku pierwszego żądania pull, które przeniosło omówioną wcześniej usługę DeviceDescriptors.js
, kod pochodził z tych źródeł:
module.exports = [
{
name: 'Pixel 4',
… // Other fields omitted to save space
},
…
]
I stał się:
interface Device {
name: string,
…
}
const devices: Device[] = [{name: 'Pixel 4', …}, …]
module.exports = devices;
W ramach tego procesu przeanalizowaliśmy każdy wiersz w bazie kodu pod kątem problemów. Podobnie jak w przypadku każdego kodu, który istnieje od kilku lat i zmieniał się na przestrzeni lat, w tym przypadku również można znaleźć obszary, w których można zmienić kod i poprawić sytuację. Zwłaszcza w przypadku przejścia na TypeScript zauważyliśmy miejsca, w których niewielka zmiana struktury kodu pozwoliłaby nam bardziej polegać na kompilatorze i uzyskać większą ochronę typów.
Wbrew pozorom bardzo ważne jest, aby nie wprowadzać tych zmian od razu. Celem migracji jest przekształcenie kodu źródłowego w TypeScript. Podczas dużej migracji należy zawsze pamiętać o ryzyku wystąpienia awarii oprogramowania i uniemożliwienia korzystania z niego przez użytkowników. Dzięki temu, że początkowe zmiany były minimalne, ryzyko było niewielkie. Po scaleniu pliku i przeniesieniu go do TypeScript mogliśmy wprowadzić dalsze zmiany, aby ulepszyć kod i wykorzystać system typów. Pamiętaj, aby ustawić ścisłe granice migracji i staraj się ich nie przekraczać.
Migrowanie testów w celu przetestowania definicji typów
Po przeniesieniu całego kodu źródłowego do TypeScripta mogliśmy skupić się na testach. Nasze testy miały świetne pokrycie, ale były napisane w języku JavaScript. Oznacza to, że nie testowano definicji typów. Jednym z długoterminowych celów projektu (nad którym nadal pracujemy) jest dostarczanie wysokiej jakości definicji typów w ramach Puppeteer, ale w naszej bazie kodu nie ma żadnych mechanizmów sprawdzania definicji typów.
Podczas migracji testów do TypeScript (zgodnie z tym samym procesem, krok po kroku) znaleźliśmy problemy z TypeScript, które w przeciwnym razie zostałyby odkryte przez użytkowników. Teraz nasze testy obejmują wszystkie funkcje i służą też do sprawdzania jakości kodu TypeScript.
Jako inżynierowie pracujący nad kodem źródłowym Puppeteer odnieśliśmy już ogromne korzyści z TypeScript. W połączeniu z znacznie ulepszonym środowiskiem CI pozwoliło nam to zwiększyć wydajność pracy nad Puppeteer i uzyskać możliwość wykrywania błędów w TypeScript, które w przeciwnym razie mogłyby dostać się do wersji npm. Cieszymy się, że dostaliśmy wysokiej jakości definicje TypeScriptu, aby umożliwić wszystkim deweloperom korzystającym z Puppeteer korzystanie z tych rozwiązań.
Pobieranie kanałów podglądu
Rozważ użycie przeglądarki Chrome Canary, Dev lub Beta jako domyślnej przeglądarki deweloperskiej. Te kanały wersji wstępnej zapewniają dostęp do najnowszych funkcji DevTools, umożliwiają testowanie najnowocześniejszych interfejsów API platformy internetowej i pomagają znaleźć problemy w witrynie, zanim zrobią to użytkownicy.
Kontakt z zespołem Narzędzi deweloperskich w Chrome
Użyj poniższych opcji, aby omówić nowe funkcje, aktualizacje lub inne informacje związane z Narzędziami deweloperskimi.
- Prześlij nam swoje opinie i prośby o dodanie funkcji na stronie crbug.com.
- Zgłoś problem z Narzędziami deweloperskimi, klikając Więcej opcji > Pomoc > Zgłoś problem z Narzędziami deweloperskimi.
- Wyślij tweeta do @ChromeDevTools.
- Napisz komentarz pod filmem Co nowego w Narzędziach deweloperskich w Narzędziach deweloperskich w YouTube lub filmach ze wskazówkami dotyczącymi Narzędzi deweloperskich w YouTube.