Monorepos are contrary to how many of us have been taught to use source control.
To start a project or app, the first thing we do is create a Git repository for it.
This leads to many focused and small repositories.
A quick check on my GitHub account shows that I have 179 non-fork repositories.
That's a lot, but I think many of us work that way.
It's not like this with monorepos.
With monorepos, you create one or a couple of repositories for your entire company.
This might have hundreds or thousands of employees working on multiple projects within a single repository.
Famously, Google, Meta, Microsoft, and Airbnb, amongst others,
all employ very large monorepos with varying strategies for coordination.
On this episode, we have David Vilyek here to give us his perspective on monorepos,
as well as highlight an architectural pattern and set of tools for accomplishing this in Python.
This is Talk Python To Me, episode 399, recorded January 13th, 2023.
Welcome to Talk Python To Me, a weekly podcast on Python.
This is your host, Michael Kennedy.
Follow me on Mastodon, where I'm @mkennedy, and follow the podcast using @talkpython, both on fosstodon.org.
Be careful with impersonating accounts on other instances.
There are many.
Keep up with the show and listen to over seven years of past episodes at talkpython.fm.
We've started streaming most of our episodes live on YouTube.
Subscribe to our YouTube channel over at talkpython.fm/youtube to get notified about upcoming shows and be part of that episode.
This episode is brought to you by Microsoft for Startups Founders Hub.
Get early stage support for your startup without the requirement to be VC-backed or verified at talkpython.fm/foundershub.
It's also brought to you by Brilliant.org.
Stay on top of technology and raise your value to employers or just learn something fun in STEM at Brilliant.org.
Visit talkpython.fm/brilliant to get 20% off an annual premium subscription.
David, welcome to Talk Python To Me.
Thank you.
I'm really excited to be on the podcast.
I'm really excited to have you on the podcast.
And we get to talk about a couple of interesting ideas.
We get to talk about software architecture.
People may know I'm a big fan of architecture.
I think putting your software together bright makes all the difference.
We're going to talk about some ideas that are new to me, this polyleth idea that you're an advocate and fan of and how it applies to Python.
We're also going to focus a good portion of this conversation on monorepos.
And what the heck are monorepos, right?
Yeah.
That'll be a lot of fun.
And I'm really looking forward to it.
But let's hear your story first.
How did you get into programming and Python?
Yeah.
How did I get into programming?
Well, so I guess it was when I was a kid.
My dad bought me Commodore 64.
It was like way back in the 1980s.
So that's when I started learning basic programming language and how to write things that are code and not pure text.
But then I went on a different path.
I was working mostly with design and things like that.
And the first thing I knew was a job in our business was a web designer.
So that was what I wanted to be at first.
So I started to learn JavaScript and copy and pasted some snippets of code and stuff like that.
Yeah.
And that's the era.
I'm just guessing from the starting computer where you were sort of in time when this must have happened.
And that was probably before all these crazy JavaScript front-end frameworks and all that.
And it was more of how do you visually design this page with graphic and more art and more focused on that, right?
Yeah.
Before like jQuery and before like all these React and all of that good stuff that we have today.
Sure.
Yeah.
And with Python, I think I started about 2015 with Python.
I started with Python 2.7 and then learning all I think in that period, I also learned started to think in more like functional programming style, more like functional-ish.
Yeah.
And that's coming from Python actually.
And I was very much into Node.js at that time too.
So I think around 2015, and I've been like jumping back and forth between different languages.
I'm a really huge fan of Clojure 2, which is like 100% functional.
So I've been like visiting some different kinds of programming languages and styles and things like that.
And now I'm back to full-time Python on my day job and almost full-time Python during nights because I really love to code on my spare time too.
When I assume there is any chance to code.
So I'll take that one.
Yeah, absolutely.
I mean, how much of open source is there's programmers and we have this project we've got to work on, but we really want to build this other thing and we're super passionate about it.
We just end up building it and sharing it and it takes off, you know?
And I think that's a very common story for sure.
So before we move on, how has working in Python influenced something like Clojure or Clojure influenced your Python thinking?
They're fairly different languages, right?
Yeah.
Functional being less stateful, which is a really big different way of programming.
Yeah.
Well, I think what I learned from Python was like the elegance and the importance of writing elegant and simplistic code.
I was really impressed by the Zen of Python.
You know, if you type import this in a shell, you get this nice list of how to write your code.
I really liked that idea.
So I guess that's where I started to think about keeping it simple, clean and short.
Yeah.
And with Clojure, it was like that's kind of a different, total different syntax, but also digging into a lot of functional aspects and how to think about state, how to separate the calculations from actions and data and things like that.
And I think I've brought a lot of those ideas back to when I'm back at Python, how to separate different kinds of code that you write.
Yeah, I can see that.
I feel like Python is really interesting because you can choose to only focus on little parts of it.
That's good for beginners because they only have to learn a little part, but it's also good for people who have particular styles that they like to work.
If you want to write functional Python, you don't have to create any global variables or any classes or any, you can just, you can write it that way, but you could completely go really deep OO patterns and can do that in Python too if you want.
Right.
It's completely up to you.
Yeah.
And I like that kind of freedom.
It's, you can't, you don't, you're not the force to do either this or that you, you can learn an experiment.
And especially when you, if you use libraries, they are like designed in different ways too.
So you can, you don't have to limit yourself to only use that kind of library or this kind of library.
So I really like Python, the capabilities of Python when it comes to that, but it's not very strict in any kind of format.
I like that too.
And I think other languages are seeing that and adopting that as well.
You know, you see Swift with their playgrounds and well, Swift in general and .NET with their, you know, maybe we don't need namespaces and classes and static main void for everything to get started.
And they're adopting those types of things.
You know, we're talking about this like, well, functional people might want to write this way and more OO oriented people that way, but that also could just be you in different situations.
You know, right now, this is the right tool to solve it.
And other times here's a different way to solve a different problem, but you can just stay in the same tools and the same editors and the same ecosystem.
It's cool.
Yeah, definitely.
Yeah.
All right.
Well, let's start in on the first half of our, our main topic here is the mono repo.
Now, yeah, it's really easy to confuse what a mono repo is with a mono lith versus same microservices.
Yeah.
Those are not really at all the same thing.
In fact, they might actually be opposites in a sense, a mono repo and a mono lith to some degree.
So maybe kick us off by telling us what is a mono repo here?
A mono repo is, I don't think it's that complicated, but I actually also, before I started to dig into this thing more,
I also had almost put like an equal sign between mono lift and mono repo because that's the way I have used the writing code.
I was in the dot net and C# world a lot.
And you like building your website and you have a data later and you have a domain layer and everything was in a repo.
So I guess Microsoft was like a reaction to that to separate code into isolated environments.
And you can have this nice and clean little code base and you have that does one thing and you have this other code base that does a different thing.
So if instead of just having the user authentication bit completely just woven into the code, we can make a little API that we call over JSON that does the authentication.
And then here's the one has the catalog.
Yeah.
And we could write just a little bit of code and, you know, the, I guess the benefit, right, is that whoever's working on the catalog bit, they theoretically can just stay focused on that little bit of code and not the entire system.
Right.
Yeah.
With a microservice story.
Yeah.
A monorepo is, I think, from the way I see it, it's like, it's a Git or any version control repo that has basically all of your code in the same repo, same repository.
And that doesn't necessarily mean that it's one program or one app that you are going to build or compile into.
You can have several projects or artifacts in that repo.
And I guess that's why it's called monorepo because you can have multiple things in it.
So I guess that's the difference between a monolith where you have, where you actually build one app and deploy it to one place.
Right, right.
The monolith is the opposite of the microservice style.
Yeah.
Whereas the monorepo is just a way of organizing your code and sharing how do you propagate changes, look at dependencies across either libraries or, there are companies that take this really far.
Crazy, crazy far.
Like Google and Facebook, I believe, haven't worked on it, but I hear that they have one repo.
Yeah.
Or like all of it.
What, one?
Really?
Just like, what's the checkout story look like on that?
It's got to be a lot of code.
Yeah.
It's got to be a lot of code.
I believe Google, I'm probably going to misassociate this, but I think Google uses Bazel and there's different tools that allow them that are kind of not just Git, but something that can handle that scale of code.
So it really, when I think about organizing my code, it's either me or me and a couple of people working on the code and it's pretty contained.
But when you start to think about hundreds or thousands of people across projects, it starts to get really wild, right?
Yeah.
That has to be a completely different story.
We're really fun to see how they do, how they work in the teams.
Yeah, absolutely.
So maybe we could talk about some of the, you know, why, if you're not doing this, you know, why would you do that?
Like it seems, you know, you highlighted that there's kind of these two trends that you saw out there in some of your articles.
And we'll talk about the articles and link to them.
You talked about seeing a trend of more people trending towards this monorepo and more people or other groups of people trending towards having more small repos.
Yeah.
For you take my little microservice example, the user access service might be its own repository separate from the catalog service.
Whereas others might say, we're going to put all that together and all the utilities and that other data reporting project.
And all of that goes into one giant repo, even though there's a big team on it, right?
Yeah.
Well, I think from my experience, what I've seen joining different teams and different companies that I've seen exactly that some quite recently, I joined a team at a company with several teams.
And they actually migrated from a monorepo to several repositories.
And it was part of their microservice journey, as they call it, because they had one repo with all their code.
But that code base was so difficult to work with.
So they kind of wanted to extract one app at a time into a separate repository just to be able to deploy that one and work with it in a reasonable way with tooling support and things like that.
So before that, I was at a different company joining a different team.
And we went the total opposite way.
We had a couple of microservices that were quite easy to work with.
But we identified issues or problems with it because maybe there's one service that has outdated dependencies.
So the biggest problem was the actual code duplication because we had one service that we had developed one thing for.
And we had another service that we needed code that was very much like the thing we had in that other service.
So I guess the solution could be to extract that one into a library, but then you have three repositories.
And I guess there's difficult to find that good balance between one or the other.
I agree.
And that extracting, I mean, that's certainly one of the possibilities.
As you say, well, we're going to, you know what, we now have a third repository and we have the data access repository and package.
And, you know, that's probably not the type of thing you publish to PyPI, but it's very likely something you would publish to some kind of internal dependency artifact.
System that you would depend upon, right?
But the problem is, if it's used in just these two places, it's, and it sounds like that sort of description, the kind of the team is probably working on both sides of those microservices and they understand the broader system.
But as it grows and more people depend upon it, it's harder to understand this little standalone project.
Who is using it and what ways are they completely?
Can we make a change here?
If we refactor this, who do we talk to about changing even just the signature of a function?
How do we reach out to the other parts of code or other stakeholders and say, look, we need to change this function, but we got to, you know, we're changing the data model and you're going to have to figure out how to go along.
On the other hand, if all of those projects were together in a giant mono repo, we have tooling that understands, well, what functions call this function or what thing imports this class or who's using it?
Is it used at all?
Actually, maybe you could delete it.
You thought someone was using it and no one's using it, right?
There's a lot of understanding of the broader integration if it's all there with you, right?
Definitely.
This portion of Talk Python to Me is brought to you by Microsoft for Startups Founders Hub.
Starting a business is hard.
By some estimates, over 90% of startups will go out of business in just their first year.
With that in mind, Microsoft for Startups set out to understand what startups need to be successful and to create a digital platform to help them overcome those challenges.
Microsoft for Startups Founders Hub was born.
Founders Hub provides all founders at any stage with free resources to solve their startup challenges.
The platform provides technology benefits, access to expert guidance and skilled resources, mentorship and networking connections, and much more.
Unlike others in the industry, Microsoft for Startups Founders Hub doesn't require startups to be investor-backed or third-party validated to participate.
Founders Hub is truly open to all.
So what do you get if you join them?
You speed up your development with free access to GitHub and Microsoft Cloud computing resources and the ability to unlock more credits over time.
To help your startup innovate, Founders Hub is partnering with innovative companies like OpenAI, a global leader in AI research and development, to provide exclusive benefits and discounts.
Through Microsoft for Startups Founders Hub, becoming a founder is no longer about who you know.
You'll have access to their mentorship network, giving you a pool of hundreds of mentors across a range of disciplines and areas like idea validation, fundraising, management and coaching, sales and marketing, as well as specific technical stress points.
You'll be able to book a one-on-one meeting with the mentors, many of whom are former founders themselves.
Make your idea a reality today with the critical support you'll get from Founders Hub.
To join the program, just visit talkpython.fm/foundershub, all one word, no links in your show notes.
Thank you to Microsoft for supporting the show.
Yeah, I totally agree.
And our editors are so smart and they can find usages and if you have this function signature isn't really correct and stuff like that.
And that's so much easier when you have your source code in a folder that is like right next to the one using it.
So that's a huge benefit, like editor-wise to the developer experience, I guess.
Yeah, absolutely.
And I think one of, this is both a benefit and a challenge.
You know, I'll maybe link to the Monorepo Wikipedia page and it says, here are some of the advantages.
One of the, the number one advantage list is ease of code reuse.
So it's possible, not necessarily suggested, but possible that you say, well, the data access functions and classes that we need on this side, we need some of them over here.
But if you have the whole Monorepo, you could just say, well, import them in both projects and deploy, you know, a larger piece of code to your server.
But who cares?
The servers have a lot of storage and they'll be fine, right?
The challenge, I think, is going to be the, you're going to end up with a tightly coupled architecture pretty badly if you just say, well, I see way over there, there's that file and that's the one I want.
And we're just going to grab that.
And, you know, it doesn't necessarily encourage good behavior, but it does make reusing code and understand how it's being used easy, right?
Yeah.
And also you have, you're like in the risk zone of actually building a Monoly again.
Yeah, exactly.
You're just part of the, part of the API points run there and part of the API points run there, but they're, they're effectively just one giant thing, right?
So I guess what I think is that if you are using a Mono, if you want to have your code in a Mono repo, I guess you would need some sort of tooling or ideas about how to separate your code into separate artifacts that don't have the entire code base in its package.
Only the code that is actually needed for this artifact.
So, again, I guess that's, that's part of the challenge, having a Mono repo.
I would say so.
I've been thinking about this a little bit leading up to our conversation today.
And certainly you can use packages in the, you know, we have this problem in Python or this challenge where packages mean different things, but it has the same word.
So a package could be just a grouping of modules into a directory that has a Dunder init, or it could be something on PyPI that you ship and you deploy and you version on its own.
And I mean, in just the on disk, the Dunder init sort of local grouping, right?
So you could create these, these sort of groups within your Mono repo and say, we're going to import that, but have a little bit of a formal separation and say, look, we're not necessarily going to deploy it through some versioning story and let other people pull it in.
Because then we lose track of who's using it and how they're using it.
Are they on the right version?
But we'll still maybe think of them as a Python package, in a sense.
Do you have any experience with doing it one way or the other?
Any preference?
Yeah.
What I was thinking of from the company that I joined that were migrating from their Mono repo, they had done a couple of attempts to do this code sharing thing.
with the likes, I think it was like Git sub modules or, or similar things.
But all of that ended up into in, it was became too complicated to, to understand what was going on.
And, and I think even the editor support where it wasn't really, really perfect when you had like these kind of dynamic linking.
So I guess that's, that's why they, they chose to, to abandon that idea.
Yeah.
It sounds a little bit like with the sub modules that it was not a pure Mono repo, but kind of a, let's have different sections on, on our repository, but bring it together.
Yeah.
like, we're going to sort of put these files and it's, this is a sub module, that's a sub module and they're kind of separate.
But then once they're all checked out and linked up, then our tool thinks of it as one giant thing.
Like the Mono repo would be right.
Yeah.
So it's kind of a, an intermediate, I also thought about this as well.
Like maybe, maybe you could put together the get tools like that.
I do want to highlight a couple of get tools because maybe I'll, I'll take a quick bit of audience feedback real quick.
But I do think that, you know, when it's five people, 10 people, you just check the thing out and it's going to be fine.
But as it gets larger and larger, both over time and lines of code and number of people, it's, it's going to be a thing where it almost becomes unmanageable to just do a, a get clone URL and see what happens.
Right.
You can go grab, grab a coffee.
And when it got back, it's not, it hasn't downloaded.
We've probably seen that XKCD where there's people like fake sword fighting on a chair.
It's like, get back to work.
Like, you know, we're doing a get clone, leave us alone.
Okay.
Sure.
Fine.
Gotcha.
It's going to be a while.
Quick bit of audience feedback says model repos are okay.
If you have a dedicated team that manages the advanced tooling required to deal with them.
Yeah, absolutely.
And I sort of related Lucas asks, they would use Bazel for your projects or rather have make files or similar in case of lints and builds.
So yeah, there's the different tools that like Facebook and Google and those folks use.
There's also pants.
Benji Weinberger has talked a lot about it.
I've had him on the show.
I've had him on the show before.
And pants is one of these tools that can kind of help pants build.
But David, how about you?
What were you all using in terms of more advanced tooling or was there anything special?
Back then it was not really more advanced than actually make files to make things happen.
But the other place, the team that I joined actually started to use this, I guess we're going to talk about it in the architecture called polyleth.
And there's also tooling for that.
Yeah.
And there's also tooling for that kind of offers a solution to many of these headaches with having a monorepo.
Yeah, absolutely.
And back then it was because polyleth originates from closure.
So we were actually writing closure code.
And then for Python, I started to look around for a solution.
I actually read a little bit about pants.
So I think that can solve a lot of problems too.
It seems like a really great tool with a lot of useful functionality.
And then there's also poetry.
I don't think that it's not really about monorepos, but you can, I believe that you can use pure poetry and have your dependencies,
like the third party libraries, your own or the one at PyPy in sort of an, not the third party, but your own in an editable mode.
So they will, as soon as you change something, it will be updated.
So I guess there are some tools that can help you along the way, but I guess there's still a lot of frustration with having that smooth and really joyful monorepo experience that you would like to have.
So that's what led me to, to, to start working on this project.
I do think that Python, the way that its dependencies and its understanding of linking, you know, files together through directories and things like that makes it a little bit more challenging than other systems.
Like if I was doing C++, I could open up Visual Studio Code and create a broader project and say, these three libraries are what I want to see as my project.
And it doesn't really matter where they come from.
You build it and they link together and there's sort of a build the Delta only type of thing.
Whereas in Python, you kind of need to bring on a little bit more tooling to say, I know it looks like there's some giant Python thing here, but just these two pieces.
That's what I want to think of as the thing, you know, what we're going to talk about with some of the stuff that you've done with poetry with polyleth and others certainly make that relevant.
I do want to talk about the Git tools, but it's also interesting.
This comment from David Poole, it says we use sub modules for legal licensing reasons.
That is to have GPL code separate from our proprietary code rather than just dropping it in, which obviously has different implications.
Oh, that was very interesting to learn about.
Yeah.
I hadn't really thought about that either, but yes, you definitely want to think about it.
So let's just talk Git for a moment.
Now, one of the big challenges is if we're going to put this all into one giant GitHub repository, which I hinted at, it could get really large.
Especially if you put binary files, like some of your build tooling or other assets, you might put it in there.
And then that makes it extra tricky.
The less something can diff, the more it kind of piles up quick.
As I was thinking about this thing, I learned about a couple cool ideas.
Let's talk about this one first.
Partial clone.
This is something that was totally new to me.
So normally it's Git clone the URL to the Git repository.
However, you can say things like filter, --filter equals blob.
Have you seen this before, David?
No, this is totally new to me.
So, but it looks really interesting.
Yeah, so what happens here is if you, the blob is like a binary file, right?
And what you're saying when you say filter blob is it'll check out all of the Git history.
And normally when you do a clone, you get, at least for the branch you're on, you get every
version of the file.
So you get clone, you disconnect from the network and you've got everything, right?
Which is the beauty of Git.
But if you've got a really huge repository, it also might be the drawback of Git.
So you can filter out these blobs in the historical sense.
And if you say this, what you see in your hard drive for the working directory is identical.
But the .git folder with the history only has the working version, not all copies of the
history of the blob.
This has like a really huge effect.
So I did this on Talk Python training, my courses website.
And if I just say git clone, the repo, it pulled down 118,000 objects.
It resolved 71,000 deltas and it updated 10,000 files and it was a gig on disk.
If I just say filter, --filter equals blob colon none, it goes from 118,000 to 10,000
downloads.
It goes, it's less than half the size.
And the resulting files on disks, those were the same, but the intermediate deltas were
like 170th or 150th.
Really a big difference.
And this is, you know, it's a pretty old repo.
It's got a lot of stuff.
It's nothing compared to what a lot of people have.
So one, there's one problem where like, okay, if I'm going to try to git clone a mono repo,
there's just no way.
Right?
So adding this aspect here, I think actually would be really valuable.
Yeah, definitely.
because it's, I guess in the normal, the use case is that you want to work with the latest
version of the source code.
You want to develop something new.
So I guess that's what you want on disk.
It makes a lot of time.
And what happens is if you say, well, actually we need to switch branches or we need to go
back three months in time.
It just goes back to the network and clones a little bit more.
It's like an incremental clone as it needs it.
So I think actually this, this would help a lot of people who don't know about it, working
with mono repos that turn out to have a lot of files and a lot of historical, especially
binaries that grow over time.
Yeah.
Because those are the ones that are huge, you know, it's not the text files usually that
are the problem.
So I have to bookmark this.
Yeah, yeah, yeah.
This portion of Talk Python to Me is brought to you by Brilliant.org.
You are a curious person who loves to learn about technology.
I know because you're listening to my show.
That's why you would also be interested in this episode's sponsor, Brilliant.org.
Brilliant.org is entertaining, engaging, and effective.
If you're like me and feel that binging yet another sitcom series is kind of missing out
on life, then how about spending 30 minutes a day getting better at programming or deepening
your knowledge and foundations of topics you've always wanted to learn better, like chemistry
or biology over on Brilliant.
Brilliant has thousands of lessons from foundational and advanced math to data science, algorithms,
neural networks, and more with new lessons added monthly.
When you sign up for a free trial, they ask a couple of questions about what you're interested
in as well as your background knowledge.
Then you're presented with a cool learning path to get you started right where you should
be.
Personally, I'm going back to some science foundations.
I love chemistry and physics, but haven't touched them for 20 years.
So I'm looking forward to playing with PV equals NRT, you know, the ideal gas law, and all the
other foundations of our world.
With Brilliant, you'll get hands-on on a whole universe of concepts in math, science, computer
science, and solve fun problems while growing your critical thinking skills.
Of course, you could just visit brilliant.org directly.
Its URL is right there in the name, isn't it?
But please use our link because you'll get something extra.
20% off an annual premium subscription.
So sign up today at talkpython.fm/brilliant and start a seven-day free trial.
That's talkpython.fm/brilliant.
The link is in your podcast player show notes.
Thank you to brilliant.org for supporting the show.
Related to that, so quirky ads, wouldn't a shallow clone be more predictable?
So this is also interesting.
So shallow clones is a older way to do this in GitHub.
The problem is with shallow clones, you don't get the full history and changelog.
With these partial clones, you have all of the history, commit history and details.
You just don't have the files and they're incrementally pulled in.
So you could do a shallow clone.
And then there's another one, what was it called?
A sparse clone.
So a sparse clone is another tool that you can bring in here for advanced Git usage, where
you say, I know I've got this huge directory structure, but I just want to get these three
directories or this subdirectory structure.
And you clone only part of the files, right?
So we were talking about how Python understands just like the whole thing as one giant project.
And maybe even you check it out and try to open it.
Your editor will just say they're indexing, indexing, and autocomplete won't really work
and very well and go crazy.
So you can just say, I want these three directories and I want them partially cloned.
So they only have like the recent history and they're not so insane.
And you can kind of combine these to get really focused views into a monorepo, which I thought
was pretty interesting.
Yeah.
So anyway, when I think back to the story you told about how you guys were using submodules,
I kind of feel like these partial clones plus sparse clones might be a better fit than trying
to, you know, symlink things together.
Because it really just is the same thing.
If you want to clone the whole thing, you do.
But then you can kind of just, as you clone it, filter out.
And you can also, with those sparse clones, you can retroactively add in.
You go, oh, I also need that directory.
You can say like, get sparse ad.
Oh, cool.
This now needs to be this piece to come in as well.
And there's some interesting ways to put these together.
So I think these tools are going to be, for people who are working with monorepos, I think
those advanced Git features that I called out might be really helpful.
What do you think, David?
Yeah, I totally agree.
Especially when you have a monorepo that is a lot of code.
So it seems like this is, you wouldn't live with, want to live without it, I guess.
Because it's probably not helpful.
Yeah, I think so too.
So sparse checkout, I believe, is actually the first.
Sparse checkout.
Okay.
I think sparse checkout is the term.
I'll link to it as well.
It's partial clone and sparse checkout.
There we go.
Nice.
Okay.
There's so many good features in Git that I guess most of us don't use.
Yeah.
I think so too.
Like I've been doing Git for a really long time and this sparse checkout is completely
new to me.
I only learned about it because I was trying to research a little bit more of some like,
well, how do you do actually manage with these monorepos as we are preparing for our chat
today?
So yeah, I think there's a lot of tools and flexibility that are not obvious or not apparent
that people can use to make monorepos work really, really well.
There's still a lot of interesting ways to structure your code and put it together and
use it once you get it checked out.
So maybe let's, what do you want to start?
You want to start with a fresh take on monorepos?
This is one of your articles.
Yeah.
Why not?
Yeah.
So tell us the story here.
I wrote this article about a year ago, almost a year ago.
Before that, I was trying to figure out how to work and have this nice developer experience
in a monorepos.
And coming from closure and having learned new things and have some new fresh ideas on how
you can solve things, I wanted to give it a try in Python too.
And also at the same time, I was actually doing work with microservices, but in several
repos.
And I kind of found myself, there was not a huge thing, but it was like a logger, sort
of a logger module or a package.
I knew that I had done it in the other microservice just a couple of weeks ago.
So, okay, what should I do?
Should I create a library?
No, this is way too small to create a library.
And it wasn't even open source.
It's like a proprietary system.
So when you would need a lot of private repo servers, things like that.
So I just ended up in copying some code.
And, you know, while people would go, of course, you should never do that.
But sometimes it's just not complicated enough or important enough or big enough to justify
all the change management and dependencies.
And you know what?
That file, it goes into this project.
And usually it's fine until they get out of sync or there's some weird, you want to upgrade
one and then, oh, where else is it, right?
Or you discover a bug in that part and you forget about that you have copied it a couple of times
and now with the other repo.
So then you have a lot of work to do.
There's probably a whole section of cybersecurity history and like breaches where they thought they fixed a problem in some system.
Yeah.
And it turns out someone else found a copy of it that wasn't fixed and broken.
And yes, this is not ideal.
I really wanted to give the polyleth in Python a try because I really enjoyed the way things are structured
and a lot of these like headaches are solved there.
Polyleth is really, really new to me.
Maybe tell people about polyleth before we go on because I suspect a lot of people don't know about this.
Yeah, maybe we should begin.
Yeah, let's begin there.
Then we'll come back to it.
Well, polyleth, it's an architecture, but it's also a tool or something with a tooling support.
And it's open source and it's developed by a fellow Swede, Joachim Tengstrand.
And I was fortunate to actually work in the same team as him.
So he was done introducing this.
We decided to give it a try.
He was new in our team.
And I have to confess, I was a little bit skeptical at the beginning because skeptical of monorepos in general too, because based on previous bad experiences.
So the polyleth is the main idea is that you have, when you write code, you're supposed to, you aim to write them in small parts.
And that's what polyleth called components.
And a component, polyleth uses the idea of Lego, but for code.
So a component could be a piece, a Lego brick that you can reuse in several ways.
And a component can be everything from a small tech, something that you normally would put in a utils folder, like functions that do maybe some parsing or something.
But it can also be a combination of other components.
They don't have to be of the same size.
It's the idea of composability and reusability that is the important thing.
So the big parts of polyleth are our components.
And then we have something called bases.
And that is also a component, but a kind of special kind of component.
If you think about Lego, if you're going to build like a house, you often have a base plate where you put your Lego bricks on it.
So a base is sort of that part.
And in code, that could be like, if you have like a FastAPI app, maybe a base plate could be where you define the endpoints.
Like you use the API decorator side or something like that.
So that could be a base.
And then the code that actually does something could be a combination of different components.
Yeah, that's an interesting way to think of it.
Whenever you talk about stuff, what are the things that's difficult as to understand?
What is the scale or how are these different?
So one way, well, our functions components, our modules components, our packages components, like, how do I identify that since it's not a formal language runtime term?
Yeah. That's really, really good. Help me understand how I make these things in Python. Yeah. Yeah. That's really interesting. Because you can see a component. It's not a full blown feature, like maybe a library that you publish in on PyPy would be. It's smaller than that. And I guess it could be a single function. But it's probably one or more functions that kind of relates to it. Let's say that you...
And what should I have prepared with an example, but let's say that you want to parse a CSV file or something, then you would probably separate the different things you want to do with that CSV file into functions already.
And the component is where you kind of group the functions that kind of relate to each other or that make sense to have in a Python package.
Then I mean a namespace with a dunder in it.
So that could be a component.
Yeah.
Although it could be modeled in one of these sub packages.
Those sub packages often have multiple jobs and roles and you're like, let's stay really focused on the one thing that it does, right?
Yeah.
And all of this lives in what Polylyph calls a workspace.
And that is basically a repository with a configuration about how your repository looks like.
So you have your components in namespace packages, basically.
And you have your basis, the entry points of your apps.
Then you have something called projects or a project.
And that is the artifacts or artifacts that you want to build.
So you can have only one project if you're going to build one thing, maybe a FastAPI service.
But the benefit comes when you are about to build something new.
Then you have your project infrastructure, like the project configuration and what it is defined in a folder called projects.
And then with the code you use, you pick the code from the components and bases folder.
So you will reuse the same source code.
And then you package it into different artifacts.
So it sounds a little bit like we've got this monorepo with all of this stuff.
And the polylyph, its job is to say, well, we're going to look into these little parts of this monolith.
And I need this part and this part and this part.
And it's some tooling and some concepts to help you manage some artifact.
We don't usually have exact build artifacts often if you're not shipping out separate packages.
But maybe these three pieces here make up the FastAPI service that we're going to host over there.
And maybe these two services make up the data science tools we're going to give to the data scientists for their notebooks.
And there could be some overlap in those, right?
Yeah, exactly.
Yeah.
Okay.
And another good thing is with the workspace is that you don't really do much work in the project folder or something like that.
Because the main idea is that you have a development environment that includes all your bases, all your components.
So the good thing with that is that you can experiment and try out code without worrying about if you have imported the correct stuff.
So you just, you have a top project folder containing all of your dependencies and packages.
And then you can take it from there.
Once you're ready to build a project, build something out of it, an app or whatever it is, then you can start constructing that project-specific configuration.
You can choose where you want to start.
But I usually start from the development workspace.
And I really like a way of working called REPL-driven development.
But I also learned from Closure, which is they try out things in the REPL, basically.
Yeah.
That's a really nice developer experience that you get from having the entire source code.
You can try out things, combine components, and develop new features.
Yeah.
I'm doing more and more of that as well, this REPL-driven development.
Or I'd say not necessarily development, but exploration.
I kind of want to understand.
I'm not really sure.
Is this going to click together right?
Or is this, rather than putting a lot of structure in place, because I'm not even sure I really want to stick with it, fire up a REPL.
For those of you who don't know, if you just type Python, what you get, redevelop print loop, that's the REPL.
I do it in PyCharm these days, because PyCharm has a Python console, but it gives you autocomplete and tab completion.
Like, of the things that are in your project when you're playing in the REPL, but, you know, still, same idea.
Yeah, that's really great.
Yeah, yeah, absolutely.
So are you guys using this on your projects right now?
Or what do you do with it these days?
Yeah, I'm fairly new to the team that I join.
So I've introduced them to the ideas, but they have, like, already code and stuff in place.
So my hopes are that we will give it, once we have something new to develop or include an existing microservice, maybe we could give this idea a try.
So that's basically because I'm coming.
Yeah, that's always the problem is, even if you yourself are not new, the ideas may be new to you, and you've done a bunch of previous work.
Like, for me, I was showing you that repo before.
I'm just, I'm thinking, there's a lot of cool stuff I could do about how I restructure this and reuse it and make it available, you know, sort of bring more of the mono repo stuff to some of the things I'm doing.
I'm like, then I got to update the continuous deployment changes, and I've got to update where the web server, I'm just like, you know, it's just like, there's all this stuff that's there.
And it's, you know, do you kind of pause what you're doing to try some new big organization of code here?
That's how it goes, right?
We still learned the polylet.
That's where we actually used it in production.
We had several kinds of different services and apps where we had everything in a polylet, not a repo.
But that was Clojure.
And Clojure is a compiled language, like C++ or C#.
Is that right?
Yeah, it's on top of the JVM.
So that is compiled through that engine.
Yeah, I do feel like things are just a little, the deliverable artifacts are slightly more obvious and easy to distinguish when you're talking about something that compiles.
And like, here's the library that drops into the bin folder, and here's the executable binary that drops.
You know, there's an output folder that has all the pieces that were selected.
Whereas Python, you've got to be a little more careful how you put that together.
So what I came up to was how can this idea be used in Python?
And then that was actually what led me to poetry, which I think is a really nice tool.
Well, because poetry, I think it's a lot of nice ways of handling projects and dependencies and structure and stuff like that.
There were a couple of things missing to make this idea work.
Because when you have a project configuration, you actually include components from a relative path.
So you navigate up and navigate down to the actual component.
And if you would just build a wheel or a source distribution from that, that wouldn't be a valid package.
Because then you would need to ship the entire monorepo structure.
And you don't want to do that.
So what I did was I developed a plugin to poetry that actually allows for having relative includes.
And that will build the code.
That will build a wheel and a source distribution with the kind of correct path.
So it takes all the package dependencies and puts them in the same folder, basically, before it does the wheel.
And then you have a valid distribution that you can use.
So it does a little bit of copying and stuff like that.
All right.
So the actual output here, it's a couple of wheels that we could say pip install into a virtual environment.
And they work together.
Is that right?
Yeah.
Okay.
Try this idea with like services, like FastAPI services.
Instead of including the source code like as a tree, installing it with pip from a wheel or a source distribution.
Preferably be from a wheel if you don't have any like operating system specific stuff.
So I think that works really.
And it's the end result.
If you do it in a Docker container, you can like have the full control of what's in there.
Yeah.
So in your Docker file that builds the Docker image, you can just say, you know, copy these three wheels over, pip install them into my Python environment I have over there.
And it's just taken the, what do you call them, workspaces that you need over there.
What is the terminology that you call the artifacts here?
I mean, I know they're wheels and packages, but is there a polyleth term that matches over here?
Oh, I don't think so.
Maybe it's like a built artifact perhaps.
Yeah.
And it's probably the most simplistic scenario is that you have like an app, like an API endpoint or maybe a CLI app or even a library.
And you probably want to install them in different places, maybe even on AWS Lambda.
So you can have the control over the deployment in your CI saying that I want to deploy this Lambda here and I want to deploy this FastAPI over there.
So, and with polyleth, you can build these wheels differently.
Nice.
Yeah.
I feel like the, if you think of microservices and monolith, the AWS Lambda or any serverless functions as a service story is like the most extreme version of this.
Yeah.
Here's a single function that gets deployed.
Here's a single function that gets deployed, like just one after another, right?
It's kind of out of control.
And you have all of them in separate repositories.
Oh, please.
No.
Yes.
That would be definitely tricky.
I want to come back and talk more about this poetry plugin because it's really cool.
But let's address this question from Lucas here.
How would you approach versioning in a monorepo, like of these different services and of the different pieces?
So if I'm going to have that FastAPI thing that builds over there, I'm going to have some other projects that are built
with some overlap that are shared over to, say, my data science team.
They're going to analyze data in some other way, but reuse some of the code.
I've got some thoughts, but what are your thoughts on versioning, say, in the repository or how you deploy them?
Yeah, that's a really good question.
If I'm looking at it from a polyleth perspective, I would suggest a very simplistic solution.
Let's say that you have your this project depends on something with this version and the other project is still on an earlier version.
And I think that can be solved with the components itself because all source code is made up of all these components.
So if you're going to build a new version of something and if that version uses a new third-party dependency or that is incompatible,
I would suggest you to add it as a new separate component.
So your new projects that will use that one will pick that component instead of the old.
I think it's a good practice if these components have the same or as long as it's possible, the same API.
So it should be easy to switch from the old to the new one.
So that would be my solution to versioning, at least when using polyleth.
If we look at not the monorepo style, but you build an artifact like a wheel from one repo
and you put it up there and someone else depends upon that, maybe through an internal artifact management, private PYPI,
you would pin your version in the requirements file for that other one, right?
Because that repo is changing at a different rate and a different cadence than maybe the library that it depends upon.
And that's really natural for us as Python people because we already have a great long list of things that are open source
that we don't build that we depend on, right? FastAPI, Pydantic, and Starlette would be an example from what we've been talking, right?
Those things you don't control and you depend on them.
So you pin the versions and upgrade them as you see fit.
But one of the advantages of the monorepo to me, as far as I see it at least, is the whole system,
not just your part of the system, but the whole system is consistent all the time.
The main branch or the production branch or whatever the shipping branch is.
So I think you would maybe branch, do some of your work, merge that back in.
And at that point, you could ship everything if you need to, right?
Yeah.
Because you're continuously keeping it together as a whole system, not like, well, that library built and that library built
because they're separate repos, but you put them together and who knows what's going to happen.
I think this is an advantage of the monorepo in a sense.
Yeah, me too.
Definitely.
I think it makes it easier to notice about using some parts of the code.
It's using a certain version of a dependency.
You will learn about it quite quickly because if you would install it and try to run the code.
it's easier to note, and hopefully it's easier to also update that code.
But if you are in a situation where there's so much code that you need to refactor,
maybe there's a breaking change that kind of has rethought the entire idea of that happens.
Maybe you need to do some sort of separation and keep the oldest until you have the time to refactor that.
I guess in most cases, it's pretty straightforward to update just everything in the monorepo.
Right. Yeah, exactly.
And if you've got some sort of continuous integration or some kind of automated check,
you're going to find out pretty quickly this change you made has a consequence over there.
And I think that's why people who are psyched about monorepos are excited about it.
It also feels to me like if there was going to be a breaking change, it's going to happen either way.
It's just, is it going to happen in small little pieces or is it going to happen in one terrible, huge,
oh, you got the new one?
Well, let me tell you, the new one's really different.
It doesn't work anymore.
Like, oh no.
You know, just like you want to merge more often or you want to try to integrate things more often
and not just wait some long period of time and go, now do they go together?
Why are there a hundred or a thousand, you know, merge conflicts?
I don't know, right?
Like the more you do these little continuous checkbacks and integrations, it's just going to be so much easier.
Oh yeah.
Totally agree.
Yeah.
I mean, the only scenario where you don't have to go back and pay that penalty is where the other service
that you're versioning against says, we're never going to go to the new version.
And if this is internal code, it's unlikely that it's never going to go to the new version
unless it becomes just dead.
And then who cares?
You're going to need to integrate them eventually.
Just keep doing it continuously.
So yeah, I'm starting to really come around to the idea of these things.
Yeah.
So this polyleth plugin for poetry, it's super cool.
So for example, here on your example, you say poetry, space poly, space info on the terminal,
on the command prompt.
And it'll say, hey, look, in here we have two projects made of two components and two bases,
the Lambda project and the FastAPI project.
And they're made up of these different elements.
And it really shows what part of your code is depending on the other ones.
It even gives you a pretty table.
Is this made with rich?
Yeah.
Yeah, it's rich.
I love that tool.
It's so great.
Yeah.
It's been my new favorite tool.
It's so good.
Yeah.
So it's a really nice looking UI that you put together here as well.
Yeah.
Thanks.
I'm really happy to hear that.
Well, how easy for people to...
Yeah, go ahead.
Sorry.
Don't mean to just talk over you.
Yeah, I just want to mention that what we see here is the tooling support for the polyleth thing in Python.
And I decided to make it as a poetry plugin.
I have some plans to break it out of poetry to make it separate.
CNI 2, maybe I'll do that in the future.
But I thought it was a good fit for poetry since I'm relying on a lot of poetry features.
And the command there, polyinfo, is showing you an overview of your monoree.
And this is my example project with...
So it's not a lot of code, but the idea is that you will list all your components and bases.
The command there for these are bricks.
Then you get a sort of an overview of what's in that monoree.
You can sort of get an idea of what's in there.
What does this thing do?
And then it's listed per project.
So you can see which project is actually using which brick.
Yeah, let me just give a little bit of visual information for people listening.
So under the brick column, it says we have the logging, we have the messaging, we have the greet API, and we have the messages for Lambda.
And then you've got, in different columns, the different projects that might be consuming them in little check marks or dashes to say using or not using it.
It makes it really visually clear how your elements fit together, right?
I'm continuing adding commands to this tooling.
There is also, you can use this information, the information about the workspace and the individual project to, in your CI to determine if, let's say that you change the message component, you do something in it.
And then you would want to have the projects that are affected built for that.
So the tooling will help your CI to make decisions, should build this project or if it should skip building because nothing has changed.
So that's part of the tooling to work.
That's pretty interesting because we've had attempts and they've always been like an awesome 80% solution that never quite works.
But really good solution, really good tool, ideas, I guess, to say, if you change this code, what actually needs to be tested again or what needs to be analyzed again?
If this other part of your system doesn't depend on it, you don't need to run those tests, right?
If just the file changes, that doesn't tell you anything.
You need to look at things like code coverage.
What part of the system was touched if this part, you know, by affected by this at all, right?
Those are always really tricky.
And how do you keep like a history of code coverage to know what to do?
And all those attempts I've seen just kind of like, we tried, but we don't really do that.
We'll just run the files that change, which is never enough.
But this kind of is a natural way to express dependencies in that tree to say, okay, if we change the greet API, we see that the Lambda thing doesn't work with it.
So we don't need to test anything to do with the Lambda stuff.
We only need to test the FastAPI aspect, right?
Yeah, exactly.
Is that tooling in place now?
Yeah, that's in place now.
Okay.
The poly info command, I think it was like the first command that I actually added.
Then there's a...
diff command and what did I add?
Yeah, the latest addition to commands is a check command.
Because since you are...
I was talking about development experience that you're working at in a development project where you have everything.
And then you might not touch the project that much.
And that means that you could potentially forget to include dependencies.
Because as of today, there's no automatic thing yet.
I'm planning to add that later.
But so far, you need to keep track of your dependencies and stuff like that.
So I added a check command that actually does performance analysis on the source code.
So if you...
Let's say that one of the components uses the requests library or something like that.
And you don't have it in your dependencies.
Then you would be notified for that particular project.
It's very likely that you would discover it anyway in your development environment.
But this is an extra check to just make sure once you're about to build something,
can I really build this specific project?
So it's a few commands, but it will be more commands that are more and more useful, I guess.
Yeah, it looks great.
And you have some examples over in the Python polyleth examples repository that people can check out.
Yeah.
So is the poetry plugin ready for people if they wanted to use it?
I think so.
I know I haven't seen any stats yet, but I have a couple of users that have contacted me through the GitHub repo and social media.
Maybe some of them are just experimented with it and others have.
I think they actually are working with it in their daily work.
And I think it's useful.
I have to remind myself to contact them regularly to just check out how it goes.
And hopefully they will come to this repo and let me know if something doesn't work as intended.
But it's a new tool and I probably need some more work on it, of course.
Yeah.
Well, a lot of people will hear about it now.
They can come check it out and play with it.
Oh, that would be great.
Yeah.
And I'm sure you're taking contributions and PRs and you wouldn't mind if people had some additions.
Oh, yeah, definitely.
Yeah.
I would love to have that.
Cool.
So contributions are very welcome.
Yeah.
And you also have really nice examples here.
Like you have two videos that show how it works.
You know, 15 minute YouTube videos.
You've got some pictures and, you know, well done on that.
It makes it really easy for people to come and just see like, okay, is this interesting?
Does it apply to me?
So good work.
I do want to make one really quick follow-up.
Corky out there had mentioned that maybe shallow clones were a better, more predictable choice than the partial clones with the filter equals blob.
In general, the people at GitHub are recommending not to use the shallow clones anymore, but to use instead these partial clones because it keeps the history and it can incrementally go back and pull the stuff in as needed.
However, there is one time where you may really want those shallow clones.
And the reason I'm thinking of this is you talked about builds and using this to make builds run faster and only focusing on the parts that have changed.
If you have a CI, the CI doesn't care about the history of your GitHub project.
It just wants the working files, right?
So you can do a shallow clone and say, just get me only the files on the tip of this branch and then build it.
And that could be dramatically faster than saying, give me all five years of history of every single file and reassociate that.
So if you're thinking about CI, this shallow clone idea that I was dismissing a little bit is exactly a good choice, I think, because you don't care about version history if you're trying to see if the current version builds or not, right?
So anyway, just a quick follow-up on that.
All right, David, I think we're probably out of time.
I definitely encourage people to go check out your poetry plugin.
They can check out polylith at polylith.getbook.io.
Of course, I'll link to it in the show notes.
Now, before you get out of here, I've got the two final questions to ask you, of course.
If you're going to write some code, if you're going to work on the poetry polylith plugin, whatever, what editor are you using these days?
Well, these days I use Emacs.
I really like the two final things.
Before Emacs, I really liked PyCharm too.
But then I decided to learn Emacs and I'm stuck.
Okay.
Every programming language, I'm going to code everything in Emacs.
Nice.
Yeah.
Long, long ago, that was my very first editor for programming.
Oh, cool.
And then, yeah.
It brings me back to working on Silicon Graphics mainframes, computers doing C++.
So, notable PyPI package.
Something we simply talked about.
Something else that you want to just tell people about that you thought was awesome.
Ran across recently.
I have to say Rich because it's such an awesome tool.
And when you're going to develop a CLI and want it to look nice, and yeah, Rich is a really, really good tool.
There were a lot of visualization features and stuff like that.
So, that's a fantastic tool.
Yeah.
Good recommendation.
There's so much momentum behind Rich these days.
And if you're making some CLI developer-oriented tool, just give it a little color.
Give it a little structure.
And something like Rich, even just a little bit of color or a little bit of distinguishing one line of text from the other makes such a big difference in being able to use it really quickly and easily.
And Rich is probably the best way to do that by far, right?
Yeah.
You should check out my new latest command, PolyCheck, because it uses a rich feature that I'm really happy about.
It's silly, but I'm really happy.
It uses an emoji while you're waiting.
Fantastic.
Oh, yeah.
I love emojis.
I love emojis and CLIs as well.
All right.
Final call to action.
People want to get started with monorepos, with polyleth, with some of these ideas we've talked about.
What do you tell them?
Head over to the polyleth git repo or the official polyleth docs and read about it and see.
Also, if you're interested in monorepos in general, check out the other solutions that are out there, because there are a lot of different approaches with different kind of focuses that maybe fit your situation best.
I'm, of course, pro polyleth because I, yeah, develop a tool and really like that.
But there's probably tools that are better for a difference to situation.
So just explore, I would say.
Right, right.
The tools that maybe Google chooses to manage its code base might be the wrong tools that you choose for yours because the scale is so different.
Right.
Yeah.
You might add so much complexity that it's not relevant.
You know, it makes it really hard, but you don't need that complexity because you've got five projects, not 5,000 projects.
Right.
So, yeah, absolutely.
Look around is good advice.
Okay, David.
Thank you for being here.
It's been a really fun chat.
I learned a bunch.
Yeah.
Thank you.
So we really have fun to be on the show.
Thank you.
Yeah.
You bet.
Bye-bye.
Bye.
This has been another episode of Talk Python to Me.
Thank you to our sponsors.
Be sure to check out what they're offering.
It really helps support the show.
Starting a business is hard.
Microsoft for Startups Founders Hub provides all founders at any stage with free resources and connections to solve startup challenges.
Apply for free today at talkpython.fm/founders hub.
Stay on top of technology and raise your value to employers or just learn something fun in STEM at brilliant.org.
Visit talkpython.fm/brilliant to get 20% off an annual premium subscription.
Want to level up your Python?
We have one of the largest catalogs of Python video courses over at Talk Python.
Our content ranges from true beginners to deeply advanced topics like memory and async.
And best of all, there's not a subscription in sight.
Check it out for yourself at training.talkpython.fm.
Be sure to subscribe to the show, open your favorite podcast app, and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the direct RSS feed at /rss on talkpython.fm.
We're live streaming most of our recordings these days.
If you want to be part of the show and have your comments featured on the air, be sure to subscribe to our YouTube channel at talkpython.fm/youtube.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
Thank you.
Bye.
you
Thank you.