In some software engineering circles, there remains a debate (or needs to be a debate) about using In-Source versus Out-of-Source Builds. For C++ developers (where I have the most experience), build management isn’t just a minor detail; it’s a critical factor that influences the maintainability, scalability, team dynamics, and overall success of your software.
TLDR; Jump straight to the Authoritative Advice
While this discussion is particularly relevant to C++, the principles I’ll cover apply equally well to other languages like C, Rust, and even some higher-level languages like Java. In any language where projects can grow in size and complexity, build management plays a crucial role.
I didn’t even realize a “debate” existed until I started consulting with various software teams. I began seeing teams led by more junior developers who, despite their talent, didn’t fully appreciate the difference between In-Source and Out-of-Source builds. This lack of understanding led to problems that could have been easily avoided with better practices.
I’ve seen firsthand how projects that start as small, demo-scale endeavors can evolve into large, complex systems. As these projects grow, the build process often remains an afterthought, leading to a tangled mess that’s difficult to manage. Teams are frequently resistant to change, preferring to stick with the familiar even when it’s no longer suitable for the scale of their work.
Searching for Authoritative Guidance
While working with a team in 2018, I advised them to switch from in-source to out-of-source builds. The team resisted (strongly) and asked for me to cite authoritative guidance on the topic. After failing at simple online searches, I reached out to Stack Exchange.
I found that there was consensus but no source of authoritative guidance. People just “knew” not to do in-source builds. From one of the comments:
Since first asking this question, it has been viewed over ten thousand (10k) times. Even with that, no authoritative guidance has been cited. Let’s fix that.
In this post, I want to provide authoritative advocacy for Out-of-Source builds. Whether you’re just starting or managing a large codebase, understanding the difference between these two approaches can save you countless hours and headaches down the line. Let’s dive into why this choice matters and how it can make your development process smoother and more efficient.
Definitions and Overview
In-Source Builds
In-Source builds occur when the build process takes place directly within the source code directory. This means that all the build artifacts—such as object files, binaries, and other generated files—are stored alongside the source files. The directory where you write your code also becomes the location where these build artifacts reside, effectively mixing the source code with the byproducts of the build process.
Systems and Tools That Default to In-Source Builds
Many build systems, especially older or simpler ones like make
, traditionally default to In-Source builds. This approach is often the default in scenarios where minimal configuration is required, or the build system is set up with simplicity in mind, such as in small, single-developer projects or educational environments.
Out-of-Source Builds
Out-of-Source builds, on the other hand, separate the build artifacts from the source code. When using this approach, the build process occurs in a different directory—often called the “build directory”—which is entirely separate from the source directory. This ensures that the source tree remains clean, with no build-related files cluttering the codebase.
Systems and Tools That Default to Out-of-Source Builds
Modern build systems like CMake and more advanced tools, such as Bazel or Meson, often default to or encourage Out-of-Source builds. These systems are designed with scalability and maintainability in mind, making them well-suited for projects that require multiple build configurations, cross-platform compatibility, or continuous integration setups.
This distinction between is more than just a matter of preference; it’s a fundamental decision that can have significant implications for your project’s maintainability and scalability.
Pros and Cons of Each Approach
In-Source Builds
Advantages
- Simplicity and Ease of Setup for Small Projects:
In-Source builds are straightforward to set up, especially for small projects or quick prototypes. Developers can often configure their build environment with minimal effort since all build artifacts are generated within the same directory structure as the source code. This simplicity makes it appealing for beginners or for cases where a project’s scope is limited and unlikely to grow significantly.
Disadvantages
- Risk of Polluting the Source Tree:
The primary downside of In-Source builds is that they intermingle build artifacts with your source code, leading to a cluttered workspace. Object files, binaries, and other generated files reside alongside your source files, which can make navigating the project structure cumbersome. This pollution can also lead to accidental commits of build artifacts into version control systems, adding unnecessary noise and complicating code reviews. - Difficulties with Version Control:
Managing a version-controlled codebase becomes more challenging when build artifacts are scattered throughout the source tree. Developers must be vigilant about which files to commit, which to ignore, and how to handle conflicts involving build-related files. This added complexity increases the likelihood of errors, such as committing temporary or generated files, which can lead to broken builds and wasted time tracking down issues. - Challenges with Large or Complex Projects:
As projects grow in size and complexity, the disadvantages of In-Source builds become more pronounced. The clutter in the source tree can lead to confusion, especially when multiple developers are working on the same project. Additionally, managing multiple build configurations (e.g., debug vs. release, different platforms) becomes increasingly difficult when all artifacts are stored in the same directory structure. This can result in build conflicts, slow build times, and challenges in maintaining a clean and efficient development environment.
Out-of-Source Builds
Advantages
- Clean Separation of Source and Build Artifacts:
Out-of-Source builds maintain a clear distinction between your source code and the files generated during the build process. All build artifacts are stored in a separate directory, leaving your source tree clean and free of clutter. This separation not only makes it easier to navigate and manage your project but also prevents accidental commits of build artifacts, streamlining version control. - Easier to Manage Large Projects:
Out-of-Source builds offer significant advantages in organization and efficiency for large and complex projects. By keeping build artifacts separate, it becomes easier to manage multiple build configurations, handle platform-specific builds, and support continuous integration pipelines. Developers can switch between different build configurations without worrying about conflicts, making the development process more agile and less error-prone. - Simplifies Cleanup and Version Control:
Because build artifacts are isolated from the source code, cleaning up your project becomes as simple as deleting the build directory. This simplicity reduces the likelihood of accidentally deleting important files or leaving behind outdated artifacts that could cause future build failures. Additionally, version control is simplified, as you no longer need to worry about ignoring or managing build-related files in your repository.
Disadvantages
- Slightly More Complex Setup:
The primary drawback of Out-of-Source builds is that they require a bit more setup than In-Source builds. Developers need to configure their build system to output files to a separate directory, which may involve learning new build tools or adjusting existing build scripts. While this additional setup can be a minor inconvenience, the long-term benefits far outweigh this initial investment. - Potential for Configuration Issues if Not Done Properly:
If the build system is not properly configured, developers may encounter issues where files are not generated in the expected locations, or dependencies are not correctly tracked. This can lead to build failures or confusion about where to find the generated files. However, these issues are typically easy to resolve with a bit of upfront planning and proper configuration.
While In-Source builds may be appealing for their simplicity in small projects, the long-term benefits of Out-Source builds—such as improved project organization, easier management of large projects, and simplified version control—make them the preferred choice for most developers.
Impact on the Product
This decision doesn’t just impact how you configure your build system, but can have significant impact on the Product itself.
Impact on Maintainability
The choice between In-Source and Out-of-Source builds can have a profound impact on the maintainability of your codebase. In-Source builds, by their nature, intermingle source code and build artifacts, leading to a cluttered and disorganized project structure. This can make it difficult for developers to navigate the codebase, especially as the project grows in size and complexity. As a result, simple tasks like finding the right file or understanding the project’s layout can become unnecessarily time-consuming.
On the other hand, Out-of-Source builds keep the source directory clean and free from generated files. This separation ensures that the source tree remains organized and easy to maintain, regardless of how large the project becomes. Developers can quickly locate the files they need without sifting through irrelevant build artifacts, making the codebase easier to understand and work with. In the long run, this leads to fewer mistakes, quicker onboarding for new team members, and a more maintainable project overall.
Impact on Scalability
As projects scale, the drawbacks of In-Source builds become more apparent. In-Source builds are often manageable in small projects, but as the number of files, configurations, and team members increases, the lack of separation between source code and build artifacts can lead to significant scalability issues. For instance, managing multiple build configurations—such as debug, release, and platform-specific builds—can become cumbersome and error-prone when all artifacts are stored in the same directory structure. This can result in build conflicts, longer build times, and a general slowdown in the development process.
Out-of-Source builds are designed to scale with the project. Out-of-Source builds make it easier to manage multiple configurations and platforms by isolating build artifacts in a separate directory. Developers can work on different build configurations simultaneously without risking conflicts or corruption in the source tree. This scalability is particularly beneficial in large projects with distributed teams, where maintaining a clean and efficient build process is crucial to productivity and project success.
Impact on Team Dynamics
The choice of build strategy can significantly influence team dynamics, particularly in collaborative environments where version control systems like Git are integral to the workflow. In-Source builds often lead to a situation known as “build pollution,” where build artifacts—such as object files, binaries, and other generated files—end up scattered throughout the source directory. When these artifacts are accidentally committed to version control, they can cause substantial issues during code reviews and merges.
In a Git-based workflow, for instance, merging branches that include build artifacts can become a tedious and error-prone process. Developers may find themselves sifting through many irrelevant changes—such as modified binary files or build outputs—just to locate the meaningful code changes. This increases the time and effort required to review and merge changes and raises the likelihood of introducing errors or missing critical code updates. Additionally, these polluted merges can create conflicts that are difficult to resolve, further slowing down the development process.
Managing such issues can strain team dynamics, leading to frustration and decreased productivity. Developers may become hesitant to make changes, fearing that they will inadvertently cause merge conflicts or break the build. Over time, this can erode trust in the development process, with team members spending more time managing build-related problems than writing and improving code.
Out-of-Source builds offer a solution to these problems by keeping build artifacts completely separate from the source code. This separation ensures that the source tree remains clean and that only relevant files are tracked in version control. As a result, merges become more straightforward, with fewer conflicts and less noise in the diff. Code reviews focus more on actual code changes, making them quicker and more effective.
Out-of-Source builds help create a more positive and collaborative team environment by reducing the friction associated with version control and builds. Developers can work more confidently, knowing their changes won’t accidentally pollute the repository or cause unnecessary conflicts. This leads to smoother collaboration, faster development cycles, and a more cohesive team dynamic overall.
Out-of-Source builds, streamlines the workflow, reduces merge conflicts, and fosters a more efficient and harmonious team environment.
Authoritative Advice
With over 25 years of experience in software engineering, I’ve seen firsthand the challenges and pitfalls that come with improper build management. From small R&D teams working on demo-scale projects to large teams with hundreds of developers building mission-critical software, one principle has consistently proven true: Out-of-Source builds are the key to maintainable, scalable, and efficient software development.
This principle isn’t just a preference; it’s a best practice that I’ve applied across various projects, from early Makefile-based systems to modern CMake and Visual Studio solutions. The benefits of Out-of-Source builds—clean separation of code and artifacts, easier version control, and improved team dynamics—far outweigh the minor setup overhead.
Avoid In-Source builds. They may seem convenient at first, but they lead to cluttered codebases, complicated merges, and unnecessary stress on your team. Instead, invest the time to configure Out-of-Source builds from the start. Your future self—and your team—will thank you.
Discover more from John Farrier
Subscribe to get the latest posts sent to your email.
John, could you help clarify if having a directory structure like the one below is an in source or out of source build?
Based on your article I’m having trouble figuring out if this is still an in source build or out of source build, or a sort of middle ground build since it’s technically not combined with the source code and has its own directory. In the example below we’re assuming the programmer creates the build directory and builds all the necessary obj/other files within it using cmake .. and then make.
my_project/
├── build/
├── include/
├── src/
│ ├── main.cpp
├── CMakeLists.txt
Good question. Your directory structure is an “Out of Source” build because you have a “build” directory (which is a good standard/default for CMake). If it were an in-source build, the artifacts of the build would be scattered under ‘src’, for example. Though your build directory is under your project folder, it is not interleaved with your source. You can delete a single directory, in its entirety, and not impact the project at all.
Thank you for the response and clarification! I guess the only caveat is having to do some version control updates so that the build directory and its contents don’t make their way into the git repository.
By the way, love the blog posts! I am an avid reader of your blog and take away a lot of useful information from them. Just subscribed.
Yes! Add
build/
to your.gitignore
file!Given your question, I plan to update this blog post to make this more clear. Thank you for reading!