Zamień ten tekst na URL Webhooka

Full-stack i Programowanie

Indie Hacking na przykładzie Alice

Wydanie nr
16
opublikowane
2023-06-22
Adam Gospodarczyk
Adam Gospodarczyk
overment
No items found.

Jeśli obserwujesz mój profil na Instagramie, to wiesz, że rozwijam różne projekty. Część z nich tworzę na własne potrzeby, w celu nauki, optymalizacji pracy lub zwyczajnie, dla zabawy. Zwykle rozwijam je w pojedynkę lub w towarzystwie jednego z przyjaciół. Jak się domyślasz, nie mamy więc zbyt wiele czasu na ich rozwój i konieczne jest pewnego rodzaju "naginanie" czasu, aby to wszystko miało sens. Pomyślałem więc, że opowiem o tym, co dokładnie mam teraz na myśli.


Projekty "po godzinach"

Rozwijanie projektów po godzinach jest bardzo powszechne w moim najbliższym otoczeniu. Część z nich powstaje na własne potrzeby, inne z planem zamiany ich w dochodowy biznes. Naturalnie tylko niektórzy są w stanie robić to długoterminowo, a zaledwie kilka z nich faktycznie zaczęła cokolwiek zarabiać. Niezależnie od celu, za każdym razem łączył je wspólny element — wszystkie były rozwijane "po godzinach" i niemal wszystkie samodzielnie (szczególnie na początku).

Rzecz w tym, że tworząc dowolny poboczny projekt, mamy przed sobą podobne wyzwania, jakbyśmy rozwijali go "full-time". Różnicą jest jednak fakt, że mamy znacznie więcej ograniczeń, zarówno w wymiarze ekonomicznym, jak i typowo operacyjnym. Dlatego potrzebujemy nieco innego schematu działania, aby móc skutecznie realizować swoje cele, bez nadwyrężania swojego zdrowia fizycznego i psychicznego.

W myśl cytatu z książki "Sources of Power" nie jestem w stanie rzetelnie wskazać Ci, co dokładnie wpływa na to, w jaki sposób sam realizuję projekty. Tym bardziej, że ze względu na formę przekazu, mogę się tylko domyślać, kim jesteś, co potrafisz i jakie projekty chcesz realizować lub czy w ogóle chcesz to robić. Jednak niezależnie od tego, jakie faktycznie są odpowiedzi na te pytania, mogę pokazać Ci, w jaki sposób sam tworzyłem jeden z moich projektów — aplikację "Alice". Twoim zadaniem będzie wyłącznie zwrócenie uwagi na pewne schematy, styl myślenia, techniki, narzędzia lub cokolwiek, co może okazać się przydatne. Mi pozostaje mieć tylko nadzieję, że w ogóle znajdziesz cokolwiek wartościowego dla siebie.

Skąd mam czas na projekty "po godzinach"?

Jeśli interesuje Cię rozwój własnych projektów, to prawdopodobnie zdarzyło Ci się czytać dobre książki na ten temat (np. Getting Real). Jeśli tak, to wiesz już jak istotne jest jasne definiowanie oraz bezpośrednie adresowanie realnego problemu, jaki w danej chwili rozwiązujesz.

Niemal zawsze odpowiedź na tak postawione pytanie nie jest oczywista i wymaga szerokiej wiedzy biznesowej, znajomości rynku, grupy docelowej czy samej technologii. Tak szeroki zakres już pierwszej chwili uzasadnia współpracę zespołową oraz rozwijanie umiejętności oraz procesów związanych ze skuteczną wymianą informacji oraz samym funkcjonowaniem różnych obszarów biznesu. Jednak gdy mówimy o realizowaniu projektów w pojedynkę, konieczne jest przełączanie się pomiędzy generalistycznym a specjalistycznym podejściem. Jest to istotne tylko i wyłącznie z jednego powodu, który można opisać często przywoływanym przeze mnie cytatem z książki Getting Real. Chodzi o odpowiedź na pytanie: "co w danej chwili nie ma znaczenia?" Nasze zadanie polega na tym, aby odpowiedzieć na to pytanie w sposób możliwie pozbawiony osobistych preferencji, obaw czy wpływu emocji. W zamian nasza uwaga powinna skupiać się na możliwym przybliżeniu do rzeczywistego obrazu tego, z czym faktycznie mamy do czynienia.

Umiejętność szukania odpowiedzi na to pytanie, jest użyteczna nie tylko w obliczu własnych projektów, ale także pozostałych obszarach życia. Dla mnie to pytanie jest fundamentalnie ważne do tego, aby móc precyzyjnie określić, co mam do zrobienia, co z tego jest istotne oraz jak mogę wykorzystać swoje zasoby, aby zrealizować swój cel lub przynajmniej w sposób znaczący się do niego zbliżyć. To pytanie nie uwolni nas magicznie od problemów, które są nieuniknione, ale** też zawsze rozwiązywalne** (cytat z książki The Beginning of Infinity). W zamian jednak zyskamy sporo dodatkowego czasu i energii na poszukiwanie odpowiedniej wiedzy oraz zwinne podejmowanie wystarczającej liczby prób, które pozwolą nam zaadresować wszystko, co pojawia się przed nami.

Gdy rozwijam swoje projekty, zawsze szukam miejsc w których mogę skorzystać z moich przewag. Jednocześnie w żaden sposób nie jestem wolny od problemów i sytuacji, które początkowo wydają się nie mieć żadnego wyjścia. Po prostu dzięki temu, że na każdym kroku próbuję ocenić "co nie ma teraz znaczenia", mogę przekierować swoją energię w miejsca, które są istotne. A to jak robić to skutecznie, jest chyba tylko i wyłącznie kwestią praktyki i doświadczenia. Myślę tak ze względu na fakt, że widzę postępy w tym jak sam pracuję. Z drugiej strony widzę też, jak wiele błędów popełniam, oraz że nawet najlepsze metodologie nie sprawdzają się w praktyce tak dobrze, jak brzmią w teorii. I choć znam większość narzędzi, które możesz kojarzyć z książek biznesowych, produktowych czy platform takich jak mindtools i HBR (spędziłem tam mnóstwo czasu), to rozpoczynając nowy projekt, zwykle kieruję się kilkoma pytaniami:

  1. Czego realnie potrzebuję / co chcę osiągnąć?
  2. Na czym polega problem, który rozwiąuje?
  3. Jakie zasoby mam do dyspozycji?
  4. Co w danej chwili nie ma znaczenia?
  5. Jakie potencjalne, krytyczne bariery mam przed sobą?
  6. Co jest krytycznie ważne dla projektu?
  7. Gdybym miał teraz zrobić tylko jedną rzecz, którą bym wybrał?

Gdy realizujemy projekt w pojedynkę lub bardzo małym zespole, to odpowiedzi na powyższe pytania przychodzą praktycznie natychmiast. Z kolei te, na których w danej chwili nie potrafimy zaadresować, zwykle mogą poczekać. W przypadku mojego projektu wyglądało to następująco:

1. Czego potrzebuję / co chcę osiągnąć?

Założenie aplikacji Alice jest dość proste. Chcę mieć dostęp do OpenAI z dowolnego miejsca na swoim komputerze, z pomocą skrótu klawiszowego i bez konieczności przełączania się do innej aplikacji. Dodatkowo fakt, że wielokrotnie sięgam po te same instrukcje, sprawia, że chciałem mieć możliwość zapisywania snippetów, które będę mógł łatwo uruchamiać. W tym wszystkim był jeszcze jeden element, w postaci możliwości realizowania zdalnych akcji i swobodnej kontroli interakcji z LLM (Large Language Model).

2. Na czym polega problem?

Źródłem problemu jest ograniczenie dostępności ChatGPT w postaci przeglądarki. Nawet skrót klawiszowy uruchamiający czat nie jest wystarczający, ponieważ sam fakt zmiany kontekstu i przełączania się do innej aplikacji, odciąga mnie od aktualnie realizowanego zadania.

3. Jakie zasoby mam do dyspozycji?

Globalne skróty klawiszowe dostępne są tylko w aplikacjach desktopowych lub w narzędziach do automatyzacji pracy (np. Keyboard Maestro). Nie chcę jednak ograniczać się do konkretnego narzędzia oraz platformy, więc potrzebuję stworzyć aplikację od postaw. Fakt, że programuję w technologiach webowych, sprawia, że dobrym kierunkiem jest Electron lub jego nowsza alternatywa, czyli Tauri.

4. Co w danej chwili nie ma znaczenia?

Na początek nie potrzebuję bazy danych oraz żadnych elementów związanych z automatyzacją, zaawansowaną konfiguracją czy dostępem do funkcji mojego komputera, poza schowkiem i rejestracją skrótów klawiszowych. Nie potrzebuję także zapisywania informacji pomiędzy uruchomieniami aplikacji, ponieważ początkowe ustawienia mogę wpisać na sztywno w kodzie.

5. Jakie bariery mam przed sobą?

Sam fakt, że projektowanie aplikacji desktopowych jest dla mnie praktycznie czymś nowym, czyni to istotnym wyzwaniem. Tauri wydaje się dobrym wyborem, ponieważ pozwala mi na korzystanie z technologii, którą już znam, a jednocześnie daje możliwość rozwijania się w tej, której się uczę (Rust). Kluczowe było dla mnie określenie czy: mogę zarejestrować globalne skróty klawiszowe, czy mogę nawiązać połączenie z OpenAI, czy mam dostęp do API systemowego schowka oraz, czy jest jakikolwiek sposób na to, aby przechowywać ustawienia aplikacji bez konieczności wysyłania ich gdziekolwiek.

6. Co jest krytycznie ważne dla projektu?

OpenAI nieustannie wydawało jakieś aktualizacje, a na rynku pojawiały się nowe narzędzia oferujące znacznie większe możliwości. Kluczowe więc było dla mnie zachowanie elastyczności projektu, dzięki której łatwo będę mógł implementować nowe funkcje. Bardzo zależało mi także na zachowaniu dobrych praktyk związanych z bezpieczeństwem i prywatnością użytkownika. Z tego powodu ważne było dla mnie przechowywanie praktycznie wszystkich danych lokalnie z wyjątkiem weryfikacji adresu e-mail oraz możliwości dostarczenia snippetów.

7. Jaką jedną rzecz bym wybrał?

Na to pytanie odpowiedź pojawiła się dość szybko i w przypadku projektu Alice, zaadresował ją Grzegorz. Chodziło mianowicie o minimalistyczny i elastyczny interfejs, umożliwiający wygodną interakcję z OpenAI. Z tego powodu w pierwszej kolejności zabrałem się za zakodowanie interfejsu czatu, obsługującego opcję strumieniowania, przez co nie jest konieczne długie oczekiwanie na wygenerowanie pełnej odpowiedzi.
Po przejściu przez te pytania zabrałem się do pracy. Czekało na mnie zaledwie kilka pytań na które nie znałem odpowiedzi:

  • jak uruchomić aplikację — zaczynając, nie wiedziałem nawet tego!
  • jak zbudować aplikację — tutaj z pomocą przyszła dokumentacja Tauri i kilka minut później miałem zainstalowane "demo"
  • jak strumieniować dane z OpenAI — tutaj pomógł mi sam ChatGPT
  • jak zarejestrować skróty klawiszowe — dokumentacja wyjaśnia to w prosty sposób ale potrzebowałem pola, które poprawnie zarejestruje wciśnięte kombinacje. Z jego stworzeniem pomógł mi ponownie ChatGPT
  • jak lokalnie przechowywać ustawienia aplikacji — odpowiedź na to pytanie znalazłem w społeczności Tauri na Discordzie

Kilka godzin wystarczyło mi, aby zaadresować wszystkie te punkty i wygenerować pierwszą wersję. I choć zrealizowałem moje założenia, widziałem, że zostało mi sporo pracy po stronie przygotowania promptów oraz obsłużenia ograniczeń modelu (np. API regularnie wyrzucało błędy, a jego szybkość działania była bardzo różna w zależności od pory dnia). To wszystko adresowałem jednak krok po kroku przez kolejne tygodnie, pracując nad tym projektem w wolnych chwilach.

Programowanie

Mając odpowiedzi na powyższe pytania, praktycznie wiadomo, co trzeba zaprogramować. Nie jest jednak jasne w jaki sposób mamy to zrobić, z pomocą jakich narzędzi oraz na które detale zwrócić uwagę a które całkowicie pominąć. Podobnie jak w przypadku etapu planowania oraz kształtowania wizji projektu, przy tworzeniu kolejnych funkcji również musimy nieustannie odpowiadać na wspomniane pytanie i kierować swoją uwagę tam, gdzie rzeczywiście jest wymagana.
Podczas nauki programowania, uczymy się dokładności, korzystania ze wzorców projektowych oraz stosowania możliwie najlepszych technik. Problem w tym, że niemal zawsze uwzględnia to utopijny scenariusz, w którym mamy do dyspozycji dużą ilość czasu. W praktyce jednak niemal nigdy nie ma to miejsca. W przypadku projektów typu "indie" praktycznie wszystkie schematy przestają obowiązywać i może to sugerować odejście od jakości na rzecz "dowożenia". W końcu "done is better than perfect" i choć to zdanie ma sporo sensu, to zwykle wykorzystywane jest jako wymówka przy dostarczaniu kiepskich rozwiązań.
Wspomniałem, że przy projektowaniu Alice, kluczowy był dla mnie estetyczny interfejs. Dzięki temu, że projekt graficzny stawiał poprzeczkę bardzo wysoko i przez cały czas wprost zachęcał aby pozostałe elementy aplikacji również były realizowane "zgodnie ze sztuką". Zatem bez wątpienia istnieje pewna granica lub rodzaj balansowania pomiędzy "jakością a dowożeniem", którą raczej powinniśmy ustalić sami.
W przypadku Alice wygląda to tak:

  • TypeScript wykorzystuję w około 60-70% i nie działam w trybie strict
  • Pisanie składni wspiera ESLint
  • Nie wykorzystuję wszystkich możliwości oferowanych przez SvelteKit i korzystam tylko z tych najbardziej istotnych
  • Do stylowania interfejsu, w 90% wykorzystuję Tailwind CSS
  • Deployment odbywa się automatycznie z pomocą Github Actions
  • System weryfikacji kont realizowany jest przez make.com oraz tokeny generowane przez kod aplikacji
  • Nie posiadam jeszcze żadnych testów
  • Utrzymuję wsparcie dla macOS oraz Windows
  • Jakość kodu aplikacji oceniam na 7/10. Miejscami logika mogłaby być lepiej zorganizowana, natomiast udaje mi się zachowywać względną elastyczność rozwoju

Patrząc na powyższą listę, można więc uznać, że popełniam krytyczne błędy. Jeżeli jednak weźmiemy pod uwagę listę funkcji oraz ogólny efekt końcowy, jaki osiągnąłem, to według mojej oceny "nie ma tragedii". Z drugiej strony, z pewnością są rzeczy, które można zrobić znacznie lepiej.
Podczas rozwoju tego projektu, istotną rolę odgrywała także kolejność wprowadzania kolejnych funkcji. Tych najbardziej kluczowych było zaledwie kilka, więc skupiłem się na tym, aby aplikacja w ogóle zaczęła działać i umożliwiała kontakt z OpenAI. Na stronie Product Plan znajdziesz bardzo prosty ale użyteczny mechanizm określania kolejności zadań.
U mnie wyglądało to tak:

  • Połączenie z OpenAI *
  • Strumieniowanie odpowiedzi *
  • Skróty klawiszowe *
  • Domyślne snippety *
  • Zapisywanie ustawień
  • Własne snippety
  • Ustawienia konta
  • Weryfikacja kont
  • Automatyczne aktualizacje
  • Historia konwersacji

Gwiazdką oznaczyłem najważniejsze zadania, które oddawały ogólną ideę aplikacji. Cała reszta stawała się bardziej istotna na późniejszym etapie rozwoju. Poza tym powyższa lista uwzględnia tylko funkcjonalności wchodzące w skład pierwszej wersji. Pełna lista jest znacznie dłuższa, ale to czy faktycznie będę ją realizować, zależy od tego, jak będzie wyglądało zainteresowanie aplikacją i czy uzasadniony będzie jej dalszy rozwój.

Przygotowanie do udostępnienia

Przygotowanie wersji aplikacji działającej na własne potrzeby jest czymś zupełnie innym, niż oddanie jej w ręce użytkowników.
W tym drugim przypadku musiałem zadbać o to, aby wyeliminować możliwie jak najwięcej błędów jeszcze przed oddaniem instalatora, a i tak nie wiedziałem, czego dokładnie mogę się spodziewać na innych urządzeniach. Z tego powodu przez kilka tygodni testowałem aplikację na różnych komputerach i systemach operacyjnych, aby wychwycić możliwie jak najwięcej takich przypadków. Oddałem także aplikację w ręce kilku osób o różnych umiejętnościach technicznych. Po raz kolejny przekonałem się, że "ostatnia prosta" pracy nad pierwszą wersją aplikacji jest najdłuższa. Co więcej, nawet gdy aplikacja stała się gotowa do udostępnienia, nadal potrzebowaliśmy dopracować kwestie dotyczące komunikacji oraz samego mechanizmu udostępnienia. Tam również pojawiły się kolejne problemy, związane nawet z tak teoretycznie prostym tematem, jak "podpisanie" kodu aplikacji. Na Developer ID z Apple czekaliśmy kilka tygodni ze względu na przedłużający się proces weryfikacji.
Gdy nadszedł już moment oddania aplikacji w ręce pierwszej, szerszej grupy użytkowników, naturalnie spotkaliśmy sporo różnych problemów. Niektóre z nich dotyczyły samej aplikacji a inne nie miały bezpośredniego związku z nią samą ale konfiguracją komputerów na których miała działać. Ponownie jednak był to okres natężonej pracy nad poprawkami i wydawaniem kolejnych wersji. Z każdą zmianą, aplikacja stawała się coraz lepsza, aż doszedłem do momentu w którym autentycznie jestem zadowolony z efektów.
Z jednej strony muszę przyznać, że włożyłem już sporo energii w ten projekt. Zegar aplikacji Rize wskazuje w przybliżeniu na 80 godzin samego kodowania. Gdy patrzę na efekty, mogę powiedzieć, że ciągłe zadawanie sobie pytania o to, co w danej chwili jest nieistotne, ma sporo sensu.

Dalsze kroki

Na ten moment nie wiem jeszcze zbyt wiele na temat przyszłości aplikacji Alice. Doświadczenie nauczyło mnie już, że aby projekt mógł się długoterminowo rozwijać, konieczny jest element ekonomiczny. Może to być bezpośrednie lub pośrednie dostarczanie zasobów umożliwiających dalszy rozwój. Tak jak wspomniałem na początku, długoterminowe prowadzenie projektu jest trudne i bardzo czasochłonne. Dlatego konieczna jest także wartość, którą będziemy otrzymywać w zamian i niekoniecznie musi być to bezpośredni przychód z subskrypcji czy sprzedaży dostępów.
Obecnie przyszedł czas na refaktoryzację, porządki oraz uzupełnianie funkcji, które do tej pory albo były zrealizowane w wersji minimalnej albo były odkładane na bok. Dodatkowy rozwój narzędzi umożliwiających interakcję z LLM (np. LangChain) również odgrywa istotną rolę, ponieważ część logiki aktualnej aplikacji, stopniowo zaczyna być zastępowana przez warstwę abstrakcji, która znacząco ją upraszcza.
Gdy kilka miesięcy temu pojawiła mi się koncepcja asystenta AI, w zasadzie nie miałem pojęcia, że dziś ten projekt będzie tak wyglądał. Tym bardziej że poświęciłem mu stosunkowo mało czasu (patrząc wyłącznie na samo kodowanie, ponieważ mnóstwo energii poświęciłem także na ogólne poszerzanie swojej wiedzy z zakresu tworzenia aplikacji dekstopowych oraz pracy z LLM).

Podsumowanie

Wszystko, co napisałem to zaledwie mały skrót tego, co wydarzyło się przez ostatnie miesiące. Pomimo tego, to o przytoczone pytania można oprzeć najbardziej istotne punkty tego projektu. W tym momencie trudno ocenić, jak rozwinie się jego przyszłość. Jednak liczba ponad 11 000 uruchomień oraz 400 użytkowników, pokazują, że trudno cokolwiek przewidywać.
O aplikacji Alice, prawdopodobnie jeszcze niejednokrotnie będę pisał. Do tego czasu liczę na to, że w powyższym tekście znajdziesz coś, co przyda Ci się w Twojej pracy. Na koniec chciałbym jeszcze polecić Ci jedną książkę, której autor osiąga sensowne rezultaty, realizując kolejne projekty adresujące bardzo konkretne problemy. Mowa tutaj o https://makebook.io Pietera Levelsa, którego również polecam obserwować na Twitterze.
Teraz pozostaje mi już tylko życzyć Ci powodzenia w dalszej nauce oraz rozwoju Twoich projektów.
Adam