Tekniske forberedelser til mobil-version

Bubbles, FC Sunnyvale Udvikler 12. november 2022, 14:18

Tekniske forberedelser til mobil-version

Det er åbenlyst at vi skal have en mobilversion, af mange årsager. Jeg har skrevet om det før, men et par af de mest kritiske årsager er:

  1. Fastholdelsen af nye managers kan måles i minutter, og for en stor del endda i sekunder. Når det går så hurtigt før de forsvinder er det pr. definition førstehåndsindtrykket, som skræmmer dem væk.
  2. Når størstedelen af nye managers, som vi får ind via markedsføring, smutter efter mindre end 1 minut, betyder det at markedsføringen giver et stort underskud.

Den seneste tid været i gang med at forberede os til at lave en mobilversion, og det vil jeg fortælle om i dag. Lad os først se på målsætningerne for en mobilversion.

Mobil-versionen skal opfylde 2 ting

1. Et responsivt design

Responsivt design betyder et design som tilpasser sig automatisk til alle skærmstørrelser. Det giver sig selv at et design er ikke mobilvenligt, hvis man f.eks skal zoome ind og ud, og har svært ved at ramme de ting man vil klikke på, fordi de er for små.

2. Lynhurtig responstid

Når man taler om mobilvenlige sites, så tænker de fleste kun på designet, men responstid er mindst lige så vigtigt. Der er forskning, som viser at vi er langt mere utålmodige, når vi tilgår et site fra mobilen. Det er den psykologiske del.

Derudover er der de tekniske udfordringer, såsom en langsom mobilforbindelse, udfald når man bevæger sig rundt, langsommere hardware, osv.

Vi kan ikke forhindre de ovenstående problemer, så i stedet skal vi gøre alt hvad vi kan for at vores del er så hurtig som muligt, når faktorer udenfor vores kontrol er langsomme.

En kæmpe udfordring

Virtual Manager er overordentligt kompleks, med over 450 unikke HTML-views og over 200 view partials. Designet udgør over 28.000 linjer CSS-kode. Derudover er der adskillige komplekse dynamiske mini-apps, såsom stadion-editor, kampviser, taktik-editor, nybegynder-kursus/achievements, trøjenummer-vælger, kontrakt-forhandling, træningsøvelse-editor, auktioner, osv.

Først lidt terminologi:

HTML udgør indholdet og strukturen af siderne på sitet.

Styles(CSS) angiver hvordan indholdet skal se ud: størrelser, farver, skriftyper, placering osv.

Javascript er program-kode som kører i din browser efter siden er hentet og som kan manipulere med indholdet og tale med vores servere via baggrundskald. Hver gang noget på siden skal animeres, fjernes, indsættes eller ændres uden sideskift er det Javascript, som gør det.

Lad os se på udfordringerne ved de 2 del-mål:

Responsivt design

Udfordringer:

  • Vi kan ikke lave designet responsivt ved at redigere i vores nuværende CSS. Vi har ALT for meget CSS i dag, og det er gammeldags, rodet og uoverskueligt.
  • I dag er ALLE styles for hele sitet samlet i 4 store bundles, som indlæses ved hver sidevisning, selvom det kun er en brøkdel af det, der skal bruges på en given side
  • Med så mange views kan vi ikke vente med at release til det hele er færdigt.
  • I den perfekte drømmeverden kan man redesigne et site ved kun at ændre CSS'en og ikke røre ved HTML'en. I praksis er dette sjældent muligt, så vi vil være nødt til at lave omfattende ændringer i stort set alle HTML-views.
  • Udover de statiske HTML-views er der mange steder hvor HTML genereres dynamisk i Javascript - i alle vores mini-apps. I årenes løb har vi løst dette med et hav af forskellige metoder og javascript-frameworks. Der er ikke een konsekvent måde, vi har gjort det på, og det giver ekstra udfordringer at lave de nødvendige ændringer i den HTML, der genereres dynamisk.
  • Det er risikabelt at rulle et nyt design ud og det vil kræve flere iterationer at få noget, der fungerer. For megen forstyrrelse af centrale spil-funktioner imens vi eksperimenterer vil resultere i klager.

Løsninger:

  • Vi starter designet på en frisk uden at genbruge en eneste linje af det gamle CSS.
  • Vi laver en løsning hvor vi kan foretage en løbende udskiftning af sider, hvor det gamle kan køre sammen med det nye.
  • De steder hvor HTML genereres dynamisk af Javascript, omskriver vi på en ny, simplere, og mere ensartet metode, med værktøjer vi har fået med opgraderingen til Rails 7 - her sender vi færdigt-renderede HTML-bidder fra serveren til en minimal JS-komponent, i stedet for at sende rå data og lade et stort javascript-framework eller gammel custom kode opbygge HTML'en.
  • For at tage hul på arbejdet på en måde, der ikke forstyrrer det kørende spil, starter vi med et miniprojekt på nogle uger, hvor vi går efter at få nogle grundlæggende principper på plads og resulterer i noget vi kan lægge op og lade jer se og bruge.
  • Derefter finder vi ud af hvilke eksisterende sider der skal redesignes til en første release, med fokus på de mest besøgte og dem som er nemmest at gå til.

Responstid

Udfordringer:

  • Kæmpestore CSS-bundles gør renderingen af sider langsomt - selv når filerne er lagret i browserens cache, skal de stadig parses ved hver sidevisning, hvilket især er en udfordring på mobil.
  • Vi har brugt mange forskellige Javascript-biblioteker og frameworks såsom jQuery, Backbone, Underscore, osv. Store komplekse frameworks gør ligesom store CSS-bundles responstiden langsommere fordi de skal indlæses, parses og eksekveres ved hver sidevisning.
  • Der er flere actions i backenden hvor responstiderne er for langsomme til mobil. Målet er at al navigering mellem sider skal føles øjeblikkelig, hvilket er langt mere vigtigt på mobil end på desktop. En total responstid på 200ms og derunder vil opleves som øjeblikkelig og føles som en native app.

Løsninger:

  • Vores asset-pipeline skal moderniseres og vores CSS-struktur skal bygges op til at vi kun sender det absolut nødvendigste CSS, som skal bruges på en given side.
  • Når en side omskrives, fjernes al brug af jQuery.
  • Vi skal anvende de nye værktøjer vi har fået da vi opgraderede til Rails 7, som bla. kan gøre navigationen mellem sider meget hurtigere og giver mulighed for at bruge websockets på en nem måde.
  • Når vi redesigner en side skal backenden (den del, som foregår på vores webserver) samtidigt revurderes i forhold til performance.
  • På sider/funktioner som udfører store komplekse opgaver (som f.eks udførsel af træning) skal vi gøre mere brug af at give brugeren øjeblikkelig visuel feedback på deres handling imens vi sender opgaven til afvikling i baggrunden.
  • Vi skal anvende mere fragment-caching (hvor vi gemmer mindre fragmenter af sider i en RAM-cache så vi sparer databaseopslag og processering næste gang) de steder hvor det giver mening.

Forberedelser de seneste par måneder

Det ovenstående er en overordnet beskrivelse af de opgaver, der ligger forude. Rent faktisk at føre dem ud i livet kræver en masse teknisk arbejde og integrering af konkrete værktøjer.

De seneste par måneder har vi været i gang med at få de tekniske forudsætninger på plads.

Arbejdet har haft flere mål:

  1. Integrering af de nye værktøjer i vores applikation
  2. Opsætning af servere, deployment scripts, og asset pipeline til at understøtte de nye værktøjer i vores live-miljø
  3. Brug af disse værktøjer til at løse konkrete opgaver i spillet, for at samle erfaringer og finde faldgruber
  4. Test af ovenstående funktioner på live-serverne

Vi startede med at opgradere vores asset pipeline til at bruge de nye indbyggede defaults i Rails 7 i stedet for alt vores eget gamle skrammel. En asset pipeline er den komponent, som står for at generere færdige stylesheets og javascript-filer ud fra delkomponenter. Opgraderingen gør det bla. nemmere at arbejde med moderne javascript, og så er det altid klogest at lægge sig tæt op af de metoder som udviklerne bag Rails har lagt op til at man bruger som default. Så sparer man som regel en masse arbejde.

I flere år har tendensen i webudvikling været at lave single-page-apps i store Javascript-frameworks, hvor alt HTML genereres af Javascript, og serveren kun sender rå data. Meningen var at undgå hele sideskift, som indebærer at browseren skal foretage en masse gentaget arbejde hver gang.

Der er dog nogle ulemper, bla. i form af kraftigt øget kompleksitet og større arbejdsbyrde, problemer for søgemaskiners mulighed for at indeksere siderne og langsommere respons ved første sideindlæsning.

For os ville den største ulempe ved denne fremgangsmåde være den store arbejdsbyrde og det at vi skulle lave alting på en fundamentalt anderledes måde, hvor vi ville være totalt nybegyndere. Det ville ganske enkelt være umuligt med vores begrænsede ressourcer.

Men, fordi vi har opgraderet til Rails 7, har vi i dag nye værktøjer, som er nemme, hurtige og elegante, og hvor vi kan udnytte al den erfaring og viden vi har i forvejen.

De består af en række komponenter som tilsammen giver os alle fordelene ved de ovennævnte single-page-apps, uden alle ulemperne. Komponenterne er Stimulus, Turbo Drive, Turbo Frames, og Turbo Streams. De teknisk interesserede kan læse mer om dem på https://hotwired.dev

De supplerer vores eksisterende fremgangsmåder, og i mange tilfælde simplificerer de endda vores arbejde, fordi de kan erstatte flere forskellige gamle, indviklede løsninger.

For at kunne danne os erfaringer med dem i realistiske scenarier og i stor skala, valgte vi en række funktioner i spillet til at teste dem af på. På den måde kunne vi finde de potentielle faldgruber og lære at løse de problemer, som kan opstå.

Stimulus

Stimulus er et minimalt javascript-bibliotek, med en yderst begrænset opgave: at tilføje lidt interaktivitet i den HTML man allerede har, sendt fra serveren.

Vi havde faktisk i forvejen brugt Stimulus - nemlig på den nye sponsorfunktion, hvor det står for at opdatere aftalens vilkår når du vælger bonusser til og fra.

De tilfælde hvor vi i vores arbejde med de andre værktøjer har haft brug for at tilføje eller omskrive små bidder af interaktivitet har vi hver gang vidst hvordan det skulle løses - med Stimulus - og det bliver fremgangsmåden fremover i stedet for at vi skal opfinde hjulet på ny hver gang vi laver noget nyt.

Turbo Drive

For at opnå vores mål om hurtigere responstider kommer vi til at bruge Turbo Drive på alle sider. Kort fortalt er det en simpel komponent, som får almindelige HTML-sider til at fungere som "single page apps", uden at vi selv skal gøre ret meget.

Vi skal sådan set bare slå det til, og så vil alle klik på links automatisk indlæse den nye side i baggrunden i stedet for at foretage en komplet genindlæsning. Når der kommer svar fra serveren, vil Turbo Drive nøjes med at udskifte de ting på siden, som har ændret sig, og browseren skal ikke starte helt forfra med at indhente og parse alle styles og javascript.

Dette har vi brugt på personalesiderne. Når man går ind på /employees starter Turbo Drive, og når man navigerer videre til personalesøgningen, og skifter mellem siderne med søgeresultater sker det med Turbo Drive.

Det er lavet således at når man navigerer væk fra medarbejdersiderne, så bryder vi ud af Turbo Drive, hvorefter alle sideskift foregår som normalt. At vi kunne få dette til at fungere var vigtigt for at sikre os at vi vil kunne omskrive siderne løbende uden at noget går i stykker, for de gamle javascript-funktioner vil i de fleste tilfælde kræve en smule tilpasning.

På medarbejdersiderne gjorde vi os en vigtig erfaring: når vi foretager sideskift på denne måde er det utroligt vigtigt at serveren svarer lynhurtigt, da det ellers kan føles som om det går langsommere end ved et almindeligt sideskift - også selvom det samlet set går hurtigere - fordi den visuelle feedback man får ved klik er en smule anderledes.

Derfor var det held i uheld at vi implementerede dette på lige netop medarbejdersøgningen, da den har nogle helt fundamentale problemer med performance, pga. det enorme antal medarbejdere i databasen. Det gav os god indsigt i samspillet mellem server-performance, og denne nye måde at foretage sideskift på.

Vi brugte noget tid på forskellige performance-forbedringer af medarbejdersøgningen, men da hele medarbejdersystemet alligevel skal have en komplet overhaling, ligesom sponsorsystemet allerede har fået, gjorde vi ikke det helt store.

Turbo Frames

Turbo Frames er en komponent, som gør at vi selektivt kan udskifte små bidder af siden med resultatet af et baggrunds-opkald til serveren, uden at vi skal skrive noget javascript overhovedet.

Her valgte vi at bruge notifikations-menuen til at teste det med, da den er så massivt brugt at vi nok hurtigt skulle finde ud af hvis der var nogen problemer.

Og dem kom der også en del af, så vi brugte en del tid på at prøve forskellige metoder og også her blev performance et problem - fordi notifikationer også er kæmpetabel. Når vi med den nye strategi henter notifikationer 1 ad gangen, så skal det altså også gå hurtigt, og her viste problemet sig at være at finde den næste og forrige notifikation, for at kunne lave knapperne til dem. Det gav nemlig adskillige ekstra databaseopslag.

Lærdommen her var at vi skulle tænke anderledes omkring hvordan vi henter dem ud, og vi fik lavet et par mindre performance-forbedringer, således at der nu er en acceptabel responstid ved skift til næste eller forrige notifikation.

Selve det at vi kan lave et baggrundskald og skifte en mindre del af siden ud er egentlig ikke noget nyt. Det har vi gjort flere steder i forvejen. Det nye er at vi nu kan gøre det på en ensartet måde, som er langt nemmere at implementere end det vi har brugt førhen.

Turbo Streams

Turbo Streams er en komponent som gør at vi kan arbejde med websockets på en nem og elegant måde.

Websockets vender den måde WWW fungerer på hovedet. Normalt fungerer WWW ved at browseren på din maskine ringer op til vores server og beder om en side eller noget data, som den så får, og forbindelsen lukkes. Kommunikationen foregår altid i den ene retning - det er altid din maskine der ringer op.

Denne model er lidt kluntet, når det drejer sig om mere dynamiske funktioner, hvor du har brug for kontinuerlige opdateringer omkring handlinger der sker, som du ikke selv har foretaget - som f.eks når du sidder og følger en auktion. Her skal du helst have besked med det samme, når en anden klub overbyder dig, f.eks.

Med websockets skal vi ikke sidde og vente på at du ringer op. Vi sende data den anden vej, på vores eget initiativ.

Skoleeksemplet på brug af websockets er en chat app, så vi startede med at lave en ultra-simpel chat, for at sikre os at alting fungerede - især i vores live-miljø. Her har vi sat en separat server op, hvis eneste ansvar er at holde websocket-forbindelser åbne, hvilket aflaster webserverne. Det fungerede fremragende.

Derefter gik vi i gang med at implementere det på auktionssiderne, da disse var en oplagt use-case til websockets, og fordi de bruges i et massivt omfang ville det være den ultimative stress-test.

På vores gamle auktionssider havde vi en bid javascript kørende i din browser, som hvert 5. sekund ringede op til vores server for at få seneste status på alle de auktioner du deltog i.... igen og igen og igen.

Med Turbo Streams fungerer det nu helt anderledes. Når du går ind på siden åbnes en websocket-forbindelse som fortæller at du gerne vil abonnere på opdateringer vedrørende de auktioner, du deltager i. Forbindelsen forbliver åben så længe du er på siden, og fidusen er at vi kan sende data over den, uden at din browser skal spørge.

Så længe ingen byder, sker der ingenting. Men, når nogen så byder, sender vi øjeblikkeligt et broadcast med auktionens nye status til alle de websockets, som er åbnet af dem, der sidder og kigger på auktionen.

Vores servere belastes nu væsentlig mindre, fordi dem der har auktionssiden åben ikke allesammen sidder og henter auktionens status hvert 5. sekund. Webserverne skal kun lave noget, når der rent faktisk sker ændringer. Førhen var købsauktioner den side som samlet set belastede vores servere allermest, på grund af det enorme antal af gentagne opslag. Nu er de næsten ikke til at se i statistikken.

Som med de værktøjer jeg allerede har nævnt, er det bedste ved Turbo Streams at det er relativt nemt at arbejde med. Dette betyder at vi i fremtiden ikke vil holde os tilbage med at bruge det i de tilfælde hvor det vil forbedre oplevelsen hvis vi kan sende dig øjeblikkelige opdateringer over en websocket, omkring ting der sker i baggrunden. At bruge det til notifikationer vil være oplagt, og måske kunne vi overtales til at genindføre en Vman-chat i fremtiden.

Opsummering

Med alt dette indledende arbejde har vi fået de grundlæggende forudsætninger på plads for at vi kan påbegynde arbejdet med mobilvenligt design.

  • Vi er færdige med alle opgraderinger og integrationer af nye værktøjer.
  • Vores live-servere og deployment-process er klar til at understøtte dem.
  • Vi har høstet rig erfaring med de nye værktøjer ved at bruge dem i eksisterende funktioner og konstateret at server-setuppet kan afvikle dem i stor skala.

Det næste skridt går ud på at vi får hamret alle disse erfaringer helt fast ved at bruge dem i et lille, hurtigt projekt hvor vi laver noget nyt, som I kan få lov at bruge. Vi har sat en deadline for release d. 1. december, så hvad kan det mon være?

Derefter begynder vi arbejdet med redesign. Her bliver en del af udfordringen at finde ud af hvilke sider der skal være med i første release for at oplevelsen føles nogenlunde sammenhængende.

Vi skal også have besluttet hvor i processen det nye medarbejdersystem skal indarbejdes. Det afhænger af en vurdering af hvor omfattende systemet bliver og hvor lang tid det tager at lave.

Resten af året vil I kunne julehygge på Vman med en runde af Odds-spillet i forbindelse med VM og så det "hemmelige projekt". Mens I gør det, vil vi fortsætte mobilversion-arbejdet og en gang efter årsskiftet vil vi undersøge muligheden at køre en betatest på en separat server, for at vi kan få noget feedback fra jer inden vi releaser første version.

Feedback til blog-indlægget, kan gives her Feedback: Tekniske forberedelser til mobil-version