S08E14 - From YAML Chaos to C# Clarity: Mattias Karlsson on Cake Build
Sponsors
Support for this episode of The Modern .NET Show comes from the following sponsors. Please take a moment to learn more about their products and services:
- RJJ Software’s Strategic Technology Consultation Services. If you’re an SME (Small to Medium Enterprise) leader wondering why your technology investments aren’t delivering, or you’re facing critical decisions about AI, modernization, or team productivity, let’s talk.
Please also see the full sponsor message(s) in the episode transcription for more details of their products and services, and offers exclusive to listeners of The Modern .NET Show.
Thank you to the sponsors for supporting the show.
Embedded Player
The Modern .NET Show
S08E14 - From YAML Chaos to C# Clarity: Mattias Karlsson on Cake Build
Supporting The Show
If this episode was interesting or useful to you, please consider supporting the show with one of the above options.
Episode Summary
This week on the Modern .NET Show, we welcomed Mattias Karlsson to discuss Cake (C# Make), a C# based build orchestration framework offering an alternative to traditional YAML-based build pipelines. The conversation centred around the benefits of using a strongly-typed language like C# for build definition, providing greater clarity, debuggability, and consistency compared to the often-opaque world of YAML. Mattias explained that Cake doesn’t replace existing build tools like MSBuild or the .NET CLI, but rather orchestrates them, simplifying complex workflows and offering a more developer-friendly experience.
A key takeaway from the discussion was the power of local debugging with Cake. Unlike YAML pipelines which often require pushing changes to a remote runner to identify issues, Cake allows developers to load scripts into Visual Studio or Visual Studio Code, set breakpoints, and step through the build process locally. This dramatically speeds up development and troubleshooting. Furthermore, Cake’s abstraction layers handle platform-specific complexities – such as file path differences between Windows and Linux – ensuring builds are consistent across different environments.
Mattias highlighted Cake’s flexibility and extensibility. With support for .NET 8, 9 and 10 and through the use of add-ins, Cake can adapt to a wide range of scenarios and integrate with various CI/CD systems. The introduction of Cake SDK further enhances this, enabling the creation of single-file executables for simple deployments and offering a smoother integration with the .NET ecosystem. This allows for the creation of a consistent build experience, from a developer’s local machine to the production environment.
The conversation also explored the concept of “recipes” (pre-defined build scripts) and the potential for leveraging Cake in complex scenarios like AI projects where consistent and reliable builds are paramount. Jamie and Mattias touched upon the advantages of a gradual adoption strategy, starting with simple build definitions and incrementally adding complexity as needed. This approach allows teams to reap the benefits of Cake without overwhelming themselves with a complete overhaul of existing infrastructure.
Episode Transcription
So it essentially is a build orchestration framework. So it doesn’t replace the .NET CL or MSBuild or whatever you’re using today. It doesn’t replace GitHub Actions or Azure pipelines. What it does is that it reduces the complexity of those things.
Hey everyone, and welcome back to The Modern .NET Show; the premier .NET podcast, focusing entirely on the knowledge, tools, and frameworks that all .NET developers should have in their toolbox. I’m your host Jamie Taylor, bringing you conversations with the brightest minds in the .NET ecosystem.
Today, we’re joined by Mattias Karlsson to talk about Cake (aka C# Make), the build orchestrator built entirely in .NET.
Like, you need to evaluate and see what works for you. Because, like, if you have an open source project and all you do is dotnet pack, then it might be too complicated
Along the way, we talked about what a build orchestrator is, why you might consider one (and when it might be too complex to have one), the recent single file application changes to .NET (i.e dotnet run file.cs), and talk about why it’s important to have multiple tools in your development toolbox.
So let’s sit back, open up a terminal, type in dotnet new podcast and we’ll dive into the core of Modern .NET.
Jamie : Mattias, thank you for joining us on the show. It’s the first recording of 2026, so woohoo. We’re going to be talking about something that is super interesting to me. We use CI/CD tooling all the time and very rarely, I feel, do we ever actually think about what it’s doing. I feel like what we’re going to be talking about is related to CI/CD tooling. I know that you can use it for that, but you can use it for a lot of other things, like making a personal build of something. Anyway, before we get onto that, welcome to the show, Mattias. It’s great to have you.
Mattias : Thank you, Jamie. It’s great to be here.
Jamie : Amazing. Would you mind giving the listeners a bit of a brief introduction to the world of Mattias, the things that you can talk about, what you work on, like this particular project we’re going to talk about?
Mattias : Sure. My name is Mattias Karlsson. I come from the west coast of Sweden, a city called Gothenburg. There I’m a partner and senior architect at a consultancy company called Devlead. I’m also a Microsoft .NET and DevOps Developer Technologies MVP. Many know me as an open source maintainer of several projects. I’m also foremost a husband of one and father of two. When I’m not writing code, I like to go downhill mountain biking, get out in nature and walk by the river and things like that. That’s basically who I am.
Jamie : That’s super interesting stuff, I have to say. I remember I used to go mountain biking when I was about 16 and then I fell off and decided that hurts. I don’t want to do that again.
Mattias : Yeah, I’m a lot more cautious now, the older I get. My son does things that I wouldn’t do nowadays.
Jamie : So we are going to be talking about C# Make, Cake, Cake Build, .NET Cake. There’s a whole bunch of different names for it that I know of. But is it just Cake? What’s the actual name? If somebody’s going to Google it, what do they need to search for?
Mattias
:
Well, it’s not very Googleable in one way, but essentially what we call it is Cake. CakeBuild is the organisation on GitHub, so that’s a good starting point. It’s had a couple of names over the years depending on which runtime you’re running on. We’ve been around since before .NET Core came, so we’ve been around for a while. We were just Cake for a while because we were just a .NET Framework executable. When .NET Core came, we had both that executable and one available on the Core CLR. So we had both Cake Core CLR and Cake .NET Framework, essentially the same, just compiled differently.
For the last couple of years, Cake Tool has been the NuGet package, but it’s still been Cake. We also had Frosting. It’s been a bit varied. But if you search for C# and Cake you will usually find cakebuild.net. What we say is usually just Cake, but Cake is very common if you search for baking and pastries and things like that, so you need to qualify the search.
Jamie : Yeah, I love the food metaphor and I get where it comes from because it’s C# Make, let’s join the words together, right? Before we get started into any of that, what is it from a 10,000 foot view level? We’re not going to go into the details. If you had to give the elevator pitch for C# Make, Cake Build or Cake Build Tool or Cake Frosting, what is it?
Mattias
:
Essentially it’s a build orchestration framework. It doesn’t replace your .NET CLI or MSBuild or whatever you’re using today. It doesn’t replace in general GitHub Actions or Azure Pipelines. What it does is that it reduces the complexity of those things. You can essentially write the workflow of your build in C# instead of YAML or PowerShell or batch or something like that. It’s cross-platform, so it will work in the exact same way, regardless if you’re on Mac or Windows or Linux or in a container. That’s the big thing.
Being C#, it’s something that .NET developers know. We used to have other tools like old Make or Gulp or Grunt or anything else, and it’s a context switch if you’re not familiar with those. I think that’s the big thing. So it doesn’t replace things but it’s doing the orchestration of the tools you use today.
Jamie : Right, okay, so it itself is not doing the low-level steps to actually convert your C# .NET code into an IL binary that can be deployed somewhere, but it’s calling the tools that will do that for you. Is that right?
Mattias : That’s correct. It’s actually automating the APIs, the CI tools and processes you need to use if you have a more complex build, or essentially release or deployment.
Jamie : Right, okay. So I still need the tools to be installed, but this allows me to, I guess, in a more C# developer-friendly way, arrange and like you said, orchestrate that. If I need to do a clean and then a build and then a test and then a pack for a specific configuration, instead of remembering all of those commands or writing hundreds of lines of YAML, I can maybe write a Cake script. Am I using the right word to do that?
Mattias : Essentially yes, Cake script is what we call it. The big feature is also that all those tools that you run also have arguments, and Cake also has fully typed classes or setting classes and arguments. Which means that you don’t need to remember that this is an int or these are what arguments are available and which can be combined and things like that. That’s also a big advantage, that you will have a strongly typed experience, which you know isn’t always offered if you use something like YAML.
Jamie : Yeah, absolutely. Because the big problem that I have with YAML-based build tools, and I’m sure I’m not the only person who has this problem, in fact I read a blog post just yesterday—we’re recording this on January 15th—I read a blog post just yesterday entitled “Why I Hate GitHub Actions” and it was all about: I can’t recreate the YAML on my machine so I have to push the YAML up to GitHub and watch it fail, figure out the problem, and then fix the problem locally but not be able to repeat it or test it and send it back up again. I’ve been in that position myself.
Mattias
:
Yeah, and that’s one big advantage with something like Cake, because it will run both locally on your machine and it will run the same flow as you run remotely on your CI or deployments. Which means that if you have a problem then you can debug it locally and you can use proper debugging tools. You can load up Visual Studio or Visual Studio Code, you can set breakpoints, inspect variables. Those are the things you can only dream of otherwise. There are things where you can simulate workflows, but they are simulating, they aren’t fully what you’re using in GitHub Actions or Azure Pipelines.
Often also there are things like Cake will have abstractions that will help you normalise things like paths. So you don’t need to know if it’s a forward slash or backward slash if you’re building for both Linux and Windows, for example. Often as well CI is things like oh the path was wrong and then I have to do the commit and do the build again. We have all the regular helpers for things, we call them aliases but they are ready-made static methods where you can just, if I want to zip a folder there’s already commands for that, if I want to copy files or if I want to invoke the .NET CLI and create NuGet packages or things like that, they’re already made in the box.
I think that’s one of the main advantages, that you can actually test it locally and do so with the same tools that you usually use. The only thing you need to have installed is the .NET CLI. Then we can add all other tools if you use those. Of course you can add other SDKs or frameworks, but the minimum thing you need is the .NET CLI.
Jamie : I think you breezed past something really quickly there. I think it’s really important to double back and just repeat that. That is you can debug it locally. You can load it into Visual Studio. You can presumably hit F5, stick a breakpoint in there and go, why did that step fail?
Mattias
:
Yeah, and also it could be that you have, I mean if you have a simple solution, then you will have a really simple pipeline and Cake might not make sense. If the only thing you’re doing is .NET pack and adding an artifact then it may be overkill. But for many scenarios you can have multiple projects, it can be that you need to version it a certain way.
It could be like one of my common scenarios for Git flow is that I will first do a clean so I always have a good starting point for the projects, like cleaning up bin and obj and things to have a reproducible build. I will then restore from my NuGet packages or those packages. I will build the code. I’ll then test the code. I’ll run code coverage if I can, I do when I can. Then you will create the artifacts, could be a ZIP file or a NuGet package or a Docker container or what have you.
That means that you want to do this in a certain flow and you can do it, but you can have things like conditions. So it can be like, if I’m running on GitHub Actions, I’ll do this. We have ready-made environment variables, like ready properties you can check, like am I running on this branch or is my release tagged, which means that you can easily do a flow and that can be very complicated for some scenarios. Then that’s nice to be able to set a breakpoint.
Often it’s just environment variables, but it means you can simulate fairly easily locally, like if I set this conditional, would it happen? If you have a debugger and it’s fairly complicated, you can even change while you’re running some conditions, which means that you can actually test flows that normally wouldn’t hit locally.
Jamie : You were saying that there’s abstractions over the environment. You said it is just environment variables, but it can be other stuff. That is super important for people who are like me, perhaps. When I do a local build, I want to have my secrets included because I want to simulate what a production build is like. Then when I push it to my CI/CD runner, it’s going to have its own environment variables. So I can be sure that I don’t have to do any variable substitution or if we’re in Azure DevOps, we don’t have to maybe go to a library to pull those in or maybe we don’t even do it for the build, we do it at runtime. Maybe I have Azure Key Vault, at runtime I go up and grab the values. I don’t have to care about that anymore. I don’t have to care about how my secrets are being substituted into the app.
Mattias
:
Yeah, sometimes like if you construct a file name based on something, it’s really nice to be able to use something like interpolated strings in C#, where you can easily concatenate something in a good way or format it. That something could be something you create really early in the DevOps process, but it could be that the result of it is first you see it in a release. So it could be that you do several tasks and then it fails because you had a bad file name to create the artifact or something. That’s nice to be able to inspect that locally too to see, oh, it makes sense, it has the right file name. Otherwise you need to output things and it can be a security thing or anything like that.
But also we have some more complex scenarios where we actually, to upload artifacts in GitHub Actions for example, we have ready-made commands which you can just call, you pass a path to it and it will upload artifacts and you can do that as part of your build script. Those scenarios are, you need to do a context switch between tasks, which means you can lose state and things like that. That’s nice to be able to just do it as part of your build. Especially if you’re building for multiple platforms it’s nice to be able to use C# as like this is a Linux build and I can construct this file name and I can do fan in, fan out easily too and things like that.
Jamie
:
A hundred percent. That’s another thing that perhaps folks who have come from the traditional path of .NET development, those who are long in the tooth and started their .NET journey back in .NET Framework time, it is perfectly reasonable for folks to be building and deploying onto a Windows machine. That is not a problem. Just because .NET is now cross-platform doesn’t mean you have to be cross-platform, right? It gives you the choice.
But for those that have come from a Windows background, you probably don’t realise that there are things that you can put into a file path in Windows that you can’t on a Linux-like or a Unix-like or a macOS-like system, and vice versa. Not just the forward slash versus backslash bit. Depending on the file system your Windows server is using, you might not be able to use certain characters in file names. Depending on how you set up Windows or depending on how your ops team has set up Windows, there are file path limitations. To know that some, if not all, of that is taken care of for me by the build tool like Cake makes my life so much easier because I can just go, hey, Cake, do the thing, right? Just put it in this directory and you figure out how to do it.
You know, maybe behind the scenes it’s doing path.join or path.combine or maybe it’s doing something else. I don’t know. But I don’t have to worry about if I send it up with backslashes, will it still work if it’s running on a Linux runner, which is what happens in YAML.
Mattias
:
Yeah, and things like that that it won’t tell you when you’re authoring it. Often there can be some subtle things like some GitHub Actions tasks, they will use PowerShell Core if you’re running on Windows and then they’ll use Bash if you’re on Linux. That’s a subtle difference. You won’t notice if it’s just to do a .NET pack or something like that. But when you address an environment variable, they will be referenced differently. But in Cake they will reference them with the same command, regardless of which shell you’re running on or which operating system.
There are things like in Linux like some variables are separated by colons and in Windows by semicolons. You don’t need to worry about that, that’s already handled by either Cake or the .NET runtime. So the heavy lifting there for being cross-platform is done for you. It’s nice to be able to have only what differs in a small set and can have it like just an if statement or a branch for that or a conditional.
Which is nice. Being a strongly compiled language also means that it’s compiled before you run it. Even before you run it, it will know it is semantically correct, which isn’t always true with something like YAML. Could be that you indent something which means that it won’t run, but it looks correct. It will still commit that text file, but it might be that you’re down there like five hours later when it does the deploy, it doesn’t work.
Jamie
:
Or indeed, I’ve had it the other way, where I may be using Visual Studio Code to write some YAML for either GitHub Actions or Azure DevOps or some other system and I’m staring at it and it’s telling me, no, that bit is wrong because reasons. I’ve got the red squigglies, you know, and they’re saying this section you’ve written is syntactically incorrect. I’m sitting there going, I didn’t write that section, that pre-exists, and that runs on the server. Which means that I then can’t trust the syntactic underlining. I don’t know the right word for it. But I can’t trust that because it’s telling me that something that pre-exists that works on the build server is incorrect. So I don’t know whether what I’ve just typed, the bit that I just added, is syntactically correct. Because the tooling is telling me it’s not correct. That is, you know, that’s a downfall of just basically a downfall of YAML because it’s so complex it’s difficult to write anything that even behaves a bit like an LSP for it to validate it correctly.
But to your point about because this is strongly typed, essentially, if it compiles, it’s going to run, right? Regardless of whether it does what you want it to do when it runs, if it compiles, it’s going to run. That means that you can compile it on your machine and know that it’s going to compile over there on your build server.
Mattias
:
Yeah. It’s also easier like if you are primarily a .NET developer and C# is what you do all day, it’s easier to reason what it does. It’s really hard sometimes like if it’s a language, often a build pipeline for many is set and forget and it’s only when they’re having problems they revisit it. Which means that in that scenario, it’s nice to be able to use the same language you use every day because if you get a pull request, anyone in the team can reason around what it does.
I think that’s a big advantage too, because it’s sometimes really hard. A good .NET developer can almost compile the code in their head whilst they review the pull request. But with YAML it’s sometimes hard to do because also there’s so much of it. Many YAML tasks and actions are really powerful, but they’re often inconsistent because it could be that this was created by one person and they use “path” and the other task uses “filepath”. The same with like if you have Azure Pipelines where you see the Azure CLI task has one syntax for which shell you use, the script task has another because they’re not unified because they’re different.
That’s why it’s hard for a linter or something like that because it’s very dynamic. Depending on what you pull in will make it behave different three lines down. I think that’s, well having a strongly typed language is sometimes less flexible, but it also means that it’s more dependable when you’re reading it.
Jamie
:
A hundred percent. You’re absolutely right. If you look at the average pipeline task, I keep saying Azure Pipelines or GitHub Actions pipelines, they are slightly different. But if you look at the average task, there are APIs that folks have to hook into to make them work. But like you said, the syntax and the style of parameter passing and what you might expect is a parameter and what might not be a parameter are completely different.
That’s because the majority of them are open source made by people who are like, I built this Azure pipeline task or this GitHub pipeline task to do this one very specific thing that I need it to do. I will build it for me and I will just put it out there for people to use. That’s not a negative thing at all. But because everyone has their own way of doing stuff, all of those APIs that the tasks themselves expose are all different. So you end up with, why is this not working? Why doesn’t, and then like you said, you can’t syntactically check for that when reading through the file because your language server, your LSP isn’t able, maybe isn’t able to go all the way out to the web, find that particular plugin or that particular task, look at the documentation for it, and then come back and say, yeah, that’s valid. It can’t do that. It can only go on what it expects it to look like. If it doesn’t look the way that it’s expecting, then it’s going to fail.
Mattias : Yeah, and also often you need to execute it to be able to actually truly know because there’s matrices you can have, dynamic templates sometimes, you can have parameters and you need to evaluate all those and you have things like well there’s pipeline secrets, environment variables and whatnot. So it can be fairly complicated to reason to exactly what’s going on. It’s very powerful, but it can be complicated to grasp what to actually do.
Jamie : Yeah, you’re absolutely right. But then, you know, if you’ve got a statically typed language, the majority of that disappears. Because the tooling will help you tell you whether it’s syntactically correct. But also if you’re using something like ReSharper or a modern Visual Studio, it will do a bunch of stuff behind the scenes to check that. Then you can hit Ctrl+Shift+B or Command+Shift+B, whatever, .NET build, whatever, and it’ll build it and tell you whether it’ll build and run.
Mattias
:
Yeah, and sometimes I feel like people, everything is like when you have a hammer, you need to evaluate and see if it works for you. Because like I said, if you have an open source project and the only thing you do is .NET pack, then it might be too complicated. But usually I’ve found that things grow and it could be that you need to, especially if you do some more enterprise software, you might need to sign binaries, you need to stamp versioning and you need to have a flow with approvals and things like that, then it quickly grows complicated, the build pipeline.
But I mean you need to see what fits your needs. But also nowadays it can be that well I need to build both in a Docker container or a Dockerfile or I need to run on GitHub Actions or I need to support GitLab because whatever. It could be different requirements or it could be that there are regulatory things that my code needs to be in this country and in that country only these services exist.
One thing is that almost all CI systems can launch an EXE, which means that that’s essentially what they’re doing when you’re launching Cake. You’re launching an EXE. The rest is C#. So you only need to think of your CI YAML or whatever definition that CI server has becomes more like describing the environment. What agent should I be running on? What variables should be set in secrets and things like that. The rest is handled by the orchestration tool, which you also can run locally, you can run it in a container or you can run it on the server or custom agent. I think that really simplifies things, especially if you have a complex scenario.
Jamie
:
Oh, a hundred percent. I’ve seen some build pipelines in my career that were horrendously complex where I was like, look guys, just take a step back. Don’t add the tooling steps in, just set up the environment for me. That’s all you need to do. Do that.
Then we’ll slowly, because what had happened was this particular build pipeline I’m thinking of is from a project from years ago, where they had not thought in a pragmatic way. It wasn’t designed as, right, let’s set up the actual build runner, you know, the environment we’re going to build in first. Let’s step back. Let’s talk about how I build out YAML pipelines. Everybody does it differently. This is how I do it. The first version is just setting it up. As you said, set up the environment. Which version am I running on, a Linux runner, on a macOS runner, or a Windows runner, or a combination of the three? Can I do that first? Brilliant. Now I’ll add a build step that just prints to the console, I’m pretending that I’m building or something like that. Because then I can prove that I can do a build and then slowly add these steps in.
Whereas I know some folks, and everybody does it differently, every single way of doing it is valid, but they will sit for hours and write out an entire YAML file with all of the steps and all of the if conditions and all of the matrices and every single thing. Then they push it up and none of it works. Then they scream that YAML is the worst and it’s stupid and it’s a poo-poo face and, you know, not in those words. But they start yelling about how bad it is. I’m like, well do it a step at a time, dude. Step back and set your environment and stuff like that.
Mattias
:
I think that’s true for any project because I usually have a couple of interns a couple of times a year and the thing that I need to teach them is start with an empty project. I think that’s the same with everything. Start with nothing. Then you build upon it because otherwise you will debug everything and not just the thing that’s at fault. So start with having, even if you have a .NET project, start with having just a global.json and the solution of an empty project and an empty test project and start from there. At least you knew that it worked before you started. So do it in steps. Don’t start with like I need a pipeline that has report generation and code complexity and like, well, did it compile? Start with that first.
It’s so easy for people to do too much at once. Which means it’s really hard to debug. It’s really hard to reason why something, if you have a PR that touches every file, you don’t know what broke everything. It’s hard to blame. But if you do more evolutionary things and small commits, then it’s really easy to see who to blame on what was the thing that broke it.
Jamie
:
Yeah. Depending on how your organisation works, lots and lots and lots of small commits probably don’t matter because when you do a PR you’re probably going to do a squash merge rather than any other kind of merge. So it will take all of those commits into one super commit, I guess, and just put that in place. So it doesn’t matter if the thing that you were trying to do required you to do five thousand commits. I would worry about the size of the task if you were five thousand commits in. But if you were doing five thousand commits and then you submitted a PR, at the end of the day, that’s going to come out as one commit into main, or whatever your trunk branch is. So it doesn’t matter.
So to your point, do it slowly, do it in pieces, and then you can figure out if things go wrong where they went wrong. I love that advice. It goes back to that pragmatism idea that I feel like some people don’t practise these days. Oh, I’m getting on my soapbox, I’d better step down, otherwise I’m going to get all ranty.
Mattias : Yeah. There’s a lot of solid advice like start with something really simple and then evolve, I think especially also because it could be that you put in a dependency and that’s what breaks your build. But if you start from an empty project you know at least that it was a good starting point. Also then you will have for all your PRs you will have CI/CD that validates your PR. It’s so easy that someone develops for two weeks and then they start committing the project and then they will have all kinds of things that it could have been that, well, if I just knew it early, I would have done it differently. Now I might need to redo it.
Jamie : That can be where Cake comes in, right? Because it’s C#, because it’s statically typed, because it’s debuggable, I can create a basic empty script and start from there. Cool. I’ve got this empty C# Make or Cake Build script that does nothing. Commit that first and make sure that my system can run that empty script. Cool, I can run the empty script. Now I’ll add maybe a build task. Now I’ll add maybe a configuration management task. Maybe now I’ll add some other really complex task that would have taken me hours to write in shell or PowerShell and then it only works on some runners. I could do all of those things. One thing at a time. You know, we don’t build a house by just standing there and building the whole house in one step. We build it one brick at a time.
Mattias :
Yeah, but also once you’ve done it a couple of times, you will build up a recipe of things, which means that for many things now I can do most like GitHub Actions scenarios for many, like if I have a .NET class library, I now know exactly how I’m going to do that because I have the recipe for it. Because that’s a known environment with a known set of tasks. When you have those, if you’ve done it a couple of times then you can barely have the recipe that will work the first time you commit. Because you can also test it locally because usually it’s just the things that differ between many projects are just like, well, the name and the path and a couple of things.
Then it’s really nice to be able to test that locally and get that feedback in milliseconds instead of you needing to grab coffee whilst the runner is starting up on some GitHub Action or something. So it’s also a matter of it’s easier to do things because it’s quicker. Sometimes you do it in batch because it takes a while to start a GitHub Actions agent or something. So you do more because the return cycle is longer.
Sponsor Message
Today's episode of The Modern .NET Show is brought to you by RJJ Software: strategic technology consulting for ambitious SMEs.
You know me as the host of this podcast, but here's what you might not know: I'm also a Microsoft MVP who's helped businesses from Formula 1 teams to funded startups transform technology from a cost center into a competitive advantage. At RJJ Software, we specialize in three things that matter to growing businesses:
- AI that actually delivers ROI: not hype, just practical implementations that pay for themselves
- Developer Experience optimization: we've helped teams achieve 99% faster deployments and 3x productivity gains
- Strategic technology decisions: from architecture reviews to fractional CTO services
The difference? We don't just advise. We ensure successful implementation through knowledge transfer to your team.
If you're an SME leader wondering why your technology investments aren't delivering, or you're facing critical decisions about AI, modernization, or team productivity, let's talk.
Visit rjj-software.co.uk/podcast to book a strategic consultation.
Now, let's back to today's episode...
I guess just relating to the speed, not exactly in the same way, but relating to the speed, since Cake Build, and you mentioned earlier on that one of the bits of Cake Build, the Cake Build Tool, is a NuGet package, which says to me it runs on the .NET runtime. Which means that with every release of the runtime as that gets faster or more efficient or more memory efficient, you get those bonuses anyway, right? So it could be that you create a Cake Build script for maybe .NET 8 and then you go through the requirement and the work required to upgrade to .NET 10, then suddenly your build script gets faster.
Mattias
:
Yeah, and also if there’s a new runtime like when they added FreeBSD support or other third party support, then you will get that too. So just being, the only dependency now we have is the .NET runtime. Which means that everywhere it would run like, and if you’re a .NET developer, well if the .NET runtime doesn’t run, well then it isn’t for you anyway. That’s a good minimum. But we don’t have any other dependencies than the .NET SDK.
Which means that wherever, if they add a new target or if they add something new then it will be supported. One of the goals is that we will release a couple of times a year now because we have a steady cadence. We are on the 11th year of Cake now, which means that we have always released almost day one support for incoming frameworks. That’s been our goal. We now support the current supported runtimes, which are 8, 9 and 10, which are the ones that are supported by Microsoft. We have tried to follow along with improvements and the things in the language. But also we still support, we have had a high bar for removing things. We try to be as backward compatible as we can be. So many things, people have been able to move forward for over a decade now and still be able to reuse most of the patterns.
Jamie : You know, to your point about if the .NET runtime doesn’t run on your system, you have slightly bigger problems. If you are only a .NET developer and the .NET runtime doesn’t run on the target system, those problems are not Cake related. That’s, whoops, we’ve targeted this system and we can’t, we’ve built this whole thing and it doesn’t run there. That’s that problem.
Mattias : Yeah, but also to that point it could be like if the .NET SDK can build for that target, you can still use Cake. So it could be you can still build .NET Framework applications with Cake even though it runs on .NET SDK. So you can target things like both Android and iOS things because you can build for those. I mean there are those scenarios too with microframework and things that you can still orchestrate that with Cake. It’s just that Cake itself needs the .NET SDK and the .NET developer tools from Microsoft.
Jamie : I guess, and this is me just posing a thought experiment. I’m not holding you or the rest of the Cake team to this. Okay, so listeners, this is just a thought experiment. Let’s say a theoretical new format, a theoretical new operating system, not just a new kernel, not just a new distribution of Linux, but a brand new operating system exists and we want to build something using .NET and we want to use Cake as the build system, the build orchestrator. All I need to do, and it’s a big all, right? There’s lots of bunny quotes around that all, but all I need to do is make the .NET runtime work on that new operating system, and then suddenly everything lights up. My application works. The build orchestration works, assuming that all the tools that it uses work on that new operating system. It didn’t have to do anything. As a .NET developer, it’s probably not in my remit to make the .NET runtime and SDK run on that new pretend operating system, but I can make it work if I work hard enough or use an LLM to help me, or maybe Microsoft will just bundle in support for that new operating system itself. So as new targets come online, my ability to run my build orchestration on different systems exponentially increases as long as the support from the runtime, as long as the runtime can run it, and as long as the SDK tools that I’m going to be using for my build orchestration work on that platform, I can target that platform.
Mattias
:
Yeah, and it could also be things like, well, we have helpers like “is running on Windows” or “is running on Unix” or “is running on Linux” or “Mac”. For a new platform, it could be like, well, your condition could be that I’m not running on a known system, which means I can have a different logic. So even if we’re not aware of that, if we can execute, you can have your conditionals regardless.
Being that you can still extend Cake because we have things we call add-ins. If there isn’t an “is running on my operating system” helper, you can write an add-in for that that extends it. Cake add-ins are just NuGet packages that you bring in and Cake will be aware of them. So you can have both add-ins which is binary, you can also have modules, but modules is also as a NuGet package, but it replaces internal behaviour, so if you want to replace the logging in Cake or the file system abstraction or things like that you can do that externally too. So we have extensibility in that way and then your possibilities are pretty much limitless then as long as something exists in the .NET ecosystem or you can make it exist in the .NET ecosystem, you can potentially add it as an add-in or a module or whatever you needed.
Jamie
:
Yeah. We have three or four hundred add-ins already. We have a lot of things built in. There’s loads of things, but there’s several things for code coverage, some code complexity thing or to some environment or to invoking another build system. There are loads of things available, but also there’s easy aliases for just executing executables or tools. So if you’re just like, oh I have this, it’s called this on Windows and this on Linux, then you can just do the command alias in Cake and execute it too to try it out.
But also it can be things like well there comes a new CI system for if you’re running on some other system or you need to read environment variables. Then the only thing you will essentially replace will be how do I start the build system and how do I for example upload artifacts. That will be different but the flow will be the same for like 90%. Which means also you can have a pipeline that runs both on, Cake itself builds on like 14 build systems now when we try it out. We have the same build script that runs on all those systems. But we all have conditionals like, oh, if it’s running on GitHub Actions, we do this. If it runs on Bitrise, we do this. If it runs on TeamCity, it does this.
But the majority is the same, which also can be really powerful. It could be sometimes with cost, especially if you do mobile development, sometimes it could be that Bitrise for one had a much cheaper macOS runner. Well then I can run there or if there’s some other, GitLab has a better support for Docker or whatever then you can use that there and have the same flow. Which is also really powerful so it’s just not cross environment, it’s cross systems too and cross operating systems.
Jamie : Right. I’m just taking one step further past this bit, but then I can imagine, and you can correct me if I’m wrong, I might even be able to take my Cake script. Can I take it and create an executable from it so I can just have my source code if I wanted to, just have a double click to build? I could have my source code and perhaps an executable and just build the application.exe and just double click that and then suddenly the build works. Is that a thing that could happen?
Mattias
:
Yeah, that actually that’s been in some ways possible. It wasn’t something we called, we have a part that’s called Cake Frosting, which is actually Cake, but more like in a feel like you’re building a build script in a ASP.NET Core style, okay. Which we introduced in 2018. But this, like this last November 2025 we introduced what’s called Cake SDK.
Cake SDK lets you do that. So essentially it’s just a .NET console application. So the .NET SDK now allows you to build containers, build tailored EXEs for one platform. So you can actually publish your build script as a ready-made EXE, container or even you can pack it as a .NET tool. So you could even have your build be a .NET tool that you just do a .NET tool restore and execute it. So that’s quite powerful.
Though the Cake SDK came, I came up with that when I saw in May last year with the .NET Preview 4 of .NET 10, they came with this file-based applications. You could have a single file and essentially that was, Cake used to be a script runner. So the Cake tool is essentially, it has a Roslyn compiler, it was fully .NET, it had to compile everything. But now with file-based applications, you can actually compile single files but also using an SDK. That was why I started writing that for us, this is an opportunity and we have a lot of advantages to that because being in the SDK we have full Visual Studio Code support out of the box. So it works with the C# Dev Kit and we can also have all those things that have come now in the SDK just like publish and container support and things like that out of the box, which is quite nice. We can focus on our code and not all of this like with the tool we needed to have our own LSP server and all that stuff. But we don’t need that any more with the SDK. It’s just a regular .NET Core console application but we have done some Roslyn code generation.
Jamie : Right and I guess with .NET 10’s single file app stuff that becomes even easier, right? Because you know, for the folks who haven’t used it, you can do .NET run file.cs, which is just, that in itself is bonkers. But behind the scenes it’s just doing a whole bunch of code generation. It’s oh you need a csproj. Well I’ll just pretend a csproj exists. There you go. Sorted. Because the csproj is pretty slim. So it’s just doing all of that stuff behind the scenes. Does that mean then if I wanted to, I can do .NET run the name of my Cake Build script.cs? It just pops up the other end.
Mattias :
Yeah, and that was, we launched in November. What we’ve done there is that we had an SDK which essentially brings in everything, all the dependencies we need for Cake. So instead of having the tool which has those dependencies, we have an SDK which brings all those. It’s just NuGet packages. So the core of Cake, the common things, and also all the dependencies like Microsoft dependency injection and things that it uses. It brings those in so you just add one line to your CS file and off you go. That’s the only thing you need to add to get everything you had in the Cake tool, you will get in a regular .NET CS file, which is more work than it seems, but when it works, it’s been a really nice experience because we do all the code generation.
So essentially you don’t need to, we generate all the namespace usings needed for Cake aliases, we will generate static helpers for things so we will actually, almost do things like, we have dependency injection working but we will generate the most common things that are just available for you out of the box. You can just type .NET build and the path of the file and it will work. Things like that. So it’s, and you have all the Cake things in what was the Cake DSL will just be available in essentially what is a top-level statement console application. We have all kinds of things and the cool thing about this, the .NET run app files is that you can also migrate those to regular csprojs if you want. So if something becomes more complex, you have the option to go up to full Visual Studio and everything, solution files, if you want.
But also it means that essentially what .NET run does is that it will create an in-memory csproj file. They have added a couple of pre-processed directives that are ignored by the regular Roslyn compiler and they will do that too. They’re actually used for metadata for that CS file. So things like package references, SDKs, MSBuild properties, and things like that. Which means that you have everything in the tooling that you used to. You can have analysers, you can have linters and even other things that will just work, which is really cool.
You know that moment when a technical concept finally clicks? That's what we're all about here at The Modern .NET Show.
We can stay independent thanks to listeners like you. If you've learned something valuable from the show, please consider joining our Patreon or BuyMeACoffee. You'll find links in the show notes.
We're a listener supported and (at times) ad supported production. So every bit of support that you can give makes a difference.
Thank you.
That’s genuinely awesome, right? Because I walked into part of this conversation with, well, most of my builds are like you said, the trivial builds. .NET build, .NET pack and then publish or something along those lines. But for either folks who have a super simple build or a super complex build that they want to then put, and it, I keep saying it, but it’s really important to really drive this point home that it is statically typed. The tooling can step in and tell you when you made a syntactical issue. You can do Ctrl+Shift+B and build and run it on your machine without pushing it up to a YAML runner. You can test the whole thing almost end-to-end on your machine, regardless of whether it’s a complex build or not. That to me is, I’m going to have to start using this for some of my more shall we say important projects because this sounds like it’s going to make my life way easier.
Mattias
:
Yeah, and it means you can reuse patterns you know. Usually what I see in some teams is that there’s one person that becomes the go-to YAML person and that means usually the one that opened the file last. Whereas there’s less context switch if you use the language you use in your daily work. So I think that’s also big.
You can do just, I can do it from the pipeline, I can do .NET packing, I can do that. But it’s usually you do it slightly different. This means you can get consistency. If you have automation in place, you have more work in the beginning but then it’s less work because then you can have things like well if I just merge to the main branch it will ship to NuGet or things like that. It’s much easier to do but you can still have the environment can steer what it’s doing, but you can test everything locally too. So it’s really easy.
We have all those, what is this environment variable named? Well, we have for most CI systems we have those as properties in there that you can access so you don’t need to know it’s underscore this or underscore that. There’s tokens from some CIs that we will parse and pass on so you can also use them and things like that. That’s 10 years of development that you don’t need to do. If something changes, we can change it in one place. Then it will just work for all your build scripts by upgrading. There’s an abstraction that has a layer and that can have issues, but also sometimes it’s nice with abstractions because you don’t have to think about things that aren’t your main expertise or something you do daily.
Jamie : Absolutely right. Abstractions, like you said, can be worrisome, but also they make things so much easier. When I’m driving, I don’t have to think that when I press on the accelerator, fuel is injected into the parts of the engine that use the fuel to then cause an explosion to drive a piston, which then is turned into rotational motion and then fed to the wheels which propel me forward. I just need to know push pedal go faster.
Mattias : Yeah, and also doing something where if you do it more consistently also if you do things like AI or where are your teams, well having something that has, AI really likes when there’s more rules to things. So I think that would be cool also if you have a strongly typed language, actually AI is better because it will know, it’s better to limit it. So it won’t go out on whatever it wants to do sometimes. Write a novel or something.
Jamie : Absolutely, absolutely. So we’ve talked a lot about how great Cake is, what Cake is, but I wonder what’s the best way to learn about it? Folks have listened to this and gone, that’s it, I’m doing this, I’m converting all my projects, and they’re in that mindset. Where are they going to learn about how to do that? How to use Cake? What the syntax is? Are they just going to Google Cake Script C# how to? Or you said earlier on cakebuild is the website. So am I going to cakebuild and all the tutorials are there?
Mattias
:
Well, cakebuild.net is the website. I think that will be a good starting point. One thing, both Tool and Frosting are still fully supported and developed, so it depends on what you’re doing there. But the new thing is the Cake SDK, and we just added loads of docs for how to get started with SDK too. We will keep adding those when we get more examples. So I think a great way to start is cakebuild.net and also we are on GitHub and the organisation is CakeBuild.
Then we also have some sample repositories if you want to look at those, because we have a lot of extension points to the SDK. So you can have things like dependency injection if you want. We have examples of how you can do if you want to split your build in multiple files. Even if .NET run at this point in time doesn’t support multiple files, actually Cake SDK does. So you can have, add an MSBuild property to include more files. But also examples for how you can do things like if you want, how do I do more complex tasks in my modules, things like that. There are examples for that.
We’ll keep adding those. It’s good to check our blog post too as soon as we introduce new features. There are, we talked about Wildebeest’s notion of recipes. Those were essentially where we can package up build scripts. That’s certainly possible because we haven’t just written the guidance for that yet. We’re working on writing guidance for you. It’s like if there’s anything that you’re wondering, we have on GitHub, on our CakeBuild organisation, there’s discussions. You can actually start a discussion if you have any questions or if you want to scroll through them. There’s the CakeBuild on Stack Overflow things like that. But I mean just hit us and ask and we can see essentially what’s needed but there’s been a lot of guidance out there on the website and we’ll add more as we go forward.
If there’s any scenario or questions please raise a discussion and we can see if it’s a good fit. We’re open source and we’re a non-profit so it’s not that we need to sell anything to anyone. We can just say state your case and often if there’s something good, well feel free to send a pull request to help us get it. Or even our documentation is open source. So if there’s something missing, you know there’s a typo, there’s just an edit button on each page so you can actually edit and send a PR straight away.
Jamie : Amazing. Amazing. Then what about if folks want to connect with you afterwards, like they want to be following you on socials or whatever? Is that just a case of go to your website and the links are there?
Mattias : So my personal website is devlead.se and there you can find links to most of my socials on Mastodon and Bluesky and GitHub and things like that. So I’m devlead.se on Bluesky and I’m @devlead@mastodon.social on there. But usually just go to devlead.se and you can find the links to most of my stuff, LinkedIn and things like that. So feel free to reach out.
Jamie : Amazing. Okay. Genuinely that’s really cool. I want to thank you, Mattias, for talking with me about Cake today because I’m genuinely walking away going, I’m about to go and do some work and I think that I might just submit a PR to bring Cake into this project because it’s, so far from our discussions it will make everything simpler and make it easier to manage. But we’ll see what happens.
Mattias
:
Yeah, sometimes even if you don’t change, it’s sometimes nice to just see what’s available for you. Then you can learn. Because we have documented, sometimes some people almost use our, because we have documented all the environment variables for GitHub Actions, people can almost look at our code to see what it’s actually doing. I mean there’s some general knowledge to be had from doing this for years, but I think just ping me if there’s something that’s not working or yeah, send either send a PR or raise an issue or a discussion and we can see where we’re going.
But also feel free to reach out to me on social if you do something because if you want to do something esoteric that we haven’t thought of, it could be sometimes easy for me to just write a blog post describing how to do that and then everyone would benefit. So don’t be a stranger in that scenario.
Jamie : Amazing. Well thank you, Mattias, for spending this time.
Mattias : Thank you for having me. It’s been great.
Jamie : Yeah, no, it really, it really has. It really has been great. You’re very welcome and thank you for being on the show. It’s, I’m really actually excited about going and replacing a whole bunch of build pipelines with something like Cake or even just looking at how Cake works. Well remember our discussions, start with one so you can get that working first.
Mattias : Send a million PRs to replace everything.
Jamie : I’m going to take the bazooka approach and just replace it all. Sounds like the developer we hate, the one that does like you know when they had the old, now we used to have reformat PRs with ReSharper PRs, so now we have the AI slop PRs. Be mindful of the ones that have to review what you’re sending them. So start small.
Mattias : Absolutely. Awesome. Well, yeah, I’ve said it three times already, but I’m really grateful, Mattias, for you spending the time with us today to talk about Cake, because I think more people knowing about more tools, like you said, it goes into your toolbox and then you can go, oh, I have this thing. This will solve this problem. Because otherwise you’re just going to shoehorn something that doesn’t solve the problem into the solution of the problem and you know yourself, being the architect and all that stuff, you know this.
Jamie : Yeah, and also even if you don’t use it, sometimes if you see something is possible with it, you know it will be possible with something else too. So it’s, that’s why I like to learn other languages and other things because that means that it’s possible. So even if you don’t use it, it can be a good learning experience to see what’s possible with something. So take it for a spin and feel free to let us know what you think about it or if there are any bugs or things that you think we can improve.
Mattias : Amazing. Well thank you very much. It’s been fantastic chatting with you.
Jamie : Thank you.
Wrapping Up
Thank you for listening to this episode of The Modern .NET Show with me, Jamie Taylor. I’d like to thank this episode’s guest for graciously sharing their time, expertise, and knowledge.
Be sure to check out the show notes for a bunch of links to some of the stuff that we covered, and full transcription of the interview. The show notes, as always, can be found at the podcast's website, and there will be a link directly to them in your podcatcher.
And don’t forget to spread the word, leave a rating or review on your podcatcher of choice—head over to dotnetcore.show/review for ways to do that—reach out via our contact page, or join our discord server at dotnetcore.show/discord—all of which are linked in the show notes.
But above all, I hope you have a fantastic rest of your day, and I hope that I’ll see you again, next time for more .NET goodness.
I will see you again real soon. See you later folks.
Useful Links
- Cake Build
- Mattias’ links:
- Supporting the show:
- Getting in touch:
- Podcast editing services provided by Matthew Bliss
- Music created by Mono Memory Music, licensed to 193 for use in The Modern .NET Show
- Editing and post-production services for this episode were provided by MB Podcast Services