Version Control#
Keeping track of different versions of software is incredibly important and beneficial, particularly as other people start to use our work.
When talking about ‘version control’ there are two subtly different topics which are important to differentiate
Versioning
Version control
Versioning (Version numbering)#
Versioning refers to the labels that we give to different versions of the software, e.g. version 1.0, version 2.3, etc.
It is important to be consistent when deciding how to increment version numbers, as it can help those that depend on our code to understand any potential implications on their work.
A simple and popular versioning system is called
Semantic Versioning, which follows the
MAJOR.MINOR.PATCH
philosphy where:
MAJOR version when you make incompatible public API changes
MINOR version when you add functionality in a backwards compatible manner
PATCH version when you make backwards compatible bug fixes
Best Practice
Use a version numbering system such as Semantic Versioning.
What is an API?#
API stands for ‘Application Programming Interface’ and describes how different bits of code, internally or externally, interact with each other. An interface is as simple as a function signature, for example:
def apply_discount(price, discount):
"""Applies a discount to a price
Parameters
----------
price : float
The original price of the item, in gbp
discount : float
The discount to apply as a percentage, expressed as a decimal
e.g. 0.1 for a 10% discount
Returns
-------
float
The adjusted price
"""
return price * (1 - discount)
Here, the interface for apply_discount
specifies that it must receive the
price
and then the discount
and it will return the adjusted price.
Other sections of the code will eventually use/rely on this function and its interface.
Breaking Change#
Later, we may decide to modify the functionality to be able to apply a fixed discount and a percentage discount:
def apply_discount(price, abs_discount, rel_discount):
"""Applies a discount to a price
Parameters
----------
price : float
The original price of the item, in gbp
abs_discount : float
A fixed discount to remove from the price
This is applied _before_ the relative discount
rel_discount : float
A proportional discount to apply as a percentage, expressed as a decimal
e.g. 0.1 for a 10% discount
Applied after the fixed discount is applied
Returns
-------
float
The adjusted price
"""
return (price - abs_discount) * (1 - rel_discount)
Now we have changed the interface to the function in a breaking way. Anywhere
that uses the function apply_discount(price, discount)
will need modifying,
e.g. to apply_discount(price, 0, discount)
, such that code still works.
However, if this function is part of the ‘public’ interface, that is, you have told other people that it exists (through the documentation) and others are using/depending on this code, then you won’t be able to change their code - which will now break.
In this case, we should increment the MAJOR
version to signal to users that
if they want to update to the latest version, some old stuff may break.
It is also CRITICAL that you convey this information in the release documentation or changelog.
Non-breaking change#
Where possible, prioritise code modifications that are backwards compatible. For example,
def apply_discount(price, rel_discount, abs_discount=0):
"""Applies a discount to a price
Parameters
----------
price : float
The original price of the item, in gbp
rel_discount : float
A proportional discount to apply as a percentage, expressed as a decimal
e.g. 0.1 for a 10% discount
Applied after the fixed discount is applied
abs_discount : float, optional
A fixed discount to remove from the price
This is applied _before_ the relative discount
Default = 0
Returns
-------
float
The adjusted price
"""
return (price - abs_discount) * (1 - rel_discount)
We have added the ability to apply an absolute discount, however, the default
is 0 such that any previous calls to apply_discount(price, discount)
will
still behave in the same way. The code is backwards compatible.
In this case we should increment MINOR
to signal to users that the newer
version contains new functionality, but old code should still work as expected.
Best Practice
Prefer code modifications that won’t break existing code.
Version Control (Git)#
Version control, on the other hand, deals with how such multiple versions are
handled and tracked in an convenient way - rather than ending up with a file
called my_file_version_3_supervisor_edits_bug_fix_3
.
In addition to preventing long and error prone filenames, version control
tools make collaborative code development much, much, easier.
Best Practice
Use Git as a version control tool when developing software.
There are many Version Control tools out there, however, by far the most popular now is Git.
Building on top of Git, there are now many online platforms that make publically sharing and distributing our code significantly easier, such as GitHub and GitLab.
Although Git may seem complex and daunting at first, once you have learnt the basics you will realise that 95% of using git relies on just a handful of commands, which essentially boil down to
Select which modifications you would like to save
Describe the modifications
Commit (save) the changes
Push (upload) the changes to a service like GitHub or GitLab (optional)
There are many Git tutorials out there, however, we recommend following the course created by the Bristol Research Software Engineering team, here.
ALL NEW CODE PROJECTS SHOULD USE GIT TO TRACK VERSIONS
Within the Bristol Composites Institute we have access to both GitHub and GitLab services. The GitHub organisation makes publishing code more straightforward (although private projects are still possible), however, might be hosted on international servers.
The GitLab server is locally hosted within the University which gives us greater control over issues such as Export Control.
For information about the GitLab server (e.g. how to access it), please contact Stephen Hallett (Stephen.Hallett@bristol.ac.uk)