De små svære problemer

»There are two hard problems in Computer Science: Cache invalidation, naming
things, and off by one-errors.« –Ukendt[citation needed]

De sværeste problemer er oftest dem man ikke er opmærksom på, men først meget
sent i et projekt viser sig som uforklarlige fejl eller kode der bare ikke
længere giver mening.

Ovenstående citat, som findes i mange varianter, nævner nogle af disse
problemer som man støder på igen og igen. Selv har jeg en liste af
problemtyper jeg altid frygter at finde når jeg begynder at kikke på en gammel
kodebase:

Tidspunkter og tidsrum

Håndteringen af tid er noget der på overfladen ser så let ud at man sjældent
specificerer det nærmere. Men på et eller andet tidspunkt bliver man ramt af
tidszoner, skift til og fra sommertid eller bare at brugerens opfattelse af
'en måned' ikke svare til de 2592 kilosekunder man brugt i sin kode. Helt
elendigt bliver det hvis man skal til at kommunikere med flere systemer
der håndterer tidszoner på helt forskellige måder.

Floatingpoint og præcision

Titalssystemet og implicitte afrundinger er så indgroet hos mange af os at vi
let bliver overrasket når computerens floating point ikke lige virker som vi
forventer. Se bare dagens historie om lommeregneren i Android
Lollipop
.

Ofte kan man let omgå alle problemerne ved at lave alle sine beregninger i
heltal. Hvis man for eksempel laver økonomiske beregninger er det måske nok at
regner i et helt antal øre istedet for vilkårligt præcise tal.

Tegnsæt og Unicode

I gamle dage var det let at håndtere tekst, men så begyndte folk at insistere
på at bruge andet end ASCII (jaja, glem lige ebcdic og andre obskuriteter).
Selv Latin-1 var til at håndtere især for os der valgte at ignorere alt andet.

I dag får jeg dog kuldgysninger hver gang jeg skal til at håndtere tegnsæt.
Mange internetprotokoller er fra før tegnsæt blev et problem man rigtigt
forsøgte at håndtere og så ender man oftest med at skulle gætte sig frem.

Unicode skaber sine helt egne problemer hvor samme tegn kan repræsenteres på
forskellige måder og vidt forskellige tegn kan se helt ens ud på brugerens
skærm. Længden af en tekststreng er pludselig et tvetydigt begreb alt efter om
man regner i glyffer eller bytes og hvis man sjusker i sin håndtering af UTF-8
kan man ende med et sikkerhedsproblem.

Og endelig er der kode der påstår at kunne håndtere Unicode, men aldrig er
prøvet med andet end 'Basic Multilingual Plane' og derfor går istykker på
mystisk vis første gang man forsøger at bruge det med Unicode Emoji.

Resten af listen

Listen af små problemer man ikke rigtig tænker på i tide er selvfølgelig meget
længere. Hvilke typiske problemer står på jeres liste af fejl I frygter at
finde i gammel uvedligeholdt kode?

Kommentarer (23)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Jakob Møbjerg Nielsen

At overtage et system med store mængder rodet/inkonsistent data eller en ringe datamodel, fx hvor den nuværende ejer ikke kan redegøre for, hvordan 2 personer kan have samme CPR-nummer, selvom koden antager at det er unikt (naturligvis uden tjek for om det også er tilfældet).

Det er i øvrigt ikke et tænkt eksempel, og det var heller ikke det eneste problem. :-)

  • 1
  • 0
Christian Nobel

I klientprogrammer er præsentation af tid og dato et helvede, fordi de præsenteres forskelligt, og hvad værre er, menneskerne der ser på skærmen har en kulturel opfattelse af hvordan det skal se ud.

Eksempelvis amerikanernes fuldstændig håbløse ide om at bytte om på dag og måned kan give ufattelige kvaler, især hvis man ikke ud fra konteksten kan gætte om der er tale om den 6. juli eller 7. juni.

Decimalkomma er et andet helvede, hvor amerikanerne desværre (igen) har inficeret hele computerbranchen, fordi de jo på ingen måde vil acceptere ISO standarder - så afhængig af kontekst og forståelse kan 1.234 være ettusindetohundredefireogtredive eller 1komma234.

Helt galt er det når man skal prøve at håndtere disse ting på en webside der retter sig mod et internationalt publikum, hvor man eksempelvis skal konvertere kommaer frem og tilbage, så de passer i den bagvedliggende database, hvor man er velsignet med punktum som decimalkomma, samt lave en frygtelig masse validering.

  • 4
  • 0
Torben Mogensen Blogger

Selv i ren ASCII er der forskellige måder at repræsentere linjeskift: CR+LF, LF eller CR. For de uindviede er CR kort for "carriage return" og har ASCII koden 13, mens LF står for "line feed" og har koden 10. På gamle mekaniske teletypeterminaler rykkede CR tromlen til højre, så skrivehovedet står til venstre på linjen, mens LF roterede tromlen, så papiret rykkede en linjeafstand op. Så for at starte på en ny linje, skulle man sende begge tegn til terminalen. Der var dog visse teletypeterminaler, der ikke kunne rykke tromlen tilbage uden også at skifte linje. Disse terminaler ignorerede derfor det ene af de to tegn og gjorde begge handlinger ved det andet. Men det var ikke konsekvent, om det var CR eller LF, der blev ignoreret. Ejheller var der en standardiseret rækkefølge af de to tegn, så LF+CR forekom også.

Konsekvensen er, at der i dag ikke er en enkelt standard for linjeskift i rene tekstdokumenter: Windows bruger f.eks. CR+LF, og hvis man skriver et LF ud uden foregående CR, sætter Windows selv et CR ind. Linux og BSD bruger LF alene og de fleste gamle 8-bit maskiner (og MacOS t.o.m. OS-9) bruger CR alene. I C og mange andre sprog bliver escape-koden \n brugt til linjeskift. Idet stammer fra Unix, betyder \n ifølge standarden LF (og \r betyder CR), men i f.eks. Windows ekspanderes \n til CR+LF ved udskrift til filer.

Endvidere er der ikke en standard for, om den sidste linje i en tekst skal afsluttes med eller uden linjeskift.

Alle disse varianter giver anledning til fejl, varierende filstørrelser for samme tekst i forskellige systemer, behov for konvertering ved flytning af tekstfiler mellem Windows og Linux, osv.

  • 5
  • 0
Jacob Christian Munch-Andersen

Man skriver en streng ved at sætte den mellem to "-tegn, hvis man gerne vil have et "-tegn i strengen skal man skrive \ foran, og således er det simpelt at sikre at en streng er escapet korrekt før den eksempelvis indsættes i et stykke kode:

kodeStreng = "\"" + streng.replace("\"", "\\\"") + "\""

Hvis man får en streng som allerede er escapet skal man blot sørge for at tjekke at det er gjort korrekt:

if(streng.match(/(?<!\\)\"/)){
  //Håndter ukorrekt escapet streng
}

Hvorfor er det egentlig at jeg nævner escape sekvenser? De er jo tydeligvis ikke særligt svære at arbejde med.

  • 3
  • 0
Carsten Hansen

Se
http://martinfowler.com/bliki/TwoHardThings.html

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

Long a favorite saying of mine, one for which I couldn't find a satisfactory URL.

There is also a variation on this that says there are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.

  • 1
  • 0
Peter Makholm Blogger

Ja, jeg fandt også Martin Fowlers side og flere andre sider der siger at Phil Karlton muligvis er kilden til en eller anden variation. Og sådan er det ofte med kildeangivelsen til den slags citater, der er sjældent nogen der kan finde hvilken kontekst citatet oprindeligt er faldet i og om vedkommende virkelig er den der oprindeligt har fundet på talemåden.

  • 2
  • 0
Thomas Peter Berntsen

Åh, ja.

Følgende begreber har af tilsvarende grunde sneget sig ind virksomhedens vokabularium:
"Tegnsætshelvede", "datoforvirring" og "linieknæk" (gæt selv).

Dernæst kommer "datakvalitet" - ofte skarpt efterfulgt af et gedigent latterbrøl. :-D

  • 0
  • 0
Peter Makholm Blogger

Hvorfor er det egentlig at jeg nævner escape sekvenser? De er jo tydeligvis ikke særligt svære at arbejde med.

Nej, det kræver jo bare at man holder tungen lige i munden når man arbejder med tekststrenge der bliver evalueret af flere forskellige lag. Tror aldrig jeg har haft brug for mere end 8 på hinanden følgende backslashes i produktionssystemer...

  • 4
  • 0
Mark Gjøl

Læsbarheden af kode. Jeg ser ofte projekter hvor folk aldrig skriver kommentarer, gerne bruger intetsigende variabelnavne og kilometerlange metoder. Det er en kunst at lave læsbar kode og desværre ikke altid en kunst alle bryder sig om.

Derudover: Lokalisering er et næsten umuligt projekt, hvis man skal have alle grammatiske finurligheder med.

  • 1
  • 0
Gorm Jensen

Brug kun a-z og nogle få specialtegn i filnavne. Andre tegn kan give de mærkeligste problemer. Og husk også på at Unix skelner mellem store og små bogstaver, det gør Windows ikke.

Hvis binærkodning af en XML-fil ikke stemmer overens med kodningen anigvet i XML-hovedet, så fejler indlæsningen. Det er kan ødelægges ved at redigere filen i en teksteditor.

Hvordan bestemmes om en dato ikke er initialiseret? 0, null, noget andet. Det bliver så til 01-01-0001, 31-12-1899, 1970, ...

Test altid systemer med datoer efter den trettende i måneden. Det går ofte godt i den første del af måneden.

  • 0
  • 0
Peter Holm Jensen

er det værste når koden bruges til at time med, så skal man ikke engang tænke på at skifte et enkelt flag til compileren, eller prøve at bruge en anden version end den koden er genereret med. Prøv lige at omskrive det til noget brugbart, så hellere fjolle lidt med UFT-8 eller datoen ;-)

  • 2
  • 0
Allan Ebdrup Blogger

Det værste at se i gammel legacy-kode er grundlæggende fejl i arkitekturen, der nu gennemsyrer hele systemet. Det er for eksempel den slags fejl der gør, at det at vise en enkelt side i en webapp laver 10.000 database-opslag, eller noget i den stil. Joh, det sker oftere end du tror.

Og så der der som regel indført noget caching for at afhjælpe nogle a performance-problemerne og så er du for alvor ude på dybt vand. Skynd dig væk!

  • 1
  • 0
Johnni Rindom

Prøv at indlæse en almindelig csv export fra en SQL server 2005 på en linux server - var ved at hive hovedet af mig selv indtil en lidt for smart fyr jeg kender sagde at windows udlæser som standard i utf-16 - og linux læser som standard i utf8

  • 1
  • 0
Thomas Peter Berntsen

Hvad mener du? Vores data er da lige til at læse, som du selv kan se står de fint i rækker og kolonner i dette Excel regneark.
De grønne celler? Jo, det viser at værdien stammer fra det her andet ark. For det meste. Der er selvfølgelig et par undtagelser...

"Spreadsheet databases" FTW... 8-D

  • 0
  • 0
Nikolaj Koch

1) Fejl i 3. partssoftware hvor sourcen ikke er tilgængelig.

2) Små memory leaks som bliver udløst af sjældne code paths og som langsomt bygger op - og som undslap statisk kodeanalyse og test, og pludselig viser sig i produktion.

3) Hertil generelt "heisenbugs" o.lign. - fejl som ikke kan fremprovokeres under kontrollerede forhold. Og som aldrig ses i testmiljløer eller miljøer med logning slået til.

4) Under pkt. 3 hører naturligvis de klassiske synkroniseringsproblemer, race-conditions m.m.

5) Uventede performanceproblemer og flaskehalse. Komponenter der i produktion ikke skalerer som ventet.

6) Mødet med slutbrugeren: Trods indgående undersøgelser inden projektet, viser det sig at slutbrugerens forventninger behov og workflow er anderledes end det der er blevet udviklet, så dette i praksis er ubrugeligt.

  • 1
  • 0
Carsten Hansen

Jeg læser lige en bog fra 1995, hvor forfatteren omtaler oprindelsen til linieskift CrLf.

Heldigvis har samme forfatter en tilsvarende beskrivelse på sin hjemmeside:
http://www.oualline.com/practical.programmer/eol.html

The End of Line Puzzle

Back in the dark ages BC (Before Computers), there existed a magical device called a Teletype Model 33. This amazing machine contained a shift register made out of a motor and a rotor as well as a keyboard ROM consisting solely of levers and springs.

It contained a keyboard, a printer and a paper tape reader/punch. It could transmit messages over the phones using a modem at the blazing rate of 10 characters a second.

The Teletype had a problem. It took 2/10 second to move the printhead from the right side to the left. 2/10 second is two character times. If a second character came while the printhead was in the middle of a return, it was lost.

The Teletype people solved this problem by making end of line two characters: <carriage return> to position the printhead at the left margin, and <line feed> to move the paper up one line. That way the <line feed> "printed" while the printhead was racing back to the left margin.

When the early computers came out, some designers realized that using two characters for end of line wasted storage (at this time storage was very expensive). Some picked <line feed> for their end of line, some <carriage return>. Some of the die hards stayed with the two-character sequence.

UNIX uses <line feed> for end of line. The newline character '\n' is code 0xA (LF or <line feed>).

MS-DOS/Windows uses the two characters: <line feed><carriage return>. Compiler designers had a problem; what to do about the old C programs which thought that newline was just <line feed>? The solution was to add code to the I/O library that stripped out the <carriage return> characters from ASCII input files and changed <line feed> to <line feed> <carriage return> on output.

Hastigheden var dog ifølge Wiki langt hurtigere end 10 tegn i sekundet, så forklaringen holder vel i bedste fald kun for en tidlig prototype.

Se
http://en.wikipedia.org/wiki/Teletype_Model_33

Modellen var den første med Ascii-tegnsæt og blev brugt, da Basic blev skabt og siden af Unix-pionererne. Bill Gates lærte senere at programmere vha. modellen. Den blev produceret indtil 1981, men /dev/tty og totegns-linieskift lever endnu.

  • 0
  • 0
Log ind eller Opret konto for at kommentere