Skip to content

Latest commit

 

History

History
379 lines (271 loc) · 21.4 KB

koe2021.md

File metadata and controls

379 lines (271 loc) · 21.4 KB
layout title inheader permalink
page
Kokeen mallivastaukset ja arvosteluperusteet
false
/koe2021/

Syksyn 2021 koe

Tehtävä 1

Tehtävän arvioi Riikka Korolainen. Omasta arvioinnista voi tarvittaessa kysyä [email protected] tai discordissa

Seuraavassa on joukko väittämiä jotka saattavat olla totta tai väärin. Osa voi olla jopa yhtäaikaa totta ja epätotta. Kerro onko väittämä totta vai/ja epätotta ja anna asialle lyhyt perustelu.

User storyä ei ole pakko testata samassa sprintissä missä se toteutetaan

Yleensä vääri: tulee toimia Definiton of donen puitteissa. Jos se määrittää että testaus kuuluu valmiin kriteeriin, niin periaatteessa sillon story kuuluu testata samassa sprintissä missä se toteutetaan. Toteutetaan sprintissä n ja testataan sprintissä n+1 ei ainakaan ole kovin kosher-agilea. Jos mainitaan, että testaus voi mennä myöhempään sprinttiin mutta tällöin story ei tule edellisessä valmiiksi, täydet pisteet. Jos mainittu, että DoD ilman testivaatimuksia tarkoittaa, ettei testejä ole pakko tehdä, ei miinusta, ellei jätetä erikseen mainitsematta, että tämä on harvinaista. Esim oikein: voidaan määritellä DoD niin, ettei testejä tarvitse tehdä, ja erityistapauksena vaaditaan testaamista, 0.25p.

Product owner määrittää user storyjen työmääräarviot

Väärin: työmääraarviot määrittelee kehittäjätiimi. Jos mainittu, että PO tekee jonkin verran yhteistyötä/auttaa estimoinnissa, ei miinusta: tavallaanhan PO kertoo, mitä story pitää sisällään, mikä vaikuttaa estimointiin.

Sprint backlogin tehtäville on pakko antaa työmääräarviot

Väärin: esim. scrum suositteleen backlogilla olevien töiden työmäärän arviointia mutta ei ota suoraan kantaa sprint backlogin tehtävien työmäärien arviointiin vaikka vihjaakin siihen suuntaan esim. sprinttien burndownkaaviolla. Uusin Scrum-kirjallisuus kyseenalaistaa sprint-backlogin estimoinnin järkevyyden. Jos vastaus on “oikein” ja perustellaan, että taskit on estimoitava jotta burndownia voidaan ylläpitää/ jäljellä olevaa työmäärää arvioida, 0.5 pistettä.

Firman paras kehitystiimi saadaan selville vertailemalla tiimien velositeettia

Väärin sikäli että (yleensä) storypointeissa mitattava velositeetti on suhteellinen ja suoranaisesti ei rinnastu mihinkään varsinaiseen työmäärään. Kukin tiimi yleensä estimoi itse ja storypointien määrällä ei ole yhteismitallisuutta. Tähän poikkeuksena toki tilanne, missä useampi tiimi työskenelee saman backlogin parissa ja estimoi yhdessä. Tälläisessä tilanteessa jonkinlaista yhteismitallisuutta saattaisi olla. Tässä täydet vain jos story pointteihin on (ainakin epäsuorasti) viitattu. Jos puhutaan ainoastaan eroista storyjen määrissä, 0.25p. Muu selitys esim. vertailu on ikävää ja luo huonoa henkeä, 0-0.25p perustelusta riippuen.

Ketteriä menetelmiä käytettäessä ohjelmiston arkkitehtuuri suunnitellaan aina ensimmäisen sprintin aikana

Väärin: alustava arkkitehtuuri hahmotellaan yleensä alussa, joskus ensimmäisessä sprintissä, joskus ns nollasprintissä. Ketterät menetelmät kuitenkin suosivat inkrementaalista arkkitehtuuria, missä arkkitehtuuri voi tarkentua ja jopa muuttua myöhempienkin sprinttien aikana. Tämä on tulkittavissa myös oikeaksi mikäli perustellaan, että arkkitehtuurin suunnittelua tehdään kyllä jonkin verran ensimmäisessä sprintissä ja sitä tarkennetaan myöhemmin.

A/B-testaus on eräs tuotannossa tapahtuvan testauksen muoto

Väärin sikäli että A/B-testaus, vaikka se tapahtuukin tuotannossa, ei suoranaisesti ole testauksen menetelmä vaan toiminnallisuuksien käyttökelpoisuuden validointimenetelmä. A/B-testaus kylläkin toteutetaan usein ns. feature-flageilla joita myös käytetään tuotannossa tapahtuvaan testaukseen. Jos on laitettu oikein ja perustellaan kuten tässä, 0.5 pistettä (testauksen määritelmä on käsitetty sisältämään käyttökelpoisuuden validointi). Jos on laitettu oikein ja perustellaan, että A/B-testauksella mitataan, toimiiko ominaisuus ja A/B-testauksen menetelmä on kuvattu oikein, 0.25 pistettä.

Arviointi:

  • 0 pistettä jos vastauksessa ei mitään oikeita perusteita
  • 0.25 pistettä jos vastauksessa pieniä virheitä tai huono perustelu
  • 0.5 pistettä jos vastaus on perusteltu hyvin.
  • Mallissa väärin mutta oikein perustellusti vastauksessa oikeaksi merkitty voi olla 0,5 pisteen arvoinen. Lisäksi ihan kaikkea, mikä mallista löytyy, ei tarvitse olla mainittuna.

Tehtävä 2

Tehtävän arvioi Antti Kantola. Omasta arvioinnista voi tarvittaessa kysyä [email protected] tai discordissa

User story kuvaa asiakkaan kannalta arvoa tuottavan toiminnallisuuden ja sisältää lyhyen tekstuaalisen kuvauksen lisäksi joukon hyväksymäkriteerejä (+ lupauksen siitä että storyn sisältö neuvotellaan/tarkennetaan asiakkaan kanssa). Hyvä noudataa lisäksi INVEST-kriteeristöä. Hyvä backlog taas noudattaa DEEP-kriteeristöä.

Ainoa story on liian iso (ei S), se sisältää osin teknistä jargonia (ei kokonaisuudessaan V), sen testattavuus on heikko (ei T) ja luonnollisesti hyväksymäkriteerit puuttuvat storyltä kokonaisuudessaan. Storyltä puuttuu myös työmääräarvio (ei E).

DEEP-kriteeristön hengessä story tulee pilkkoa pienemmiksi, estimoiduiksi ja priorisoiduiksi storyiksi. Korkean prioriteetin storyt tulee olla tarkemmin määritellyt ja niillä tulee olla hyväksymäkriteerit.

Pisteytys: max 4p:

  • +1,5p : User story INVEST kunkin kirjaimen ongelmallisuuden avaaminen +0,25 p / kirjain, pelkkä INVEST maininta tai kirjainten luettelu +0,25 p tai INVEST kirjainten avaaminen +0,5p

    • Independent: Sillisalatti, sisältää monta eri toimintoa
    • Negotiable: Sisältää asiakkaalle vierasta kieltä, ottaa kantaa tekniseen toteutukseen.
    • Valuable: Sisältää osittain teknistä jargonia
    • Estimable: Puuttuu työmääräarvio
    • Small: Liian iso
    • Testable: Testattavuus on heikko
  • +1 p: Product backlog DEEP kunkin kirjaimen avaaminen tehtävään vastaten, pelkkä maininta +0,25 p, pelkkä luettelo +0,5p

    • Detailed appropriately: Sopivan detaljoitu, korkean prioriteetin storyt pieniä ja hyväksymiskriteerit selvillä.
    • Estimated: Työmäärä arviotu
    • Emergent: Kehkeytyvä, muokattavissa tarpeen mukaan
    • Priorisoitu: Asetettu tärkeysjärjestykseen
  • +2 p: Järkevästi pilkotut user storyt +0,125 p / story (max +1,5 p)

    • 0 p: Liian laaja tai epämääräinen user story
    • -0,25 p: User storyssä teknistä jargonia (kuuluu sprint backlogille)
    • +0,5 p: Kysyntäpiikki on ei toiminnallinnen laatuvaatimus. Jos se halutaan user storyksi, niin se tulee muotoilla tyyliin:
      • +0,5 p: “Käyttäjän vasteaika saa olla korkeintaan 𝑥 millisekuntia 99 % tapauksista, kun sivulla on korkeintaan 𝑦 yhtäaikaista käyttäjää”, missä 𝑥,𝑦 ∈ ℕ
      • Muuten 0 p
  • +1 p: Hyväksymiskriteerit korkean prioriteetin user storyille.

    • +0,25 p / user story
  • -0,5 p: Ehdotettu, että product owner valitsee mitkä user storyt otetaan seuraavaan sprinttiin

Huomioita:

  • User storyt on hyvä tässä tilanteessa kirjtata ”Käyttäjä voi…”, eikä käyttää ”Asiakas voi.., koska ”asiakas” on Copypizza.

Tehtävä 3

Tehtävän arvioi Touko Puro. Omasta arvioinnista voi tarvittaessa kysyä [email protected] tai discordissa

(1p) Ulkoinen laatu tarkoittaa kuinka sopiva käyttötarkoitukseensa ohjelmisto on eli pystyykö käyttäjä tekemään sillä haluamansa asiat.

(1p) Ohjelmiston validointi käyttäjän kanssa. Validoinnissa tarkistetaan, että ollaan tekemässä asiakkaan tarpeita vastaavaa ohjelmistoa. Varmistetaan että määrittelydokumentit vastaavat asiakkaan tarpeita.

Ketterässä ohjelmistotuotannossa validointi tapahtuu iteraatioiden lopussa demonstraatioiden kautta (Scrumissa Sprint Review). Näin asiakas voi havaita jos kehitys ei etene haluttuun suuntaan, vaatimuksissa on jonkinlainen kommunikaatiovirhe ja jos hänen tarpeensa eivät enää vastaa alkuperäisiä vaatimuksia.

(1p) Automatisoidut testit ovat avainasemassa ulkoisen laadun varmistamisessa. Tärkeimmät testityypit tähän liittyen on järjestelmätestaus, joka testaa järjestelmää kokonaisuutena ja esimerkiksi tarkistaa, että Storyn toiminnallisuus on toteutettu oikein. Muut testityypit kuten regressio- ja yksikkötestit parantavat myös ulkoista laatua, varsinkin jatkuvan integraation kanssa, eliminoiden mahdollisia bugeja. Testit auttavat ulkoista laatua eliminoimalla bugeja ja validoimalla että ohjelmisto täyttää sen vaatimukset.

(1p) Ketterän menetelmien testauskäytänteitä. Jatkuva integraatio, TDD, ja acceptance test driven development kaikki pyrkivät parantamaan ohjelmiston ulkoista laatua. Jatkuva integraatio eliminoi integraatio vaiheessa syntyviä bugeja, TDD pyrkii edistämään koodin testattavuutta joka vuorostaan edistää välillisesti sen ulkoista laatua ja acceptance test driven development varmistaa user storyjen toteutuksen oikeellisuuden. Leanin mukainen build quality in ja lyhyt aikaväli valmiista testattuun kummatkin ovat ketteriin menetelmiin sisältyviä ominaisuuksia, jotka edistävät testausta ja ulkoista laatua. Tutkiva testaus auttaa ulkoista laatua löytämällä virheitä joita automaattiset testit eivät välttämättä löytäisi. Tuotannossa tapahtuva testaus esim blue-green-deployment tai canary release varmistavat että ohjelmisto toimii tuotantoympäristössä halutulla tavalla.

Tehtävä 4

Tehtävän arvioi Antti Kantola. Omasta arvioinnista voi tarvittaessa kysyä [email protected] tai discordissa

Leanissa ideana on arvon nopea virtaalinen minimoiden aika asiakkaan tilauksesta valmiiseen, asiakkaalle toimitettuun tuotteeseen. Sykliaikaa minimoidaan poistamalla tuotantoketjusta siinä ilmeneviä arvoa tuottamattomia asioita eli hukkaa (muda, mura, muri), joista muda-tyyppisiä hukan muotoja Lean tunnistaa 7 kappaletta.

Työskentelyssä 3 kuukauden sykli asettaa alarajan arvon virtaamiselle, asiakkaan on mahdotonta saada tilaamiaan featureita nopeampaa. Tiimijako taas aiheuttaa monenlaista hukkaa, joista fundamentaalein lienee tarpeeton siirtely. Toteutettavan ominaisuuden vaikutukset arkkitehtuuriin suunnittelee tiimi E, toteutuksesta tiimit A ja B, testauksen taas tekee tiimi D. Koodia jää myös helposti välivarastoon esim odottamaan testausta. Samoin osa tiimeistä voi joutua odottamaan toisen tiimin aikaansaadosta (esim selainpuolen tiimi palelinpuolelle toteuettuja ominaisuuksia), tai tiimit saattavat toteuttaa jotain toiminnallisuuksia pitäksi aikaa “välivarastoon”, odottamaan että toinen tiimi toteuttaa ominaisuuksiin oman vastuualueen osansa. Koska testaus suoritetaan erillisen tiimin toimesta, jää toteutettuihin ominaisuuksiin todennäk pitkäksi ajaksi bugeja

Uudistuksessa tiimeistä tulee tehdä cross functional “feature-tiimejä”, jotka kukin toteuttavat kokonaisia asiakkaan tilaamia arvoa tuottavia toiminnallisuuksia. Kussakin uudessa tiimissä tulee siis olla jäseniä kutakuinkin jokaisesta vanhasta tiimistä. Kehitystyön iteraation pituus tulee valita mahdollisimman lyhyeksi tai kannattaa jopa toimia puhdasverisemmän pull-mallin mukaan ilman iteraatioita, siten että kukin tiimi valitsee yhteisen backlogin kärjestä sitä mukaa uusia storyjä kun tuotantokapasiteettia vapautuu.

Arvostelu:

Monessa vastauksessa vastattiin ennemminkin kysymykseen ”Mitä on Lean?”, kun tässä pyydettiin kertomaan mitä ongelmia Copypizzan kehitystyössä on ja miten se kannattaisi organisoida. Pelkästään Lean termien luettelu ei tuo pisteitä. Vastaukset tulee perustella ja tulee vastata kysymyksiin.

Pisteytys +0,25 p / kohta, jollei toisin mainita.

Ongelmat (max 2 p)

  • Asiakkaalle arvon tuoton ongelmat (max 0,75 p)
    • 3 kk sykli asettaa alarajan arvon virtaamiselle
    • Asiakas ei voi saada featureita nopeampaa -A siakas ei pysty vaikuttamaan ominaisuuksiin
  • Työn virtaavuuden ongelmat ja syntyvä hukka (max 1,5 p)
    • Muda, 7 lähdettä (max +1,25 p) pelkkä listan luettelu ei tuo pisteitä, kohdat tulee perustella.
      • Tarpeeton siirtely tiimiltä toiselle on suurin ongelma (+0,5 p)
      • Ylituotanto – ylimääräiset ominaisuudet
      • Välivarastointi
      • Liikatyö – turha dokumentointi tai testaaminen
      • (Tarpeeton liikkuminen – task switching)
      • Odotus
      • Viat
      • Ihmisten potentiaalin alihyödyntäminen
    • Mura (max +0,25 p)
      • Kommunikaatio haasteet tiimien välillä
      • Epäsäännöllisyyttä
    • Muri (max +0,25 p)
      • Ylikuormitus ja mahdottomat vaatimukset

Ratkaisuja (max 2 p)

  • Lean filosofia, periaateet ja oppimisen tehostaminen (max 0,5 p)

    • Koko sovelluskehitys ja johto Lean koulutukseen
    • Työntekijöiden arvostaminen
    • "Go see"
    • Lean on filosofia, ei pelkästään kokoelma kikkoja
    • Jatkuvan parantamisen mentaliteetti
    • Build – measure – learn -syklit
    • Kaizen-eventit <3 Retrot
    • Laatu ja testaus
  • Tiimien uudellen muodostaminen ja perustelut (max 0,5 p)

    • Cross functional "feature tiimejä"
      • Otetaan yksi jäsen tiimeistä A, B, C, E ja E ja perustetaan uudet tiimit. Tiimistä löytyy kaikkea osaamista, joten voi toteuttaa user storyn alusta loppuun. 0,5 p
      • Pelkkä maininta 0,25 p
    • Backend ja frontend -tiimit 0,25 p
    • Toiminnallisuuden mukaan (pitsan rakentaminen, tilauksen maksaminen, kamppanjakoodit, jne), koska viidelle tiimille tämä on siiloutumista. ( 0 p)
    • Yksi iso tiimi (-0,25 p)
  • Asiakkaalle enemmän arvoa (max 0,25 p)

    • Asiakkaan tapaaminen
      • koko tiimi tapaa asiakasta (0 p)
      • koko tiimi ei tapaa asikasta (-0,25 p)
    • Asiakkaan palveleminen mahdollisimman ”viime hetkellä” (0,25 p)
  • Sykliajan lyhentäminen ja perustelut (max 0,5 p)

    • Pull-systeemi, Jatkuva käyttöönotto, sekä toteutus +0,5 p tai,
    • 1-2 viikon syklit (+0,25 p) tai,
    • 1 kuukauden sykli (0 p)
    • Perusteluja ja keinoja vastaukseen (0 p)
      • Kanbanin avulla
      • kukin tiimi valitsee yhteisen product backlogin kärjestä sitä mukaa uusia storyja, kun tuotantokapasiteettia vapautuu
      • JIT-tuotantomalli
      • Päivittäinen tuotantoon vienti
  • Tiimien toiminta yhdessä (max 0,25 p)

    • Yhteinen Product backlog
    • SAFe, LeSS tai DA
    • Scrum of Scrums

Tehtävä 5

Tehtävän arvioi Matti Luukkainen. Omasta arvioinnista voi tarvittaessa kysyä [email protected] tai discordissa

Mitä tarkoitetaan ohjelmiston sisäisellä laadulla? (1p)

Ohjelman sisäisellä laadulla tarkoitetaan koodin sisäisen rakenteen hyvyyttä. Siihen vaikuttavia seikkoja ovat mm. koodin jatkokehityksen helppous, virheiden jäljityksen ja korjaamisen helppous ja kuinka vaivatonta koodin toiminnallisuuden oikeellisuus varmistaminen on muutoksia tehtäessä.

Mitä sisäisen laadun kannalta ongelmallisia asioita esimerkkikoodissa on? (2p)

Luokalla CopyPizza liikaa vastuita. Tiedoston käsittely sekä eri formaatteihin eksporttaus tulee eriyttää omiin luokkiin. Tiedostonkäsittely kannattaa injektoida, sille myös tulee injektoida tiedoston nimi.

Molemmat merkintämetodit etsivät id:n perusteella tilauksen, logiikka kannattaa refaktoroida omaan metodiin.

Listausmetodit sisältävät toisteisen läpikäyntilogiikan. Voi refaktoroida esim. strategyn avulla

Exporttauksesta huolehtiva metodi on koheesioltaan huono, se tulee uudessa luokassa jakaa erikseen eri formaatit hoitaviin osiin, metodeihin tai jopa luokkiin. Eksportattavien tilausten valinta tulee hoitaa esim. listausmetodin generalisaation avulla.

Tilauksen status säilytetään nyt taikastringina, kannattaa sensijaan käyttää esim luokkamuuttujana määriteltyjä vakioita

Asiakkan tiedot kannattaa eriyttää omaan luokkaan.

0.5p per kohta, paitsi kaksi viimeistä 0.25p per kohta. Näiden lisäksi muista osuvista huomioista saattoi saada 0.25-0.5 pistettä. Eräs tälläinen oli huomautus id:n generointitavasta.

Selitä miten refaktoroisit alla olevan koodin soveltaen suunnittelumalleja tai muita tilanteeseen sopivia ratkaisuja (2p)

Yllä mainttujen kohtien korjaus, 0.5p per kohta, paitsi kaksi viimeistä 0.25p per kohta.

Alkuperäinen koodi täällä

Refaktoroitu versio:

import random

class Customer:
    def __init__(self, name: str, address):
          self.name = name 
          self.address = address

    def __str__(self):
        return f'{self.name} {self.address}'

class Order:
    ORDERED = "ordered"
    DELIVERED = "delivered"
    MAKING = "making"

    def __init__(self, customer_name: str, customer_address: str, ingredients):
        self.status = Order.ORDERED
        self.customer = Customer(customer_name, customer_address)
        self.ingredints = ingredients

    def set_delivered(self):
        self.status = Order.DELIVERED

    def set_making(self):
        self.status = Order.MAKING

    def __str__(self):
        return f'{self.id} {self.status} {self.customer} {self.ingredints}'

class Repository:
    def __init__(self, file_name):  
        self.file_name = file_name

    def read(self):
        orders = []
        with open(self.file_name, "r") as file:
            for row in file:
                parts = row.strip().split(";")
                order = Order(parts[2], parts[3], parts[4].split(','))
                order.id = int(parts[0])
                order.status = parts[1]
                orders.append(order)
                
        return orders

    def save(self, orders):
        with open(self.file_name, "w") as file:
            for order in orders:
                ingredients = ','.join(order.ingredints)
                file.write(f'{order.id};{order.status};{order.customer.name};{order.customer.address};{ingredients}\n')

class CopyPizza:
    def __init__(self, repository):
        self._repository = repository
        self._orders = self._repository.read()

    def save(self):
        self._repository.save(self._orders)

    def _list_matching(self, matcher):
        return [order for order in self._orders if matcher(order)]

    def list_delivered(self):
        def order_delivered(order):
            return order.status == Order.DELIVERED

        return self._list_matching(order_delivered)

    def list_new(self):
        def order_new(order):
            return order.status == Order.ORDERED

        return self._list_matching(order_new)

    def list_customer(self, customer_name: str):
        def customer(order):
            return order.customer.name == customer_name

        return self._list_matching(customer)
  

    def list_ingredient(self, ingredient: str):
        def has_ingredient(order):
            return ingredient in order.ingredients

        return self._list_matching(has_ingredient)

    def _order_with_id(self, id: int):
        for order in self._orders:
            if order.id == id:
              return order 
        return None

    def mark_delivered(self, id: int):
        order = self._order_with_id(id)
        if not order:
            return
        order.set_delivered()

    def mark_making(self, id: int):
        order = self._order_with_id(id)
        if not order:
            return
        order.set_making()

    def take_order(self, order: Order):
        order.id = random.randint(1,10000000)
        self._orders.append(order)

    def export2(self, format: str, status: str):
        if format=='xml':
            print('<orders>')

        for order in self._orders:
            if format=='xml':
                if order.status == status:
                    print("  <order>")
                    print(f"    <id>{order.id}</id>")
                    print(f"    <customer><name>{order.customer.name}</name><address>{order.customer.address}</address></customer>")
                    print(f"    <numberOfIngredients>{len(order.ingredints)}</numberOfIngredients>")
                    print("  </order>")
            elif format=='tsv':
                if order.status == status:
                    print(f"{order.id}\t{order.customer.name}\t{order.customer.address}\t{len(order.ingredints)}")
            else:
              raise('unsupported format')

        if format=='xml':
          print('</orders>')

    def export(self, format: str, status: str):
        def has_status(order):
            return order.status == status

        orders_to_export = self._list_matching(has_status)
        Exporter(orders_to_export).export(format)

class Exporter:
    # nyt yhdellä eksportterioliolla voi hoitaa saman tilausjoukon eksportoinnin useisiin formaatteihin
    def __init__(self, orders):
        self._orders = orders
        
    def export_xml(self):
        print('<orders>')

        for order in self._orders:
          print("  <order>")
          print(f"    <id>{order.id}</id>")
          print(f"    <customer><name>{order.customer.name}</name><address>{order.customer.address}</address></customer>")
          print(f"    <numberOfIngredients>{len(order.ingredints)}</numberOfIngredients>")
          print("  </order>")
 
        print('</orders>')    

    def export_tsv(self):
        for order in self._orders:
            print(f"{order.id}\t{order.customer.name}\t{order.customer.address}\t{len(order.ingredints)}")

    def export(self, format):
        if not format in ['xml', 'tsv']:
            raise('unsupported format')

        if format=='xml':
            self.export_xml()

        if format=='tsv':
          self.export_xml()