Monorepo with C++: Code Organization in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why a Monorepo for C++?



In modern C++ development, especially in medium and large-sized projects, code organization becomes a critically important task. A monorepo is an approach where the code for all components, libraries, and services is stored in a single repository. Unlike multi-repositories, a monorepo offers a single entry point, simplifies refactoring, and streamlines dependency management. In practice, this means you can change a library's API and immediately see how it affects all dependent modules.



In this article, we will break down the practical aspects of organizing C++ code in a monorepo: from folder structure to setting up CI/CD. You will learn how to avoid common mistakes and make the build process fast and predictable.



1. Monorepo Structure for C++



A proper folder structure is the foundation of a successful monorepo. Let's look at a recommended hierarchy for C++ projects.



Basic Directory Hierarchy



my_project/├── CMakeLists.txt          # Root build file├── cmake/                  # Custom CMake modules│   └── FindMyLib.cmake├── libs/                   # Internal libraries│   ├── core/│   │   ├── CMakeLists.txt│   │   ├── include/│   │   │   └── core/│   │   │       ├── utils.h│   │   │       └── types.h│   │   └── src/│   │       ├── utils.cpp│   │       └── types.cpp│   └── network/│       ├── CMakeLists.txt│       ├── include/│       │   └── network/│       │       └── http_client.h│       └── src/│           └── http_client.cpp├── apps/                   # Executable applications│   ├── server/│   │   ├── CMakeLists.txt│   │   └── src/│   │       └── main.cpp│   └── cli/│       ├── CMakeLists.txt│       └── src/│           └── main.cpp├── tests/                  # Integration tests│   ├── CMakeLists.txt│   └── test_integration.cpp├── third_party/            # External dependencies (vendoring)│   ├── fmt/│   └── spdlog/├── .github/                # CI/CD configuration│   └── workflows/│       └── build.yml└── README.md


Key Principles:

  • libs/ — all internal libraries. Each library has its own folder with a CMakeLists.txt.
  • apps/ — executable files. They depend on libraries from libs/.
  • third_party/ — vendored dependencies (git submodules or copied sources).
  • tests/ — integration tests that verify the interaction of multiple libraries.


Example CMakeLists.txt for a Library



# libs/core/CMakeLists.txtadd_library(core    src/utils.cpp    src/types.cpp)

target_include_directories(core PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)

target_link_libraries(core PUBLIC fmt::fmt spdlog::spdlog)


Important: Use PUBLIC and PRIVATE for proper dependency propagation. This is a key point — if library A uses library B only in its source files, make B PRIVATE.



2. Dependency Management in a Monorepo



One of the main challenges of monorepos is "dependency hell." In C++, this is solved through CMake target-oriented design.



Rule: One Target — One Library



Each library should export exactly one CMake target. This simplifies linking and prevents circular dependencies.



# Root CMakeLists.txtcmake_minimum_required(VERSION 3.20)project(MyProject VERSION 1.0.0 LANGUAGES CXX)

# Include all librariesadd_subdirectory(libs/core)add_subdirectory(libs/network)

Blogs

Book Recommendations