Przejdź do głównej zawartości

Używanie Cursor jako AI Pair Programmer

Budujesz system preferencji powiadomień. Wymagania są jasne, ale implementacja ma trudne części: planowanie uwzględniające strefy czasowe, reguły dostarczania specyficzne dla kanału, nadpisania na poziomie użytkownika na szczycie domyślnych na poziomie organizacji i podgląd w czasie rzeczywistym tego, jakie powiadomienia otrzymałby użytkownik. Mógłbyś napisać to wszystko sam, ale wiesz z doświadczenia, że przypadki brzegowe zajmą trzy razy dłużej niż happy path. Potrzebujesz kogoś, kto przemyśli projekt z Tobą, wyłapie Twoje ślepe punkty i obsłuży powtarzalne części, podczas gdy Ty skupisz się na trudnej logice.

Tak wygląda pair programming z Cursor. Nie narzędzie, które generuje kod na żądanie, ale współpracownik, który uczestniczy w całym łuku funkcji — od dyskusji projektowej przez implementację do debugowania.

  • Model mentalny, kiedy używać trybów Ask, Agent i Plan podczas sesji parowania
  • Techniki utrzymywania kontekstu podczas długiej sesji deweloperskiej
  • Prompty do skopiowania do rozpoczynania dyskusji projektowych, implementacji przyrostowej i obsługi objawów debugowania
  • Workflow do przeglądania kodu generowanego przez AI w czasie rzeczywistym, a nie po fakcie
  • Strategie utrzymywania AI na właściwej ścieżce, gdy odchyla się od Twojej architektury

Efektywne pair programming w Cursor zależy od przełączania między trybami w odpowiednich momentach. Pomyśl o tym jak o pair programmingu z człowiekiem: czasami chcesz omówić podejście (Ask), czasami chcesz, żeby Twój partner prowadził (Agent), a czasami potrzebujesz szczegółowego planu, zanim którykolwiek z was dotknie klawiatury (Plan).

Dla każdej funkcji, która dotyka więcej niż dwóch plików lub obejmuje nieoczywiste decyzje projektowe, zacznij w trybie Plan. Naciśnij Shift+Tab z inputu czatu, aby obrócić do trybu Plan, lub pozwól Cursor zasugerować to, gdy wykryje złożone żądanie.

Tryb Plan zbada Twoją bazę kodu, zada pytania wyjaśniające (jak “Czy już masz tabelę ustawień użytkownika? Jakiego systemu powiadomień używasz?”), i wyprodukuje plan krok po kroku. Możesz edytować ten plan, kwestionować decyzje i udoskonalać go, zanim zostanie napisana jedna linia kodu.

To jest fundamentalnie inne niż skakanie prosto do trybu Agent z “zbuduj system preferencji powiadomień.” Plan daje Ci wspólne zrozumienie z AI przed rozpoczęciem implementacji.

Gdy trafisz na rozdroże projektowe, przełącz się do trybu Ask (Ctrl + . aby obracać tryby). Tryb Ask jest tylko do odczytu — nie może modyfikować plików — co czyni go bezpiecznym dla dyskusji eksploracyjnych.

@src/models/user.ts @src/models/organization.ts
Decyduję między dwoma podejściami dla preferencji powiadomień na poziomie org vs poziomie użytkownika:
Opcja A: Pojedyncza tabela preferencji z kolumną "scope" (org lub użytkownik) i
systemem precedencji, który scala domyślne org z nadpisaniami użytkownika w czasie zapytania.
Opcja B: Oddzielne tabele dla org_preferences i user_preferences, z
funkcją merge w warstwie aplikacji.
Biorąc pod uwagę nasze istniejące modele danych, które podejście:
- Jest łatwiejsze do zapytania dla "jakie powiadomienia otrzyma ten użytkownik?"
- Obsługuje przypadek, gdy użytkownik nie ustawił żadnych preferencji (wraca do domyślnych org)?
- Jest łatwiejsze do rozszerzenia, gdy dodamy preferencje na poziomie zespołu później?

Tryb Ask daje Ci przemyślaną analizę bez generowania jakiegokolwiek kodu. Ty podejmujesz decyzję, następnie przełączasz się do trybu Agent dla implementacji.

Gdy masz plan i rozwiązałeś pytania projektowe, tryb Agent jest Twoim partnerem implementacyjnym. Kluczową techniką jest praca przyrostowa — jeden kawałek na raz, przeglądając każdą zmianę przed przejściem dalej.

@src/models @src/lib/db/schema.ts
Zaimplementuj krok 1 z naszego planu: model danych preferencji powiadomień.
Utwórz tabelę preferencji z:
- id (uuid, klucz główny)
- scope_type (enum: 'organization', 'user')
- scope_id (uuid, odwołuje się do org lub user)
- channel (enum: 'email', 'sms', 'push', 'in_app')
- enabled (boolean, domyślnie true)
- delivery_window_start (time, nullable dla "dowolny czas")
- delivery_window_end (time, nullable)
- timezone (text, domyślnie 'UTC')
- created_at, updated_at timestamps
Podążaj za tymi samymi wzorcami Drizzle ORM użytymi w naszym istniejącym schemacie.
Wygeneruj także plik migracji.

Po tym jak Agent wygeneruje kod, przejrzyj diff dokładnie. To jest “para” w pair programmingu — nie tylko akceptujesz kod, oceniasz go. Jeśli coś jest nie tak, powiedz agentowi:

Migracja wygląda dobrze, ale delivery_window_start i delivery_window_end
powinny używać typu "time" bez strefy czasowej, ponieważ przechowujemy strefę czasową osobno.
Także dodaj ograniczenie unique na (scope_type, scope_id, channel), aby zapobiec
duplikatom preferencji.

Agent zaktualizuje kod na podstawie Twojego feedbacku. Ta wymiana to miejsce, gdzie pochodzi prawdziwa produktywność — nie w pierwszym generowaniu, ale w szybkiej iteracji.

Długie sesje parowania (budowanie całej funkcji przez kilka godzin) wymagają zarządzania kontekstem. AI nie pamięta Twoich poprzednich rozmów automatycznie, ale możesz ustrukturyzować swój workflow, aby utrzymać ciągłość.

Gdy Agent pracuje nad jednym krokiem, możesz kolejkować instrukcje follow-up. Wpisz swoje następne żądanie i naciśnij Enter — czeka w kolejce i wykonuje się po zakończeniu bieżącego zadania. To pozwala Ci planować z wyprzedzeniem podczas gdy Agent pracuje, naśladując jak ludzka para myślałaby z wyprzedzeniem, podczas gdy ich partner pisze.

Jeśli stworzyłeś plan wcześniej, zapisz go do swojego workspace (tryb Plan oferuje opcję “Save to workspace”). Następnie odwołuj się do niego w kolejnych promptach:

@plans/notification-preferences.md
Ukończyliśmy kroki 1-3. Teraz zaimplementuj krok 4: funkcję merge preferencji,
która łączy domyślne org z nadpisaniami użytkownika. Podążaj za podejściem, na którym się zdecydowaliśmy
(pojedyncza tabela z precedencją opartą na scope).

Jeśli Twoja rozmowa staje się wystarczająco długa, że odpowiedzi zaczynają tracić koherencję, użyj komendy /summarize. To kompresuje historię rozmowy, zachowując kluczowe decyzje i kontekst. Alternatywnie, rozpocznij nowy czat i odwołaj się do pliku planu plus plików, które już zmodyfikowałeś:

@plans/notification-preferences.md @src/models/preferences.ts @src/services/preferences.ts
Kontynuuję z poprzedniej sesji. Kroki 1-4 są ukończone (model danych, migracja,
endpointy API, funkcja merge). Teraz zaimplementuj krok 5: endpoint podglądu w czasie rzeczywistym,
który pokazuje, jakie powiadomienia otrzymałby użytkownik z obecnymi ustawieniami.

Gdy coś pójdzie nie tak podczas implementacji, powstrzymaj pokusę debugowania po cichu. Zamiast tego wprowadź Cursor do procesu debugowania w taki sam sposób, w jaki zaangażowałbyś ludzką parę.

Przełącz się do trybu Debug (dostępny w pickerze trybów) dla trudnych bugów. Tryb Debug jest specjalnie zaprojektowany do tego: generuje hipotezy, dodaje instrumentację, prosi Cię o odtworzenie buga, następnie analizuje zebrane logi, aby znaleźć główną przyczynę.

Dla prostszych problemów tryb Ask działa dobrze:

AI przeanalizuje ścieżkę kodu i zidentyfikuje prawdopodobnych winowajców. Może zauważyć, że ID organizacji użytkownika nie jest przekazywane poprawnie do funkcji merge, lub że zapytanie filtruje na złym scope_type.

AI pair programmerzy dryfują. Wprowadzają wzorce, na które się nie zgadzałeś, używają bibliotek, których nie chcesz, lub rozwiązują inny problem niż ten, o który pytałeś. Oto strategie utrzymywania ich w zgodzie.

Zaimplementuj sprawdzenie okna dostarczania. NIE:
- Używaj moment.js ani żadnej biblioteki dat poza wbudowanym API Temporal
- Dodawaj nowego zapytania do bazy danych -- użyj już załadowanych danych preferencji
- Zmieniaj sygnatury funkcji checkDeliveryWindow()

Negatywne ograniczenia są zaskakująco skuteczne w zapobieganiu dryfowaniu.

Przed tym jak Agent dokona dużej zmiany, utwórz checkpoint ręcznie lub polegaj na automatycznych checkpointach Cursor. Jeśli wygenerowany kod idzie w złym kierunku, przywróć checkpoint i spróbuj innego promptu. Myśl o tym jak o git stash dla Twojej sesji parowania.

Nie proś Agent o implementację całej funkcji jednym promptem. Podziel to na kroki i przejrzyj diff każdego kroku przed przejściem dalej. To jest wolniejsze na krok, ale szybsze ogólnie, ponieważ łapiesz problemy wcześnie zamiast rozwikłać je z 500 linii wygenerowanego kodu.

Jeśli stwierdzasz, że powtarzalnie poprawiasz ten sam problem (“nie, używaj naszego niestandardowego loggera, nie console.log”), utwórz regułę projektu:

.cursor/rules/coding-standards.md
# Standardy kodowania
- Używaj loggera z src/lib/logger.ts, nigdy console.log
- Używaj API Temporal dla dat, nie moment ani dayjs
- Używaj Zod do walidacji żądań, nie ręcznych sprawdzeń
- Odpowiedzi błędów muszą używać klasy AppError z src/lib/errors.ts
- Zapytania do bazy danych przechodzą przez warstwę repozytorium w src/repositories/

Ta reguła stosuje się do każdej interakcji Agent, więc przestajesz się powtarzać.

Agent ignoruje Twój plan i improwizuje. Dzieje się to, gdy plik planu nie jest w kontekście. Zawsze @odwołuj się do pliku planu w swoim prompcie. Jeśli plan jest zbyt długi, wyciągnij odpowiedni krok.

AI generuje więcej kodu niż prosiłeś. Poprosiłeś o model danych, a ono zbudowało także endpointy API i testy. Bądź wyraźny co do zakresu: “Zaimplementuj TYLKO model danych. Zatrzymaj się po wygenerowaniu migracji. Nie twórz żadnych route’ów API ani testów.”

Okno kontekstu wypełnia się podczas długiej sesji. Po wielu wymianach tam i z powrotem, odpowiedzi stają się krótsze i mniej spójne. Użyj /summarize lub rozpocznij świeży czat z wyraźnymi odwołaniami do plików do pracy wykonanej do tej pory.

AI sugeruje fundamentalnie złe podejście. Jeśli jesteś trzy prompty w głąb i zdajesz sobie sprawę, że AI poszło złą ścieżką, nie próbuj korygować kursu przyrostowo. Przywróć checkpoint, zrewiduj swój prompt, aby był bardziej konkretny co do podejścia, które chcesz, i rozpocznij tę sekcję od nowa.

Wygenerowany kod nie pasuje do Twoich istniejących wzorców. To jest najczęstszy problem w sesjach pair programmingu. Poprawka jest zawsze taka sama: odwołaj się do konkretnego przykładu wzorca, który chcesz. @src/services/orders.ts "Podążaj za dokładnie tym samym wzorcem obsługi błędów użytym tutaj" pokonuje akapit opisu.