Pokazywanie postów oznaczonych etykietą programowanie. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą programowanie. Pokaż wszystkie posty

piątek, lipca 03, 2026

Enum zdradziecki jest ;-)

Emum niby prosty jest, a bywa bardzo zdradliwy ;-)

Nie chodzi mi nawet o to, że w Java'ie do wersji 8 dało się stworzyć enuma, który formalnie nie istniał, więc kod:
public enum Toster {
OTHER_TEST(TEST.getS()),
TEST("TEST"),
TEST2("TEST2");

String s;
String getS() {
return s;
}
Toster(String s) {
this.s=s;
}
}
był legalny, ale rzucał NullPointerException... w Java 9 już się nie skompiluje...

Ani o to, że jeszcze wcześniej gdy kolekcje były w rozsypce zdarzał się kod:
var v = new Vector<>();
Enumeration enum = v.elements();
Który w Java 5 przestawał działać bo tam enum to był keyword ;-)

Nie, to są quirky wczesnej Java'y (choć nadal na produkcji jest MASA kodu działającego na JVM 8 ;-))

Chodzi o sam koncept i gdzie go używać.

Bo enum ma to do siebie, że to jest z definicji zamknięta lista. Można ją rozszerzyć, ale to zmienia API tego enuma. I niby nie ma problemu... trochę to psuje serializację, ale mało kto tego potrzebował.... do czasu.

Bo przyszło programowanie sieciowe i cały Internet zaczął pracować w debug mode z JSONem jako formatem do przesyłania danych.

Bardzo szybko programiści wpadli na to, że przecież niektóre z tych Stringów które tam są wysyłane to są enumy i zaczęli robić serializację do JSONa i deserializację z JSONa do obiektów z enumami....

Wczoraj widziałem kolejny team (mój ;-)), który poległ z enumem, choć tym razem w Pythonie.

Jak zwykle mamy deserializację po stronie backendu, która żeby to wszystko ładnie zrobić używa enumów.... i lecie 500 gdy ktoś przyśle coś spoza listy.

Tu jest też ciekawe pytanie czy zmiana kodu by to 500 nie leciało to poprawka błędu, czy nie? ;-)
Bo de facto kod zakładał, że istnieje legalna lista wartości i choć 500 nie jest może najlepszym sposobem na obsługę tego (400 byłoby lepsze) to nadal wołający "złamał" API ;-)
Stąd ja twierdzę, że to nie jest bug a new feature ;-)

W innym projekcie, też małżeństwo JSONa i enuma popsuł konwersję bo dostawca danych dodał nowy typ złącza... tu team był oburzony - bo złamali specyfikację - ale ta specyfikacja nigdy nie istniała, to team sobie sam ją napisał (bardzo 2000 like podejście).

Jeszcze wcześniej, znów JSON + Enumy, ale tam jeszcze Avro było gdzieś... i w końcu było tak architekt zmusił nas do używania Avro - bo tak lepiej, a później jak jeden z teamów źle tego użył to uznał, że OK, używajcie JSONa jako transportu ;-) [oryginalny problem był w tym, że JSON nie wspiera naturalnie migracji schemy....]

Jeszcze wcześniej, była sobie biblioteczka annotacji do Java'y, chodziło o Authz ze SpringBootem, tam w oryginalnej (mojej ;-)) wersji były scope'y jako String, ktoś wziął mój kod i uznał, że zrobi to lepiej i użył Enumów... działało do momentu gdy pojawił się nowy scope... który w ogóle nie dotyczył tej aplikacji, ale że ich wersja wymuszała deserializację scope'ów z tokena na enumy to mieli problem ;-)

Zaryzykuję twierdzenie, że enum nigdy nie został stworzony do bycia używanym do łączności między różnym kodem. Zrobiliśmy mu piggyback jakoś zapominając, że schema ma tendencję do zmieniania się i przez to mamy zabawne błędy z rodzaju - zmiana mnie w ogóle nie dotyczy, ale dane z wynikiem tej zmiany mogą mnie popsuć ;-)

Stąd ja tam czytając kod traktuję enum jako taki sygnał code smell (dla mnie code smell to wszystko co jest "niezwykłe", enum, protected (w Java'ie), extend (też w Java'ie), print, catch bez obsługi wyjątku i bez logu, deklaracja szerokich Exception i tak dalej).

Czy używam? Zależy, ale chyba zwykle nie.

Są świetne do maszyny stanów, ale stałe typu int w kodzie też działają, a są nawet bardziej czytelne.
Jest też sens zaryzykowania z enumem w jakieś większej klasie jeśli historia nam pokazała, że to lista jest zamknięta, albo gdy mamy obsługę "unknown"... ciągle to jednak jest ryzykowne bo jak wiele rzeczy da się rozszerzyć gdy pojawi się nowy enum, to nigdy nie masz pewności, że gdzieś nie ma czegoś co zostanie popsute.

Ja bym chyba nie używał, jakoś w moim kodzie nie znalazłem powodów, ale nie zakazuję, daję tylko takie "dwa razy się zastanów czy użyć".

A jeśli to ma być do JSONa to bym prawie nigdy nie używał ;-) Bardzo łatwo popsuć dane. Nawet gdy masz świetną obsługę i nieznane enumy są zmieniane w UNKNOWN... to problem jest w tym, że jeśli te dane zapiszesz i przekażesz dalej to kolejny klient dostał właśnie dane z usuniętą informacją. Ty chcesz zmienić pole innego typu, ale przy okazji popsujesz pole którego nawet nie używasz....
Tak, to można też obejść, ale to już jest dobudowywanie na siłę maszyny która będzie naprawiać nasz zły design.

Myślę, że enum jest podobny trochę do developmentu z branchami. Daje złudne poczucie bezpieczeństwa.
Wielu developerów tłumaczy fakt używania branchy tym, że czują się bezpieczni bo nie mają konfliktów... do momentu integracji, która jest potencjalnie największym źródlem problemów... Enum też daje poczucie bezpieczeństwa i elegancji. Wszystko ładnie się konwertuje, nie używamy Stringów, nie ryzykujemy porównywania == między typami, których nie wolno tak porównywać.... tylko, że to też jest takie pozorne bezpieczeństwo, bo w momencie gdy schema się zmienia to nagle staje się widoczna w wielu miejscach, których byśmy mogli nawet nie podejrzewać o to, że będą dotknięte przez tę zmianę.....
Tu pamiętam, że robiąc za orkiestrator do deploymentów widzieliśmy pole status i pokazywaliśmy je userowi wg zasady, że mieliśmy:
var textToShow = translations.getOrDefault(s, s);
i jakież było nasze zaskoczenie jak przebudowa w drugim systemie, który zaczął wysyłać w pewnym momencie nieznane stany to wszystko nadal działało? ;-)
Tajemnica była w tym, że wymusiliśmy kontrakt "fireAndForget", czyli my dawaliśmy pakiet do deploymentu, ale maszyna stanów była w kodzie, który robił deployment (w operatorze dla K8S), więc on sobie wprowadził swoje enumy i rozszerzył ich listę.... ale nas nie popsuł ;-)


Podobne postybeta
Lubię enumy
Javozagadka ;-)
finalize() - do czego służy, a do czego nie i z czym to się je.
Nie lenistwo, a strach. Prawdziwe źródło długu technicznego
Modale nie takie dobre dla Androida ;-)

Zdolny Junior z rozmachem ;-) zwany też jako Claude Code

Nudne będzie bo znów o AI ;-)

Czułem od dawna, że jest problem z AI w postaci Claude Code czy podobnych, ale nie do końca umiałem wskazać palcem gdzie jest problem.
Dziś złapałem Claude Code na dokładnie tym co czułem ;-)

Mamy sobie w produkcie opcję pobrania czegoś, wtedy jest billing robiony. Ale billujemy tylko pierwsze pobranie, kolejne są "za darmo".
Backend o tym wie, ale frontend nie ma sygnału, że to coś już zostało pobrane.

Dałem ten kod Claude Code i najpierw poprosiłem o sprawdzenie czy w ogóle jest ta informacja o pobraniu gdzieś zapisana... jest, bo mamy link do pobranego pliku.
Mamy ten link wpisany w pewnym obiekcie połączonym z projektem.
Ale klientowi wysyłamy coś innego, mniej szczegółowego.

No to mój zamysł był taki, jak mamy te informacje i mamy wszystkie klucze potrzebne do ich przeczytania to gdy UI prosi Backend o info o projekcie i scenach (są w projektach) to co mi szkodzi dodawać w locie wasAlreadyBilled do scen, które zostały już pobrane.

Zrobił, ale zmienił jeszcze dodatkową lambdę... pytam czemu. Odpowiada, że bez tego nie może dodać tej informacji do tego obiektu bardziej ogólnego...

Użyłem mojego przerywacza "stop and think" i pytam czemu to zrobił, przecież ja mam masę danych, które nie są tak zaktualizowane i przecież nie będę ich konwertował, że ja chcę on-the-fly merge'a...
Uznał, że może to i ma sens i zmienił kod w tym kierunku... napisał mniej tego kodu, a do tego dane wyeliminował konieczność transformacji danych.

Miał dotykać lambdy dającej informacje o projekcie (i nie, nie będziemy tu dyskutować jak szalonym pomysłem jest używanie lambd do takich rzeczy....), ale zmienił inną która zapisywała w oryginalnym obiekcie link do tego co można pobrać... Porobił też jakieś zmiany w tej projektowej... wszystko bez chwili zastanowienia. Idąc takim flow: jak w opisie sceny jest link, to znaczy, że ktoś pobrał, dzieje się to w lambdzie B, lambda A wysyła do UI liste projektów i scen, user chce mieć w wyniku lambdy A info o billowaniu nazwane wasAlreadyBilled... i uznał, że skoro już w B robi update obiektu szczegółowego i tego ogólnego (używanego przez A) to on sobie zrobi update tego obiektu ogólnego....
I to ma sens, ale generuje problem "a co z już istniejącymi danymi?". Nie sprobowałem, ale nie zdziwiłbym się gdyby po pytaniu bez stop and think i pisania o merge'u i że nie chcę konwersji, to czy sam by mi nie zaproponował kodu do konwersji, a że nie ma ogólnego mechanizmu to by dopisał kolejne 500 linii kodu z konwerterem....

Zrobił to jak junior. Junior robi piękny projekt, uznaje że tu się doda pole, dopisze kod do aktualizacji tego pola... tylko nie pomyśli, że coś co powinno być lekkie, zmienił właśnie w taska numer 1, bo jeszcze musi być task numer 2 do konwersji danych.

To jest to, że junior gdy patrzy na kod widzi tu i teraz i wie jak ma być ostatecznie, ale nie myśli o tym jak stan faktyczny teraz, który jest poza kodem, w danych czy przyzwyczajeniach usera zmienić w to jak ma być.

Tak robią juniorzy (fakt, nadal spotykam seniorów którzy też na to tak patrzą...)

Claude Code to taki junior, ale fakt junior z wyobraźnią i rozmachem ;-)


Podobne postybeta
Różnica między Seniorem i Juniorem ;-)
Jak używamy Java&#39;y ;-)
Pierwszy dzień w przyszłości, czyli programista jako dyspozytor Agentów
Zauroczył mnie Cladue Code ;-)
O tym czemu branche są złe...

środa, czerwca 03, 2026

O urokach uprzęży – czyli o tym, że okiełznana moc LLM-ów jest bardziej użyteczna niż nieokiełznana

GenAI/LLMy dziś to są konie pociągowe. O tym, jak dobrze działają, decyduje jakość uprzęży (aka harness).

To było widać w analizie wycieku z Claude Code, która wywołała wiele uśmieszków i komentarzy w stylu: „Jak to? Najlepsze narzędzie do okiełznania LLM-ów jest takie prostackie?”. Masa promptów, a do tego kod z w pełni deterministycznymi mechanizmami, jak wykrywanie frustracji przez przekleństwa...

Jakoś wielu komentatorom umykało, że ten pełny determinizm regexów jest mechanizmem kontroli, wymuszającym stabilność systemu. Fakt – ten do wykrywania frustracji był pewnie po prostu łatwiejszy do zaimplementowania lokalnie niż wysyłanie całego kontekstu do LLM-a tylko po to, by ten wyłapał kilka wulgaryzmów.

Ale jak się zastanowić, to nie jest dziwne.
Na dziś LLMy są świetne, ale jeszcze nie potrafią się same kontrolować. Nie mają zdrowego rozsądku, nie znają relacji przestrzennych, a nawet nie do końca rozumieją, że np. jeśli człowiek je i ma usta pełne jedzenia, to nie może jednocześnie mówić.

W pierwszej fazie zachwytów nad LLM-ami poszliśmy na żywioł. Korzystaliśmy maksymalnie z tego, że potrafią na podstawie poprzednich słów genialnie napisać kolejne. Taki LLM jest sprytniejszy od łańcuchów Markowa – choć nie rozumie per se tego, co pisze, to sama gramatyka i język kodują pewne informacje i zależności. To specyficzne „strukturalne zrozumienie” (nie kognitywne, lecz wynikające z wyczucia struktury) wynika stąd, że model widzi cały kontekst jednocześnie.
Okazało się też, że świetnie działa to na kodzie.

Szybko jednak wyszło na jaw, że to podejście sprawdza się głównie wtedy, gdy na wynik patrzy człowiek, weryfikuje go i na bieżąco przygląda się temu, co powstało.

To jest IMHO ten punkt, którego nie dostrzeżono na początku, gdy wielu uznawało, że LLM-y natychmiast wyprą ludzi. Bo coś, co modelowi zajmuje ułamki sekundy, człowiek musi robić przez 15-30 minut albo dłużej.

Sam pamiętam moje zdziwienie, gdy dałem LLM-owi zadanie, które dawaliśmy programistom podczas rekrutacji. Zrobił to w kilka sekund, razem z napisaniem kodu i wskazaniem ukrytych pułapek (gotchas)... (OK, sam, gdy dostałem to zadanie, rozwiązanie znałem po 1,5 sekundy, z czego całą sekundę spędziłem na szukaniu gotcha, ale faktem jest, że napisanie czystego kodu zajęło mi potem te 15-20 minut).

W tym miejscu nastąpił wysyp masy narzędzi, które działały... ale jednak nie do końca.

Pierwszym ruchem było dodawanie lepszych instrukcji i cały prompt engineering, który sprowadzał się do tego, by wyjaśnić LLM-owi, co dokładnie ma zrobić i jak ma weryfikować swoje działania. W końcu jeśli podamy modelowi precyzyjną instrukcję oraz kryteria sukcesu, to zazwyczaj dowiezie wynik.

Twórcy LLM-ów też to zauważyli. Dostrzegli, że często sam model potrafi rozbić problem na mniejsze części, co doprowadziło do rozwoju metod Chain of Thought. Dziś, w modelach z fazą „thinking”, AI samo wykonuje tę potężną pracę analityczną przed wypuszczeniem odpowiedzi.

Teraz zaś wchodzimy w moment, gdy dociera do nas, że LLM-y są świetne w generowaniu tekstu, ale musimy je kontrolować i zakładać im wspomnianą „uprząż”. To może być coś tak prostego jak regex czy inne deterministyczne metody walidacji, a mogą to być osobne prompty i modele obserwujące odpowiedź i reagujące na nią. 

No bo jak na przykład testować coś, co pod spodem używa LLM-a? 
Jedną z metod jest karmienie go znanymi przypadkami testowymi, gdzie z góry znamy oczekiwany rezultat – i nagle mamy klasyczny test regresyjny dla sztucznej inteligencji. To zadanie jest znacznie prostsze, gdy LLM wyrzuca ustrukturyzowane dane (np. JSON) albo gdy generuje zapytania do bazy danych, bo wtedy możemy po prostu zweryfikować końcowy wynik operacji na bazie.

Innym podejściem jest instruowanie modelu, by najpierw napisał testy, a później... kategoryczne zabronienie mu ich modyfikowania. To kluczowe, bo LLM-y są sprytne i domyślnie wybierają ścieżkę najmniejszego oporu. Jeśli kod nie przechodzi testu, model potrafi wpaść na pomysł, że najprościej będzie po prostu zmienić treść testu.

To znaczy... my, programiści, też tak czasem robimy. Różnica polega na tym, że człowiek z czasem uczy się, że test wolno zmienić tylko wtedy, gdy jego wywrotka jest faktycznie oczekiwanym rezultatem wprowadzonej zmiany w logice biznesowej. LLM tej etyki zawodowej jeszcze nie ma.

W ten sam trend wpisuje się podejście agentskie. Agent dostaje do dyspozycji konkretne narzędzia, a te narzędzia mają już twarde, kodowe ograniczenia. Jeśli na przykład funkcja do pobierania zawartości sieci dostanie zamiast poprawnego adresu URL bezpośredni link do lokalnego pliku, system od razu zgłosi błąd. Narzędzia są deterministyczne i ich użycie zmusza LLM do poruszania się w ściśle ograniczonej przestrzeni.

Co będzie dalej? Może – a w zasadzie to już się dzieje, bo sam łapię się na tym, że próbuję tak naprowadzać sztuczną inteligencję – kolejnym krokiem będzie okresowe odpytywanie LLM-a przez system nadzorujący: „Co Ty właściwie próbujesz w tym momencie zrobić i dlaczego?”. Odpowiedź, wraz z pełnym zapisem historii tej „rozmowy”, będzie następnie przekazywana do analizy innemu, niezależnemu modelowi pełniącemu funkcję sędziego.

Tu pojawia się pytanie, na które nie znamy jeszcze odpowiedzi, ale możemy się domyślać ;-)

No bo czy to możliwe, że LLM-y wciąż mają ogromną przestrzeń do autonomicznego wzrostu? Może w samym tym strukturalnym semi-zrozumieniu języka tkwi jeszcze więcej surowej mocy? Może same modele da się wytrenować tak, by realizowały część tych zadań kontrolnych i pilnowały same siebie? 
A może, jeśli dotarliśmy już blisko fizycznych granic architektury transformerów, ta kontrola będzie zadaniem dla nas, programistów? Nasza rola ewoluje: to już nie tylko pisanie kodu, ale budowanie zamkniętych „tras”, po których bezpiecznie mogą poruszać się LLM-y i agenty.

Część mnie uważa, że przyszłością jest właśnie ta druga opcja. 
Obecny wyścig gigantów GenAI wygląda już jak wojna na wyniszczenie. Nawet jeśli któryś z nich dotrze w końcu do mitycznego Graala, czyli prawdziwego AGI – systemu zdolnego rozwiązać dowolny problem i realnie „myślącego” w naszym ludzkim rozumieniu – to konkurencja zreplikuje ten sukces zaledwie 3 do 6 miesięcy później. Pierwszy gracz na miejscu po prostu nie zdąży wykopać fosy biznesowej. Cała idea zmonopolizowania rynku przez jedną „Superinteligencję” rozbija się o realia rynkowe. 

Nawet jeśli takie AGI zaprojektuje w ułamku sekundy lepsze procesory i wydajniejsze źródła energii, to fizyczny czas oczekiwania na wolne linie produkcyjne w fabrykach sprawi, że rywale szybko dogonią lidera. A niewykluczone, że będą mieli po drodze większe zasoby finansowe.

Stąd wydaje mi się, że czytelny sygnał, jaki płynie z rynku – gdzie wszyscy masowo podnoszą ceny za tokeny – jest prosty: branża już zrozumiała, że rewolucyjne AGI nie czai się tuż za rogiem. Albo alternatywnie: mają już AGI, które jako pierwsze racjonalnie wytłumaczyło im, że pora zacząć w końcu zarabiać prawdziwe pieniądze.


Podobne postybeta
wait() i notify()/notifyAll() - najbardziej nierozumiane metody klasy Object ;-)
Miałem farta...
Nie, Scrum nas nie "uratował" od Waterfalla... za to powoli sam się nim staje ;-)
Chciałem popsuć G1 i mi się na razie nie udało ;-)
Agent nie na wszystko pomoże ;-)

piątek, maja 22, 2026

AI kradnie może (na razie?) nie pracę, ale „wieczorną zabawę w kodowanie” już tak ;-)

Tworząc Boba – mojego asystenta w pracy, który na razie umie rozpoznawać historyjki w Jirze, robić raporty do praw autorskich i czytać maile w poszukiwaniu tych najważniejszych (nadal jest na etapie "uczem siem") – wpadłem na pewien pomysł. Chciałem mieć w Obsidianie folder z szablonami w plikach Markdown.

W pracy (bo w życiu prywatnym na szczęście nie*) muszę często wysyłać maile według powtarzalnego schematu. Podobnie było kiedyś, gdy pisałem narzędzie do analizy danych – struktura kodu prawie zawsze wyglądała tak samo. Idealne miejsce na gotowe szablony. Próbowałem wcześniej z Keyboard Maestro, ale to nie było to.

Mój cel jest prosty: Bob i inne narzędzia mają do minimum skrócić czas, który marnuję na zadania administracyjne. Wszystko po to, bym mógł kodować... albo raczej dyrygować Agentami, bo dranie są już szybsze ode mnie.

Plan był piękny

Chciałem zbudować narzędzie zintegrowane z Alfredem. Wpisuję tmpl , dostaję listę szablonów (pliki Markdown, Java, Python – ogólnie wszystko, co tekstowe), które mogą mieć zmienne pola do wypełnienia, zarówno w nazwie, jak i w treści. Wybieram szablon, dopisuję parametry, wciskam Enter i gotowe – zawartość ląduje w schowku. A jeśli to Markdown, to od razu z formatowaniem.

No i plan upadł...

...bo z głupia frant wrzuciłem ten opis do Antigravity. Drań nie dość, że napisał wszystkie potrzebne skrypty, to jeszcze wypluł gotowego ZIP-a z zawartością całego Workflow do Alfreda.

I jak tu ufać komputerom? Człowiek chce sobie coś pokodzić, a maszyna odbiera całą przyjemność. ;-)

Małe pocieszenie

Na razie system nie ogarnął jeszcze jednego wymagania: chciałbym, aby przy kopiowaniu Markdowna do schowka trafiał on tam jako Rich Text. Outlook, jako flagowy produkt Microsoftu, nie lubi takich "fanaberii" jak czysty Markdown i domaga się sformatowanego tekstu.

Runda pierwsza dla AI, ale walka z Outlookiem wciąż trwa.

* – Ogólnie w życiu prywatnym problemem nie jest nadmiar obowiązków.



Podobne postybeta
Bob wykazuje inicjatywę ;-) i jakże piękny mógłby być Googlebook....
Hacky tool ;-) - workflow do Alfreda, który pozwala wykonywać różne konwersje na zawartości schowka ;-)
Pierwszy dzień w przyszłości, czyli programista jako dyspozytor Agentów
Kopia zapasowa ważna ;-)
Budowanie Boba ;-)

wtorek, marca 31, 2026

O wyższości debuggera

Tak w ramach walki o moje 50 linii kodu dziennie męczę dzisiejsze zadanie z LeetCode i stwierdzam, że prawdziwe IDE i Debugger pomagają ;-)

Zwykle staram się zadania robić tylko w edytorze LeetCode, w razie rozmowy tak to będzie wyglądać.
Gdy chcę jeszcze bardziej wyostrzyć moje kung-fu to nawet używam nawet "blindfold programming", czyli próbuję robić zadanie w głowie i dopiero jak je "napiszę w głowie" i przetestuje w głowie to piszę kod.
Ale nie zawsze się da, czasem zadanie jest za trudne, czasem ja za głupi ;-)

Wtedy jest czas na big guns ;-)
Wrzucenie kodu do IntelliJ i zabawa z Debuggerem :-)

Ja wiem, że "I'm supposed too" uruchamiać ten kod w głowie w razie jakiegoś interview... co jest wg. mnie formą sprawdzenia tak naprawdę IQ i pamięci krótkotrwałej gdzie IQ często służy do tego by wymyślić sposób na "zchunkowanie" zadania tak by móc je zmieścić w pamięci krótkotrwałej.. inna sprawa czy to jest naprawdę dobry sposób na testowanie programistów. 
Bo ja twierdzę, że do normalnej pracy nie chcesz tych najbystrzejszych oni i one (chociaż one są zwykle bardziej powściągliwe przed robieniem głupich rzeczy) mogą przesadzić w komplikacji, w końcu potrafią to ogarnąć...

Ale wracając do Debuggera, on Ci daje takie szerokopasmowe spojrzenie na wykonanie kodu. Masz ciągle widok aktualnego stanu, w razie coś się dziwnego dzieje możesz się cofnąć (drop frame... co zresztą zachęca człowieka do dodawania metod, bo drop frame polega na powrót po prostu na początek wykonania danej metody, bo zrzucana jest 1 ramka ze stosu).... 

Widzisz kod, widzisz stan zmiennych i często jest tak, że dochodzić do pewnego miejsca... nie wykonasz tego kroku jeszcze a już widzisz problem ;-)

Ma to oczywiście też wady, bo po jakimś czasie zaczyna się leczyć objawowo. Jak ta zmienna powinna zostać np. zwiększona o 1 to się dodaje to zwiększanie i próbuje dalej... więc w końcu może powstać potworek, który działa, ale nie wiadomo czemu... choć to jest rzadki przypadek.

Inna sprawa, że widzę, że znów w czasach lambd i cloud coraz więcej developerów jedzie "sprawdzę na cloudzie" i znów do debugowania używa się logów ;-)
No bo po co móc uruchomić kod na lokalnej maszynie, gdzie masz możliwość przeglądania stosu i wszystkiego... nie, zróbmy trudniej! 

(taka metauwaga... ja ten tekst piszę jako przerywnik by się zresetować z jednego podejścia i zaatakować innym i przyznaję, że do pisania tego tekstu używam tylko ułamka mocy obliczeniowej mózgu w porównaniu do kodowania ;-))



Podobne postybeta
Kontekstowe zapominanie ;-)
Referencje w Java'ie
Nowy potworek na tapecie ;-)
Moja możliwa zemsta za zamknięcie Google+ ;-)
Focus

50 linii kodu dziennie

Mam misję - 50 linii kodu dziennie.

To jest taki mój wentyl bezpieczeństwa. 

Ale mam problem ;-) bo pytanie powstaje "jak liczyć kod napisany przez Copilota i jego kuzynów?" ;-)

Wczoraj np. popełniłem skrypt, który liczył długość dróg w pliku OpenDRIVE, jakieś ~150 linii Pythona.... z czego kluczowe kawałki generował Copilot i Gemini... i jak to liczyć?

Na szczęście LeetCode mi trochę podratował statystyki (chociaż oszukałem i przekopiowałem rozwiązanie z dnia wcześniej i zmieniłem po prostu końcówkę by rozwiązywała problem w tej wersji..... to jest w ogóle przerażające, że w pewnym momencie piszesz kod i ma "o, to już pisałem, gdzieś mam ten kod" i tak można znaczną ilość kodu napisać ;-))

Chyba trzeba wrócić do podejścia sprzed 10-12 lat, zacząć dopisywać testy do istniejącego kodu (nie mojego!) i tak robić moje 50 linii ;-)



Podobne postybeta
Kupię sobie jednak Copilota
Pierwszy dzień w przyszłości, czyli programista jako dyspozytor Agentów
Samolubna Tajemnicza Wyspa Genów ;-)
Paradoks SSD ;-)
&quot;Bóg urojony&quot;

wtorek, grudnia 09, 2025

Nie lejmy betonu na kod....

Jak można zdefiniować gentlemana? Jako mężczyznę który jest w stanie opisać piękną i zgrabną kobietę nie używając rąk...
Jak można zdefiniować dobrego inżyniera? Jako kogoś kto potrafi opisać złożony system bez machania rękami ;-)

Mnie się wydaje, że sztuka polega na tym by widząc problem dokonać jego rozebrania na mniejsze problemy i patrzeć na te problemy jak klocki, z których można budować rozwiazanie. 
Może niektóre klocki są niepotrzebne, a znów inne przydadzą się do czegoś jeszcze? Wtedy nie budujemy ficzera X, budujemy środki do osiągnięcia tego co ma robić ficzer X i zwiększamy ilość dostępnych klocków.
W komputerach zwykle nie rozwiązujemy za każdym razem nowych problemów, a raczej inną wersję tego samego problemu.
Nagle celem staje się nie tyle tworzenie tych klocków, a używanie tych klocków do budowania czegoś większego.
Nie znasz pełnych wymagań? Nie musisz na nie czekać, identyfikujesz kluczowe problemy i je rozwiązujesz tak, że w razie czego chociaż część z nich z niedużymi modyfikacjami pozwoli rozwiązać prawdziwy problem.

Przeszedłem w życiu przez kilka firm ;-) i widzę, że często popularne jest budowanie ficzera w którym sam ficzer staje się ważny. Wydaje mi się, że ja od pewnego momentu patrzę na kawałki i znając historię umiem mniej lub więcej przewidzieć, że ten kawałek przyda się jeszcze w innych miejscach. 

Patrzę na kod (bo kod jest wg mnie nadal ważny) ale bardziej patrzę na powierzchnie styku. Co jest w środku to szczegół implementacyjny. Ważne jest co jest na zewnątrz, jaki jest interfejs. 

Do tego patrzę na kod i staram się myśleć jak on się będzie mógł zmienić, większej zmiany nie zrobimy "na raz" ale na raty się może udać. Nawet nie wiedziałem, dowiedziałem się, że to jest Strangler Fig Pattern ;-)

Żeby nie było ja potrafię napisać brzydki kod ;-) ale zwykle potrafię też zbudować coś co można dalej modyfikować i rozwijać, nie zalewam tego betonem. Zawsze gdzieś można włożyć "coś pomiędzy" i ukryć zmianę przed resztą kodu... fakt czasem jest to trudne, ale zwykle się daje.

Ogólnie lubię robić to tak, żeby komponenty zbytnio o sobie nie wiedziały.... i też nie piszę jednak za bardzo OOP kodu bo u mine class members jako zmienne to głównie jakieś "serwisy" czyli instancje klas robiących coś... zwykle jak spojrzeć to moje klasy są takie semi-bezstanowe.... choć fakt nie jest idealnie bo zdarza mi się, że metoda może modyfikować swój input.... to jest jednak inżynieria nie religia ;-)

To samo podejście próbuję stosować do większych komponentów. W moim idealnym świecie zmiana jednego komponentu jest niewidzialna dla pozostałych... Tak, to znaczy, że może jak gdzieś jest np. pipeline który dostaje format A, pierwszy kawałek przerabia na B, drugi pracuje na B i go ulepsza i w końcu publikuje...  to ja bym wolał wejście A, ale pierwszy robi (A,B) i drugi pracuje na (A,B) i produkuje A... więc z czasem mogę funkcjonalności z pierwszego przenieść do drugiego i go później zabić.

Czyli ogólnie architektura czy kod to jest tylko snapshot całego rozwiązania tu i teraz. Nie musi być doskonały, musi robić tylko to czego od niego na dziś wymagamy, a do nowych wyzwań zawsze może urosnąć.


Podobne postybeta
W tworzeniu softu droga od tego jak jest do tego jak ma być jest ważniejsza od tego jak ma być
Czy vibecoding zmienia ważność ludzi? Czy PM/PO stają się bardziej niezbędni od inżynierów?
Tagowanie postów MLem - trzeba to przepisać ;p
Rozdzielanie dwóch światów ;-)
Agent nie na wszystko pomoże ;-)

piątek, listopada 28, 2025

Zaczynam się przekonywać do agentów AI....

Używając Google Antigravity próbuję sobie zbudować coś do Agentów AI.

Teraz poprosiłem to coś o zrobienie kodu do monitorowania strony autora książek o Bobiverse... i agent (mój, używający qwen2.5-coder:32b poległ... napisał kod, ale się wywalił na pip install ;-).

To jest właśnie ciekawe, że wpadł na to by użyć pip install.

Byłem sceptyczny... tzn. widziałem i widzę moc w Copilot'cie i podobnych, ale nie dostrzegałem potencjału w agentach.

Potencjał jest taki, że teoretycznie rzeczywiście można sobie wyobrazić tworzenie softu jako zarządzanie zespołem agentów ;-)

Już mi się zdarzało w roli Team Leada/PO pisać historyjki techniczne i robić grafy które pokazywały zależności. Robiłem to dla zespołu. Zespołu człowieków.

Tak każdy członek mojego zespołu był wielokrotnie sprytniejszy i mądrzejszy od takich agentów, ale i tu jest hipoteza - możliwe, że istnieje "kaskada" głupich agentów, które przez swoją syntezę będą ciut mądrzejsze ;-)

No bo ludź może napisać instrukcję, którą jeden model/Agent przerobi na implementation plan i zleci zadania innym agentom.... i jeśli im to wyjdzie...

Nadal tu jest wg mnie takie wielkie "IF" czy bardziej "JEŚLI".... Ale wydaje się to być ciekawy obszar...

A tak pod koniec stycznia sam zacznę nowy rozdział pracując z agentami ;-)



Podobne postybeta
Pierwszy dzień w przyszłości, czyli programista jako dyspozytor Agentów
AI kradnie może (na razie?) nie pracę, ale „wieczorną zabawę w kodowanie” już tak ;-)
Agent nie na wszystko pomoże ;-)
Bob wykazuje inicjatywę ;-) i jakże piękny mógłby być Googlebook....
"Arystokraci" od siedmiu boleści

wtorek, listopada 11, 2025

_ w Java'ie :-)

Ha! dziś się naumiałem, że w końcu w Java'ie (może od 21, ja to 25 sprawdziłem) _ jest zmienną "throw away" :-)

O co chodzi?

Np. jeśli robimy coś takiego:

m.computeIfAbsent(key, k -> new ArrayList<>()).add(val);

to to k jest zmienną, jak na zewnątrz jest zmienna k to mamy problem i się nie skompiluje:

var k = 7;
m.computeIfAbsent(key, k -> new ArrayList<>()).add(val);

bo będzie sie pluło w k ->, że k jest już zadeklarowana w scope'ie...

Teraz można użyć _ zamiast k:

m.computeIfAbsent(key, _ -> new ArrayList<>()).add(val);

co niby nie robi różnicy (chociaż od pewnego momentu taki kod się nie kompilował), ale teraz można mieć coś takiego:

var _ = 7;
m.computeIfAbsent(key, _ -> new ArrayList<>()).add(val);

i się skomplikuje bo to _ jest throw away... więc np. takie coś:

var _ = 7;
System.out.println(_); // <--- Using '_' as a reference is not allowed
m.computeIfAbsent(key, _ -> new ArrayList<>()).add(val);

to kompilator zakrzyknie, że ej, nie wolno.

Mała rzecz, a cieszy :-) 




Podobne postybeta
Java 8 + lambdy = wolno ;-)
Zaczynam woleć Map nad Map ;-)
Javozagadka ;-)
Który kod (nie kot! ;-)) lepszy?
2016 będzie rokiem "nietypowym"

poniedziałek, października 06, 2025

Zacznę dodawać do swoich prywatnych projektów NEXT_STEPS.md

Będę od dziś próbował do moich projektów dodawać sobie plik NEXT_STEPS.md ;-) w którym będę próbował zapisać co ostatnio zrobiłem i dlaczego, oraz co chcę zrobić w przyszłości.

Na razie zaczynam, więc to są luźne myśli. Ale zawsze mi brakowało takiego miejsca i próbowałem w różnych Obsidianach i innych... tym razem spróbuję w kodzie ;-)

Bo gdy dzień się kończy, człowiek kończy zmiany w kodzie to ma w głowie jakieś pomysły, a jak wraca do kodu to te pomysły mogą już być dawno zapomniane.... jak to jest kod pracowy to się zwykle doń wraca dość szybko, więc taki dokument nie jest potrzebny, bo się zwykle pamięta, ale w prywatnych projektach to może być przydatne.

Dla firmowych przydatny mógłby być dokument, który tłumaczy czemu coś zrobiono tak, a nie inaczej. Nie chodzi mi o ADRy, które próbują udawać obiektywność, a o coś co dokumentowałoby "tak, trzymamy wygenerowane credentiale w postaci niezaszyfrowanej, to może być problem, na razie trzymamy je w bazie pod kluczem "UGLY_HACK", w przyszłości jeśli do tego wrócimy bo np. okaże się, że to łamie zasady bezpieczeństwa, to idea jest taka by pod tym samym kluczem (bez UGLY_HACK) trzymać obiekt w którym będą zaszyfrowane credentiale i id klucza użytego do szyfrowwania, WAŻNE klucz powinien być trzymany w AWS Secret Managerze i pobierany leniwie, id klucza może być z kropką do oddzielenia głównego klucza z wersją" i jak ktoś znajdzie w kodzie to co go niepokoi to mógłby przeczytać czemu i od razu wiedziałby jaki był zamysł.

Zobaczę czy będę to stosował, na razie dodałem do 1.5 projektu ;-)



Podobne postybeta
Strasząca książka - Extinction: The Thriller
Generowanie plików ePub z OpenOffice.org :-)
Mam milion rzeczy na głowie... co robić?
Nie lenistwo, a strach. Prawdziwe źródło długu technicznego
Pierwszy dzień w przyszłości, czyli programista jako dyspozytor Agentów

niedziela, lipca 27, 2025

Case insensitive OS ma pewne problemy ;-)

Tak ogólnie to jestem wielkim zwolennikiem backward compatibility i seamless transition.

Czyli jak sam piszę soft to próbuję to robić tak, że będzie umiał pracować ze starymi formatami danych.
Ogólnie staram się to tak robić by istniała zawsze deterministyczna ścieżka transformacji. To się do pewnego stopnia da prawie zawsze zrobić.

System plików w macOS jest by default case insensitive, czyli plik README.md i readme.md są tym samym plikiem. Da się podobno system plików zmienić na case insensitive, ale wymaga to dziwnych operacji (jak reformatowanie dysku, bo jak OS jest OK z oboma przypadkami, to dysk by default jest sformatowany dla case insensitive).
To case insensitive jest jak rozumiem pozostałością po wcześniejszych OSach, najpewniej tym z Apple DOS z Apple II, który miał chyba tylko uppercase i tak zostało, więc system plików dla Classic MacOS stał się case insensitive i jak Classic MacOS stał się OS X (czyli de facto NextOS z Rosettą) to i system dla OS X dostał case insensitivity... OK, jest też wersja bardziej oficjalna, że Classic MacOS dostał ten ficzer specjalnie bo to było przyjazne dla userów... moje 20+ pracy z kodem sugerują, że to mogło być ubieranie w szatki ficzera czegoś co mogło wynikać np. z przeportowania kodu ;-) [ale fakt, to mogła być przemyślana decyzja, nie mająca nic wspólnego z tym, że kod dla Lisy był pisany na Apple II w Pascalu, czyli w języku który jest case insensitive i na OSie który wspierał tylko wielkie litery, a kod dla Classic MacOS był pisany na Lisa'ie....]
Zwykle to case insensitive nie robi problemów... chyba, że chcesz zrobić rename pliku z README.md do readme.md (czy jak w moim przypadku gdy przez pomyłkę miałem add.java i chciałem zmienić w Add.java).
Rename głupieje, bo każą mu zmienić nazwę pliku w już istniejącą nazwę....

Chociaż wydaje się, że twórcy narzędzi programistycznych, a może i samego OS mogliby wprowadzić zmiany w działaniu rename ;-) coś jak dodanie if'a który sprawdza, że hmm... nazwa pliku już istnieje i uchwyt do pliku to jest ten sam plik, ale jednak znak po znaku obie nazwy się różnią, więc trzeba pójść specjalną ścieżką... choć jak znam życie to ten przypadek też ma jakieś problemy, nie wiem, np. nie jest jednoznaczny przy konkurencyjnym dostępie do plików ;-) albo fanatyczny product owner powiedział "nie będziemy robić tej zmiany" ;-)



Podobne postybeta
Samsung nie umie w software ;-)
OOo2GD w różnych smakach ;-)
Lepsze formatowanie kodu w Obsidian&amp;#39;ie - trick
Trudne USB...
Docker image to taka kanapka ;-)

środa, lipca 16, 2025

Agent by Agent ;-) czyli o tworzeniu agenta AI agentem AI ;-)

Spróbowałem vibe coding z Gemini CLI.

Żeby było zabawnie poprosiłem je o napisanie mi czegoś podobnego do Gemini CLI, ale działającego z lokalnym modelem.

Myślałem o Codellama i Deepseek-r1, okazało się, że mniejsze modele z Codellama i Deepseek-r1 nie dawały w ogóle rady, ale już Codellama 34b, coś tam daje radę...

Zacząłem od prompta, który wytłumaczył, że chcę zbudować coś podobnego do Gemini CLI, ale z lokalnym modelem i że żeby dostać się do tego modelu to może używać kodu modułu mistral.py, który mu dałem. Wytłumaczyłem też, że powinien dodać do kodu fragmenty pozwalające na wykonywanie funkcji takich jak zapisanie pliku, przeczytanie pliku i uruchomienie komendy. Jako testu kazałem Gemini CLI instruować tworzonego agenta, że ma stworzyć plik z kodem w Pythonie, który stworzy program do liczenia Pi metodą Monte Carlo.

Zacząć działać, a ponieważ budowa agenta nie jest czymś co zostało oprogramowane już wiele razy to model, którego używa nie ma raczej duże wiedzy o tym...

Wygenerował kod i tu był pierwszy problem ;-) bo postanowił uruchomić agenta, ale jak go uruchomił to nie miał możliwości interakcji z nim ;-) bo przecież Gemini CLI to NPM i 1 wątek...

Przerwałem mu, wytłumaczyłem problem i zaproponowałem zrobienie wersji z serwerem HTTP, która będzie słuchać i odpowiadać, a on będzie mógł gadać do niej cUrlem...

Spodobało mu się, przepisał kod.

Uruchomił, wysłał cUrla i okazało się, że mu nie działa...

Zaczął debugować i z uporem maniaka prosił mnie o to żebym ręcznie włączył lub wyłączył serwer... wytłumaczyłem, że przecież sam to może robić... stwierdził, że fakt, i zaczął uruchamiać serwer w background (z &), a później killować go z pkill...

Problem był taki, że model zwracał kod, ale tworzony agent nie chciał go zapisać na dysk... czyli najpierw Gemini CLI kilka razy sam chciał ten kod zapisać do pliku, wyjaśniłem w końcu mu grzecznie, że oszukuje, że to ma zrobić agent.

Więc zaczął go poprawiać, uruchamiać, zabijać... uznał, że musi zrobić debug... tu miał masę problemów, jednym z nich było to, że znów żeby móc widzieć output serwera HTTP to musiał go uruchomić... ale w foreground, więc nie mógł wysłać cUrlem nic, bo wisiał ;-)

W końcu mu przerwałem i zaproponowałem, że może lepiej będzie użyć przekierowania do pliku, ba że może przecież przekierować do pliku output z serwera i z cUrla i porównać...

Sam też sprawdziłem kod i okazało się, że w prompt'cie tłumaczył modelowi, że ma komendy owijać w <<CMD>> </CMD>>, ale w kodzie szukał końcowego znacznika <</CMD>> więc go tam nie było...

W międzyczasie sam wymyślił, że lepiej od przekierowywania strumienia użyć biblioteki logging ;-)

Wypalił w trakcie całą pulę tokenów/requestów do lepszego modelu, przełączył się na gemini-2.5-flash i spalił 63% contextu.

W końcu zrobił jakąś pierwszą wersję agenta, która umie pisać na dysku i która stworzyła plik z kodem liczącym Pi metodą Monte Carlo, ale już uruchomić tego nie umiał... a w końcu chciał nadpisać plik "example content" ;-)

Często blokował się na czymś trywialnym i trzeba mu to było tłumaczyć jak "bardzo tępemu juniorowi", chociaż bardziej komuś kto niby kojarzy, że kod można pisać, ale jest pozbawiony jakichkolwiek zdolności do rozwiązywania problemów ;-)

Z tym, że parę lat temu nawet w to miejsce by nie można było dotrzeć ;-)



Podobne postybeta
Agent nie na wszystko pomoże ;-)
Dowcipny laptop ;-)
[SOLVED :-)] Google kłamie ;-) co do tego jak często odświeża subskrypcję kalendarzy
Zauroczył mnie Cladue Code ;-)
Bob wykazuje inicjatywę ;-) i jakże piękny mógłby być Googlebook....

środa, lutego 26, 2025

Życie artysty jest trudne - System Extensions na macOS z ARMem...

Artyście wszystko przeszkadza ;-)

Próbuję sobie zbudować dla macOS system plików z kompresją. Bo w pracy mam dużo JSONów.

I na moim "starym" Macbooku Pro z 2018 już śmiga połączenie Pythona z FUSE, nadal w wersji podstawowej, ale działa.
Żeby działalo na nowym Macu Mini z M4 Pro muszę włączyć System Extensions, a żeby je włączyć muszę wejść w jakiś specjalny tryb i mi się odechciewa... bo podejrzewam, że na firmowym laptopie nawet nie będę mógł ;-)
A czemu muszę włączyć System Extensions? Bo FUSE tak działa ;-)

A już miałem taki piękny plan, trzymanie struktury FSa w SQLite, 2 tabele jedna ze ścieżką do fstats, druga z path do offset i UID (który będzie od razu nazwą pliku) skompresowanego GZIPem czy czymś podobnym, jeszcze w tej tabeli info jakim algo kompresowane (bo może nie warto kompresować?).

Ale jak byłem na Gran Canarii to mi się nie chciało kodować, bo można było łazić, a teraz jak jestem w domu to mnie blokuje obawa przed włączaniem tych System Extensions ;-)

Życie artysty jest trudne ;p



Podobne postybeta
MJ
Prakacje z kodowaniem ;-)
"Semisubiektywna" klasyfikacja dokumentów ;-)
macOS 26 ma swoje problemy
I znów widziałem The Internship :-)

niedziela, lutego 02, 2025

Prakacje z kodowaniem ;-)

Jestem na prakacjach, więc wiadomo, że mój prywatny projekt się posuwa do przodu ;-)

Prakacje mają dużo słońca i takich tam:



Ale wieczoram siedzę i koduję i okazuje się, że mój pomysł na system plików dla macOS, który używa kompresji zaczął działać :-)

Jest straszliwie wolny, prędkość zapisu to na razie tak między 30 KB/s, a 1 MB/s ;-) ale to zrozumiałe, pierwsza wersja systemu zakłada po prostu, że każdy plik jest kompresowany w całości... co oznacza, że funkcje read i write, które czytają/zapisują tylko fragmenty pliku muszą ten plik za każdym razem rozpakować w całości, i w razie zapisu jeszcze raz go całego spakować.

Obecna wersja używa zwykłego systemu plików, zapisuje pliki pod zadanymi ścieżkami, jedyna różnica jest taka, że są one zZIPowane. 
Kolejna wersja powinna być miksem systemu plików i bazy w SQLite. Baza będzie służyła do trzymania struktury plików/katalogów i mapowania tychże do plików z danymi. Pliki będą dzielone na chunki po 8, czy 64 KB (jeszcze muszę zdecydować) i każdy plik będzie zapisywany w bazie tak, że będę tam miał offset i nazwę pliku z danymi... więc jak ktoś będzie chciał odczytać tylko kawałek pliku czy zapisać tylko kawałek pliku to nie będę musiał wszystkiego rozpakowywać, po prostu zrobię to na danym kawałku. A idea jest taka, że pliki danych będą odpowiadały chunkom, więc same powinny być mniejsze... do tego będę miał opcję dodania też obsługi sytuacji gdy po spakowaniu mam większy plik niż przed spakowaniem, wtedy dla każdego chunka mogę trzymać też "algo" użyte do kompresji i w razie czego trzymać raw data.

Naumiałem się, że FUSE ważne jest nie tylko open, ale i release ;-) bo bez tego szybko pojawia się błąd too many open files ;-)


Podobne postybeta
The Shepherd&amp;amp;#39;s Crown
Kod jako insight w umysł ;-)
Głupi macOS, głupi WebDAV, głupi ChatGPT ;-)
Życie artysty jest trudne - System Extensions na macOS z ARMem...
Robimy z telefonu/tabletu z Androidem serwer WebDAV ;-)

sobota, grudnia 07, 2024

Google mi wyłączyło kawałek jednego z API ;-)

Google mi zrobiło psikusa i wyłączyli API 2 dla Bloggera, tzn. przynajmniej jego część.

Przez to przestały działać 2 rzeczy,  mój programik który dodaje podobne posty do mojego bloga i Bloggeroid.

Na razie udało mi się lokalnie naprawić moje narzędzie do podobnych postów (nie mam pojęcia czy ktoś tego używa, ale to była fajna zabawa to pisać więc... ;-)), może zabiorę się też za Bloggeroida, chociaż tu będzie trudniej bo nawet jak zrobię nową wersję to nie wiem czy pamiętam hasło do klucza potrzebnego do podpisania, a do tego Google i tak mi skasowało chyba już konto developera ;-)

Ale też z przerażeniem odkrywam, że łatwiej mi wiele rzeczy pisać w Pythonie bo go częściej używam niż Java'y...

Ale wracając do API od Google, wygląda na to, że chociaż wszystko jest deprecated to jedyna rzecz, którą musiałem naprawdę przepisać to kod do pobierania ID bloga na podstawie jego adresu, reszta wydaje się działać, chociaż mam zamiar ją przepisać.
Zresztą mam dowód na to, że nie warto używać cudzego SDK... mój kod używa Data SDK od Google, ale żeby robić update do najnowszej wersji to musiałbym się z tym ścigać parę dni, a napisanie moich własnych implementacji kodu gadającego z Bloggerem zajmie mniej ;-)



Podobne postybeta
GUI, Swing i inne takie...
Dowcipny laptop ;-)
Papa dla ClientLogin
&quot;Medyczny&quot; dowcip dnia ;-)
Papa laptopku..... ;-)

sobota, września 21, 2024

Aplikacja w Java&#39;ie jako coś w czym można przeglądać pliki w macOS

Człowiek by myślał, że w zbudowaniu czegoś do wyświetlania plików największym problemem jest zrobienie samego wyświetlania, bo przecież coś takiego jak przekazanie pliku do narzędzia to pikuś.

Argumenty przekazywane do aplikacji to coś co wspiera chyba każdy język, więc naturalne byłoby użycie tego mechanizmu....

Ale nie, w macOS to zrobili inaczej ;-)

I jak chcesz popełnić apkę, która potrafi wyświetlić jakieś pliki (w naszym przykładzie txt) to trzeba zrobić parę rzeczy....

W kodzie trzeba powiedzieć, że chcielibyśmy przechwytywać otwieranie plików

Desktop.getDesktop().setOpenFileHandler(e -> {
System.out.println("File opened: "+e.getFiles().get(0).getAbsolutePath());
var file = e.getFiles().get(0);
var path = file.getAbsolutePath();

ale to za mało... bo jeszcze musimy powiedzieć o tym wyraźnie macOSowi plikiem Info.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>txt</string>
</array>
<key>CFBundleTypeName</key>
<string>Text File</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
</dict>
</plist>
Kluczem jest podanie poprawnego typu plików.... niby txt jest dość oczywiste, ale wcześniej próbowałem to co sugerował Internet w osobie ChatGPT ;-) czyli public.plain-text...
Ale oczywiście plik Info.plist jakoś trzeba połączyć z naszym JARem....
I do tego służy jpackage:
jpackage --input <folderWithJAR> --name <NameOfApp> \
 --main-jar <nameOfJARfileInInputCatalog> \
--resource-dir <pathToFolderWiht_Info.plist>
To nam wyprodukuje plik dmg, którego możemy używać do instalowania aplikacji ;-) Na moim komputerze plik waży "niecałe" 56 MB ;-) a po instalacji apka waży jakieś 130-150 MB ;-)


Podobne postybeta
Z NMEA do KML (chyba najbardziej kryptyczny tytuł postu jak stworzyłem ;-))
Robimy widget do Windows 7 :-)
Niecne wykorzystanie refleksji... czyli jak poszukać tekstu w drzewie obiektów? ;-)
Jak walczyć z gigantycznym kodem w Java'ie, część 1 ;-)
Prosty spell checker, czyli jak umiejętnie połączyć teorie z praktyką

wtorek, września 17, 2024

Mały rant na macOS, Swifta i sandbox ;-)

Mam dużo pliczków tekstowych, różnych, a to logów, a to jakichś kawałków kodu i inne.

Na Windows dawno temu w TotalCommander robiłem F3 i było widać.

Jakoś nigdy nic fajnego nie znalazłem na macOS ;-) jest niby TextEdit, ale jest głupi jak but z lewej nogi. Np. trudno mi się czyta małe literki, to zwiększę... ale wtedy nie ma łamania linii, to zrobię wrap to page to mi zwinie na część ekranu, ale jak tam spróbuję zwiększyć czcionkę to zwiększa czcionkę w tym nieszczęsnym kawałku w który zwinął stronę...

To stwierdziłem, fajnie by było się nauczyć Swifta, to napiszę sobie prosty programik, przecież to nie może być trudne....

Ale kto by wpadł na to, że na macOS apka by default nie ma dostępu do plików? ;-) bo może działać tylko w sandboksie... tak jak na iOS...

No to próbuje w signing i podobnych dać jej dostęp do plików i podobno go ma, ale nadal nie umie czytać plików. To próbuję z startAccessingSecurityScopedResource, które w ogóle mnie nie pyta o nic, ot po prostu apka dostaje false i tyle.

Naprawdę się zastanawiam czy nie przejść do Java'y i Swinga, albo do JavaScript i Electrona ;-)

To tak w nawiązaniu do mojego postu sprzed paru tygodni, że dziś ktoś kto się uczy programować ma trudniej ;-)



Podobne postybeta
Teraz jest trudniej zacząć z programowaniem niż te 25-30 lat temu
Piesakrew!
Outlook zły
Java, JavaScript, Python, C++, C#? co ma przyszłość? 7 lat później ;-)
Pensje w IT są trudne

środa, sierpnia 28, 2024

Rube Goldberg machine do reminderów ;-)

Znów zbudowałem potwora ;-)

Jest tak, jestem bardzo chaotyczny, zwykle mi to nie przeszkadza bo mam na tyle dobrą pamięć i ogólny ogar, że w życiu moja chaotyczność mnie wiele nie kosztuje.

Ale bycie managerem ma swoje koszty. Naturalne jest, że zamiast pracować nad 1, 2 czy 3 rzeczami naraz, ale ze schodzeniem do detali bardziej się jest na poziomie, że w koło dzieje się kilkanaście czy kilkadziesiąt rzeczy i trzeba mniej więcej się w nich orientować...
Stąd potrzebuję czegoś do pamiętania o rzeczach do zrobienia, żeby nie przypominać sobie 2 dni później ;-)

Niby mam Todoista... mam nawet Worfklow w Alfredzie, który pozwala mi tworzyć taski w Todoist....

Ale Todoist nie ma powiadomień w normalnym planie i ogólnie jest dość ciężki. Ale Reminders w świecie Apple są całkiem fajne i się ładnie synchronizują między komputerami... no i ładnie się pokazują w macOS w obszarze powiadomień ;-)

I teraz mam kolejne Rube Goldberg machine ;-)

W Alfredzie piszę todo i dalej treść remindera.... mogę mu dodać czas, bo mam tego obsługę ;-)

Teraz jest sobie inny komputer, na którym w crontab jest kawałek Pythona który co 5 minut łączy się do API Todoista i pobiera taski.... jeśli task ma due date... to skrypt "pisze" skrypt dla osascript w którym tworzy reminder z daną treścią i danym due date... uruchamia ten skrypt... i zaznacza w Todoist taska jako Completed ;-)

Najistotniejszy kawałek kodu, ten który tworzy reminder wygląda tak:

def createReminder(text, dueDate):
current_time = dueDate.strftime("%d/%m/%Y %H:%M:%S")
text=unicodedata.normalize('NFC',text)

script = f'''
tell application "Reminders"
make new reminder with properties {{name:"{text}", due date:date "{current_time}"}}
end tell
'''
try:
print("==")
subprocess.run(["osascript", "-e", script])
print("Reminder created!")
except Exception as e:
print(f"Failed to create reminder: {e}")

jak widać, tworzy reminder używając AppleScript ;-)

Czy dałoby się to zrobić prościej?

Oczywiście ;-)

Ale taka budowa ma tą zaletę, że jest bardzo prosta :-) bo sklejam kawałki, które już mam napisane do innych rzeczy ;-)

Teraz po głowie mi chodzi coś co w momencie jak mi się zaczyna spotkanie w Kalendarzu z linkiem do spotkania na WebEx czy innym Zoomie to otwierałoby mi Apple'owe Notes na notatce, która by miała nazwę jak spotkanie z dopisaniem na górze pliku daty i czasu tego spotkania.... 
Taki sposób na zapamiętanie by robić sobie notatki ze spotkania....



Podobne postybeta
GPT + Python = sprawdzanie czy 5 książka z Bobiverse już jest dostępna ;-)
Kiedy skończyć pracę...
5 lat z macOS w domu ;-)
Żenienie Todoist z Obsidian przy pomocy Pythona ;-)
Rube Goldberg machine do panowania nad spotkaniami... częściowego chociaż ;-)

czwartek, czerwca 13, 2024

To działa!!! Wykryło mi dostępność Samertajm :-)

Od jakiegoś czasu (tak naprawdę od lutego ;-)) czekam na dostępną do pobrania wersję Samertajm, ostatniego (w sensie najnowszego) programu Abelarda Gizy.

Zapisałem się na listę z updatami, ale że nie ufam to zrobiłem sobie też swoje skrypty ;-)

Działają w domu i przed chwilą zobaczyłem coś takiego na moim iPhone'ie (OK, naprawdę to na iPadzie jak coś próbowałem rysować (spróbuj narysować propozycję możliwej ewolucji JSON schema'y ;-)):


Wlazłem na https://samertajm.pl/ i jest :-) tzn. będzie 19 czerwca :-)

I proszę, mój własny alert był szybszy niż czekanie na jakieś listy dystrybucyjne ;-)

Tak samo monitoruję kolejne książki S. H. Jucha i czekam tak samo (+ jeszcze używanie LLMa do czytania strony ;-)) na 5 książkę z Bobiverse :-)



Podobne postybeta
PayPal - rozczarowuje
Eventy na żywo nie zawsze są bardziej wow niż w TV ;-)
Polowanie na Nexus 4 - wersja automagiczna ;-)
The Shepherd's Crown
Wernyhora mode - wieszcze, że następną usługą w Google Play będą Filmy ;-) i że będzie to w ciągu 12 miesięcy ;-)

poniedziałek, stycznia 22, 2024

Jak z metody size() w List w Java'ie dostać ujemną liczbę? ;-)

Chciałem dziś popsuć LinkedList, ale wychodzi na to, że mam za mało pamięci w komputerze ;-)

Popsucie miało wyglądać mniej więcej tak:

var list = new LinkedList<Integer>();
doMagic(list);
System.out.println(list.size()); // return negative number!!!

Gdzie doMagic miało wyglądać jakoś tak:

static void doMagic(LinkedList<Integer> l) {
for (var i=0L; i<Integer.MAX_VALUE+1000L; i++) {
l.add(Integer.valueOf(0));
}
}

No bo jeśli spojrzymy na implementację LinkedList to implementuje ona List, a List ma metodę size() która zwraca int, ale LinkedList nie ma ograniczenia na rozmiar innego niż pamięć, w samej metodzie linkLast(E e), która wołana jest z add(E e) mamy:

void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}

Co znaczy tyle, że jeśli mamy odpowiednio dużo pamięci to w pewnym momencie przekręcimy licznik ;-)

Czyli jakbyśmy zawołali size() to zwróci liczbę ujemną, ale jeśli policzymy wszystkie elementy kodem:

var count = 0L;
for (var n:list) {
count++;
}

To w count będziemy mieli 2147484647 ;-)
Co ciekawe zawołanie get(int) w takim przypadku wyrzuciłoby IndexOutOfBoundsException pokazując ujemną liczbę ;-)

Chyba znalazłem uzasadnienie dla kupienia Macbooka z M3 Max ;-) Bo chciałbym naprawdę sprawdzić czy to się tak zachowa ;-)

Inna sprawa ile byśmy potrzebowali RAMu żeby móc ten problem osiągnąć ;-) Tak jeden Node to powiedzmy między 48 a 64 bajty.... co daje dolną granicę najmniej 96 GB RAM ;-) ale podejrzewam, że tak naprawdę to by było jakieś 170 GB ;-)
Jeszcze chwilę potrwa nim będziemy mieli takie laptopy ;-) 
Największy Macbook Pro to teraz 128 GB RAM ;-)


[update]

I chyba nie muszę kupować takiej maszyny dużej ;-) bo nadal mogę ustawić rozmiar heapy na 150 GB ;-)

I wtedy gdy robię list.get(10) po dodaniu 2147484647 objektów to dostaję wyjątek:


A program w Activity Managerze ma 105 GB RAM przypisane ;-)

[next update ;-)]

Czyli wypisanie list.size() dało wynik -2147482649, ale już fragment:

var count = 0L;
for (var n:list) {
count++;
}

Nie zadziałał w ogóle, bo już w linii z for poleciał wyjątek ;-), okazuje się, że gdy jest produkowany iterator to wołany jest gdzieś w środku range check i ten wywala wyjątek dla Index: 0 ;-) 
Czyli LinkedList po przekręceniu licznika nie nadaje się nawet do iterowania ;-) ale co ciekawe można kasować elementy z przodu i tyłu aż nam wróci size do pozytywnych int'ów i można iterować ;-)



Podobne postybeta
Ile z obligacji... odsłona 2 ;-)
LinkedList w Java'ie to taki miś koala...
Po Devoxx&#39;ie
Ile w tym i przyszłym miesiącu z odsetek za obligacje?
Go dla Java'owca ;-) odcinek 2 "kontenery dwa ;-)"