← Back

Migrating alphazed-utils to src/scripts: Monorepo Consolidation

·infrastructure

Migrating alphazed-utils to src/scripts: Monorepo Consolidation

Published: February 2026 Category: Infrastructure & Developer Experience Reading Time: 7 minutes

The Problem: Multi-Repo Dependency Hell

Managing utility scripts across separate repositories creates unnecessary friction. Every update requires version bumps, package publishing, dependency updates, and cross-repo testing. What should be a simple script change becomes a multi-step deployment process.

Our alphazed-utils package lived in a separate repository from the main alphazed-alqosh application. This separation seemed elegant initially—shared utilities could be versioned and imported cleanly. Reality proved otherwise.

Before: Two-Repository Architecture

Development Workflow (Multi-Repo)
┌──────────────────────────────────────────────────────────────┐
│ Make change to utility function                              │
│ ├─ Edit alphazed-utils/utils/analytics.py                   │
│ ├─ Bump version in setup.py (1.2.3 → 1.2.4)                 │
│ ├─ Run tests in alphazed-utils repo                         │
│ ├─ Build package: python setup.py sdist bdist_wheel         │
│ ├─ Publish to PyPI or private registry                      │
│ ├─ Wait for package to propagate (5-10 min)                 │
│ ├─ Update alphazed-alqosh/requirements.txt                  │
│ │   alphazed-utils==1.2.3 → alphazed-utils==1.2.4           │
│ ├─ pip install -r requirements.txt                          │
│ ├─ Run tests in alphazed-alqosh repo                        │
│ ├─ Deploy main application                                  │
│ └─ Total time: 30-45 minutes                                │
└──────────────────────────────────────────────────────────────┘

Project Structure:

alphazed-alqosh/                    alphazed-utils/
├─ src/                             ├─ utils/
│  ├─ services/                     │  ├─ analytics/
│  ├─ resources/                    │  ├─ content/
│  └─ models/                       │  └─ reporting/
├─ requirements.txt                 ├─ setup.py
│  └─ alphazed-utils==1.2.3         ├─ tests/
└─ tests/                           └─ README.md

Two repositories, two test suites, two deployment pipelines

Pain Points

1. Slow Iteration Cycles

A one-line bug fix in a utility function required publishing a new package version. Development velocity suffered from ceremony.

2. Version Management Overhead

Coordinating compatible versions between repos became a manual tracking exercise. Which version of alphazed-utils worked with which commit of alphazed-alqosh? Documentation lagged behind reality.

3. Testing Complexity

Integration tests requiring utilities needed careful environment setup. Local development required installing packages from private registries or using pip install -e with absolute paths.

4. Dependency Resolution Issues

Python's dependency resolution occasionally picked incompatible versions. Pinning exact versions (==1.2.3) prevented automatic security updates. Using ranges (>=1.2.0,<2.0.0) risked breaking changes.

5. CI/CD Pipeline Duplication

Both repositories needed separate CircleCI configurations, separate test suites, separate deployment steps. Maintaining two pipelines for tightly coupled code made no sense.

The Solution: Monorepo Consolidation

We migrated alphazed-utils directly into alphazed-alqosh under src/scripts/. No packages. No versions. No publishing. Just code.

After: Single-Repository Architecture

Development Workflow (Monorepo)
┌──────────────────────────────────────────────────────────────┐
│ Make change to utility function                              │
│ ├─ Edit src/scripts/analytics/report.py                     │
│ ├─ Run tests: ./run_tests.sh                                │
│ ├─ Commit + push                                             │
│ ├─ CI/CD deploys automatically                               │
│ └─ Total time: 5-10 minutes                                 │
└──────────────────────────────────────────────────────────────┘
    6× faster iteration

Project Structure (Consolidated):

alphazed-alqosh/
├─ src/
│  ├─ services/
│  ├─ resources/
│  ├─ models/
│  ├─ scripts/  ← Migrated utilities
│  │  ├─ analytics/
│  │  │  ├─ amplitude_report.py
│  │  │  ├─ user_metrics.py
│  │  │  └─ export_data.py
│  │  ├─ content/
│  │  │  ├─ tts_batch_generate.py
│  │  │  ├─ media_validator.py
│  │  │  └─ import_subjects.py
│  │  ├─ reporting/
│  │  │  ├─ daily_stats.py
│  │  │  ├─ cost_analysis.py
│  │  │  └─ test_coverage.py
│  │  └─ __init__.py
│  └─ ...
├─ requirements.txt  ← No external package dependency
└─ tests/
   ├─ unit/
   └─ integration/

Single repository, single test suite, single deployment pipeline

Migration Process

Phase 1: Copy & Verify (Commits: 94e76c7, a0aa6c3)

# Copy utilities into main repo
cp -r ../alphazed-utils/utils/* src/scripts/

# Update import statements
# Before: from alphazed_utils.analytics import generate_report
# After:  from src.scripts.analytics import generate_report

# Run full test suite
./run_tests.sh --suite full

Phase 2: Remove External Dependency

# requirements.txt
-alphazed-utils==1.2.3
 flask==3.0.0
 sqlalchemy==2.0.23
 ...

Phase 3: Update CI/CD

No changes needed. The existing CircleCI configuration already tested all code under src/. Scripts became first-class citizens automatically.

Phase 4: Archive Old Repository

We archived alphazed-utils as read-only with a clear deprecation notice:

# alphazed-utils [ARCHIVED]

This package has been migrated to alphazed-alqosh/src/scripts/
No further updates will be published.

Implementation Details

Import Path Updates

Python's import system required careful path handling:

# src/scripts/analytics/report.py
from src.services.analytics import AmplitudeService
from src.models.user import User
from src.scripts.reporting.daily_stats import calculate_metrics

# Absolute imports from project root
# No relative imports across script boundaries

Script Entry Points

Scripts remain executable as standalone tools:

#!/usr/bin/env python3
# src/scripts/analytics/amplitude_report.py

import sys
from pathlib import Path

# Add project root to path
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))

from src.services.analytics import AmplitudeService

def main():
    # Script logic here
    pass

if __name__ == "__main__":
    main()

Testing Integration

Scripts inherit the main test infrastructure:

# tests/unit/scripts/test_amplitude_report.py
from src.scripts.analytics.amplitude_report import generate_report

def test_generate_report():
    result = generate_report(user_id=123, date_range="2026-01")
    assert result["total_events"] > 0

CI/CD Behavior

No additional configuration needed. Scripts tested automatically:

# .circleci/config.yml (unchanged)
- run:
    name: Run tests
    command: |
      ./run_tests.sh --suite quick --fail-fast src/tests
      # Includes src/scripts/ automatically

Results

Development Velocity

  • 45-minute deployment cycle → 10-minute deployment cycle (4.5× faster)
  • One-line fixes deployed in minutes, not hours
  • Eliminated package publishing ceremony

Reduced Complexity

  • 2 repositories → 1 repository
  • 2 CI/CD pipelines → 1 CI/CD pipeline
  • 2 test suites → 1 test suite
  • Zero version management overhead

Improved Testing

  • Integration tests run against current code, not stale packages
  • Local development setup simplified (no private package registries)
  • Test failures caught immediately

Better Code Organization

Scripts grouped by domain (analytics, content, reporting) rather than artificially separated:

Before: Navigate between two repos to understand workflow
After:  All related code in one place

Dependency Management

Eliminated an entire class of dependency issues:

  • No version conflicts
  • No "which version works with which?" questions
  • Security updates happen automatically (no pinned package versions)

When NOT to Consolidate

This approach works when:

  • Utilities are tightly coupled to the main application
  • The "shared package" has one primary consumer
  • Iteration speed matters more than formal versioning

Keep separate packages when:

  • Multiple independent projects consume the utilities
  • Utilities have significantly different release cycles
  • You need strict semantic versioning for API contracts
  • External teams depend on the package

Key Lessons

1. Packaging Creates Ceremony

Every package boundary introduces process. Question whether that process adds value.

2. Monorepos Reduce Coordination Overhead

When repositories must evolve together, combining them removes synchronization problems.

3. Scripts Are Code

Treating scripts as second-class citizens (separate repos, minimal tests) creates technical debt. Bring them into the main codebase.

4. Version Management Is Real Work

If you're spending time tracking version compatibility, you're working around a structural problem. Fix the structure.

Related Improvements

This consolidation enabled:

  • Faster CI/CD - Single pipeline runs all tests
  • Better onboarding - New developers clone one repo, not three
  • Improved discoverability - All code searchable in one place
  • Consistent tooling - Same linters, formatters, test frameworks

Conclusion

The alphazed-utils package existed because "that's how you share Python code." But blindly following conventions without questioning context creates unnecessary complexity.

Before: Two repositories, version coordination, 45-minute deployment cycles. After: One repository, no versions, 10-minute deployment cycles.

Sometimes the best architecture decision is deleting a repository.


Technical Stack:

  • Python 3.11
  • Flask 3.0
  • CircleCI for CI/CD
  • Migration commits: 94e76c7, a0aa6c3

Impact:

  • 4.5× faster iteration cycles
  • Zero version management overhead
  • 50% reduction in CI/CD maintenance