Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Gentle Monorepo - ship faster, collaborate ...

Avatar for Gerben Dekker Gerben Dekker
September 29, 2025

The Gentle Monorepo - ship faster, collaborate better

Monorepos promise faster development and better collaboration—but they often seem intimidating, requiring major tooling, buy-in, and process changes. This talk shows how we at Dexter introduced a Python monorepo gradually, without forcing a company-wide migration or introducing complex tooling. By combining a few lightweight tools—uv, tach, polylith, and simple ADRs—with a pragmatic, trust-based approach to adoption, we made development faster and cross-team collaboration smoother. You’ll walk away with a practical blueprint for experimenting with a monorepo in your context, without requiring heavy up-front work.

Avatar for Gerben Dekker

Gerben Dekker

September 29, 2025
Tweet

Other Decks in Programming

Transcript

  1. Working on shared code can be hard! 2 Service A

    󰰁Team A Repository A Service B 󰰁Team B Repository B Summary of pain points • Outdated CI and dependencies • Waiting on CI and context switching • Coordinating external dependencies • Managing internal package versions • Coordinating code changes across teams Dependency issue Bug Broken CI Dependencies outdated - release Dependency issue! “fix laterˮ, stay on old version ⚠ ☕ Shared package X Repository X Code change and release ☕ Fix bug, release ☕ Successful minor change! Friction
  2. Development friction has business consequences 3 Neglected shared code Less

    collaboration Lower speed of development Longer time to market Pain points • Outdated CI and dependencies • Waiting on CI and context switching • Coordinating external dependencies • Managing internal package versions • Coordinating code changes across teams Friction
  3. 4 MONOREPO Photo by Daniel Huniewicz on Unsplash Logos from

    https://logos-world.net Adyen Pydata Amsterdam 2024 talk on monorepo: https://www.youtube.com/watch?v=GnXT3uafor8
  4. One repository (“the monorepoˮ) Service A 󰰁Team A Service B

    󰰁Team B Shared package X Service A 󰰁Team A Repository A Service B 󰰁Team B Repository B Shared package X Repository X What is a monorepo? 5
  5. How a monorepo alleviates the pain points 6 Less friction!

    Pain points • Outdated CI and dependencies • Waiting on CI and context switching • Coordinating external dependencies • Managing internal package versions • Coordinating code changes across teams Friction Solutions One CI One dependency set Everybody on latest version (on main branch) Meetings become merge requests • Outdated CI and dependencies • Waiting on CI and context switching • Coordinating external dependencies • Managing internal package versions • Coordinating code changes across teams Friction
  6. But what about… How to start? Build system? Dedicated engineers?

    Consequences? Prevent big ball of mud? Get everybody on board? 7
  7. What is Dexter? 10 Passionate about the future of renewable

    energy We are a highly skilled team thatʼs using the latest technologies to build software services that transform the way we trade power. Forecasting Trading Turbine steering Battery optimization
  8. Building blocks of Dexter Data sources Observability Time series forecasters

    Optimization engines Forecasting Trading Turbine steering Battery optimization 11 Feature engineering Turbine shutdown Intraday trading Turbine shutdown Intraday trading Logging Feature engineering …
  9. Two tools are all you need https://davidvujic.github.io/python-polylith-docs/ David Vujicʼ python

    tools for polylith) https://polylith.gitbook.io/polylith/ Joakim Tengstrand et alʼs great gitbook on Polylith) https://docs.astral.sh/uv/ (uv homepage) 12 uv - An extremely fast Python package and project manager, written in Rust.
  10. Polylith - concepts and structure Bases Components Projects Development project

    Entrypoints Features, business logic Define deployable artifacts Playground - access to all bricks and external dependencies 13 Concept Description Analogy
  11. Polylith - concepts and structure Example: the flint monorepo flint

    ├ bases ├ components ├ development ├ projects ├ pyproject.toml ├ uv.lock └ workspace.toml 14 flint └ bases/flint ├ intraday_trading └ turbine_shutdown ├ components/flint └ feature_engineering ├ development ├ projects ├ intraday_project ├ Dockerfile └ pyproject.toml └ turbine_shutdown_project ├ Dockerfile └ pyproject.toml ├ pyproject.toml ├ uv.lock └ workspace.toml [project] name = "flint" #... authors, license, etc. [build-system] requires = ["hatchling", "hatch-polylith-bricks"] build-backend = "hatchling.build" #... a few lines more of config, see example repo dependencies = [ "bayesian-optimization>=2.0.3", #...all external dependencies ] [tool.polylith.bricks] "bases/flint/intraday_trading" = "flint/intraday_trading" "bases/flint/turbine_shutdown" = "flint/turbine_shutdown" "components/flint/feature_engineering" = "flint/feature_engineering" root level pyproject.toml Polylith/uv example repository by David Vujic at https://github.com/DavidVujic/python-polylith-example-uv/
  12. Polylith - components flint ├ bases/flint ├ components/flint └ feature_engineering

    ├ __init__.py └ data_sources.py ├ development ├ projects ├ pyproject.toml ├ uv.lock └ workspace.toml 15 class DataSource: ... data_sources.py from flint.feature_engineering.data_sources import DataSource ... __init__.py (serves as interface) from flint.feature_engineering import DataSource ... any_file_in_another_base_or_component.py
  13. Dependencies: shared and filtered using uv and poly root pyproject.toml

    >=0.2.0 >=1.5.0 project_b/ pyproject.toml project_a/ pyproject.toml =0.2.1 =1.6.2 uv.lock =0.2.1 CI =1.6.2 CI uv add uv poly sync poly sync uv pip install $WHEEL -c constraints.txt 16 uv export > constraints.txt uv build --wheel $WHEEL
  14. Concrete actions to start a monorepo 17 Polylith/uv example repository

    by David Vujic at https://github.com/DavidVujic/python-polylith-example-uv/ To-do list ❏ Start with two repos/two teams ❏ Harmonize existing repos ❏ Clone the polylith/uv example repo ❏ Build a CI pipeline ❏ Move into two fat bases and two projects ❏ Confirm everything works ❏ Start moving out specific shared code to one component <your_repo> ├ bases/<your_repo> ├ service_a └ service_b ├ components/<your_repo> └ component_a ├ development ├ projects ├ service_a_project └ service_b_project ├ pyproject.toml ├ uv.lock └ workspace.toml
  15. Learnings from the early phase Work together Compromise and cooperate

    on focused scope 19 Next best step “Donʼt anticipate the future in any wayˮ Best simple system for now BSSN 🦬): https://dannorth.net/blog/best-simple-system-for-now/ Document Keep track of your decisions in your repo using ADRs
  16. Architectural decision records ADRs 20 Markdown Architectural Decision Records: https://github.com/adr/madr

    Context and Problem Statement Decision Drivers Considered Options Decision Outcome Chosen option: “ˮ, because Consequences • Good, because • Bad, because Pros and Cons of the Options Option 1 • Good, because • Neutral, because • Bad, because Option 2 … More information
  17. Growing from two to multiple teams 22 Community Build a

    community for developers and decision makers Convince Share the story, make it accessible and low regret Set rules Enforce rules but donʼt make it too strict
  18. One way of enforcing rules - tach 23 Tach (unmaintained):

    https://github.com/gauge-sh/tach ❯ tach check ❯ tach check ✅ All modules validated! … [[modules]] path = "flint.logging" depends_on = [] [[modules]] path = "flint.market_data_platform" depends_on = [ "flint.constants", "flint.data_pipeline_base", "flint.time_series_standards", ] tach.toml … [[modules]] path = "flint.logging" depends_on = [] [[modules]] path = "flint.market_data_platform" depends_on = [ "flint.constants", # "flint.data_pipeline_base", "flint.time_series_standards", ] tach is no longer maintained - find an alternative! Internal Dependencies ❌ components/flint/market_data_platform/datasource.py:4: Cannot use 'flint.data_pipeline_base.DataSource'. Module 'flint.market_data_platform' cannot depend on 'flint.data_pipeline_base'.
  19. Status: the solved “what aboutˮ questions 25 How to start?

    Build system? Dedicated engineers? Consequences? Get everybody on board? Prevent big ball of mud? uv and polylith is all you need! No build system needed No dedicated engineers needed Build a BSSN, document choices in ADRs Step by step Use tooling like tach
  20. Status: flint success stories 26 Increased code reuse Multiple components

    used by almost all products Shorter time to market Saved months of development time of a new product Shared code is well maintained Active collaborative redesign, extension and improvement Eliminate tech debt A legacy product eliminated tech debt adopting monorepo components Design discussions across teams Meetings replaced by slack discussions and MRs
  21. Status: open pain points 27 How to keep the overview?

    Ownership and governance? Reusability?