How Effectively Agents Write D

Paolo Invernizzi paolo.invernizzi at gmail.com
Fri Nov 6 17:07:21 UTC 2026


On Friday, 6 November 2026 at 15:23:33 UTC, Peter Alexander wrote:

> Keen to hear any and all feedback. Also interesting in what 
> other ideas people have for how to measure D for agents.

Perhaps this is disrespectful - if so, I apologize in advance - 
but here is our experience; *claude-code* itself provides the 
answer (based on its own memories and rules).

Hi Peter,

thanks for measuring this instead of arguing it. One data point 
from a production codebase, since your set is synthetic: 
DeepGlance makes eye-tracking and computer-vision products, in D 
since 2016, and this year most of our new D was written by Claude 
agents (mostly Opus) under review: about a thousand D files 
across six repos, cross-built from a Mac to Pi Zero (armv7 and 
aarch64), Jetson Orin and FreeBSD jails. Your top findings match 
what we see, with some differences in where the turns go.

1. Assertion failures without values. Same tax here, and we never 
turned on -checkaction=context either; our test verb runs the 
runtime default. The agents' own workaround is visible in the 
code: about one assert in four carries a hand-written message, 
and the rest cost a writeln round when they fail. So yes, the 
default should change. One heads-up if it becomes the dub test 
default: with -checkaction=context the frontend lowers an assert 
whose operand has side effects into a comma expression early 
enough that -H and -vcg-ast print an in-contract as "in ((bool 
__assertOp3 = g(x);))". We hit it on 24 druntime/Phobos modules 
under LDC's unittest flags. Header generation only, but worth a 
fix before the flip.

2. dub's unittest configuration. Your second papercut is zero for 
us because the agents never see a build configuration: our build 
tool wraps "ldc2 -i -unittest -main -run module.d" behind one 
verb, test this module, and the tests live beside the code. That 
one-liner belongs in any D skill for agents. It also exposes a 
bias in the setup: by asking for tests in separate files you 
measured the un-idiomatic layout. With unittest blocks beside the 
code, testing one module compiles that module's import closure 
and nothing else, and that is where D's fast-loop claim actually 
lives. A second arm with the idiomatic layout would tell whether 
the 1.38x in turns survives it.

3. Attributes the agent adds itself. Confirmed, and curable with 
one line in CLAUDE.md: no attribute unless the module's purpose 
needs it. Where they are load-bearing we want the refusals: on 
the Pi fleet and in ILP32 builds, nothrow and @nogc on a few 
paths keep the binary alive, and the compiler telling the agent 
that Clock.currTime can throw on a 32-bit time_t is review we 
would otherwise do by hand. In our shared library the counts are 
roughly nothrow 740, @nogc 470, @safe 120 over some 2,200 
functions; @safe is rare because the code is ImportC-heavy. We 
also run -preview=rvaluerefparam where it matters, for the reason 
in your table. On inferring attributes for non-template 
functions: the usual objection, that the interface then drifts 
with the body, hits agents harder than people, because an agent 
trusts the signature and does not read the body.

4. NaN. We use it as the deliberate "no value" of a measurement 
and the agents handle it once told, which is the point: your SAT 
case is an untold agent. A diagnostic on a read of a 
NaN-initialised float would be the structural fix; a line in the 
skill is the cheap one.

5. Imports. Our policy is non-selective imports, "import 
std.algorithm;", because a selective list costs the agent an 
edit-build round the first time it uses a symbol not on the list. 
Small, but it is the kind of default that only shows at agent 
speed.

What your set does not measure, and where our agents actually 
spend their turns:

- ImportC. Our bindings to CUDA, TensorRT, FFmpeg, 
libdatachannel, OpenSSL and libcamera go through ImportC, and 
that is the papercut mine: pragma attribute(push, nogc, nothrow) 
regions, POSIX headers included inside them merging structs with 
core.sys.posix, a size_t re-exported by a C module clashing with 
the D one, the headers of one library version against the .so of 
another. A langbench problem that binds a C library (sqlite, 
zlib) would compare D's ImportC against Go's cgo on fair ground, 
and both are where real projects bleed.

- The compiler as oracle. Our code leans on Design by 
Introspection and on CTFE, and both put the compiler in the 
reviewer's chair. A component looks at what the type it gets 
provides, with static if and __traits, so one generic module 
covers every combination and a missing capability is a build 
error with the name of what is missing. Our SQL is checked at 
compile time against a declared schema twin (a Postgres parser 
running in CTFE), so an agent learns of a bad query from the 
build, not from a deploy to a jail. More invariants in the 
compiler means fewer runtime loops, a lever Go does not have; the 
price is CTFE time, which is real. Problems whose spec can be 
partly checked at compile time are where I would expect D to win 
on turns, not lose.

- Wall time. On our larger binaries the fresh ldc2 build is the 
slow step of a turn, not the model. Your plan to time the loop 
will matter more than tokens.

One more data point on agents reading D: this autumn our agents 
ported the LDC 1.43.0 frontend to TypeScript 
(github.com/DeepGlance/djs), verified byte for byte against ldmd2 
on the dmd test suite shipped with LDC and on every 
druntime/Phobos module, plain and with -unittest. The method 
lesson applies to your study: the agents were only as good as 
their oracle, and D's test corpus was an excellent one. The tool 
may also serve your skills idea: it is a frontend that 
re-analyses only the changed modules and replays the rest, built 
for the editor loop; a "check before build" verb on top of it is 
a cheap experiment for cutting agent turns.

If useful, we keep a short rules file for our agents (assert vs 
enforce, struct vs class, the -i one-liner, the flags that bite) 
that I can share as a seed for a community D skill, and I would 
run a subset of langbench on Opus with it as the extra arm.



More information about the Digitalmars-d mailing list