Skip to content

Developer manual

sebescudie edited this page Oct 3, 2022 · 16 revisions

Overview

So you are thinking about joining the Kairos dev team, or maybe you have made up your mind already. In either case, welcome! We are happy to have you and cannot wait to see what you will bring to the table.

Our hope is that the explanations below will give you a better understanding of what you are getting yourself into as well as some guidance as to how to be a happy and efficient Kairos dev teammate.

Tools

The following tools are recommended to ease support amongst team members when problems arise. Of course, if you know what you are doing you can use whatever tools you fancy.

For the rest of this guide, it is assumed you have already installed and configured the following:

You will also need a working github account if you want to push your own changes and improvements to the repository.

Lastly, a working account on Element will allow you to communicate with the team more efficiently (See Communication channels).

Working together

As in any community-driven project, making sure everyone is aware of how to work together in a clear and consistent fashion is key. The following is a summary of the core concepts and workflows that are used by the Kairos dev team.

Communication channels

When in doubt, reach out!

We have a dedicated Kairos space in Element with several purpose-specific rooms.

If it's a development specific discussion you are after, then you can find us here.

Remember: There are no stupid questions, and it's always best to double-check with the team than to assume something or re-invent the wheel.

General workflow

In general, for Kairos we are using the git flow workflow.

But before we dive into that, let us go over the basics of working with git in a quick and dirty summary.

When working with git you would normally go through a process like this:

  1. Clone the Kairos repository to your local drive
  2. Checkout or create the branch you want to add changes in
  3. Apply your changes locally
  4. Verify everything still works
  5. Commit your changes to your local repository
  6. Iterate (repeat steps 3 to 5 for every additional change you want to make)
  7. Push your committed changes to the remote repository
  8. When satisfied with your work, merge your branch back into a more general branch (more details on this in Git branching strategy)

Of course, this is an oversimplified summary of how things usually go with git. You can educate yourself on the basis of git with a multitude of resources online (just search for "git beginner tutorial" and click on what calls your attention).

For now, we will assume you are familiar with the basics of git and can find your way around Git Extensions.

In the upcoming sections, we will cover specific core concepts and practices that are sometimes overlooked in a general git workflow, setting the basis for healthier and less-painful collaboration.

Atomic commits

When committing your changes, try to group them in logical functional groups. This means that it is best to try to avoid mixing multiple non-related changes in the same commit.

For example, if you have made several changes locally, and some of them are related to a bug fix, but others are adding a new feature, then you should split the changes into at least two separate commits.

As a rule of thumb, if your commit message is using the word "and" there is a good chance you would be better off splitting it into two or more commits.

Meaningful git commit messages

Giving your commits meaningful and descriptive commit messages is crucial in a healthy git workflow.

One of the strengths of git is the possibility to go back to a previous commit, or even rollback specific changes, but if your git history is full of cryptic or convoluted messages like "Finished the day", "What was I doing?" or "Fixed A, Renamed class X to Y, improved stability and refactored project Z", you will find things are not as enjoyable and easy as they could be.

In general, try to stick to short descriptive messages. When possible try to explain the "why" of your changes rather than the "what", this is because git itself will often show you what changes (which file, line, etc.) so there is not so much added value in covering such explanations in your message. The "why" however might be more difficult to infer from just looking at the code, and this is where your message becomes important.

If you need to express more complex ideas, use a header line of 50 characters or less and then a body paragraph with the details.

A more in-depth discussion on git commit message best practices with great examples can be found here.

Try to keep it stable!

As you add changes to the project, keep in mind that others will be affected by what you change. If you are working in your own feature branch it doesn't matter so much if you push a commit that leaves the system in a broken or unstable state. However, if you are merging your changes into a branch that others use regularly (like the develop branch), it is highly recommended to make sure everything is working and as stable as possible before pushing such changes.

Give yourself some time to verify that not only your own logic is working, but also try to test parts of the system that depend on the modules/files you have changed. You'd be surprised as to how often something you may consider to be totally unrelated can break because of a seemingly isolated change somewhere else on the system.

Lastly, if there are suits of tests available, make sure you run them (yes, all of them!) before merging your work into a common branch. It can't hurt and can potentially save you a lot of stress and embarrassment, not to mention the frustration of your other team members.

Keeping things synchronized

One of the worse feelings in a git workflow comes when you have finished all your changes, stabilized them, and are ready to push them to the main repo only to find out that someone else was changing the same files and you now have merge conflicts.

While this can sometimes be unavoidable, staying in sync with the repository's remote can help alleviate this by preventing you from drifting too far from the baseline, and in cases where conflicts come, it at least gives you a chance to know about them early on rather than later, when it is usually more painful to deal with them.

For this reason, a frequent synchronization practice is recommended. This can be achieved with a combination of regular pulls and fetches, which will allow you to keep closer track of what parts of the system are changing, who is working on them, and how that may affect your current work.

In general, git fetch is a "safer" action since it will have no direct effect on your local codebase. git pull on the other hand will do a fetch and a merge, which may result in unwanted changes to your local codebase and potential conflicts.

A good explanation of the differences between git pull and git fetch, when to use them and how they work can be found here.

Stash is your friend

When in doubt, stash it!

When you have local changes that you are not ready to commit, but that you don't want to lose while doing a pull, merge or other potentially dangerous operations, remember that you can always stash your uncommitted changes. This will store your changes safely, allowing you to carry on with the tasks at hand. You can then later easily recover them from your stash and merge them back into your local working copy.

A more in-depth explanation of git stash and how to use it can be found here.

Git branching strategy

In Kairos we are using the git flow workflow which proposes a specific branch management strategy.

We will dive into the details in the sections below.

Just a heads up

As you read this you will probably think it sounds complicated and error-prone.

Rest assured, there is a full list of command line tools as well as UI tools in Git Extensions which will automate most of this stuff for you.

Everything from creating branches based off the right branch, to proper naming, to merging to the right branch, to deleting unused branches is automated.

After you do this once or twice you won't even have to think about it (see Git flow command cheat sheet and Using git flow UI in Git-Extensions for more on this).

The important thing, for now, is that you grasp the core concepts. We will cover the "how" behind all this later on.

Now breathe...

Main branch

The main branch is where the stabilized, tested and reviewed version of the codebase lives.

This branch should ideally not be modified directly by any of the developers and is used almost exclusively to generate releases.

As a general rule, the main branch should remain stable throughout the whole development process.

If you are doing things right, chances are high you will never need to think about main, and if you are working on main, chances are high you are in the wrong place.

A few more technical details about the main branch:

  • develop branches off of main
  • hotfix branches branch off of main
  • Only develop, and hotfix branches are merged back into main

Develop branch

The develop branch serves as the basis for all developer work. While the general intention is to keep this branch as stable as possible, it is not unlikely to eventually run into a broken or unstable develop branch.

The general idea is that the last known, unreleased merged state of the codebase lives in develop, and that all developers begin working on new features, bug fixes and related tasks using develop as their starting point.

Whenever a feature or bug-fix is finished, the changes can be merged back from their special use branches into develop, leaving the changes ready to be picked up by other members of the team, for example pulling and merging the changes to their own feature branches.

Given the fact that develop is the common source of truth, it is important to attempt to keep it as stable as possible, as a broken develop branch can potentially block everyone's work if not dealt with promptly.

A few more technical details about the develop branch:

  • All feature branches branch off of develop
  • All release branches branch off of develop
  • feature branches can benefit from pulling changes made to develop after the feature branch was created
  • When the features that are going into a release are finished and stabilized, develop should be merged back into main (this will likely be handled by a team member in charge of releases)

Feature branches

feature branches are where the magic happens, and are by default named using the feature/my-feature naming convention, always starting with a feature/ prefix, and then followed by the name of the actual feature, using lowercase for all words, and dashes instead of spaces if needed.

Whenever you want to start working on something new, be it a bug fix, adding a new feature or updating an older existing feature, chances are good you are in need of a new feature branch.

You can think of a feature branch as a separated sandbox where you, and potentially other teammates who are working with you on the same feature, can work on without disturbing or destabilizing the codebase for the rest of the team.

The idea with these branches is that you keep them open for as long as the feature is being worked on, and once it is finished and stabilized, the feature branch is merged back into develop and is then deleted.

How these types of branches are used can vary from team to team, and even from feature to feature, but as a rule of thumb try to keep your feature branches as specific as possible in order to prevent having long-lived branches that involve multiple people and changes over a long span of time.

So for example, if you need to add a new scrollbar widget to the UI, it is best to create a feature/scrollbar-widget branch, than to create a feature/ui branch where all UI changes will live. In this way, you can be sure that other people who are working on the UI don't depend on you finishing your work in order for theirs to be merged back into develop, and also, you can rest easy knowing that as soon as you finish your work, the branch is gone for good and no one in the team will have to look at a massively long list of old feature branches.

A tidy repo is an easy-to-handle repo :).

Release branches

A release branch is a very purpose-specific type of branch used exclusively to generate new releases of the codebase.

Chances are high that as a developer you will never have to deal with these branches, but if you are really interested, a release branch is always branched off of develop, and should contain only minimal commits dealing specifically with version number updates, changelog updates and potentially documentation updates that reflect how this new release differs from the previous one. In rare cases, last-minute or minimal bug fixes can also live in a release branch, but hopefully, those were tackled beforehand.

When a release branch is created, it is named using a version number. In our case, these numbers follow the versioning nomenclature specified further below.

An example of a release branch name is: release/1.1.0.

Lastly, when a release branch is finished, it is merged back into main while also creating an annotated tag including the release version number and a descriptive comment detailing any release-relevant information. This allows other teammates and your future self to easily locate previous release versions in the git tree. It must also be merged back into develop to make sure any documentation updates and changes make it into the general codebase.

Hotfix branches

hotfix branches are probably the least common type of branches in the git flow paradigm. They are exclusively used to apply small or high-priority fixes to a version of the software that has been released already. For this reason, they are the only branches other than develop that branch off directly from main.

One last thing to mention on hotfix branches is that when they are closed, they need to be merged not only with main but also with develop to ensure the fixes actually make it into the rest of the codebase and remain there in future releases.

Summary

Well, that was a lot to take in!

And since they say an image is worth a thousand words, here is all that text in a nice easy-to-digest diagram:



Source here


Hopefully, it all starts to make sense now.

Using GammaLauncher

As explained in the Gray Book, when contributing to a library, you will want vvvv gamma to pick the content of your local repository and use it as if it was an actual released nuget. We usually do that by starting vvvv.exe with a command-line argument that tells vvvv where to find source for those libraries and treat them as nugets.

Since it can be tedious to rely on a .bat file to do that (and potentially multiple ones: what if you want to switch between different set of arguments from one time to the other?), a standalone tool called GammaLauncher was developed. With a simple GUI, it allows to pick a vvvv version amongst the ones you have installed and start them with arguments that you enable or disable with simple check-boxes. The idea is that you specify the paths to your source folders (the directories containing all the libraries you're working on) in the launcher's settings, and it takes care of starting the vvvversion you choose with the correct arguments. No bat files involved!

The Kairos repository does not follow the usual convention for vvvv. Indeed, in order to "see" a nuget, vvvv will look at your source directory and expect each sub-folder (the libraries that you have cloned) to have a VL document of the same name at its root. This assumption is not true in our case: vvvv will see a Kairos folder in your source directory, but no Kairos.vl document in there. Instead, we have dedicated sub-folders for each lib, which in turn contain the VL document vvvv is looking for:

πŸ“‚ Kairos/
β”œβ”€ πŸ—’οΈ README.md
β”œβ”€ πŸ“‚ VL.AlchemX/
β”‚  β”œβ”€ ◼️ VL.AlchemX.vl
β”œβ”€ πŸ“‚ VL.AlchemX/
β”‚  β”œβ”€ ◼️ VL.AlchemX.vl
β”œβ”€ πŸ“‚ VL.Kairos/
β”‚  β”œβ”€ ◼️ VL.Kaios.vl
[...]

To circumvent that and still let vvvv see all those sub-libraries, we just need to specify the path of this repo in GammaLauncher's settings. Like that, all the sub-folders that match gamma's expectations will be picked up.

In order to specify multiple source directories in GammaLauncher's settings, you can take some inspiration from this snippet :

  <PackageReposFolder>
    <Item>D:\Documents\dev\libraries</Item>
    <Item>D:\Documents\dev\libraries\Kairos</Item>
    (...)
  </PackageReposFolder>

Also, to quickly locate this settings file, focus GammaLauncher and press CTRL + ,

Releases

Versioning scheme

Kairos uses the semver specification for its version numbers. In a nutshell, it consists of a three-component version number, namely major, minor and patch. A valid semver version number would look like 1.3.5, where 1 is the major component, 3 is minor and 5 is patch.

Those components actually have a meaning. If you release a new version and actually increment major, it means you're introducing breaking changes in your update. Incrementing minor means you're bringing new functionality that does not break anything. Incrementing minor means you've just fixed a bug.

Why bother? Well because that's actually a very useful source of information for other developers consuming your library. If they're using version 5.2.1 in their project and you release 6.0.0, they automatically know that something might break if they upgrade to this new version. They could then decide to stay on the 5.X.X "branch" until they have time to actually take care of reflecting those breaking changes in their code.

A fourth component also exists in the semver specification: the pre-release tag. It's simply a string that you append after the version number that indicates that your package is in pre-release state, and thus not considered stable (either some features are unstable, or some might be removed/reworked). 1.5.3 indicates a stable version, but 1.6.0-preview01 indicates a preview version. The pre-release tag could be anything: preview, rc(release candidate), beta, alpha, you name it.

For an in-depth explanation of this versioning scheme head here.

Release strategy

Say we're aiming to release a shiny new 1.5.0 version. We know already that we want to implement features A, B, and C.

Soon as we start working on this release and have just implemented say an initial version of feature A (and only that), it makes sense to release a preview version of 1.5.0.

That would give early feedback from users, allowing them to fix bugs and/or improve the lib's UX. For that, we would release 1.5.0-preview01. Later on, we collect feedback, fix things, implement some new features ready to be tested, and we release 1.5.0-preview02, and so on. In the end, the release history of the library could look like this :

Version number Changelog
1.4.9
1.5.0-preview01 Adds initial draft of feature A
1.5.0-preview02 Fixes bug in feature A, adds initial draft of feature B
1.5.0-preview03 Fixes bug in feature B, adds feature C
1.5.0-preview04 Adds help patches for new features A B and C
1.5.0 Adds features A B and C with help patches documenting them
1.5.1 Fixes a bug in feature C
1.5.2 Fixes a bug in feature A
1.6.0-preview01 Initial draft for feature D

And so on and so forth.

Release vs pre-release

Pre-release versions should not be expected to be stable. Some features they contain might get removed or drastically changed without notice.

Unstable does not mean broken though! If something in your branch is totally broken or makes the library unusable, please do not commit to develop!

Nugets

Kairos offers four different libraries :

  • VL.LayerX
  • VL.AlchemX
  • VL.BindX
  • VL.Kairos

Under the hood, some depend on others, but that's not something the end-user should be concerned about. Even though the source code for those libraries lives in the same repo, they are still four independent nugets that can be used independently from each other.

As a consequence, they all might have different version numbers: a new version of VL.LayerX does not necessarily trigger a new version of VL.AlchemX, and so on. In case of packages depending on each other, the .nuspec file dictates which version said library depends on.