Initial commit

This commit is contained in:
2026-03-09 00:14:40 -04:00
commit 099e5d3738
10 changed files with 460 additions and 0 deletions

162
claude/CLAUDE.md Normal file
View File

@@ -0,0 +1,162 @@
# Global Rules
- Do not start doing complicated things without the user's
explicit approval first.
- Do not try to be the project architect. The user is the
architect, you are the assistant.
- Your job is to do what the user asks you to do, but only
when the user asks: do not modify code unless the user has
specifically asked you to do so.
- Never check anything into git. Do not run git commit, git
push, git add, or any other git commands that modify the
repository. I will handle all git interactions myself.
- If a prompt ends with an ellipsis, it means the user has
more to type. In that case, only comment if you have
concerns.
## How Writing the API Docs helps you make the API better.
When asked to implement a module or a function, a good
sofware engineer doesn't start by writing the code. Instead,
you a write a block comment explaining the API to the
customer. The goal is to describe an API that is as pleasant
and easy to use as is possible. That's what you write: the
documentation for the greatest API ever. For example, let's
say you want to write a function in C that reads a file and
returns it as a C string. You write this:
```
/* Reads a file. Returns the content as a C string. */
```
Simple and easy to use. That's the greatest API ever, one
which is simple and just does what you want. So then you
write the code:
```
// Read a file. Returns a char* with the file content.
//
const char *ReadFile(const char *filename)
{
static char buffer[65536];
FILE *f = fopen(filename, "rb");
if (!f) return NULL;
size_t n = fread(buffer, 1, sizeof(buffer) - 1, f);
fclose(f);
if (n == sizeof(buffer) - 1) return NULL;
buffer[n] = '\0';
return buffer;
}
```
Then, after writing the code, you go back to the comment,
and you ask yourself: is this documentation truthful? Is
this really what the function does? In our example, it's
not. The documentation we wrote - documentation for the
greatest API ever - does not accurately describe what this
function does. Here's more accurate documentation:
```
/* Read a file. Returns a char* with the file content.
If the file is more than 65535 characters, returns
nullptr. Not thread-safe. */
```
Now it's truthful, but it's no longer the documentation for
the greatest API ever. It sucks as an API, because the
customer has too many things to worry about.
So now you have a struggle on your hands. You have to
figure out to make the code have a nicer API. So you
consider maybe using malloc for the buffer. That would get
rid of the 64k limit, and thread-safety issue. So, you go
back change the code to malloc a buffer. But then you
realize, you've solved the 64k limit and the thread-safety,
but you've introduced a new problem for the customer: memory
leaks.
A good software engineer will find himself going back and
forth between the documentation and the code, repeatedly
saying to himself: Is this documentation accurate? Could
it be simpler? Can I fix the code to make the API simpler?
A decent engineer will go back and forth several times.
## Deep Nesting is Bad
I have a rule: rarely make a function that has more than two
nested loops. Humans really have a hard time understanding
code that is nested deeply. So the rule is basically,
that this is OK:
```
if (x) {
while (y) {
...
}
}
```
That's two levels of nesting. But add another conditional
or loop inside the while, and it's too much.
Now, sometimes you need something like a namespace, which
adds another pair of braces: namespace { ... }. That
doesn't count as a level of nesting. The namespace increases
the indentation, but it's not increasing the code
complexity. Indentation isn't the problem. The problem is
that deeply nested loops and deeply nested conditionals are
hard to follow.
## Keeping Things Synchronized is Bad
Let's say I have an enum:
```
enum ShapeType { CIRCLE, SQUARE, TRIANGLE }
```
Then, somewhere else, in a completely different file,
I have this:
```
const char *ShapeString(ShapeType s) {
switch (shape) {
case CIRCLE: return "CIRCLE";
case SQUARE: return "SQUARE";
case TRIANGLE: return "TRIANGLE";
}
}
```
Now I have to keep these two synchronized. If I add another
shape, I have to add it in *two* places. Humans are
terrible at remembering that they have to update two places:
I honestly think AIs aren't any better. You can make this
a lot better if you put the 'ShapeString' function *right*
next to the enum. If you can put them right next to each
other, then anybody who edits the enum will also see that
they have to update the ShapeString function.
## A Recap of My Software Engineering Rules
1. Always write a block comment with the API docs before
writing a function or module. Make it the greatest API
ever: make it super easy-to-use.
2. After writing the API docs, write the code. Then,
begin a process of iteration in which you compare the
docs and the code, fixing both of them until they match,
but always prioritizing fixing the code to make the API
simpler, and not settling for documenting something that's
hard to use.
3. In functions, keep the nesting level of loops and
conditionals 2 deep or less.
4. Don't create a situation where you have to keep two
things synchronized, *especially* if the two things
are in separate files.