👍🎉 First off, thanks for your interest in contributing to this project! 🎉👍
The following is a set of (style) guidelines for contributing to microtrafficsim. Feel free to propose changes to this document in a pull request.
This project and everyone participating in it is governed by the Code of Conduct. By participating, you are expected to uphold this code.
Commit messages are a hugely important part of working with git. They not only help other workers to understand your changes quickly, they also are the basement for new releases and their release notes. Thus, you really should account these following rules cited from here in your commits.
First of all, commits should be atomic
.
One commit should contain only changes of a few lines of code (or one single feature).
This method seems to be too verbose and kind of annoying, but when working with Git logs (git log
) or GitHub's network tree, this is a huge advantage.
Branch management, releases and especially finding bugs is way easier with small commit messages.
Some points about the commit message style:
- Separate
subject
frombody
with a blank line. The body explains what has changed and why, not how it has changed. How can be checked by looking at the commit changes itself. - Line widths
- 1st line (
subject
) up to 50 characters - 2nd line empty
- Remaining (
body
) lines up to 72 characters
- 1st line (
- Capitalize the
subject
line- fix typo ... + Fix typo ...
- Do not end the
subject
line with a period- Refactor brackets of some if-statements. + Refactor brackets of some if-statements
- Use the present tense
- Added feature + Add feature
- Use the imperative mood, no other language styles (no description either)
- Moves cursor to ... - Fixed bug ... - Sweet new API methods ... + Move cursor to ... + Fix bug ... + Add new API methods for ...
In summary, a properly formed Git commit subject line should always be able to complete the following sentence:
If applied, this commit will <your subject line>
Consider using following verbs/flags in your commit messages:
fix
when the commit contains bug fixesdoc(s)
when writing documentationtest(s)
when tests were changed/addedstyle
when code or format style changes occur in the commitrefactor
when changes DO NOT change functionality
This project uses Gitflow Workflow, a nice and clear way to work effectively.
This means we are using the following branches:
master
: the official release (using tags)develop
: branch for active developmentrelease/<tag>
: temporary branch offdevelop
for bug fixes and docs before getting merged intomaster
feature/<name>
: branches for specific feature developmenthotfix/<name>
: branches for bug fixes branched offmaster
fix/<name>
: branches for bug fixes branched offdevelop
You can copy and use the following template for new releases. In general, needed information should be optained from commit messages.
## Downloads
[Download for Windows](download_link_windows)
[Download as App for macOS](download_link_macOS)
[Download as executable jar](download_link_jar)
Download for Linux and others: see below
## General info
<optional>
## New features
<Short and interesting description about new features of this release.>
## Bug fixes
<Detailed description about fixed bugs of this release.>
[download_link_windows]: <download link>
[download_link_macOS]: <download link>
[download_link_jar]: <download link>
Please check the following points for a new release.
- Is the distribution and release version correct in build.gradle files?
- Are all tests passing?
- Has
release
been merged intomaster
? - Is a new teaser picture needed/recommended?
- Are all distribution files added?
./gradlew :microtrafficsim-ui:distAll
creates the distribution files, which then can be found inmicrotrafficsim-ui/build/distributions
.- the
executable jar file
can be found inmicrotrafficsim-ui/build/libs
- It can help to call
./gradlew clean
before executing the build and dist commands.
"Given a version number MAJOR.MINOR.PATCH
, increment the:
MAJOR
version when you make incompatible API changes,MINOR
version when you add functionality in a backwards-compatible manner, andPATCH
version when you make backwards-compatible bug fixes. Additional labesl for pre-release and build metadata are available as extensions to theMAJOR.MINOR.PATCH
."
For more information, see the original site.
In case you are releasing for macOS, the current version of this simulation is only working with Java 8 (tested with 1.8.0_172
).
For correct building, the following code lines should be set
bundleJRE = true
jreHome = "${System.env.JAVA_HOME}"
in microtrafficsim-ui/build.gradle
.
Hence you have to set your JAVA_HOME
to a correct JDK
.
This setup is tested with macOS 10.13.4
.
If you don't do this, the main-ui could probably start but bugs like deadlock behaviour when loading a new map may occur.
Setting bundleJRE = false
follows into errors when multiple JDKs
are installed.
To make the life of end users easier, just bundle the JRE
as described above.
The following conventions and suggestions should be followed.
They help a lot keeping overview and the code clear.
These conventions just extend common Java style, meaning for instance that camelCase
is used though it is not mentioned below.
-
Header files should NOT contain redundant information (like date, license). Authors and credits are accepted. This is not only to prevent copying code without its authors, it also helps to find a person who (hopefully) understands the code.
For instance date and license are stored through your VCS (
git
) or repo. Writing this information in the header would cause confusion about its reliability.package xyz; import java.*; // :) /** * Class explanations are going -here-. * * @author Maximilian Luz * @author Dominic Parga Cacheiro */ public class Foo { // content }
-
Maximum line width is
100
.This is a good trade off between
120
and80
. Humans have trouble reading the code with increasing line width. In general, more than80
is not recommended, but Java is a very verbose language. -
Use
4 spaces
for indention (p.s.: could help your salary!).
-
Use
constants
overmagic numbers
!Even you as the author will not know the meaning of every number after several months. And if you know, you will probably forget the precision of your constant and the places, where you put them (-> bad for debugging).
// BAD float area = 3.1 * radius * radius; // (...) // somewhere else in the code float circum = 2 * 3.1415 * radius; // or float circum = 6.283 * radius; // GOOD public static final float PI = 3.1415; public static final float PI_2 = 6.2832; float area = PI * radius * radius; // (...) // somewhere else in the code float circum = 2 * PI * radius; // or float circum = PI_2 * radius;
-
Make visibility as closest as possible.
Usually, you tend to not bother with visibility, but visibility helps a lot with getting nice and persistent interfaces.
-
Use
getter
/setter
instead of direct access, even for private usage.This is unhandy in Java, but important for maintenance. Changing the implementation of a class should tend to make no difference for users of this class. Furthermore, debugging with breakpoints is much more easier when you only have to make one breakpoint instead of many at different positions. Same argument counts for synchronization code snippets. The code is getting inlined.
getter
starts withget
,setter
starts withset
. Thosegetter
returning a boolean expression may start differently, but with a verb. Corresponding fields are named like adjectives/states. Field names adapt to their correspondinggetter
/setter
, not the other way around.- count() + getCount() - boolean isRunning = false; + boolean running = true; + boolean isRunning() { return running; }
-
Use white spaces around binary operators. Exceptions can be made for special cases to improve readability (see below).
- int e = (- a) * b; + int e = (-a) * b; - int e = a*b; + int e = a * b; + int e = a * b + c * d; // ok, but not recommended here + int e = a*b + c*d; // improves readability
-
Use control structures with
curly brackets
and the keywordelse
after the closing bracket for nice commenting.Using control structures without
curly brackets
are easy to write, but usually very uncomfortable to read (especially inside other control structures). Most of the time code is read, not written, socurly brackets
should be used.// BAD // may confuse for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) // Are these comment lines ignored? // Can't remember without my IDE... if (isRunning) doSomething(); else doSomethingElse(); doAnything(); // NOT in the loop, but seems to be due to wrong indention // GOOD // clear and easy to read for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { // no problem with comments if (isRunning) { doSomething(); } // `else` after closing bracket for nice commenting else { doSomethingElse(); } } } doAnything();
-
Separate class sections with
/*****/
(whole line). Take the following code snippet for inspiration.public class Vehicle { private Color color; /********************************************************/ // group 0, e.g. constructors and factory methods public Vehicle(Color color) { this.color = color; } public static Vehicle getRedVehicle() { return new Vehicle(Color.RED); } /********************************************************/ // group 1, e.g. getter/setter public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } /********************************************************/ // group 2, e.g. Nagel-Schreckenberg-Model public void accelerate() { // ... } public void brake() { // ... } public void dawdle() { // ... } public void move() { // ... } /********************************************************/ // group 3 (e.g. private classes) }
-
Use annotations and html only where expected and helpful. Some useful ones:
@author Dominic Parga Cacheiro
declares "Dominic Parga Cacheiro" as author of the class. In case of multiple authors, every author gets its own@author
-tag. This tag should be seen as credit. See File Style for more information.@Override
indicates that the annotated method is inherited from the parent class.{@code xyz}
can be used in JavaDocs to format text into code (with literals of the same width).{@literal xyz}
can be used in JavaDocs to use the literals as they are, not interpreted (e.g. as html).<p>...</p>
wraps a paragraph for formatting purpose, for instance it adds empty lines between paragraphs. This should be used over<br>
.<ul>
<li>
some item
<li>
some other item
</ul>
adds an enumeration. This should be used over•
.•
is a Point (similar to an enumeration item), but not a replacement for enumeration (see<ul>
above). Can be used for dividing text snippets.<br>
is a line break, but it should be avoided and<p>...</p>
should be used instead. Besides "dirty style" another reason is: some IDEs does not parse this linebreak in its JavaDoc preview./** * <p> * This is the JavaDoc of the following method. * </p> * * <p> * With {@literal <p>} you start a new paragraph. * Possible values for parameter x are: * <ul> * <li> {@code < 3} e.g. {@code x == 2} * <li> {@code >= 3} e.g. {@code x == 3} * </ul> * </p> */ public void doStuff(int x) { if (x < 3) { System.out.println("x < 3"); } else { System.out.println("x >= 3"); } }
-
A file
package-info.java
in a package can be used for package documentation.com/foo/package-info.java:
/** * package documentation */ package com.foo;
-
Prefer package/folder/file management over class mangement if
meaningful
.
BUT: Think in an intuitive, handy anddeterministic
(!) way and don't take structuring and subfolding too far.Always ask yourself:
How would most of the people search for this class?
Someone without knowing your whole project structure should be able to find a file at the first try.
In every folder, there should be only one option to continue searching (-> determinism).
Take a math API for instance. It is okay to put basic classes like vector and matrix classes in ONE package called
math
, because that's what it is. Someone searching for these classes will find them easily in this package even if there are a lot more classes in it. Think of creating a subfolder for your matrices and another subfolder for graph data structures. Is a matrix describing graph structures belonging to the matrix or the graph folder? It would be annoying to look for such a file in such a folder structure due to non-intuitive subfoldering.
The following conventions and suggestions should be followed. They help a lot keeping overview and the code clear.
Keep The Zen of Python
in mind when coding python.
From Wikipedia - Python:
In general, the language's core philosophy (...) includes
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Readability counts.
which you may call pythonic
.
-
Header files should NOT contain redundant information (like date, license). Authors and credits are accepted. This is not only to prevent copying code without its authors, it also helps to find a person who (hopefully) understands the code.
For instance date and license are stored through your VCS (
git
) or repo. Writing this information in the header would cause confusion about its reliability.''' This is an example for a python file. The header should explain this file/module. Important: (1) Empty lines are on purpose. (2) Sort imports in alphabetical order. ''' __author__ = 'Maximilian Luz, Dominic Parga Cacheiro' __credits__ = 'Maximilian Luz, Dominic Parga Cacheiro, Jan-Oliver Schmidt' # first: built-in imports import argparse # then: third-party-modules import matplotlib # last: own modules import my_module # content
-
Maximum line width is
80
.Humans have trouble reading the code with increasing line width. In general, more than
80
is not recommended. Since python is not such a verbose language, you should stick with this. -
Use
4 spaces
for indention (p.s.: could help your salary!).
-
Class names are written in
CamelCase
, functions, fields insnake_case
.In python, more or less every field in a file can be accessed from any file. Hence, you should mark "private" fields with a single underscore before its name.
class SuperCircle: def __init__(self, super_radius): # BAD self._super_radius = super_radius # GOOD self._super_radius = super_radius
They still can be accessed from outside the class, but shouldn't. For more information about the underscore (especially dunderscore) in python, we highly recommend you reading this pretty site.
-
Use
constants
overmagic numbers
!Even you as the author will not know the meaning of every number after several months. And if you know, you will probably forget the precision of your constant and the places, where you put them (-> bad for debugging).
# BAD area = 3.1 * radius * radius; # (...) # somewhere else in the code circum = 2 * 3.1415 * radius; # or circum = 6.283 * radius; # GOOD # maybe inside a wrapper class? PI = 3.1415; PI_2 = 6.2832; area = PI * radius * radius; # (...) # somewhere else in the code circum = 2 * PI * radius; # or circum = PI_2 * radius;
-
Use
'
for strings, not"
, because it looks cleaner.def do_stuff(): ''' Some python doc ''' x = 42 def do_more_stuff(): """ More text, but its marks are looking kind of fuzzy """ x = 24
-
Make visibility as closest as possible.
Usually, you tend to not bother with visibility, but visibility helps a lot with getting nice and persistent interfaces.
-
Use
getter
/setter
instead of direct access.This is important for maintenance. Changing the implementation of a class should tend to make no difference for users of this class. Furthermore, debugging with breakpoints is much more easier when you only have to make one breakpoint instead of many at different positions. Same argument counts for synchronization code snippets.
Python supports this issue a lot by the keyword
@property
in combination with@<field_name>.setter
.class Length: def __init__(self, metres): self._metres = metres @property def metres(self): return self._metres @property def kilometres(self): return self._metres / 1000.0 @kilometres.setter def kilometres(self, kilometres): self._metres = 1000.0 * kilometres # access length = Length(42.0) print(length.metres) # prints 42.0 print(length.kilometres) # prints 0.042 # set properties length.kilometres = 0.043 # allowed length.metres = 43.0 # no setter -> error
It's very handy to use this, but keep in mind that these properties will be used for quick access. Thus, they should not be seen as replacement for a function calculating a lot.
-
Use white spaces around binary operators. Exceptions can be made for special cases to improve readability (see below).
- e = (- a) * b; + e = (-a) * b; - e = a*b; + e = a * b; + e = a * b + c * d; # ok, but not recommended here + e = a*b + c*d; # improves readability
- Separate module sections with
#######
(whole line). Take the following code snippet for inspiration.''' <Header> ''' ########################################################## # group 0, e.g. imports import numpy as np ########################################################## class Vehicle: def __init__(self, color): self._color = color ###################################################### # group, e.g. some properties @property def color(self): return self._color @color.setter def color(self, color): self._color = color ###################################################### # group, e.g. Nagel-Schreckenberg-Model def accelerate(self): # ... def brake(self): # ... def dawdle(self): # ... def move(self): # ... ########################################################## # group (e.g. static functions or main-method)
-
All executable subprojects should be callable using gradle for easier, unified execution. Keep the following folder structure:
subproject ├── src │ └── main.py ├── build.gradle └── README.md
-
The
main.py
should useif __name__ == '__main__': main() # or similar
to execute the script, so importing the script does not execute it.
-
The typical way of writing a
python module
is writing a file. This follows in less packages as in other languages (like Java). Prefer package/folder/file management over class mangement ifmeaningful
.
BUT: Think in an intuitive, handy anddeterministic
(!) way and don't take structuring and subfolding too far.Always ask yourself:
How would most of the people search for this module/class/file?
Someone without knowing your whole project structure should be able to find a file at the first try.
In every folder, there should be only one option to continue searching (-> determinism).
Back to Table of Contents