Recently in a project, I stumbled into an interesting puzzle and decided to share it with you along with my own solution. Part of the credit goes to Jarkko Hyöty for great feedback and ideas.
We needed a data model where exactly two persons could have a partnership with each other during some time period, while one person should never belong to more than one partnership simultaneously. Initially, the problem sounded quite simple, but it starts to get a little more tricky as you dive deeper into it – especially if you want to model those rules as database constraints. It might make sense to model this sort of a thing rather in a graph database, but we wanted to stick with the good old PostgreSQL which we were already using.

First steps towards the solution

So how might we start to model this in a relational database? My first idea for modelling this kind of one-to-two relationship was something like this:

CREATE TABLE person (
    id              SERIAL PRIMARY KEY, -- auto-incrementing integer
    name            TEXT NOT NULL
    -- other person details ...
);
CREATE TABLE partnership (
    id              UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
    person1         INTEGER REFERENCES person(id) ON DELETE CASCADE,
    person2         INTEGER REFERENCES person(id) ON DELETE CASCADE,
    start_date      DATE NOT NULL,
    end_date        DATE NOT NULL CHECK (start_date <= end_date)
);

That does perfectly limit the number of partners in a partnership to exactly two. However, there is a troublesome distinction between person 1 and person 2. The roles in the partnership are not strictly equal. So maybe something like this instead?

CREATE TABLE partnership (
    id              UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
    start_date      DATE NOT NULL,
    end_date        DATE NOT NULL CHECK (start_date <= end_date)
);
CREATE TABLE partner (
    id              UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
    partnership_id  UUID REFERENCES partnership(id) ON DELETE CASCADE,
    person_id       INTEGER REFERENCES person(id) ON DELETE CASCADE
);

Limiting the maximum number of partners

Now we no longer distinguish between partners 1 and 2, so that is good, but since this is now a standard one-to-many relationship, it no longer covers the requirement of there being exactly two people in a partnership. That requirement can be divided into two parts: 1) a partnership must have at least two members, and 2) a partnership must have at most two members. The first one is actually quite difficult, so let’s return to it later. The second one, however, can be solved for example like this:

CREATE TABLE partnership (
    id              UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
    start_date      DATE NOT NULL,
    end_date        DATE NOT NULL CHECK (start_date <= end_date)
);
CREATE TABLE partner (
    partnership_id  UUID REFERENCES partnership(id) ON DELETE CASCADE,
    ind             SMALLINT NOT NULL CHECK (ind BETWEEN 1 AND 2),
    person_id       INTEGER REFERENCES person(id) ON DELETE CASCADE,
    PRIMARY KEY (partnership_id, ind),
    UNIQUE (partnership_id, person_id)
);

Here we have removed the separate primary key id from the partner table and instead added a composite primary key of partnership_id and ind. Here ind (i.e. index) is a new integer column, where the value must equal either 1 or 2. Because it is used in the composite key, it cannot have the same value twice within a partnership. Therefore it enforces the constraint that there can be at most two people in a partnership. Finally, we also add a unique constraint for (partnership_id, person_id) to prevent one person from being in a partnership with him/herself, although we will soon be adding another constraint which effectively also does prevent this.

Preventing overlapping partnerships

What about the requirement of no overlapping partnerships then? At first, it sounded impossible to enforce this on the database constraint level, but after some research, I found out that the PostgreSQL exclude constraint combined with a date range and an overlap operator might be a potential fit for this. For example, in this presentation by Jeff Davis, those were used to guarantee that there are no overlapping reservations for the same room. However, applying it to our current data model does not seem to quite work out. This kind of constraint can only compare rows within one table, but we have person_id and dates in different tables. Could we somehow still make it work?
Let’s try changing our data model a bit more by moving the date columns from the partnership association table into the same partner table with person_id. At this point the partnership table has no other columns left except for the primary key id. Therefore it is actually redundant and can be left out if we just generate the shared partnership_id at the application level instead.

CREATE TABLE partner (
    partnership_id  UUID NOT NULL,
    ind             SMALLINT NOT NULL CHECK (ind BETWEEN 1 AND 2),
    person_id       INTEGER REFERENCES person(id) ON DELETE CASCADE,
    start_date      DATE NOT NULL,
    end_date        DATE NOT NULL CHECK (start_date <= end_date),
    PRIMARY KEY (partnership_id, ind),
    UNIQUE (partnership_id, person_id)
);

Since all the relevant data is now located at the same table, it becomes possible to add an exclusion constraint which prevents one person from having two simultaneous partnerships. The idea we want to express in the constraint is that if any two rows have the same person_id and their durations overlap, then that is a constraint violation. The syntax for that is this:

EXCLUDE USING gist (
    person_id WITH =,
    daterange(start_date, end_date, '[]') WITH &&
)

Note that this uses a gist index so you need to have the btree_gist extension enabled.

CREATE EXTENSION IF NOT EXISTS btree_gist;

As you may have noticed while changing the data model we simultaneously created a new problem though. Since the dates are no longer shared through the association table, it is now possible for the two members of a partnership to have different durations for it. This does not really make sense here. Fortunately, that can be remedied by adding a couple more exclusion constraints which will cause a constraint violation if two rows with equal partnership_id values have differing start_date or end_date values. Note that when updating dates of an existing partnership, this constraint will temporarily break until the second row has also been updated, so we must make these constraints deferred to only enforce them at transaction commit time.

EXCLUDE USING gist (partnership_id WITH =, start_date WITH <>) DEFERRABLE INITIALLY DEFERRED,
EXCLUDE USING gist (partnership_id WITH =, end_date WITH <>) DEFERRABLE INITIALLY DEFERRED

Limiting the minimum number of partners

Finally, let’s return to the constraint that a valid partnership should always have two persons in it – not just one. As far as I know, this can only be solved by a trigger function which checks the partnership member count on insert, update and delete, and raises an exception if the constraint is violated. Furthermore, it has to be a deferred trigger, so that the rule is only enforced at transaction commit time, instead of raising an error immediately when you try to insert the first person to the partnership before you even have had a chance to insert the second person.
This kind of triggers can cause a considerable amount of complexity and may be difficult to maintain and debug, so in the end, we decided that enforcing this constraint is best left to the application code since it is anyway trivial to do there.

Testing out the solution

So does our finished data model now make sense and is it usable? Let’s draft a couple of queries to validate that!
Creating a new partnership can be done by inserting two rows having same partnership_id, start_date and end_date with each other and different ind and .

-- Assuming we have two persons in the person table with ids 1 and 2:
-- INSERT INTO person (name) VALUES ('Donald'), ('Daisy');
INSERT INTO partner VALUES
    (
        '336a7c66-a43c-478d-a724-a65b377d77ee',     -- some generated partnership_id
        1,                                          -- first ind
        1,                                          -- first person_id
        '2018-01-01',                               -- start_date
        '2019-06-30'                                -- end_date
    ),
    (
        '336a7c66-a43c-478d-a724-a65b377d77ee',     -- same partnership_id
        2,                                          -- second ind
        2,                                          -- second person_id
        '2018-01-01',                               -- same start_date
        '2019-06-30'                                -- same end_date
    );

Fetching partners for a given person is slightly more complex, but not too bad either. We just need to fetch all rows where there exists another row with the same partnership_id and the desired person_id. So to get for example Donald’s partners we could write:

Fetching Donald’s partners
SELECT person_id, name, start_date, end_date
FROM partner p1                                         -- Select from partner table
LEFT OUTER JOIN person ON person.id = p1.person_id      -- (joined with the person table just to get the partner's name too)
WHERE EXISTS (                                          -- all the rows for which there exists
    SELECT 1 FROM partner p2                            -- another partner row
    WHERE p2.person_id = 1 AND                          -- where the person is Donald (id 1)
          p2.partnership_id = p1.partnership_id AND     -- which is part of the same partnership
          p2.ind <> p1.ind                                -- but is not the same row
);

Resulting in

person_id
name
start_date
end_date
2
Daisy
2018-01-01
2019-06-30

Updating a partnership duration can also be done with just one simple statement, even though the dates must be updated on both rows. Note also, that in order to keep the time comparisons simple we did not make end_date nullable, but we can use a postgreSQL special value ‘infinity’ if we want to express that a partnership currently has no known end date. That value can later be converted into null at the application level if desired.

UPDATE partner
SET start_date = '2018-02-10', end_date = 'infinity'
WHERE partnership_id = '336a7c66-a43c-478d-a724-a65b377d77ee';

All in all the solution seems to work fine, even though one can question if it would have made more sense to just check all the constraints in the application level to keep the data model simpler. Anyway, if you have alternative solutions for this little puzzle I’ll be interested in hearing them. Cheers!

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.

Do you know a perfect match? Sharing is caring

Double the money, triple the fun?


I’m Minna Vänskä, a service designer at Gofore. I’ve been working in the tech world now for 21 years. By training, I’m a Master of Arts. Earlier in this blog series Jaana told how she focused on mathematics instead of English. Funnily enough, now I’m working with Jaana side by side for the same customer at Gofore and I did study English as my main subject at the university. 🙂 Other studies included information studies (earlier library science), journalism and mass media, multimedia and audio-visual culture. And now that I look back on it, it’s a pretty perfect combo together with the years of experience in IT and added with some studies in service design.

While growing up I remember wanting to be a queen or an archaeologist. I was telling (lecturing) people everything I knew about king Tut at the age of 8. Then, of course, everyone wanted to be a ‘lakasukoneenkuljettaja’, basically driving a Zamboni, or ice resurfacer to be exact. I’ve always liked to make stuff: To create a doll out of a used shampoo bottle, to paint and do renovations. The kind of problem-solving that I like is to construct and sew a winter jacket or cut and build a stained-glass lamp from scratch.

Being part of a product building machine

At the age of 22 I got a summer job at Nokia, I did my thesis there and got paid for it, something that is still too rare in the Arts faculties. By the time I was 25 I had graduated and was busy writing the user guide for the first-ever Symbian OS phone. At Nokia I got to work with amazingly talented, world-class specialists, experts, and product builders, I learned about mobile technologies, customer care, logistics, factory setup, design for sustainability, and package design to name a few. And I think we were among the first to try out the legal design. I got enthusiastic about user research, observing people in context, about analytics and visual communication. I still think that one of my greatest career achievements has been the Visual user guide for the emerging markets: innovating and creating something new, a visual user guide, serving millions of customers better with a deliverable that cost 0.01€, it saved money and the environment.

I’ve always worked in a male-dominant workplace with an 80/20, or even 90/10 ratio and if you weren’t technically trained or “an engineer” you really were the odd one out. We used to joke about the elevators at Nokia, if a man didn’t crush you at the doors, he must be a lawyer. Being from an arts background didn’t make me a lesser employee for the company though. I remember the HR being brilliant at the time. If someone was able to get the work done and stand the scheduling pressures, they wanted to keep you. And no, not everyone could stand the pressure or be able to handle the complexity of 5 simultaneous product programs and their 200+ different SW variants and that didn’t have anything to do with gender or training.

Systemic problems cannot be solved in silos

I had heard about service design already when working at Nokia and saw it as a solution to the many systemic problems that I had seen. I strongly believe that multi-disciplinary teams working towards a shared goal of customer success is the most efficient way for organisations to a) invent new solutions and b) fix customer issues. However, organisational structures are often preventing this, teams are incentivised wrongly and rarely are decisions made based on data.
Currently, at Gofore I’m responsible for user needs studies, design sprints, innovation, concepting and service design. I like to arrange multi-disciplinary workshops, involve users by applying service design methods.  My eyes shine in the moments when I see teams gathered around a customer journey or customer problem and working hard together to fix it. What I currently want to focus even more on are:

  • AI and robotics combined with the understanding of human behaviour
  • helping companies to be more knowledge lead with the help of analytics and data visualisation
  • building services that bridge the digital and physical realms and, for example, use drama exercises in testing these out

Equal support for your career

My career in tech has taken me to a London park for a morning run, to Texas, Georgia, Poland, Italy, Belgium, and Lapland. Early on I realised that a career in tech would be a much better provider than many of the possible careers in arts. Being a teacher or a librarian would’ve meant maybe half of the salary. But there’s no glory unless you’re enjoying it all. For me having a partner to balance the load has been essential. It’s not easy having small kids and mom off to London semi-regularly.

I’m a third generation of women with a university degree. But things are not always progressing for the better. My granny had 5 kids and worked in industry as an economist. But unlike many mommies nowadays she didn’t even try to shine in every area of life. In the 1950’s she had both a housekeeper and a nanny to help her. And in the summer kids were sent to the “farm” out of the way. Today, women try to be perfect on all fronts of life. For me, it’s been a life long path to finding balance, deciding what I like to focus on, what I value most, where I enjoy being and not worry about the rest so much.

Keep at it

I was listening to the keynote talks from Charity Wanjiku of Strauss Energy Ltd. and Laura Tirkkonen-Rajasalo of Sulapac at the WiT event on Friday and the experiences felt similar. Don’t mind what society is telling you your role or path should be like, you can do it. Perseverance or ‘sisu” is key and 99% of achievement is showing up every day and doing the work. Also, don’t shy away from new opportunities, it’s OK to be scared and by practice, you can overcome your fears.
My motto is from Virkkukoukkunen:

Lentäminen on asennekysymys – To fly is all about the attitude.

Read the previous parts of this Women in tech blog series here:
Passion for continuous improvement
A love for mathematics led the way
Subconscious career design
Value your skills – they are needed in tech
My career in tech – a continuous learning curve
Finding my own material to design
Working as a woman in tech

Minna Vänskä

Minna Vänskä

Minna has several years of experience working with international teams, across organizational boundaries, running UX studies, concepting, development and quality improvement projects. She is an experienced user researcher and communication specialist. At Gofore she is responsible for user research, user experience design, and service design projects. Minna loves to apply service design methods in user research and to involve organisations and people to discover the insights.

Do you know a perfect match? Sharing is caring

This blog series is split into four parts:

  1. General information of secrets management
  2. Example how to store secrets into a version control
  3. Example how to use encrypted secrets in CI-pipelines
  4. Security issue and threat analysis based on previous examples

I’ll provide a small threat and security issue analysis based on this blog post series and the examples used, case-by-case.

Compromised personal or service account cloud credentials

It is possible that some member’s or service account’s credentials are compromised one way or another.
Simple steps for protecting compromised accounts:

  • Revoke and reissue credentials
  • Regenerate API keys
  • Look logs for unauthorised access and usage
  • Remove unexpected resources on a cloud platform

At least the Google Cloud Platform (GCP) has its own detailed guide for compromised credentials which can be read here: https://cloud.google.com/security/compromised-credentials
Last but not least: always use the multi-factor authentication for personal credentials.

Reducing vulnerability to a key compromise

If an attacker gets access to the key and encrypted secrets then all the secrets can be exposed – well, game over. The attacker can take your skeletons from version control and use them to carry out harmful acts like exposing IP addresses, passwords, API keys or even something worse.
However, you can reduce vulnerability to a key compromise with a few simple things.

Don’t re-use keys too often

With proper secrets management, you should never use a single, “one-and-only”, key for encrypting and decrypting all the secrets you have. You should create a new key which has its own purpose and encryption and is only for specific data.
In this blog series, I’ve used only one key to demonstrate how Mozilla SOPS work. You could make environment or version control repository based keys which would make things harder for an attacker. In the Very secret development operations, part II: Storing skeletons into a version control -blog, there was an example of how multiple different keys can be used with environment-specific rules (Advanced usage – Creation rules configuration).

Rotate keys and remove old versions of a key

Key rotation is a simple method to prevent key compromise: the old version of the key is versioned to history and a new, primary version of the key is created. Only the primary key is used for encrypting (and decrypting) the secrets while the old versions of key are used only for decrypting the secrets. Still, an attacker can have an old version of the key and use that for data leakage – but not for long if you remove old versions of the key!
You can manually or automatically rotate or destroy keys in cloud platforms. GCP has multiple guides regarding key management like:

In the Very secret development operations, part III: CI-pipelines -blog, there was an example of how to setup rotation period for a key, so GCP rotates keys automatically.
With SOPS you can renew the data key from the secret by command:

sops -r test.enc.yaml

For further reading about key rotation with SOPS: https://github.com/mozilla/sops#key-rotation

A person leaving the project/organisation

If a person is leaving a company or a project they can be a sort of security issue if they still have access to resources after they have left. You have to always revoke access to all systems and keys which they have used.
While SOPS handles access to keys automatically, you only have to revoke access to cloud platforms and servers where your keys are stored. GCP has a good guide for revoking access to different resources: https://cloud.google.com/security/data-loss-prevention/revoking-user-access
Also, remember to revoke access to a version control – like remove a member from a Gitlab group or project.

What could happen after a compromise?

As I mentioned earlier, an attacker could use a compromised key to make harmful acts like exposing IP addresses and passwords. But things can be even worse than that, so I’ll mention a few aspects.

  • Loss of sensitive information
    • Personal data
    • Industry secrets
    • IP addresses
  • Financial losses
    • Illegitimate financial transactions
    • Fines
    • Compensation to customers
  • Loss of reputation
    • Customer
    • Professional
    • Business

After all, your business can close down pretty quickly after the security breach. So keep your skeletons well hidden and secure secrets with proper secrets management, follow common security practices and follow, or even create security policies for your business and project.

Further reading:

Jarkko Koistinaho

Jarkko Koistinaho

Jarkko works as a technical project manager at Gofore and he is a quality and testing oriented professional in the software industry. Depending on the situation, he could be a software engineer or a Scrum Master in addition to being a software tester. Jarkko can also do some DevOps-tasks. Model-based testing and performance testing are his special skills.

Do you know a perfect match? Sharing is caring

During the past two years, I had the luxury to be a part of a large-scale program that involved several development teams across the world. As an agile coach and scrum master of one of the teams, together with my colleagues, we built a development team that ended up being one of the best crews I’ve been working with so far. However, we didn’t get it right on the first – or even on the second try. There were some important lessons to be learned. In this posting, I’ve listed my three most important takeaways.
 

Encourage active and respectful face-to-face communication

Ideally, all the teams should be co-located. There is no better way than to simply walk to another person and to start asking those questions. This overcomes any other means of communication. However, in a global environment, this is not always possible. Thus, it’s crucial that, if there are multiple teams in different locations, you really need to go out there and meet those people. Or, alternatively, fly them over to your country and make them feel comfortable. This is a must and we learned it the hard way. If you don’t have the travel budget, pick it from your own pocket – I can assure you it will be one of your best investments for the project.
After you have made acquittances, use video conferencing tools as much as possible in everyday communication. It might feel awkward in the beginning, but again, it will improve how the teams communicate. If the other team does not have the equipment, it’s a great idea to buy them a proper webcam as a gift when you pay a visit.
Meeting people in person, having a laugh and working together, side-by-side makes all the difference. This will also enable people to establish common rules for working together more easily. Different cultures have different customs and e.g. ways of saying things. Like it or not, we also all have sub-conscious stereotypes of different countries and cultures. If the people you’re interacting with daily are mere virtual icons in your teleconferencing tool it easily becomes “us and them”. This is definitively not the setup to be in when you have to deal with more difficult issues.
So, in essence,

  • Co-locate the teams, or fly them over to visit as soon as you can
  • Get to know people, then use video conferencing tools and encourage relaxed communication
  • Beware of “us and them”

Enable the team to evolve and remember to have a safety net

Preferably, the team’s composition should not change too often. Effective communication within the team, building an identity and your sense of humor will take some time, so be patient. Yet, setting the team composition in stone from day one might be problematic. The skillset the team needs at the very beginning of the project is usually different from the skills team members require over a longer period of time. In the beginning, you may need to have more specialists working with, say, concepting and service design. Later on, as the product evolves, the team might gravitate more towards operations or security-related topics.
An experienced team can identify these needs themselves, but it’s worth making this clear from the very beginning: changing the composition of the team is natural as the project evolves and everyone should keep their eye on whether the team has the best fit to deliver at a given time. Of course, the team is usually very capable of learning new things and sharing skills, as long as there is a decent time frame for this. Sudden changes will affect the psychological safety of the team, so avoid hasty decisions – involve the team, they know best what is required.
The chemistry within the team is something to look after. Even the brightest minds don’t work well together if the way of working simply does not match each other. Active discussion and even strong opinions are quite all right, as long as the team can work things out. However, very strong personalities can sometimes dictate discussion, even unintentionally. In this case, there’s a great danger of losing valuable insights and ideas. To overcome this, the team can take advantage of a plethora of tools available ranging from online anonymous feedback systems to tools used in retrospectives. Also, having an external coach to facilitate these discussions can prove to be valuable. The team should be coached towards non-violent communication and you should lead the example. As a last resort, don’t be afraid to make changes to the team. Having a fruitful working environment weighs more in the longer run, even in the case where the team might temporarily lose some technical talent.
Lastly, the team should take care of easy and fast onboarding processes. You will never know when one of your team members finds the love of her life or even a better job opportunity – which is on the other side of the world. For a highly efficient team, it’s a great safety net to make sure that whenever new team members are joining, they will get going as quickly as possible. It will improve the ability of a team to get back in the normal pace and reduces the anxiety of a new member of the team starting anew. Make sure everyone knows how to get started, where to obtain credentials and access rights, where to look for introductory tutorials and so on. And when the new team member joins the team, her insights and perceptions of the project might prove very valuable – especially with regards to the on-boarding process, but also on the project in general. The team might be blind to things and habits that have lost their value a long time ago.
Three key points regarding team evolution:

  • Encourage people to step outside their comfort zones to learn new things – remember that changing the team composition should not be the first choice
  • However, the skills needed in the project might change over time – involve the team to determine what is required at a given moment and then proceed
  • Even the best teams will also face unexpected attrition, so be prepared

Be curious, challenge the status quo and empower the team

The same way as a newcomer to the team can see things differently, so the whole team can see the new project in a different light. There might be something very evident blocking the team on its way to success, which the team can immediately spot. So, challenging existing structures and the status quo should be encouraged.
When it comes to these blockers, there are many sayings, such as “it’s better to beg for forgiveness than to ask for permission”. With this kind of thinking it is often tempting to start from a clean slate. “Things would be so much easier and more fun if we simply ditched this box here and recreated it ourselves”. Sound familiar? Honestly, if you don’t need it, get rid of it. Still, it’s good to think twice before blindly overhauling all the existing processes and tools already in place. They usually are there for a reason. Before making any big changes, the team needs to understand how the underlying system of work works.
The other point of view is that a fresh team does not yet carry the weight of the organization. Hopefully, the team is free of any strong opinions regarding other departments in the organization. As no bridges have yet burned, the team might be able to approach different parts of the organization in a more neutral way and thus learn more this way. This should be encouraged.
The point is to actively seek the actual users of the system, even if (and especially when) it has not been the custom, or when there is a proxy entity representing actual end-users. What are the users really trying to do – do they actually need this system? This is often overlooked, or users are misrepresented by some other entity. There might be existing tools or structures, parts of the system, that are then, once again, recreated. But the team can easily go on for a long time before really understanding what it is actually building and for whom – or does it even make sense? Once the team really understands the real needs, it should be empowered to make even bold decisions regarding what really should be done next.
In the end, it comes back to active communication. Perhaps it’s about pointing out the elephant in the room no-one else is willing to see. Then, having the right people expressing themselves in a polite but decisive manner – and at the same time being able to express and reason themselves clearly – will make a world of difference.
Remember that

  • A new team has “fresh eyes” – try to benefit from it
  • There might be existing structures that, in the end, serve no real meaning
  • Understanding the system, i.e. what users are trying to achieve, is the key thing

Conclusion

From my experiences, I can guarantee that building a highly-effective team from scratch is challenging and will require some trial and error. If you work with a global customer and with different cultures and time zones, things will not get any easier.
There are many things to keep in mind, but always make sure that you communicate your intentions, what the team is currently doing and where you are heading next in the clearest and concise way possible.
Do your best to find the most suitable composition for the team. Try to keep changes to a minimum, but always remember to think ahead and have a plan B ready, if and when you are forced to change the team.
Encourage the team actively to seek a better way. Usually, things are in place for a reason and they can always be done differently. The trick is to know whether the features the team is implementing are actually taking the product forward from the real customer’s point of view or are they merely feats of engineering. And finally, always remember to ask why.

Avatar

Kustaa Huhtala

Kustaa is a software professional with strong expertise in versatile modern technologies, agile methodologies and project management. He has also worked as a lead developer, agile coach and scrum master in several projects.

Linkedin profile

Do you know a perfect match? Sharing is caring

Digitalization is helping organizations and individuals build and expand their networks which leads to meaningful cooperation. Increasingly these networks are sharing time, insights and information and co-creating new business models and services. Business rules are in such constant change that regulators are struggling to keep up. To be resilient and stay relevant in this networked world, organizations need to constantly innovate new meaningful ways to communicate, interact and form relations with different participants. This does not happen from inside the company.

Understanding the wider scope, systems, value streams and relationships and how they work is a key element in driving innovations in this networked world. Many organizations claim to be customer centric however if you ask their customers the answer might be quite different. Customer surveys or ‘Happy or Not’ buttons at checkouts might give a quick impression of the organisation’s concern but this can be a false impression. Truly customer centric organizations curiously explore their customers’ holistic experiences in their world and changing contexts. To understand these, design methods such as observation techniques and contextual participatory methods are required.

The same can be said for understanding employees within the organisation. Employees know best what happens at the intersections with the external network participants they work with. Companies should never outsource their eyes and ears. Innovations do not flourish in an environment that does not listen to both their internal and external network participants.

Making the shift from company centric to customer and network-centric

Value in co-creation needs to be mutually beneficial whether it is monetary, experiential, environmental or societal. Meaningful innovations require a radical mindset shift in organizations – from company centric to customer centric and all the way to network centric. To drive innovations that are meaningful to different participants, real network centric organizations build their innovations around experiences. They try to understand people’s activities, practices and experiences in their world and in a context that extends beyond the organisation’s products and services. That is only possible when understanding individual behaviour and that isn’t easy.

People might be end-users, citizens, consumers, customers, employees, clients, partners or contributors and you need to observe them and listen to their stories, find out what is important to them in their world and in changing contexts, and find out why.

Are you company-centric or network-centric?

Using Design Thinking to facilitate constant change

Organizations that fearlessly withstand uncertainty and trust non-linear and iterative innovation processes driven by people-centric data have an advantage. The Design Thinking approach drives valuable innovations that are new to a specific context and time, creating value for all collaborative participants in a meaningful way. Innovations ultimately always need to be aligned with actual network participants’ unsatisfied and important jobs, pains and gains if they are going to be successful. This means that if an organisation`s innovation intent is not people driven, but technology and business driven, those innovations need to be validated with evidence that people really care about the innovation intent.

The powerful mindsets of design thinking guide the whole organization to break down silos and build an open, transparent and trust-building atmosphere that supports collaboration and the sharing of information and knowledge. This helps to cultivate an innovation culture that embraces the experiences of employees and external network participants.

The world in which we are living, and the future may seem foggy, but when you go out and observe the world with an open mind and with empathy, everything becomes clearer. The future does not just arrive – it is co-created within networks.

Read the extended version of this blog from our publication: Recoding change (pages 106-111)

Do you believe in positive change? That our actions can change this world for the better? Read more in our pubilication, recoding change (NOTE: Booklet is written mostly in finnish): Recoding change

Marjukka Rantala

Marjukka Rantala

Marjukka is a Senior Business Designer at Gofore. She combines design thinking and business expertise with innovation processes. Together with co-creation this brings value to all key stakeholders in a network, business, and ultimately society. She helps clients to grow their innovation capability, break down silos and to develop an innovation culture that embraces the experiences of employees and external network participants to build ecosystems and services that are meaningful for people in their everyday life.

Linkedin profile

Do you know a perfect match? Sharing is caring

This blog series is split into four parts:

  1. General information of secrets management
  2. Example how to store secrets into a version control
  3. Example how to use encrypted secrets in CI-pipelines
  4. Security issue and threat analysis based on previous examples

After the secrets are stored securely on a version control, it is time to look how a continuous integration pipeline should handle the secrets. In this guide, I’ll use Gitlab’s pipelines.

Configuring GCP

First of all, you have to create a new service account and a new KMS key which is used only by the service account.

  1. Head to https://console.cloud.google.com/iam-admin/serviceaccounts, select the project used and create a service account
  2. Create the private key in JSON format and temporarily store it on your computer. It will be used later when configuring Gitlab CI-pipeline.
  3. Then open https://console.cloud.google.com/security/kms, select your preferred keyring (or create a new one) and create a new key
    1. Purpose: Symmetric encrypt/decrypt
    2. Rotation period: 7 days (I’ll tell about this on the last blog post)
  4. Select the key created and add the service account with decryption access (‘Cloud KMS CryptoKey Decrypter’) to it


Modifying access to the cryptographic key

Encrypting with a new key

If you have already encrypted secrets, you must add the key to the secrets after setting up the new cryptographic key.

# Adding GCP key
sops -r -i --add-gcp-kms projects/<gcp project>/locations/global/keyRings/sops/cryptoKeys/gitlab-test api-key.json
# Removing GCP key
# sops -r -i --rm-gcp-kms projects/<gcp project>/locations/global/keyRings/sops/cryptoKeys/gitlab-test api-key.json
# -r option: 'generate a new data encryption key and reencrypt all values with the new key'

For .sops.yaml configuration, you have to add the new cryptographic key and re-encrypt the secrets. The previous blog post contained the Git pre-commit script which will re-encrypt all secrets if the configuration file is changed.

.sops.yaml
creation_rules:
  # Global enc-files (typically for testing and dev-environment)
  - path_regex: .*\.enc(\.yaml|\.json)?$
    gcp_kms: projects/<gcp dev project>/locations/global/keyRings/sops/cryptoKeys/dev-sops-key,projects/<gcp dev project>/locations/global/keyRings/sops/cryptoKeys/gitlab-test

Gitlab configuration

Variables

You can add variables for the CI-pipeline in the project or group settings. I’ll add the following variables to the project settings (Gitlab > Project > Settings > CI/CD > Variables).

Key
Value
GCLOUD_PROJECT_NAME <project name>
GCLOUD_SERVICE_KEY <service account key file content in JSON-format)
SOPS_VERSION 3.4.0

CI Pipeline

Build contains following steps:

  1. Authenticate and setup gcloud CLI
  2. Install SOPS
  3. Decrypt secrets and cache them for the next build steps
    1. decrypt_secrets.sh -script is the same that was used in the previous blog post.
.gitlab-ci.yml
decrypt secrets:
  image: google/cloud-sdk:alpine
  cache:
    # Caches decrypted secrets to next build step (among with other untracked files)
    untracked: true
  script:
    # Authentication for google cloud
    - echo "$GCLOUD_SERVICE_KEY" > ${HOME}/gcloud-service-key.json
    - export GOOGLE_APPLICATION_CREDENTIALS=${HOME}/gcloud-service-key.json
    - gcloud auth activate-service-account --key-file ${HOME}/gcloud-service-key.json
    - gcloud config set project $GCLOUD_PROJECT_NAME
     
    # Install SOPS
    - apk update && apk add --no-cache ca-certificates
    - wget https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux -O /usr/local/bin/sops
    - chmod 0755 /usr/local/bin/sops
     
    # Install GNU findutils for proper regex support
    - apk add findutils
    - sh decrypt_secrets.sh

Gitlab missing features and bugs

In the last part of blog series: what vulnerabilities my examples could contain and how to manage them.

Jarkko Koistinaho

Jarkko Koistinaho

Jarkko works as a technical project manager at Gofore and he is a quality and testing oriented professional in the software industry. Depending on the situation, he could be a software engineer or a Scrum Master in addition to being a software tester. Jarkko can also do some DevOps-tasks. Model-based testing and performance testing are his special skills.

Do you know a perfect match? Sharing is caring

It was already clear for me in high school, that I want to become an engineer. This was partly because I was not very keen on reading thick books for admission tests and partly because mathematics was easy for me and I believed, that technical education would provide many different career opportunities. However, I had no idea what those opportunities could be. Thus, I applied to the electrical engineering department at the University of Technology and was accepted.

The first course was about programming basics and I was immediately thrilled. I had never touched a computer before that.  It was amazing for me, that with the combination of my brain and the keypad I could accomplish all kinds of cool things on the computer screen. There were many boys hanging around in the computer class and offering their help, but I systematically rejected their offers and wanted to solve the programming exercises myself. This really paid off, because learning happens primarily by trial and error.

After graduation I wanted to move towards product development, because I felt, that it would give me the opportunity to do something unique that has not been done before. This was probably the most important decision during my entire career, although I did not realize it at the time. As a result of this choice, I have been involved in the development of forefront technologies and innovations throughout four decades:

  • 80’s: Embedded SW development for research vessel Geldysh and diving bell Mir, which were part of the Titanic movie, searching for the lost necklace
  • 90’s: Web services for the consumers, digital television and mobile games
  • 00’s: Smart phones
  • 10’s: Digitalization and Internet of Things

It is easy to understand, that this has required continuous learning and renewal to keep up with developments – not only technologies, but also business. Several paradigm shifts have taken place during this time.

Personally, I do not think that it has been very different being woman in tech compared to being a man. I have been very fortunate to have had managers and colleagues that have supported and trusted me. Understandably, there have been sometimes situations of gender related prejudice, but those have been due to stereotypical assumptions, which have been corrected quickly.  I have never felt ignored or diminished because of my gender.

Having said that, I do feel, that there is quite high pressure from the “environment” to squeeze us women into a certain kind of mold. For example, many people tend to think, that women are more suitable for non-technical or supporting roles than men even if they work in the IT industry. As a woman, you must be very determined to keep your targets and priorities clear to fight back against these kinds of forces.

I had both my children (boy and girl) at a fairly young age and combining work and family has always been part of my life. It has not always been easy, but for me, these two roles have strongly supported each other: an interesting job with all the challenges has been a necessary counter force to raising children and taking care of their well-being. Spending free time with family has given me a huge amount of energy and helped me to recover from the work. Children have taught me many useful skills, that can be applied also to work life.

Luckily, they are already teaching programming in elementary school now, so maybe I will have a chance to help my granddaughter with her programming exercises as I did with my daughter during her studies.

According to a colleague, my hidden superpower is an outstanding resistance to pressure. This skill is the result of 35 years of practice in priorization, determination and focus on the essential.

 

Read the previous parts of this Women in tech blog series here:
A love for mathematics led the way
Subconscious career design
Value your skills – they are needed in tech
My career in tech – a continuous learning curve
Finding my own material to design
Working as a woman in tech

Terhi Vesanen

Terhi Vesanen

Terhi Vesanen works currently as Chief Growth Officer at Gofore. In this role she is leading the activities to further develop the operations and information systems to support company growth and at the same time enforce Gofore’s mission to produce outstanding employee and customer experience with a very low organizational structure and a self-direction. Terhi’s background is in product development and project management and she has the passion for continuous improvement of the ways of working.

Do you know a perfect match? Sharing is caring

This blog series is split into four parts:

  1. General information of secrets management
  2. Example how to store secrets into a version control
  3. Example how to use encrypted secrets in CI-pipelines
  4. Security issue and threat analysis based on previous examples

This time I’ll give a straight forward example how to use Mozilla SOPS with GCP KMS and Git.

Prerequisites

This guide is based on following technologies:

  • Debian-based OS
  • Mozilla SOPS 3.4.0
  • GCP’s  Key Management Service (KMS)
  • Git

Actions:

  1. Create a Google account and GCP project
  2. Install and initialise Google Cloud SDK on your machine
  3. Install Git

Download and install SOPS on local machine

On Debian-based OS

1
2
3
wget https://github.com/mozilla/sops/releases/download/3.4.0/sops_3.4.0_amd64.deb
sudo dpkg -i sops_3.4.0_amd64.deb
rm -f sops_3.4.0_amd64.deb

Releases for other operating systems

Download proper installation file of Mozilla SOPS from releases.

Setup Google KMS and key

Make Google Cloud SDK authentication

1
gcloud auth application-default login

Create a new keyring

1
2
# Create a new keyring
gcloud kms keyrings create sops --location global

If the following error occurs: “ERROR: (gcloud.kms.keyrings.create) FAILED_PRECONDITION: Google Cloud KMS API has not been used in this project before, or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudkms.googleapis.com/overview?project=<id> then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.”. Follow the link and enable Google KMS API.

Create a new key

1
2
3
4
5
6
7
8
# Create a new key to the keyring
gcloud kms keys create dev-sops-key --location global --keyring sops --purpose encryption
# List all keys in the keyring
gcloud kms keys list --location global --keyring sops
NAME                                                                           PURPOSE          ALGORITHM                    PROTECTION_LEVEL  LABELS  PRIMARY_ID  PRIMARY_STATE
projects/<gcp project>/locations/global/keyRings/sops/cryptoKeys/dev-sops-key  ENCRYPT_DECRYPT  GOOGLE_SYMMETRIC_ENCRYPTION  SOFTWARE                  1           ENABLED

SOPS usage

Basic usage – encrypt and decrypt existing file

1
2
3
4
5
# Encrypt
sops --encrypt --gcp-kms projects/<gcp project>/locations/global/keyRings/sops/cryptoKeys/dev-sops-key api-key.json > api-key.enc.json
# Decrypt
sops --decrypt api-key.enc.json > api-key.json

If the following error occurs on encryption: “Could not generate data key: [failed to encrypt new data key with master key “projects/<gcp project>/locations/global/keyRings/sops/cryptoKeys/dev-sops-key”: Cannot create GCP KMS service: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.]“. You have to make Google Cloud SDK authentication (a few steps earlier in this post)

Basic usage – create or modify encrypted file by SOPS

With the default text editor, you can create or modify encrypted file by SOPS by running following command:

1
sops api-key.enc.json

Creating a new encrypted file needs key parameters like those used in the previous step. But if you have configured creation rules for SOPS, you don’t have to add any key parameter – so head to the next step.

Advanced usage – Creation rules configuration

Create a file named .sops.yaml to root directory where you can set specific encrypted file creation rules (example content below).

.sops.yaml
1
2
3
4
5
6
7
8
9
creation_rules:
  # Staging
  - path_regex: staging/.*\.enc(\.yaml|\.json)?$
    gcp_kms: projects/<gcp staging project>/locations/global/keyRings/sops/cryptoKeys/dev-sops-key
    pgp: 43EBC42D5F6BE0B4617A2C78E2855047997055EC
  # Global enc-files (typically for testing and dev-environment)
  - path_regex: .*\.enc(\.yaml|\.json)?$
    gcp_kms: projects/<gcp dev project>/locations/global/keyRings/sops/cryptoKeys/dev-sops-key,projects/<gcp dev project>/locations/global/keyRings/sops/cryptoKeys/gitlab-sops-key

You can specify multiple path_regex -variables where you set specific regex-named path. You can add one or more keys for the specific path_regex -variable.

Whenever you create an encrypted file to root or sub directories of the configuration file, the specific rules are affected.

Adding to version control

When you have created an encrypted file, you can store it to version control and ignore the decrypted file.

1
2
3
4
5
# Ignore original file
echo "api-key.json" >> .gitignore
# Add crypted file
git add api-key.enc.json

Automating encrypt and decrypt

Because everybody loves automation I’ll show you my tricks by using Git hooks. The basic flows are:

  1. Encrypt secrets when committing changes.
  2. Decrypt secrets when pulling, merging or doing checkout on branches.

Encrypt

Add the following script to <repository>/.git/hooks/pre-commit -hook. This script will automatically encrypt secrets when you try to commit changes. Also you can add this script to a version control and call it via hook.

encrypt_secrets.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/sh
# If SOPS configuration is changed, force update
GIT_SOPS_CHANGED=`git status -s | grep ".sops.yaml"`
FORCE=0
if [ ! -z "$GIT_SOPS_CHANGED" ] || [ "$#" -eq 1 -a "$1" = "-f" -o "$1" = "--forced" ]; then
  FORCE=1
fi
# Find all encrypted files
ENCRYPTED_FILES=`find . -type f -regex ".*\.enc\(\.yaml\|\.json\)?\$"`
for FILE in ${ENCRYPTED_FILES}; do
  DECRYPTED_FILE=`echo "$FILE" | sed 's/.enc././g'`
  if [ ! -f $DECRYPTED_FILE ]; then
    # Decrypt file if none exists
    echo "Decrypted file does not exist. Decrypt and re-encrypt: $FILE"
    sops --decrypt $FILE > $DECRYPTED_FILE
  fi
  # Check if secret is changed
  SECRET_CHANGED=`sops -d $FILE | diff $DECRYPTED_FILE - -q -Z`
  if [ $FORCE -eq 1 ] || [ ! -z "$SECRET_CHANGED" ]; then
    echo "Secret has changed or update is forced. Update: $FILE"
    # Replace old encrypted file with a new one
    cp $DECRYPTED_FILE $FILE
    sops --encrypt --in-place $FILE
    if [ ! -z "`git status -s $FILE`" ]; then
      # Add encrypted file to commit.
      git add $FILE
    fi
  fi
done

Decrypt

Add the following script to a version control and call it from <repository>/.git/hooks/post-checkout and post-merge. It will decrypt secrets when you are changing, pulling or merging a branch. Also this script will be used in a CI-pipeline in the next blog.

decrypt_secrets.sh
1
2
3
4
5
6
7
8
9
#!/bin/sh
ENCRYPTED_FILES=`find . -type f -regex ".*\.enc\(\.yaml\|\.json\)?\$"`
for FILE in ${ENCRYPTED_FILES}; do
  DECRYPTED_FILE=`echo "$FILE" | sed 's/.enc././g'`
  echo "Decrypting $FILE"
  sops --decrypt $FILE > $DECRYPTED_FILE
done

Using Git diff

Add the following configuration to ~/.gitconfig

[diff "sopsdiffer"]
  textconv = "sops -d"

Then create and add <repository>/.gitattributes file with following lines

*.enc diff=sopsdiffer
*.enc.json diff=sopsdiffer
*.enc.yaml diff=sopsdiffer

Now you can use `git diff` so it will show diff between decrypted files!
Next up in the blog series: “How to use secrets in CI-pipelines”-guide.

Jarkko Koistinaho

Jarkko Koistinaho

Jarkko works as a technical project manager at Gofore and he is a quality and testing oriented professional in the software industry. Depending on the situation, he could be a software engineer or a Scrum Master in addition to being a software tester. Jarkko can also do some DevOps-tasks. Model-based testing and performance testing are his special skills.

Do you know a perfect match? Sharing is caring


 
Every developer will occasionally run into a similar problem: where and how to store secrets? And this time I don’t mean hiding skeletons in the closet, but storing credentials, passwords, IP addresses, API keys and something like that. There are many ways to store secrets but this time I will focus on how to store them securely in a version control.
This blog series is split into four parts:

  1. General information about secrets management
  2. Example how to store secrets in a version control
  3. Example how to use encrypted secrets in CI-pipelines
  4. Security issue and threat analysis based on previous examples

So, why should you be serious about security and secrets management? Too many times I’ve seen that some secrets are stored in a version control as a clear text and I admit that I’ve made the same mistake many times – it is just too easy to store a secret and forget that it exists. Anyone who has access to the repository can expose and use the secret. Not to mention that hackers or possible vulnerabilities in systems which have access to data can also expose the secret. Private repositories aren’t enough.
On one day, I was thinking about what would be the best way to store secrets so that all the following requirements are fulfilled:

  1. Multiple encryption types support.
  2. Globally working solution with centralised key management.
  3. Easy to install and use. Also easy to fix any compromised information.
  4. Encrypted information is stored to a version control.

I read up further about secrets management in a version control and came up with five solutions: git-crypt, git-secret, BlackBox, Mozilla SOPS and Transcrypt. There is also Hashicorp Vault but as far as I know, it is not quick to setup from scratch and is not easier to use than other solutions.
I took a look to what encryption types are supported in each solution:

GPG
Symmetric key
Amazon KMS
Google KMS
Azure Key Vault
git-crypt   X            X
git-secret   X
BlackBox   X
Mozilla SOPS   X            X          X              X
Transcrypt            X

I didn’t want to setup any GPG-based keyservers by myself, so a cloud based key management service (KMS) is the way-to-go. In a current client project we use Google Cloud Platform (GCP), Git and Gitlab, so my biggest motivation was to use an encryption solution which supports those technologies – especially GCP. After all, reading detailed information of encryption solutions, I ended up to SOPS.
Why? For me, the most important feature was that I can encrypt and decrypt secrets with multiple keys. If one key is exposed, the key can be rotated or removed without any bigger hassle – just rotate or remove and then encrypt the secrets again. No need to change all the keys.
Another thing is that if the keys are managed on a cloud platform, all authentications are based on a cloud platform’s user and service management with trace logging.
 

 
Mozilla SOPS (Secrets OPerationS) is an open-source editor of encrypted files that supports YAML, JSON, ENV, INI and binary formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault and PGP.
SOPS will encrypt and decrypt secrets in the text form so running any diff tools for tracking changes is possible. It is built in the Go programming language and SOPS is compatible with the Windows, Mac and Linux platforms.
In the second part of this blog series, I’ll dive into a technical “How to add secrets to version control”-guide. It will give a tour of using Mozilla SOPS with GCP and Git.

 

Further reading:

Jarkko Koistinaho

Jarkko Koistinaho

Jarkko works as a technical project manager at Gofore and he is a quality and testing oriented professional in the software industry. Depending on the situation, he could be a software engineer or a Scrum Master in addition to being a software tester. Jarkko can also do some DevOps-tasks. Model-based testing and performance testing are his special skills.

Do you know a perfect match? Sharing is caring

I have worked in tech all my professional life. In high school that was not my plan though. I dreamed about studying the English language and becoming a translator. Luckily, I didn’t get in when applying to university 😀

It can be said that my career is more or less a coincidence. And it has been affected by two men, my father and my high school math teacher. My father gave me an example of how easy and fun mathematics can be and my high school teacher encouraged (or pushed) me to apply to study mathematics at university (even though it was only my second choice). Fortunately, I got in and have not regretted a single day. At the age of 19, I could not imagine what a future in the technology industry would bring, and it has been quite a journey.

I had always loved mathematics in school and when I started my studies at university it was quite a shock. I soon realized that mathematics is a lot of work. But it is worth it, trust me. Hard work has been the key to my success throughout my career. There is no way you can quit and say this is not working. You just have to try another approach and there will always be a solution. It might not be what you thought it would be, but things will work out one way or another. Studying mathematics has taught me a lot of problem-solving skills and a logical way of thinking – things that are still at the core of technology and software development.

At that time (in the early 90’) there was no subject called computer science, it was called applied mathematics. But I loved it and felt I had found my home. Creating software was inspiring and there were a lot of things to learn. It was so much fun trying to find a bug in the program and fix it. Or maybe I just have a crooked mind. Still today I sometimes miss the times when I was writing code.

After I finished my studies (in the mid 90’), I ended up working for Nokia. It was a ride that lasted almost 16 years. I started in coding, did some project management tasks and ended up doing standardization and technology insights. I got to travel around the world, work in different roles and learnt a lot. I admit, when I (finally) got the notice of employment termination in November 2011, I felt relieved. One door closed and another opened for me.

After being unemployed I planned to stay at home with my 2-year-old daughter, but another coincidence happened and changed my plans. I had applied to a study program and is had an internship as part of the program. In the first info session about the program, the organizer told me that they had already sent my CV to Gofore for the internship and that I had an interview with them the next day. In that interview I said that I didn’t need any study program and that I wanted to work for Gofore without any internship. Two weeks later I signed a contract. So, I ended up working for Gofore without ever even applying to the company. And once again I have not regretted a single day. It has now been 7,5 years.

I was hired as a software developer and I got to write code and find bugs for the first 3 years at Gofore. I was also handling some project management tasks and gradually shifted totally onto that path. Currently my title is technical project manager and I’m so proud of it. I get to work actively with customers to find out their actual needs. And as I have a coding background it gives me a lot of understanding on the technical details and restrictions that might need to be considered with the customer. And the other way around, I can make the message from a technical level more understandable for the customer. So, my job is mostly communication.

Throughout my career my background in mathematics and problem-solving has helped me. Problems vary and nowadays they are not technical, but still there is always a solution for them. And it is my job to find it. Even though I didn’t get into studying English, I ended up as a translator. A translator between customers and software developers.

The best advice I’ve ever received was from my high school math teacher: “There are too many linguists, but the world needs mathematicians”.
Mathematics is not just for boys. And studying it will help you no matter where your career takes you. Logical thinking and problem-solving skills are essential in almost all occupations. I hope I can be an example of that to my daughter.

 

Read the previous parts of this Women in tech blog series here:
Subconscious career design
Value your skills – they are needed in tech
My career in tech – a continuous learning curve
Finding my own material to design
Working as a woman in tech

Jaana Majakangas

Jaana Majakangas

Jaana is Gofore's technical project manager. She has twenty years of experience in a variety of roles within the IT industry including extensive knowledge of the public sector and how digitalisation can benefit society.

Linkedin profile

Do you know a perfect match? Sharing is caring