~ $ cd craft && cat README.md

One small step,
and the code
works again.

That is the mantra I try to keep with every change, and the rest of what I hold to grows from it. The links go to where each line runs, here; the numbers, to the essays where I argued it.

In the code

Small, safe steps, always. Not only when testing: it is how I make any change, in steps small enough to check one by one, so that the code is never more than a step away from working: a test that fails is put right before the next step is taken. [1, 2, 3]

A test first, and seen failing. A test that has never failed has not yet shown that it can. At the end of the kata the perfect game passes the moment it is written, so it is made to fail once, and only then gets its 300 back. [1, 2]

test("perfect game", () => {
  rollMany(12, 10);
  expect(g.score()).toBe(300);
});

All tests pass.

test("perfect game", () => {
  rollMany(12, 10);
  expect(g.score()).toBe("fail");
});

Expected: "fail". Received: 300.

test("perfect game", () => {
  rollMany(12, 10);
  expect(g.score()).toBe(300);
});

All tests pass.

A test is an example of use. I write it the way I would explain the code to someone about to use it. Then it survives a refactor, and it catches the bug that the tests looking inside sleep through: an example shows it. [1, 2, 3]

it("delivers messages to listeners", () => {
  dispatcher.addListener(cb);
  dispatcher.deliver("message");
  expect(cb).toHaveBeenCalledWith("message");
});

All tests pass.

The need comes first, in its own words. A feature starts as the words of whoever needs it — in my course, the post came first — and each sentence becomes a step of its test. [1, 2]

* The last post title should be "Hello Blog", this post
* The last post title should be "Hello Blog", this post
context.theLastPostTitleShouldBeSThisPost("Hello Blog");

Clean as I go. Before a change, where it will land; while I make it; and once more when it is done, for whoever reads it next. Not as a phase saved for the end, and never by starting again: when the design is wrong, I move the code towards the right one a little with every change. Kent Beck put the first part in one line: make the change easy (warning: this may be hard), then make the easy change. The book is the rest, whole. [1, 2, 3]

score += rolls[frameIndex]+rolls[frameIndex + 1];

All tests pass.

score += sumOfBallsInFrame(rolls, frameIndex);

All tests pass.

To the trunk, small and often. Continuous delivery is small steps for a whole team: every change integrated into the trunk soon after it is made, through a pipeline fast enough that nobody waits for it, so that the code is always releasable — and everyone is working on nearly the same code, as if in a meeting that never interrupts them. When a team integrates too seldom, I look at what is in its way before I look at the team: how often it merges, and what makes merging slow — often the CI, the speed of light of a team, and one that can be changed. This site goes out on every push. [1, 2, 3]

on:
  push:
    branches: [main]
# …
      - run: npm ci
      - run: npm run coverage
      - run: node tools/architecture-history.mjs
      - run: npm run build

I build the tools. When a part of the work can be done by a program, I write the program, so that nothing starts from scratch: a compiler from posts to tests, a reader of Gherkin that names its own steps, graders that read a repository's history, koans, and a game of passing a test in the fewest keystrokes. [1, 2, 3]

Given I have 12 cucumbers
Given I have 12 cucumbers
Given I have 12 cucumbers
Given I have 12 cucumbers
Given I have 12 cucumbers()
Given I have 12 cucumbers
given I have 12 cucumbers()
Given I have 12 cucumbers
given I Have 12 cucumbers()
Given I have 12 cucumbers
given I Have 12 cucumbers(12)
Given I have 12 cucumbers
given I Have N cucumbers(12)
Given I have 12 cucumbers
given I Have N cucumbers(number1)
Given I have 12 cucumbers
given I Have N Cucumbers(number1)
Given I have 12 cucumbers
givenI Have N Cucumbers(number1)
Given I have 12 cucumbers
givenIHave N Cucumbers(number1)
Given I have 12 cucumbers
givenIHaveN Cucumbers(number1)
Given I have 12 cucumbers
givenIHaveNCucumbers(number1)

The method Gherkin Genie asks for: the sentence is the name.

With others

Pairing is the better code review. It never stops, I often use it to bring beginners up to speed, and it leaves nobody indispensable — and nobody should be, not even an AI. On the critical parts that all of us will work on later, I would rather we mob, test first. [1, 2, 3]

If they find any problem, it’s been written for only a few seconds; therefore, it can be changed instantly.

Help is a smaller step, not the answer. When someone asks me for help, I often keep the solution to myself and propose trying again in steps so small that the way becomes plain. It looks simple, and it takes practice; that is what katas are for. [1]

function add(a, b) {
  return 11;
}

All tests pass.

function add(a, b) {
  return 0+11;
}

All tests pass.

function add(a, b) {
  return 7+4;
}

All tests pass.

function add(a, b) {
  return a + 4;
}

All tests pass.

function add(a, b) {
  return a + b;
}

All tests pass.

Curious before right. In a review I try to understand why the author did it, and ask with genuine curiosity. A comment should block a merge only when the product is at risk; the rest can wait for a later change, or for the whole team, on a Friday. [1, 2]

“Why did you put this variable here?” — or — “I have seen this variable here, I tried to understand how is it here, but I failed, what I am missing?”

Change the environment, not the people. The environment changes us, so a rule that matters goes into the code, where it holds for a person and for an agent alike, rather than into a guide: this site is built that way. And a change to how we work is an experiment, with a date to look back at it together. [1, 2, 3]

it("has no boxes that need each other round in a circle", () => {
  expect(boxCycles(shipped)).toEqual([]);
});

All tests pass.

The credit is the team's. When a system where every new feature brought new bugs turned around without stopping delivery, it was not me who fixed it: it was my team, working a new way. [1, 2]

The programmer had to separate his ego from the code itself: egoless programming.

Behind it

I started against it. I was a detractor of testing: at meetups everyone showed how, and no one showed why. That began to change when I contributed to AngularJS. So I teach it starting from why — at the Legacy Code Rocks meetup, the talk was Testing: what, how, why? — because it is counterintuitive. In 2017 I told a room of students that doctors have the Hippocratic oath, and we have testing. [1, 2, 3]

Why test code that I know works?

Assume I got it wrong. That, for me, is the essence of agile: leave room to find the mistake and fix it, in the design and in the requirement alike. It takes the cases that fail to tell one rule from another: guess one. [1, 2, 3]

  1,  2,  3  ✅ # ascending order!
  1,  2,  3  ✅ # ascending order!
  1,  1,  1  ❌ # but it might be a <= b < c
  1,  2,  3  ✅ # ascending order!
  1,  1,  1  ❌ # but it might be a <= b < c
  1,  2,  2  ❌ # or it might be a < b <= c

The responsibility stays mine. With an AI, on code I mean to keep, the rhythm is the same: I direct it one step at a time and commit between steps. I ask for the clean step on every cycle, because left alone it forgets the refactor; and I tell it that finding nothing to clean is a correct answer, because pushed, it invents something. When I caught myself letting my assistant do the designing, I switched it off until the design was mine again. [1, 2, 3]

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}
function Greeting({ name = 'John' }) {
  return <h1>Hello, {name}!</h1>;
}
What did the AI do? Add a name = ‘John’ just in case it was empty.

Share what worked. Whoever has something worth sharing should share it: it is what took me to meetups, and what keeps me publishing every Saturday — the essays are gathered by subject. I teach it as well — a recipe for concurrency, a course built on posts that are tests — and say it out loud in talks. [1, 2]

Whoever came to present was presenting something that had actually worked for them.

~/craft $ ls

~/craft $

~/craft $