diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9886782736293c0ce57797db01a31b99e1006918..fbf5bbf008d23cb443bc1a7dbf8e28aaca96c4df 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,69 +1,61 @@
-default:
-    services:
-        - postgres:alpine
-
-stages:
-    - code_quality
-    - test
+# Pip's cache doesn't store the python packages
+# https://pip.pypa.io/en/stable/topics/caching/
+#
+# If you want to also cache the installed packages, you have to install
+# them in a virtualenv and cache it as well.
+cache:
+  paths:
+    - .cache/pip
 
-include:
-    -   remote: 'https://api.r2devops.io/job/r/gitlab/dorianturba/r2devops_catalog/r2_metadata/black@latest.yaml'
-    -   remote: 'https://api.r2devops.io/job/r/gitlab/dorianturba/r2devops_catalog/r2_metadata/ruff@latest.yaml'
-    -   remote: 'https://api.r2devops.io/job/r/gitlab/dorianturba/r2devops_catalog/r2_metadata/pytest@latest.yaml'
-    -   remote: 'https://api.r2devops.io/job/r/gitlab/dorianturba/r2devops_catalog/r2_metadata/flake8@latest.yaml'
-    -   remote: 'https://api.r2devops.io/job/r/gitlab/dorianturba/r2devops_catalog/r2_metadata/mypy@latest.yaml'
-    -   remote: 'https://api.r2devops.io/job/r/gitlab/dorianturba/r2devops_catalog/r2_metadata/python_install@latest.yaml'
+default:
+  image:
+    name: python:latest
 
 variables:
-    PACKAGE_NAME: "fake_session_maker"
-    POSTGRES_DB: test
-    POSTGRES_USER: test
-    POSTGRES_PASSWORD: test
-    POSTGRES_HOST_AUTH_METHOD: trust
-    IMAGE_TAG: "3.12"
-
-black:
-    variables:
-        PYTHON_SETUP: "pip install ${PROJECT_PATH}[QUALITY]"
-
-ruff:
-    variables:
-        PYTHON_SETUP: "pip install ${PROJECT_PATH}[QUALITY]"
-
-flake8:
-    variables:
-        PYTHON_SETUP: "pip install ${PROJECT_PATH}[QUALITY]"
-
-mypy:
-    variables:
-        PYTHON_SETUP: "pip install . mypy"
-        MYPY_RUN: "mypy"
-
-pytest:
-    variables:
-        PYTHON_SETUP: "pip install ${PROJECT_PATH}[TEST]"
-
-pytest_311:
-    extends: pytest
-    variables:
-        IMAGE_TAG: "3.11"
-
-pytest_310:
-    extends: pytest
-    variables:
-        IMAGE_TAG: "3.10"
-
-pytest_39:
-    extends: pytest
-    variables:
-        IMAGE_TAG: "3.9"
-
-pytest_38:
-    extends: pytest
-    variables:
-        IMAGE_TAG: "3.8"
-
-pytest_37:
-    extends: pytest
-    variables:
-        IMAGE_TAG: "3.7"
+  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+  PIP_REGISTRY: https://gitlab-ci-token:${CI_JOB_TOKEN}@lab.frogg.it/api/v4/projects/1427/packages/pypi/simple
+
+########################### RULES ################################
+.package_rules: &package_rules
+  rules:
+    - if: $CI_COMMIT_BRANCH == "main"
+      changes:
+        - src/*
+        - pyproject.toml
+
+########################### STAGES ################################
+stages:
+  - code quality
+  - tests
+  - build package & push
+
+########################### LINTING ################################
+"Linter":
+  stage: code quality
+  before_script:
+    - pip install ruff
+  script:
+    - ruff check --output-format=gitlab src/fake_session_maker/fsm.py
+  only:
+    variables:
+      - $CI_PIPELINE_SOURCE == "push"
+
+########################### TESTING ################################
+"Tests":
+  stage: tests
+  before_script:
+    - pip install pytest
+  script:
+    - pytest
+  only:
+    variables:
+      - $CI_PIPELINE_SOURCE == "push"
+
+########################### BUILDING ################################
+"Build and Push FSM PyPI Package":
+  stage: build package & push
+  <<: *package_rules
+  script:
+    - pip install build twine
+    - python -m build
+    - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --verbose --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4da57243f2aa4519554906b2ba7e57369b8ac453..e35e5e8c7e43e51bbf8354eb5aa1f62a45f949a5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,34 +1,15 @@
 repos:
-    -   repo: https://github.com/psf/black
-        rev: 23.9.1
-        hooks:
-            -   id: black
-                args: [ --check, --diff, --quiet ]
-                language_version: python3
-    -   repo: https://github.com/PyCQA/flake8
-        rev: 6.1.0
-        hooks:
-            -   id: flake8
-    -   repo: https://github.com/pycqa/isort
-        rev: 5.12.0
-        hooks:
-            -   id: isort
-                args: [ --check-only ]
     -   repo: https://github.com/pre-commit/mirrors-mypy
-        rev: v1.5.1
+        rev: v1.10.0
         hooks:
             -   id: mypy
                 exclude: ^tests/
-    -   repo: https://github.com/jackdewinter/pymarkdown
-        rev: v0.9.13.4
-        hooks:
-            -   id: pymarkdown
     -   repo: https://github.com/astral-sh/ruff-pre-commit
-        rev: v0.0.292
+        rev: v0.4.6
         hooks:
             -   id: ruff
     -   repo: https://github.com/pre-commit/pre-commit-hooks/
-        rev: v4.5.0
+        rev: v4.6.0
         hooks:
             -   id: check-yaml
             -   id: check-case-conflict
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 1a4ff4616f18bf57186a1abd3df54675ff22f54c..e421332518cc4ae08caa572bb4ae6148d3106ab1 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -10,4 +10,4 @@ Development Lead
 Contributors
 ------------
 
-None yet. Why not be the first?
+* Hamza Diouane <hamza.diouane@try-it.fr>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ccc41a5afc80130dbf0ab757e34a251574824977..4718ce4c79b33e10cb4fc633d54e500361d90f7a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -110,7 +110,7 @@ the codebase.
 
 ```bash
 pip install --upgrade pip
-pip install -e .[QUALITY]
+pip install -e ."[QUALITY]"
 pre-commit install
 ```
 
@@ -163,7 +163,7 @@ GRANT ALL PRIVILEGES ON DATABASE test TO test;
 ##### Install Python Tests requirements
 
 ```bash
-pip install -e .[TEST]
+pip install -e ."[TEST]"
 ```
 
 ##### Run tests
@@ -177,7 +177,7 @@ pytest
 ### Generate requirements
 
 ```bash
-pip install -e .[REQUIREMENTS]
+pip install -e ."[REQUIREMENTS]"
 pip-compile --output-file requirements/requirements-3_11.txt pyproject.toml
 ```
 
diff --git a/README.md b/README.md
index 16311c172ebf28f2a03eeab19dd05e0e102d9442..62cef44201961dd2ad5c17650d17e1d6bc09668a 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@ Below is an example of how to use fake_session_maker fixture in a test:
 # Each test will have a fresh database, empty of any data
 @pytest.mark.parametrize("name", ["jane", "joe"])
 def test_create_example(fake_session_maker, name):
-    result = create_example('test')
+    result = create_example(name)
     assert result == 'success'
     with fake_session_maker() as session:
         # Each time we check, only the data created in this test will be present
diff --git a/pyproject.toml b/pyproject.toml
index baf631da54e9eeb49478a534aad628d3be74e79a..e220b86d6e4398c26f5768a56f989bd81fd38a3b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -34,9 +34,6 @@ MANAGE = [
     "bump-my-version",
 ]
 QUALITY = [
-    "black",
-    "flake8",
-    "isort",
     "mypy",
     "pre-commit",
     "ruff",
@@ -54,9 +51,6 @@ TEST = [
 "Bug Tracker" = "https://lab.frogg.it/dorianturba/fake_session_maker"
 "Homepage" = "https://lab.frogg.it/dorianturba/fake_session_maker"
 
-[tool.black]
-line-length = 88
-
 [tool.bumpversion]
 allow_dirty = false
 commit = true
@@ -84,10 +78,6 @@ disallow_untyped_defs = true
 packages = "fake_session_maker"
 python_version = 3.7
 
-[tool.pymarkdown]
-strict-config = true
-plugins.line-length.line_length = 88
-
 [tool.pytest.ini_options]
 addopts = "-rA -q --strict-markers"
 markers = [
@@ -101,6 +91,48 @@ testpaths = [
     "tests",
 ]
 
+[tool.ruff]
+exclude = [
+    ".bzr",
+    ".direnv",
+    ".eggs",
+    ".git",
+    ".git-rewrite",
+    ".hg",
+    ".mypy_cache",
+    ".nox",
+    ".pants.d",
+    ".pytype",
+    ".ruff_cache",
+    ".svn",
+    ".tox",
+    ".venv",
+    "_build",
+    "buck-out",
+    "build",
+    "dist",
+    "node_modules",
+    "pypackages",
+    "venv",
+]
+indent-width = 4
+line-length = 88
+target-version = "py312"
+
+[tool.ruff.format]
+docstring-code-format = true
+docstring-code-line-length = "dynamic"
+
+[tool.ruff.lint]
+select = ["ANN", "C90", "D2", "D3", "D4", "E", "F", "I", "N", "RUF", "UP", "W"]
+
+[tool.ruff.lint.flake8-annotations]
+suppress-dummy-args = true
+
+[tool.ruff.lint.pydocstyle]
+convention = "pep257"
+ignore-decorators = ["typing.overload"]
+
 [tool.tomlsort]
 all = true
 in_place = true
@@ -116,8 +148,7 @@ legacy_tox_ini = """
     min_version = 4.0
     env_list =
         py{37,38,39,310,311}
-        black
-        flake8
+        ruff
         mypy
     isolated_build = true
 
@@ -125,13 +156,9 @@ legacy_tox_ini = """
     deps = pytest
     commands = pytest
 
-    [testenv:black]
-    deps = black
-    commands = black --check .
-
-    [testenv:flake8]
-    deps = flake8
-    commands = flake8
+    [testenv:ruff]
+    deps = ruff
+    commands = ruff check .
 
     [testenv:mypy]
     deps = mypy
diff --git a/requirements/requirements-3_11.txt b/requirements/requirements-3_11.txt
index 43878b946b392882a3b8dd3518805fe952c785b9..fb41ac1294fe353e3503da04a497f64f09d76f96 100644
--- a/requirements/requirements-3_11.txt
+++ b/requirements/requirements-3_11.txt
@@ -1,11 +1,9 @@
 #
-# This file is autogenerated by pip-compile with Python 3.11
+# This file is autogenerated by pip-compile with Python 3.12
 # by the following command:
 #
-#    pip-compile --output-file=requirements/requirements.txt pyproject.toml
+#    pip-compile --output-file=requirements/requirements-3_11.txt pyproject.toml
 #
-colorama==0.4.6
-    # via pytest
 greenlet==2.0.2
     # via sqlalchemy
 iniconfig==2.0.0