diff --git a/.codeclimate.yml b/.codeclimate.yml
deleted file mode 100644
index 306cd96368ab3e378a86786e13751dbf60142d59..0000000000000000000000000000000000000000
--- a/.codeclimate.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-engines:
-  duplication:
-    enabled: true
-    config:
-      languages:
-        python:
-
-  fixme:
-    enabled: true
-  markdownlint:
-    enabled: true
-  pep8:
-    enabled: true
-  radon:
-    enabled: true
-exclude_paths:
-- tests/**
-ratings:
-  paths:
-  - firanka/**
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..87b1eada9c084bade3df586ace841b573e82a3e0
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,38 @@
+stages:
+  - unittest
+  - build
+image: zoo.smok.co/build/build:latest
+
+pages:
+  before_script:
+    - python setup.py install
+    - pip install --break-system-packages nose2 coverage
+  only:
+    - develop
+  stage: build
+  script:
+    - cd docs
+    - make html
+    - cd ..
+    - mv docs/_build/html public
+  artifacts:
+    paths:
+      - public
+
+
+build_python:
+  stage: build
+  before_script:
+    - pip install --break-system-packages --upgrade setuptools pip twine
+    - python setup.py install
+    - pip install --break-system-packages nose2 coverage
+  script:
+    - python setup.py bdist_wheel
+    - mv dist/*.whl .
+  after_script:
+    - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python3 -m twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi *.whl
+  only:
+    - tags
+  except:
+    - branches
+
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 1a93f2bf212a577f2e3e6697fff4dd0ec5460c2c..0000000000000000000000000000000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-language: python
-python:
- - "3.5"
- - "3.6"
- - "3.7"
- - "3.8"
- - "nightly"
- - "pypy3"
-before_script:
-  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
-  - chmod +x ./cc-test-reporter
-  - ./cc-test-reporter before-build
-install:
-  - pip install -r requirements.txt
-  - pip install --upgrade coverage nose2 mock
-script:
-  - python setup.py test
-after_success:
-  - coverage xml
-  - ./cc-test-reporter after-build -t coverage.py --exit-code $TRAVIS_TEST_RESULT
diff --git a/LICENSE.md b/LICENSE.md
index 13cc922681642cc17c4eb7bed396ea99c5512a02..bbf7ef7bbf64ede5201f7c0cf898ff83ecf31a87 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -2,6 +2,7 @@ MIT License
 
 Copyright (c) 2017-2018 P.W. DMS s.c.
 Copyright (c) 2018-2020 Piotr Maślanka
+Copyright (c) 2020-2024 SMOK sp. z o. o.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/docs/series.rst b/docs/series.rst
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d534aac853b9560daf7881c1ee290ac92ce75c68 100644
--- a/docs/series.rst
+++ b/docs/series.rst
@@ -0,0 +1,15 @@
+Basic series
+############
+
+First some basics
+-----------------
+
+.. autoclass:: firanka.intervals.Interval
+    :members:
+
+.. autoclass:: firanka.series.Series
+    :members:
+
+.. autoclass:: firanka.series.DiscreteSeries
+    :members:
+
diff --git a/firanka/intervals.py b/firanka/intervals.py
index e0e9ddc2ea131bd254d5e0ae5eae55bd660e941f..ead6d2b3546ad4c05c5bccab52769d97032f1754 100644
--- a/firanka/intervals.py
+++ b/firanka/intervals.py
@@ -24,7 +24,7 @@ class Interval(object):
     """
     Interval of real numbers. Immutable.
     """
-    __slots__ = ('start', 'stop', 'left_inc', 'right_inc')
+    __slots__ = 'start', 'stop', 'left_inc', 'right_inc'
 
     def extend_to_point(self, p):
         """
diff --git a/firanka/series/base.py b/firanka/series/base.py
index b68a37e28416b79d62e2039d3aaec8a92509851b..3cc7fc4a88eaf0e7c6a1f4cfb8c7e4683adffada 100644
--- a/firanka/series/base.py
+++ b/firanka/series/base.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+import typing as tp
 import inspect
 
 from sortedcontainers import SortedList
@@ -19,6 +21,8 @@ class Series:
     for minimum functionality
     """
 
+    __slots__ = 'domain', 'comment'
+
     def __init__(self, domain, comment=u''):
         if not isinstance(domain, Interval):
             domain = Interval(domain)
@@ -79,7 +83,7 @@ class Series:
 
         return DiscreteSeries([(i, self[i]) for i in points], domain)
 
-    def join(self, series, fun):
+    def join(self, series: Series, fun: tp.Callable[[...], float]) -> JoinedSeries:
         """
         Return a new series with values of fun(index, v1, v2)
 
@@ -91,7 +95,7 @@ class Series:
 
         return JoinedSeries(self, series, fun)
 
-    def translate(self, x):
+    def translate(self, x) -> AlteredSeries:
         """
         Translate the series by some distance
         :param x: a float
@@ -105,6 +109,8 @@ class DiscreteSeries(Series):
     A series with lots of small rectangles interpolating something
     """
 
+    __slots__ = 'data'
+
     def __init__(self, data, domain=None, *args, **kwargs):
 
         data = SortedList(data)
@@ -121,7 +127,7 @@ class DiscreteSeries(Series):
             if self.domain.start < data[0][0]:
                 raise DomainError(u'some domain space is not covered by definition!')
 
-    def apply(self, fun):
+    def apply(self, fun) -> DiscreteSeries:
         assert _has_arguments(fun, 2), u'fun must have at least 2 arguments'
 
         return DiscreteSeries([(k, fun(k, v)) for k, v in self.data],
@@ -224,6 +230,8 @@ class AlteredSeries(Series):
     Internal use - for applyings, translations and slicing
     """
 
+    __slots__ = 'fun', 'series', 'x'
+
     def __init__(self, series, domain=None, fun=lambda k, v: v, x=0, *args, **kwargs):
         """
         :param series: original series
@@ -256,6 +264,8 @@ class JoinedSeries(Series):
     Series stemming from performing an operation on two series
     """
 
+    __slots__ = 'ser1', 'ser2', 'op'
+
     def __init__(self, ser1, ser2, op, *args, **kwargs):
         """:type op: callable(time: float, v1, v2: any) -> v"""
         assert _has_arguments(op, 3), u'op must have 3 arguments'
diff --git a/setup.cfg b/setup.cfg
index e9035617b167f018ab562d2e1216e5c9033a842a..0a05929bb3b8cf9fc77d0eb81f12370340bec3e3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -8,6 +8,9 @@ classifiers =
     Programming Language :: Python :: 3.7
     Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
+    Programming Language :: Python :: 3.10
+    Programming Language :: Python :: 3.11
+    Programming Language :: Python :: Implementation :: CPython
     Programming Language :: Python :: Implementation :: CPython
     Programming Language :: Python :: Implementation :: PyPy
     Operating System :: OS Independent
@@ -17,9 +20,18 @@ classifiers =
     Topic :: Software Development :: Libraries :: Python Modules
 description = Calculations on real functions
 url = https://github.com/smok-serwis/firanka
-
+platforms =
+    posix
+    win32
 [pycodestyle]
 max-line-length=100
 
 [bdist_wheel]
 universal=1
+
+[isort]
+add_imports =
+    from __future__ import absolute_import
+    from __future__ import division
+    from __future__ import print_function
+    from __future__ import unicode_literals