diff --git a/.github/actions/build_android/action.yml b/.github/actions/build_android/action.yml
new file mode 100644
index 000000000..7a3fb081a
--- /dev/null
+++ b/.github/actions/build_android/action.yml
@@ -0,0 +1,48 @@
+name: Build Android app
+description: Build and upload Android app
+inputs:
+  version:
+    required: true
+    description: "App version"
+  build_version:
+    required: true
+    description: "Build version"
+  store_artifacts:
+    required: false
+    description: "Store artifact"
+  prod_release:
+      required: true
+      description: "Is prod release"
+
+runs:
+  using: "composite"
+  steps:
+    - name: Setup Java
+      uses: actions/setup-java@v3.13.0
+      with:
+        distribution: "adopt"
+        java-version: 17
+
+    - name: Setup Flutter environment
+      uses: ./.github/actions/setup_flutter_environment
+      with:
+        prod_release: ${{ inputs.prod_release }}
+
+    - name: Build appbundle (dev)
+      if: ${{ inputs.prod_release != 'true' }}
+      run: flutter build appbundle --flavor development --release --build-name ${{ inputs.version }} --build-number ${{ inputs.build_version }} --target lib/main_development.dart
+      shell: bash
+
+    - name: Build appbundle (prod)
+      if: ${{ inputs.prod_release == 'true' }}
+      run: flutter build appbundle --flavor production --release --build-name ${{ inputs.version }} --build-number ${{ inputs.build_version }} --target lib/main_production.dart
+      shell: bash
+
+    - name: Upload Android build
+      if: ${{ !!inputs.store_artifacts }}
+      uses: actions/upload-artifact@v3.1.3
+      with:
+        name: android
+        path: build/app/outputs/bundle/release
+        retention-days: 1
+        if-no-files-found: error
diff --git a/.github/actions/build_ios/action.yml b/.github/actions/build_ios/action.yml
new file mode 100644
index 000000000..663880699
--- /dev/null
+++ b/.github/actions/build_ios/action.yml
@@ -0,0 +1,64 @@
+name: Build iOS app
+description: Build and upload iOS app
+inputs:
+  version:
+    required: true
+    description: "App version"
+  build_version:
+      required: true
+      description: "Build version"
+  apple_ios_signing_cert:
+      required: true
+      description: "iOS signing certificate"
+  apple_ios_signing_cert_pw:
+      required: true
+      description: "iOS signing certificate password base 64 encoded"
+  apple_ios_provisioning_profile:
+      required: true
+      description: "Provisioning profile"
+  apple_keychain_pw:
+      required: true
+      description: "Keychain password"
+  store_artifacts:
+      required: true
+      description: "Store artifact"
+  prod_release:
+      required: true
+      description: "Is prod release"
+
+runs:
+  using: "composite"
+  steps:
+    - name: Install Apple certificate and provisioning profile
+      if: ${{ inputs.prod_release != 'true' }}
+      run: .github/scripts/setup_certs.sh
+      env:
+        APPLE_IOS_SIGNING_CERT: ${{ inputs.apple_ios_signing_cert }}
+        APPLE_IOS_SIGNING_CERT_PW: ${{ inputs.apple_ios_signing_cert_pw }}
+        APPLE_IOS_PROVISIONING_PROFILE: ${{ inputs.apple_ios_provisioning_profile }}
+        APPLE_KEYCHAIN_PW: ${{ inputs.apple_keychain_pw }}
+      shell: bash
+
+    - name: Setup Flutter environment
+      uses: ./.github/actions/setup_flutter_environment
+      with:
+        prod_release: ${{ inputs.prod_release }}
+
+    - name: Build iOS (dev)
+      if: ${{ inputs.prod_release != 'true' }}
+      run: .github/scripts/build_ios_dev.sh ${{ inputs.version }} ${{ inputs.build_version }}
+      shell: bash
+
+    - name: Build iOS (prod)
+      if: ${{ inputs.prod_release == 'true' }}
+      run: .github/scripts/build_ios_prod.sh ${{ inputs.version }} ${{ inputs.build_version }}
+      shell: bash
+
+    - name: Upload iOS build
+      if: ${{ inputs.store_artifacts }}
+      uses: actions/upload-artifact@v3.1.3
+      with:
+        name: ios
+        path: build-output/ios
+        retention-days: 1
+        if-no-files-found: error
\ No newline at end of file
diff --git a/.github/actions/setup_flutter_environment/action.yml b/.github/actions/setup_flutter_environment/action.yml
new file mode 100644
index 000000000..93fdfa4df
--- /dev/null
+++ b/.github/actions/setup_flutter_environment/action.yml
@@ -0,0 +1,32 @@
+name: Setup Flutter environment
+description: Setup the Flutter environment
+inputs:
+  prod_release:
+    required: false
+    default: 'false'
+    description: "Is prod release"
+
+env:
+  FLUTTER_VERSION: 3.13.7
+
+runs:
+  using: "composite"
+  steps:
+    - name: Setup Flutter environment
+      uses: subosito/flutter-action@v2.11.0
+      with:
+        flutter-version: ${{ env.FLUTTER_VERSION }}
+        channel: "stable"
+
+    - name: Download dependencies
+      run: flutter pub get
+      shell: bash
+
+    - name: Replace target URI in Production
+      if: ${{ inputs.prod_release == 'true' }}
+      run: sed -i'' -e 's/.env.develop/.env.production/' lib/env/env.dart
+      shell: bash
+
+    - name: Generate code
+      run: dart run build_runner build
+      shell: bash
\ No newline at end of file
diff --git a/.github/scripts/build-dev.sh b/.github/scripts/build_ios_dev.sh
similarity index 100%
rename from .github/scripts/build-dev.sh
rename to .github/scripts/build_ios_dev.sh
diff --git a/.github/scripts/build-prod.sh b/.github/scripts/build_ios_prod.sh
similarity index 100%
rename from .github/scripts/build-prod.sh
rename to .github/scripts/build_ios_prod.sh
diff --git a/.github/scripts/setup-certs.sh b/.github/scripts/setup_certs.sh
similarity index 100%
rename from .github/scripts/setup-certs.sh
rename to .github/scripts/setup_certs.sh
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index eda819db7..ead262534 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,4 +1,4 @@
-name: Build and test
+name: Build application
 
 on:
   pull_request:
@@ -8,22 +8,20 @@ on:
 
   workflow_call:
     inputs:
-      storeArtifacts:
+      store_artifacts:
         type: boolean
         required: false
         default: false
-      prodRelease:
+      prod_release:
         type: boolean
         required: false
-      tagName:
+      tag_name:
         type: string
         required: false
 
 env:
-  FLUTTER_VERSION: 3.13.1
-  JAVA_VERSION: 17.x
-  PROD_RELEASE: ${{ inputs.prodRelease || 'false' }}
-  TAG_NAME: ${{ inputs.tagName || 'dev' }}
+  PROD_RELEASE: ${{ inputs.prod_release || 'false' }}
+  TAG_NAME: ${{ inputs.tag_name || 'dev' }}
 
 jobs:
   version:
@@ -55,67 +53,35 @@ jobs:
     name: Build iOS App
     runs-on: macos-latest
     needs: [version]
-
     steps:
       - name: Checkout code
         uses: actions/checkout@v4.1.0
 
-      - name: Install Apple certificate and provisioning profile (dev)
-        if: env.PROD_RELEASE != 'true'
-        run: .github/scripts/setup-certs.sh
-        env:
-          APPLE_IOS_SIGNING_CERT: ${{ secrets.APPLE_IOS_SIGNING_CERTIFICATE_DEVELOPMENT }}
-          APPLE_IOS_SIGNING_CERT_PW: ${{ secrets.APPLE_IOS_SIGNING_CERTIFICATE_DEVELOPMENT_PASSWORD }}
-          APPLE_IOS_PROVISIONING_PROFILE: ${{ secrets.APPLE_IOS_PROVISIONING_PROFILE_DEVELOPMENT }}
-          APPLE_KEYCHAIN_PW: ${{ secrets.APPLE_KEYCHAIN_PW }}
-
-      - name: Install Apple certificate and provisioning profile (prod)
-        if: env.PROD_RELEASE== 'true'
-        run: .github/scripts/setup-certs.sh
-        env:
-          APPLE_IOS_SIGNING_CERT: ${{ secrets.APPLE_IOS_SIGNING_CERT_PROD }}
-          APPLE_IOS_SIGNING_CERT_PW: ${{ secrets.APPLE_IOS_SIGNING_CERT_PW }}
-          APPLE_IOS_PROVISIONING_PROFILE: ${{ secrets.APPLE_IOS_PROVISIONING_PROFILE_PROD }}
-          APPLE_KEYCHAIN_PW: ${{ secrets.APPLE_KEYCHAIN_PW }}
-
-      - name: Setup Java
-        uses: actions/setup-java@v3.13.0
+      - name: Build iOS app (dev)
+        if: ${{ env.PROD_RELEASE != 'true' }}
+        uses: ./.github/actions/build_ios
         with:
-          distribution: "adopt"
-          java-version: ${{ env.JAVA_VERSION }}
-
-      - name: Setup Flutter environment
-        uses: subosito/flutter-action@v2.11.0
+          version: ${{ needs.version.outputs.version }}
+          build_version: ${{ needs.version.outputs.build_version }}
+          apple_ios_signing_cert: ${{ secrets.APPLE_IOS_SIGNING_CERTIFICATE_DEVELOPMENT }}
+          apple_ios_signing_cert_pw: ${{ secrets.APPLE_IOS_SIGNING_CERTIFICATE_DEVELOPMENT_PASSWORD }}
+          apple_ios_provisioning_profile: ${{ secrets.APPLE_IOS_PROVISIONING_PROFILE_DEVELOPMENT }}
+          apple_keychain_pw: ${{ secrets.APPLE_KEYCHAIN_PW }}
+          store_artifacts: ${{ inputs.store_artifacts }}
+          prod_release: ${{ env.PROD_RELEASE }}
+
+      - name: Build iOS app (prod)
+        if: ${{ env.PROD_RELEASE == 'true' }}
+        uses: ./.github/actions/build_ios
         with:
-          flutter-version: ${{ env.FLUTTER_VERSION }}
-          channel: "stable"
-
-      - name: Download dependencies
-        run: flutter pub get
-
-      - name: Set URI (prod)
-        if: env.PROD_RELEASE == 'true'
-        run: sed -i '' 's/.env.develop/.env.production/' lib/env/env.dart
-
-      - name: Generate code
-        run: dart run build_runner build
-
-      - name: Create Development build
-        if: env.PROD_RELEASE != 'true'
-        run: .github/scripts/build-dev.sh ${{ needs.version.outputs.version }} ${{ needs.version.outputs.build_version }}
-
-      - name: Create Production build
-        if: env.PROD_RELEASE == 'true'
-        run: .github/scripts/build-prod.sh ${{ needs.version.outputs.version }} ${{ needs.version.outputs.build_version }}
-
-      - name: Upload iOS build
-        if: ${{ inputs.storeArtifacts }}
-        uses: actions/upload-artifact@v3.1.3
-        with:
-          name: ios
-          path: build-output/ios
-          retention-days: 1
-          if-no-files-found: error
+          version: ${{ needs.version.outputs.version }}
+          build_version: ${{ needs.version.outputs.build_version }}
+          apple_ios_signing_cert: ${{ secrets.APPLE_IOS_SIGNING_CERT_PROD }}
+          apple_ios_signing_cert_pw: ${{ secrets.APPLE_IOS_SIGNING_CERT_PW }}
+          apple_ios_provisioning_profile: ${{ secrets.APPLE_IOS_PROVISIONING_PROFILE_PROD }}
+          apple_keychain_pw: ${{ secrets.APPLE_KEYCHAIN_PW }}
+          store_artifacts: ${{ inputs.store_artifacts }}
+          prod_release: ${{ env.PROD_RELEASE }}
 
   build_android:
     name: Build Android App
@@ -125,79 +91,10 @@ jobs:
       - name: Checkout code
         uses: actions/checkout@v4.1.0
 
-      - name: Setup Java
-        uses: actions/setup-java@v3.13.0
-        with:
-          distribution: "adopt"
-          java-version: ${{ env.JAVA_VERSION }}
-
-      - name: Setup Flutter environment
-        uses: subosito/flutter-action@v2.11.0
-        with:
-          flutter-version: ${{ env.FLUTTER_VERSION }}
-          channel: "stable"
-
-      - name: Download dependencies
-        run: flutter pub get
-
-      - name: Set URI (prod)
-        if: env.PROD_RELEASE == 'true'
-        run: sed -i 's/.env.develop/.env.production/' lib/env/env.dart
-
-      - name: Generate code
-        run: dart run build_runner build
-
-      - name: Build appbundle (dev)
-        if: env.PROD_RELEASE != 'true'
-        run: flutter build apk --flavor development --release --build-name ${{ needs.version.outputs.version }} --build-number ${{ needs.version.outputs.build_version }} --target lib/main_development.dart
-
-      - name: Build appbundle (prod)
-        if: env.PROD_RELEASE== 'true'
-        run: flutter build apk --flavor production --release --dart-define=IS_PROD=true --build-name ${{ needs.version.outputs.version }} --build-number ${{ needs.version.outputs.build_version }} --target lib/main_production.dart
-
-      - name: Upload Android build
-        if: ${{ inputs.storeArtifacts }}
-        uses: actions/upload-artifact@v3.1.3
-        with:
-          name: android
-          path: build/app/outputs/flutter-apk
-          retention-days: 1
-          if-no-files-found: error
-
-  test:
-    name: Test Flutter app
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v4.1.0
-
-      - name: Setup Flutter environment
-        uses: subosito/flutter-action@v2.11.0
-        with:
-          flutter-version: ${{ env.FLUTTER_VERSION }}
-          channel: "stable"
-
-      - name: Download dependencies
-        run: flutter pub get
-
-      - name: Generate code
-        run: dart run build_runner build
-
-      - name: Check formatting
-        run: dart format --set-exit-if-changed lib/
-
-      - name: Static Analysis
-        run: flutter analyze
-
-      - name: Run Code Metrics
-        run: dart run dart_code_metrics:metrics --reporter=github lib
-
-      - name: Run tests
-        run: flutter test --coverage
-
-      - name: Upload test report to Codecov
-        uses: codecov/codecov-action@v3.1.4
+      - name: Build Android app
+        uses: ./.github/actions/build_android
         with:
-          flags: unittests
-          file: coverage/lcov.info
+          version: ${{ needs.version.outputs.version }}
+          build_version: ${{ needs.version.outputs.build_version }}
+          store_artifacts: ${{ inputs.store_artifacts }}
+          prod_release: ${{ env.PROD_RELEASE }}
diff --git a/.github/workflows/codesee-arch-diagram.yml b/.github/workflows/codesee.yml
similarity index 87%
rename from .github/workflows/codesee-arch-diagram.yml
rename to .github/workflows/codesee.yml
index 806d41d12..5e38b969b 100644
--- a/.github/workflows/codesee-arch-diagram.yml
+++ b/.github/workflows/codesee.yml
@@ -2,8 +2,7 @@
 # This is v2.0 of this workflow file
 on:
   push:
-    branches:
-      - main
+    branches: [ main ]
   pull_request_target:
     types: [opened, synchronize, reopened]
 
@@ -15,7 +14,7 @@ jobs:
   codesee:
     runs-on: ubuntu-latest
     continue-on-error: true
-    name: Analyze the repo with CodeSee
+    name: Analyze repository
     steps:
       - uses: Codesee-io/codesee-action@v2
         with:
diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml
index 9fe2f63b4..cbcfc326a 100644
--- a/.github/workflows/release-dev.yml
+++ b/.github/workflows/release-dev.yml
@@ -5,17 +5,20 @@ on:
     branches: [ main ]
 
 jobs:
-  build_and_test:
+  build:
     uses: ./.github/workflows/build.yml
     with:
-      storeArtifacts: true
-      prodRelease: false
+      store_artifacts: true
+      prod_release: false
     secrets: inherit
 
-  dev_upload_ios:
+  test:
+    uses: ./.github/workflows/test.yml
+
+  upload_ios:
     name: Upload iOS build to Firebase App Distribution
     runs-on: ubuntu-latest
-    needs: [build_and_test]
+    needs: [build, test]
 
     steps:
       - name: Download Artifact
@@ -33,10 +36,10 @@ jobs:
           groups: analogio-devs
           file: Analog.ipa
 
-  dev_upload_android:
+  upload_android:
     name: Upload Android build to Firebase App Distribution
     runs-on: ubuntu-latest
-    needs: [build_and_test]
+    needs: [build, test]
 
     steps:
       - name: Download artifact
diff --git a/.github/workflows/release-prod.yml b/.github/workflows/release-prod.yml
index fd207e87e..5f628d277 100644
--- a/.github/workflows/release-prod.yml
+++ b/.github/workflows/release-prod.yml
@@ -2,21 +2,24 @@ name: Release Production App
 
 on:
   release:
-    types: [created]
+    types: [ created ]
 
 jobs:
-  build_and_test:
+  build:
     uses: ./.github/workflows/build.yml
     with:
-      storeArtifacts: true
-      prodRelease: true
-      tagName: ${{ github.event.release.tag_name }}
+      store_artifacts: true
+      prod_release: true
+      tag_name: ${{ github.event.release.tag_name }}
     secrets: inherit
 
-  prod_release_ios:
+  test:
+    uses: ./.github/workflows/test.yml
+
+  release_ios:
     name: Upload iOS build to App Store connect
     runs-on: macos-latest
-    needs: [build_and_test]
+    needs: [build, test]
 
     steps:
       - name: Download artifact
@@ -31,10 +34,10 @@ jobs:
         run: |
           xcrun altool --upload-app -t ios -f "Analog.ipa" -u "$APP_STORE_CONNECT_USERNAME" -p "$APP_STORE_CONNECT_PASSWORD"
 
-  prod_release_android:
+  release_android:
     name: Upload Android build to Play Store
     runs-on: ubuntu-latest
-    needs: [build_and_test]
+    needs: [build, test]
 
     steps:
       - name: Download artifact
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..0ee0e0936
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,33 @@
+name: Test
+
+on:
+  pull_request:
+    branches: [ main ]
+
+  workflow_call:
+
+jobs:
+  test:
+    name: Test app
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v3
+
+      - name: Setup Flutter environment
+        uses: ./.github/actions/setup_flutter_environment
+
+      - name: Check formatting
+        run: dart format --set-exit-if-changed lib/
+
+      - name: Static Analysis
+        run: flutter analyze
+
+      - name: Run tests
+        run: flutter test --coverage
+
+      - name: Upload test report to Codecov
+        uses: codecov/codecov-action@v3.1.4
+        with:
+          flags: unittests
+          file: coverage/lcov.info
\ No newline at end of file