Skip to content
kc@kumarChandrachooda.com:~$ cd /blog/two-workshops-that-forked && read --section="top" 0%
Architecture

Two Workshops That Forked

Inflow ships the same exercise twice, on two branches prepared eleven days apart from the same commit. They do not start from the same place, neither converges on master, and both carry a README describing components they have deleted.

By Kumar Chandrachooda 09 Feb 2026 6 min read
Two branches leaving one point and never meeting again

Part 6 read one workshop branch as a lecture recording. There are two. origin/workshops and origin/warsztaty — the second word is Polish for “workshops” — branch from the same commit, 95fe438, and set up the same exercise: delete the cross-module integration, hand the repository to a room, have them build it back.

They were prepared eleven days apart. They do not start from the same place, and neither of them ends up at master.

The same exercise, twice

a28cb75, Thursday 3 March 2022 at 12:28. Twelve files, 316 deletions, no insertions:

Deleted Module
CreateCustomer, CreateCustomerHandler, the POST /customers action Customers
SignedUp, SignedUpHandler Customers
UserStateUpdated, UserStateUpdatedHandler Customers
The contract registrations in CustomersModule Customers
CustomerVerified, CustomerVerifiedHandler Payments
DepositCompleted, DepositCompletedHandler Wallets

That is a clean, well-chosen exercise. It removes exactly the seam — the events crossing module boundaries and the handlers on the far side — and leaves everything else intact, including the Saga, the logging decorators and the full Wallets onion. A student clones this and has four working modules that ignore each other, plus a README explaining that the modules integrate through events.

4b36951, Monday 14 March at 18:18. Twenty-six files, 641 deletions, four insertions. It removes everything a28cb75 removes, and then keeps going.

What the second one takes as well

git diff a28cb75 4b36951 is fifteen files and −326 lines. Beyond the shared exercise, warsztaty also deletes:

  • The entire Saga implementation. NewCustomerBonusFundsSaga (98 lines), SagaEventHandler, and all five files under Messages/AddFunds, CustomerVerified, DepositCompleted, FundsAdded, WalletAdded.
  • All three logging decoratorsLoggingCommandHandlerDecorator, LoggingEventHandlerDecorator, LoggingQueryHandlerDecorator — plus Logging/Extensions.cs and the line in Shared.Infrastructure/Extensions.cs that registers them.
  • CustomerCreated.cs, the event that part 2 found is published by SignedUpHandler and consumed by nobody.
  • The four appsettings.json booleans covered in the last part.

And one more difference, which I include because it is the kind of thing that only shows up when you diff two things nobody expected you to diff. The two branches' CustomersController.cs differ by exactly this:

         return NotFound();
     }
-
+        
     [HttpPut("complete")]

A blank line acquiring eight characters of trailing whitespace. That is the entire difference between the two exercises in that file — an editor's auto-indent, committed, eleven days apart, by the same person preparing the same lesson twice.

The substantive divergence is not cosmetic though, and it changes what is being taught. In the English exercise the room builds the module seam inside a system that still has a saga orchestrating across it. In the Polish one the saga is gone, so the room builds the seam in isolation and never sees what sits on top of it. Two workshops, ostensibly the same exercise, forked into two different exercises eleven days apart — and nothing anywhere records that they differ, or why.

The README that describes a deleted system

Both branches carry a README.md byte-identical to master's. I checked with git diff master <branch> -- README.md; both return empty.

So on origin/warsztaty, a reader is handed a document that says:

Saga

Sample Saga pattern implementation for transactional handling the business processes spanning across the distinct modules.

against a branch with no Sagas/ folder, no Messages/ folder and no saga class. And on both branches the same document says the modules integrate through events, against a state in which every cross-module event handler has been deleted.

This is the cleanest example of documentation drift in the estate, and its mechanism is worth naming precisely: the README did not drift. The code moved out from under it. A branch is a copy of every file, including the documentation, and nothing in git flags a doc that is now describing a different tree. Of the sixteen accuracy findings I logged across this repository, this is the only one where the document is word-for-word correct about one branch and materially wrong about the branch it is actually on.

Even the solution does not converge

origin/warsztaty ends with completed flow. It is the only branch in the repository that contains a worked solution, so the obvious expectation is that its tip equals master.

It does not. git diff origin/warsztaty master is 32 files. Strip out the .csproj package bumps that master picked up in July and what remains is that the Saga is still deleted, the three logging decorators are still deleted, and CustomerCreated.cs is still gone. The Saturday session rebuilt the module seam, which was the lesson, and never restored the two things that had been cut only to reduce noise. There are also small shape differences left over from live coding — the handler file still named UserStateUpdated.cs rather than UserStateUpdatedHandler.cs, and a slightly different DepositCompletedHandler.

Nothing is wrong with that. The branch achieved what it was for. But it means the repository contains a branch a reader would reasonably take as “the answer”, which is not the answer, and no document says so.

The multilingual question, answered

An estate with a Polish-named branch invites an obvious assumption, and it is worth killing it with the search that establishes it.

git grep -licP '[ąćęłńóśźżĄĆĘŁŃÓŚŹŻ]' <ref> -- '*.cs' '*.md' '*.json' '*.rest'

Run against master, origin/workshops, origin/warsztaty and origin/microservices, it returns zero files every time. There is not one Polish identifier, comment, log message, README line, commit subject or user-facing string anywhere in 540 tracked files. Every commit message on warsztaty is English: create customer, signed up, user updated, completed flow. The one exception is the branch's own name, and 4b36951's subject, which is that name again.

The natural-language boundary in this estate falls at the branch name and nowhere else. There are no diacritics to mis-encode, so there is no encoding hazard, no collation question, no tooling risk. The asymmetry is real but it is not linguistic — it is pedagogical. The Polish-named branch got the recorded solution, five commits of live coding on a Saturday. The English-named branch got only the exercise.

Branch-as-variant, and how it decays

Step back and this is a repository that ships its alternatives as six long-lived branches rather than directories, tags or feature flags, and the README explicitly sends readers to one of them. Four of the six are live variants rather than history: net5, net5-microservices, microservices, and the two workshops.

None is ever merged. None is ever updated after its authoring day. master moved on 23 July 2022 with a package update, and not one of the four received it — which is why git diff origin/warsztaty master is padded with .csproj version bumps that have nothing to do with either workshop.

That is the structural cost of the pattern, and it is worth stating as a rule: a variant kept as a branch decays silently, because nothing about it is visible from the branch anybody actually works on. A variant kept as a directory shows up in every grep, every solution file and every search. A variant kept as a tag is explicitly frozen and reads as frozen. A long-lived branch reads as live and behaves as dead, and the README's own pointer at microservices is written in exactly that confident present tense.

Two of those four variants exist because of a framework upgrade. The next part reads the one that made it onto master: 519 files of .NET 6 migration, of which almost none of it is a migration. The upgrade that only moved braces.