Steven
Steven6 min lesing

Vi slettet 5 000 linjer lydkode – og transkripsjoner begynte å dukke opp to ganger

GeekBye v1.6 rev ut to Swift-transkriberere på enheten og erstattet dem med én samlet pipeline – en netto sletting på over 5 000 linjer, gjort mens folk satt midt i et møte i appen. Så begynte transkripsjonene å dukke opp to ganger, fordi feilen flyttet seg til det ene laget som fortsatt trodde det var to motorer.

Utvikling
Transkribering
Arkitektur
GeekBye-releaser
Vi slettet 5 000 linjer lydkode – og transkripsjoner begynte å dukke opp to ganger

Det finnes en helt spesiell form for skrekk i det å refaktorere kode som kjører akkurat nå, for folk som er midt i et møte. Du kan ikke ta funksjonen ned for vedlikehold – noen stoler på at den skal transkribere en samtale de ikke kan gjenta. GeekBye v1.6 gjorde nettopp dette med den mest kritiske delen av appen: lydpipelinen. Dette er historien om hva vi slettet, hva som gikk i stykker, og de to lærdommene om driftssikkerhet som kom ut av det.

To motorer, én av dem i Swift

Før v1.6 kjørte GeekByes tale-til-tekst på enheten i Swift-binærer. Det var to av dem: én som pakket inn Apples Speech-rammeverk, og en større (over 1,500 linjer) som fanget dobbel lyd – mikrofonen din pluss systemlyden – via ScreenCaptureKit og åpnet sin egen WebSocket rett til backend-en vår for å strømme til Deepgram. To transkriberere, to fangstveier, to måter å snakke med nettverket på, alt i et kompilert språk som betydde at hver endring krevde rekompilering av en binær.

v1.6.0 slo alt sammen til én pipeline. Den ene samlende commiten slettet Apple Speech-transkribereren (~1,145 linjer), Deepgram Swift-fangeren (~1,523 linjer), begge TypeScript-broene, den gamle orkestratoren og IPC-håndtererne for dobbel lyd – en netto sletting på over 5,200 linjer. I stedet: rendereren fanger lyd gjennom standard Web APIs, sender den som PCM over IPC, og Electron-hovedprosessen eier én enkelt WebSocket-vei til backend-en, med leverandøren valgt av backend-konfigurasjonen. Én fangstvei, én eier av socket-en, ett sted å resonnere fra.

Å slette fem tusen linjer føles fantastisk. I omtrent én dag.

Feilen flyttet til laget som fortsatt trodde det var to

Så begynte transkripsjonene å dukke opp to ganger. En enkelt talt setning ble lagret som to identiske oppføringer – noen ganger verre.

Grunnårsaken er det mest lærerike i hele denne releasen, fordi den er en generell lov om samling. Vi hadde samlet leverandørene i hovedprosessen – én socket, én håndterer. Men React-laget hadde ikke fått med seg beskjeden: det monterte fortsatt to leverandørspesifikke hendelseskroker samtidig, én bygget for Deepgram og én for ElevenLabs. Begge lyttet. Begge lagret. Hver transkripsjon ble lagret av hvilke som helst kroker som var i live – og det var begge.

Og det lå en annen, mer lumsk dobling under den: ElevenLabs sender ut hver ferdige linje to ganger – én gang som en committed-hendelse og igjen som committed_with_timestamps. Så selv med én enkelt krok kunne én setning ankomme som to hendelser.

Løsningen (i v1.6-linjen) er en ryddig todeling som kartlegges nøyaktig mot de to årsakene: sett hver hendelseskrok bak et enabled-flagg drevet av backend-ens konfigurasjon for aktiv leverandør, slik at bare én lytter noensinne er i live; og legg til dedupliseringen av tekst per kilde (husk den siste lagrede linjen for mikrofon og for systemlyd) for å svelge ElevenLabs' doble utsending.

Lærdommen er større enn denne feilen: når du samler to backends bak ett grensesnitt, forsvinner ikke duplikasjonen – den migrerer til laget som fortsatt antar at det er to. Vi slo sammen rørleggingen i hovedprosessen, og lekkasjen dukket opp to lag lenger opp, i brukergrensesnittets hendelsesoppkobling. Å samle en pipeline er ikke ferdig når produsenten er samlet; det er ferdig når hver konsument har sluttet å tro på den gamle formen.

Den første gjenkoblingen: «gi opp etter fem forsøk»

v1.6.0 leverte også GeekByes aller første automatiske WebSocket-gjenkobling med eksponentiell backoff – og den er verdt å vise nettopp fordi den er den primitive stamfaren til den skuddsikre versjonen vi kjører i dag.

Den opprinnelige var begrenset. Fem forsøk, forsinkelser på 1s, 2s, 4s, 8s, 16s – og så ga den opp, og sendte ut en fatal feil: «Tilkobling mistet – vennligst start transkribering på nytt.» Der og da føltes det ansvarlig: ikke prøv i det uendelige, si det ærlig til brukeren. I praksis var det en UX-feil i forkledning. Et seksten sekunders nettverksavbrudd – en Wi-Fi-overlevering, en VPN-gjenkobling, en tunnel – er forbigående, men et begrenset gjenforsøk behandler det som endelig. Brukeren gjorde ingenting galt og ble bedt om å starte et pågående møte på nytt.

Det er nøyaktig den feilen dagens design fjernet. I dag gjenkobler GeekBye med ubegrenset backoff og sykler til og med gjennom leverandører, og stopper bare noensinne av en genuint fatal grunn – autentisering, kvote, fakturering. Hele reisen fra «gi opp etter fem» til «aldri gi opp på en forbigående feil» fortelles i hvorfor AI-notatverktøyet ditt stopper på dårlig Wi-Fi. v1.6 er der den veien startet, med den naive versjonen som måtte eksistere først.

Utdaterte tilkoblinger hjemsøker deg med mindre de har navn

Enda en lærdom om driftssikkerhet landet i v1.6.3, og fikset en subtil klasse feil som alle som driver med gjenkobling mot en tilstandsfull backend før eller siden vil møte.

Når du gjenkobler – eller bytter transkriberingsspråk midt i en økt, som gjenkobler under panseret – dør ikke den gamle tilkoblingen alltid stille. Dødsraslemeldingene dens (CONNECTION_LOST, disconnected) kan ankomme etter at den nye tilkoblingen allerede er sunn, og rive ned den helt gode erstatteren. Løsningen gir hvert tilkoblingsforsøk en identitet – en økt-ID per tilkobling stemplet på WebSocket-URL-en – pluss et kort nådevindu etter gjenkobling der utdaterte frakoblingsmeldinger fra et tidligere forsøk ignoreres. Den sender også en eksplisitt stop-melding før den lukker en socket, slik at backend-en kan rive den gamle økten ned rent.

Prinsippet: all gjenkoblingslogikk over en tilstandsfull backend må merke hvert forsøk med en identitet og behandle sene meldinger fra tidligere forsøk som støy – ellers blir en vellykket gjenkobling drept av forgjengerens døende åndedrag.

Tre ting v1.6 lærte oss

  1. Samling flytter feil; den sletter dem ikke. Slå sammen to motorer til én og gjennomgå hver konsument som fortsatt tror det er to. Vår var brukergrensesnittet, som monterte to hendelseskroker etter at rørleggingen allerede var slått sammen.
  2. Din første gjenkobling vil være begrenset, og begrenset er feil for direktestrømmer. «Gi opp etter N forsøk» gjør et forbigående avbrudd til en endelig feil brukeren får skylden for. Skill fatalt (autentisering, kvote) fra forbigående (en droppet socket) helt fra den aller første versjonen.
  3. Gjenkoblinger trenger identitet. Merk hvert forsøk; ignorer de sene meldingene fra døde forsøk. En tilkobling uten navn kan bli myrdet av sitt eget spøkelse.

Dette er det andre kapittelet i historien om driftssikkerhet som blir GeekBye v2. For det første, se auto-oppdateringssagaen som tok seks releaser på fire dager (v1.5.x); for hvor gjenkoblingsarbeidet til slutt havnet, hvorfor AI-notatverktøyet ditt stopper på dårlig Wi-Fi; og for hele buen, anatomien til det å skipe programvare til perfeksjon.

Relaterte artikler