<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bargan Constantin</title>
    <description>The latest articles on DEV Community by Bargan Constantin (@bargan).</description>
    <link>https://dev.to/bargan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3543196%2F9791d07a-3a9b-4467-89ac-f71870243f93.png</url>
      <title>DEV Community: Bargan Constantin</title>
      <link>https://dev.to/bargan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bargan"/>
    <language>en</language>
    <item>
      <title>From 15 Minutes to 30 Seconds: Cutting Build Time in a Large .NET Project</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Mon, 06 Oct 2025 11:09:49 +0000</pubDate>
      <link>https://dev.to/bargan/from-15-minutes-to-30-seconds-cutting-build-time-in-a-large-net-project-4m83</link>
      <guid>https://dev.to/bargan/from-15-minutes-to-30-seconds-cutting-build-time-in-a-large-net-project-4m83</guid>
      <description>&lt;p&gt;&lt;em&gt;Tired of waiting forever for your .NET builds? Our project had 400+ EF Core migrations and builds took 15 minutes — until we tried these two fixes.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When you start a new .NET project, everything feels smooth — builds are fast, migrations are few, and iteration is quick. But as projects evolve into long-term, business-critical systems, reality starts to look very different.&lt;/p&gt;

&lt;p&gt;In one of the projects I’m working on, we’ve accumulated more than &lt;strong&gt;400 EF Core migrations&lt;/strong&gt; over several years of development. On paper, this doesn’t sound too bad — migrations are just C# files, right? But in practice, the build process became painfully slow:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Even with solid hardware, a full build took &lt;strong&gt;10–15 minutes&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Small surface-level changes still needed &lt;strong&gt;3–4 minutes&lt;/strong&gt; to build.
&lt;/li&gt;
&lt;li&gt;Changes in the application or domain layer (where entities live) triggered &lt;strong&gt;full rebuilds&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, the development process looked like this: you could realistically build and test &lt;strong&gt;3–4 times per hour&lt;/strong&gt;. And as every .NET developer knows, waiting on builds is one of the fastest ways to lose focus and momentum.  &lt;/p&gt;

&lt;p&gt;So I started digging into ways to optimize this. What I discovered turned out to be surprisingly simple at first — and then led me to a more sustainable long-term solution.  &lt;/p&gt;

&lt;p&gt;In this article, I’ll share:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;quick win&lt;/strong&gt; you can apply in minutes to cut build time drastically.
&lt;/li&gt;
&lt;li&gt;A more &lt;strong&gt;elegant solution&lt;/strong&gt; with a dedicated migrations project.
&lt;/li&gt;
&lt;li&gt;Some thoughts on the &lt;strong&gt;bigger picture&lt;/strong&gt; (modular monoliths, legacy realities, microservices).
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;EF Core migrations are useful and necessary — they keep your database schema in sync with your evolving domain. But in very large projects, they can also become a hidden bottleneck.  &lt;/p&gt;

&lt;p&gt;Here’s what we found when analyzing why our builds had become painfully slow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every migration is just C# code:&lt;/strong&gt; EF migrations are stored as generated &lt;code&gt;.cs&lt;/code&gt; files. When you have a handful, it’s fine — but with &lt;strong&gt;hundreds of migrations&lt;/strong&gt;, every build must recompile all of them.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep changes trigger everything:&lt;/strong&gt; Modifying code in the domain layer means the compiler re-checks all dependencies — migrations included. Even a small change can cause a full rebuild.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Builds become painfully long:&lt;/strong&gt; With &lt;strong&gt;400+ migrations&lt;/strong&gt;, a clean build can take &lt;strong&gt;10–15 minutes&lt;/strong&gt;, even on powerful hardware.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer experience suffers:&lt;/strong&gt; Productivity drops fast. Developers start avoiding builds “just to save time,” which delays feedback loops and slows the whole team down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, in theory, you wouldn’t end up here. &lt;strong&gt;Bounded contexts, modular monoliths, or even microservices&lt;/strong&gt; could have prevented such a massive DbContext. But in real life, many long-term projects grow this way — often under business pressure where refactoring isn’t a priority.  &lt;/p&gt;

&lt;p&gt;So instead of a rewrite, we needed &lt;strong&gt;practical fixes&lt;/strong&gt; that worked immediately.  &lt;/p&gt;




&lt;h2&gt;
  
  
  First Solution: Exclude Migrations from Build
&lt;/h2&gt;

&lt;p&gt;The first discovery was almost embarrassingly simple.  &lt;/p&gt;

&lt;p&gt;If you’re &lt;strong&gt;not actively working on migrations&lt;/strong&gt;, you don’t need them compiled every time. In .NET, you can exclude files or folders from the build inside your &lt;code&gt;.csproj&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Compile&lt;/span&gt; &lt;span class="na"&gt;Remove=&lt;/span&gt;&lt;span class="s"&gt;"Migrations\**\*.cs"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. Just one line.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Results
&lt;/h3&gt;

&lt;p&gt;As soon as we added this, build times dropped dramatically:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From &lt;strong&gt;10–15 minutes&lt;/strong&gt; → to &lt;strong&gt;30–60 seconds&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Incremental builds felt instant again.
&lt;/li&gt;
&lt;li&gt;Developer flow was restored.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first build after excluding migrations felt almost magical. Of course, the trade-off is obvious: if you need to create or apply migrations, you’ll have to temporarily remove the exclusion. But migrations are usually touched less frequently, so this trade-off is worth it.  &lt;/p&gt;




&lt;h2&gt;
  
  
  More Elegant Solution: A Dedicated Migrations Project
&lt;/h2&gt;

&lt;p&gt;Excluding migrations is great for a quick fix, but it’s not the cleanest long-term strategy. Constantly toggling your &lt;code&gt;.csproj&lt;/code&gt; can be annoying.  &lt;/p&gt;

&lt;p&gt;A better approach is to &lt;strong&gt;move migrations into a separate project&lt;/strong&gt;.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Why a Separate Migrations Project?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keeps the main project lean — no extra compilation overhead.
&lt;/li&gt;
&lt;li&gt;Improves separation of concerns — domain code stays clean.
&lt;/li&gt;
&lt;li&gt;Optional compilation — build migrations only when you actually need them.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to Do It
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a new Class Library (e.g., &lt;code&gt;MyApp.Migrations&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;Move your &lt;code&gt;Migrations&lt;/code&gt; folder into this project.
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a reference to the domain project that contains your &lt;code&gt;DbContext&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ProjectReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"..\MyApp.Domain\MyApp.Domain.csproj"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update EF Core commands to use the new project:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet ef migrations add Init &lt;span class="nt"&gt;--project&lt;/span&gt; MyApp.Migrations &lt;span class="nt"&gt;--startup-project&lt;/span&gt; MyApp.Web
dotnet ef database update &lt;span class="nt"&gt;--project&lt;/span&gt; MyApp.Migrations &lt;span class="nt"&gt;--startup-project&lt;/span&gt; MyApp.Web
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, your migrations live in their own project, isolated from your main application builds. You only compile them when you actually need them.  &lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;This is where the architecture conversation comes in.  &lt;/p&gt;

&lt;p&gt;Yes, ideally, you wouldn’t have hundreds of migrations in a single DbContext. Modern approaches like &lt;strong&gt;modular monoliths&lt;/strong&gt; encourage splitting the system into modules — each with its own DbContext and migrations. That way, you get strong boundaries without the full complexity of microservices.  &lt;/p&gt;

&lt;p&gt;In hindsight, adopting a modular monolith could have kept our migrations more manageable over time. But for a legacy project, it’s not always realistic to refactor the entire architecture just to speed up builds.  &lt;/p&gt;

&lt;p&gt;That’s why small steps like excluding migrations or moving them into a separate project can be game-changers. They don’t erase architectural debt, but they give developers back their time.  &lt;/p&gt;

&lt;p&gt;In our case, we went from:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3–4 builds per hour → to as many as needed without frustration.
&lt;/li&gt;
&lt;li&gt;Developers stopped avoiding builds “just to save time.”
&lt;/li&gt;
&lt;li&gt;The feedback loop became fast again, which improved both focus and morale.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Long build times silently kill productivity. In our case, EF Core migrations were the culprit — and the fixes were surprisingly simple.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exclude migrations from build when you don’t need them.
&lt;/li&gt;
&lt;li&gt;Move migrations into their own project for a sustainable long-term solution.
&lt;/li&gt;
&lt;li&gt;Keep modular monoliths or bounded contexts in mind to prevent this issue in the future.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These changes helped us cut build times from &lt;strong&gt;15 minutes to under 1 minute&lt;/strong&gt; — and restored developer flow in a project that had grown massive over the years.  &lt;/p&gt;




&lt;p&gt;💬 &lt;strong&gt;What about you?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Have you faced similar issues with EF Core migrations in large projects?&lt;br&gt;&lt;br&gt;
Did you solve them with modular monoliths, microservices, or something else entirely?  &lt;/p&gt;

&lt;p&gt;I’d love to hear your experience in the comments 👇  &lt;/p&gt;




&lt;h3&gt;
  
  
  ✍️ Author Note
&lt;/h3&gt;

&lt;p&gt;Appreciate you reading this! I’m a software developer and computer science engineer exploring .NET, architecture, and productivity. I might be wrong sometimes — and that’s why I’d love to hear your feedback or alternative approaches in the comments.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>database</category>
    </item>
  </channel>
</rss>
