A successful DevOps culture requires a genuine and holistic change in behaviour through people.

In the last ten years in my own role, I have supported changes which have basically entailed the removal of various walls, fences, gaps and barriers blocking the view. When you dare to peek across to the other side, that is an achievement in its own right: “Oh, so that’s what they really do there!” In addition, if a conscious effort is made to engage in dialogue with those working on the other side of the fence instead of just looking, even more can be achieved. Prejudices and uninformed beliefs can be broken down into fragments of knowledge that can be put to use in a work community or organisation in many ways.

Knowledge of what others in your organisation do creates an understanding that in turn creates a foundation for interaction, empathy, and a generally humane approach. These things are elements of psychological security, to which we will return a little later.

Although what DevOps is may ultimately be easy to understand on paper, it may be more difficult to determine how to succeed in it. Most often, we start from a situation where fences between communication gaps and corporation culture have developed over long periods. A functional and productive DevOps requires the following, at least:

  1. A strong common will at the organisational level that is embraced by all individuals. For DevOps, however, the state of mind cannot be just anything; it must be related to working for customer value. Everything that is done is related in one way or another to making improvements for the benefit of customers. A common will helps in many ways. It can steer debate onto the right track, provide an opportunity to address situations where the common will may not materialise, and everyone knows that the work done is judged against the common will. Fair for everyone, and also pretty simple, right?
  2. Concrete action at both the individual and organisational level to remove walls, fences, communication gaps and visual barriers. Instead, efforts should be made to create seamless collaboration between software development, administrators and a multi-expert business team, where responsibility for the end result is shared. It’s not just a way of doing things together, of asking others for feedback from time to time. In my experience, leaving things to “asking sometimes, and seeing where it leads”, most of the time leads nowhere. How many fences are there for you to break down, and how high are they?
  3. Cooperation at its most authentic and intensive. In order to be able to say out loud in a work community that people are in disagreement, or on the other hand to reveal the weaknesses of one’s own work processes to a colleague, people need to be able to feel psychologically secure. When a solid psychological security is experienced in the work community, everyone can dare to be themselves. This creates the best possible basis for learning from others and for more productive work by daring to fail, whether faced with a colleague, supervisor of your own team or CEO. Rapid failure is also well known to be a prerequisite for inspiration and the emergence of new ideas. This is what DevOps also strives for. In the DevOps state of mind, everyone can make their voice heard despite differences, and in the end it is precisely because of these differences that the opinions of others are sought. Everyone’s voice is heard, and everyone’s voice counts. What could be a more effective way to create a sense of belonging, and a desire to succeed?

DevOps is described in several contexts as a culture, and points 1 to 3 above are each building blocks of culture. Culture is defined in various ways, for example as a body of shared attitudes, values, goals, and practices. And in an organisation, culture is embodied in various practices, for instance in recurring rituals such as meetings and gatherings around specific themes, different milestones, rewards, and organisational discourse. If one tried to describe the DevOps-style culture with adjectives, words like open, honest, permissive, self-nurturing, enterprising, imaginative and enthusiastic come to mind.

The first step toward a successful DevOps culture is to recognise and accept that the change needed in organisational culture and collaboration is potentially large, and that achieving it will require both sufficient time and change and a strong will to see the change through to completion.

In my experience, the best possible result in efforts toward any sort of change is achieved whenever it involves all the people who will be affected by it. When you get to be personally involved in defining your own future ways of working, it’s much easier to commit to them – and commitment brings success, always. Have you begun the transition towards people-oriented and productive DevOps work?

Avatar

Kirsi Keränen

Kirsi Keränen works as a Senior Consultant at CCEA Oy. She is a positive, calm and relentless change expert who has executed change in the roles of project manager, internal/external consultant and coach. In her daily work, she is first and foremost driven by helping others.

Linkedin profile

Do you know a perfect match? Sharing is caring

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, part 2) about what he learned.

Migrating your dependencies and code to nul safety is quite easy, and Google has done a good job documenting it here: https://dart.dev/null-safety/migration-guide. I’ll try to do a more straight-to-the-point explanation of the processes, but recommend that you also read the documentation if this is your first time.

The basic steps for migration, as described in Google docs:

  • Wait for the packages that you depend on to migrate to null safety.
  • Migrate your application’s code, preferably using the interactive migration tool.
  • Statically analyze your application’s code.
  • Test to make sure your changes work.

One key thing to remember is that migrating an application is technically the same as migrating a package. So before you start to migrate your application, make sure that all of your dependencies are ready for null safe.

You can migrate to null safety before your dependencies support null safety, but you may have to change your code when your dependencies finally support null safety. Dart and Flutter core libraries are null safe and are still usable by applications that haven’t migrated to null safety.

Migrating dependencies

Step 1: switch to Flutter 2.01 and Dart 2.12

Run command “flutter –version” to output your current versions;

As you can see from the image, I have Flutter SDK version 1.22.6 with Dart SDK version 2.10.5.

To upgrade into Flutter 2 with Dart 2.12, you simply run “flutter upgrade”.

The result after the upgrade with “flutter –version”:

As you can see, my Flutter SDK version is 2.0.3 and Dart SDK version 2.12.2, which means that I’m ready to continue!

Then you need to set your minimum Dart SDK version to 2.12 in your pubspec.yaml file:


Step2: Check the status of your dependencies

To get the null safety migration state of your application’s dependencies, run the following command: “dart pub outdated –mode=null-safety”

From the image above, we can see that both “get” and “percent_indicator” dependencies are not null safe, but have prereleases (green text) that are null safe.

If the output says that all the packages support null safety, then you’re ready to migrate!

Otherwise, upgrade dependencies if possible using the command output.

One important thing to remember about dependencies & null safety:

When all of an app’s direct dependencies support null safety, you can run the app with sound null safety. When all the dev dependencies support null safety, you can run tests with sound null safety.

You might also need null-safe dev dependencies for other reasons, such as code generation.

Step 3: Update dependencies

Before migrating your application’s code, update all the dependencies to the latest versions which supports null safety: “dart pub upgrade –null-safety”

As you can see from the image, “get” and “percent_indicator” dependencies have been upgraded. We can even see from the version names that they support null safety.

Then we need to run “dart pub get” to actually fetch the dependencies.

Now when I run “dart pub outdated –mode=null-safety” I get the following result:

Migrating your code

Most changes will likely revolve around inserting the ? or late keywords, or perhaps missing required keywords for non-nullable named parameters.

You have two options when it comes to migration:

  1. Use the migration tool, which can resolve many of the basic nullable cases.
    1. I’ll refer you to Googles documentation for this, as it’s quite detailed: https://dart.dev/null-safety/migration-guide#migration-tool
  2. Migrate your code by hand.
    1. This is quite straightforward

Migrating by hand

Google recommends that you first migrate leaf libraries, you can find more information about that here: https://dart.dev/null-safety/migration-guide#migrating-by-hand

To migrate a package by hand, follow these steps:

  1. Edit the package’s pubspec.yaml file, setting the minimum SDK constraint to
    1. sdk: ‘>=2.12.0 <3.0.0’
  2. Run “dart pub get” (running “dart pub get” with a lower SDK constraint of 2.12.0 sets the default language version of every library in the package to 2.12, opting them all into null safety)
  3. You’ll probably see a lot of different analysis errors in your application, and that is completely normal.
  4. Migrate the code of each Dart file to null safe using the analyzer to identify all the problems (adding ?, !, .?, required or late keywords, null checks etc.)
  5. Done!

Analyze code

Analyze your code in order to check for possible errors by running “flutter analyze” or “dart analyze”

Test your code

Remember to also fix your test files so that they are null safe, and then running all the tests to ensure that everything works as it should by running e.g “dart test”.

That’s it, hopefully, you’ve found this article helpful! Migrating to null safety is quite easy (depending of course on the complexity of your project) and provides many benefits.

Thank you for reading!

Source: https://dart.dev/null-safety/migration-guide

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

Do you know a perfect match? Sharing is caring

Our Gofore: Juha Siltanen

My name is Juha Siltanen and I work as a Software Architect at our Turku office. I’ve involved myself in various software projects during my 20-year career and my duties have been mostly in the backend components doing the heavy lifting. I’m still loving what I do. I’ve had more than a few employments during my career and I’ve never felt more welcome than when I started working for Gofore.

Tell us about your career and study background?

When I was a young boy, I never had much interest in computers nor in technology for that matter. I was mostly interested in arts and drawing and considered myself a decent would-be artist. That is until I decided to draw a human face. I just couldn’t draw the eyes good enough, so that pretty much thrashed that path. I got decent grades at school and when it was time to pick a profession, I just browsed the selection of educations you could get, and “Bachelors degree in computer science, digital information technology” had a tone that spoke to me for some strange reason. I’ve involved myself in various software projects during my 20-year career and my duties have been mostly in the backend components doing the heavy lifting. I’m still loving what I do.

What makes Gofore a great company for you?

The company has great values which you can remember and that the company follows. There is a strong drive to make Gofore a great workplace for everyone and you can actually see it happening in your day-to-day work. Being a somewhat senior grunt makes me appreciate the provided independence and the responsibility that comes with that. I also enjoy being able to allocate my time to other things than pure billable work. I have been helping with our recruitments and also, together with Eero Rostiala, building up a sponsorship relationship with TPS Salibandy, positive impact with Gofore. I feel these internal activities are very important to me and give me a nice energy boost.

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

Most of my so-called close circle wouldn’t understand what I’m actually doing, so I would probably tell something about the nice people our Turku office has attracted. We have an exceptionally good atmosphere and when we have the opportunity to meet each other, it’s always pure gold.

What’s the best Gofore memory?

I’ve had more than a few employments during my career and I’ve never felt more welcome than when I started working for Gofore. So, I would say the first days at Gofore are the most memorable. I feel very lucky to have found such a nice place to be.

What is your favorite internal Slack channel at Gofore?

I have to say “turku” of course! 😊

What are the technologies and tools you mostly work with?

Currently, I am working on a project of a larger scale than I’ve ever worked on before. We are using SAFe framework for scaling the agile work we do in teams and it has worked very nicely. My technology stack at the moment includes (among others) Java, Git, Spring Boot, Azure, PostgreSQL, ElasticSearch, RabbitMQ, and very soon Kafka -which is an entirely new thing for me. The main point is to keep evolving and the list of used technologies and the way we use them evolves constantly and we are getting better at them. In the end, the technologies are just tools for solving the given problem.

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

My daily work usually consists of having a couple of short meetings and the rest of the day is spent sleeves rolled up and building software. I enjoy software development, and everything it encompasses, and hopefully will be able to do so in the future.

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

Gofore culture and the way we work enables us to pretty much shape the personal responsibilities ourselves. I enjoy that and hope to continue doing that in the future. Hopefully, I will be building even better software for our customers.

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

Hopefully, we will be providing tools that have an actual positive impact on the world we live in. Make someone’s chore easier to complete or a process finish much faster.

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/jointhecrew

Gofore Oyj

Gofore Oyj

Do you know a perfect match? Sharing is caring

Instead of a tool- and process-oriented approach, we should focus on fostering a shared culture and noticing the needs of the people around us.

DevOps was designed to enable effective collaboration and abolish information silos in software development and digital solutions. In recent years, the method has become increasingly widespread.

Gofore’s DevOps architect Jani Haapala wishes to highlight a common challenge in implementing DevOps.

“With DevOps, people often focus on the tools and process. We feel that DevOps is first and foremost a culture and that we should progress with people in mind. When the culture has been defined and the shared values have been established, we can create the processes and choose the tools that best support them,” he says.

According to Haapala, this creates genuine, sustainable digital evolution and a safe working environment for everyone.

Cultural capital investments as a part of operations

The popularity of DevOps is partially explained by the fact that it’s such an integral part of the biggest success stories in the digital industry, such as Apple or Google.

“In these companies, culture plays a big role, it’s almost like a religion. It allows everyone in the organisation to move in the same direction.”

Haapala believes that once the culture is right and commonly understood, DevOps will deliver the desired benefits.

“Both speed and amount of innovations are increased.”

Haapala wishes that people would be as willing to invest in culture as they are in tools. He finds this to be especially important in our current situation, where our methods of collaboration have been completely recast.

“I have witnessed personally that in companies where there’s a strong cultural model of collaboration, remote work hasn’t really changed a thing. In other companies, however, moving to a home office might have cut off communication completely,” Haapala says.

He also wishes to draw attention to the fundamental principles of DevOps that consist of both freedom and responsibility.

“We shouldn’t attempt to control the method from above, but rather give its users unlimited freedom – but also the responsibility – to fulfil the client’s wishes,” he says.

 


The original article has been published 30.4.2021 as a part of Future of Finland publication.

Gofore organises webinars on Real DevOps this spring. The webinars are hosted by the expert consulted in the article, Jani Haapala.

 

 

Gofore Oyj

Gofore Oyj

Do you know a perfect match? Sharing is caring

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

Do you know a perfect match? Sharing is caring

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

Do you know a perfect match? Sharing is caring

Remember our Working on the move experiment? It’s finally time to share thoughts and tips on why and how to work on the move.

“The main surprise was and is – why haven’t I done this before!?” —Andres Allkivi, Software Architect

During the four weeks period, Goforeans worked on the move together impressive 400 hours! They logged together 690 entries into the Heiaheia app where we tracked hours moved while working, and shared well over 100 pictures among colleagues. Challenge was spread around the whole group and people were participating in all our cities from Madrid to Jyväskylä. Also, people from different roles took actively part in the campaign.

The most active Goforean during the campaign was test automation specialist Sergei Pavlov. He worked on the move during the campaign time impressive 52 hours which means a rough 2,5 hours every day. “I found it easier to pace my day and get more done when doing some exercises while working. Working remotely alone at home I easily spend all day sitting on my computer, when I get a walk or a jog during the day, I feel more fresh and relaxed after work,” he highlights.

Are you interested in working on the move, but don’t know where to start? Our experts share their tips:

Goforeans suggest you try these sports while working

Double the joy and take your furry friend with you when working on the move.
  • kettlebell weightlifting
  • walking
  • dancing
  • stretching
  • running
  • skiing
  • rubberband workout (tip: the rubberband can be fastened to the chair)

If you still need some motivation, check these

What happens in the future: do we continue working on the move? 

” I think this was  “the” starting point for my movement habit – done once, could happen twice “, Tero Vartiainen. 

We already know that people will work more on the hybrid model in the future. We strongly see that people should pay attention to the way they work especially when they are working from their homes. Going to the office, or some other working location and moving around during the day is much more active than staying at home. This leaves room for innovations that would activate the office worker during the day. We believe that simply taking breaks and pauses is not enough. The logic behind ‘working on the move’ is simple: when you move, you also activate your thinking muscles. When you move you are more productive and happier. Ta-daa! 🎉  “I’ll definitely continue working on the move,  and will try to find new ways to work on the move.”, Andres says and we suggest you follow his advice.

Nina Etelävuori

Nina Etelävuori

Nina is a Communications Strategist who works with Gofore's internal communications and employer brand. She thinks that these two tasks go well hand in hand to make sure that Gofore's recruitment marketing and employer brand correspond to the internal employee experience.

Do you know a perfect match? Sharing is caring

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

Do you know a perfect match? Sharing is caring

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

Do you know a perfect match? Sharing is caring

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/en/join-the-crew/

Gofore Oyj

Gofore Oyj

Do you know a perfect match? Sharing is caring