Modeling a project's infrastructure as a graph
September 24, 2026
Part of CNP: Cloud-Native Platform
CNP is an internal developer platform on Kubernetes, built by a team of six in the SIGL major at EPITA. Teams create projects under IAM-scoped access and declare the non-pod parts of their stack, managed databases and similar, without writing Terraform themselves or waiting on a platform team to do it by hand. I owned the Crossplane layer end to end, and this post is about the one change to it I'd point to first: how a project describes its infrastructure.
Claims, the thing a team actually touches
A claim is the abstraction a team interacts with: a database, a queue, whatever the project needs. Crossplane is what turns that claim into real infrastructure on EKS behind the scenes. I built the claim model, the provisioning path, and both sides that talk to it, the API and the front end.
Most of the work here was keeping the boundary between what a team asks for and what actually gets provisioned simple enough that teams wouldn't need to understand the underlying cloud resources to use it correctly. That boundary is also where the next problem showed up.
What a flat list can't say
The platform originally modeled a project's infrastructure as a flat list of claims. That works until a project grows past a handful of resources. At that point the list can't express which resources depend on which, and it has no way to tell a resource nobody uses anymore from one that's load-bearing. Both look like one more line in the list, and both cost money every month.
Nobody deletes a database they aren't sure about, so without that information unused resources just accumulate.
Composing a project as a graph
I proposed and built the alternative: let teams compose a project as a dependency graph. Resources declare what they depend on, and the graph itself becomes the source of truth for the project's shape.
That unlocks something a list can't do at all. Traverse the graph from the project's entry points and flag any resource that isn't reachable from one. An unreachable resource is either a mistake or dead weight, and either way it's worth surfacing instead of quietly costing money in the background.
The front end for this shipped. The backend integration that makes traversal authoritative, so the platform acts on it rather than only showing it, is still in progress.
One repo per resource
Beyond my own slice I worked across the rest of the system, including how changes actually land on the cluster. The model the team settled on generates one repo per resource, and ArgoCD reconciles each of those onto the cluster under an app-of-apps pattern: one parent application whose job is to manage the set of child applications rather than any workload directly.
That keeps a change to one resource from requiring a deploy of everything else, and gives every resource its own independently reviewable, independently revertible history instead of one shared monorepo where every team's changes are tangled together.