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