<?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.us-east-2.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>The scarce resource is not tokens, it is my attention — why I built ccdeck</title>
      <dc:creator>Bargan Constantin</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:01:28 +0000</pubDate>
      <link>https://dev.to/bargan/the-scarce-resource-is-not-tokens-it-is-my-attention-why-i-built-ccdeck-4pik</link>
      <guid>https://dev.to/bargan/the-scarce-resource-is-not-tokens-it-is-my-attention-why-i-built-ccdeck-4pik</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I build &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;ccdeck&lt;/a&gt;, a local dashboard for Claude Code and Codex sessions. This is the story behind it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Four agents running, and the machine has been quiet for twenty minutes.&lt;/p&gt;

&lt;p&gt;One of the four had stopped to ask me something — a permission prompt for a shell command — and I had not seen it go by. From the outside every terminal tab looked the same: the one that was working and the one that had been holding a prompt since I went for coffee. I found it by clicking through the tabs, and of course it was the agent that had been closest to finished.&lt;/p&gt;

&lt;p&gt;The agent was not slow. I was. And nothing in my setup could tell me that. That situation is the one ccdeck was built for.&lt;/p&gt;




&lt;h2&gt;
  
  
  A tree, drawn as a scroll
&lt;/h2&gt;

&lt;p&gt;The second problem is wider. An agent session is a &lt;strong&gt;tree&lt;/strong&gt;: a main agent, subagents it spawns, tool calls under each of them. A terminal shows that tree as a &lt;strong&gt;scroll&lt;/strong&gt;. Five subagents working in parallel arrive as one interleaved column of text.&lt;/p&gt;

&lt;p&gt;The questions I actually had were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what is running right now?&lt;/li&gt;
&lt;li&gt;what did that subagent do?&lt;/li&gt;
&lt;li&gt;which one is stuck?&lt;/li&gt;
&lt;li&gt;what is this costing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are exactly the questions a scroll answers worst. So the first version of ccdeck did one thing: it drew the tree.&lt;/p&gt;




&lt;h2&gt;
  
  
  Day one
&lt;/h2&gt;

&lt;p&gt;The first commit is dated &lt;strong&gt;12 June 2026&lt;/strong&gt;: a live graph of Claude Code agents, fed by Claude Code's own hooks. Versions 0.1 to 0.6 are all commits from that same day — richer nodes, a detail view for tool calls, persistence, token usage.&lt;/p&gt;

&lt;p&gt;By the end of September the repository had a little over 3,000 commits and about 390 tagged releases. It grew Codex support, cost and quota, several Claude accounts, a desktop app, a machine panel. But the thing I use most is still the narrowest one.&lt;/p&gt;




&lt;h2&gt;
  
  
  The wait is the metric
&lt;/h2&gt;

&lt;p&gt;Tokens matter, and ccdeck shows them. But tokens were never what I ran out of. &lt;strong&gt;I ran out of attention.&lt;/strong&gt; The expensive part of that afternoon was not the API bill; it was twenty minutes of an agent waiting for me while I did not know.&lt;/p&gt;

&lt;p&gt;So the sharpest job of the deck became one sentence: &lt;strong&gt;say which agent is blocked on a human, and for how long.&lt;/strong&gt; Every session stopped on a permission prompt, a question or a finished turn goes to the top of the list, longest wait first, and the count in the topbar jumps to the oldest one. The question stops being &lt;em&gt;which terminal tab&lt;/em&gt; and becomes &lt;em&gt;this one&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Everything else on the canvas is context for that sentence.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I decided it must never do
&lt;/h2&gt;

&lt;p&gt;There are four rules I keep for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Measure, do not steer.&lt;/strong&gt; A hook that runs on every tool call could, in principle, allow, deny or rewrite that call. ccdeck's hook cannot: it posts the event to the deck on &lt;code&gt;127.0.0.1&lt;/code&gt;, prints nothing and always exits &lt;code&gt;0&lt;/code&gt;, and a test checks both the source and the running script for exactly that. A dashboard you install on every tool call should not be able to become a participant. The README's sentence for it is the one I hold myself to: &lt;em&gt;it never steers an agent or edits your code, but it is not read-only either&lt;/em&gt; — it manages two tools it relies on and refreshes the Codex token it reads quota with, and says so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Name the blindness.&lt;/strong&gt; When the deck cannot know something, it says so instead of guessing quietly. Codex records no approval request in its log, so a Codex session is never shown as waiting — and the docs say that, rather than leaving the queue to look complete. The tool name next to a wait is an inference, so it is labelled as one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It must work from a cold &lt;code&gt;npx&lt;/code&gt;.&lt;/strong&gt; No config file, no account, nothing to read first. &lt;code&gt;npx ccdeck&lt;/code&gt; and a page opens. The npm package has no runtime dependencies at all; anything the deck fetches for itself, it fetches after it is already up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Local first, and honest about what is not.&lt;/strong&gt; Your sessions, prompts, files, project names and paths never leave the machine. What does go out is listed in the README: a version check, the tools it installs, quota reads signed with your own credentials, status pages — and usage reports about the deck itself (version, system, IP address, a device fingerprint, errors), on by default, with one switch to turn them off. I would rather you read that list than a slogan.&lt;/p&gt;




&lt;h2&gt;
  
  
  Things I got wrong
&lt;/h2&gt;

&lt;p&gt;A few, because they taught me more than the things that worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hook was slower than its own timeout.&lt;/strong&gt; The hook declared a 2-second timeout to Claude Code, and its internal deadline started when its &lt;code&gt;main()&lt;/code&gt; ran. Measured against a stalled deck, the whole process took 1.84 s idle and up to 2.19 s on a loaded machine — the shell fork, Node's startup and reading a large payload all happened outside the budget. Now the cap counts from process start, the declared timeout is a 3-second backstop, and a test pins the two numbers against each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A second deck nobody asked for.&lt;/strong&gt; Typing &lt;code&gt;ccdeck&lt;/code&gt; beside a deck that was already running used to start a second one: port 4317 was busy, so the new deck took a random port, and neither mentioned the other. Now a plain &lt;code&gt;ccdeck&lt;/code&gt; opens the running deck's tab — after the deck on that port proves it is yours with the same token handshake the hook uses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Temperatures that do not exist.&lt;/strong&gt; I wanted a temperature row on every machine. On a Windows 11 laptop with an Intel i5-9300H, the firmware declares no thermal zones at all, and the one source that has the sensors refuses anyone but an administrator. There is no standard user-mode API for CPU temperature on Windows. So the deck shows a Thermal section only where the machine actually answers, and no row at all where it does not.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;p&gt;ccdeck is an instrument panel and a flight recorder, not an orchestrator. It does not schedule agents, assign work or supervise anything. &lt;em&gt;One canvas, no tabs, no kanban&lt;/em&gt; is a constraint I keep on purpose: the moment a feature wants a tab bar, it is asking for the canvas.&lt;/p&gt;

&lt;p&gt;It cannot answer a prompt for you either. You still switch to the terminal. It tells you which one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it is now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ccdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the desktop app for macOS, Windows and Linux, with the waiting count in the menu bar. Claude Code and Codex CLI on one canvas, local, AGPL-3.0, and every guide on &lt;a href="https://ccdeck.dev/guides/" rel="noopener noreferrer"&gt;ccdeck.dev&lt;/a&gt; says which version it was checked against.&lt;/p&gt;

&lt;p&gt;If you run more than one agent at a time, I would like to know what you lose track of. Issues and discussions are open:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/BarganConstantin/ccdeck" rel="noopener noreferrer"&gt;https://github.com/BarganConstantin/ccdeck&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discussions: &lt;a href="https://github.com/BarganConstantin/ccdeck/discussions" rel="noopener noreferrer"&gt;https://github.com/BarganConstantin/ccdeck/discussions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Site: &lt;a href="https://ccdeck.dev" rel="noopener noreferrer"&gt;https://ccdeck.dev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>claudecode</category>
      <category>ai</category>
    </item>
    <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>
