{"id":45,"date":"2026-07-21T20:00:00","date_gmt":"2026-07-21T18:00:00","guid":{"rendered":"https:\/\/airex.se\/blogg\/res-mer-nu-tva-varumarken\/"},"modified":"2026-08-04T16:26:39","modified_gmt":"2026-08-04T14:26:39","slug":"res-mer-nu-tva-varumarken","status":"publish","type":"post","link":"https:\/\/airex.se\/blogg\/res-mer-nu-tva-varumarken\/","title":{"rendered":"Tv\u00e5 varum\u00e4rken ur en kodbas \u2014 och v\u00e4ggen som ledde n\u00e5gon annanstans"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Samma kodbas driver tv\u00e5 sajter med olika namn, olika f\u00e4rger och olika ordval. Den ena arrangerar racingresor, den andra gruppresor. Under ytan \u00e4r det samma program.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Det intressanta visade sig inte vara temalagret. Det var att jag gick in i en v\u00e4gg som fick mig att bygga n\u00e5got helt annat.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ett tema \u00e4r mer \u00e4n f\u00e4rger<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Temavalet sker vid bygget: samma k\u00e4llkod kompileras tv\u00e5 g\u00e5nger med olika flagga, och resultatet l\u00e4ggs p\u00e5 tv\u00e5 separata instanser. Temat styr tre saker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>F\u00e4rgerna<\/strong>, vilket \u00e4r det uppenbara och det minst viktiga.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Lexikonet<\/strong>, vilket \u00e4r det som faktiskt betyder n\u00e5got. Samma lista heter <em>startlista<\/em> p\u00e5 den ena sajten och <em>deltagare<\/em> p\u00e5 den andra. Samma knapp s\u00e4ger &#8221;nu rullar vi&#8221; respektive &#8221;nu \u00e5ker vi&#8221;. Samma funktion heter <em>Race Control<\/em> i det ena fallet och <em>Reseledningen<\/em> i det andra.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Det \u00e4r inte kosmetika. En f\u00f6rening som ska p\u00e5 bussresa till \u00d6land k\u00e4nner sig inte hemma i n\u00e5got som pratar om startlistor och Race Control, hur bra funktionerna \u00e4n \u00e4r. Och omv\u00e4nt: den som ska k\u00f6ra Nordschleife vill inte ha &#8221;reseledningen&#8221;. Ordvalet avg\u00f6r om produkten k\u00e4nns gjord f\u00f6r dig eller om du anv\u00e4nder n\u00e5gon annans verktyg.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Funktionsflaggor<\/strong>, som st\u00e4nger av det som inte h\u00f6r hemma. Fartvisning finns i racingversionen och \u00e4r avst\u00e4ngd i resversionen \u2014 inte f\u00f6r att den inte fungerar, utan f\u00f6r att en buss inte beh\u00f6ver den, och att visa den vore att s\u00e4nda fel signal om vad produkten \u00e4r.<\/p>\n\n\n\n<figure class=\"airex-fig\">\n<svg viewBox=\"0 0 760 300\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\"\n     aria-label=\"En kodbas byggs tv\u00e5 g\u00e5nger med olika tema och blir tv\u00e5 varum\u00e4rken\">\n  <style>\n    .box  { fill: none; stroke: #0e1a1c; stroke-width: 1.5; }\n    .accent { fill: none; stroke: #a5381f; stroke-width: 1.5; }\n    .t   { font-family: \"Familjen Grotesk\", system-ui, sans-serif; font-weight: 700; font-size: 15px; fill: #0e1a1c; }\n    .m   { font-family: \"IBM Plex Mono\", ui-monospace, monospace; font-size: 10.5px; fill: #78898a; letter-spacing: .08em; }\n    .b   { font-family: Newsreader, Georgia, serif; font-size: 13px; fill: #3a4c4d; }\n    .r   { fill: #a5381f; font-family: \"IBM Plex Mono\", ui-monospace, monospace; font-size: 10.5px; letter-spacing: .08em; }\n    .arrow { stroke: #0e1a1c; stroke-width: 1.5; fill: none; }\n  <\/style>\n  <defs>\n    <marker id=\"a\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto\">\n      <path d=\"M0 0 L10 5 L0 10 z\" fill=\"#0e1a1c\"\/>\n    <\/marker>\n  <\/defs>\n\n  <rect class=\"box\" x=\"20\" y=\"110\" width=\"170\" height=\"80\"\/>\n  <text class=\"t\" x=\"38\" y=\"142\">En kodbas<\/text>\n  <text class=\"m\" x=\"38\" y=\"164\">SAMMA LOGIK<\/text>\n\n  <path class=\"arrow\" marker-end=\"url(#a)\" d=\"M190 135 L275 60\"\/>\n  <path class=\"arrow\" marker-end=\"url(#a)\" d=\"M190 165 L275 240\"\/>\n  <text class=\"r\" x=\"196\" y=\"92\">TEMA=RACING<\/text>\n  <text class=\"r\" x=\"196\" y=\"232\">TEMA=RESA<\/text>\n\n  <rect class=\"box\" x=\"280\" y=\"22\" width=\"180\" height=\"76\"\/>\n  <text class=\"t\" x=\"298\" y=\"52\">Racingsajten<\/text>\n  <text class=\"b\" x=\"298\" y=\"74\">startlista \u00b7 Race Control<\/text>\n\n  <rect class=\"box\" x=\"280\" y=\"202\" width=\"180\" height=\"76\"\/>\n  <text class=\"t\" x=\"298\" y=\"232\">Resebyr\u00e5n<\/text>\n  <text class=\"b\" x=\"298\" y=\"254\">deltagare \u00b7 Reseledningen<\/text>\n\n  <path class=\"arrow\" marker-end=\"url(#a)\" d=\"M460 60 L545 60\"\/>\n  <path class=\"arrow\" marker-end=\"url(#a)\" d=\"M460 240 L545 240\"\/>\n\n  <rect class=\"accent\" x=\"550\" y=\"22\" width=\"150\" height=\"76\"\/>\n  <text class=\"t\" x=\"568\" y=\"52\">Instans A<\/text>\n  <text class=\"m\" x=\"568\" y=\"74\">EGEN DEPLOY<\/text>\n\n  <rect class=\"accent\" x=\"550\" y=\"202\" width=\"150\" height=\"76\"\/>\n  <text class=\"t\" x=\"568\" y=\"232\">Instans B<\/text>\n  <text class=\"m\" x=\"568\" y=\"254\">EGEN DEPLOY<\/text>\n\n  <path stroke=\"#a5381f\" stroke-width=\"1.5\" stroke-dasharray=\"5 4\" fill=\"none\" d=\"M625 98 L625 132 M625 172 L625 202\"\/>\n  <text class=\"r\" x=\"625\" y=\"152\" text-anchor=\"middle\">H\u00c5LL I SYNK<\/text>\n<\/svg>\n<figcaption>En kodbas, tv\u00e5 byggen, tv\u00e5 varum\u00e4rken \u2014 och tv\u00e5 driftsatta instanser som m\u00e5ste h\u00e5llas i takt.<\/figcaption>\n<\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">F\u00e4llan med tv\u00e5 instanser<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En kodbas, tv\u00e5 driftsatta kopior. Det l\u00e5ter oskyldigt och har en uppenbar f\u00f6ljd som jag \u00e4nd\u00e5 gick p\u00e5 tv\u00e5 g\u00e5nger: <strong>en ny funktion som bara drifts\u00e4tts p\u00e5 den ena sajten finns inte p\u00e5 den andra.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00f6rsta g\u00e5ngen var en AI-funktion som l\u00e5g kvar p\u00e5 gamla versionen i en vecka p\u00e5 den ena sajten. Andra g\u00e5ngen var hela den digitala reseledningen \u2014 checklistor och uppdateringar om trafik och \u00e4ndringar \u2014 som fungerade p\u00e5 racingsajten medan resesajten l\u00e5g kvar utan den.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Regeln som v\u00e4xte fram: <strong>ingen sajtspecifik kod i en commit.<\/strong> Skiljer sig sajterna \u00e5t ska skillnaden ligga i temat, aldrig i logiken. D\u00e5 kan exakt samma bygge g\u00e5 till b\u00e5da instanserna, och fr\u00e5gan &#8221;\u00e4r de i synk?&#8221; blir en j\u00e4mf\u00f6relse av tv\u00e5 versionsnummer i st\u00e4llet f\u00f6r en genoml\u00e4sning.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Och s\u00e5 det som verkligen var sv\u00e5rt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">B\u00e5de varianterna har ett live-l\u00e4ge: under resan syns deltagarnas position p\u00e5 en karta, s\u00e5 man ser om konvojen h\u00e4nger ihop och vem som stannat f\u00f6r att tanka.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Det \u00e4r en av de d\u00e4r funktionerna som \u00e4r triviala att demonstrera och n\u00e4rmast om\u00f6jliga att f\u00e5 att fungera p\u00e5 riktigt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>En webbapp kan inte l\u00e4sa position n\u00e4r sk\u00e4rmen \u00e4r sl\u00e4ckt.<\/strong> B\u00e5de iOS och Android fryser sidans kod n\u00e4r telefonen l\u00e5ses. Det \u00e4r inte en bugg, det \u00e4r hela po\u00e4ngen med hur mobiler sparar batteri \u2014 och det inneb\u00e4r att positionsdelningen d\u00f6r i samma sekund f\u00f6raren stoppar telefonen i fickan.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Det finns ett API f\u00f6r att h\u00e5lla sk\u00e4rmen vaken, och det anv\u00e4nds. Men d\u00e5 lyser sk\u00e4rmen hela resan och \u00e4ter batteri, i en bil, ofta utan laddare. L\u00f6sningen blev ett m\u00f6rkerl\u00e4ge: en n\u00e4stan svart sk\u00e4rm med bara klocka och hastighet, sn\u00e5lt p\u00e5 en OLED-sk\u00e4rm, som h\u00e5ller sidan vaken utan att lysa upp kup\u00e9n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Till det kommer en tyst felk\u00e4lla: n\u00e4r fliken v\u00e4cks efter vilol\u00e4ge kan positionsbevakningen ha d\u00f6tt utan att s\u00e4ga n\u00e5got. Den ser aktiv ut och levererar aldrig mer. Fixen \u00e4r att starta om den varje g\u00e5ng fliken blir synlig \u2014 man kan inte lita p\u00e5 att den \u00f6verlevde.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Slutsatsen var obekv\u00e4m: <strong>\u00e4kta sp\u00e5rning med l\u00e5st telefon kr\u00e4ver en riktig app<\/strong> med bakgrundsr\u00e4ttigheter. Det \u00e4r parkerat, medvetet, f\u00f6r det \u00e4r ett helt annat \u00e5tagande \u00e4n en webbsida.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">H\u00e5let i kartan<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Den l\u00e5sta sk\u00e4rmen var \u00e4nd\u00e5 ett problem man kan leva med. Den som k\u00f6r bil kan l\u00e4gga telefonen i en h\u00e5llare.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Det verkliga h\u00e5let visade sig n\u00e4r resan inte gick p\u00e5 v\u00e4g.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En gruppresa d\u00e4r deltagarna flyger har ett f\u00f6nster p\u00e5 flera timmar d\u00e4r kartan helt enkelt \u00e4r tom f\u00f6r dem. Fr\u00e5n det att telefonen g\u00e5r i flygl\u00e4ge vid gaten till att den kopplar upp mot ett n\u00e4t p\u00e5 destinationen finns ingen position att dela. Inte f\u00f6r att appen \u00e4r d\u00e5lig \u2014 utan f\u00f6r att det \u00e4r <em>regeln<\/em>. Det finns ingen fix, ingen beh\u00f6righet att be om, ingen bakgrundstj\u00e4nst som hj\u00e4lper.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Och det \u00e4r precis den str\u00e4ckan man vill se. Hela po\u00e4ngen med en reseledning \u00e4r att veta var folk befinner sig, och den timme man verkligen undrar \u00e4r den d\u00e5 de sitter i luften.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">V\u00e4ndningen: sp\u00e5ra farkosten, inte passageraren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">N\u00e4r man stirrat p\u00e5 det tomma f\u00e4ltet n\u00e5gra g\u00e5nger v\u00e4nder fr\u00e5gan sig sj\u00e4lv. Jag f\u00f6rs\u00f6kte f\u00e5 <em>personen<\/em> att rapportera var hon var. Men personen sitter i n\u00e5got som redan s\u00e4nder sin position \u2014 h\u00f6gt, tydligt och oavbrutet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Varje trafikflygplan skickar ut identitet, h\u00f6jd, fart och koordinater flera g\u00e5nger i sekunden, okrypterat, till vem som helst som lyssnar. Ingen app, inget tillst\u00e5nd, ingen batteridiskussion, inget flygl\u00e4ge som st\u00e4nger av det.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vet man flightnumret vet man allts\u00e5 var deltagarna \u00e4r \u2014 inte p\u00e5 metern, men fullt tillr\u00e4ckligt f\u00f6r en karta som ska svara p\u00e5 &#8221;var \u00e4r de nu, och n\u00e4r landar de?&#8221;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Det kr\u00e4ver en mottagare, en antenn och en hel del l\u00e4rdomar om radio. S\u00e5 jag byggde en: <a href=\"https:\/\/ftrack.mer.nu\/\">ftrack.mer.nu<\/a>. Den historien f\u00e5r ett eget inl\u00e4gg, f\u00f6r d\u00e4r v\u00e4ntade en helt annan sorts f\u00e4lla \u2014 och en obehaglig l\u00e4xa om att lita p\u00e5 ett spec-blad.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Vad jag tar med mig<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ett varum\u00e4rke \u00e4r billigare \u00e4n en produkt.<\/strong> Att g\u00f6ra en andra sajt av samma kod kostade ett temalager och en byggflagga. Att g\u00f6ra en andra <em>produkt<\/em> hade kostat allt. Skillnaden ligger i disciplinen att aldrig l\u00e4gga sajtspecifik logik i koden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Orden \u00e4r en del av funktionen.<\/strong> Jag underskattade hur mycket lexikonet g\u00f6r. Samma sk\u00e4rm med bytt vokabul\u00e4r k\u00e4nns som tv\u00e5 olika produkter f\u00f6r anv\u00e4ndarna \u2014 och det \u00e4r i praktiken det de \u00e4r.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Och den b\u00e4sta id\u00e9n kom ur en funktion som inte gick att bygga.<\/strong> Det tomma f\u00e4ltet i kartan n\u00e4r deltagarna flyger g\u00e5r inte att fylla med mer m\u00f6da p\u00e5 telefonsidan \u2014 flygl\u00e4get \u00e4r inte ett hinder att kringg\u00e5, det \u00e4r ett villkor. F\u00f6rst n\u00e4r jag slutade f\u00f6rs\u00f6ka \u00f6vertala apparaten och fr\u00e5gade vad som redan s\u00e4nder frivilligt fanns det ett svar. Det satt i luften ovanf\u00f6r huset.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Temalagret var den l\u00e4tta delen. Det sv\u00e5ra var h\u00e5let i kartan n\u00e4r deltagarna flyger \u2014 flygl\u00e4ge betyder ingen position, och det finns inget s\u00e4tt runt det. L\u00f6sningen l\u00e5g n\u00e5gon annanstans.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-45","post","type-post","status-publish","format-standard","hentry","category-bygget"],"_links":{"self":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/posts\/45","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/comments?post=45"}],"version-history":[{"count":2,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/posts\/45\/revisions"}],"predecessor-version":[{"id":49,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/posts\/45\/revisions\/49"}],"wp:attachment":[{"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/media?parent=45"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/categories?post=45"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/airex.se\/blogg\/wp-json\/wp\/v2\/tags?post=45"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}