Wszystkie wpisy

Copilot Chat w maju 2023 - preview w VS Code Insiders
Copilot Chat w maju 2023 - preview w VS Code Insiders

English

Copilot Chat w maju 2023 - preview w VS Code Insiders

Maj 2023: Copilot Chat po waitliście. Insiders, Nightly, kontekst pliku. Nie GA i nie public beta.

22 marca zapisałem się na listę, dziś mam czat w edytorze

22 marca GitHub ogłosił Copilota X i razem z tym ogłoszeniem pojawił się formularz. Wypełniłem go tego samego dnia, bo interesowała mnie jedna rzecz z całej zapowiedzi: czat, który siedzi w edytorze i widzi plik, nad którym pracuję. Potem przez kilka tygodni nic się nie działo, aż w maju dostałem maila, że mam dostęp.

Piszę ten wpis dzień po tym, jak pierwszy raz otworzyłem to okno przy prawdziwym pliku testowym. Nie mam za sobą miesiąca pracy, nie mam opinii o tym, jak to działa w zespole, i nie mam pojęcia, kiedy to wyjdzie z preview. Mam kilka wieczorów, jeden syntetyczny plik NUnit i listę rzeczy, które musiałem z odpowiedzi wykreślić.

Zaznaczam to od razu, bo widzę, że w rozmowach zaczyna się to zacierać: Copilot Chat w maju 2023 nie jest produktem, który dostaje każdy. To jest technical preview za waitlistą. Nie ma go w cenniku, nie ma go w stabilnym VS Code, nie włącza się go przełącznikiem w ustawieniach. Ghost text, czyli szary tekst przed kursorem, to zupełnie inna sprawa: ten wyszedł z technical preview prawie rok temu i chodzi u mnie codziennie od tamtej pory. Mieszanie tych dwóch rzeczy w jedno zdanie o tym, że “Copilot ma teraz czat”, jest po prostu nieprawdziwe.

Druga granica, którą chcę postawić na początku. Od stycznia szkicuję przypadki testowe w oknie przeglądarki i to się nie zmienia. Czat w przeglądarce nie widzi mojego repozytorium. Za każdym razem, kiedy chcę zapytać o konkretny helper, muszę go tam skopiować razem z kawałkiem kontekstu, a potem przepisać odpowiedź z powrotem. Cała różnica, o której jest ten wpis, mieści się w tym jednym zdaniu: pytanie o ten plik zadaję teraz w tym samym oknie, w którym ten plik mam otwarty.

Setup, który u mnie działa w maju

Kanon maja jest ciasny i nie ma w nim wersji “zainstaluj rozszerzenie i gotowe”. Wypisuję dokładnie to, co mam u siebie, bo za pół roku nikt nie odtworzy, jak to wyglądało na starcie.

Po pierwsze, aktywna subskrypcja Copilota. To jest ta sama subskrypcja co do ghost textu, ta za 10 dolarów miesięcznie albo 100 dolarów rocznie. Sama subskrypcja nie daje czatu i to jest najczęstsze nieporozumienie, jakie widzę.

Po drugie, to samo konto GitHub, którym zapisywałem się na listę. Wpisanie na waitlistę jednego konta, a logowanie w edytorze drugim, to najprostszy sposób na to, żeby przy poprawnie zainstalowanym rozszerzeniu nie zobaczyć żadnej ikony i nie wiedzieć dlaczego. Sprawdziłem to na sobie, bo mam osobne konto do zabawek.

Po trzecie, VS Code Insiders. Nie stabilny VS Code. Insiders to osobna instalacja, z zielonym logo, z własnym katalogiem ustawień i własnym zestawem rozszerzeń, chodzi obok wydania stabilnego i nie kłóci się z nim. Aktualizuje się codziennie, więc nie jest to środowisko, w którym trzymam wszystko, co robię.

Po czwarte, rozszerzenie GitHub Copilot Nightly. Instaluję je z marketplace w Insidersie i wyłączam w tym profilu zwykłe rozszerzenie Copilota, bo dwa naraz wchodzą sobie w drogę. Potem pełny restart okna, nie samo przeładowanie. Po restarcie w pasku po lewej pojawia się ikona czatu, a w palecie poleceń widok rozmowy.

Efekt uboczny tego setupu jest taki, że mam dwa edytory. W stabilnym VS Code piszę to, co idzie do repozytorium, i tam mam sam ghost text. W Insidersie mam czat i tam trafia to, przy czym chcę zadać pytanie. Nie polecam tego jako docelowej organizacji pracy. Tak wygląda życie z preview i nie zamierzam udawać, że to jest wygodne.

Jedna uwaga o wersjach, żeby nie było wątpliwości, na czym to sprawdzałem. Plik z tego wpisu to NUnit na .NET 7. W drugim repozytorium mam testy end to end na Playwrightcie w 1.33.0 i pytania zadaję tam dokładnie tego samego rodzaju, tylko odpowiedzi są słabsze, o czym za chwilę.

Co Chat widzi, a czego nie

To jest sekcja, którą chciałbym przeczytać, zanim zacząłem, bo pierwsze rozczarowanie bierze się z fałszywego założenia, że po drugiej stronie siedzi coś, co przeczytało mój projekt.

Czat widzi aktywny plik i to, co w nim zaznaczyłem. Jeżeli zaznaczę metodę i zapytam “co to robi”, pytanie dotyczy tej metody i nie muszę jej nigdzie wklejać. Widzi też diagnostykę z edytora, czyli te same czerwone podkreślenia i kody błędów, które mam na ekranie. To jest ta część, której czat w przeglądarce nie ma i której nie da się obejść wklejaniem, bo błąd kompilacji bez reszty kontekstu wygląda jak zagadka.

Nie widzi całego repozytorium. Klasa, którą testuję, siedzi w innym projekcie w tym samym rozwiązaniu i dopóki nie otworzę jej w sąsiedniej karcie i nie wskażę palcem, odpowiedzi na jej temat są zgadywaniem po nazwie. Nie widzi historii gita, nie widzi wyników ostatniego przebiegu i nie wie, że ten jeden test wywala się co dziesiąty raz na agencie.

Nie widzi też, i to jest teraz moja świadoma decyzja, niczego z sekretów. Nie mam otwartego .env, kiedy pracuję z czatem, i nie wklejam do rozmowy connection stringów ani kluczy do API. Nie dlatego, że mam dowód na to, że dzieje się z nimi coś złego. Dlatego, że to jest zewnętrzna usługa w preview, warunki mogą się zmienić, a wklejenie cudzego sekretu do zewnętrznej usługi nie jest decyzją, którą podejmuje się po cichu. Ta sama zasada, którą zapisałem sobie przy pierwszych eksperymentach z API w 2021 roku: nic produkcyjnego, żadnych prawdziwych danych, żadnego fragmentu dumpa.

Trzy pytania, które zadaję przy teście

Plik jest wymyślony na potrzeby wpisu i dotyczy tej samej nieistniejącej funkcji, na której robiłem marcowe porównanie modeli, czyli kodu rabatowego w koszyku. Nie ma tu ani jednej linii z żadnego projektu, w którym pracuję.

[TestFixture]
public class VoucherTests
{
    private const decimal MinimumTotal = 100.0;

    private static Cart CartWith(decimal total, string currency = "PLN") =>
        new Cart { Total = total, Currency = currency };

    private static Voucher Discount(string code, DateTime expiresAt) =>
        new Voucher { Code = code, MinimumTotal = MinimumTotal, ExpiresAt = expiresAt };

    [TestCase(99.99, false)]
    [TestCase(100.00, true)]
    public void Applies_only_above_minimum(decimal total, bool expected)
    {
        var service = new VoucherService(new FixedClock(new DateTime(2023, 5, 15)));

        var applied = service.TryApply(CartWith(total), Discount("SPRING", new DateTime(2023, 6, 1)));

        Assert.That(applied, Is.EqualTo(expected));
    }
}

Pytanie pierwsze: “wyjaśnij, co robią te dwa helpery i czego w tym pliku nie widać”. To jest pytanie, przy którym czat wypada najlepiej, i nie dlatego, że jest mądry. Dlatego, że odpowiedź opisuje kod, który ma przed sobą, więc nie ma z czego zmyślać. Dostałem poprawny opis obu metod, uwagę o tym, że Discount zaszywa minimum na sztywno i przez to żaden test nie sprawdzi zachowania przy innym progu, oraz zdanie, że data wygaśnięcia jest zawsze w przyszłości względem zegara, więc gałąź z wygasłym kodem nie jest tu wykonywana ani razu. Trzy trafienia, z czego dwa sam bym zauważył, a jednego, tego z progiem, nie zauważyłem przez dwa dni.

Wykreśliłem jedno zdanie. Czat dopisał, że domyślna waluta PLN “odpowiada walucie sklepu”, czego w tym pliku nie ma i czego nie ma nigdzie w moim kodzie. To jest ta sama klasa błędu co zmyślony selektor: model bierze prawdopodobnie brzmiące uzasadnienie biznesowe i wkłada je do opisu tak, jakby je gdzieś przeczytał.

Pytanie drugie: “napisz test na gałąź z wygasłym kodem, w konwencji tego pliku”. Tu wykreśliłem więcej, niż zostawiłem. Propozycja użyła DateTime.Now zamiast wstrzykniętego zegara, czyli dokładnie tego, przed czym broni się reszta pliku, i przy okazji zrobiłaby z tego testu bombę zegarową na dzień po wygaśnięciu. Wykreślone. Wywołała service.Validate(...), metodę, której w tej klasie nie ma, bo istnieje tylko TryApply. Wykreślone. Nazwała test TestExpiredVoucher, choć w pliku ma przed nosem konwencję z podkreśleniami. Wykreślone.

Zostało jedno i to jedno było tego warte: propozycja zawierała przypadek dokładnie na granicy, z datą równą sekundzie wygaśnięcia, i pytanie w komentarzu, czy kod ma być w tym momencie jeszcze ważny, czy już nie. Na to pytanie w mojej wymyślonej specyfikacji nie ma odpowiedzi i to jest dokładnie ta dziura, po którą chodzi się do analityka. Wynik netto: dostałem szkic, z którego zostawiłem może jedną trzecią, plus jedno pytanie, którego sam bym nie zadał.

Pytanie trzecie: “co oznacza ten błąd kompilacji”. Pierwsza linia mojego fixture jest błędna i zostawiłem ją celowo, bo to najczęstsza literówka, jaką popełniam w kodzie z pieniędzmi. private const decimal MinimumTotal = 100.0; daje CS0664, czyli literał typu double nie konwertuje się niejawnie do decimal. Zaznaczyłem linię, zadałem pytanie i dostałem poprawną odpowiedź razem z poprawką na 100.0m. Nie musiałem przepisywać komunikatu błędu do przeglądarki i nie musiałem tłumaczyć, co to za typ. To jest ten moment, w którym czat w edytorze faktycznie wygrywa z kartą obok.

Wykreśliłem drugą część odpowiedzi. Czat zaproponował jako alternatywę zmianę typu pola na double. Formalnie błąd znika. Praktycznie jest to najgorsza rada, jaką można dać w kodzie liczącym kwoty, i gdybym tego nie czytał, tylko klikał “wstaw”, miałbym w projekcie testowym typ zmiennoprzecinkowy przy pieniądzach.

Pytania w repozytorium z Playwrightem wyglądały tak samo, tylko trafień było mniej. Tam kontekst siedzi w page objectach w innych plikach i w konfiguracji, których czat nie widzi, więc odpowiedzi robią się ogólne. To dobrze pokazuje, na czym stoi cała ta funkcja: na jednym otwartym pliku.

Czego nie deleguję

Dwa tygodnie z ghost textem opisałem sobie już przy pierwszym boilerplacie testowym i wniosek z tamtego wpisu przenosi się tu w całości: to, co dostaję, jest szkicem, a recenzja jest moja. Rozmowa niczego w tej umowie nie zmienia, poza tym, że odpowiedzi są dłuższe, brzmią pewniej i łatwiej je wkleić bez czytania. Różnica jest taka, że przy szarym tekście oceniam jedną linię i widzę ją w kontekście, a przy czacie dostaję dwadzieścia linii w osobnym oknie, gdzie o kontekst muszę zadbać sam.

Nie oddaję decyzji o merge’u. Kod, który wyszedł z czatu, przechodzi przez ten sam przegląd co każdy inny i nie ma w opisie pull requesta zdania, że “wygenerowane, więc pewnie dobre”.

Nie oddaję oceny flake’a. Czat nie widzi dwudziestu ostatnich przebiegów na agencie, nie wie, że ten test przewraca się tylko przy równoległym uruchomieniu, i wszystko, co powie na ten temat, jest zgadywaniem po treści pliku.

Nie oddaję decyzji, czy dany test w ogóle ma być w CI. To jest pytanie o czas przebiegu, o koszt utrzymania i o to, czy ktokolwiek zareaguje na czerwony wynik. Nic z tych trzech rzeczy nie znajduje się w otwartym pliku.

Nie rezygnuję też z ghost textu. On zostaje tam, gdzie był, czyli w stabilnym edytorze, przy pisaniu kolejnej linii. Czat jest do pytań, na które wcześniej otwierałem przeglądarkę, a nie zamiennikiem uzupełniania.

I nie przepisuję tego, co zapisałem wcześniej. Wpis o technical preview Copilota opisuje luty 2022 i stan wiedzy z lutego 2022. Wnioski z ubiegłego miesiąca o czytaniu podpowiedzi zostają takie, jakie były. Dopisywanie do archiwum dzisiejszej wiedzy sprawia tylko, że za rok nie da się odtworzyć, co kiedy było wiadomo.

Podsumowanie

Po kilku wieczorach mam jeden wniosek, który da się obronić: czat w edytorze skraca przełączanie kontekstu i nic poza tym.

Rozbijam to na trzy zdania. Pytanie o otwarty plik zadaję bez kopiowania go do przeglądarki i bez przepisywania odpowiedzi z powrotem, a przy błędzie kompilacji ta różnica jest duża, bo cały komunikat i cała linia są już po drugiej stronie. Jakość odpowiedzi spada dokładnie wtedy, kiedy odpowiedź wymaga czegoś spoza otwartego pliku, więc w repozytorium z rozbitym kontekstem zysk jest mniejszy niż w pojedynczym fixture. Zmyślanie nie zniknęło ani trochę: w trzech pytaniach wykreśliłem nieistniejącą metodę, wymyślone uzasadnienie biznesowe, złą konwencję nazw, DateTime.Now w miejscu wstrzykniętego zegara i radę, żeby liczyć pieniądze na double.

Czego z tego nie wynika. Nie wynika, że mam nowe narzędzie w procesie zespołu, bo połowa zespołu nie ma dostępu, a druga połowa nie będzie stawiała drugiego edytora po to, żeby zadać pytanie. Nie wynika, że to jest gotowy produkt, bo to jest preview za listą oczekujących, z rozszerzeniem nightly i z wersją edytora, która zmienia się codziennie. Nie wynika w końcu, że każdy, kto płaci za Copilota, to ma, bo nie ma.

Do czego wrócę za kilka miesięcy. Chcę zobaczyć, jak ten czat zachowa się przy kontekście rozbitym na kilka plików, bo to jest realny kształt każdego projektu testowego, jaki prowadzę, i dziś jest to jego najsłabsza strona. Chcę też zobaczyć, ile z mojej listy wykreśleń zniknie samo, a ile jest wpisane w naturę narzędzia, które generuje prawdopodobny tekst. Obstawiam, że wykreślenia zostaną i że to nadal będzie opłacalne, tylko trzeba je liczyć jako część kosztu, a nie jako niespodziankę.