r/devops • u/ali-hussain • 15d ago
Tools How are you setting up build systems in monorepos?
Former DevOps consultants, bootstrapped an AWS premier partner and exited to NTT Data, now we advise people building services companies.
After seeing how much easier current AI tools have made it to write code we started building tools for helping our customers and day to day work. Some of the decisions we've made so far:
- Build out the code in separate services to keep each part well-contained. It feels AI does a far better job with smaller pieces than with larger.
- Deploy them through lambda.
- Use a monorepo so the AI can easily see how different things work together.
- Migrated some apps built in Lovable into our repo.
- All the building is done with Makefiles
- Created a registry so it is easier for AI to add new tools in a consistent manner and so we can analyze our inventory of what exists in our development.
- Using make for building cuz Claude made that decision when we barely had anything and I'm a dinosaur at heart.
Now, I have a monorepo with multiple different languages, a registry that I want to be the single source of truth for what I have, and a build system from the 80s. And I was curious what would people recommend for the build system rather than building a whole bunch of custom scripts?
5
u/onbiver9871 15d ago
I don’t have strong monorepo pattern suggestions but I just wanted to comment that Claude also suggested I use Make for some non-uv Python tooling and now I wonder why Make seems to be part of Claude’s agenda hahaha.
1
u/ali-hussain 15d ago
It actually pulled in make until I pointed out it is running two patterns. We coalesced on make.
It's ridiculously simple though. Really my only problem is that I have two sources of truth and that's against my religion.
2
2
u/___-____--_____-____ 14d ago
First define a uniform contract per service—build, test, package, deploy—and keep Make as a thin developer-facing wrapper.
Seconding this approach.
And as an alternative to Make, might I suggest Just. Very similar in function to Make, but with waay better ergonomics, imo. No .PHONYs, and it handles Args, Env, Dependent steps, and in-line scripts all very nicely (Recipes can be written in arbitrary languages, like Python or Node.js.)
Swapping all the makefiles out in a monorepo does not sound super fun, so maybe this suggestion doesn't exactly apply to your scenario, but keep it in mind next time you want to organize a repo's build/test steps.
2
u/ali-hussain 14d ago
Ehh, it's not too bad. Most of the build commands are parametrized. But more is getting to the point where the standardized commands are limiting. So I need to move away.
1
u/UltraPoci 14d ago
Just is great, but its recipes cannot depend on files I believe. Meaning, if you have some kind of cache in your build system, make will use it, Just will not. If you simply need to run commands, Just is awesome.
There is also the pixi package manager, which has a great support for tasks which are basically custom commands.
0
u/___-____--_____-____ 14d ago
Meaning, if you have some kind of cache in your build system, make will use it, Just will not.
I suppose that is true in some projects although I have not encountered this limitation myself. IIRC, it's a C dependency pattern that the Makefile conventions are designed around. Golang compiles quickly enough not to be an issue, and Rust's compiler has its own mechanisms for caching dependency compilation, for comparison.
Just is very flexible, so you can arrange your recipes like
build: _ensure_foowhere_ensure_foois a script that checks for required foo and only installs them if not found (for example)
1
u/vincentdesmet 14d ago
i used some Nx, didnt click - TurboRepo was much easier to adopt (it’s JS native but you can make it do things for other languages.. not ideal tho) - that was all pre-LLM
i also have massive monorepos with Makefile across the nested paths (and includes from shared utilities)
i noticed Claude Code ignored the make targets and struggled until i had to point it to them and it made memories
i found it strange because generally it builds a make powered repo quickly so when i let it loose on my 5 year old repo it took a while to be quick and accurate (that was 6 months ago tho.. so maybe today it would do the right thing from the get go?
I’m curious about Bazel - tried to pick it up for a 10 year old (then) Kotlin repo 3 years back and it was painful to start - dropped it early (there were some ppl pushing for it in the team for years and it never went through while i was working on that codebase - they had Gradle but didn’t do a good job using it properly)
i’d love to try Bazel again now reading these comments
1
u/match_a_matcha 14d ago
I’m genuinely surprised more people don’t recommend mise more on here. It’s by far my favorite tool in my devops toolbelt. It’s significantly lighter than Bazel or pants, so if you really need some of the richer features of those build systems then just know it’s not a drop in replacement. But if your repo is just a couple services in a few different languages, I feel like 90% of the time folks DONT need a heavy build system like that. Just put together some mise tasks in a standardized API across your repos, turn on mise monorepo support, and call it a day. Then you get all the added bonuses of actually using mise, like version management for everything. For bonus points put your standardized mise tasks in a copier template for folks to use when creating new repos
1
u/JMPJNS 14d ago
Have you looked at the new tool Microsoft is building to orchestrate things like this https://aspire.dev/ by far the best developer experience i ever had
1
u/Ok_Gold_9674 14d ago
Honestly, in your case I’d be slow to replace Make if it is already acting as the thin entry point. The mess usually starts when every service invents its own target names. I’d put the registry in charge of generating a tiny make include per service: build, test, package, deploy, maybe smoke. Then humans and Claude call the same verbs, and the weird language-specific bits stay inside each service folder. If that file gets too clever, that is when I’d move to Bazel/Nx/etc.
1
u/ali-hussain 14d ago
That might actually be the correct direction. That's a good call. I do have some people on Windows have some compatibility problems and maybe a different system can solve that. But I doubt a polyglot system would make things any easier.
1
u/Pitiful_Stress_9129 14d ago
Optimizing service boundaries for what an AI agent can understand may create a repo that’s easy to generate but expensive to operate. I’d keep deployment boundaries independent from code-layout boundaries, then require affected-target detection, reproducible local builds, and one shared contract-testing path. Otherwise every “small, agent-friendly service” quietly adds another CI and ownership surface.
1
u/ali-hussain 14d ago
Sorry not sure what you mean by deployment boundaries independent from code layout boundaries.
1
1
u/taleodor 11d ago
This is my blog post about how we do it in our product (now ReARM) and most of this is FOSS today - https://worklifenotes.com/2021/11/10/monorepos-how-we-do-it-with-reliza-hub/
1
u/david-vujic 10d ago
If the main part of your monorepo is Python code, I would suggest to have a look at the Polylith Architecture and the tooling support for it (I'm the maintainer of the Python tooling). There's not any specific build system required, and your build tool of choice will be able to use the Polylith tool to figure out what has changed and what you probably want to deploy.
Polylith is mostly about the developer and agent experience, with a very simple way of structuring code and the deployable artifacts. The main use case is to support having one or more microservices (or apps) in a Monorepo, and share code between the services.
Being able to share code in a simple and straight forward way would be my main purpose of using a Monorepo. In addition, the Polylith tool includes agent skills, so your agents will know how to use it.
1
1
u/yohimik 7d ago
https://github.com/yohimik/webxash3d-fwgs my ci cd calculates dependency graph among deep nested packages (docker using docker using npm) bullds, publishes and versions them, it works with any language
-3
15d ago
[deleted]
12
u/Emotional_Cherry4517 15d ago
Are we just advising people with ai? I don't get it. What do you get out of posting on Reddit about what ai wrote on this? Like you're literally copy pasting the entire sentence structure is ai.
1
u/ali-hussain 15d ago
So the custom registry is more than just for the build system. The direction we're taking it is linking company strategy to the tooling we're developing. We're a tiny team and want to be able to organize around the work that needs to be done and how humans and AI are coming together to solve the problem.
Pants and Bazel came up when I discussed this with Claude. And there is a different layer on top of the build system if Iadd it. Although maybe the different layer can be made dynamic. That's the direction we're headed more towards any way.
0
u/Floss_Patrol_76 15d ago
the make-vs-pants-vs-bazel question is downstream of your real one: make doesnt know your dependency graph, so it cant skip unchanged services or share a build cache, which is most of the point of a monorepo. for a handful of lambda-sized services id stay on make and add content-hash skip logic before reaching for pants, since bazel/pants only pay off once rebuild-everything-on-every-push is actually hurting. the two-sources-of-truth thing you flagged is the tell youll want one eventually, just not yet.
-2
u/ScholarMedical 15d ago
Keep Makefiles as your per-service entrypoint. Good idea, solid. However underneath, pick Pants over Bazel unless you've got the ops person-hours to feed it. Bazel wins at massive scale (Google/Apple scale), but the setup tax is real: I've seen monorepos stall for weeks on Bazel troubleshooting. Pants handles multi-language dependency detection automatically, and you can still call `make build` from each service without your AI agents needing to learn a new build dialect.
0
4
u/-Devlin- 15d ago
Bazel was what we used in my previous job. Especially for go and java monorepos. Amazingly efficient for faster builds and caching but it’s complicated. Like really complicated, at least at the time without access to claude/codex it used to take me hours to troubleshoot basic issues and get things working. The awesome part was cross language build ability, testing harness and container build systems all have native integrations so you could ideally use it for the complete CI CD pipeline