"Attempt to read from null array"

I den sidste post viste jeg hvordan det ser ud at lave en opdatering af køreplanerne. Som endte med at jeg opdagede at der var en fejl i app'en, så opdateringen ikke kan bruges. Jeg tænkte, i forlængelse kunne jeg osse vise hvordan det ser ud at finde og rette en fejl i koden.

Når der sker en fejl i app'en bliver der automatisk sendt en fejlrapport tilbage til en server. Du kan se et eksempel på hvordan sådan en fejlrapport ser ud her. Den indeholder ikke særligt meget men nok til at jeg har noget at gå efter når jeg skal finde årsagen. Derudover, første gang en given fejl sker sender systemet mig automatisk en email, så jeg bliver opmærksom på at der er et problem. Emailen indeholder den samme information som er i rapporten. Så det er lissom der det starter. 


Hvis jeg vil have et overblik over alle kendte fejl i systemet bliver de osse gemt i et lagersystem jeg har. 


På den måde er jeg ikke afhængig af at søge rundt i min inbox for fejl-information, det er osse gemt samlet med alle detaljer.

I dette tilfælde kan jeg se at fejlen sker i NativeVisitList, og fejlen er Attempt to read from null array. Jeg ved at NativeVisitList er den kode der bygger listerne af afgange. Det passer med at jeg kan huske det skete da jeg kiggede på afgange fra Aarhus H til Odense St. Så der er muligvis et problem med hvordan afgange bliver beregnet.

I A til B er der en skarp opdeling mellem den kode der laver beregninger (f.eks, find afgange fra Aarhus H til Odense St.) og brugergrænsefladen, den del der viser resultaterne til brugeren. Det første jeg gerne ville checke var beregnings delen. For at kunne teste den del har jeg en udgave af A til B der slet ikke har en brugergrænseflade, som hedder atb. Den kan det samme som app'en men viser bare resultatet som tekst. Det første jeg forsøger er at bruge atb til at vise den liste afgange som gav problemer.

Det er lidt mere omstændigt end at gøre det gennem app'en, jeg kan f.eks ikke bare få vist afgangene men skal først slå forskellige kodenumre op. Det gør app'en osse men det sker bag kulisserne. Det viser sig at der ingen problemer er – den kan sagtens vise både 10, 100, og 1000 afgange. Det tyder på at problemet er i den kode der viser resultatet.

Brugergrænsefladen til android laver jeg i Android Studio så der kan jeg prøve at køre den komplette app og fange fejlene i en debugger.

Jeg kan ikke genskabe problemet på samme måde som da det først skete hvilket tyder på at fejlen er det man kalder nondeterministisk – fejlen opstår sådan lidt tilfældigt og der er ikke noget du kan gør som fremprovokerer den på den samme måde hver gang. Men det lykkes at finde den et andet sted, nu færgen fra Sælvig til Hou.

Problemet er at der på et tidspunkt mangler et stykke data som koden forventer findes. Jeg prøver at holde øje med hvor det data kommer fra og det viser sig at være en liste som tilfældigvis er tom. Så det er bare et spørgsmål om at noget kode (jeg ikke har skrevet) har valgt at repræsentere en tom liste som at listen slet ikke er der, i modsætning til at der er en liste men listen har ikke nogen elementer. Det er lissom, forestil dig at jeg skal ud og købe ind og beder dig om en liste af ting du skal bruge. Det koden gjorde svarede til at hvis ikke du mangler noget svarer du "nej tak, ikke noget til mig" og giver mig ingen liste. Det min kode forventede svarer til at du altid giver mig en liste, selv hvis ikke du skal bruge noget, der giver du mig bare et tomt stykke papir. (Man skal muligvis være indoktrineret programmør for at det giver mening, men det er altså forklaringen). Begge måder at gøre det giver mening, problemet opstår fordi mine forventninger var forkerte – eller nærmere at det ikke var noget jeg nogensinde havde overvejet.

Det er ikke en ny fejl, den er osse til stede i sidste version f.eks, men man ser den kun hvis man rammer lige i starten eller slutningen af en køreplan, det er der listen er tom, og langt det meste af tiden er man et eller andet sted midt inde i den. Det var derfor jeg opdagede den lige da jeg testede den nye køreplan, og det var derfor det holdt op med at ske for Aarhus til Odense, fordi der var gået en dag mere så jeg skulle scrolle længere tilbage for at komme til starten. Hvorimod Sælvig til Hou går så sjældent at den stadig var tæt på første afgang. Så det var slet ikke en nondeterministisk fejl, den var deterministisk men afhang af tidspunktet jeg testede.

Det var i sidste ende en ret nem fejl, det tog ikke meget mere end en times tid at finde og fixe. Fejl kan være meget mere langhårede dog.

"Opdaterede køreplaner"

Nu skrev jeg i sidste post om at jeg ville arbejde på at strømline det arbejde jeg skal gøre når der kommer nye køreplaner for at app'en bliver opdateret. Måske er det et godt tidspunkt at beskrive hvordan det ser ud, helt lavpraktisk, når jeg opdaterer køreplanerne i dag.

Jeg får køreplanerne fra rejseplanen, de kommer som en stor zip fil. Når der er en ny udgave sender de en mail ud og det er første trin for mig – jeg opdager at der er kommet en mail fra dem,

I mailen er der et link direkte til zip filen men det link bruger jeg aldrig. For lang tid siden lavede jeg en proces der en gang imellem automatisk henter filen fra rejseplanen og hvis den har ændret sig siden sidst gemmer den en kopi. Det kom sig af at jeg var på ferie og jeg så mailen fra rejseplanen men kunne ikke (og havde ikke lyst til) at sidde og rode med at hente filer. Så nu ved jeg altid at processen gør det for mig. Den gemmer filen i google cloud, og der ligger den klar.

Som der står i detaljerne, de nye køreplaner ligger i en fil der hedder 20190314_20190605_v00.zip som er 33.4MB stor. Navnet er ikke det rejseplanen kalder den, det er et internt navn den er blevet tildelt fordi køreplanerne i filen løber fra 14. marts (20190314) til 5. juni (20190605) og det er den første fil vi har set der gør det (v00).

For at kunne bruge køreplanerne i A til B skal de processeres og transformeres og komprimeres ret omfattende. Det har jeg et script der gør. Det står osse for at hente filen fra google cloud, jeg skal ikke downloade den manuelt men bare checke at den findes og se hvad den hedder. Jeg skriver detaljerne af hvad scriptet skal gøre i en konfigurationsfil, hvoraf det meste typisk er det samme som sidste opdatering, og så starter jeg det. Så står det og tygger et kvarters, tyve minutters tid og efterlader en fil i det specielle format A til B bruger, som indeholder køreplanerne. Filen er typisk omkring 10% af størrelsen på den fil jeg hentede fra rejseplanen, hvilket er en stor del af pointen.

Her er hvordan en kørsel af scriptet ser ud, med store dele af ventetiden klippet ud

Når jeg har filen med køreplanerne skal jeg bygge en ny udgave af android app'en som køreplanerne bliver "bagt" ind i. Det ligner meget den proces jeg brugte til at lave køreplans filen: ændre nogle konfigurationsfiler og køre et script. Så bliver app'en bygget. Det tager måske 5 min.

Derefter skal jeg opdatere app'en i google play. Det vil man typisk gøre på deres hjemmeside, plus det er nødvendigt at lave ændringer et par andre steder. Jeg gjorde det manuelt til at starte med men blev ved med at lave fejl, jeg glemte altid et eller andet. Nu har jeg pakket hele operationen sammen så et script gør det, jeg skal bare – du gættede det – ændre en konfigurationsfil og så køre scriptet, så laver det opdateringen.

Hele processen med at bygge til android og publicere på google play ser sådan her ud,


Første forsøg på at opdatere på google play fejler på grund at netværket i min lejlighed. (Jeg skal have ringet til stofa og klaget igen).

På google play udgives A til B i tre forskellige "spor": internt, beta, og produktion. Produktion er den udgave langt de fleste har. Beta er den eksperimentelle udgave der kommer ud til nogle folk jeg kender, venner og familie. Internt er en udgave det kun er mig selv der ser. Opdateringer starter som interne, så får jeg opdateringen på min telefon og kan prøve om den virker. Hvis jeg mener den gør det forfremmer jeg den til beta, så er der lidt flere folk der får den. Efter lidt tid der, hvis alt stadig ser ud til at virke, forfremmer jeg koden helt til produktion og så kommer den ud til alle.

Stort set med det samme jeg har kørt scriptet ovenfor og det har udgivet opdateringen på det interne spor kan jeg se den på min telefon,

Så leger jeg lidt rundt med den og når det bare er en opdatering af køreplaner og ikke en rigtig kodeændring er der stort set aldrig problemer så under normale omstændigheder ville jeg hurtigt forfremme opdateringen. Desværre var dette et eksempel på at der er forskel på "aldrig" og "stort set aldrig" for faktisk er der problemer med opdateringen,

Så du kommer ikke til at se version 0.3.1, jeg bliver nødt til at fixe problemet og så kommer den opdatering der kommer ud til alle brugere til at hedde 0.3.2. Det bliver næste uge. Der er stort overlap mellem opdateringerne. Den sidste, den der er med i 0.3.0, holder frem til 22. maj så jeg har ikke panik-travlt med at få den næste opdatering ud.

Spam og selvpromovering

I sidste uge blev jeg færdig med den nye hjemmeside. Jeg er faktisk helt godt tilfreds efterhånden.

Det tog noget tid at få videoen til at fungere som jeg gerne vil have den til men jeg synes jeg er endt med noget der fungerer selv når man ser siden på en telefon, og det var målet. En del af udfordringen var osse at få det lavet så det giver mening hvad der foregår på skærmen og jeg ved ikke om det jeg har lavet er perfekt, men jeg tror det er godt nok.

Jeg lagde sidste hånd på hjemmesiden fredag eftermiddag og så tænkte jeg, jeg poster om app'en på reddit på mandag. Jeg har tidligere postet på r/Aarhus og jeg syntes jeg fik noget mega godt feedback. Plus det virkede som om folk syntes det var relevant. Derfor tænkte jeg, hvis jeg nu poster på r/Denmark er det sikkert fint og jeg havde allerede nogle halvhøje forventninger til at det ville blive godt modtaget. At poste på reddit i det hele taget ligger i forlængelse af min ide om at gå direkte til potentielle brugere.

For at springe direkte til moralen, min post var oppe nogle få minutter så endte den sådan her

Så, det var en skuffelse. Som i, ligge vågen om natten, har ikke arbejdet siden, har mest lyst til at opgive projektet niveau skuffelse.

Det har ikke noget med det der skete på reddit som sådan. Det er mere at jeg er enig – det jeg postede var spam og selvpromovering. Jeg troede oprigtigt det var noget der ville være nyttigt for folk men hvor meget af det spam jeg modtager, og som jeg synes er mega irriterende, er fra folk der lige så oprigtigt mener det de sender vil være nyttigt for mig? I hvert fald en del er jeg sikker på. Den slags, og en masse andet af samme skuffe, har jeg grublet meget over de sidste par dage.

Jeg er stadig ikke færdig med at gruble men i hvert fald en smule af min motivation for at fortsætte er kommet tilbage. Min tanke lige nu er at jeg holder resten af ugen fri og laver alt andet end at arbejde (og at skrive dagbog tæller åbenbart ikke som arbejde). Når jeg så kommer tilbage næste uge går jeg i gang med at arbejde på automatisk opdatering, det vil sige at køreplanerne bliver opdateret i app'en helt automatisk når der kommer nye uden at jeg behøver gøre andet end måske trykke godkend et eller andet sted. Det skal jeg have lavet under alle omstændigheder og det er en forudsætning for at jeg kan holde op med at arbejde aktivt på projektet. Når det så er på plads ser jeg hvordan jeg har det og tager stilling til om projektet er slut. Hvis jeg kender mig selv ret er det ikke, men det vil vise sig til den tid.

I mellemtiden forsøger jeg mig med lidt mere selvpromovering og hvis jeg finder noget positiv respons og måske endda nogle nye brugere vil det gøre det meget nemmere at fortsætte. Hvis ikke er det selvfølgelig surt men det vil osse være nyttig input.

Video video

Det slog mig i begyndelsen af denne uge at der faktisk er lang vej fra at høre/læse om hvad A til B kan og så have en fornemmelse for hvordan den er at bruge i praksis. Det er et stort spring. Det er et problem for hvis ikke du har en fornemmelse af at det her er bedre hvorfor så installere den?

For at gøre det spring mindre fik jeg lyst til at lægge noget video ind på siden på atilb.dk som kan given en ide om hvordan den her app virker. Jeg er nok faldet lidt bagud med hvordan man laver hjemmesider så det viste sig at være et lidt omfattende projekt men det nærmer sig noget jeg synes virker okay. Der er mange trin undervejs.

Her er det sidste trin af udviklingen: jeg polerer en af de nye elementer i den nye hjemmeside med udviklingsværktøjerne i chrome. Det er mest farver, skrifttyper, marginer, den slags.

Den kode der tegner hjemmesiden arbejder jeg på i PyCharm. Det er den overordnede struktur.

Der er en smule grafik arbejde oveni. F.eks bliver videoen vist med omridset af en telefon nedenunder og noget halvgennemsigtig reflektion ovenpå. Det endte jeg at tegne i Inkscape.

Pointen er selvfølgelig at demonstrere app'en. Der er heldigvis indbygget skærmoptagelse i android emulatoren så jeg skal bare køre app'en og så bede emulatoren om at optage skærmen.
Der er en masse detaljer man skal have styr på – jeg har optaget hvert klip måske 10-20 gange for at ramme det rigtigt – men det er overkommeligt. En detalje jeg havde glemt var at tiden gik mellem optagelserne så når klippene blev sat sammen hoppede uret rundt. Det gjorde at jeg måtte optaget det hele igen hvor jeg huskede at indstille uret til 11.20 lige inden hver optagelse.

Jeg håber jeg kan få poleret færdig i morgen så jeg kan opdatere siden inden weekenden.

Et skridt tilbage, et skridt frem

Som et led i at få bedre kontakt til brugerne vil jeg gerne, ud over bare at være på twitter, facebook, osv, gøre noget gennem selve app'en. Men det er svært. Jeg vil ikke, ikke, lave den sædvanlige pop-up med "din mening er vigtig for os" for jeg synes selv de er irriterende. Men samtidig er brugernes mening vigtig for mig, det er lidt det der er pointen. Så spørgsmålet er om det at jeg gerne vil i kontakt med mine brugere er grundlæggende irriterende uanset hvordan jeg griber det an? Eller om det er okay og kan være ikke-irriterende hvis jeg gør det på den rigtige måde? Det er ikke noget jeg har et godt svar på, jeg tror det må komme an på et forsøg.

Her er mit design indtil videre:


En besked der dukker op under dine bogmærker og som du nemt kan slippe af med hvis ikke du er interesseret. Tanken er at jeg lavet noget logik der sørger for at beskeden først dukker op når du har brugt app'en et stykke tid.

Det næste er så at jeg gerne vil have et minimum af statistik omkring om beskeden så faktisk bliver vist, og om folk trykker på den. Brugsstatistik er en anden følsom ting, på samme måde som "din mening er vigtig for os" er det. Folk er som et gennemgående princip ikke interesseret i at der bliver opsamlet statistik om dem. Det er noget jeg har tænkt meget over, rigtig meget, og jeg mener efterhånden jeg har fundet en løsning der er acceptabel.

Nøglen er at hvor jeg tror folk har et problem med at nogen opsamler information specifikt om dem er det et mindre problem at gøre det på tværs af brugere. For eksempel, hvis jeg ved at "Trine brugte A til B i dag" er det problematisk. Hvorimod hvis jeg ved at "1632 brugere brugte A til B i dag", hvor Trine indgår som en af de 1632 uden at der er nogen måde at identificere at det er hende, er det mindre problematisk. Det er det sidste jeg reelt er interesseret i, jeg har ingen interesse i hvad individuelle brugere gør. Og ikke bare fordi det er noget folk ikke har lyst til at jeg ved, lovgivningsmæssigt og specielt efter GDPR blev indført, det nye regulativ om databeskyttelse, følger der en masse forpligtelser med.

Der findes mængder af grydeklare løsninger til at opsamle brugsstatistik men fælles for dem er at de gør præcis det jeg ikke vil: gemmer data om brugere individuelt. Det problem er jeg tidligere rendt ind i omkring fejlrapporter. Hvis jeg vil have en løsning der opfører sig som jeg gerne vil have skal jeg selv bygge den fra bunden, præcis som jeg gjorde med fejlrapporterne. Det er jeg gået i gang med men der er jeg så løbet ind i det næste problem. Jeg ville gerne bare udvide det system jeg bruger til fejlrapporter som er bygget på google app engine men så skal jeg bruge nogle funktioner som ikke bare er tilgængelige. Det kræver at jeg flytter fra den platform fejlrapport systemet bruger lige nu (standard python2) til en anden (enten standard python3 eller flexible python). Det kræver igen at jeg skriver en helt masse kode om fordi der er store forskelle mellem platformene. Det er jeg så gået i gang med men det har været super smertefuldt. Dårligt dokumenteret, svært til umuligt at teste, ugh, bare dødens pølse.

I sidste ende er jeg blevet enig med mig selv om at hvis jeg alligevel skal skrive det hele om så er det tid til helt at droppe google app engine og køre koden selv. App engine er bare for meget smerte for for få fordele. Det ville jeg skulle gøre under alle omstændigheder før eller siden og det bliver altså nu. Det føles som at smide en masse arbejde væk og så bruge tid på at gøre det hele igen, plus det føles som om jeg er kommet virkelig langt væk fra det jeg egentlig skulle, nemlig at vise en besked i app'en. Men jeg tror det er sådan det må være.

Jeg kommer til at skrive mere om brugsstatistik, jeg har virkelig stærke holdninger til privacy og der ligger en mængde tanker og arbejde bag min tilgang til det, men det kommer til at vente. Jeg vil hellere beskrive hvordan det virker når det virker, hellere end hvad jeg har tænkt mig som kan risikere at ændre sig når jeg går i gang med at implementere det.