O projekcie
Osobny panel wewnątrz aplikacji desktopowej, który zbiera świeże informacje z dziesięciu źródeł, sprowadza je do jednego miejsca i składa w PROFILE FIRM — uporządkowane, przeredagowane i podpisane tym, skąd każda pojedyncza informacja pochodzi.
Zacznę od zdania, które przewraca zwykłe wyobrażenie o takim narzędziu: TO NIE JEST PROJEKT O POBIERANIU, TO PROJEKT O TOŻSAMOŚCI. Pobranie dziesięciu stron jest najłatwiejszą częścią i zajmuje dzień. Cała trudność zaczyna się sekundę później, bo te same dziesięć źródeł nazywa jedną firmę dziesięcioma sposobami: raz z formą prawną, raz bez, raz wielkimi literami, raz ze skrótem, raz z literówką z cudzego formularza. Scalanie po nazwie daje śmieci — i to śmieci wyglądające na porządek.
Dlatego scalanie idzie WYŁĄCZNIE po identyfikatorach rejestrowych: numerze NIP, numerze REGON i numerze KRS. Dopiero gdy rekord da się przypiąć do identyfikatora, wchodzi do profilu; bez identyfikatora ląduje w poczekalni do rozstrzygnięcia przez człowieka, a nie w profilu „najbardziej podobnej” firmy. Powód jest asymetryczny i warto go nazwać: DWA DUPLIKATY SĄ KŁOPOTEM, ALE BŁĘDNIE SCALONY PROFIL JEST FAŁSZEM. Pierwszy da się połączyć, drugi wymyśla firmę, która nie istnieje, i nikt się nie dowie, dopóki ktoś nie zadzwoni pod zły numer.
Numer NIP ma sumę kontrolną, a REGON występuje w wersji dziewięcio- i czternastocyfrowej. Sprawdzenie sumy kontrolnej przed zapisem kosztuje mikrosekundę i zatrzymuje literówkę, zanim zdąży urosnąć w osobny, widmowy rekord. To jedna z tych kontroli, których prawie nikt nie robi, a która zdejmuje całą klasę problemów: identyfikator, który nie przechodzi sumy kontrolnej, NIE JEST identyfikatorem, choć wygląda jak on.
Drugą rzeczą, która odróżnia profil od zlepka, jest POCHODZENIE ZAPISANE PRZY KAŻDYM POLU, a nie przy rekordzie. Pierwsze pytanie, jakie zadaje każdy użytkownik takiego panelu, brzmi: skąd wiadomo, że ta firma ma czterdziestu pracowników. Odpowiedź „z agregatu dziesięciu źródeł” jest bezwartościowa. Dlatego każde pole niesie źródło, moment pobrania i SUROWĄ WARTOŚĆ w brzmieniu, w jakim ją podało źródło — żeby dało się sprawdzić nie tylko wynik, ale i drogę do niego.
Z tego wynika trzecia rzecz: SPRZECZNOŚĆ ŹRÓDEŁ JEST NORMĄ, NIE AWARIĄ. Jedno źródło mówi czterdziestu pracowników, drugie sto dwudziestu, bo liczą w innym momencie i w innej definicji. System, który po cichu wybiera jedną liczbę i pokazuje ją jako fakt, kłamie przez zamilczenie. Tutaj obowiązuje zadeklarowana kolejność pierwszeństwa — osobna dla każdego rodzaju pola, bo do adresu najlepszy jest rejestr, a do oferty strona własna firmy — a rozjazd przekraczający próg jest OZNACZONY I WIDOCZNY, zamiast wygładzony.
Słowo „najnowsze” też wymaga definicji, bo w tej dziedzinie jest zdradliwe. Data na stronie bywa nieobecna, bywa nieprawdziwa, a najczęściej jest datą ostatniej zmiany szablonu, nie treści. Świeżość liczy się więc z tego, co obserwowalne: ze ZMIANY WYDOBYTYCH PÓL, a nie z deklarowanej daty ani z odcisku surowego HTML-a. To rozróżnienie jest praktyczne — przemalowanie strony przez źródło zmienia cały HTML i zgłosiłoby „wszystko nowe”, zalewając panel fałszywymi zmianami.
Dziesięć źródeł znaczy DZIESIĘĆ RZECZY, KTÓRE PSUJĄ SIĘ OSOBNO, i to jest najtrudniejsza część utrzymania. Każdy adapter ma dlatego kanarka: znany rekord o znanej z góry wartości, sprawdzany przy KAŻDYM przebiegu. Kiedy kanarek milczy, adapter jest zepsuty — i wtedy właściwym zachowaniem jest WSTRZYMANIE zasilania z tego źródła, a nie zapisanie pustych pól. To jest najczęstsza droga, którą takie systemy niszczą własną bazę: zepsuty adapter zwraca zero rekordów, logika nadpisuje dobre dane pustką, a przebieg kończy się sukcesem.
Redakcja, o którą chodzi w tym projekcie, ma ostrą granicę: WOLNO PRZEREDAGOWAĆ, NIE WOLNO DOPISAĆ. Surowy tekst ze strony nie jest profilem — bywa marketingową sieczką, bywa powtórzeniem tego samego w czterech miejscach. Warstwie redakcyjnej wolno więc skrócić, uporządkować, usunąć powtórzenia i ułożyć w stałe sekcje. Nie wolno jej dołożyć ANI JEDNEGO faktu, którego nie ma w źródle, a przy każdym zredagowanym akapicie zostaje odnośnik do surowego tekstu, z którego powstał.
Granica prawna jest tu częścią projektu, nie załącznikiem. Zbieranie publicznych danych o firmach jest dozwolone, ale nie w dowolny sposób: reguły w pliku robots i regulaminy źródeł są respektowane, zabezpieczeń dostępu się nie obchodzi, a tempo zapytań jest uprzejme wobec cudzego serwera. Rzecz, którą najczęściej się przegapia: DANE JEDNOOSOBOWEJ DZIAŁALNOŚCI TO DANE OSOBOWE. Pola, które ich dotyczą, są oznaczone jako takie, mają zapisaną podstawę i mają drogę do sprostowania oraz usunięcia — a nie „usuniemy, jeśli ktoś napisze”.
Panel jest LOKALNY, bo mieszka w aplikacji desktopowej i musi działać, gdy sieci nie ma. Pracuje na ostatniej synchronizacji, synchronizuje się przyrostowo i wznawia po przerwaniu, a nie zaczyna od zera. Najważniejsza reguła jest jednak inna i dotyczy ludzkiej pracy: DECYZJE REDAKCYJNE PRZEŻYWAJĄ SYNCHRONIZACJĘ. Gdyby przychodzące dane nadpisywały to, co redaktor poprawił, każde odświeżenie kasowałoby jego robotę — a narzędzie, które zjada własną pracę użytkownika, przestaje być używane w tydzień.
Na koniec rzecz, która wygląda na drobiazg, a rozstrzyga o zaufaniu: ŚWIEŻOŚĆ JEST CECHĄ POLA, NIE PROFILU. Adres może mieć dwa lata, a przychód dwa dni. Jedna data „zaktualizowano” na całym profilu jest przez to myląca w najgorszym możliwym momencie — kiedy ktoś na tej podstawie dzwoni. Każde pole ma więc własny wiek, a pole po przekroczeniu sufitu nieświeżości jest POKAZANE JAKO NIEAKTUALNE, zamiast udawać bieżące.
Problem
Informacje o firmach leżą w dziesięciu miejscach, w dziesięciu formatach i z dziesięcioma wariantami tej samej nazwy. Ręczne zbieranie ich do jednego profilu zajmuje godziny na firmę, a scalanie po nazwie produkuje albo duplikaty, albo — gorzej — profile scalone błędnie, których nikt nie wychwyci, bo wyglądają porządnie.
Rozwiązanie
Scalanie wyłącznie po identyfikatorach rejestrowych ze sprawdzoną sumą kontrolną, pochodzenie zapisane przy każdym polu, zadeklarowana kolejność pierwszeństwa i widoczny znacznik przy sprzeczności źródeł. Każdy adapter ma kanarka, a jego milczenie wstrzymuje zasilanie zamiast nadpisywać dobre dane pustką.
Funkcje
- scalanie po numerach NIP, REGON i KRS — nigdy po nazwie firmy
- suma kontrolna identyfikatora sprawdzana przed zapisem, nie po
- rekord bez identyfikatora idzie do poczekalni, nie do najbardziej podobnego profilu
- pochodzenie przy KAŻDYM polu: źródło, moment i surowa wartość ze źródła
- zadeklarowana kolejność pierwszeństwa, osobna dla każdego rodzaju pola
- rozjazd źródeł ponad próg oznaczony i widoczny, a nie wygładzony
- kanarek w każdym adapterze; jego milczenie wstrzymuje zasilanie ze źródła
- redakcji wolno skrócić i uporządkować, nie wolno dopisać faktu
- wiek liczony per pole; pole po suficie nieświeżości pokazane jako nieaktualne
Moduły
zrodla-adaptery
warstwa pobieraniaDziesięć adapterów, każdy z własnym kontraktem, tempem i kanarkiem.
- jeden adapter to jedno źródło i jeden kontrakt — brak wspólnego kodu do współdzielenia awarii
- kanarek: znany rekord o znanej wartości sprawdzany przy każdym przebiegu
- milczenie kanarka WSTRZYMUJE zasilanie; zero rekordów nie nadpisuje dobrych danych
- tempo zapytań i reguły z pliku robots respektowane osobno dla każdego źródła
- odpowiedź źródła zachowana w całości, żeby dało się odtworzyć, skąd wzięło się pole
tozsamosc-firm
rozpoznawanie podmiotuPrzypina rekord do firmy po identyfikatorze rejestrowym albo odmawia przypięcia.
- klucze: numer NIP, REGON i KRS; nazwa firmy NIGDY nie jest kluczem
- suma kontrolna numeru NIP i obie długości numeru REGON sprawdzane przed zapisem
- rekord bez identyfikatora trafia do poczekalni z podpowiedziami dla człowieka
- scalenie dwóch profilów jest operacją odwracalną i zapisaną w historii
- nazwy i skróty trzymane jako ALIASY przy profilu, do wyszukiwania, nie do scalania
pola-pochodzenie
magazyn z metrykąTrzyma każde pole razem z jego źródłem, momentem i surową wartością.
- pochodzenie przy polu, nie przy rekordzie — pytanie zawsze dotyczy jednej liczby
- surowa wartość w brzmieniu źródła obok wartości znormalizowanej
- historia pola jest dopisywana; poprzednia wartość nie znika przy aktualizacji
- wiek pola liczony osobno i wystawiony do interfejsu
- pole po przekroczeniu sufitu nieświeżości oznaczone, nie ukryte i nie odświeżone na siłę
rozstrzyganie
warstwa uzgadnianiaWybiera wartość widoczną w profilu i pokazuje, gdy źródła się nie zgadzają.
- kolejność pierwszeństwa ZADEKLAROWANA i osobna per rodzaj pola, nie ustalana doraźnie
- do adresu i formy prawnej pierwszeństwo ma rejestr, do oferty — strona własna firmy
- rozjazd ponad próg zapala znacznik przy polu razem z listą wartości i źródeł
- ręczna decyzja człowieka bije kolejność i jest zapisana jako decyzja, nie jako dane
- brak wartości we wszystkich źródłach zostaje brakiem; pole nie dostaje wartości domyślnej
swiezosc
wykrywanie zmianRozstrzyga, co jest naprawdę nowe, nie opierając się na dacie ze strony.
- porównanie na WYDOBYTYCH POLACH, nie na odcisku surowego HTML-a
- data podana przez źródło traktowana jako poszlaka, nie jako fakt
- przemalowanie strony przez źródło nie zgłasza się jako zmiana wszystkich danych
- zmiana pola tworzy zdarzenie z wartością przed i po, gotowe do przeglądu
- próg istotności: literówka i zmiana wielkości liter nie budzą panelu
redakcja
warstwa redakcyjnaZamienia surowy tekst w profil czytelny dla człowieka, bez dokładania treści.
- wolno skrócić, uporządkować, usunąć powtórzenia i ułożyć w stałe sekcje
- NIE WOLNO dopisać faktu, którego nie ma w źródle — to jest granica, nie wytyczna
- każdy zredagowany akapit ma odnośnik do surowego tekstu, z którego powstał
- profil ma stałą strukturę sekcji, więc dwie firmy da się czytać tym samym ruchem
- wersje redakcji zapisane; da się wrócić do poprzedniego brzmienia
panel-lokalny
interfejs desktopowyPanel w aplikacji desktopowej: działa bez sieci i nie zjada pracy redaktora.
- pracuje na ostatniej synchronizacji; brak sieci nie blokuje przeglądania
- synchronizacja przyrostowa i wznawialna, ze stanem po każdej porcji
- DECYZJE REDAKCYJNE PRZEŻYWAJĄ SYNCHRONIZACJĘ — przychodzące dane ich nie nadpisują
- kolizja lokalnej poprawki z nową wartością ze źródła pokazana do rozstrzygnięcia
- baza lokalna w SQLite, więc panel startuje natychmiast i nie czeka na serwer
granice-prawne
warstwa zgodnościPilnuje, żeby zbieranie i trzymanie tych danych było zgodne z prawem.
- reguły z pliku robots i regulaminy źródeł respektowane; zabezpieczeń dostępu się nie obchodzi
- dane jednoosobowej działalności oznaczone jako DANE OSOBOWE, z zapisaną podstawą
- droga do sprostowania i usunięcia opisana i wykonalna, nie obiecana
- retencja osobna dla pól firmowych i dla pól osobowych
- eksport profilu niesie informację o źródłach, więc dalsze użycie nie gubi pochodzenia
Architektura
- Podstawa
- panel w aplikacji desktopowej (Electron, TypeScript) z lokalną bazą SQLite; pracuje na ostatniej synchronizacji, gdy sieci nie ma
- Źródła
- dziesięć adapterów z osobnym kontraktem i osobnym tempem; awaria jednego nie zatrzymuje pozostałych dziewięciu
- Kanarek
- znany rekord o znanej wartości sprawdzany przy każdym przebiegu; milczenie wstrzymuje zasilanie ze źródła
- Zasada nadpisywania
- zero rekordów NIGDY nie nadpisuje danych istniejących — to najczęstsza droga, którą takie systemy niszczą własną bazę
- Tożsamość
- scalanie po numerach NIP, REGON i KRS ze sprawdzoną sumą kontrolną; nazwa firmy służy do szukania, nie do scalania
- Rekord bez identyfikatora
- poczekalnia z podpowiedziami dla człowieka; nigdy automatyczne przypięcie do najbardziej podobnego profilu
- Pochodzenie
- przy KAŻDYM polu: źródło, moment pobrania, surowa wartość w brzmieniu źródła i wartość znormalizowana
- Sprzeczności
- zadeklarowana kolejność pierwszeństwa per rodzaj pola; rozjazd ponad próg oznaczony przy polu wraz z listą źródeł
- Świeżość
- porównanie na wydobytych polach, nie na odcisku HTML-a; wiek liczony per pole, z sufitem nieświeżości
- Granica redakcji
- skracanie i porządkowanie wolno, dopisywanie faktów nie; każdy akapit ma odnośnik do surowego tekstu
- Praca człowieka
- decyzje redakcyjne i ręczne rozstrzygnięcia przeżywają synchronizację i są zapisane jako decyzje, nie jako dane
- Dane osobowe
- pola jednoosobowej działalności oznaczone, z podstawą i osobną retencją; droga do sprostowania i usunięcia wykonalna
- Uprzejmość wobec źródeł
- reguły z pliku robots i regulaminy respektowane, tempo zapytań ograniczone, zabezpieczenia dostępu nietykane
Technologie
- TypeScript
- Electron
- SQLite
- Node.js
- Playwright
- REST API
Autor
Mariusz PerzyńskiClient Relations & Operations