Skip to content

T01, one line through ten stages - #39

Merged
tamnd merged 1 commit into
mainfrom
lesson-t01
Sep 6, 2026
Merged

tamnd merged 1 commit into
mainfrom
lesson-t01

Conversation

@tamnd

@tamnd tamnd commented Sep 6, 2026

Copy link
Copy Markdown
Owner

The first lesson of Part I. It takes one line of C#, public static int Count(string line) => line.Split(' ').Length;, and follows it all the way down.

Ten stages. Five of them happen on the machine that runs the build and five happen on the machine that runs the program, and the split between those two halves is the thing the lesson is actually for. Almost everything people find confusing about .NET sits in that gap.

What is in it

Fourteen blocks, all E0. No C++, no debugger, no privilege a reader does not already have. It is System.Reflection.Metadata and System.Reflection, both of which ship in the box, pointed at a small fixture program next door.

The evidence for each stage comes out of the file or out of the live process rather than out of my memory. The overload the compiler picked is read back out of the signature blob. The claim that there is no machine code in the assembly is the ILOnly flag and a zero sized directory, both printed. The claim that the launcher contains none of your code is two byte searches over the launcher and two over the assembly. The claim that the JIT runs on first call is a timing, and the claim that a line with no new in it allocates five objects is the per thread allocation counter, measured one allocation at a time so the arithmetic on the page comes from the machine rather than from me.

Three prediction gates: what the CPU field in the header says, how many arguments the IL call passes, and how many objects reach the heap. Every option carries a written explanation, including the wrong ones.

One diagram, docs/diagrams/ten-stages.dg, with the build row along the top and the run row along the bottom.

The four dropped blocks

Four blocks are marked capture=drop, so the page cannot quote them. The file sizes move with every SDK. The JIT timings are a function of the machine and the weather. The type handles and the entry point are addresses in one process on one run. The runtime version and platform are different by definition.

Each of those carries assertions instead, ten in total, and every claim has a written reason. The load bearing one is the first one cost at least ten times the rest. Ten is a long way below what any machine here actually shows, which is a few hundred, and it is set low on purpose so a busy shared runner does not turn a true statement red.

The analyser change

lessons/Directory.Build.props turns off the trimming, AOT and single file analysers.

A file based app has them on, because the point of one is that dotnet publish can turn it into a native binary. A lesson is never published. It runs once during the build and once for a reader, and a good deal of what these lessons exist to show is done through reflection, which is exactly what those analysers warn about. Left on, this lesson printed five warnings before it printed anything a reader wanted, and warnings nobody can act on are how people learn to scroll past warnings.

A lesson that is about trimming or AOT turns them back on in its own directory, and Part IX is where that happens.

Checked

xray build, xray check, xray numbers, xray assert, xray lint and dotnet format --verify-no-changes all clean locally on osx-arm64.

Also run on server3, linux-x64, against the committed expected output, because a first lesson with nine captured outputs is exactly the kind of thing that turns out to differ on the second platform. It matched byte for byte. The remaining two platforms are on CI.

Not in this PR

No runtime: citations, because pin.json still holds a null commit and this repository refuses a citation it cannot resolve to one. Everything claimed here is either a claim about the file format, checkable against ECMA-335, or a claim about behaviour, checkable against the output on the page. Z01 through Z03 are waiting on the same pin, which is why the tour starts at T01 rather than at the top of the issue.

No boss fight. T10 is the chapter boss for Part I.

The first lesson of the tour. It takes one line of C#, follows it through
the five stages that happen on the build machine and the five that happen
on the machine running the program, and shows the evidence for each one
out of the file or out of the live process.

Fourteen blocks, all E0, no C++ and no debugger. Four of them are dropped
from the page because what they print is a property of the machine rather
than of .NET, and each of those carries assertions instead.

Also turns off the trimming, AOT and single file analysers for lessons.
A lesson is never published, half of what these lessons do is reflection,
and left on they printed five warnings ahead of the first line of output.
A lesson about trimming turns them back on in its own directory.
@tamnd tamnd added this to the M2 The tour milestone Sep 6, 2026
@tamnd tamnd added priority/p0 Blocks the current milestone area/pedagogy Sequencing, the beginner ramp and assessment labels Sep 6, 2026
@tamnd
tamnd merged commit 580a568 into main Sep 6, 2026
22 checks passed
@tamnd
tamnd deleted the lesson-t01 branch September 6, 2026 00:11
This was referenced Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/pedagogy Sequencing, the beginner ramp and assessment priority/p0 Blocks the current milestone

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant