Archive for the ‘Thinking about code’ Category

The Rise of AI Coding Assistants

Wednesday, June 17th, 2026

In 2024 I worked on a project where we called into an API for an LLM AI Service. We gave it a set of data and a list of rules and asked it to provide a JSON-formatted response. We got a JSON-formatted response about three quarters of the time. The other quarter of the time we got a response that looked like: “Ok, here’s your JSON formatted response: {…}”.

After that project (but still in 2024) I installed a VSCode extension that added AI-powered autocomplete. It would add a light-colored preview of what it thought ought to come next and you could hit tab to accept it. This was pretty useful when implementing the editor extensions we were working on, which used a lot of configuration, much of which was very repetitive. On the other hand, it regularly suggested non-functional continuations. One that sticks in my mind is suggesting that .filter() was appropriate. Unfortunately, the data structure was a Set, not an Array, and Sets don’t support .filter.

In 2025 I got access to Cursor, and it was good. The combination of a chat window and access to the project files meant that I could just say “This function needs to be async. Please adjust all callers and grandcallers to be async,” and it would successfully do that. (And, yes, I did use the word “grandcallers”, and it understood what I was asking for.) It had pretty decent ability to reason about the code base. On that particular async refactor, I actually argued with it where it suggested that a synchronous return of cached data would be necessary to make sure all callers were correctly handled, but I knew that all the callers were inside async functions at some level, and we just had to validate that higher up. It had the background knowledge to recognize the risk, but lacked the analytical capacity to determine that it was illusory.

In 2026, I got access to Claude Code, and it was very good. Its analytical capacity is pretty high, and the ability to make tool integration “skills” is extremely useful. My work sessions with Claude look like saying “Read Jira ticket XYZ-1212 and make a plan to implement it”. And then it asks something like “How should we handle the case where the network fetch fails?” To which I reply “Make a toast show to the user”, then it goes and implements the whole thing, and writes the commit messages and the PR too. At this point my input is reduced to having good technical taste and critiquing the approach! (This is still necessary though.)

Cursor and Claude Code don’t make mistakes like trying to call filter on a Set. I don’t know how the fix was made, but it was. My understanding is that the APIs are much better at generating structured output too. The kinds of errors that modern coding assistants make look more like “correcting” tests to match the new implementation, and not like failing to generate the closing brace on page-long JSON. Or like making 10,000 line components that contain the entire page, rather than breaking out reusable parts. They’re reaching the level of a moderately capable programmer, to whom you can hand off the grunt work.

(I have deliberately failed to name the early stage products that disappointed me. Their current versions might be much better! Coding assistant capabilities are growing fast.)

Full Size Kata Project

Saturday, October 16th, 2010

So I’m sitting here at SCNA, and it’s great to talk about improving our craftsmanship, but what are we doing about it? And I’m very struck by what Keavy says about artists having a culture of critical review, which we totally don’t have. So I talk to Max, and I say, “We’ve got all the right people in the room, but we’re not pushing the state of the art forward!” And I whine about the state of programming education, and how we don’t have the culture of critique, and all that, and he says, “We need a fully worked example that we can all stand behind, like a Kata, but for a full size problem.”

So, that’s what we’re going to do. Thirty days of development, with review and commentary on the code at each step, hopefully tracking real changes to requirements and being a full sized app. We want reviews of the code at each step, and as much feedback as we can get. Once we get the thing “complete”, We should build a finished “episode” with code, commentary, and reviews. Ultimately, We’d like something that someone can read and get a good example of how software craftsmen work and do a full sized project.

We have a twitter hashtag: #fullsizekata

Questions: What is the best way to foster consensus on each iteration?

YAGNI Football Analogy

Tuesday, May 6th, 2008

There’s been some scuttlebutt recently on the extreme programming mailing list about the value of YAGNI — You Aren’t Going to Need It. Apparently, some people have been interpreting it as “Don’t think about the future.” It’s supposed to be “Don’t build things today that you don’t need today.”

If you know that someday, down the road, you may have to support multiple platforms, It’s entirely reasonable to choose OpenGL over Direct3D — OpenGL will run on Windows, Macs, and X platforms, where Direct3D will only run on Windows.

Contrariwise, if you’re only releasing on Windows this version, there’s no reason to build that classic hierarchy of GenericWindowInterface—->MSWindowsInterface today. You Aren’t Going to Need it. When we do a Mac build, then we’ll add that.

Anyway, here’s my replacement analogy for YAGNI: We only need ten yards. (For those not familiar with American football, the team gets four chances to move the ball ten yards. When you have moved the ball ten yards, this process resets, and you get another four chances to move the ball ten yards. Eventually, you’ll make it over the goal line and score points, but that’s later; This play is to get ten yards. A yard is just slightly shorter than a meter.)

So when someone says, “We need to add extensibility hooks here in case we want to offer plugins later,” you can reply, “We only need ten yards.”

Redefining Test Terminology

Sunday, April 27th, 2008

So, earlier I mentioned Mock Objects — The TDD variant that requires isolation of the tested component. The Mock Objects technique requires a hierarchy of tests:

  1. At the lowest level, objects are tested in isolation against mocks.
  2. Higher up, objects are tested in collaboration with a few other real objects.
  3. Higher up, the entire system is tested.
  4. At the top level, test define if the system is complete.

That’s a good hierarchy. My only problem is with the names that they use.

Mock Object developers use these names:

  1. Unit
  2. Integration
  3. System
  4. Acceptance

Long before that, I head people use these names:

  1. “Why are you testing at such a low level? Don’t waste your time.”
  2. Unit
  3. “We probably ought to run the whole thing on some sample data.”
  4. Acceptance

On top of which, there are two other things called “Integration Tests”, which long predate Mock Objects:

QA Integration Tests: We’ve upgraded to the latest version of of GUI library. Let’s run some integration tests to make sure nothing broke. (Usually a full regression sweep.)

(Pre-Agile) Developer Integration Tests: We’re merging three new feature branches on Tuesday. We’d better do some testing to make sure they integrate properly.

At this stage of the game, it’s just not a good idea to re-use existing terms for different things. Especially when telling the difference between those things requires you to read the mind of the speaker.

Families of Test Driven Development

Thursday, April 24th, 2008

So I’ve been looking in at Test Driven Development more lately, and I’m trying to separate the several things that call themselves TDD. Here’s what I’ve got so far:

Classic TDD: as described in Kent Beck’s Test Driven Development by Example, this technique tests and writes code a a very low level. No line of code is written without a failing test. Design comes from refactoring to remove duplication.

Mock Objects: Steve Freeman and co. promote this version of TDD. Its distinguishing feature is a requirement for near-complete isolation of objects under test. Design happens during the writing of the test when new collaborators are discovered.

The Un-named TDD: Maybe we should call it “Test-First”. Medium size tests are written, often close to the level of a use case or scenario. Corner cases are usually not tested. (Disk is full, so you can’t write the file, etc…) Just enough testing is done to verify that the system works. Design is done via refactoring, but the decision of how to refactor is based on the developers taste, not just on removing duplication.

The Un-named TDD (with Mocks): Some people combine the convenient mock generation frameworks of Mock Objects with the unnamed TDD. However, they use them for conveniently removing parts of the app that are annoying to configure, not for isolating the system under test.

Presenter First: Maybe just a sub-species of Mock Objects, this strategy is notable for recommending that the application be built outside-in.

Behavior Driven Development: I know this is related, but I don’t know enough about it to say anything.

I used to do the unnamed TDD, and have only recently discovered that I wasn’t doing Classic TDD. I’ve been trying out Classic TDD, and it’s very intriguing. I learned TDD from a combination of Wiki, XP Explained, Refactoring, and XP Installed. (Sort of.) I wonder if those older sources don’t contain as good a description of it as the TDD book, or if I just missed it.