diff --git a/.gitignore b/.gitignore
index 3a1ec4c01c7de14864c8405f0cf60cef6a8116ba..bdecffe1d54acd670993aeb7b5d36424f03f797e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,4 @@ dev-23a/
 *.log
 dist/
 build/
-*.egg-info/
\ No newline at end of file
+*.egg-info/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b70ecaf92cd7962f8a0386c1669495facadf6b0e..c7020b02cfe561ff9ee66bb67806687d8a188399 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -91,11 +91,10 @@ test-bullseye:
   image: debian:bullseye-slim
   script:
     - *prepare_debian
-    - pytest -v -l 
+    - pytest -v -l
 test-bookworm:
   stage: test
   image: debian:bookworm-slim
   script:
     - *prepare_debian
-    - pytest -v -l 
-
+    - pytest -v -l
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..00e5c865b272e6d5426b2f2c1622a3623f00cab8
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,65 @@
+# Config based on https://gitlab.com/octopus-code/postopus/-/blob/main/.pre-commit-config.yaml
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+  rev: v4.4.0
+  hooks:
+  - id: trailing-whitespace
+  - id: check-ast
+  - id: check-docstring-first
+  - id: check-executables-have-shebangs
+  - id: check-merge-conflict
+  - id: check-toml
+  - id: debug-statements
+  - id: end-of-file-fixer
+  - id: mixed-line-ending
+    args: ['--fix=auto']  # replace 'auto' with 'lf' to enforce Linux/Mac line endings or 'crlf' for Windows
+
+# other hooks
+# Ruff:
+- repo: https://github.com/astral-sh/ruff-pre-commit
+  # Ruff version.
+  rev: v0.0.274
+  hooks:
+    - id: ruff
+      args: [--fix, --exit-non-zero-on-fix]
+
+# Black
+- repo: https://github.com/psf/black
+  rev: 23.3.0
+  hooks:
+  - id: black
+    language_version: python3
+
+## If like to embrace black styles even in the docs:
+# - repo: https://github.com/asottile/blacken-docs
+#   rev: v1.9.1
+#   hooks:
+#   - id: blacken-docs
+#     additional_dependencies: [black]
+
+# pydocstyle
+- repo: https://github.com/PyCQA/pydocstyle
+  rev: 6.3.0
+  hooks:
+  -   id: pydocstyle
+      name: pydocstyle
+      description: pydocstyle is a static analysis tool for checking compliance with Python docstring conventions.
+      entry: pydocstyle
+      language: python
+      types: [python]
+
+- repo: https://github.com/abravalheri/validate-pyproject
+  rev: v0.13
+  hooks:
+    - id: validate-pyproject
+
+- repo: https://github.com/myint/rstcheck
+  rev: v6.1.1
+  hooks:
+    - id: rstcheck
+      additional_dependencies:
+        - sphinx==4.5.0
+        - sphinx-click==3.1.0
+      args: [
+        --report-level=WARNING,
+      ]
diff --git a/README.rst b/README.rst
index 491a64aec85a13fd9c6a577a42c8c63069df1397..05ddf0fda53a6afa019455917b21432cab0fb171 100644
--- a/README.rst
+++ b/README.rst
@@ -165,7 +165,7 @@ The ``mpsd-software-manager`` python package.
 - Install via pip or pipx.
 
   Pipx commands are:
-  
+
   - to install: ``pipx install git+https://gitlab.gwdg.de/mpsd-cs/mpsd-software-manager``
   - to update: ``pipx upgrade mpsd-software-manager``
   - to uninstall: ``pipx uninstall mpsd-software-manager``
@@ -183,7 +183,7 @@ Requirements for particular toolchains and package sets
 
 - ``foss*-serial`` should compile with the dependencies outlined above
 - ``foss*-mpi`` currently needs linux header files installed (to compile the ``knem`` package)
-- ``foss*-cuda-mpi`` (proably as `*-mpi, needs testing TODO)
+- ``foss*-cuda-mpi`` (proably as `*-mpi, needs testing TODO`)
 
 
 Working example
@@ -192,7 +192,7 @@ Working example
 There is an
 `example <https://github.com/mpsd-computational-science/octopus-with-mpsd-software>`__
 compilation that shows the complete compilation cycle (including compilation of
-Octopus) using the ``foss2022a-serial`` toolchain. 
+Octopus) using the ``foss2022a-serial`` toolchain.
 
 
 
@@ -249,3 +249,6 @@ Frequently asked questions
   to experiment with toolchains etc.
 
 
+Development
+-----------
+Developers documentation is available at development.rst.
diff --git a/development.rst b/development.rst
new file mode 100644
index 0000000000000000000000000000000000000000..4d029e42a767aa17d4ddc14a3bf76d3cd3f9d8d9
--- /dev/null
+++ b/development.rst
@@ -0,0 +1,19 @@
+Developers Setup
+================
+This section is for developers wanting to contribute to the project.
+
+Setup pre-commit hooks
+----------------------
+The project uses `pre-commit <https://pre-commit.com/>`__ to run some checks before committing code.
+Install pre-commit by running the following command::
+
+    pip install pre-commit
+
+To setup the pre-commit hooks, run the following command from the root of the project::
+
+    pre-commit install
+
+Then every time you commit, pre-commit will run all checks defined in `.pre-commit-config.yaml`.
+you can run the pre-commit checks manually by running::
+
+    pre-commit run --all-files
diff --git a/install-dev23a.sh b/install-dev23a.sh
index c01e10b7078becf1735c680f3e5ac8b219910416..5a97c1a1844441efddb3ad560ce89dd3a8fdc2d9 100755
--- a/install-dev23a.sh
+++ b/install-dev23a.sh
@@ -1,21 +1,21 @@
 #!/bin/bash
 # Script to build all toolchains for this MPSD release ( 23a )
 # Run this script inside the cloned folder for eg:
-# mpsddeb@mpsd-hpc-ibm-022:/opt_mpsd/linux-debian11/mpsd-software-environments$ ./install-dev23a.sh 
+# mpsddeb@mpsd-hpc-ibm-022:/opt_mpsd/linux-debian11/mpsd-software-environments$ ./install-dev23a.sh
 
 set -e
 cd ..
 mkdir -p dev-23a
 cd dev-23a
 # clone repo if it doesn't exist yet
-[ -d 'spack-environments' ] || git clone git@gitlab.gwdg.de:mpsd-cs/spack-environments.git 
+[ -d 'spack-environments' ] || git clone git@gitlab.gwdg.de:mpsd-cs/spack-environments.git
 pushd spack-environments
 git checkout dev-23a
 git pull
 popd
 mkdir -p sandybridge
 cd sandybridge
-../spack-environments/spack_setup.sh -b global 
+../spack-environments/spack_setup.sh -b global
 ../spack-environments/spack_setup.sh foss2021a-serial
 ../spack-environments/spack_setup.sh foss2021a-mpi
 ../spack-environments/spack_setup.sh foss2021a-cuda-mpi
diff --git a/pyproject.toml b/pyproject.toml
index 43fabe2af26474f3f89816fcbb6aec5ceb31dd79..951bf9263ac72b3ea70c86a06055686cb92e153b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -22,6 +22,7 @@ Home = "https://gitlab.gwdg.de/mpsd-cs/mpsd-software-manager/"
 [project.optional-dependencies]
 dev = [
     "black",
+    "pre-commit",
     "pytest",
     "pytest-mock",
     "pytest-cov",
diff --git a/src/mpsd_software_manager/mpsd_software.py b/src/mpsd_software_manager/mpsd_software.py
index b3b4da2add905eba42bae758ebbcac5fa9c4026a..aa896e693c0de95258e7645b43992109be5cd7bc 100755
--- a/src/mpsd_software_manager/mpsd_software.py
+++ b/src/mpsd_software_manager/mpsd_software.py
@@ -35,10 +35,10 @@ Build software as on MPSD HPC.
     It follows recipes as used on the MPSD HPC system and the (spack-based)
     Octopus buildbot. Compiled software is organised into MPSD software release
     versions (such as `dev-23a`) and CPU microarchitecture (such as `sandybridge`).
-    
+
     Compiled packages and toolchains can be activated and used via `module load` as
     on the HPC system.
-    
+
     Further documentation is available in the README.rst file, online at
     https://gitlab.gwdg.de/mpsd-cs/mpsd-software-manager/-/blob/main/README.rst
 
@@ -56,17 +56,17 @@ Examples:
 
     1. Query what package sets and toolchains are available for installation in
        release dev-23a
-    
+
        $> {sys.argv[0]} available dev-23a
-    
+
     2. Install foss2022a-serial toolchain from the dev-23a release
-    
+
        $> {sys.argv[0]} install dev-23a foss2022a-serial
-    
+
     3. Check what package sets and toolchains are installed from release dev-23a
-    
+
        $> {sys.argv[0]} status dev-23a
-    
+
        The `status` command also displays the `module use` command needed to load
        the created modules.
 
diff --git a/tests/test_mpsd_software.py b/tests/test_mpsd_software.py
index 6efde257bc6818fcbc2c87ea2688a19d11731d82..514c8e5fabd8817fe693ed19e2c50d0ca60abc15 100644
--- a/tests/test_mpsd_software.py
+++ b/tests/test_mpsd_software.py
@@ -107,7 +107,7 @@ def test_run_method(tmp_path):
 
 def test_prepare_environment(tmp_path):
     """Simulate running preparation of environment.
-    
+
     Simulate running ./install-software-environment.py --release dev-23a \
       --target-directory /tmp/test_prepare_env
     prepare_env is run when cmd is not specified, we can test cmd='prepare'