Dags att lösa miljöproblemen!

Systemutvecklare tacklas ofta med miljöproblem i sin vardag. Denna artikel handlar om vad det oftast beror på och ger exempel på hur det kan hanteras genom att separera det enkla från det komplexa i problemet.

Som systemutvecklare finns det ett problem som alltid tycks återkomma. Även om du jobbar enligt konstens alla regler. Du jobbar agilt, med continous delivery och continous integration. Du har enastående testtäckning. Koden är väldokumenterad. Även om du skulle jobba under de bästa förutsättningarna tycks du alltid ha ett problem som återkommer. Problemet med miljön. Då menar jag inte den smältande polarisen utan uppdateringar i Mountain Lion, nya versioner av Java, uppdaterad IDE…you name it! Nya eller uppdaterade komponenter som på nåt vis korrumperar helheten. För precis som växthusgaserna delvis är en ny komponent i vår globala miljö som orsakar oanade konsekvenser för tillsynes orelaterade system som polarisar, kan en ny version av Java, Ruby eller vad det nu vara månde korrumpera tillsynes orelaterade delar i den miljö som en systemutvecklare tänker på när den hör ordet miljö: utvecklingsmiljön. D.v.s. maskinen och dess ingående komponenter (IDE, ramverk, servrar et.c.) som systemutvecklaren använder i sin dagliga utveckling. Det är om denna miljö och dess problem som denna artikel handlar om.

Foto: Rita Willaert.
Foto: Rita Willaert.

Lösa globala miljöproblem lokalt (Det fungerar på min maskin!)

Hur ska man då lösa problemet med miljön? Ett sätt är att dokumentera en fungerande miljö via en wiki exempelvis. Att beskriva hur en miljö för ett specifikt system skall se ut och hur man går till väga för att installera de ingående komponenterna. Tyvärr håller det oftast enbart ett tag. Wikin blir ganska snart överfull med kommentarer (”funkar ej med Java 7”, ”ej testat”, ”bara på Mac OS”), överstrykningar och inaktuell information. Nya komponenter har med tiden lagts in, antingen medvetet eller omedvetet p.g.a. automatiska uppdateringar eller liknande och dessa förändringar har inte förts in i wikin. Dessutom kan väldigt lätt felaktigheter smyga sig in. Någon redigerar instruktionerna men glömmer att det inte gäller majoriteten av användarna eftersom majoriteten använder ett annat operativsystem exempelvis. Till slut hamnar man i ”Det fungerar på min maskin”-fällan. Någon har en maskin som systemet fungerar på men ingen, inklusive maskinens ägare, vet varför.

En annan variant är att virtualisera de miljöer som fungerar. Att t.ex. göra en klon på den maskin som fungerar och dela denna klon med samtliga som behöver använda den specifika miljön. Detta kan låta som ett utmärkt förslag och det fungerar ofta bra. Ett tag. Ganska snart får dock respektive klon ett eget liv hos respektive användare med nya beroenden och snart har man en ohanterlig soppa av olikartade miljöer och man är tillbaka på ”det fungerar på min maskin”. Även om man inte gör några lokala förändringar i den klonade miljön så kommer man sakna spårbarheten. Vad är unikt för denna miljö? På vilket sätt skiljer sig denna miljö från ett rent operativtsystem utan modifieringar? Om man vill replikera miljön på andra miljöer, exempelvis andra operativsystem, blir det mycket svårt att se vad som gör miljön unik. Plötsligt är man återigen tillbaka på ”det fungerar på min maskin” kombinerat med ”…och jag har ingen aning varför”.

Att låta det enkla vara enkelt

Grundproblemet i ovanstående scenarior är bristen på vad man inom systemutveckling kallar Separation of concerns (enkelt beskrivet: håll isär saker i ett system som på ett abstraktionsplan inte har med varandra att göra). Detta underlättar saker som modularitet, tydlighet och framförallt möjligheten att underhålla system över tid. Hur skulle vi kunna nyttja designmönstret Separation of concerns för att lösa stora delar av miljöproblemen? Om vi abstraherar problemet så består våra miljöer i grunden av två saker: en maskin och ett antal regler som beskriver hur den maskinen skall vara konfigurerad. Om man accepterar den abstraktionen och funderar vad systemet då består av så inser man att komplexiteten ligger i konfigureringen och inte i maskinen. Maskinen är en ’dum’ behållare som styrs av den komplexa konfigureringen. Oavsett vilken miljö jag använder kan jag enkelt svara Ja eller Nej på frågan om jag kör Ubuntu version 13.04, men om frågan blir hur maskinen är konfigurerad blir det plötsligt mer komplext.

Människa konfigurerar maskin
Människa konfigurerar maskin. Foto: Michael Coghlan

Man vs. Machine – Om att tala till bönder på bönders vis och med datorer på datorers vis

Om konfigureringen är det komplexa i problemet och inte maskinen i sig, så bör man också ha i beaktande att konfigureringen i slutändan skall tolkas av den mindre komplexa maskinen och inte av en människa för att i slutändan utgöra en miljö (den bör möjligen vara läsbar av en människa men i slutändan består konfigureringen alltid av något som maskinen tolkar). Med det i åtanke blir plötsligt en wiki eller övrig ordinär skriftlig dokumentation olämpliga medium. I dokumentationen kan jag skriva: ”Steg 1. Installera Java”. Problemet är att jag som människa i mitt läsande av informationen tolkar den, överför sedan min tolkning till maskinen och plötsligt har vi en sannolik felkälla: Ska jag installera Java 6 eller 7? Standard edition eller enterprise edition? Är det JDK’n jag skall installera eller bara JRE’n? Bör jag installera stöd för Java i webbläsaren, i operativet eller både och? Vi människor är bra på att tolka instruktioner på olika sätt vilket gör oss olämpliga att konfigurera maskiner på ett enhetligt sätt. Om maskinen är vad som skall konfigureras och maskinen för detta kräver tydliga instruktioner, vore det då inte bättre att dokumentera de instruktioner maskinen kan förstå snarare än att försöka dokumentera instruktioner till oss människor som sannolikt kommer tolkas på olika sätt?

I dagläget hanterar vi systemutvecklare redan instruktioner till maskiner, källkod, i versionshanteringssystem för att kunna följa hur koden utvecklas över tid. Vore det inte trevligt om vi på samma sätt kunde flytta den komplexa konfigureringen av maskinerna till versionshanteringssystemet, låta maskinerna vara ’dumma’ mottagare av dessa instruktioner och på så sätt erbjuda en miljö som alltid är uppdaterad för alla? Då blir komplexiteten plötsligt åskådliggjord över tid och vi kan beskriva miljön på ett enkelt sätt som ”Ubuntu 13.04 64-Bit konfigurerad med HEAD revision från Bitbucket”, ett sätt som både människa (läs: systemutvecklare) och maskin förstår. I nästa bloggpost ska jag beskriva hur man enkelt kan göra just detta med hjälp av öppna verktyg som VirtualBox, Vagrant och ett valfritt versionshanteringssystem!