Our customer AimoPark’s earlier infrastructure was mostly hosted in AWS Elastic Beanstalk. At that point, the version control platform was GitHub & CircleCI was used to offer some CI/CD steps, though some of them were still manual.

AimoPark had decided that they needed to renew their infrastructure quite and lot. We opted to get rid of the Elastic Beanstalk entirely and going for containerized microservices with Kubernetes on EKS for the container orchestration. We would be developing both new microservices & migrating the older applications to Kubernetes as well. They also had a need for a modern deployment pipeline for all their new Kubernetes apps.

Starting phases

We initially left the old AWS accounts with the Elastic Beanstalk envs as they were and made new ones for the new infrastructure. The older AWS accounts would eventually be cleaned & removed after we’d be done with migrations.

We built the new infra mostly using Terraform (and a complementary tool called Terragrunt), with the bot users & roles used to run Terraform created with CloudFormation StackSets to escape some chicken & egg problems. From the ground up we wanted to run Terraform only from CI/CD as well just like any other code. Ended up initializing a couple of infra-specific repos on GitHub & building first iterations of the pipelines with Circle CI.

At the same time we’d been looking for artifact store solutions. We ended up going with GitLab.com because it could provide us with version control platform, artifact store & CI/CD platform (among others) all in the same SaaS product. Migrated all the repos from GitHub to GitLab & started work on the new GitLab CI pipelines, loosely based on the CircleCI pipelines that we’d had before. They’ve gone through a bunch of iterations but the GitLab CI pipelines are what we’re still using.

Defining the pipelines

We’ve broken our pipelines down to some common components & dedicated a repository for them. For instance we’ve got a pipeline for building Docker images and pushing them to AWS ECR and another for Kubernetes deployments. We’ve also got pipelines for deploying infra-as-code with Terraform & couple of other pipelines as well. These separate pipelines can be mixed & matched with each other to a point and modified with app-specific custom jobs as well.

In the end this allows us to have very brief GitLab CI definitions in application repos. For example for Java services we can just have something like this example belowo and it’ll do all the necessary basic things. The referenced java_service_ci.yml uses both the ECR- & kubectl-pipelines, in addition to having some common Java-specific things like Maven stuff & SonarCloud code analysis.

The workflow

So here’s how the most common workflow for us works. Developers always work in feature-branches that are eventually merged to the main branch. When developer has pushed some commits & opens a Merge Request in GitLab the pipeline automatically starts working.

First it builds things like the final Kubernetes definition .yamls (with all the Deployments, Services etc. resources) using a tool called kustomize. With it we can have things like base configurations that are the same for all envs and overlay configurations that can be used to further add or modify things depending on the env. After the .yamls are built we run some validations against them with kubectl & some static code analysis with kube-score to improve security & resilience.

For getting environmental variables in the .ymls we had to do some envsubst trickery:

When those jobs pass the pipeline continues & builds the Docker image & pushes it to a registry hosted on Amazon ECR, tagged as a review-image. ECR does image vulnerability scanning on push.

To connect to the EKS cluster & other AWS services the pipeline uses a separate CICD-user with IAM role assuming & limited access rights. Early on we made some AWS Lambda functions to automatically rotate the CI/CD access keys very often for additional security.

Review environments

The pipeline then leverages a GitLab CI feature called Review App and creates a new namespace appended with the Merge Request id on our EKS dev cluster. The full app is deployed using an open-source tool Krane. It uses kubectl under the hood, makes sure that the deployment is successful and prints the relevant events & container logs if the job is failed. Naturally, with new pushes, the full deployment to the review namespace is repeated.

The deployment jobs themselves are pretty simple:

Developers can then test the app against all the resources on dev environment before merging. After merging or closing the MR the review namespace & all resources in it are mercilessly destroyed by the pipeline.

When merged another pipeline is started and it does much the same things as before, but this time deploys the app to the main namespace on the EKS dev cluster. Additional jobs are used to deploy the app to EKS clusters on staging & production accounts and these can behind manual approval steps depending on the app.

Final thoughts

Our development teams have ownership over their microservices and are responsible for doing all the deployments & maintaining the Kubernetes configurations etc. by themselves.

For debugging purposes, developers have edit rights to the development cluster, but for other clusters access any edit (& thus deployment) rights are limited to just the CICD and a small number of our AWS admins.

As things stand all of our EKS deployments are done through the CICDs. It has worked mostly very well for us, though sometimes it’s hard to tell which versions have been deployed to which env or coordinate deployments for multiple services. In the future, we might be interested in looking into some GitOps deployment tools like ArgoCD or Flux.

Toni Mauno

Toni Mauno

Toni Mauno is a Cloud Specialist at Gofore, with a background in software engineering. He is currently working mostly with AWS and DevOps projects, building infra-as-code solutions, working on CI/CD pipelines and backend services, among other things.

Linkedin profile

Piditkö lukemastasi? Jaa se myös muille.

Koko seuran treenit TPS x Gofore

Tämä on yhden näkökulman tarina siitä, miten Gofore nosti näkyvyyttään Turussa asettamalla lapset ja nuoret etusijalle hieman erilaisen urheilumarkkinoinnin avulla.

Vaikka salibandy ei ikänsä puolesta olisikaan enää lapsen kengissä, niin monilta osin se silti sitä yhä on. Talkoohenki, vapaaehtoisuus ja rakkaus lajiin on edelleen lajin parissa toimivien primus motor. Tästä samasta eteenpäinajavasta voimasta nousi myös halukkuus toimia yhdessä paikallisen salibandyseuran TPS Salibandyn kanssa. Tässä vaiheessa Gofore oli Turussa vielä hyvin pieni toimija lähes millä mittarilla tahansa. Kourallinen IT alan ammattilaisia piti toimistoaan lelukauppa Casagrande yläkerrassa ja tämän 100-vuotiaan entisen vakuutustoimiston lankkulattioiden natinan säestämänä syntyi yksi Goforen merkittävimmistä yhteistyökumppanuuksista urheilusponsoroinnin parissa.

Positive Impact with Gofore luonnosteltiin tuskallisen kuumassa Gasagranden yläkerrassa valkotaululle yhdessä TPS:n silloisen toiminnanjohtajan sekä Goforen puolelta Eeron ja Juhan toimesta. Pääasiallisena tarkoituksena oli ensinnäkin tehdä asiat jotenkin muuten, miten muut ovat tehneet. Niin sanotulle laitamarkkinoinnille ja jäämainoksille on aikansa ja paikkansa ja ne täyttävät oman tarkoituksensa varsin hyvin.

Me halusimme enemmän.

Halusimme luonnollisesti näkyvyyttä Goforelle, mutta myös tehdä hyvää ja osallistua TPS:n seuratoimintaan. Olihan meillä kummallakin usean vuoden tausta Seurasta sekä lastemme harastuksien että toimihenkilöinä häärämisen myötä ja Eerolla vielä lisäksi pitkän pelaajauran myötä. Perimmäisenä ajatuksena oli keskittyä hyvän tekeminen lasten ja nuorten kautta. Emme kuitenkaan halunneet lähteä sille linjalle, missä lapset puetaan Goforen logoihin ja missä lapset toimisivat sympaattisina mainostelineinä digitaalisen Suomen puolesta. Tavoitteena oli tehdä oikeasti hyvää ja ansaita se näkyvyys, mikä siitä mahdollisesti seuraisi. Tältä pohjalta lähdettiin luonnostelemaan Positive Impact -pelaajien konseptia. Juniorijoukkueista valittaisiin kaksi pelaajaa kantamaan rinnassaan Goforen logoa. Nämä PI-pelaajat muodostaisivat oman pienen yhteisönsä ja toimisivat napanuorana Goforen ja TPSn yhteistyössä. Säännöllisen epäsäännöllisesti kokoontuneet PI-pelaajat pääsivät mm. ideoimaan Granny-botille salibandysisältöä lelukaupan lankkulattioiden vaihduttua Turun näyttävimmälle paikalle rakennettun katohuoneiston valoihin.

Viimeistään siinä vaiheessa, kun Granny chatbot astui mukaan yhteistyöhön oli tunnustettava, että kasvaneen työmäärän myötä tarvitsimme apua. Saimme avuksemme urheilumarkkinoinnin asiantuntijayrityksen Aboen sekä heidän mukanaan tulevan energisyyden. Turusta luonnollisesti. Muutaman piispanmunkilla kuorrutetun palaverin jälkeen asiat lähtivät ihan toisella tavalla lentoon. Grannyn myötä löytyi täysin oma graafinen identiteetti -joten aivan uutta. Goforelta löytyy ensiluokkaista tekoälyosaamista ja onneksemme saimme chatbot experttimme Ossian Rajalan ja Aapo Tanskasen auttamaan Grannyn henkiinpuhaltamisessa. PI-pelaajien avustamana saimme Grannyn vastaamaan salibandyaiheisiin kysymyksiin niin tulospalvelun kuin edustuspelaajien taustatietojen osalta. TPS:n ja Goforen yhteistyö oli hitsautunut yhteen ja tässä vaiheessa jo pystyi selkä suorana toteamaan, että olimme saavuttaneet tavoitteemme. Positive Impact oli enemmän kuin kaksi sanaa, olimme aidosti saaneet aikaiseksi jotain hyvää.

”Eikä siinä vielä kaikki”

Edessämme on yhteistyömme toistaiseksi kunnianhimoisin hanke -koko seuran etätreenit. Tarkoituksena on kerätä koronan varjostaman arjen keskellä seuran pelaajat etänä tapahtuvaan yhteistreeniin, jossa paikalla on sekä edustusjoukkueen pelaajia että muutamia yllätyksiä. Positiivisia sellaisia, luonnollisesti. Jälleen kerran osallistamme TPS:n PI pelaajia, edustusjoukkueiden pelaajia sekä koko seuran junioripelaajia yhteisen hyvän äärelle. Koordinoitavaa, suunniteltavaa ja toteutettavaa löytyy tekniikasta lähtien vaikka kuinka paljon, mutta yhdessä toimien tämäkin onnistuu.

Taas ollaan tekemässä jotain, mitä ei olla tehty aikaisemmin.

Kun vielä narisutettiin lelukauppa Casagranden yläkerran lankkulattioita, ei olisi ikinä uskonut, että edustusjoukkueen pelipaidassa on joskus isolla keskellä Goforen logo ja erätauolla näytöillä komeilee Granny pirteän oranssia taustaa vasten. Aika näyttää mitä kaikkea me tulevaisuudessa vielä keksimmekään.

Erityisesti lämmittää nähdä junioripelaajia kaukaloissa Goforen logo rinnassa levittämässä positiivista asennetta lajin pariin.

Rakkaudesta lajiin,
Eero ja Juha
Gofore

Kuva: Salibandyliitto / Anssi Koskinen

Gofore Oyj

Gofore Oyj

Piditkö lukemastasi? Jaa se myös muille.

Yritysten ja urheiluseurojen yhteistyöllä voidaan levittää hyvinvointia ja rakentaa parempaa arkea. Tässä Recoding-podcastin jaksossa pohditaan, millaista on uudenlainen yrityskansalaisuus ja entistä syvempi kumppanuus. Vieraana on TPS Salibandyn toiminnanjohtaja ja hyvinvoinnin puolestapuhuja Toni Autio.

Keskustelua vetävät paremman arjen puolesta puhuja ja julkishallinnon liiketoiminnasta vastaava Riikka Vilminko-Heikkinen sekä datan keräämiseen intohimoisesti suhtautuva ja Turun toimistoa vetävä Eero Rostiala Goforelta.

Logo laitamainoksessa tai pelipaidassa. Siinä perinteinen tapa, jolla yritykset ovat mukana urheiluseurojen toiminnassa. Yritysten ja seurojen yhteistyö kuitenkin syvenee ja muuttuu. Yritykset ovat vahvemmin mukana kehittämässä yhdistysten ja urheiluseurojen toimintaa – ja toisaalta seurat tuovat omaa tietotaitoaan yritysten hyväksi.

Goforekin on haastanut itseään ja miettinyt, miten se voisi toteuttaa yrityskansalaisuuttaan muutenkin kuin rahaa lahjoittamalla. Yksi hyvä esimerkki on vuonna 2018 aloitettu yhteistyö TPS Salibandyn kanssa. Se ei rajoittunut vain seuran edustusjoukkueisiin vaan kaikkiin juniorijoukkueisiin. Yhteistyössä etsittiin tapoja, joilla vahvistaa molempien tärkeitä arvoja, kuten positiivisuutta.

“Perimmäisenä ajatuksena oli keskittyä hyvän tekemiseen lasten ja nuorten kautta. Emme halunneet lähteä sille linjalle, jossa lapset puetaan Goforen logoihin ja jossa he toimivat sympaattisina mainostelineinä digitaalisen Suomen puolesta. Ennemmin tavoitteena oli tehdä oikeasti hyvää ja ansaita se näkyvyys, mikä siitä mahdollisesti seuraisi”, kertoo Recoding-podcastin toinen vetäjä Eero Rostiala.

Goforen ja TPS Salibandyn yhteistyössä syntyi Positive Impact -pelaajien konsepti. Se tarkoitti sitä, että jokaisesta juniorijoukkueesta valittiin kaksi pelaajaa, jotka olivat tunnettuja hyvästä asenteestaan. Positive Impact -pelaajat pääsivät käymään myös Goforella.

“Pelaajat tulivat toimistollemme esimerkiksi ideoimaan salibandy-aiheista sisältöä sisäisessä viestinnässämme käytettävälle Granny-botillemme”, Eero kertoo.

Positiivisuuden viestiä jaettiin ilolla myös TPS Salibandyn toiminnassa.

“Yhteisestä kumppanuudestamme on tullut paljon positiivista palautetta. Olemme pystyneet tuomaan näkyväksi niitä arvoja, joihin kannattaa pyrkiä. Yksi pelaajista pyydettiin kertomaan positiivisuudesta myös omassa koulussaan”, TPS Salibandyn toiminnanjohtaja Toni Autio kertoo.

Recoding-podcast pohtii urheiluseurojen ja yritysten yhteistyötä

Toni Autio on vieraana myös uusimmassa Recoding-podcastissa. Jaksossa keskustellaan siitä, millainen on toimiva kumppanuus yritysten ja urheiluseurojen välillä.

Yksi tapa syventää yhteistyötä yritysten ja seurojen välillä on se, ettei tekemisen fokus ole vain voitoissa ja menestyksessä. Rahalla kun ei voi menestystä ostaa.

“Tärkeämpää on luoda sellaiset puitteet, jossa voi menestyä. Siinä olennaista on hyvinvoiva ja innostunut yhteisö harrastuksen ympärillä”, Autio pohtii.

Yrityksille yhteistyön syveneminen voi tarjota vaikuttavampia tapoja kertoa omasta brändistä ja siihen liittyvistä arvoista. Yhtä lailla se on tapa rakentaa kiinnostavaa työnantajamielikuvaa. Seuroille yhteistyö voi taas olla merkittävä apu siinä, miten pitää harrastusmaksut riittävän alhaisina.

Urheilulla ja liikunnalla on suuri vaikutus hyvinvointiin. Urheiluseurat ovat myös avainroolissa ehkäisemässä syrjäytymistä. Tässä työssä yrityksetkin haluavat olla mukana.

“Tämä on tulevaisuudessa seuroille entistä tärkeämpi tehtävä. Sen merkitys on tullut esille kuluneen vuodenkin myötä, kun monet eivät ole päässeet koronan takia harrastuksiin ylläpitämään sosiaalisia suhteita”, Autio sanoo.

Aution mukaan seurojen ykköstehtävänä on yhteisöstä huolehtiminen. Se, että uusia jäseniä pääsee mukaan ja olemassa olevista jäsenistä huolehditaan hyvin.

“On tärkeää luoda mahdollisuuksia siihen, että jokainen pääsee mukaan omalla tavallaan. Jos paikkaa pelikentältä ei löydy, voi tulla tekemään taustalle asioita, vaikkapa viestintään, tapahtumien järjestämiseen tai tuomariksi.”

Seurat mukaan tuottamaan uusia palveluita?

Urheiluseuroissa on valtavasti osaamista, jota voisi tarjota myös seuratoiminnan ulkopuolelle. Recoding-podcastissa kysytään, voisivatko seurat olla myös palvelujen tuottajia, vaikkapa kunnille ja valtiolle?

Autio sanoo, että osittain seurat tätä jo tekevät. Seurat ovat apuna vaikkapa koulujen aamu- ja iltapäivätoiminnan pyörittämisessä, joskus myös liikuntatunneilla. Potentiaalia olisi, sillä seuroissa on valtavasti sitoutuneita tekijöitä sekä hyviä käytänteitä.

Toiminnan laajentamisessa on kuitenkin rajana resurssit. Työtä kun tehdään pitkälti vapaaehtoisvoimin. Urheiluseuroissa onkin nyt ensin otettava hetki happea ja selvitä ensin koronan tuomista haasteista.

“Potentiaalia monenlaiseen olisi paljonkin, ja uskon, että mullistuksia tulee seuraavan kymmenen vuoden aikana.”

Voit kuunnella Recoding-podcastia SpotifyssaSoundCloudissa ja Applen podcastissa.

Gofore Oyj

Gofore Oyj

Piditkö lukemastasi? Jaa se myös muille.

Aito DevOps huomioi tekijänsä

Uudet teknologiat ovat vallanneet alaa ja sen myötä ihmiskeskeinen DevOps on jäänyt taka-alalle. Kulttuurilla on kuitenkin ohjelmistokehityksessä suuri merkitys, ja alalla kaivataankin nyt aidon DevOps-kulttuurin elvyttämistä.

Ohjelmistoalalla ongelmana on nykyään voimakas teknologialähtöisyys. Kehitys liittyy vahvasti historiaan. DevOpsin alkuaikoina tehtiin kaikki itse, nyt kaikkeen on olemassa oma sovellus, mikä on johtanut siihen, että nyt ohjelmistokehityksessä edetään usein teknologia edellä. Viime aikoina yleistyneet pilvialustat ovat tästä hyvä esimerkki, kaikki haluavat pilveen ja sinne mennään teknologian ehdoilla, eikä välttämättä mietitä parasta toimintatapaa.

”DevOpsin kohdalla mennään työkalut edellä, ja koitetaan keksiä siihen sopivia prosesseja, ja odotetaan kulttuurin sopeutuvan siihen, vaikka aidossa DevOpsissa sen pitäisi mennä juuri toisin päin. Ensin pitää luoda oikea ohjelmistokehityskulttuuri, ja sitten vasta miettiä siihen sopivat prosessit ja valita sopivat työkalut”, kertoo Jani Haapala, Goforen DevOps-arkkitehti.

DevOps tarkoittaa yhdessä tekemistä

DevOpsissa lähdetään liikkeelle siitä, että on olemassa ohjelmistojen kehittäjiä ja ylläpitäjiä. Aidossa DevOpsissa kulttuuriin kuuluu purkaa siiloja ja yhdistää toiminnot kuten kehittäminen, ylläpito ja tietoturva saman yhdessä tekemisen alle. Nykyään ohjelmistokehityksessä tuotannon määrä on lisääntynyt ja syklit nopeutuneet, mikä on johtanut siihen, että alalla ei varsinkaan nuorempien tekijöiden keskuudessa ole ehtinyt muodostua kulttuuria, ja lisäksi alan kirjallisuus keskittyy prosesseihin kulttuurin jäädessä sivuosaan.

”Nyt kaikki haluavat mukaan DevOps-hypeen, ja kuvitellaan että liittämällä Ops-pääte oman jutun perään siitä tulee kulttuuri, vaikka todellisuudessa se ei mene ihan näin. Pyörää ei kuitenkaan tarvitse keksiä joka kerta uudelleen, vaan DevOpsiin voidaan lisätä kyvykkyyksiä sen mukaan mikä on tarve, eikä näitä kaikkia erillisiä Ops-juttuja tarvita, sillä ”, tiivistää Haapala.

Aito DevOps on jatkuvaa parantamista

DevOps ei ole organisaatiotasoinen johtamismalli, vaan enemmänkin tahtoa tehdä maailmanluokan sovelluksia. Siinä annetaan tekijöille vapautta toteuttaa ratkaisuja, ja vastuuta ylläpitää niitä tuotannossa.
Tärkeä osa aitoa DevOpsia on sen ideologia, joka kannustaa tekemään asioita paremmin, ja miettimään parempia tapoja toimia yhdessä. Oikeanlaisen kulttuurin avulla eri toiminnoissa olevat asiantuntijat pystyvät paremmin ymmärtämään oman osuutensa kokonaisuudessa. Tämä ymmärrys auttaa esimerkiksi pilviosaajia ja tietoturvaeksperttejä näkemään itsensä osana kulttuuria, ja lisää yhteenkuuluvuutta eri alojen asiantuntijoiden välillä.

”Aitoon DevOpsiin kuuluu oleellisesti myös se, että työntekijöillä on turvallinen ympäristö ja mahdollisuus innovoida, ja olla ylpeä aikaansaannoksistaan, ja vasta sitten vasta katsotaan mitkä prosessit tukevat tätä ja valitaan työkalut sen mukaan”, muistuttaa Haapala.

Tavoitteena kestävä ohjelmistotuotanto

Arvokas yhdenmukainen kulttuuri tulisi saada koodattua yrityksen DNA:n ytimeen. Tämä edellyttää asioiden kirjaamista ylös, ettei kulttuuri ole vain sen hetkisten työntekijöiden mielissä ja toimintatavoissa, jolloin sekä nykyiset, että tulevat työntekijät pääsevät vaikuttamaan siihen.

Koska DevOps-kulttuuri lähtee ohjelmistokehityksestä, kulttuurin jalkauttaminen laajemmin organisaatiossa voi olla haastavaa. Yhtenäisen kulttuurin jalkauttamisessa voivat auttaa erilaiset muutosvalmentajat, joiden avulla toimintamallia voidaan saada laajemmin läpi eri organisaatiotasoilla. ”Agile-coachit ovat jo arkipäivää, ehkä seuraava trendi ovat DevOps-coachit?”, pohdiskelee Haapala.

Organisaatiokulttuuri rakentuu vuorovaikutuksen ja tekemisen kautta. Yhteiset toimintamallit, kuten tiettyjen asioiden automatisointi tai testiautomaation käyttö kehitystyössä, luovat hyvää yhteistä pohjaa kestävälle kehittymiselle.
Tyytyväiset ja hyvinvoivat ihmiset yleensä tuottavat hyvää jälkeä. Aidon DevOpsin tavoitteena on kestävä ohjelmistotuotanto, johon kuuluu se, että töissä voidaan keskittyä merkityksellisten ja asiakkaille relevanttien asioiden edistämiseen. Esimerkiksi testiautomaation ansioista aikaa ei tarvitse käyttää ohjelmiston virheiden korjaamiseen.

”Tulevaisuudessa kokonaisvaltainen ajattelu valtaa alaa. Koneoppiminen ja tekoäly tulevat nousemaan vahvasti myös DevOpsin puolella, näitä kyvykkyyksiä tarvitaan varmasti lisää lähitulevaisuudessa”, veikkaa Haapala.

Gofore järjestää keväällä Aitoa DevOpsia käsitteleviä webinaareja. Juontajana niissä toimii Jani Haapala.
Lue lisää ja ilmoittaudu mukaan.

 


Alkuperäinen artikkeli on julkaistu 19.4.2021 Tivissä.

Jani Haapala

Jani Haapala

Jani works as a DevOps architect at Gofore. He is passionate about measurement, feedback, and continuous automated quality feedback loops. Jani’s journey started from manual testing and has evolved to full-scale software development automation. Jani thinks that automation can help everybody and increase value in anything.

Linkedin profile

Piditkö lukemastasi? Jaa se myös muille.

With the release of Dart 2.12 & Flutter 2, sound null safety is finally here! What are the benefits, and how do you migrate to null safety? It was all new for Jesper, who decided to read up on the subject and write a few blog posts (start with part 1) about what he learned.

Lists, Sets, and maps can both be non-nullable or nullable and contain non-null or null values.

Map behaves a bit differently than Lists and Sets, which is described in more detail below.

For a more detailed read, visit Google’s documentation: https://dart.dev/codelabs/null-safety.

List, Set

Here’s an informative image on Lists and Sets from Google docs, which is quite helpful:

 

List and Set examples:

List<String?> nullableValues = <String?>[’Kitchen’, ’Bedroom’, ’Bathroom’];

List<String?>? nullableListAndNullableValues = <String?>[’Kitchen’, ’Bedroom’, ’Bathroom’];

Set<String?> nullableValues = <String?>{‘Utensils’, ’Glasses’, ’Plates’};

Set<String?>? nullableSetAndNullableValues = <String?>{‘Utensils’, ’Glasses’, ’Plates’};

Map

Here’s another informative image on Maps from Google docs, which is also quite helpful:

Map types have one special feature: a lookup may return a null value, as null is the value for a key which doesn’t exist in the map, and because of this, you can’t assign them to non-nullable variables.

A short example of this behaviour;

Map<String, int> categories = <String, int>{”Kitchen”: 1, ”Bedroom”: 2};

int value1 = categories[”Kitchen”]; // ERROR – value is non-nullable

The value variable has to be nullable in order for the lookup to work:

int? value2 = categories[”Kitchen”]; // OK

You can also use the ! operator if you’re sure the lookup succeeds:

int value3 = categories[”Kitchen”]!; // OK

A much safer option would be to use the ?? operator, as so:

Map<String, int> otherCategories = <String, int>{”Kitchen”: 1, ”Bedroom”: 2};

int value4 = otherCategories[”Kitchen”] ?? 0;

Required function parameters

One annoyingly behaving feature before Dart 2.12 is the @required keyword. You use it to annotate a specific parameter as required for a function, which takes optional parameters as well. Should the parameter be absent, then the application would throw some kind of error. The Dart analyzer will only warn you if you invoke that function without the required parameter instead of showing an error! This makes no sense, as you have explicitly indicated something as required. That should result in an error, not in a subtle warning.

With Dart 2.12, the parameters of a class constructor or function are required by default. And fortunately, @required has been replaced by required. The Dart analyzer will now show an error if a required parameter is absent, which is quite helpful especially in bigger codebases.

The three null safety principle

As Google puts it, Darts null safety support is based on three design principles:

  • Non-nullable by default. Unless you explicitly tell Dart that a variable can be null, it’s considered non-nullable. This default was chosen after research found that non-null was by far the most common choice in APIs.
  • Incrementally adoptable. You choose what to migrate to null safety, and when. You can migrate incrementally, mixing null-safe and non-null-safe code in the same project. We provide tools to help you with the migration.
  • Fully sound. Dart’s null safety is sound, which enables compiler optimizations. If the type system determines that something isn’t null, then that thing can never be null. Once you migrate your whole project and its dependencies to null safety, you reap the full benefits of soundness —- not only fewer bugs but smaller binaries and faster execution.

The first principle was explained in Part 1; you have to explicitly mark a variable nullable.

The second is something some of you might go for; mixing null-safe and non-null-safe code, nothing wrong with that. Third-party library developers have fortunately been quite active in migrating to null safety. That means that most of our favorite libraries are already null-safe at some capacity, and ready to be used in our next null-safe project.

The last principle is more practical, and what it’s all about; eliminating bugs, better performance, a more enjoyable developing experience, and so on. I strongly recommend that you opt into null safety in your next Flutter application.

In the last part, I’ll be covering migration to null safety. Thank you for reading!

Source:

https://dart.dev/codelabs/null-safety

https://web.archive.org/web/20210304070312/https://dart.dev/null-safety/tour#list-and-set-type

https://web.archive.org/web/20210304070312/https://dart.dev/null-safety/tour#map-types

Jesper Skand

Jesper Skand

Jesper has a great deal of experience in various mobile and full-stack developer roles. He has developed several different Flutter & Android apps and has currently three published apps on Google Play Store. During his spare time, Jesper enjoys time with his kids, coding, and outdoor activities.

Linkedin profile

Piditkö lukemastasi? Jaa se myös muille.

With the release of Dart 2.12 & Flutter 2, sound null safety is finally here!

I haven’t had the time to check it out in more detail, but now that I’ve read about the subject, I decided to write a few blog posts about it. I also created a simple Flutter application, which has non-null-safe & null-safe versions, to support this blog post. The blog posts are also meant to function as a kind of cheat sheet that can be used by anyone.

Sound null safety

Sound null safety itself isn’t a new concept, but now we can finally use it in Flutter, as long as Dart SDK minimum version is set to 2.12!

I’ll go through the basics here, but you can find the complete documentation at https://dart.dev/null-safety.

First of all, what does “sound” or “soundness” mean in a null safety context? Well, Google’s definition is that “if an expression has a static type that does not permit null, then no possible execution of that expression can ever evaluate to null”.

The principles that guided Google when they had to make null safety choices were threefold;

  1. Code should be safe by default
  2. null safe code should be easy to write
  3. the resulting null safe code should be fully sound

A good statement can be found from the docs: “It is not null that is bad, it is having null go where you don’t expect it that causes problems.

When you finally take the step to dive into sound null safety, types in your code are non-nullable by default. That means that variables can’t contain or be null unless you explicitly state so. 

With null safety enabled, the ”runtime null-dereference errors turn into edit-time analysis errors”.

That means that we can avoid unexpected runtime errors if a null value was passed somewhere it shouldn’t have been.

As an example, the following variables are non-nullable when opted for null safety:

 

int amountChecked = 0;

double amountCheckedPercentage = 0.0;

String amountCheckedLabel = ”0.0%”;

int maxChallengesAmount = 0;

To make a variable nullable, we’ll simply insert the “?” keyword after type declaration;

String? title; // declare as nullable, as null indicates that something failed when setting title value

 

That might look familiar to many, as e.g. Swift, C#, and Kotlin use the exact same nullable declaration logic. It was done intentionally the same way in Dart too, to make it easier for developers to declare nullable types. Jumping between languages can sometimes be a hassle, so it’s convenient if they have as many similarities as possible.

Using “late” keyword

The “seasonJourneyModel” variable that contains all the necessary data for the application shouldn’t be null, otherwise, we can’t do anything with our Flutter app. If we don’t make it null, then the Dart analyzer will complain about possible null problems. We know the variable will be initialized later and used only after that. But how do we tell the analyzer that it won’t be null?

The “late” keyword comes to the rescue! It does just what we want; allows us to initialize our variable at a later point, and won’t complain about possible null problems.

 

class Controller extends GetxController {

  // This variable contains all data related to current season journey

  late SeasonJourneyModel seasonJourneyModel;

  …

  Future<void> _init() async {

    …

    String jsonString = await rootBundle.loadString(”assets/seasonJourney.json”);

    seasonJourney = SeasonJourney.fromJson(json.decode(jsonString));

    …

  }

  …

}


However, if we use the variable before it’s an initialization, then a “LateInitializationException” will be thrown during runtime, which ultimately crashes the app.

We can also use the final keyword late, and we don’t have to initialize that variable straight away. We can do that later at our choosing, but we can only assign it once, which is checked at runtime. Calling it twice will cause an exception to be thrown. Using final with late is a great way to model your state that gets initialized at some point and is immutable afterward.

late final SeasonJourneyModel seasonJourneyModel;

According to Google docs, the late keyword has two effects:

  • The analyzer doesn’t require you to immediately initialize a late variable to a non-null value.
  • The runtime lazily initializes the late variable. For example, if a non-nullable instance variable must be calculated, adding the late modifier delays the calculation until the first use of the instance variable.

Using variables and expressions

The Dart analyzer will generate errors when it discovers a nullable value somewhere where a non-null value is required (explicitly or implicitly). It can usually recognize nullable types in variables or expressions inside a function that can’t have a nullable value. 

It is important to handle null values when using nullable variables or expressions. Some possible ways to handle null values can be e.g. if statement or for example the ???. or ! operator;

Using ?? operator:

// value is set as object property if it’s not null, otherwise ”unknownSeason”

 

title = seasonJourneyModel.title ?? ”unknownSeason”;

Null check:

List<Widget> _getDrawerItems(Controller controller) {

  List<Widget> items = [];

  if (controller.seasonJourneyModel.chapters == null) {

    return items;

  }

  …

  return items; // Never null!

}


You should be a bit more careful with the ! operator, since it may throw an exception if the value is null, even the documentation says that ”
If you aren’t positive that a value is non-null, don’t use the ! operator”, so use it with care!

// This will throw an exception if possibleNullValue is null

 

int? possibleNullValue = 1;

int value = possibleNullValue!;

If you need to change the type of a nullable variable, you can just use the as typecast operator.

This example contains as operator to convert a num? to an int?:

print(possibleNullValue()); // prints “null”

int? possibleNullValue() {

  num? age;

  return age as int?;

}


If you change the function to non-nullable and remove nullable declaration from 
as operator, then an error will be thrown:

 

// throws error

print(possibleNullValue());

int possibleNullValue() {

  num? age;

  return age as int;

}


When you’ve opted into null safety, you can’t use the member access operator (.) anymore, since the operand might be null. You can instead use the null-aware version of that operator, 
?.:

selectedChapter?.challenges!.forEach(…


In the next part, I’ll be covering collections among other things. Stay tuned!

 

Jesper Skand

Jesper Skand

Jesper has a great deal of experience in various mobile and full-stack developer roles. He has developed several different Flutter & Android apps and has currently three published apps on Google Play Store. During his spare time, Jesper enjoys time with his kids, coding, and outdoor activities.

Linkedin profile

Piditkö lukemastasi? Jaa se myös muille.

Onko etäpalaverissa ollut olo kuin chat-juontajalla? Luimme Ylen juttua etäopetuksesta, opetustilanteista, joissa vuorovaikutus voi puuttua jopa kokonaan. Ajatus tuntui niin tutulle!

Etäfasilitointi on tullut osaksi yhä useamman työnkuvaa, ja yhdistää myös opettajaa ja muotoilijaa.

“Onko siellä ketään?”

Ennen viime vuotta olisi tuntunut hullulta pitää työpaja etänä, ellei sitten osallistujat olleet eri mantereilla. Oli tärkeää päästä näkemään ihmiset kasvotusten: tulkitsemaan eleitä, kiertelemään tilassa ryhmätehtävien aikaan ja neuvomaan, jos joku näytti tarvitsevan apua. Sitten siirryttiin työskentelemään etänä. Huone vaihtui videopuheluksi ja ihmiset nimikirjainpalloiksi. Saimme vain luottaa siihen, että viesti kulkeutuisi perille sillä osaamisella mitä meille siinä hetkessä oli, sillä osallistujien reaktioita ei nähnyt kuten ennen.

Viimeinen vuosi on mennyt etäfasilitoiden. Olemme kohdanneet paljon uusia haasteita ja päivittänyt metodimme ja työkalumme uusiin. Miettimään erilaisia uusia kannustamisen ja osallistamisen tapoja.

Kun Yle nosti maaliskuussa aiheen esille opettajien näkökulmasta, mietimme että heidän jos jonkun ammattiryhmän olisi tärkeää saada osallistujat mukaan ja oivaltamaan! Artikkeli listasi erilaisia opettajien kokemia haasteita, joista mieleenpainuvin ja samastuttavin oli vertaus chat-juontajaan – juuri tuo edellä kuvattu tunne siitä, kun puhut tyhjyyteen ja toivot, että yleisösi on siellä ja pysyisi mukana.

Palvelumuotoilijoiden ideat myös opettajien käyttöön

Keskustelimme artikkelista muotoilijakollegojen kanssa. Miten tuttuja haasteita siinä listattiinkaan! Aloimme pohtia, josko meidän kokemuksista ja metodeista voisi olla hyötyä myös opettajille. Voisimmeko me jotenkin auttaa?

Palvelumuotoilijan työssä tärkeintä on se, että saamme osallistujat aktivoitua, osallistumaan ja oivaltamaan. Meidän tehtävämme on saada osallistujat tuntemaan olonsa turvalliseksi, jotta he uskaltavat jakaa kriittisempiäkin ajatuksia ja huolenaiheita. Ilman tätä emme voi onnistua työssämme.

Muutama viesti artikkelin lukemisen jälkeen ja olimme jo ideoineet, kuinka jakaa vinkkejä ja oppeja opettajille osallistavaan etäfasilitointiin. Pyörät pyörivät, webinaarin agenda rakentui ja lähdimme kartoittamaan opettajien tilannetta kentällä. Haluammekin jakaa etäfasilitoinnin menetelmiä, työkaluja ja iloa opettajien käyttöön.

Webinaariin ilmoittautuminen on käynnissä, tule mukaan!

Tule mukaan webinaariin: Osallistava virtuaalifasilitointi opettajille 5.5.2021

Päivi-Mari Hietamies

Päivi-Mari Hietamies

Päivi-Mari Hietamies työskentelee Goforella Senior Designerina palvelumuotoilun, UX-suunnittelun ja visuaalisen suunnittelun parissa. Työssään hän fasilitoi virtuaalisesti työpajoja sekä yhteissuunnittelee käyttäen työkaluja Teamsista Zoomiin ja Mirosta Muraliin. Hän on pirskahtelevan energinen, tehokas ja luova suunnittelija, jolle on tärkeää ottaa huomioon käyttäjien aidot tarpeet ja oppia uutta.

Linkedin profile
Linda Macken

Linda Macken

Linda on kokenut palvelumuotoilija, jonka sydän sykkii empaattiselle muotoilulle. Hänen vahvuuksiansa ovat laaja-alainen ammattitaito, kyky hahmottaa suuria kokonaisuuksia ja rohkeus tarttua uusiin haasteisiin. Työssään Linda on mukana uusien toimintamallien ja digitaalisten palvelujen strategisessa suunnittelussa.

Linkedin profile

Piditkö lukemastasi? Jaa se myös muille.

“Kun et tunne toista ihmistä, et osaa ajatella häntä”, sanoo konsultti ja saavutettavuusguru Azra Tayyebi. Saavutettavuudesta puhutaan yhä enemmän, ja nyt siitä keskustellaan myös tässä Recoding-podcastissa.

Keskustelua vetävät paremman arjen puolesta puhuja ja julkishallinnon liiketoiminnasta vastaava Riikka Vilminko-Heikkinen sekä datan keräämiseen intohimoisesti suhtautuva ja Turun toimistoa vetävä Eero Rostiala Goforelta.

Jos Azra Tayyebin tehtäväksi annettaisiin suunnitella toimistorakennus, voisi käydä niin, että hän unohtaisi sieltä valot. Konsultti, saavutettavuusguru ja verkkoteknologiden asiantuntija Tayyebi ei välttämättä muistaisi niiden tarpeellisuutta, sillä hän on ollut viimeisen kymmenen vuoden ajan täysin sokea.

Se, mikä on Tayyebille tarpeetonta, voi olla muille elintärkeää. Sama pätee toisinpäin: Tayyebilla on tarpeita, joita taas joku muu ei kaipaa yhtä paljon. Hän käyttää esimerkiksi kokopäiväisesti ruudunlukijaohjelmaa tietokoneessaan.

Vuonna 2018 Euroopan Unionissa asetettiin saavutettavuusdirektiivi, joka velvoittaa suunnittelemaan palvelut niin, ettei kukaan jää niiden ulkopuolelle. Esimerkiksi nettisivujen on oltava käytettäviä yhtä lailla sokeille ja näkeville kuin kuuroille ja kuuleville. Täysin tasa-arvoisia useimmat palvelut eivät kuitenkaan vielä ole. Oikeastaan totuus on vielä hyvin kaukana siitä.

“On valitettavaa, että olen ennemmin yllättynyt, jos jokin palvelu jopa toimii saavutettavuuden kannalta”, Tayyebi sanoo.

Toisaalta Tayyebi antaa kehuja siitä, että direktiivin myötä asiat menevät nyt eteenpäin. Suomessakin on parissa vuodessa tapahtunut selvä muutos. Esimerkiksi Kelan palvelut ovat jo paljon saavutettavampia, samoin Suomi.fi-verkkopalvelussa on tehty isoja edistysaskeleita. Siellä on rakennettu esimerkiksi toimiva digitaalinen tunnistuspalvelu.

“Suomi.fi-palvelun toimivaa esimerkkiä voisi hyödyntää muuallakin. Se alkaa olla jo erittäin hyvin saavutettava”, Tayyebi toteaa.

Ei erityispalveluita, vaan palveluita kaikille

Tayeebi työskentelee konsulttina Avaava Oy:ssä, joka myy ja edustaa esteettömyyttä ja saavutettavuutta parantavia ratkaisuja. Tällaisista ratkaisuista puhuttaessa vilahtelevat puheessa usein sanat erityinen, erityisratkaisut tai erityistä apua tarvitsevat ihmiset.

“Minä ja kollegani emme kuitenkaan pidä sanasta erityisryhmä. Se kuvastaa asennetta, jossa on olemassa erityisihmisiä, joille pitäisi olla jotakin erityistä tarjolla. Lähtökohta pitäisi ennemmin olla se, että asioita tehdään käyttäjille ja asiakkaille, joilla on erilaisia mutta samanarvoisia tarpeita.”

Tällaiseen ajatteluun ja toimintatapaan on kuitenkin vielä paljon matkaa. Saavutettavuusdirektiivi on yksi askel oikeaan suuntaan, mutta mitä muuta tarvitaan, jotta yhdenvertaisuus toteutuisi oikeasti? Tayyebin mukaan lakien säätämisen lisäksi kaivataan kulttuurin muutosta.

“Tasa-arvo ei ole vielä aidosti kulttuurissa sisällä, jos eri tarpeita palvellaan vain, koska halutaan olla hyviä ja kilttejä ihmisiä. Yhteiskunnan arvoissa ja toimintatavoissa pitäisi olla automaattisena se, että toimitaan yhdenvertaisesti, eikä niin, että asioita tehdään välillä erikseen ja lisätöinä.”

Kulttuuria puolestaan muokataan Tayeebin mukaan tietoa jakamalla ja tietoisuutta lisäämällä.

“Emme tunne vielä kunnolla toisiamme. Monilla on vielä tosi vähäinen ymmärrys vammaisuudesta ja siitä, mitä oikeasti tarkoittaa, että joku ihminen on sokea tai kuulovammainen. Kun et tunne toista ihmistä, et osaa ajatella häntä.”

Saavutettavuus suunnitteluun alusta asti

Paras tapa varmistaa saavutettavuuden toteutuminen on huolehtia, että se on näkökulmana mukana palvelun suunnittelussa alusta asti. Tayyebin mukaan tämä onnistuu niin, että projektiin palkataan erikseen saavutettavuudesta vastaava ihminen. Tällöin vältetään myös kalliit viime hetken virheet ja korjausliikkeet.

Ylipäätään saavutettavuuden toteutuminen pitäisi olla samalla viivalla kaiken muun kanssa, vaikkapa visuaalisuuden. Oikeastaan saavutettavuus voisi olla vahvana arvona jo brändin rakennuksessa, sanoo Azra Tayyebi.

“Toivon, että näin tehtäisiin yhä useammin. Saavutettavuuden esiin tuomisella on mahdollisuus myös erottautua.”

Voit kuunnella Recoding-podcastia SpotifyssaSoundCloudissa ja Applen podcastissa.

Gofore Oyj

Gofore Oyj

Piditkö lukemastasi? Jaa se myös muille.

Neural network from scratch

When I was studying computer science at the University of Helsinki, I went to one machine learning course. During the first lesson, there was a prerequisites test, which I failed spectacularly, and gave up the course right there. After that, I did not look into neural networks for many years despite making my master’s thesis about optimisation algorithms in real-time strategy games, where they certainly could have been one viable solution.

Somewhat recently I was interested in stock trading. That inspired me to read a little about neural networks and I tried building one that predicts the stock market with Google’s open-source solution TensorFlow. Obviously, that did not work, but still, I learned the basics of TensorFlow, which is probably one of the best options to use if you want to get something real done. You only need to describe the network, give it a bunch of parameters and TensorFlow handles the actual implementation.

Some weeks ago I stumbled across these four great videos describing neural networks in a way that really increased my understanding of how they actually work. But still, I felt that the last piece was missing. To really understand what is happening and how I needed to implement one myself. All the way from scratch.

Recognising numbers – an approach with less math

For this project I chose probably the most standard schoolbook problem there is: recognising handwritten digits. This kind of image recognition task – while far from trivial – is one where artificial neural networks have been proven to work well countless times already. Also, large amounts of labeled training data is easily available.

If one wants to build an efficient neural network, the obvious and likely easiest way to go is to make heavy use of matrix operations and other linear algebra. I suppose that is one reason why python is a common language here, given its good library support for such things.

But that was not my goal. If I wanted to build a concrete understanding as possible of how the network works, I felt that for me personally, the math would be in the way, obscuring things, although I do see why many would disagree.

So instead I chose a more object-oriented approach to model the network, with a sprinkle of functional programming on top (don’t want to feel like a dinosaur after all, despite maybe having a minor 30’s crisis). My language of choice was Kotlin, which along with TypeScript is one of my clear favourites at the moment. I did not use any dependencies, everything is plain handwritten Kotlin.

Given that a random number generator would guess the digit correctly 10% of the time, I set my initial target to 15 % success rate, just to prove that the network is at least doing something right. As it turns out, this was rather pessimistic and to my surprise, the final success rate made it past 90%, and after adding some ad-hoc convolution layers it exceeded 96%. Let’s see how we got there!

What is it and how does it fly

First of all, I want to emphasise that this is not an area that I have extensively studied, so some of the terminology and theory here may be slightly inaccurate. But then again, the whole point of this post is to show that you can already get some pretty cool things done without spending a lot of time with your nose stuck in a book.

That out of the way, I will now first attempt to explain how an already-trained network functions. How does it get from a bunch of pixel values in the input image to a single number being the recognised digit?

Basic concepts

Most of this revolves around three very simple concepts, which are also at the center of my object-oriented model.

  1. A neuron, with a loose analogy to the brain, is a node in the network. Each neuron has an activation value; a number describing how active it is. You might also visualise the neuron as a light bulb and think of the activation as how brightly it is lit. Each neuron also has a value called bias, which could be thought of as a minimum power needed for the neuron to light up.
  2. Between neurons there are connections. Each connection has a weight, which describes the strength of the connection, i.e. how much the activation of the previous neuron affects the activation of the latter. Weight can also be negative, in which case when the input neuron gets brighter the output neuron gets dimmer and vice versa.
  3. While this may not really have an analogy with brains, usually the network is organised into layers. Each layer is simply a set of neurons. The very first layer is the input layer, the very last one is the output layer, and any layers between them are so-called hidden layers. In a most basic network, every neuron on every layer is connected to every neuron in the next layer. How many hidden layers there should be and how many neurons should each have is very difficult to know in advance. Mostly it is a matter of trial and error: keep trying different things until you get lucky and something gives good enough results.

Propagation from input to output

Now that we have laid out some terminology and described what the network consists of, let us next look into how it works and how it can be used.

First, we shall manually set the activation of each neuron on the input layer so that they somehow encode the input data. In this case, the input is a grayscale image of 28×28 pixels, each of them having a value of how bright/dark that pixel is. Our input layer will thus consist of 784 neurons, each of them associated with one pixel, and the activation value between 0.0 and 1.0 will be set to the brightness of that pixel.

Next, we will start moving on layer by layer. For every neuron, we will calculate its activation based on the connections from the previous layer. I will soon explain how exactly is each activation calculated.

Finally, we arrive at the output layer. In this classification problem where we are trying to recognise digits between 0 and 9, there are 10 different options. Thus our output layer should have 10 neurons. Once we have calculated an activation level for each of them, our best guess for the digit in the image is simply the index of the output neuron that has the highest activation.

Calculating activations

Calculating the activation of a neuron is done with a simple propagation function. At its core, it is just a weighted sum. You look at each neuron on the previous layer and multiply their activations with the weights of the associated connections. Then you just sum them up and finally add the bias of the neuron.

The last step we should do is to put the result through a so-called activation function. Here again, there are many alternatives that you can try, but the most classic option is a function called sigmoid. That function basically squishes the input from the previous step so that the end result is always between 0.0 and 1.0.

Class model and forward propagation implementation

Now we have a general understanding of the terminology. As mentioned, I chose to implement the network in an object-oriented model, so I know you must already be itching to see some UML diagrams. No worries, I got you, buddy!

So essentially there are just those three concepts we talked about – neurons, layers and connections – but they have been broken down a bit more. For example, every Neuron has activation, but OutputNeurons on the OutputLayer have no further output connections since it is the last layer. Therefore, the outputs field is on the TransmitterNeuron interface, which is implemented by InputNeuron and HiddenNeuron but not OutputNeuron. Connection has weight and ReceiverNeuron has bias. That is pretty much all.

With these constructs, after we have set the activations on InputNeurons based on some image, the matter of propagating the activations forward all the way to the OutputLayer only requires calling one function for every ReceivingLayer in order

which in turn only calls a simple eval function for each ReceiverNeuron

As discussed earlier, the activation is calculated simply by taking a weighted sum of the input activations, adding the bias, and then crushing the result through an activation function such as sigmoid.

How to train your network

As seen above, implementing the network is very simple if you somehow already have a set of values for every single weight and bias. Assuming our network has those 784 input neurons, 10 output neurons and for example just one hidden layer of 16 neurons, that already means there are 784 * 16 + 16 * 10 = 12 704 connections with weights, and 16 + 10 = 26 biases (neurons on input layer do not have bias). A total of 12 730 parameters. Making the network ”recognise” something is all about finding values for these parameters that happen to cause certain inputs to produce correct outputs.

If we were to consider only one image, say one that represents digit 2, it would be rather easy to find some weights and biases even manually so that the output neuron associated with digit 2 would have activation close to 1.0 and every other output neuron would have activation close to 0.0. But that is of course not enough. We need to find values that simultaneously lead to correct answers with any of the thousands of training images representing different digits.

So how do we actually get those? The first idea might be to just randomly try different values until we find something that works. Unfortunately, since there are so many parameters, that would be way too slow. It would be like trying to navigate in a huge forest without a map or a compass while blindfolded. Fortunately we can devise sort of a compass by using a process called gradient descent.


Credit: https://xkcd.com/1838/

Gradient descent and backward propagation

If you really don’t like math, skip this chapter. 😉

First, we need to define a cost function, which tells us how badly the network behaves at the moment. The cost function typically used in neural networks is

C = (a – y)2,

where a is the activation value of an output neuron and y is the value we would have wanted it to be. That of course needs to be summed up overall output neurons and also averaged over sufficiently many training images. Now we have a single number to measure how bad the network is and we can improve the network by trying to minimise that.

Now imagine that you are standing on a hillside. If we think of the cost function as how high you are, that means you want to go down the hill and find the bottom of the valley. However, it is night and you can not see anything. In that case, you could try to feel the ground around yourself, find out the direction where the descent is steepest, take a step towards that direction and repeat. By using this algorithm you are at least guaranteed to soon end up at the bottom of some valley. That is the idea of gradient descent.

So we could just adjust the parameters a tiny bit and see which way the cost function decreases the most. Unfortunately, the number of parameters is still an issue. Basically, we are not standing on a normal hillside, but on one that is 12 730 dimensional, so there are just too many possible directions to feel around. So again we need to reach into the toolbox of mathematics to find something that helps us speed up the process.

While the cost function seems to be a function of the activation values, those, in turn, are functions of the network parameters. Therefore, the cost function is in the end just a function of all those different parameters, albeit rather complex ones, and it is possible to analytically find a partial derivative of the cost function with respect to each different parameter. Then in order to take a step towards the right direction, we simply evaluate the partial derivative for every parameter, multiply it with some small constant (a step size) and subtract it from the parameter’s current value. Now we have a working compass that tells us the direction to go!

I will not go too deep into the mathematics of how to derive those partial derivatives, but if you start from the last layers, it starts off quite simple. For example, with some high school mathematics, you can see that if

C = (a – y)2 = a2 – 2ay + y2

then the partial derivative of it in respect to a is:

dC/da = 2a – 2y = 2(a – y)

Next, we need to consider how for instance some weight w0 would affect a. That is, what is the partial derivative da/dw0. Once we have that we can use the chain rule to find the partial derivative of the cost function C with respect to the weight w0

dC/dw0 = da/dw0 dC/da

As I promised not to dive too deep into mathematics, I will not even try to show what is that da/dw0, but hopefully, you now have at least a very vague idea of how we would approach solving these derivatives. As you might have guessed, the derivatives get rather complex once you move further away from the output layer, since there are so many things that chain together. Fortunately, though, it turns out that once we have figured out all the partial derivatives of the last hidden layer LN-1, it is enough to find partial derivatives of the activations on that layer with respect to the parameters of the second last layer LN-2. Then we can chain those together to find the partial derivatives of the cost function with respect to the parameters on layer LN-2. This way we only have a limited amount of math to figure out, and then we just apply it recursively, moving back one layer at a time. This is called backward propagation.

Implementation of the training algorithm

It is quite possible that I derived some of the equations incorrectly, but be as it may, the end results are decent and with machine learning that is all that really matters, so I will dare to present my code that relies on those equations. If we take those for granted, my solution becomes quite simple.

First, we need to do a couple of small additions to our data model. To be able to average partial derivatives for weights and biases over multiple images and to keep track of partial derivatives by neuron activations when back-propagating we add these lists:

  • Connection
    • derivativesOfCostByWeightPerSample: List<Double>
  • ReceiverNeuron (i.e. HiddenNeuron and OutputNeuron)
    • derivativesOfCostByBiasPerSample: List<Double>
  • HiddenNeuron
    • derivativesOfCostByActivation: List<Double>

For OutputNeuron we also add a field desiredActivation: Double which will contain the expected activation value based on current image.

Then the training algorithm consists of these 8 relatively simple steps:

Step 1

Clear derivativesOfCostByBiasPerSample for every neuron and derivativesOfCostByWeightPerSample for every connection

Step 2

Clear derivativesOfCostByActivation for every HiddenNeuron

Step 3

Use a training image as input and propagate forward to set all activation values. Set desiredActivation for each OutputNeuron to 0.0 or 1.0 based on what digit the image represents.

Step 4

Call the train function on each OutputNeuron:

Here on line 4 we have the previously discussed derivative of the cost function. The train function starting on line 6 simply calculates some values and adds them to lists.

On line 7 we need the derivative of the activation function, which in the case of sigmoid is

Step 5

Call the train function on each hidden neuron on the last hidden layer. Then do the same for every neuron on the previous layer, and so on.

Here we again just calculate similar stuff and add the results into lists. On lines 8, 16 and 21 you get a taste of Kotlin’s nice functional API with the averageBy function. The lambda you give it is evaluated for each element of the list and then an average is calculated from those. Clean, concise and simple!

Step 6

Repeat steps 2-5 for at least some portion of the training images.

Step 7

Call the nudgeParameters function for each ReceiverNeuron, which will adjust all the parameters based on the previously calculated results.

Here on line 5 I chose to limit the bias always between -0.8 and 0.8.

Step 8

Repeat the steps 1-7 until the results no longer improve. That is all!

Online demo

For presentation purposes, I also wrote this demo application with React and Typescript. There you can draw a number yourself and the app will try to recognise it. The results are not quite as great as with the pre-made test data set, possibly due to differences in normalising the images, but still rather impressive in my opinion. How it works is that I implemented the forward propagation part of the network also in TypeScript and imported a pre-trained network from a JSON file that I had exported from the Kotlin version. The rest is just basic React and HTML5 stuff.

Feel the force, read the source

I challenge you to go through the same exercise yourself during some rainy weekend to make use of these times of isolation. I feel this was a very interesting and rewarding little project. However, you may also have a look at all the code in my full solution and use it as you wish: https://github.com/Joosakur/neural-network-test

Are you eager to learn something new? Are you interested in challenging tech projects? Check out also Join the Crew 

Joosa Kurvinen

Joosa Kurvinen

Joosa is an experienced fullstack software developer with a very broad skill set. He is always eager to learn and try out new things, regardless of whether that is with backend, frontend, devops or architecture. He has an agile mindset and always strives after clean and testable code. Joosa graduated from the University of Helsinki and wrote his master's thesis on AI and optimization algorithms.

Linkedin profile

Piditkö lukemastasi? Jaa se myös muille.

Our Gofore: Jani Haapala

I’m Jani Haapala and I work as a Quality Consultant and DevOps evangelist. I’m very fortunate since I do not get to do these things only during my working hours but I can use all my knowledge and learnings in my free time when I am coding my own megalomanic home automation project.

Gofore is an extremely warm and employee-focused company. Gofore makes a great deal of effort to make employees happy and satisfied with the work. There is a huge number of different groups that you can join to do things that you feel interested in. You can affect a lot that what kind of work you want to do. Gofore has also lots of opportunities and possibilities for self-development.

Tell us about your career and study background?

I have a background of being an electrician who got super-interested in technology and went to polytechnic to study telecom networks and coding. During the studies, I got an internship place from Ericsson and as a bonus, got a possibility to do my thesis work for them also. My thesis subject was selected to be ”An automation solution for testing team needs”. That was the moment when testing and automation flies bit me… hard. From that moment on I have been extremely interested in testing, test automation, and lately infrastructure automation and automating software development processes. I stayed on Ericsson for about three years and switched to a company called Espotel to be a consultant, more specifically, test automation consultant. My first assignment was with Nokia Siemens Networks. That was the time that got me interested in consultant work since it is a profession whose role is to help others to succeed with your expertise. I stayed on Espotel for about three years until I found Qentinel.

Qentinel was focused on testing and test automation so it felt like a natural move, so I joined them. I have been now in Qentinel/Gofore as a Quality Consultant for a bit over ten years and I am still loving my company every day. My career has taken me from a manual tester into becoming a Lean and DevOps evangelist with a flavor of software development process automation specialist. I also feel that I am very fortunate since I do not get to do these things only during my working hours, but I can use all my knowledge and learnings in my free time when I am coding my own megalomanic home automation project.

What makes Gofore a great company for you?

Gofore is an extremely warm and employee-focused company. Gofore makes a great deal of effort to make employees happy and satisfied with the work. There is a huge number of different groups that you can join to do things that you feel interested in. You can affect a lot that what kind of work you want to do. Gofore has also lots of opportunities and possibilities for self-development.

I appreciate that the employer offers training opportunities such as Udemy online courses as well as libraries. Another important point is the opportunity to attend conferences to get new ideas. All of these have been things keeping my passion alive and made it possible that I have got to try new learnings in customer projects and also my perspective have evolved. And once you have learned and tried things enough and been able to use them in many contexts, it is natural to start helping colleagues as well.

What are the things you most likely tell your close circle about Gofore?

About all the enthusiastic people that Gofore has and all the things that these people do daily to make work fun and inspirational. Also, all the possibilities that you have to develop yourself.

What is the best Gofore memory?

There are lots of warm memories with all the lovely customers that I have been able to work but the Gofore memory, I have to say some of the many gatherings that we have had. For example, our Coding days where we have been together, coding with Legos and RasperryPi’s. Competing on various wacky coding challenges together and won some cool prizes too.

What is your favorite internal Slack channel at Gofore?

I am a huge photography fan and was so amazed to see that there is an own channel for us, so it has to be the #photograpy-gerho. 😊

What are the technologies and tools you mostly work with?

I mostly work nowadays with Infrastructure as Code (IaC), DevOps, and CI/CD pipelines so the tooling and technologies come around that. Some of the key tools and technologies are AWS, Docker, Kubernetes, Jenkins, Python, RobotFramework, GithubActions, Linux.

How does your typical day look like? What excites you most in your daily work?

My calendar tends to get quite fast full of meetings. Meetings with various customers, sales support meetings, and team meetings. I do nowadays lots of designing, documenting, and coaching work also but I always try to reserve some time for the pure coding, experimenting, and building work. The most exciting and rewarding moments in my work are the situations where I manage to help somebody else to be happier on their work. That can be through coaching, solution design, or solution building that helps somebody else to be more efficient, do things more easily or enjoy one’s work more. And maybe learn something new also.

What would you like to do in the future at Gofore?

I would like to take the DevOps concept further on our company and make it more understandable for everybody. I would also like to help to create opportunities for everybody to have a good career path, to evolve and gain new skills. Especially in the areas of DevOps and Test Automation.

What kind of digital world do you want to build with others?

The digital world and digitisation are all about the speed and fast phase of ideas, competencies, and concepts that are filling our daily life. It is not enough anymore to study a profession and do that until you get retired. Nowadays there needs to be a humane and ethical way to deal with this digitisation fuzz and help people to stay ahead of it. This means that we need to create enthusiasm, opportunities, and learning paths for people to constantly learn new things and develop their skills. Having needed skills makes people feel needed and part of something greater, and that brings happiness.

For me example, I do not feel that I am working, I feel that I have a passion for the things that I do and as a bonus I get also paid to do those. I live and breathe the things that I do and the technologies that I use. I want that everybody can also feel this passion and get the same feeling and opportunity to be passionate. Everybody needs the possibility to be good at their passion.

 

It’s often said that digitalisation has changed every part of human life – how we live, how we work and how we interact with the world around us. But when you think about it, digitalisation alone can’t change anything. Without people, it’s all just hardware and code. That is why we want you to get to know our people, Goforeans. Read Our Gofore stories! 🧡

Are you interested in job opportunities at Gofore, read more about us, and check out the open positions gofore.com/liity-joukkoon/

Gofore Oyj

Gofore Oyj

Piditkö lukemastasi? Jaa se myös muille.