Centralisering på rätt sätt

Love Mayrhofer
Bonnier News Tech
Published in
8 min readMay 27, 2021

— Eller hur vi tillsammans räddade våra sajter från undergång

“Om tre månader kan två av dina största nyhetssajter gå ner och inte komma upp igen om du inte byter ut ditt backend. Om fyra månader ska du släppa en helt ny sajt-plattform. Just det! Du ska byta CMS (Content Management System, “skrivverktyg”) för alla som skriver nyheter till sajterna också. Och du måste skicka data om dina artiklar till analys såklart. Och bilderna kan inte ligga på samma CDN (Content Delivery Network, “distributionsnätverk”) som innan. Kodbasen är okänd för större delen av dina utvecklare, om jag glömde säga det. Lycka till, och ses i maj!”

Det här var situationen Bonnier News Locals utvecklingsavdelning stod inför i februari. Nu är det maj och … allt gick faktiskt bra! Som vanligt när något går riktigt bra finns det ingen genial idé, ingen superhjälte-utvecklare som jobbar dygnet runt, och ingen perfekt produkt som löser alla problem. Det är i vanlig ordning istället gott samarbete, prestigelöshet och strikta prioriteringar som räddar oss.

Jag arbetar inte på Locals utvecklingsavdelning och har därför fått beundra deras stordåd lite från sidlinjen. Gruppen jag jobbar i är ansvarig för det nya centrala API som Local konsumerar för att få innehåll (artiklar och sånt) till sin sajt. En stor del av Locals utmaning var som sagt att “byta backend” och en stor del av den pucken handlade om att konsumera vårt API.

Den bästa koden är kod vi slipper skriva

Som tur är visar det sig att det inte behövs vidare många API-metoder för att bygga en riktigt bra nyhetssajt! Nu när Locals nya sajt-plattform gått live använder de i princip 7 olika metoder i vårt API, alla ganska lika:

  • getArticle(s)
  • getArticlesBySection
  • getArticlesByAuthor
  • getArticlesByOrganization
  • getArticlesByStory
  • getArticlesByMetaTag
  • getLayoutData

Vi använder GraphQL och låter därmed våra konsumenter specificera vilken data de är intresserade av i grafen av beroenden mellan datapunkter varje gång de gör ett anrop. Ett och samma anrop kan alltså till exempel hämta 20 artiklar, för varje artikel hämta dess skribent och för varje skribent hämta dess senaste 5 artiklar. Om du är intresserad av hur den sortens frågor går att optimera är den här artikeln ett bra ställe att börja på.

Det senaste året har vår grupp arbetat med innehållsformat och API:er inom Bonnier News. Något vi har lärt oss är att nyhetssajter nästan bara hanterar nyhetsartiklar. Vi har också lärt oss att väldigt många som skriver kod till nyhetssajter skriver mjukvara som gör en massa annat än att hantera nyhetsartiklar.

Hur vi lyckades skriva så lite kod som möjligt

Ett stort problem i Locals gamla backend var att de försökte lösa ett, eller fler, generella problem. Det är inte första gången utvecklare försöker göra det, varken på Bonnier News eller i allmänhet. Det finns ofta en idé om att ett kompetent API ska låta sina konsumenter:

  • Söka på så många fält som möjligt i så många datatyper som möjligt.
  • Lista data av så många typer som möjligt.
  • Sortera data baserat på så många fält som möjligt.
  • Traversera grafen av datas relationer så fritt som möjligt.
  • Optimera för snabba svarstider för så många features som möjligt.

Det är lätt, framför allt i större företag, att vilja leverera en “färdigutvecklad”, “kompetent” produkt till sina medarbetare. Vi visste inte vilka 7 metoderna Local behövde var när vi började, eller om det var 2, eller 53 metoder. Det vi visste var att både Local, vi själva och många andra har misslyckats med att leverera värde till sina slutanvändare i tid för att vi skriver mjukvara som gör mer än vad som behövs. För att undvika detta misstag insåg vi att våra grupper behövde jobba väldigt nära varandra.

Första steget var att Local tog bort all kod från sin sajt som använde det gamla API:et. I nästa steg kom vi in, kollade vad som nu saknades på sajten (allt!) och kopplade på vårt embryo till ett API. Nu gick det att läsa en artikel med rubrik. Men förstasidan fungerade inte. “OK, ni behöver en lista på de senaste artiklarna, det kan vi ordna”. Nu funkar förstasidan. Men blocket med fotbollsartiklar är borta. “Jasså, ni behöver ett sätt att hämta artiklar baserat på ämne, vi fixar”. Eftersom vi har lagt mycket energi på att ta fram en sund struktur för vårt data tog det aldrig lång tid för oss att lägga till den här sortens funktionalitet. I verkligheten är det såklart mer komplext. Det ska synkas med CMS-gruppen och beställare, ibland dyker något riktigt stort upp som tar ett par veckor att ordna, men det var så här vi arbetade. Efter ett par månader började den nya sajten fungera riktigt bra och vi kunde börja finputsa och optimera.

Om du jämför de två listorna ovan ser du att de beskriver två nästan helt olika behov. Local behöver bara söka efter artikeldata, och då bara baserat på fem olika fält. De behöver en lista för data av en typ. Nästan all deras graftraversering börjar i en artikel och är fokuserad på data som kan visas på deras sajt. Dessutom visade det sig att de aldrig ville få artiklarna sorterade på annat än publiceringsdatum eller senaste ändring. (Notera att Local efter att de har hämtat artiklar från vårt API gör väsentligt mer avancerad sortering baserat på användarbeteende m.m. innan listor når slutanvändarna.) Efter att vi gjort en enkel analys blev det också tydligt att en överväldigande majoritet av anropen till vårt API var från getArticles och getArticlesBySection. När vi optimerat vår databas för att klara dessa två frågor effektivt gick medianen på API:ets svarstider ner till 6–10 millisekunder under produktionslast.

Better done than perfect

Det finns en skrattretande, aningen provocerande, idé om att mjukvara skall vara “färdigutvecklad” innan den tas i bruk. Den idén framförs framför allt av beställare med begränsad kunskap om mjukvaruutveckling men den blir riktigt farlig när vi utvecklare accepterar den. Vi går tillbaka till de två listorna igen. Om vi i februari hade sagt:

“Bonnier News API för innehåll tillhandahåller endast dessa 7 metoder”

Ja, då är jag helt säker på att vissa beställare skulle säga till oss att vårt API inte var färdigutvecklat nog för deras sajt. Om vi accepterade detta och gav oss hän till att uppfylla kraven i den senare listan så hade det varit fullkomligt omöjligt för Local att hinna lansera sina nya sajter i maj.

Receptet för framgång har istället, föga förvånande, varit det motsatta. Vi utgår från behov och löser problem pragmatiskt fall för fall. Självklart inom ramarna för vår övergripande teknikstrategi.

Alla måste göra uppoffringar

getLayoutData -metoden i första listan högst upp är ett bra exempel. Layout-data är något jag rynkar på näsan åt när någon säger att de vill ha i vårt innehålls-API. Min grupp vill bidra till att öka datakvalitéten på Bonnier News. Vi vill att så mycket data som möjligt ska gå att dela mellan olika varumärken. Det ska vara enkelt för våra analytiker att jämföra data från flera produkter. Vi ska kunna leverera vår data i flera kanaler för att erbjuda våra slutanvändare ännu högre värde.LayoutData är ett arv från Locals gamla backend. Formatet är grötigt och svårt att förstå . Datan är värdelös för alla andra varumärken eftersom de har helt andra sätt att generera layout på sina sajter. Men vi har ett API som kan lagra datan och de andra alternativen för att lösa behovet tar antingen för lång tid eller bryter mot vår teknikstrategi. I det här fallet fick vår grupp bita i det sura äpplet och inkludera LayoutDatai vårt API för det var vad som behövdes för att uppnå målet. Rädda sajterna från undergång.

Ett annat exempel på pragmatiskt samarbete är getArticlesBySection. Ingen utvecklare i någon av grupperna som arbetar med CMS, API:er eller Locals sajt tycker att “sektion” är ett bra sätt att annotera artiklar med metadata. Det är mycket bättre att beskriva artiklar med metadata som har en betydelse utanför en specifik sajts navigationsstruktur. Att annotera en artikel med den sajt-specifika sektionen “Senaste nytt om Kungahuset” har lågt värde för analys och är en värdelös datapunkt för andra varumärken. Att annotera den med det globala ämnet “Kungahuset” har högt värde eftersom vi då enkelt kan göra analys och mappning till sektioner utanför kontextet av en specifik sajt. CMS-gruppen brinner för att generera artiklar med hög datakvalitet. Att låta de nya användarna från Local annotera artiklar med sektion skadar kvaliteten på deras data. Tyvärr behövdes det för att lösa behoven och komma i mål. Alla andra alternativ var sämre, så de fick bita i det sura äpplet den gången. Förhoppningsvis kommer vi kunna prioritera datakvalitet mer i framtiden och lyckas förankra vikten av det bättre i organisationen, men det är en annan bloggpost.

Så här fortsatte det iallafall mellan våra tre grupper mellan februari och idag. Oftast var vi nöjda med de lösningar vi kom fram till. Ibland fick någon uppoffra sig. Vi löste tillsammans alla viktiga krav; nytt backend, nytt CMS, analysdata, CDN för bilder, och en uppsjö små features som ingen hade en aning om behövdes när vi började. Jag har lärt mig massor av att arbeta med mina fantastiska kollegor de senaste månaderna, smått som stort. Den absolut viktigaste lärdomen är:

Om någon av våra tre grupper hade kommit till bordet med en “färdigutvecklad” produkt och krävde att de andra skulle anpassa sig, skulle vi ha misslyckats.

Om någon av våra grupper ens hade som mål att leverera sin grupps mjukvara som en “färdigutvecklad” produkt hade vi misslyckats. Ödmjukhet, öppenhet och absolut fokus på slutanvändarna (läsarna) är ett måste. Jag är stolt och tacksam över hur effektivt vi har kunnat arbeta tack vare att mina kollegor har fokuserat på våra slutanvändare istället för på en idé om sin “produkt”.

Det är jobbigt att göra rätt

Det är svårt att säga till chefer och beställare att vi inte har en färdig lösning, att vi helt enkelt inte vet hur vi ska lösa vissa problem förrän vi verkligen förstår slutanvändarnas behov. Att den bästa och snabbaste lösningen kräver att alla inblandade parter löser problemet tillsammans. Kanske kommer vi fram till att kostnaden i tid och pengar gör att vår professionella rekommendation är att inte lösa det alls. Kanske behöver vi iterera fram en lösning som vi testar på användarna om och om igen. Det är frustrerande att förklara den agila processen om och om igen. Att svara på frågan om hur långt ett snöre är för femte gången på samma möte. Det är bekvämare att acceptera absurda beställningar, att skriva kul optimerad kod som aldrig kommer leverera värde men som känns bra. Kod som under resten av veckan håller beställarna nöjda. Att njuta av hantverket och strunta helt i slutprodukten. Det har visat sig att ingen av de kollegor jag har fått arbetat med under de senaste månaderna är intresserade av att ge upp på det viset. De vet av erfarenhet att den sortens inställning slutar i projekt som aldrig blir färdiga, och de bryr sig för mycket om vår slutprodukt, det fria ordet.

Eva Hamilton, som numera är styrelseledamot i Bonnier News, gästade ett stormöte på vår utvecklingsavdelning i början av året. Hon karaktäriserade den centralisering Bonnier News genomgår som en mognadsprocess för bolag som nyligen har digitaliserats och drog paralleller till SVT.

“Men hallå i äldreboendet, vi har väl varit digitaliserade i typ 15 år!”

tänkte jag. Efter de senaste månadernas insats för att införa ett centralt CMS och innehålls-API på Local börjar jag förstå hur rätt hon har. Varken våra system eller vår kultur är idag mogen att möta de utmaningar organisationen står inför. Jag är ganska säker på att vi kan mogna i tid, och jag är övertygad att den sorts samarbete vi har haft den här våren är vägen dit.

--

--

Love Mayrhofer
Bonnier News Tech

Mjukvaruutvecklare på Bonnier News sedan 2017. Stort fan av Node.js, TDD och pressfrihet.