When you book a flight, you trust the airline to get you where you need to be in one piece. Ticket details like destination city, departure time, seat, and fare class comprise a declared source of truth, and the airline mobilizes its vast logistical apparatus to assign a plane, pilot, and flight plan to carry you to your destination.
Software development benefits from a similar abstraction approach. Layers of delivery complexity have pushed teams to incorporate GitOps. The desired states of applications and infrastructure are maintained in version control (Git), and automation tools enforce changes to the target environments (Ops). Just like how I rely on the airline to engineer my transportation to Cabo, software developers can now focus on business logic while letting CD tools handle the deployment plumbing.
Implementing proper source control patterns has therefore become critical to a successful software delivery practice. Before GitOps, engineering efforts largely went toward creating tedious deployment runbooks. With imperative deployment steps automated, a core design problem now involves properly structuring the repositories hosting our declarative configurations.
Well-Architected Route Maps
Consider our air travel analogy. An airline typically organizes its routes in one of two ways. The most common choice is a hub and spoke model. The airline centralizes operations in a few cities (the hubs), and those cities serve the outlying destinations (the spokes).
In this model, passengers generally need to go through a hub city to reach their destination, reducing the number of direct flights. However, airlines are able to better respond to emergencies and stresses on the network by surging planes and staff only within a few hubs. The hub model is less efficient on a per route basis but is overall easier to administer.
The second model is point-to-point. A point to point model connects cities directly without using central hubs. Southwest and other low-cost carriers are famous for this approach, implementing a mesh of routes connecting individual cities.
Point-to-point permits more nonstop flights (no need to connect through a hub), while also allowing airlines to easily add or remove new routes. However, managing all those route dependencies has high operational overhead. And a single route failure (say, a thunderstorm in the midwest) can have a cascading effect on the rest of the network, as it is more difficult to surge or reallocate resources than in the hub and spoke model. Routes in the point to point model are therefore more efficient but can be administratively burdensome.
It turns out that these approaches can inspire how we organize our software projects.
The Monorepo Approach: Integrated and Atomic
A monorepo approach means all source files for all services live in a single repository. This is comparable to the hub model described earlier. For example, the structure of an e-commerce app for “Widgets Co.” might look like the following:
The whole team collaborates on this single repository, with some roles potentially focusing on different folders. For example, UX designers might spend the bulk of their time in frontend/, and the technical writers might camp out in docs/.
Teams that choose to go the monorepo route cite some the following benefits:
Atomic changes: The entire codebase shares a common commit history. Each new commit, whether a feature update or rollback, is applied to the entire application.
Ease of refactoring: Changes in dependencies or application logic that affect multiple components can propagate all from one place.
Centralized tools: CI/CD and infrastructure configurations are centralized alongside application code. It’s straightforward to ensure the entire codebase must pass build and test pipelines before deployment.
Reduced administrative overhead: Security policies, approval rules, and visibility tools all need to point to only one repository.
Monorepos can serve as a logical starting point for brand new initiatives. Developers can easily collaborate as they have a single point of access to the entire application. However, while monorepos handle scaling fairly well, they can encounter challenges as codebase complexity grows.
Repository size: Git operations can start to show strain as the size and history of the monorepo grows, though this can be somewhat remediated through interventions like Git LFS and shallow cloning.
Longer build times: Each build pipeline runs against the entire application, even if only a small part changed.
Tooling limitations: Existing security and collaboration tools need to handle a massive, complex codebase. Challenges in enforcing granular access control can lead to over-permissioning.
Organizations have successfully maintained monorepos of massive scale. Still, some teams decide that as the business grows the most proactive way to manage complexity is to break up the codebase.
The Polyrepo Approach: Focused and Decoupled
A polyrepo approach is more akin to the “point to point” airline model previously discussed. Instead of consolidating in a single repo, each application component (microservices, manifests, libraries) is version controlled separately. The example “Widgets Co” app might therefore be structured into multiple repos like this:
Each piece of the application lives in its own Git repository. Each repo then has its own version history, pipeline configurations, and access control. Developers gain granularity and tailored focus. Other benefits include:
Isolation: Each service has its own commit history. Changes to a single feature do not require a rebuild or rollback of the entire app. A smaller codebase means newly onboarded developers have a more manageable learning curve.
Decoupled CI/CD: If each repo has its own pipelines, new builds and deploys are faster and more targeted.
Granular access control: Permissions can be managed at the repo level and even outsourced to a managed RBAC tool, rather than having to manage ownership of file paths in a single large repository.
The polyrepo approach encourages teams to specialize and experiment, and alleviate fears of breaking the larger whole. Yet the model does pose some challenges.
Dependency management: Teams must manage tracking and securing inter-repo dependencies, and ensure updates and versions are correct across all projects.
Duplication: Strict modularity is difficult, and duplicated tooling or CI/CD configurations across repos can lead to configuration drift.
Coordination overhead: Teams must invest in communicating and coordinating changes that span multiple repositories.
Companies large and small have found success leveraging the polyrepo approach, particularly for running federated service models. Investing collaboration and dependency management is key to ensure consistency across the many codebases.
Which model works best? Leveraging Harness to manage GitOps complexity
At this point you're probably wondering which model is the “best practice”. In short, it depends. Some organizations advocate for and use monorepos with a host of custom tools and integrations, while others prefer the granularity and flexibility of polyrepos.
As a general starting point, a team devoting the bulk of its engineering effort toward a singular core application could benefit from keeping work centralized in a monorepo while they scale. Meanwhile, teams developing a federated service model, or heavily leveraging microservices, could find a polyrepo setup to be a more natural fit.
Whichever model you choose, a robust toolset is needed to manage security and access control, application builds, and deployment pipelines. Harness provides declarative GitOps that connects to your existing source code management system, and leverages ArgoCD to continuously reconcile your repo’s declarative state with your live environment. The UI then offers visibility to a monorepo model, while also offering centralized organization if you choose to go the polyrepo route.
This intro just scratches the surface of GitOps source patterns. Like modern airlines preparing intercontinental route maps, today’s software landscape requires intelligent logistical planning and strategic design decisions in order to quickly and reliably deliver features to users. The next posts in this series will dive deep into advanced GitOps patterns and practices.
In the meantime, we encourage you to get started with GitOps with a free Harness account, and then try out some of the guided tutorials to help you take to the skies on your continuous delivery journey.