Siirry sisältöön

Tapaustutkimus

Dredge: monilähteinen operaatioiden seurantakortti

Rooli: Yksin työskentelevä kehittäjä (tietoisesti valittu teknologiapino)Kesto: Rakennettu kesäkuussa 2026Tila: Suoraan osoitteessa dredgeapp.com
ElixirPhoenix 1.8LiveView 1.1OTPPostgreSQLEctoCloakAnthropic ClaudeReqDockerUpCloud

Laajuus

~7,300

Elixirin rivit (kirjasto)

64

Moduulit

4

Lähdeadapterit

14

Ecto muuttoliikkeet

3

Anthropic asiakastilat

~1,180

Testisarjat

Ongelma

Pienet kehittäjätiimit seuraavat maailmaansa kuuden välilehden kautta samanaikaisesti: GitHub-ilmoitukset, Dependabot-hälytykset, tietokanta, jota heidän on pidettävä silmällä, RSS-tietoturvailmoitukset sekä valtava määrä webhook-ilmoituksia osoitteista Sentry, Datadog ja PagerDuty. Mikään ei ole yhteydessä toisiinsa, mikään ei ole priorisoitu, ja ongelmien luokittelu tapahtuu kontekstin vaihtelun kautta, kunnes jokin asia jää huomaamatta.

Dredge koostaa kaiken yhdeksi reaaliaikaiseksi kanban-tauluksi. Tehtävät virtaavat sisään mistä tahansa liitetystä lähteestä, ohjataan sääntöjen mukaan sarakkeisiin ja näkyvät tärkeysjärjestyksessä – joten tiimi voi priorisoida tehtävät yhdessä paikassa sen sijaan, että sen pitäisi selata kuutta eri välilehteä. Se oli myös, avoimesti sanottuna, keino oppia Elixir, OTP ja Phoenix LiveView kunnolla sen sijaan, että olisi opiskeltu niitä oppaiden avulla.

Mitä rakennettiin

Lähteet (sovituskerros)

  • ·RSS / Atom – kyselypohjainen, syötteen URL-osoitteen automaattinen tunnistus; optimoitu CVE- ja toimittajien tietoturvasyötteille
  • ·GitHub - OAuth; ilmoitukset, osoitetut ongelmat, tallennukset sekä Dependabot-hälytykset, joissa CVSS on liitetty kortin prioriteettiin
  • ·Tietokanta – yleiskäyttöinen, SELECT-only-kyselyohjelma sivustoille Postgres ja MySQL, joka merkitsee epäilyttävät rivit ja tunnistaa automaattisesti otsikko- ja vakavuus-sarakkeet
  • ·Webhook – push-pohjainen, HMAC -vahvistettu POST /webhooks/:id; tukee seuraavia syötteitä: GitHub, Sentry, Datadog ja PagerDuty
  • ·Jokainen lähde toteuttaa yhden toimintatavan (fetch/3 + normalize/1), joten prosessiketjun ei tarvitse koskaan tietää, minkä tyyppisen lähteen kanssa se on tekemisissä

Hallitus ja luokitus

  • ·Sääntöihin perustuva reititys: kohdista kohteet sarakkeisiin kentän / operaattorin / arvon perusteella, valinnaisella prioriteettisäännöllä (CVSS >= 9 asettaa prioriteetin 10)
  • ·Luokittelija, joka valitsee ensimmäisen vastaavuuden ja käyttää varasuunnitelmana ”Inbox”-sääntöä; koko pelilaudan uudelleenluokittelu sääntöjen muuttuessa

Skiff (tekoälykerros)

  • ·Tekoälyprosessin määritykset – kuvaile selkokielellä, mitä haluat seurata; komento Skiff palauttaa vahvistetun lähdekoodin ja reitityssääntöjen määritykset, jotka voit tarkistaa ja hyväksyä ennen kuin mitään kirjoitetaan
  • ·Tekoälypöytäavustaja – agentti-pohjainen työkalunkäyttösilmukka, joka vastaa koko pelilaudalla esitettyihin kysymyksiin, osaa luoda kortteja ja voi tarvittaessa suorittaa vain lukuoikeudella varustetun SQL-kyselyn liitettyyn tietokantaan
  • ·Anthropicin kautta lähetetyt kolumnien yhteenvedot Viestit osoitteeseen API osoitteessa SSE

Yhteistyö ja reaaliaikaisuus

  • ·Jäsenten kutsuminen (katselija / muokkaaja), julkiset vain luku -jakolinkit, läsnäoloavatarit ja korttikohtaiset kommenttiketjut
  • ·Phoenix PubSub + Presence: taulu päivittyy heti, kun uusi kohde saapuu, ilman että asiakaspuolella tarvitaan kyselyjä

Tekninen arkkitehtuuri

RuntimeElixir / Phoenix 1.8 / LiveView 1.1 osoitteessa Bandit
PersistenceEcto + PostgreSQL, jossa käytetään avaimia UUID; cloak_ecto salattujen tunnistetietojen ja salaisen määritystiedoston osalta
ConcurrencyOTP valvontapuu: yksi UserSupervisor käyttäjää kohti, yksi väliaikainen SourceWorker GenServer lähdettä kohti, joihin pääsee osoitteen Registry kautta
IngestionLähteestä riippumaton sovittimen toiminta (fetch/3 + normalize/1); työntekijät lataavat tiedot tietokannasta käynnistyksen yhteydessä
AIKäsin koottu Anthropic-asiakasohjelma Req-palvelimelle – ei-suoratoisto, SSE-suoratoisto sekä monivaiheinen työkalunkäyttösilmukka; Claude putkistokokoonpanon ja yhteenvetojen hallintaan
Parsingfast_rss (Rust NIF) syötteille; Floki HTML:lle; Req kaikille HTTP-pyynnöille
DeploymentItsenäinen Elixir-julkaisu osoitteessa Docker sivustolla UpCloud, joka toimii Caddy-sivuston takana automaattista TLS-yhteyttä varten

Mitä opin

Tämä oli ensisijaisesti oppimista varten tehty projekti, joten tässä ovat ne asiat, joita tämä kehitysympäristö minulle opetti – useat niistä kantapään kautta, kuten git-historiasta näkyy.

1. OTP määrittelee vikasietoisuuden uudelleen rakenteelliseksi valinnaksi, ei virheiden käsittelyksi

Koska olen tottunut pyyntö-vastaus-tyyppisiin taustapalvelimiin, ensimmäinen ajatukseni tilanteeseen ”mitä jos syöte on poissa käytöstä” oli try/catch-käsittely ja uudelleenkäynnistysliput. OTP nosti ratkaisun uudelle tasolle: mallintaa jokainen lähde omaksi valvottavaksi prosessikseen. Dredge käyttää yhtä SourceWorker GenServer -prosessia lähdettä kohden käyttäjäkohtaisen DynamicSupervisor -palvelun alla, joka rekisteröidään osoitteessa {user_id, source_id}. Epävakaa RSS-syöte voi kaatua ja käynnistyä uudelleen itsestään koskematta käyttäjän GitHub-kyselyyn, ja työntekijä on :transient, joten puhdas pysäytys pysyy pysäytettynä, kun taas kaatuminen käynnistyy uudelleen. Käynnistyksen yhteydessä root-valvoja lukee kaikki aktiiviset lähteet tietokannasta ja käynnistää jokaiselle työntekijän. Vian eristäminen ei ole enää jotain, mitä koodaan, vaan se on jotain, jonka järjestelmän rakenne tarjoaa minulle ilmaiseksi.

2. Käyttäytymismallit tekevät ”lisää uusi integraatio” -ongelmasta ratkaistun ongelman

Ensimmäisessä versiossa source_type-haarat käsiteltiin koodissa, joka suoritti kyselyjä. Tilanne paheni lähdettä kohden. Korvaamalla se sovitintoiminnolla – neljällä kutsukäskyllä, joista ytimenä toimivat fetch/3 ja normalize/1 – työntekijä kommunikoi abstraktin lähteen kanssa eikä luo koskaan uutta haaraa. Reddit:n tai Hacker News:n lisääminen myöhemmin on yhden moduulin toteuttamista, ei putkilinjan muokkaamista. Se on selkein hyöty, jonka olen kokenut suunnittelemalla rajapintaa varten sen sijaan, että olisin suunnitellut ensimmäisenä päivänä sattumalta käytössä olleita tapauksia varten.

3. Hallinnoitu infrastruktuuri pettää tavoilla, joista dokumentaatio ei varoita

Aikaa vievin vaihe oli saada tietokanta-adapteri toimimaan yhdessä Supabase:n kanssa, eikä tässä ollut mitään syytä Elixir:lle. Kolme todellista esteitä, kaikki git-historiassa: Supabase:n transaktiopooleri hylkää nimetyt valmistellut lauseet (korjattu pakottamalla prepare: :unnamed osoitteessa Postgrex), "suora" yhteys on vain IPv6 ja saavuttamaton useimmilta palvelimilta (korjattu ohjaamalla käyttäjät IPv4-pooleriin), ja postgres://-URL-osoitteet on pilkottava erillisiksi host/port/user/pass-vaihtoehdoiksi, koska ohjain ei hyväksy URL-osoitetta. Tärkein korjaus ei ollut lainkaan koodia – se oli yhteysmerkkijono-ohjeiden kirjoittaminen asennusliittymään, jotta seuraava henkilö ei törmää samaan esteeseen. Puolet integraatiotyöstä on oman vianetsinnän muuntamista jonkun toisen ohjeiksi.

4. Tekoälyominaisuus vaatii luottamusmallin ja kustannusmallin jo alusta alkaen

Kaksi asiaa, jotka olisin lisännyt vasta myöhemmin, jos en olisi joutunut pohtimaan niitä. Luottamus: avustaja voi tehdä kyselyjä kytkettyyn tietokantaan, joten kyselypolku varmistaa tiukasti, että sovittimessa noudatetaSELECT-only-periaatetta, rajoittaa rivimäärää ja yhteysmerkkijono säilytetään salattuna – malli saa lukuoikeuden, mutta ei koskaan kirjoitusoikeutta. Sama ajattelutapa näkyy myös putkigeneraattorissa: tekoälyn luomaa konfiguraatiota käsitellään epäluotettavana ehdotuksena, joka tuodaan tarkasteltavaksi ja vahvistetaan vasta käyttäjän hyväksyttyä sen, eikä sitä koskaan kirjoiteta suoraan tietokantaan. Kustannukset: jaettu API-avain tarkoittaa jaettua laskua, joten käyttö kulkee kiintiön ratkaisijan kautta – järjestelmänvalvojilla on rajaton käyttöoikeus, käyttäjä, joka toimittaa oman Anthropic-avaimensa, voi käyttää sitä rajattomasti omalla kustannuksellaan, ja kaikille muille asetetaan jaetulle avaimelle pehmeä päivittäinen yläraja, jossa monikutsun agenttisilmukka lasketaan yhdeksi pyynnöksi. LLM:n tuominen tuotteeseen riippuu yhtä paljon siitä, mitä se ei voi tehdä ja mitä se ei voi maksaa, kuin siitä, mitä se voi tehdä.

5. LiveView tarjoaa reaaliaikaisen toiminnon ilman käyttöliittymäkehystä

Foorumi päivittyy reaaliaikaisesti – uudet viestit, läsnäoloavatarit, kommentit – ilman väliaikaista tallennustilaa (React), ilman asiakaspuolen tallennustilaa eikä kyselyjä (polling). Phoenix PubSub lähettää muutospyynnön (:new_item), ja palvelin piirtää kyseisen fragmentin uudelleen WebSocket-yhteyden kautta. Tämä vaatii kurinalaisuutta: kokoelmien on oltava LiveView-virtoja (jotta pitkäikäinen taulu ei täytä palvelimen muistia) ja on harkittava tarkkaan, mikä tila säilyy socketissa. SPA:n vaihtaminen palvelinohjatun renderoinnin tilalle poisti kokonaisen luokan asiakaspuolen ja palvelimen tilan synkronointivirheitä, joiden kanssa olen tottunut taistelemaan.

6. Tekoälyn nimeäminen muutti tuotteen luettavuutta

Pieni muutos, jolla oli valtava vaikutus. Alun perin avustaja näkyi käyttöliittymässä ja kehotteissa nimellä ”Claude”. Sen nimeäminen uudelleen muotoon ”Skiff” – eli tuotteen nimen antaminen toimittajan nimen sijaan – sai sen näyttämään Dredge-sivuston omalta ominaisuudelta pikemminkin kuin sinne kiinnitetyltä chatbotilta, ja irrotti huomaamattomasti tuotteen äänimaailman sen takana olevasta mallista. Brändäysmuutos ja arkkitehtuurimuutos (Anthropic-asiakkaan pitäminen yhden moduulin takana) osoittautuivat samaksi vaistoksi.

Turvallisuus

  • ·cloak_ecto -palvelun avulla salattuina tallennetut tunnistetiedot ja tietokantayhteysmerkkijonot
  • ·Webhook-syötteen luotettavuus tarkistetaan lähdekohtaisesti osoitteessa HMAC ennen kuin mitään dataa pidetään luotettavana
  • ·Sekä tekoälytietokantatyökalu että tietokanta-adapteri noudattavat ehdottomasti periaatetta ”kirjoitusoikeudet vain tarvittaville käyttäjille” (SELECT-only); asennusohjeissa suositellaan vain lukuoikeudet omaavien tietokantakäyttäjien käyttöä
  • ·Tekoälyn käyttöä on rajoitettu käyttäjäkohtaisilla kiintiöillä jaetun API-avaimen suojaamiseksi

Linkit