2020 has been an exciting year for Kolibri Games. We joined the Ubisoft family and shifted to a predominantly remote work style following our COVID-19 measures. In addition, we launched our Data2020 initiatives to become more data-driven and have made great strides on the product side over the past months: starting work on new games!
We strongly believe in the potential of our current games! With Idle Miner Tycoon, we revolutionized the idle genre – we have reached millions of players worldwide and proved that these games are suitable for the mainstream. We are still just as committed to making the game a little bit better, week by week, turning it into a game for billions.
At the same time, we’re incredibly excited to test the waters of our genre even further! We’re compiling all the things that we have learned about idle games over the past years into new products; new mechanics, new looks, and new stories for our players to enjoy!
In this blog post, Idle Miner Tycoon Head of Client Engineering Jonas discusses how we overcome some of the technical challenges that simultaneously working on multiple games with multiple teams entails. He explains the benefits of code sharing and how we manage to foster a culture of collaboration between our developers, all while keeping each individual team independent.
What’s our story?
Earlier this year, we faced a substantial challenge: splitting up our developers into smaller teams to independently work on new games.
Based on our maintaining of two live games, Idle Miner Tycoon and Idle Factory Tycoon, we were well aware of the advantages of cross-team collaboration between developers. Now it was up to us to figure out how we could leverage our existing knowledge to build multiple new games at the same time, and how we could enable our developers to support each other across teams.
Code Sharing Options
In what follows, I will compare different common approaches to code sharing, their advantages and downsides, and explain how we found a solution that fits our needs best.
We use Unity to develop our games, so any direct technical references are a result of our work with that game engine.
Every developer knows that copy & paste is the fastest way to get your code running. When you work at a small studio, speed is everything, so copy & paste is exactly what we did at Kolibri Games in the past if one game had a feature we wanted to introduce to the other.
Usually, the code you just copy-pasted needs some adjustments to fit the rest of the codebase or to even compile. At this point, the code you copied and adjusted is already doing different things compared to the original code.
A week later, the team that supports the original code fixes a bug that is also causing trouble for your game. While they will let you know that there is a bug and that they have the solution, copy-pasting their fix might not be that easy anymore, due to all the adjustments you have made.
An improved solution to this problem is using a shared repository. Such a system could be set up in the following way:
- Team A extracts code from their project to a new Git repository.
- Team A copies the code from the repository back to their project.
- Team B also copies code from the repository to their project.
- Everyone agrees to only change this code in the repository from now on
- Team B makes a change in the repository and again copies it to their project.
- Team B informs Team A about the change and also copies it to their project.
Integrating the code from the shared repository to the individual project does not have to be done via copy & paste. Instead, packaging the code into a .unitypackage achieves the same result, is more convenient and is safer.
In theory, this solution is almost flawless. In reality, its application is often limited by point 4: “Everyone agrees to only change this code in the repository from now on”. Even though code is distributed from the central repository to the individual projects with .unitypackages, it can still be modified within those projects.
If code CAN be changed, then chances are that it WILL be changed – it doesn’t matter whether that’s out of convenience, speed, or lack of knowledge. If the code in the projects differs from the shared repository, you will inevitably run into the same issues that were limiting you when you were just copy-pasting.
There are currently two common ways to ensure that the code can only be modified inside the shared repository:
- Building a .dll from the shared repository code and importing it to the projects.
- Creating packages from the shared code repository that can be downloaded in the projects with the unity package manager.
In addition, when you implement this solution, you will have to decide whether to create one shared Git repository for every unit of code or one single shared Git repository for all shared code. Each method will differently impact the overall convenience of the system, your integration testing, as well as the way you build your unity project. In our experience, starting with a single Git repository has proven to be beneficial for later iterations.
As a workaround to most of the aforementioned problems, we have implemented the following solution: We use one shared Git repository as a Unity project at the root and add multiple packages, divided into folders, under a Packages folder.
Here, we take advantage of the Unity Package Manager (UPM) to distribute the packages. Individual folders allow us to version every package and to easily add documentation. When versioning, we follow the SemVer standard with the addition of the PREVIEW version for when we’re in the process of testing our version update. We set up a build pipeline that automatically publishes new package versions whenever a new version is available. Every game team has access to that new version and can download it directly via our private package repository with UPM.
In addition, we’ve established communication channels for all our developers to discuss and collaborate on package development. We try to keep our individual game teams as independent as possible and instead trust in an internal collaborative open-source model, where each developer works on packages as they become relevant for the game they’re working on.
To maintain a high level of quality in the package code and ensure that packages are shareable to multiple games, we have established a Maintainer system: Each package has one developer acting as a Maintainer. That developer is responsible for the quality of the package, helps to ensure that everyone working on it understands how to use it best and that it remains applicable to multiple games. Modifications to a package can be done by any developer from any team, as long as they follow the conventions and practices of the given package.
There are a few guidelines that any Maintainer must fulfill at all times:
- They must have in-depth knowledge of the package they are maintaining
- They must be available to review merge requests related to their package
- They ensure that code changes are consistent with the package intention and existing conventions
- They ensure that code changes remain generic enough to be used by multiple games
- They support other developers who want to use the package
Obviously, our solution isn’t perfect and there are a few potential issues that we are aware of.
Since folders are located in the same Unity project and git repository, we can run into the danger of creating dependencies between those folders. We rely on Assembly Definitions to prevent this from happening. In addition, the repository itself can get very large if the amount of packages increases. Finally, when building, the repository tries publishing all packages, as it doesn’t know which ones were modified. The build fails if the version is the same, but continues with other packages anyway, which is a small inefficiency that we are willing to accept.
While the technical part of our solution is strict and aims to prevent mistakes, the people part of the solution is based on trust instead of policies – we believe that giving freedom to anyone to modify packages will deliver better results in the long run.
With the help of this system – a single shared repository with multiple packages and Maintainers – we have been able to smoothly transition to the work on new games, ensuring that developers are still benefiting and learning from each other’s work across teams.
Even though our solution has yielded great results for us so far, we are always adjusting our work processes whenever needed – and, who knows, with a little luck, players will be able to enjoy these results very soon!
Can you see yourself working in such a system? We’re still looking for ambitious developers to revolutionize the idle genre with us! Check out our open positions below.