Model językowy pisze poprawny kod. To nie jest ironia — naprawdę pisze. Problem polega na tym, że „poprawny” i „działający na produkcji przez rok” to dwie różne rzeczy, a różnica między nimi nie mieści się w składni.

Poniżej dziesięć przypadków z naszych własnych projektów. Każdy wystąpił naprawdę, każdy wygląda w kodzie sensownie i każdy przeszedłby przegląd osoby, która nie widziała jeszcze, jak to samo się psuje.

1. Cache, który zjadł dysk

Trasa catch-all pod filtry katalogu cache’owała każdą permutację adresu podaną przez crawlera. Cache urósł do 38 GB w 1 395 316 plikach, dysk serwera doszedł do stu procent i położył pięć usług: API jednej aplikacji na sześć godzin, silnik automatyzacji, statystyki, bazę PostgreSQL i trzy jednostki systemd.

Kod był poprawny. Problem powstaje po tygodniach, przy ruchu bota, w warstwie zapisywalnej kontenera — nie widać go ani w przeglądzie kodu, ani w testach. Naprawa poszła celowanym find -delete z wnętrza kontenera, bez przestoju: rm -rf całego katalogu zabiłby trasę, bo obok cache leżą artefakty builda.

2. Nazwa pliku, która była szablonem

W tym samym cache leżał plik o dosłownej nazwie ${encodeURIComponent(e.slug)}.html. Niezinterpolowany szablon łańcucha, który stał się realnym adresem URL.

Składnia poprawna, TypeScript nie protestuje, strona się buduje. Zobaczyliśmy to dopiero przy przeglądaniu zawartości cache po awarii z punktu pierwszego.

3. Nagranie, którego model nigdy nie usłyszał

Nagranie .m4a z telefonu szło prosto do modelu transkrypcji. Model czyta pliki przez libsndfile, które nie zna AAC — notatka powstawała bez treści nagrania.

Kod „działa”: nie ma wyjątku, jest pusty wynik. A model chętnie napisze tekst „na temat” pliku, którego nie przeczytał. Naprawa: ffmpeg w obrazie, przekodowanie na 16 kHz mono, cięcie na segmenty i jawny zakaz zgadywania treści w prompcie.

4. Dwa razy ta sama strona skanu

Załączniki dopasowywane po nazwie pliku. Telefon wysyła każde zdjęcie jako image.jpg, więc dwie strony skanu dawały dwie kopie pierwszej strony.

Wygląda rozsądnie: find(a => a.name === p.name). Błąd widać tylko na realnych danych z telefonu. Naprawa: dopasowanie po indeksie plus numeracja [i/N] w prompcie.

5. Sklep w Zatoce Gwinejskiej

Panel treści zapisuje brak współrzędnej jako 0, a lat: 0, lng: 0 to punkt na Atlantyku u wybrzeży Afryki. Pinezka sklepu lądowała w oceanie.

Walidacja typeof === "number" przechodzi, bo zero jest poprawną liczbą. Potrzebny jest świadomy filtr wartości zerowych w warstwie danych — nikt go nie napisze, jeśli nie zobaczył wcześniej pinezki w oceanie.

6. Oszczędność 57 kB, która kosztowała 11 punktów

Font monospace wyjęty z preload „na oszczędność 57 kB”: plakietka łamała się na dwie linie w foncie zastępczym i po podmianie skakała cała treść. CLS 0,22, minus 11 punktów Lighthouse.

Zysk był policzony, koszt nie. Bez pomiaru brzmi to jak oczywista optymalizacja. Zmiana wróciła na miejsce, a zysk wzięliśmy inną drogą — własnym subsetem fontów.

7. Tekst, który brzmi dobrze i kłamie

Model generujący treść przy 97,6 procent wierności przekręcał czytelne fragmenty źródła: „Canada” na „w Kanaanach”, odwrócona relacja między osobami, słowa pieśni przypisane Pismu.

Wynik brzmi dobrze i czyta się gładko. Błąd widać wyłącznie po porównaniu ze źródłem. Wyszedł z benchmarku: dwa niezależne przebiegi, liczone fakty krytyczne, ręczna weryfikacja w transkrypcie.

8. Poradnik opisujący starszą wersję

Silnik automatyzacji od wersji 2.0 nie udostępnia zmiennych środowiskowych w węźle z kodem. Każdy poradnik w sieci pokazuje starszą wersję, więc model odtwarza to, czego się nauczył.

Jedyne, co pomaga: test na żywej instancji przed zbudowaniem automatu.

9. Archiwizacja, która kasuje historię

Archiwizacja zadania przez przeniesienie do innego kubełka zeruje datę wykonania. API przyjmuje żądanie i zwraca 200, więc nic nie sygnalizuje problemu.

Historia „co zostało zrobione” znika po cichu. Archiwizujemy etykietą, nie kubełkiem.

10. Częściowy POST, który czyści pola

W tym samym API częściowy POST czyści pola, których nie ma w treści żądania. Wysyłasz {"priority": 4}, a tracisz opis.

REST bez schematu na to pozwala, a klient nie widzi problemu. Wykryte przy pierwszej integracji i opisane w kanonie narzędzia, żeby nie wróciło.

Co z tego wynika

Żaden z tych błędów nie jest błędem składni. To błędy sensu, danych i eksploatacji — specyficzne dla wersji biblioteki, dla hostingu i dla realnych danych. Model językowy nie ma o nich wiedzy, bo nie ma dostępu do waszej produkcji ani do waszego dysku.

Wyłapuje je programista, który wie, gdzie patrzeć, i który zostaje po wdrożeniu.

To jest dokładnie ta różnica, za którą warto zapłacić: nie za pisanie kodu, bo to akurat AI robi szybciej, tylko za wiedzę, co sprawdzić, zanim ten kod zacznie żyć własnym życiem.