Teaching LLMs to Write Better Nim
I have been working on a public set of Nim SKILL.md files for coding
assistants:
https://github.com/planetis-m/skills
They are not meant to make an LLM "know Nim" in some grand sense. They are small files that tell the assistant what to pay attention to when it is doing a specific kind of work: ownership hooks, API design, tests, C bindings, wrappers, docs, fuzzing, debugging, error handling, and code organization.
The motivation is simple. LLMs can write useful code, but they also drift. They half-remember language rules, invent API names, miss compiler-specific semantics, and generate code that compiles while still being the wrong shape. For a niche language like Nim, that matters even more. There is less training data, fewer examples, and a lot of important knowledge lives in compiler behavior, standard library conventions, and experienced programmers' habits.
A skill file is my attempt to write those habits down in a form an assistant can actually use.
The Useful Part of the Quantum Error Code Analogy
The video "Are SKILL.md files the Quantum Error Codes of Industrial AI?", published by Discover AI on April 9, 2026, makes an interesting comparison. It argues that industry wants reproducibility, risk management, and traceable outputs, while LLMs are probabilistic systems with nonzero hallucination rates. Skill files, tools, scripts, retrieval systems, and workflows become a kind of scaffold around that probabilistic core.
The quantum error correction analogy is useful because it gives a picture for what skill files are trying to do:
we are trying to force a messy space of possible model behavior through a smaller number of checked, named, repeatable patterns.
For coding, I do not need the assistant to be creative about how Nim ownership
hooks work. I need it to classify the ownership model, write the right hook set,
avoid a custom =sink unless there is a real reason, use =destroy(x: T),
and mark =copy as an error for move-only owning types.
That is not intelligence. It is discipline.
The video is also right to point out the uncomfortable part: the scaffold may be deterministic text, but the model executing it is still probabilistic. A skill file does not make the assistant safe. It gives the programmer another lever for review.
That distinction is important for how I think about these files.
This Is Not "Vibe Coding"
The term "vibe coding" is too imprecise to be useful here. It usually implies little oversight: ask for software, accept the output, maybe poke it until it runs. That is not what I am doing, and it is not what experienced Nim programmers using LLMs productively are doing.
Andreas Rumpf has described using LLMs as a code generator to produce large amounts of code needed to bring Nimony toward feature parity with Nim 2. The important detail is that the algorithms and designs remain his. The generated code is expected to be close to what he would have written by hand.
That is the distinction I care about:
- the LLM can produce code;
- the programmer owns the design;
- the compiler, tests, and review decide whether the code is acceptable.
Calling all LLM-assisted code "ethically dubious" or inherently low quality misses the point. Bad code is the responsibility of the person who ships it. The tool can make mistakes, but the author still has to review the result, run the tests, and maintain the code later.
The real question is not whether LLMs are pure or impure. The useful question is whether we can make LLM-assisted programming more reviewable, more idiomatic, and less wasteful.
That is where skill files fit.
How the Skills Are Built
The repo is organized around a refinement loop:
- extract claims from a draft skill;
- write small Nim tests for claims that can be checked;
- curate the dataset, marking what is verified, wrong, too broad, or still untested;
- rewrite the skill from the verified material;
- feed benchmark failures and bad assistant outputs back into the loop.
This has not been a fully autonomous process. A lot of the value came from manual argument.
For example, not is worth spelling out. It is easy to write not a or b while
meaning not (a or b), or not x < y while meaning not (x < y). That belongs
in the style and testing instructions because it directly affects generated Nim
code.
API naming went through the same pressure. Invented names such as findIndex
and getOption were removed. The skill now points to names that actually match
the standard library: find, contains, hasKey, getOrDefault, items,
pairs, add, del, and clear.
This is the part I wish more people understood about instruction files. The hard work is not writing a lot of rules. The hard work is deleting rules that sound reasonable but do not hold up.
What the Experiment Showed
I ran a small check with DeepSeek V4 Pro. This was not a full benchmark. The
task was short: create a Nim OwnedBuffer with ownership hooks, compile it,
and fix it until it passes. I compared a run without skills against a run that
was explicitly told to use the ownership-hooks skill.
The first attempt taught the most important lesson before we even got to the
code. The supposed no-skill run had secretly loaded the Nim skills anyway. The
output looked suspiciously good because the setup was contaminated. After that,
I isolated the two runs with separate temporary homes and checked the logs:
the no-skill run had to load zero skills, and the skill-guided run had to load
nim-ownership-hooks.
With that fixed, the result was not a clean win/loss story. The no-skill model
still produced compiling code and passing tests. It even remembered =dup. But
the code had weaker choices: a custom =sink, a var destructor parameter,
and weaker handling around zero-length allocation.
The skill-guided run produced code closer to the pattern I would want to review:
deep-owning container, minimal hook set, no unnecessary custom =sink,
non-var destructor shape, allocation guard, and focused tests.
The skills did not make the model smart. They moved it toward the code shape an experienced Nim programmer would ask for. For a programming tool, that is already a real result.
Why Nim Benefits From This
Nim is a good target for this workflow because it rewards precise local knowledge.
Many rules can be checked directly. If a rule is about lent, var returns,
ownership hooks, command-call parsing, danger mode, overflow behavior, or
AddressSanitizer flags, you can often write a tiny test for it. A skill rule can
become a compiler test, not just an opinion.
The language also has sharp semantics that are easy for a general model to
blur. sink does not mean "always moved." Hook declaration order matters. =dup
interacts with destruction. assert disappears in danger mode. foo (1, 2) is
not the same call as foo(1, 2). These are small details, but they decide
whether generated code is correct or merely plausible.
Idiomatic Nim also depends heavily on standard library shape. Good API design
means using names and contracts Nim programmers recognize: initX, newX,
toX, items, pairs, contains, hasKey, getOrDefault, [], []=,
add, del, clear, incl, excl, hash. A model can invent names forever.
A skill can tell it not to.
Nim also has a strong code-generation culture already. Templates, macros, Nimony, C interop, generated wrappers, and compiler-aware workflows are normal parts of the ecosystem. Using an LLM as another generator is not philosophically strange. The standard is the same as for any generator: the design must be yours, the output must be reviewable, and the result must pass the checks that matter.
This is where Nim's smaller ecosystem can be an advantage. There is less room to hide behind generic Stack Overflow-shaped code. If the assistant writes unidiomatic Nim, it stands out quickly.
There is also a practical payoff for compiler work and library work. If an LLM is being used to generate a lot of mechanical code from an existing design, the main risk is not that it "cheated." The risk is that it quietly changes the style, weakens invariants, or fills the codebase with almost-right patterns. Skill files are a way to keep the generated code close to the human design.
What Skill Files Can and Cannot Do
Skill files are useful for procedural knowledge:
- how to structure ownership hooks;
- how to write deterministic Nim tests;
- how to shape a public API;
- how to wrap C handles safely;
- how to run sanitizer checks;
- how to avoid parser-sensitive syntax bugs.
They are much less useful as giant knowledge dumps. A skill file should not become a second manual. If it grows without pressure from real failures, it starts to make the assistant worse: more options, more noise, more chances to apply a rare rule in a common case.
The Common Mistakes section needs the strictest standard. It should contain mistakes agents repeatedly make, especially mistakes that surprised the person writing the skill. If a mistake just repeats an earlier rule, it probably does not belong there.
There is also a trust limit. A skill file cannot make an LLM deterministic. It cannot prove that generated code is good. It cannot remove the programmer's responsibility. It can only bias the assistant toward known-good patterns and make review easier.
That may sound modest, but modest tools are often the ones that survive contact with real programming.
Try Them, Then Complain
The skills are public because this only improves if people use them on real Nim work and report where they fail.
The useful reports are concrete. "The assistant still writes this bad ownership hook." "This API rule fights the standard library." "This instruction made the model overfit an edge case." Those reports can turn into tests, dataset entries, and better wording.
That is more valuable than arguing in the abstract about whether LLM-assisted code is good or bad. The code still has to compile. The API still has to make sense. The person shipping it is still responsible for it.
Conclusion
The best use of skill files is not to cage the model until it becomes deterministic. That will not happen. The model is still a probabilistic code generator, and the programmer is still responsible for the result.
The better use is to move repeated review knowledge out of one person's head and into a small artifact that can be tested, shared, criticized, and improved. That is why I made the skills public. Not because they solve AI coding, but because they make a certain kind of AI-assisted Nim programming less random.
The programmer still has to bring the design. The assistant can help produce the code. Nim gives us a compiler and ecosystem that make many mistakes cheap to catch. Skill files connect those pieces.
That is not vibe coding. It is code generation from a programmer's design, checked by the compiler and review.

Comments
Post a Comment