Mediawiki som datalager

lagen.nu använder jag ingen
relationsdatabas. Alla dokument ligger i varsin statisk fil, och den
samling av metadata som behövs för att jag ska kunna exempelvis skapa
index över alla dokument ligger i en gigantisk .n3-fil som jag går
igenom med RDFLib. Anledningen är
förstås min djupt
kända misstro
mot konceptet databasserver i allmänhet och
relationsdatabaser i synnerhet. Jag tycker filsystemet är underskattat
som databas. Det finns alltid där, är snabbt, går att debugga och
manipulera med välbekanta verktyg (ls, find,
grep, xargs, tar, rsync), har ett
begripligt rättighetssystem integrerat med operativsystemet, osv.

Till nästa stora iteration av lagen.nu-koden kommer jag ändå att
börja använda någon sorts server för datalagring. Men det blir
förmodligen inte en traditionell relationsdatabas med
SQL-gränssnitt. Min datamodell är inte särskilt relationell. Med tanke
på hur djupt jag integrerat RDF i systemet blir det förmodligen en
kombination av en triplestore tillsammans med någon form av dokumentdatabas.
För det tidigare blir det förmodligen Sesame, för det senare har jag
tittat nyfiket på CouchDB.

Men i kommentarerna till ett tidigare
inlägg
om en mediaövervakningsbot föreslog Peter Krantz att använda Mediawiki som
datalagring. Jag började kolla på hur man kan automatisera hämtning
och lagring av data från en Mediawikiserver, och det visar sig att det
finns en mycket kompetent pythonmodul, mwclient, för
ändamålet (det finns även ett annat ramverk, pywikipedia,
men det gav inte alls ett lika bra första intryck). Så här enkelt är
det att skapa och ändra en sida:

    import mwclient
    
    site = mwclient.Site('www.example.org','/path/to/mediawiki/')
    site.login('myuser','secret')
    page = site.Pages['Testpage']
    page.save("Hello world", summary="initial version")
    page.save("Goodbye world", summary="updated version")
    print "Page has %d revisions" % len(page.revisions())

Allt det som mediawiki ger — revisionshantering med diffar,
användarhantering, admin- och slutanvändargränssnitt, spamkontroller,
roll- och rättighetssystem, och utökningsmöjligheter
— får man på köpet. Det är kanske inget man vill använda för att
hantera jättemånga updateringar i sekunden, men om man kan se till att
exv cachea de anrop som bara hämtar data kan man nog få det snabbt
nog.

Lagstiftaren borde lära sig av webben

There are no reasons at all in theory for people to change URIs (or stop maintaining documents), but millions of reasons in practice

2003 ändrade riksdagen en av de mest centrala lagar vi har, riksdagsordningen (RO). RO är ett mellanting mellan grundlag och vanlig lag, och ändras inte utan vidare. När det gäller en sådan lag tycker man att man borde gå försiktigt fram när man ändrar den, inte minst vad det gäller kapitel- och paragrafnumreringen.

Jag menar, det finns sjukt mycket material som refererar till RO. Om någon skriver ”Enligt RO 3:18 så gäller […]” så vill man gärna att den referensen ska gälla i överskådlig tid.

På både http://blog.tomtebo.org/ och http://lagen.nu/ har jag gjort mitt bästa för att länkar ska funka över tid; om någon är snäll nog att länka till någon av mina blogpostningar eller någon av lagarna på lagen.nu, så vill jag att den länken ska funka, åtminstone så länge domänerna blog.tomtebo.org och lagen.nu är under min kontroll.

Riksdagen har RO under sin kontroll. Trots det valde man i 2003, iom SFS 2003:180 att numrera om stora delar av RO. Det var en del i arbetet att modernisera språket i RO, vilket i sig är en god tanke. I programmeringsvärlden kallar vi sådant ”refactoring”, när syftet är ”changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure”. Det betraktas i allmänhet som en God Sak.

Problemet är att moderniseringen av RO även ändrade det yttre beteendet (”external behaviour”). All gammal juridisk text (ffa doktrin och rättsfall) är plötsligt inte att lita på. Om man läser ”Enligt RO 3:18 så gäller […]” så måste man veta om texten skrevs före eller efter den 1:a juli 2003, eftersom det påverkar innehållet i texten.

Har du någonsin fått felmeddelandet ”The requested URL /[...] was not found on this server.”? Det beror på att någon glömt att URL:er1 aldrig skall ändras. Det är egentligen aldrig OK att lämna felkod 404 för en URL som en gång har fungerat (jag har brutit mot denna regel fler gång än jag kan räkna, men jag har lärt mig av mina misstag). Det enda som är acceptabelt är 301 (Moved permanently) eller 410 (Gone) (eller möjligtvis 307 (Temporary Redirect) i vissa fall). Det är sånt som gör att webben funkar dåligt.

När lagstiftaren numrerar om lagar funkar juridiken dåligt. Det gör att massor av läroböcker, artiklar, rättsfall och andra rättskällor plötsligt har felaktiga referenser. Det här problemet finns på många andra ställen än RO, jag tar bara upp just det här exemplet eftersom jag läser statsrätt just nu och RO är en rätt viktig lag.

Vad som är intressant är att webben bara funnits i tio år. Tim Berners-Lee och andra arkitekter bakom webben förstod det här problemet från dag ett. Juridik har funnits i flera tusen år och har fortfarande inte förstått problemet.

På måndag går jag tillbaka till det rena tekniksnacket och orerar över MacOS X hantering av Unicode i filsystemet. Användbar pythonkod utlovas!

1: …eller URI:er. Skillnaden mellan URL:er och URI:er är intressant för en mycket mycket liten del av mänskligheter. Nån annan dag ska jag ranta om de som använder det ena uttrycket när de menar det andra, och vice versa.

Standardbaserad DHTML-programmering

När man vill etikettera en lag på lagen.nu och klickar på ”Ändra” så skickas man inte till ett inmatningsformulär på en annan sida1; istället görs den beiga rutan med existerande etiketter om till ett inmatningsformulär, med tangentbordsfokus satt i textrutan. Man behöver inte ens klicka på ”Ändra”, det räcker med <acceleratorknapp>2+Ä. Det där är lite DHTML-magi av enklaste sorten, men just nu är det den roligaste programmeringen jag vet.

Vilket jag inte hade trott för fem år sedan. Jag hade lekt med Javascript sedan Netscape 2.0-tiden (då man i princip bara kunde ändra text i inmatningsfält) och det har alltid känts härkigt. Javascript var ett fult språk och ”API”:et var dåligt, ostandardiserat och hade sjukt många olika egenheter på olika webläsar/operativsystemplattformar. Debugmöjligheter var begränsade till ”debug by printfalert()”. När allting eskalerade i en inkompatibilitetens kapprustning nånstans runt 4.0-versionerna av IE och NS fick jag nog.

Men idag ser världen annorlunda ut. API:et (DOM) är rätt smutt. Webläsarna stödjer det överlag ganska bra, och kompatibilitetsproblemen är överlag kända och runtarbetade. Och den fula ankungen till språk som var javascript har plötsligt blivit, om än inte en svan, så åtminstone ett ganska snyggt dynamiskt typat språk med objektorientering, högre ordningens funktioner och closures och grejjer. Och som kronan på verket har den nya häftiga buzzakronymen AJAX kommit att nästan ersätta det gamla skamfilade ”DHTML”.

För ett tag sedan såg jag en recension på slashdot av boken ”DHTML Utopia: Modern Web Design Using JavaScript & DOM”, och, eftersom jag redan lurade på lite fräcka summer of 2005-funktioner beställde jag den. Det är en jättebra bok. Inte för tjock, inte en referensbok, utan bara en koncis guide till vad man egenligen kan göra med javascript nuförtiden. Boken håller sig till stora delar borta från flashiga effekter och förklarar ganska grundligt exakt hur de mer grundläggande sakerna i språket verkligen funkar.

Det stora aha-ögonblicket kom när jag läste kapitlet om eventhantering (som, tillsammans med några andra kapitel, går att ladda ner här). Jag, som alltid gjort javascriptande med diverse onClick="doSomething()"-kod, insåg att precis som jag kan binda utseende till element med CSS utan att röra HTML-koden, så kan jag binda beteende till element med javascript (addEventListener/attachEvent) utan att röra HTML-koden. När man har en sajt med 16632 statiska HTML-sidor är det en big win.

Och, som extra bonus kan man nu debugga sin javascriptkod på ett vettigt sätt. Mozillas kombination av javascriptdebuggern och DOM-inspectorn gör att det är galet lätt att få sin kod att funka i Mozilla-baserade browsers. Därifrån är det ibland problem att få saker att funka i Opera, Safari/Konqueror och IE, men ”DHTML Utopia” hjälper en bra bit på vägen med en pragmatisk inställning till browserbugworkarounds.

Så, ja, om tiden tillåter blir det mer DHTML-festival på lagen.nu? Gillar du det inte? Ladda ner lagarna i XML-format och transformera dem till hur tråkig HTML du vill!

Nu har det varit mycket tekniksnack flera dagar i rad här. Imorgon blir det en passionerad rant om lagstiftningsteknik.

1: Det stämmer faktiskt inte riktigt, om man har javascript avslaget skickas man till ett vanligt amish-formulär. Graceful degradation IN DA HOUSE!

2: ”Alt” på Windows/Firefox, ”Ctrl” på Mac/Safari, och på Opera funkar det inte alls ety den verkar inte kunna binda svenska tecken till acceleratorer på det viset.

Allt du lärt dig om webutveckling är fel

Ett uttryckligt designmål för lagen.nu har varit att göra så mycket som möjligt med statiska filer. Det var en nödvändighet av prestandaskäl när tjänsten körde på en 166Mhz Pentium, och det är även idag en vettig målsättning, inte minst ur robusthets- och säkerhetssynpunkt.

Så länge som en webtjänst inte förändras på grundval av användarnas interaktion finns det inte någon strikt anledning att göra den med dynamisk teknik som ASP, PHP, CGI-script, servlets/JSP etc. Det kan förstås finnas utvecklingsmässiga fördelar; i princip alla nya tekniker för webutveckling utom möjligtvis XSLT är anpassat för dynamisk sidgenerering, så man når kanske sina utvecklingsmål snabbare med en dynamisk lösning. Men jag har ingen brådska på det viset.

Etiketteringen är dock den första funktionen som tar emot data från användaren, och har som sådan berett lite extra problem. Det första problemet jag hade att lösa var att jag ville att funktionen bara skulle vara tillgänglig för de som explicit ville ha den under en testperiod. I dynamiska system gör man oftast liknande saker med inloggningsförfaranden och sessionsobjekt, samt villkorlig kod som, beroende på inloggningsstatus, spottar ur sig lämplig HTML-gränssnittskod.

Men i en statisk lösning kan man inte göra sådant på servern. Däremot kan klienten åläggas att göra en hel del dynamiska trick! Lösningen fick bli att den gränssnittsdetalj som används för etiketteditering initialt gömdes med en style="display: none;", för att sedan, när sidan laddats klart, eventuellt göras synlig baserat på om kakan tags är satt till enabled med javascript i den här stilen (elementet heter ”comment” av hysteriska skäl):

    if (GetCookie('tags') == 'enabled')
    {
	if (document.getElementById('comment'))
        {
	    document.getElementById('comment').style.display="block";
         }
    }

Naturligtvis kan man inte använda en sådan här lösning om den detalj som man eventuellt vill gömma innehåller någon typ av hemlig information — det är trivialt för en angripare att kolla på HTML-koden för att hitta informationen.

Ett annat designmål med lagen.nu är att så långt som möjligt undvika att använda en traditionell RDBMS som MySQL, SQL Server, Postgres etc. Även här rör det sig om en önskan att hålla saker så enkelt som möjligt genom att undvika en separat körande process, som dessutom implementerar sin helt egen rättighetsmodell (vanligtvis), kräver sina egna backup- och synkroniseringsrutiner. En databasserver är, om man tänker efter, bara ett uppsvällt userland-filsystem med en väldigt bra ls 🙂

På lagen.nu jobbar jag framförallt med dokument – lagtexter och liknande, som är väldigt naturligt att representera som filer i ett filträd, snarare än BLOBar i en databas. Men även för datamodeller som traditionellt skulle representeras i en RDBMS har jag valt att använda filer – XML, närmare bestämt. Ett exempel är kopplingen rättsfall – lagparagrafer. Varje rättsfall kan referera noll eller flera lagparagrafer (inklusive i olika lagar), och varje lagparagraf kan naturligtvis refereras från många rättsfall. Istället för att ha en Verdicts-tabell, en LawParagraphs-dito, och sen en många-till-många-mappingtabell har jag ett stort XML-dokument med grundläggande uppgifter om alla rättsfall, ordnade efter lagar och paragrafer. Sen länkar jag in relevanta rättsfall under XSLT-transformationen, beskrivet här (under ”Accessing data in other documents”). XML-dokumentet innehåller redundant data, men eftersom det inte är den ursprungliga källan till informationen (det skapas av bara farten under den vanliga transformationen av rättsfallen) är det ok.

Den stora fördelen med detta är att mycket funktionalitet kan implementeras i XSLT istället för python eller SQL (vissa skulle ifrågasätta om det är en fördel, men jag tycker det har sin inneboende charm).

Etiketter har en liknande datamodell med en många-till-många-relation mellan etiketter och lagar, och även här är tagdatabasen en stor XML-fil (eller, än så länge är den ganska liten, men ge den tid).

För att ta emot den nya informationen måste jag tyvärr frångå mina principer om en totalt statisk sajt. Ett litet CGI-script tar emot formulärdatan, uppdaterar etikettdatabasen, och ansvarar för att fixa till HTML-filen: när en användare lägger till en etikett på en lag måste den statiska HTML-filen som utgör den formatterade lagen ändras för att inkludera den nya etikettexten.

Och här råkar vi på ett problem som inte finns i en helt statisk sajt: prestandakrav.

Sidorna på lagen.nu genereras vanligtvis (i sista steget) från XML till XHTML genom en XSLT-transformation från helvetet. Den gör mycket magiska saker, och den tar lång tid – den mest krävande lagtexten, Rättegångsbalken, tar strax under 45 sekunder. Det är inte OK om användaren måste vänta så länge för att sätta en tag. Istället opererar CGI-scriptet på den färdiggenererade HTML-filen genom att mmap’a den, söka sig fram till en markör-kommentar, och dumpar sedan in den nya taglistan.

För att tokfort hitta relevanta taggar för lagen i fråga använder jag cElementTree , och sen spottar jag ut HTML på gammalt klassiskt f.write("<a href='foo'>bar</a>")-manér.

Det är dock fortfarande långsamt att lägga till stora mängder etiketter på en lag, detta på grund av lite suboptimalitet på ett annat ställe som jag för tillfället helst sopar under mattan. Dessutom skalar det inte, men när jag får så mycket etiketter att det blir ett problem ska jag jubla.

Så, för att summera: Undvik dynamiska webutvecklingsramverk, och undvik relationsdatabaser. För att vara mer tvärtemot etablerad webutvecklingskutym skulle jag behöva programmera i assembler.

Gränssnittet för etikettering i sig är däremot en dynamisk HTML-festival i miniatyr. Imorgon ska jag berätta om min nyvunna fascination för den här utskällda webdesigngrenen.

Smalltalk, risk and success stories

In the comments section to an earlier post  Göran Krampe brought up some excellent points, backed by experience, on my remark that more dynamic languages increases risk in projects, especially if all developers in the project are not on the same level. Here are some of my comments on those, but please do read Göran’s entire comment for more context. Since I find the discussion to be very interesting, I thought I’d bring it up as a new article.

Just read your article at IDG regarding Alan Kay/Smalltalk – and it was good. For once not a single glaring misconception regarding Smalltalk 🙂 – I am a bit jaded by all people throwing around ”truths” about Smalltalk without having actual experience themselves. So kudos for that. 🙂

Thank you! However, I must confess that my Smalltalk experience is limited to playing around with Squeak in my spare time, so I have never used it in a large project. Bear this in mind when you read my response…

Above though it sounds like you are saying that Java would mean a lower risk than Smalltalk for larger projects or projects in which the developers are of ”average” or ”mixed” level. That I definitely do NO agree with on the other hand. 😉

[…]

So my experience is the exact opposite. I claim that less experienced developers can more easily be made productive in Smalltalk than in Java. I have repeatedly taught OO *and* the basics of Smalltalk in a single day including practice and then had these pupils find bugs and complement a working small system the next day.

I suspected that a smalltalker might not agree with that 🙂 Please not that I’m only talking about risk, not programmer effiency. Risk isn’t inherently bad, since it usually goes hand in hand with opportunity. Your points about new programmers learning Smalltalk quicker than Java is obviously backed up by experience, but a little beside the point.

The reason that I feel projects done in more dynamic environments (I’m going to lump Smalltalk, Python and Lisp together here, which might not be entirely fair) to have a higher level of risk is that the dynamic properties of the language will allow your system to take on aspects of a domain specific language, especially if some members of your team understands the Zen of Smalltalk/Lisp/Python. For example, in both Lisp and Smalltalk you can essentially redefine parts of the syntax, like the if/then/else statements, and if it makes sense in your design, you might very well want to do that. But if another programmer, who has yet to be enlightened (sticking with the Zen theme) on the expects the syntax to work like default, this might trip him up (Operator overloading in C++ and .Net gives us essentially the same problem). This risk increases if the programmer has been taught more traditional imperative programming in the style of Java/C# and have not been given a good introduction to Smalltalk.

Things like tail recursion, higher order functions, metaclasses and even polymorphism cam be difficult to wrap your head around, especially since you can get a lot of stuff done without them, and in not-so-dynamic environments, you usually do.

Or to put it more soundbite-friendy way: The more dynamic and open-ended a language is, the more choices you have at any given time. Every choice introduces opportunity — and risk.

However, since I haven’t done any large projects in either Smalltalk or Lisp (I did a elevator control system in Scheme in school, though :-)), maybe my opionions should be taken with a grain of salt. I’d be happy to hear your opinions on why the things I’ve mentioned above aren’t such big risk factors after all.

Also, the idea that there is something inherent in Smalltalk making it unsuitable for large scale development is simply not true either. And there is ample evidence for that.

There are very large systems having been built in Smalltalk with great success over a long period of time. And I mean *large*. 14000 classes, 65 developers/staff, 500 users etc, see for example the ”Kapital” system at JP Morgan Bank, https://secure.cwheroes.org/briefingroom_2004/pdf_frame/index.asp?id=4909)

I don’t think I’ve implied anything like that. It is interesting to hear about these big successful projects, but I keep wondering why we don’t hear more about them. Paul Graham published an essay about how good Lisp was for building ViaWeb, and most people that are familiar with XP has at least heard about the C3 project, but there must be more stories, right?

The point of my column that started the debate was that while many of the inventions and discoveries that come from the Smalltalk community are now used everywhere (MVC, JIT, design patterns…, XP), the language itself isn’t. I don’t believe it’s as simple as Sun and Microsoft having a larger marketing budget, but I’m very interested in a discussion of what the real reason could be, as I still think there are more that the general programmer community could learn from Smalltalk.

Premature cliches are the root of all evil (and considered harmful)

Did you know that you can use wild cards in Google searches? Searching for ”premature * is the root of all evil” yields a lot of people quoting Knuth in an almost fanboy like fashion (though it seems that Tony Hoare is the actual source of the quote). I’d like to nominate ”Premature optimization is the root of all evil”, together with ”Goto considered harmful”, as the most tired and overrated quotes in computer science ever.

Don’t get me wrong, these quotes made a lot of sense thirty, or even ten years ago, but now that the ideas that they illustraded has been absorbed into the programming community, they do more harm than good. For example, Dijkstras paper titled ”Goto considered harmful” argued against the practices found in ALGOL and similar environments 36 years ago. Most common languages at the time had weaker support for structured programming, and the general consciousness about structuring code in the programming community was low, leading to ”spaghetti code” with goto’s all over, making it hard to figure out how the program actually worked. At that time, the condemnation of goto’s made sense.

Well, a lot have happened since that time, both language-wise and with programmer’s attitude towards the systems they’re building. ”Software ICs”, object-orientation, components, SOA… all leading to environments and programs that can grow quite large without collapsing under the weight of it’s own complexity (some say that we’ve exchanged the problems of ”spaghetti code” for that of ”ravioli code”, but that’s another discussion). When a programmer nowadays considers using ”goto”, it’s usually for situations where it actually makes sense (breaking out of nested loops, for example). Having the mantra ”Goto considered harmful” alive in the collective programming community consciousness actually makes things worse, as it discourages people for using the construct where it actually would make the program easier to read.

The quote about ”Premature optimization is the root of all evil” is more of a ”timeless” truth, but of course it’s basically a truism — premature anything, is by definition, never a good thing. The quote is rooted in a time where optimizing code would make it harder to read and maintain, and really is aimed against those micro-optimizations that make code harder to read (explicit loop unrolling, contrived program flow, unneccesary use of bitshifting and bitfields) without providing a huge performance benefit. However, this quote has been repeated so often, often in an authorative voice, that new programmers think that there’s something wrong with thinking about performance as you design and write the initial version of your program — fearing that it will lead to a design that is worse and a program that’s harder to maintain.

These fears do not match my experiences. I think it makes sense to think about performance through the design phase, particularly considering which pieces of code will be run often (I seem to recall statistics saying that 98% of a program’s running time is spent in 2% of it’s code), and what is needed to get those parts to run really fast. Of course, I don’t consider these optimizations to be premature.

I’ve seen cases where architectural descisions were made early on, without measuring performance, leading to a system that was not only slow, but basically impossible to optimize without rearchitecting. As these systems were built without much thought about performance, when the performance problems surfaced, it was basically too late. These scenarios are, in my experience, much more common than systems where optimization has made the code hard to read and maintain.

However, there are other stuff that often is done prematurely, and in my experience are a much larger source of evil — abstraction and/or generalization, things that are generally considered to be good things. New (or even experienced) programmers that are ambitious will often design overly generalized systems without being familiar enough with the problem domain to know which generalizations makes sense. Similarly, they will create abstractions through elaborate class hierarchies and interfaces with the intention that it will make the individual pieces of the system easier to understand, without realising that the abstractions get in the way of understanding the system as a whole.

Code that has been obfuscated by premature optimization attempts usually can be made more readable piece by piece, but an overengineered system with sophisticated class hiearchies that turned out not to match the problem domain is much harder to refactor into something useable. I wholeheartedly agree with this page on the original wiki that states:

It’s probably important not to create an abstract base class or an interface until you can think of at least two descendants or implementations — that you are actually going to use right now

When reading and understanding other peoples code, I find that it much more common to find it over-abstracted than under-abstracted. Maybe we need new quotes/cliches/memes to warn us of where the real risks actually are now, as opposed to where they were thirty years ago.

Next time, I’ll take apart the Fred Brooks quote ”Adding manpower to a late software project makes it later” 🙂