Demoefekti

Ohjelmistokehitys on kuin standup-komiikkaa. Jotta vitsit toimisivat, niitä pitää esittää yleisölle. Yhä uudestaan, kunnes naurua alkaa kuulua. Yksin kopissa harjoittelemalla ei pääse rajaansa pidemmälle.

Samalla efektillä voidaan selittää laajalti ketterien menetelmien idea: Softaa esitellään asiakkaalle yhä uudestaan ja uudestaan, kunnes se on mitä asiakas haluaa.

Kutsukaamme tätä demoefektiksi.

Demoefekti on sitä, että kun softa pitää jatkuvasti esitellä toimivana autenttisessa ympäristössä, siitä kuin huomaamatta tulee itse asiassa toimiva autenttisessa ympäristössä. Efektin voi muotoilla hypoteesiksi, että softaprojekti onnistuu sitä todennäköisemmin, mitä enemmän sitä demotaan.

Mikä tahansa powerpoint-demo ei riitä, vaan demolle on ehtonsa. Toimivaa softaa pitää esitellä asiakkaalle säännöllisesti lopullisessa tai sitä vastaavassa käyttöympäristössä.

Toimivaa

Softaa pitää esitellä niin, että se oikeasti tekee ne asiat joita esitellään eikä vain kääntele vipuja käyttöliittymässä.

Asiakkaalle

Demoja ei tehdä sisäisesti, vaan ne suunnataan asiakkaan edustajalle, jolla on valta päättää muutoksista projektin tavoitteissa, tuoteomistajalle.

Säännöllisesti

Demoja järjestetään usein, yleensä kahden viikon välein. Alkaen heti projektin alusta.

Lopullisessa tai vastaavassa ympäristössä

Jos järjestelmässä on kolme erillistä klusteria, demossa on kolme erillistä klusteria, tosin ne voivat olla yhden pienen virtuaalikoneen kokoisia. Taustajärjestelmiä ei korvata stubeilla, vaan ne (tai niiden kopiot) ovat jatkuvasti käytössä.

Ketteryyden hyödyt voidaan nähdä lähinnä seurauksina siitä, että softaa demotaan tarpeeksi usein ja tarpeeksi aikaisin.

Demo ohjaa tekijät korjaamaan softaa, ei selittämään sen puutteita

Kun yksi demo menee pieleen, sen kanssa voi elää. Kun sama virhe toistuu, se on noloa. Kolmatta kertaa samalla tavoin toimimatonta softaa ei voi demota, vaan se on oikeasti pakko jo korjata.

Demo paljastaa ongelmat

Jos lippukauppaa ensin koodataan kaksi vuotta ja sitten testataan viisi kuukautta ennen kun sitä demotaan, ei liene yllätys kenellekään, että luvassa on ongelmia. Jos samaa kauppaa demottaisiin kerran kuussa kahden vuoden ajan, eikä se toimisi, ongelmat huomattaisiin paljon aiemmin, ja niille tehtäisiin jotain. Ks. edellinen kohta.

Demo auttaa asiakasta ymmärtämään

Kun asiakkaan päättäjä näkee softan toiminnassa säännöllisesti, hänen on paljon helpompi suunnitella sen tulevaa käyttöä ja huomata mahdolliset muutostarpeet organisaatiossaan tai softassa. Tämä helpottaa käyttöönottoa. Lisäksi erilaiset sidosryhmät voidaan kutsua esittämään kommenttejaan joihinkin demoihin, mikä antaa arvokasta palautetta sekä sitouttaa organisaatiota.

Kaikki ketterät käytännöt, ajattelutavat ja menetelmät ovat itse asiassa tapoja sopeutua demoamiseen kahden viikon välein.

Kevyt speksaus

Kun softan speksi voi muuttua kahden viikon välein, ei sitä ole mitään järkeä kirjoittaa kovin tarkasti. Muutama sana paperilla ja käsin piirretty kaavio riittävät siihen, että yhteinen käsitys tavoitteesta säilyy kaksi viikkoa.

Automaattinen käännös ja testaus (CI)

Kun softa pitää saada toimimaan aina kahden viikon välein, ei erillisille integraatiovaiheille ole aikaa. Softan on pakko toimia koko ajan, joten jokainen commit pitää testata automaattisesti oikeassa ympäristössä.

Vertikaalinen työnjaottelu

Kaksi viikkoa on lyhyt aika. Jotta siinä saa tehtyä jotain asiakkaalle näkyvää, työtehtävät (user storyt) täytyy jäsentää asiakkaalle näkyvien osien mukaan, ei kerroksina tai komponentteina. Uusia API-kutsuja on ikävä demota, jos niitä käyttävä käyttöliittymä puuttuu.

Kevyt hallinto

Kahta viikkoa varten ei piirellä GANTT-kaavioita. Tiimin jäsenten on pakko työskennellä oma-aloitteisesti yhteistyössä. Perinteinen projektipäällikön rooli lähes poistuu ja korvautuu scrum masterilla, jonka tehtävä ei ole ohjata työtä, vaan poistaa esteitä sen edestä. Esteet nimittäin pitää poistaa heti, tai seuraavassa demossa ei ole mitään uutta näytettävää.

Koko tiimi mukana alusta asti

Kun kaikki pitää saada toimimaan heti ja koko projektin ajan, tarvitaan jokaista spesialistia alusta loppuun asti. Vielä parempi, jos tarvitaan mahdollisimman vähän erilaisia spesialisteja.

Demot selittävät ketteryyden hyödyt, ja ne selittävät ketterät työtavat. Seuraavan kerran kun joku kysyy, mitä ketteryys on, tiedät vastauksen: se on sitä, että softaa demotaan kahden viikon välein, kunnes yleisö nauraa.

Tämän artikkelin on kirjoittanut Otso Kivekäs ja sitä ovat sittemmin muokanneet muut Codenton työntekijät.

Seikkailuja editorimaassa

Tilapäisessä mielenhäiriössä aloin taas toteuttaa pientä kirjastoa
C:llä. Ajattelin kokeilla samalla (olihan kyseessä puhdas
harrasteprojekti, ja näissä on hyvä aina kokeilla) namespace-ratkaisua
nimitörmäysten välttämiseksi.

No, aloin kirjoittaa:

#ifndef G
#define G(symbol) pieni_ ## symbol
#endif

typedef struct G(foobar) *G(foobar_t);
                         typedef ...

Ja cc-moden autoindent hajosi pyytämättä ja yllättäen jo viidennellä
rivillä.

Mitäs tässä tapahtui? No, C:n preprosessori mahdollistaa tietysti
kaikennäköisen huonosti motivoidun temppuilun, mutta eroon siitä ei
enää päästä, ja C:tä indentoivan järjestelmän on jollakin tavalla
elettävä preprosessorin olemassaolon kanssa. Vaikkakin se tekee
täydellisestä syntaktisesta analyysista vähintäänkin hyvin vaikeaa,
ehkä jopa mahdotonta (jos kaikkia relevantteja headereita ei
esimerkiksi pystytä jostakin syystä löytämään).

Tapaus on erityistapaus yleisemmästä ongelmaluokasta. Joskus ongelmaa
ei voida ratkaista täydellisesti, ja täydellisyyden yrittäminen johtaa
siihen, että ero toteutuneen ja tavoitetilan välillä ärsyttää
käyttäjää erityisesti. Voi olla kaikkien kannalta parempi etsiä
paikallinen optimi huomattavan yksinkertaistuksen keinoin.

Mietin tapausta C:n sisennys. Jos unohdetaan jäsentäminen C:n
sääntöjen mukaan, ja keksitään oma, yksinkertaisempi, täydellisesti
jäsennettävissä oleva syntaksi, josta C on vain
erityistapaus. (Lisäbonuksena tämän syntaksin erityistapauksia ovat
C++, Java ja Javascript, puhumattakaan sadoista vähemmän tunnetuista
kielistä.)

kieli ::= lauseke*
lauseke ::= atomi | "{" lauseke "}" | "(" lauseke ")" | "[" lauseke "]"
atomi ::= [^{}()[]] | merkkijono
merkkijono ::= """ [^"]* """

Ohjelmointikielen sisällön tulkinnan kannalta tämä syntaksi on
hyödytön, mutta emme tulkitse sisältöä, vaan sisennämme.

Jokaista riviä sisentäessä katsomme edellisen ei-tyhjän rivin
ensimmäistä ei-whitespacemerkkiä. Selvitetään siitä kuinka monien
sulkujen sisällä se on, ja monenteenko sarakkeeseen se on sisennetty.
Seuraavaksi katsomme monienko sulkujen sisällä sisennettävän rivin
ensimmäinen ei-whitespacemerkki on. Lisätään edellisen rivin
sisennyssarakkeeseen niin monta sisennysaskelta kuin sulkutasoilla on
eroa.

int foo(int x) {
    bartholomew(exp(1/(x+1), 2)*10*exp(1-(1/(x+1)), 2),
        + exp(1-(1/(x+1)), 2));
    while (quux(x + really_long,
            other_parameter)) {
        repeat_this(x);
    }
    return x;
}

Pieniä kauneusvirheitä. Varsinainen haitta on funktiomäärittelyn
parametrilistan jatkorivien sisennys samalle tasolle kuin funktion
leipäteksti. Parametrilista olisi parempi sisennettynä enemmän, jotta
nämä erottaisi toisistaan.

int quux(int x,
    int y) {
    return x + y;
}

Tämä voidaan korjata lisäämällä {:sta aina yksi taso, mutta (:sta ja
[:sta ensin kaksi tasoa ja seuraavasta samasta avaavasta merkistä vain
yksi taso (Eli (( tuottaa kolme tasoa ja ({( tuottaa viisi.)

Nyt esimerkit sisentyvät näin.

int foo(int x) {
    bartholomew(exp(1/(x+1), 2)*10*exp(1-(1/(x+1)), 2),
            + exp(1-(1/(x+1)), 2));
    while (quux(x + really_long,
                other_parameter)) {
        repeat_this(x);
    }
    return x;
}

int quux(int x,
        int y) {
    return x + y;
}

Tätä voidaan edelleen korjata huomioimalla, että jos edellisen rivin
viimeinen efektiivinen avaava sulje (eli jos rivi loppuu a(b()c,
viimeinen efektiivinen avaava sulje on tuo stringin ensimmäinen sulje)
ei ole rivin viimeinen ei-whitespacemerkki, sisennetään suoraan tuon
sulkeen jälkeiseen sarakkeeseen, eikä lasketakaan sisennyksiä
askeleina. Nyt saadaan:

int foo(int x) {
    bartholomew(exp(1/(x+1), 2)*10*exp(1-(1/(x+1)), 2),
                + exp(1-(1/(x+1)), 2));
    while (quux(x + really_long,
                other_parameter)) {
        repeat_this(x);
    }
    return x;
}

int quux(int x,
         int y) {
    return x + y;
}

Ylläesitettyä syntaksia ei varsinaisesti jäsennetä missään välissä,
sisennys perustuu vain sulkulaskentaan. Vain kommentit ja stringit
pitää jäsentää oikein. Kommenttien ja stringien rajoja ei voi edes
sotkea C-preprosessorilla, joten tämä ei palauta meille alkuperäistä
ongelmaa, jota lähdettiin korjaamaan.

C-like-mode:n jatkokehitys jatkuu. Älkää pidättäkö hengitystänne.

Tämän artikkelin on kirjoittanut Teemu Kalvas ja sitä ovat sittemmin muokanneet muut Codenton työntekijät.