Replies: 6 comments 5 replies
-
Solutions to the Dependency Update Problem: A Comparative Analysis1. Overview of ApproachesThe dependency update problem with integration testing can be solved through several architectural and tooling approaches. Each has different trade-offs in terms of complexity, flexibility, and scalability. Categories of Solutions:
2. Monorepo Approach2.1 ConceptAll packages live in a single repository with a shared version control history. Dependencies between packages use local paths rather than published versions during development. Key Insight: Version constraints are effectively ignored for internal packages - the build system always uses the latest local code. 2.2 ArchitectureDependency Resolution:
2.3 Who Uses ThisGoogle (Bazel)Scale:
Build System: Bazel # packages/models/BUILD
cc_library(
name = "models",
srcs = ["models.cc"],
deps = [
"//packages/base:base", # Local dependency
],
)Key Features:
Advantages:
Challenges:
Facebook/Meta (Buck2)Similar to Google's approach:
Microsoft (parts of Windows, parts of Office)
2.4 Advantages✅ No version coordination problems - always use latest local code 2.5 Disadvantages❌ Tooling requirements - needs sophisticated build system 3. Version Range Strategies3.1 ConceptAllow packages to declare compatibility with multiple versions through ranges. The resolver picks the best version that satisfies all constraints. Key Insight: Preventive widening of compatibility ranges allows testing unreleased versions without breaking existing users. 3.2 Implementation in Different EcosystemsRust (Cargo)Syntax: [dependencies]
base = "1" # Any 1.x.y version
base = ">=1.5, <2" # Explicit range
base = "1.5" # 1.5.x (caret by default)Pre-release support: base = "2.0.0-beta" # Explicitly opt into pre-releasesWho uses it:
JavaScript (npm/pnpm/yarn)Syntax: {
"dependencies": {
"base": "^1.0.0", // 1.x.x (caret)
"base": "~1.5.0", // 1.5.x (tilde)
"base": ">=1.0.0 <2.0.0"
}
}Pre-release tags: "base": "2.0.0-beta.1"Who uses it:
Python (pip/poetry)Syntax (Poetry): [tool.poetry.dependencies]
base = "^1.0" # 1.x.x
base = ">=1.5,<2.0" # Explicit rangeWho uses it:
Julia (Pkg.jl)Syntax: [compat]
CTBase = "1" # 1.x.x
CTBase = "1.5" # 1.5.x
CTBase = "1, 2" # Both major versions
CTBase = "0.16, 0.17.0-" # Include pre-releasesWho uses it:
3.3 Advantages✅ Flexibility - packages can work with multiple versions 3.4 Disadvantages❌ Range maintenance - need to test with all claimed compatible versions 4. Local Override Mechanisms4.1 ConceptDuring development, temporarily substitute registry packages with local versions. Production builds use published versions. Key Insight: Separate development configuration from production, allowing testing without publishing. 4.2 Implementation PatternsCargo (Rust) - [patch]# Root Cargo.toml
[patch.crates-io]
base = { path = "../base-dev" }
# Or from git branch
base = { git = "https://github.com/org/base", branch = "v2-dev" }Behavior:
npm/yarn - link and resolutionsnpm link: cd ../base
npm link
cd ../control
npm link base # Use local versionYarn resolutions: {
"resolutions": {
"base": "file:../base-dev"
}
}Python - editable installspip install -e ../base-dev # Editable modePoetry: [tool.poetry.dependencies]
base = { path = "../base-dev", develop = true }Julia - Pkg.develop()using Pkg
Pkg.develop(path="../CTBase-dev")
# Or from git
Pkg.develop(url="https://github.com/org/CTBase", rev="v2-dev")Key feature: Completely ignores version constraints for developed packages. 4.3 Who Uses ThisCommon in:
Examples:
4.4 Advantages✅ No repository changes - keeps polyrepo structure 4.4 Disadvantages❌ Manual setup - developers must configure locally 5. Workspace Systems5.1 ConceptHybrid approach: multiple packages in one repository, but treated as separate during publication. Internal dependencies use local code during development, published versions in production. Key Insight: Get monorepo benefits during development, polyrepo benefits for publication. 5.2 ImplementationCargo Workspaces (Rust)# Root Cargo.toml
[workspace]
members = [
"packages/base",
"packages/models",
"packages/control",
]Behavior:
npm/yarn/pnpm Workspaces{
"workspaces": [
"packages/*"
]
}pnpm workspace protocol: {
"dependencies": {
"base": "workspace:*" // Always use local version
}
}Poetry Workspaces (Python)Still experimental, but supported via: [tool.poetry.dependencies]
base = { path = "../base", develop = true }Lerna/Nx (JavaScript)Lerna:
Nx:
5.3 Who Uses ThisRust Ecosystem
JavaScript Ecosystem
Examples of Corporate Usage
5.4 Advantages✅ Best of both worlds - monorepo dev, polyrepo publish 5.5 Disadvantages❌ Complexity - more setup than pure monorepo or polyrepo 6. Pre-release Workflow Strategy6.1 ConceptUse semantic versioning pre-release identifiers (alpha, beta, rc) to stage rollouts. Users opt-in to pre-releases explicitly. Key Insight: Pre-releases are published but ignored by default, allowing safe testing in production-like environments. 6.2 Implementation PatternVersion progression: Resolver behavior:
6.3 Best PracticesProgressive compatibility widening:
6.4 Who Uses ThisRust Ecosystem
JavaScript Ecosystem
Python
6.5 Advantages✅ Safe rollout - users must explicitly opt-in 6.6 Disadvantages❌ Multiple releases - need to publish several versions 7. Comparative Analysis7.1 Decision Matrix
7.2 Choosing an ApproachUse Monorepo If:
Use Workspaces If:
Use Version Ranges If:
Use Local Overrides If:
Use Pre-releases If:
8. Hybrid Approaches8.1 Combining StrategiesReal-world projects often combine multiple approaches: Example 1: Workspace + Pre-releases Example 2: Local Overrides + Version Ranges Example 3: Monorepo + External Packages 8.2 Real-World Examples
Microsoft
Meta/Facebook
9. Recommendations for Different Scales9.1 Small Projects (1-10 packages, 1-5 developers)Recommended: Version ranges + local overrides
Workflow:
9.2 Medium Projects (10-50 packages, 5-50 developers)Recommended: Workspaces + pre-releases
Workflow:
9.3 Large Projects (50+ packages, 50+ developers)Recommended: Full monorepo (if resources allow) or advanced workspaces
Workflow:
10. Tooling Ecosystem Summary10.1 Build Systems
10.2 Package Managers with Workspace Support
11. Conclusion11.1 Key Takeaways
11.2 General PrinciplesFor the dependency update problem specifically:
Success factors:
|
Beta Was this translation helpful? Give feedback.
-
CTBase v0.16 → v0.17 Migration Guide with Pre-release Strategy1. OverviewThis guide explains how to migrate CTBase from v0.16.0 to v0.17.0 (a breaking change) using a pre-release strategy that ensures:
2. Package Dependency GraphCurrent state:
Goal:
3. How Julia's Pkg.jl Resolver Works3.1 Version Selection RulesJulia follows Semantic Versioning 2.0.0 strictly with specific resolution rules: Rule 1: Version OrderingPre-releases are considered lower than the corresponding stable release. Rule 2: Pre-release IgnoringBy default, Pkg ignores pre-releases completely. using Pkg
Pkg.add("CTBase") # Will install 0.16.0, ignores 0.17.0-beta.1Even if Rule 3: Explicit Pre-release Opt-inTo use a pre-release, you must explicitly allow it in compat: # This ignores pre-releases
[compat]
CTBase = "0.16"
# This accepts pre-releases
[compat]
CTBase = "0.16, 0.17.0-"The notation Rule 4: Resolver IntersectionWhen multiple packages have constraints, the resolver finds the intersection: # Package A requires: CTBase ∈ {0.16, 0.17.0-}
# Package B requires: CTBase ∈ {0.17.0-}
# Intersection: {0.17.0-}
# Result: Pkg installs 0.17.0-beta.1 (latest pre-release)Rule 5: Maximum Version PreferenceWhen multiple versions satisfy constraints, Pkg chooses the highest: # Available: 0.17.0-beta.1, 0.17.0-beta.2
# Constraints satisfied by both
# Result: Pkg installs 0.17.0-beta.23.2 Example: User Installing OptimalControlScenario 1: Before migration (stable) using Pkg
Pkg.add("OptimalControl")Registry state:
Result: Installs CTBase v0.16.0 ✅ Scenario 2: During migration (beta available) using Pkg
Pkg.add("OptimalControl")Registry state:
Result: Still installs CTBase v0.16.0 ✅ Why? Because Scenario 3: After CTModels migration using Pkg
Pkg.add(["OptimalControl", "CTModels"])Registry state:
Result: Installs CTBase v0.17.0-beta.1 ✅ Why? Resolver finds intersection:
Scenario 4: After stable v0.17.0 release using Pkg
Pkg.add("OptimalControl")Registry state:
Result: Installs CTBase v0.17.0 ✅ Why? v0.17.0 (stable) is now the maximum in the allowed set. 4. Migration Workflow with Pre-releasesPhase 1: Preventive Compatibility WideningGoal: Prepare all packages to accept CTBase v0.17 pre-releases Step 1.1: CTModelsFile: name = "CTModels"
uuid = "..."
version = "0.10.2" # Bump patch version
[deps]
CTBase = "..."
[compat]
CTBase = "0.16, 0.17.0-" # ← Changed from "0.16"
julia = "1.9"Actions:
User impact: None - users still get CTBase v0.16.0 Step 1.2: CTParser[compat]
CTBase = "0.16, 0.17.0-"
CTModels = "0.10" # Accept new CTModels versionActions: PR → CI → Merge → Register CTParser v0.8.4 Step 1.3: CTDirect[compat]
CTBase = "0.16, 0.17.0-"
CTModels = "0.10"Actions: PR → CI → Merge → Register CTDirect v0.12.6 Step 1.4: CTFlows[compat]
CTBase = "0.16, 0.17.0-"
CTModels = "0.10"Actions: PR → CI → Merge → Register CTFlows v0.6.3 Step 1.5: OptimalControl[compat]
CTBase = "0.16, 0.17.0-"
CTDirect = "0.12"
CTModels = "0.10"
CTFlows = "0.6"
CTParser = "0.8"Actions: PR → CI → Merge → Register OptimalControl v0.15.9 Result after Phase 1:
Phase 2: CTBase v0.17.0-beta.1 DevelopmentGoal: Develop breaking changes and release as pre-release Step 2.1: Develop ChangesBranch:
Step 2.2: Run Breakage CIYour breakage workflow automatically tests: # From .github/workflows/breakage.yml
strategy:
matrix:
pkg:
- {user: control-toolbox, repo: CTModels.jl, group: ALL}
- {user: control-toolbox, repo: CTParser.jl, group: ALL}
- {user: control-toolbox, repo: CTDirect.jl, group: ALL}
- {user: control-toolbox, repo: CTFlows.jl, group: ALL}
- {user: control-toolbox, repo: OptimalControl.jl, group: ALL}What happens:
# For stable version
Pkg.add(name="CTModels") # Gets registry version
# For main branch
Pkg.add(url="https://github.com/control-toolbox/CTModels.jl", rev="main")
Expected result:
Step 2.3: Release BetaFile: name = "CTBase"
uuid = "..."
version = "0.17.0-beta.1" # ← Pre-release versionActions:
Registry after registration: User impact: using Pkg
Pkg.add("CTBase") # Still gets v0.16.0 ✅
Pkg.status()
# [1234...] CTBase v0.16.0Users are completely isolated from the beta. Phase 3: Migrate CTModels to v0.17Goal: Adapt CTModels to work with CTBase v0.17.0-beta.1 Step 3.1: Adapt CodeBranch:
using Pkg
Pkg.develop(path="../CTBase") # Or use beta from registry
Pkg.test("CTModels")Step 3.2: Update CompatFile: name = "CTModels"
version = "0.11.0" # ← Bump MINOR (breaking since v0.x)
[compat]
CTBase = "0.17.0-" # ← Now REQUIRES beta (removed 0.16)Why bump minor? In semantic versioning, for v0.x packages, minor bumps indicate breaking changes (v0.x is considered pre-1.0 development). Step 3.3: Test with Breakage CIWhen you push this PR, your breakage workflow tests:
If tests pass: CTModels changes are backward compatible! 🎉 If tests fail: OptimalControl needs updates too. Step 3.4: Register CTModelsActions:
Registry state: User impact: # User installing just OptimalControl
Pkg.add("OptimalControl")
# Gets: CTBase 0.16.0, CTModels 0.10.2 ✅
# User explicitly wanting CTModels latest
Pkg.add("CTModels")
# Gets: CTModels 0.11.0, CTBase 0.17.0-beta.1
# (because CTModels 0.11.0 requires it)
# This is opt-in behavior ✅Phase 4: Migrate Remaining PackagesFor each package (CTParser, CTDirect, CTFlows, OptimalControl): If Breakage Tests Failed (API Changes):Example: CTParser needs updates # CTParser/Project.toml
version = "0.9.0" # Bump minor (breaking)
[compat]
CTBase = "0.17.0-"
CTModels = "0.11" # New versionActions:
If Breakage Tests Passed (No API Changes):Example: OptimalControl still works # OptimalControl/Project.toml
version = "0.15.10" # Bump patch (not breaking)
[compat]
CTBase = "0.16, 0.17.0-" # Keep wide range
CTModels = "0.10, 0.11" # Accept both versions
CTDirect = "0.12, 0.13"
# ... etcActions:
User impact: # User on stable OptimalControl 0.15.9
Pkg.add("OptimalControl")
# Gets: CTBase 0.16.0 (still stable preference)
# User installing OptimalControl + CTModels together
Pkg.add(["OptimalControl", "CTModels"])
# Gets: CTBase 0.17.0-beta.1 (to satisfy CTModels)Phase 5: Final Stable ReleaseGoal: Release CTBase v0.17.0 stable Step 5.1: Verify ReadinessCheck:
Step 5.2: Release StableFile: version = "0.17.0" # ← Remove -beta suffixActions:
Registry state: User impact: using Pkg
Pkg.update() # Or new install
# Now gets CTBase v0.17.0 (stable)Julia's resolver now prefers v0.17.0 over v0.16.0 because:
Step 5.3: (Optional) Tighten CompatIf you want to force users to upgrade: # CTModels/Project.toml
version = "0.12.0" # Bump minor (forcing change)
[compat]
CTBase = "0.17" # ← Removed 0.16Effect:
Consider:
Alternative: Keep both [compat]
CTBase = "0.16, 0.17" # Still allow bothThis is more user-friendly but means you maintain compatibility. 5. How Breakage CI Works Through Migration5.1 During Beta PhaseYour workflow tests each package in two modes: Mode 1: Stable Version- name: test stable
run: |
julia --project -e 'using Pkg; Pkg.add(name="${{ matrix.pkg.name }}")'What happens: # Package from registry with its declared compat
# Example: OptimalControl v0.15.9 with CTBase = "0.16, 0.17.0-"
# Resolver chooses: CTBase v0.16.0 (stable preferred)
# Uses: CTBase from your PR branch (via override mechanism)Mode 2: Main Branch- name: test main
run: |
julia --project -e 'using Pkg; Pkg.add(url="...", rev="main")'What happens: # Package from GitHub main branch
# Example: OptimalControl@main with CTBase = "0.16, 0.17.0-"
# Resolver chooses: Depends on what main declares
# Uses: CTBase from your PR branch5.2 Key InsightThe breakage workflow overrides CTBase to use your development version while respecting compat constraints of dependent packages. This means:
Why Phase 1 is critical:
6. Timeline ExampleReal-world timeline for CTBase v0.16 → v0.17 migration: Week 1: Preparation
Week 2: Beta Development
Week 3-4: Package Migration
Week 5: Beta Testing
Week 6: Stable Release
Total time: ~6 weeks for careful migration Faster possible: 2-3 weeks if no issues found 7. Best Practices and Tips7.1 Version NumberingFor v0.x packages (pre-1.0):
For v1.x+ packages:
7.2 Pre-release SuffixesProgression: For your case: Can skip alpha/rc and just use beta if:
7.3 CommunicationAnnounce on:
Template announcement: # CTBase v0.17.0-beta.1 Released
We're preparing CTBase v0.17.0 with the following breaking changes:
- [List breaking changes]
## Testing
To try the beta:
```julia
using Pkg
Pkg.add(name="CTModels", version="0.11.0") # This pulls in betaTimeline
FeedbackPlease report issues at: [link] To go back to stable: Pkg.add(name="CTModels", version="0.10.2") # Downgrades to stableQ2: Can different packages use different CTBase versions?A: No. Julia's Pkg can only have one version of a package in an environment. The resolver finds a single version that satisfies all constraints. If packages have conflicting requirements (one needs v0.16, another needs v0.17), installation fails. This is why the widening phase is critical. Q3: What if breakage tests fail after widening compat?A: This means code changes are needed in the dependent package. The widened compat says "we're compatible" but the code doesn't actually work yet. You need to:
Q4: How do I test locally with the beta?Option 1: Use registered beta using Pkg
Pkg.add(name="CTBase", version="0.17.0-beta.1")Option 2: Use local development Pkg.develop(path="../CTBase")Option 3: Use git branch Pkg.add(url="https://github.com/.../CTBase.jl", rev="feature/v0.17")Q5: What happens to v0.16 after v0.17 is released?Registry state: All versions remain in registry. Users can pin to v0.16 if needed: Pkg.add(name="CTBase", version="0.16")You can (but don't have to) continue bug-fix releases: 9. Summary ChecklistPhase 1: Preparation (Week 1)
Phase 2: Beta (Week 2)
Phase 3: Migrate CTModels (Week 3)
Phase 4: Migrate Others (Week 3-4)
Phase 5: Stable Release (Week 5-6)
10. ConclusionThe pre-release strategy with preventive compatibility widening provides a safe and systematic way to migrate breaking changes in Julia packages. Key advantages:
Requirements:
Your existing breakage CI workflow at Good luck with your migration! 🚀 |
Beta Was this translation helpful? Give feedback.
-
Practical Workflow for Small Team (3-4 devs, Multiple Repos)1. Recommended Approach: Simplified Polyrepo with Local DevelopmentFor a small team (3-4 developers) with a few packages (5-10), I recommend: Core Strategy:
Why this approach?
2. Repository Structure2.1 Keep Current StructureNo changes needed! Polyrepo is fine for small teams. 2.2 Local Development StructureEach developer has a local folder: All repos cloned side-by-side in one parent folder. 3. Daily Development Workflow3.1 Initial Setup (Once per Developer)Step 1: Clone all repositories cd ~/projects
mkdir control-toolbox
cd control-toolbox
git clone https://github.com/control-toolbox/CTBase.jl.git CTBase
git clone https://github.com/control-toolbox/CTModels.jl.git CTModels
git clone https://github.com/control-toolbox/CTParser.jl.git CTParser
git clone https://github.com/control-toolbox/CTDirect.jl.git CTDirect
git clone https://github.com/control-toolbox/CTFlows.jl.git CTFlows
git clone https://github.com/control-toolbox/OptimalControl.jl.git OptimalControlStep 2: Create a development environment cd OptimalControl
julia --project=@.using Pkg
# Develop all local packages
Pkg.develop(path="../CTBase")
Pkg.develop(path="../CTModels")
Pkg.develop(path="../CTParser")
Pkg.develop(path="../CTDirect")
Pkg.develop(path="../CTFlows")
# This ignores version constraints completely!
# Always uses your local codeStep 3: Create a startup script (optional but recommended)
using Pkg
packages = [
"../CTBase",
"../CTModels",
"../CTParser",
"../CTDirect",
"../CTFlows"
]
println("Setting up development environment...")
for pkg in packages
println(" Developing: $pkg")
Pkg.develop(path=pkg)
end
println("✓ Development environment ready!")
println("All packages use local versions.")Usage: julia> include("dev-setup.jl")3.2 Working on ChangesScenario: You want to change CTBase cd ~/projects/control-toolbox/CTBase
git checkout -b feature/new-api
# Make your changesTest in isolation: cd CTBase
julia --project
using Pkg
Pkg.test() # Run CTBase testsTest with dependents: cd ../OptimalControl
julia --project
using Pkg
Pkg.test() # This uses your local CTBase automatically!No need to:
Everything is instant and local! 3.3 Checking What's Developedusing Pkg
Pkg.status()Output: The 3.4 Switching Back to Registry Versionsusing Pkg
Pkg.free("CTBase") # Use registry version insteadOr free everything: for pkg in ["CTBase", "CTModels", "CTParser", "CTDirect", "CTFlows"]
Pkg.free(pkg)
end4. Version Management (Simplified)4.1 Development Phase: No Version BumpsWhile actively developing:
Example workflow: # Day 1: Feature in CTBase
cd CTBase
git checkout -b feature/improve-api
# ... make changes ...
git commit -m "Improve API"
# Day 2: Adapt CTModels
cd ../CTModels
git checkout -b feature/use-new-ctbase-api
# ... adapt to new CTBase ...
git commit -m "Use new CTBase API"
# Day 3: Update OptimalControl
cd ../OptimalControl
# ... test everything together ...
# Everything works? Great!No version changes, no registry, no waiting! 4.2 Release Phase: When You're ReadyOnly register when:
Process:
Order matters: 4.3 Very Relaxed Compat StrategyFor a small team, use wide compat ranges: # CTModels/Project.toml
[compat]
CTBase = "0.16" # Accepts all 0.16.x
# Or even wider for development packages:
CTBase = "0.16, 0.17, 0.18" # Very permissivePhilosophy:
5. Handling Breaking Changes (Simplified)5.1 Small Team AdvantageYou don't need the complex pre-release workflow! Why?
5.2 Simple Breaking Change ProcessStep 1: Announce in team chat Step 2: Make changes across all packages # Work in all repos simultaneously
cd CTBase
# ... breaking changes ...
cd ../CTModels
# ... adapt ...
cd ../CTParser
# ... adapt ...
# etc.Step 3: Test everything locally cd OptimalControl
julia --project
using Pkg
Pkg.test() # Tests with all your local changesStep 4: When all works, merge everything # Create PRs for all packages
cd CTBase && git push
cd ../CTModels && git push
cd ../CTParser && git push
# ... etcStep 5: Merge in order (dependencies first) Step 6: Register new versions Timeline: Can be done in a few hours to a day (not weeks!) 5.3 If You Need More SafetyOptional: Use feature branches # Everyone works on a shared feature branch
cd CTBase
git checkout -b epic/v0.17-migration
git push -u origin epic/v0.17-migration
cd ../CTModels
git checkout -b epic/v0.17-migration
# ... etc for all reposThen:
6. Minimal CI Setup6.1 What You Actually NeedFor a small team, keep CI simple: Per repository: # .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: '1.10'
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1That's it! Just test the package in isolation. 6.2 Skip Complex Breakage TestingWhy?
When to add it:
For now: Don't build what you don't need! 7. Team Coordination7.1 Communication ToolsMinimal setup:
Key practices:
7.2 Branch StrategySimple GitHub Flow: Rules:
No need for:
7.3 DocumentationKeep a simple CHANGELOG.md in each repo: # Changelog
## Unreleased
- Improved API for X
- Fixed bug in Y
## [0.17.0] - 2025-01-20
### Breaking
- Changed function signature for `solve()`
- Removed deprecated `old_function()`
### Added
- New solver algorithm
## [0.16.0] - 2025-01-10
...Update as you go, not before release. 8. Scaling Strategy8.1 When You're Still 3-4 PeopleCurrent approach works great:
8.2 When You Grow to 5-10 PeopleAdd:
8.3 When You Grow to 10+ PeopleConsider:
But that's future you's problem! 9. Common Scenarios9.1 "I Need to Test a Feature Across Packages"# In OptimalControl (or any test package)
using Pkg
# Develop all packages you're changing
Pkg.develop(path="../CTBase")
Pkg.develop(path="../CTModels")
# Run tests
Pkg.test()
# Or test manually
using OptimalControl
# ... your test code ...9.2 "Someone Else Changed CTBase, I Need to Update"cd ~/projects/control-toolbox/CTBase
git pull origin main
# Your local OptimalControl automatically uses the new code!
cd ../OptimalControl
julia --project -e 'using Pkg; Pkg.test()'9.3 "I Want to Use Registry Version for One Package"using Pkg
# Free specific package
Pkg.free("CTBase")
# Now CTBase comes from registry
# Others still use local versions9.4 "New Team Member Onboarding"Give them:
Total time: 30 minutes including downloads. 9.5 "I Broke Everything Locally"using Pkg
# Nuclear option: reset everything
Pkg.free("CTBase")
Pkg.free("CTModels")
# ... etc
# Or delete Manifest.toml and rebuild
rm("Manifest.toml")
Pkg.instantiate()10. What NOT to Do (Don't Overcomplicate!)❌ Don't Set Up a Monorepo
❌ Don't Build Complex CI
❌ Don't Obsess Over Versions
❌ Don't Use Pre-releases (Yet)
❌ Don't Write Extensive Docs (Yet)
11. The "Lazy Developer" Workflow (Recommended!)This is what I'd actually do with 3-4 people: Daily Work
Weekly or Bi-weekly
For Breaking Changes
Total overhead: Maybe 1-2 hours per week on coordination. 12. Concrete Example: Real Breaking ChangeScenario: Need to change CTBase API Monday Morning (2 hours)Monday Afternoon (1 hour)Total time: 3 hours, breaking change deployed. Compare to complex workflow: multiple weeks! 😱 13. Setup ChecklistOne-Time Setup (30 minutes)
Per-Developer Setup (30 minutes)
Per-Release (1-2 hours)
14. Why This Works for Small Teams✅ Advantages
|
Beta Was this translation helpful? Give feedback.
-
|
nice @ocots !!! so either pre-release, or dev locally on the full hierarchy, right? @amontoison any thought on this? |
Beta Was this translation helpful? Give feedback.
-
|
See new repo https://github.com/control-toolbox/DevelopmentGuide. |
Beta Was this translation helpful? Give feedback.
-
|
@ocots although it does not look so standard in Julia, seems good practice here to use the local reg you revived 👍🏽 https://github.com/control-toolbox/DevelopmentGuide |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
Formalization of the Dependency Update Problem with Integration Testing
1. Problem Description
1.1 Context
In an ecosystem of interdependent packages, we develop several packages A, B, C, ... simultaneously. These packages have dependency relationships and declare version compatibility constraints.
Central Problem: When introducing a breaking change in a base package C, we want to:
1.2 The Integration Testing Paradox
When we modify C and want to test B (which depends on C), compatibility constraints can prevent building the test environment:
2. Mathematical Modeling
2.1 Simple Dependency Graph (Diamond)
Consider the minimal case with three packages forming a "diamond":
Relations:
2.2 Formal Definitions
Universe:
Graph Node:
Compatibility Function:
For each node (p, v), we define:
where 𝒫(V) is the power set (all subsets) of versions.
Interpretation: Compat(p, v)(p') gives the set of versions of p' compatible with (p, v).
Example (strict compatibility):
Example (compatibility with range):
2.3 Configuration and Resolution
Configuration:
A configuration K is a set of nodes:
with the version uniqueness constraint:
Valid Configuration:
A configuration K is valid for a target node (p_target, v_target) if:
(p_target, v_target) ∈ K
Dependency Closure:
SAT Problem:
Given (p_target, v_target), does there exist a valid configuration?
2.4 Resolution Algorithm with Preference
When multiple versions satisfy constraints for a package p':
Maximal Resolution Strategy:
where max is defined according to a total order on versions (typically chronological order).
3. System States and Evolution
3.1 Initial State (Baseline)
Available Nodes:
Compatibilities:
Valid Configuration for A v1:
3.2 State After Creating C v2
New Nodes:
Compatibilities unchanged for A v1 and B v1
Valid Configuration for A v1: K₀ (identical, as A v1 doesn't know about C v2)
3.3 State with B' (development version)
We create B v1' that uses C v2:
Nodes:
New Compatibility:
Unsatisfiability Problem:
Testing (A, v1) with (B, v1'):
3.4 Solution: Preventive Widening
Modify A before the problem:
Test 1: (A, v1') alone
Test 2: (A, v1') with (B, v1')
4. Abstract Migration Workflow
Phase 1: Preparation (widening compatibilities)
Before creating C v2, widen compatibilities:
For all packages p depending on C:
Guaranteed Property:
Phase 2: Introduction of C v2
Create (C, v2) possibly with a pre-release version:
Advantages of pre-releases:
Phase 3: Adaptation of Direct Dependents
For each package B directly depending on C:
Create (B, v_new) with:
Integration tests on A pass because:
allows resolution with C v2
Phase 4: Adaptation of Indirect Dependents
For each package A depending on B:
If necessary (breaking changes in B), adapt A.
Otherwise, simply update compatibility ranges to accept new versions of B.
Phase 5: Stabilization
Publish C v2 (stable version, without pre-release)
Optionally, tighten compatibilities:
to force user migration
5. Guaranteed Properties
5.1 Non-Regression
Property: If a configuration K_i is valid in state N_i, it remains resolvable in N_{i+1} after adding new versions.
Proof: Adding new nodes doesn't invalidate old configurations because old compatibilities remain unchanged.
5.2 Progressive Testability
Property: With preventive widening of compatibilities, we can test packages under development without blocking existing tests.
Reason: Version ranges {v1, v2} allow the resolver to choose the appropriate version depending on context.
5.3 User Isolation
Property: End users are not affected during transition if we use pre-releases.
Mechanism: Resolvers ignore pre-releases by default, so users stay on stable versions until final publication.
6. Complexity and Limitations
6.1 SAT Problem Complexity
The dependency resolution problem is NP-complete in the general case.
However, with heuristics (maximal version choice) and reasonable constraints, it becomes practical.
6.2 Combinatorial Explosion
With n packages each having k compatible versions:
Practical Solution:
6.3 Transitive Conflicts
In a complex graph with many diamonds, conflicts can emerge even with preventive widening.
Example:
If B requires C v2 and D requires C v1, A cannot use them simultaneously even with a widened range.
Solution: Coordinate migration (migrate B and D together).
7. Conclusion
7.1 Key Principles
7.2 Generalization
This workflow applies to any package ecosystem with:
Examples: Julia (Pkg.jl), Rust (Cargo), JavaScript (npm/pnpm), Python (pip/poetry), etc.
Beta Was this translation helpful? Give feedback.
All reactions