diff --git a/Makefile b/Makefile
index 8f1f1a0..9506c7a 100644
--- a/Makefile
+++ b/Makefile
@@ -7,9 +7,20 @@ compile-requirements: ## Compile requirements files
pip-compile -o requirements/dev.txt requirements/dev.in
pip-compile -o requirements/docs.txt requirements/docs.in
-test: ## Run unit tests
+test: test-lint test-unit test-format ## Run all tests by decreasing order or priority
+
+test-format: ## Run code formatting tests
+ black --check --diff tutor
+
+test-lint: ## Run code linting tests
+ pylint --errors-only tutor
+
+test-unit: ## Run unit tests
nosetests --nocapture tests/
+format: ## Format code automatically
+ black ./tutor
+
###### Deployment
bundle: ## Bundle the tutor package in a single "dist/tutor" executable
@@ -37,7 +48,7 @@ ci-info: ## Print info about environment
python3 --version
pip3 --version
-ci-test: ## Run unit tests but without nosetests, which is not available on Mac OS in travis-ci
+ci-test: test-format test-lint ## Run unit tests but without nosetests, which is not available on Mac OS in travis-ci
python3 -m unittest discover tests
ci-bundle: ## Create bundle and run basic tests
diff --git a/docs/tutor.rst b/docs/tutor.rst
index 6827b59..ac55b75 100644
--- a/docs/tutor.rst
+++ b/docs/tutor.rst
@@ -22,7 +22,22 @@ Run tests
make test
-Yes, there are very few tests for now, but this is probably going to change.
+Yes, there are very few unit tests for now, but this is probably going to change.
+
+Code formatting
+---------------
+
+Tutor code formatting is enforced by `black `_. To check whether your code changes conform to formatting standards, run::
+
+ make test-format
+
+And to automatically fix formatting errors, run::
+
+ make format
+
+Static error detection is performed by `pylint `_. To detect errors, run::
+
+ make test-lint
Bundle ``tutor`` executable
---------------------------
diff --git a/requirements/dev.in b/requirements/dev.in
index 1e41c00..e684a8c 100644
--- a/requirements/dev.in
+++ b/requirements/dev.in
@@ -1,4 +1,6 @@
-r base.txt
+black
nose
pip-tools
+pylint
pyinstaller
diff --git a/requirements/dev.txt b/requirements/dev.txt
index a9996e9..18b831d 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -6,6 +6,9 @@
#
altgraph==0.16.1 # via macholib, pyinstaller
appdirs==1.4.3
+astroid==2.2.5 # via pylint
+attrs==19.1.0 # via black
+black==19.3b0
cachetools==3.1.0
certifi==2019.3.9
chardet==3.0.4
@@ -14,10 +17,13 @@ click==7.0
future==0.17.1 # via pefile
google-auth==1.6.3
idna==2.8
+isort==4.3.18 # via pylint
jinja2==2.10.1
kubernetes==9.0.0
+lazy-object-proxy==1.3.1 # via astroid
macholib==1.11 # via pyinstaller
markupsafe==1.1.1
+mccabe==0.6.1 # via pylint
nose==1.3.7
oauthlib==3.0.1
pefile==2018.8.8 # via pyinstaller
@@ -26,12 +32,16 @@ prompt-toolkit==2.0.9
pyasn1-modules==0.2.4
pyasn1==0.4.5
pyinstaller==3.4
+pylint==2.3.1
python-dateutil==2.8.0
pyyaml==5.1
requests-oauthlib==1.2.0
requests==2.21.0
rsa==4.0
six==1.12.0
+toml==0.10.0 # via black
+typed-ast==1.3.5 # via astroid
urllib3==1.24.2
wcwidth==0.1.7
websocket-client==0.56.0
+wrapt==1.11.1 # via astroid