Incoherent Thoughts

One Move at a Time

Published 14 Feb 2026. By Jakob Kastelic.

Complex problems can be broken down into steps that are small and easy, or, at worst, small and tedious. A big chess game unfolds move by move. In life too, you can only make one move at a time, and then it’s the universe’s turn.

The only question is, what is my move right now? But few ask it. Be aware!

DSP

SHARC+ DSP Over QSPI

Published 9 Feb 2026. By Jakob Kastelic.

In the previous article, we compiled a “blink” program and sent it to the ADSP-2156 chip via UART. Now we can try to do the same over a faster interface: QSPI.

FTDI USB to I2C & SPI

The quad SPI2 interface on the evaluation board EV-SOMCRR-EZLITE is connected to the FT4222HQ-D-T chip that connects to one of the USB-C ports on the carrier boards.

FTDI provides software examples for the FT4222HQ on various operating systems. There is also a Python library, ft4222, which we’ll use for this test.

First we make sure the computer sees the FTDI chip:

import ft4222
for i in range(ft4222.createDeviceInfoList()):
    print(ft4222.getDeviceInfoDetail(i, False))

In the printout, we see FT4222 A and FT4222 B. The first one is used for I2C and SPI, and the second for GPIO.

GPIO expander

The same FTDI part also controls an I2C GPIO expander, ADP5587ACPZ-1, which is in charge of the LEDs on the carrier boards as well as the QSPI interface. We’ll be interested in the following pins at U22:

C0 = USBI_SPI0_EN*
C1 = USBI_SPI1_EN*
C2 = USB_QSPI_EN*
C3 = USB_QSPI_RESET*
C4 = ETH0_RESET
C5 = ADAU1372_PWRDWN*
C6 = PUSHBUTTON_EN
C7 = DS8 (green LED on SOMCRR)
C8 = DS7 (green LED on SOMCRR)
C9 = DS6 (green LED on SOMCRR)

Let’s open the I2C device:

dev = ft4222.openByDescription('FT4222 A')
dev.i2cMaster_Init(100)

We can write to it by means of this helper function:

def wr(d):
    for a, b, c in d:
        dev.i2cMaster_WriteEx(
            a, ft4222.I2CMaster.Flag.START_AND_STOP, bytes([b, c]))

First, we’ll make sure that all pins are set as GPIO (as opposed to “keypad” mode):

wr([(0x30, 0x1D, 0x00)]) # R7:0 -> GPIO
wr([(0x30, 0x1E, 0x00)]) # C7:0 -> GPIO
wr([(0x30, 0x1F, 0x00)]) # C9:8 -> GPIO

Next, we would like to set the pin output states to USBI_SPI0,1 disabled, USB_QSPI neither enabled nor under reset, ETH0 reset asserted, ADAU1372 powered down, pushbutton disabled, LEDs off:

# default output config
wr([(0x30, 0x18, 0b1000_1111)])
wr([(0x30, 0x19, 0b0000_0011)])

Finally, to make this configuration active, we enable the outputs on all port-C pins. Make sure to do this after configuring the pin output states, otherwise you can put the FT4222 into reset (pins default to 0 output states, and pulling USB_QSPI_RESET* low puts the FT4222 into reset).

wr([(0x30, 0x24, 0xff)]) # C7:0 -> output
wr([(0x30, 0x25, 0xff)]) # C9:8 -> output

To test that this all works, we can enable the three green LEDs on the SOMCRR one by one:

wr([(0x30, 0x18, 0b0001_1111)]) # enable LED DS8
wr([(0x30, 0x19, 0b0000_0010)]) # enable LED DS7
wr([(0x30, 0x19, 0b0000_0000)]) # enable LED DS6

These are the green LEDs on the SOMCRR board, not the yellow ones on the SOM board.

QSPI master vs slave

The FT4222 can act either as an SPI master or a slave, and so can the SHARC. Both modes are of interest to us:

FT4222SHARCS5 SetApplication
MasterSlave2-3 / DnBoot SHARC from USB
SlaveMaster1-2 / UpTransmit data from SHARC to USB

S5 is the slide switch located on the SOMCRR board next to the USB-C QSPI connector. With the SOMCRR board oriented so the USB-C connectors are on the top, the S5 slider is in the 1-2 position when it is pushed up (towards the USB-C connectors), and 2-3 when it is down (away from USB-C connectors).

SPI boot

To enable the SPI boot, we need to make three hardware selections:

  1. Enable the SPI interface by pulling USB_QSPI_EN* low:

       wr([(0x30, 0x18, 0b1001_1011)])

    Make sure to follow the steps from the previous section to set the GPIO expander into GPIO mode, set to correct output defaults, etc., or else it will not work!

  1. Flip the S5 switch up so that the 2-3 position is selected, making the FT4222 the SPI master. The switch slider must face away from the USB-C connectors.

  1. Set the rotary boot mode selector switch to mode 2 decimal (010 binary, “External SPI2 host”).

While the elfloader utility supports both -b UARTHOST and -b SPIHOST flags, it appears to generate the same bootstream with either flag. So, let’s try to use the same bootstream as we had used in the Blink example from the previous article, except to make sure to send 0x03 as the first byte as instructed by the SHARC manual:

boot_buffer = bytearray([0x03]) # SPICMD = 0x3: Keep single-bit mode
with open('blink.ldr', 'r') as f:
    for line in f:
        boot_buffer.append(int(line.strip(), 16))

The SHARC hardware reference manual describes SPI mode 1 where the clock idles low and data is sampled on falling edge and shifted out on rising edge:

In SPI slave boot mode, the boot kernel sets the SPI_CTL.CPHA bit and clears the SPI_CTL.CPOL bit. Therefore the SPI_MISO pin is latched on the falling edge of the SPI_MOSI pin.

With that in mind, we can initialize the FT4222 device for SPI as follows, send the bootstream, and close the device:

dev = ft4222.openByDescription('FT4222 A')
dev.spiMaster_Init(
    ft4222.SPIMaster.Mode.SINGLE,
    ft4222.SPIMaster.Clock.DIV_8,
    ft4222.SPI.Cpol.IDLE_LOW,
    ft4222.SPI.Cpha.CLK_TRAILING,
    ft4222.SPIMaster.SlaveSelect.SS0)
dev.spiMaster_SingleWrite(boot_buffer, True)
dev.close()

On my first attempt, this did not work since I had the S5 switch set in the wrong polarity (the slider should face away from the USB-C connectors!). Fixing that, the code boots in an instant—much, much faster than the couple seconds it took over UART.

Boot time

With a bootstream of only about 62 kB, the boot time is not a significant consideration. For larger binaries, ADI offers estimates[1] of how long the boot will take as a function of the binary size. Here’s some representative numbers from the SPI2 figure:

Size / kBSingleDualQuad
20000.3 s0.15 s0.08 s
40000.6 s0.3 s0.15 s
80001.1 s0.6 s0.3 s

Chances are that your bootstream is much, much smaller than these figures, so it would in general add a negligible duration to the boot process.


  1. Analog Devices, Engineer-to-Engineer Note EE-447: Tips and Tricks Using the ADSP-SC59x/ADSP-2159x/ADSP-2156x Processor Boot ROM. V01, May 11, 2023.
Theology

Notes on Cassian, Conference 1

Published 8 Feb 2026. By Jakob Kastelic.

Notes while reading The Conferences of John Cassian, Conf. 1, chs. I–XV.

Latin original is from the collection Corpus scriptorum ecclesiasticorum latinorum, vol. XIII: Iohannis Cassiani Opera, Part II: Conlationes XXIIII. Edited by Michael Petschenig, MDCCCLXXXVI. Available online here.

English translation: The Conferences of John Cassian. Translation and Notes by Edgar C.S. Gibson. From: A Select Library of Nicene and Post-Nicene Fathers of the Christian Church New York, 1894. Available online here.

Places and people

Mentioned in the Preface:

Things better and things worse

Also from the Preface:

Scopon vs telos

In Chapter II of Conference I (hereafter I:II) we find the distinction, supposedly made by “Abbot Moses”, between two kinds of goals. All arts and sciences (omnes artes ac disciplinae) have two kinds of purposes:

Strange practices

At the end of the same chapter (I:II) we find an enumeration of strange practices which are done “for the sake of the kingdom of heaven” (I:III). These are done with some perverse joy, even though Cassian was well aware that most people would find these things unpleasant:

At a first glance, this is pure, undisguised spiritual showing off. Indeed, I:I says that Abbot Moses was reluctant to say anything on the matter lest “he might appear to lay himself open either to the charge of bragging” (iactantia).

Few things, one thing

According to Cassian, the Lord of the gospel of St. Luke has declared that:

“few things” are needful for perfect bliss, i.e., that contemplation which is first secured by reflecting on a few saints: from the contemplation of whom, he who has made some progress rises and attains by God’s help to that which is termed “one thing,” i.e., the consideration of God alone [I:VIII]

It’s not clear to me at this point whether the Lord does really advocate the contemplation of saints at any point in any of the scriptures. St. Paul comes closest, saying “Be ye followers of me, even as I also am of Christ” (1 Cor 11:1).

Saints are special

The Preface warns that we may come across, in these Conferences, things that may appear impossible or very difficult (vel inpossibilia putaverit esse vel dura). They are to be explained by

The really judge it properly, we should try living in the same way:

But if any one wants to give a true opinion on this matter, and is anxious to try whether such perfection can be attained, let him first endeavour to make his purpose their own, with the same zeal and the same mode of life, and then in the end he will find that those things which used to seem beyond the powers of men, are not only possible, but really delightful.

Always on Him?

In I:XIII we get advice that seems to go directly against the contemplation of Saints idea we saw above: “even a momentary departure from gazing on Christ is fornication” (fornicatio: fornication, whoredom, prostitution). Thus the mind, when distracted from its correct focus, should acknowledge the lapse and return to Christ.

Two kinds of joy

Sometimes the phrase “kingdom of God” refers to some eschatological final condition (i.e., heavens). But in I:XIII things are more down to earth, so to speak:

the actual kingdom of God is righteousness and peace and joy, then the man who abides in these is most certainly in the kingdom of God, and on the contrary those who live in unrighteousness, and discord, and the sorrow that worketh death, have their place in the kingdom of the devil, and in hell and death.

But not all kinds of joy would do:

the holy Apostle does not say generally or without qualification that every joy is the kingdom of God, but markedly and emphatically that joy alone which is “in the Holy Ghost.” For he was perfectly aware of another detestable joy, of which we hear “the world shall rejoice,” and “woe unto you that laugh, for ye shall mourn.”

Meditation on God

The idea of constant focus on the highest Good, or meditation on the true Way, is of considerable interest to me as it appears to be a simple, direct answer to the big questions of life: What to do, and why? Unfortunately it’s a case of “easier said than done”. First of all, what does it even mean to say a thing like always gaze on the Christ, all else is whoredom? Cassian admits that there’s no one answer. In I:XV, we get a whole enumeration of the variety of ways to contemplate God (edited for length):

A fine list of meditations! At the end, the flow of language carries Cassian away into an argument not entirely logical:

These considerations certainly no one will preserve lastingly, if anything of carnal affections still survives in him, because “thou canst not,” saith the Lord, “see My face: for no man shall see Me and live;” viz., to this world and to earthly affections.

The reference is to Exodus 33:20, where God is speaking to Moses right after threatening to destroy Israel because of the golden calf incident. Moses asks God, in a bit of a non sequitur, to show his glory, and God refuses for Moses would not be able to live in such a case. But Cassian makes a claim that goes beyond what seems reasonable: it’s perfectly fine to see God, so long as you are dead to the world and earthly affections.

That’s also a point where I lose interest, since speculating on after-death experience is no part of my job description.

DSP

Boot SHARC+ DSP Over UART

Published 6 Feb 2026. By Jakob Kastelic.

Most microcontrollers can be built and programmed without vendor IDEs or expensive debug probes. The ADSP-21569 SHARC DSP, at first glance, appears to be an exception. The official workflow assumes Analog Devices’ CrossCore Embedded Studio IDE and dedicated expensive debugging hardware. But instead we can just boot the thing over plain UART—the hardware supports it, the manuals describe it, and the tools exist to generate the boot streams.

These are my notes getting started with the Analog Devices SHARC processor installed on the EV-21569-SOM evaluation board, plugged into the EV-SOMCRR-EZLITE carrier board. Since there is very little information available online about these chips, compared to the more “usual” parts from ST or NXP, I hope the writeup will be of some use to someone.

Hardware setup

Hardware setup: install the SOM board into the SOMCRR board and establish the Default Configuration specified in the EV-SOMCRR-EZLITE Manual.

Connect the provided 12V 1.6A power supply to the POWER IN connector on the SOMCRR board. Connect a USB A-to-C cable to P16, labeled USB-C DA (debug agent).

Software setup

Although the debug tools are relatively expensive, Analog Devices offers a board-locked license that allows the CCES IDE to be used without additional cost after purchasing the evaluation boards.

Install the CrossCore Embedded Studio from the Analog Devices website. I’m using “Product version” 3.0.2.0 (w2410301) with “IDE version” 3.0.2029.202410301506.

Install also the EV-21569-EZKIT Board Support Package, Current Release (Rev. 3.0.0) from the SOM website; this provides some additional code examples to start from.

Code sharing is less common in the DSP ecosystem compared to some other software domains. However, basic reference examples are included within the IDE. To access them, follow these steps:

File
  New
    Project ...
    C/C++
      CrossCore Project
        Next >
        (enter project name, say "test")
        Next >
        (select ADSP-21569 revision "any")
        Next >
        Finish
"Click here to browse all examples."

Search for blink under Keywords, select the LED_Blink example for EV-2156x EZ-KIT v2.0.0 [2.0.0], and press “Open example”. Now we can delete the "test" project created just before: right click it in the Project Explorer, select “Delete”, and also check “Delete project contents on disk”.

Only the LEDBlink project remains; compile it (Project -> Build All) and run it (Run -> Debug, followed by Run -> Resume). With some luck, all three yellow LEDs on the SOM board will start blinking. Great!

Boot from UART

Previously we have run the code through the “Debug Agent”, which is an ATSAME70Q21B-CFNT on the SOM carrier board. Now, let’s try to download the same program through the UART interface.

Locate the P14 pin header on the right side of the carrier board, right under the Analog Devices logo. Locate the two top right pins, labelled SPI0 CLK and MISO. Cross-referencing with the datasheet, we learn that these two pins are UART0_TX and UART0_RX, respectively. Connect them to a 3.3V UART to USB adapter (I’m using the UMFT230XB-01 adapter).

The Processor Hardware Reference[1] describes the UART Slave Boot Mode. In particular, the part supports Autobaud Detection which works as follows:

  1. Send the @ character (0x40) to the UART RXD input.

  2. DSP returns four bytes: 0xBF, UART_CLK [15:8], UART_CLK [7:0], 0x00.

  3. Send the entire boot stream.

Step 1. Let’s attempt this in Python:

import serial
s = serial.Serial('COM20', baudrate=115200, timeout=1)
s.write(b'@')
res = s.read(4)

If we print(res), we get out b'\xbf2\x00\x00'. Great, the processor succeeded with the autobaud detection.

Step 2. Next, we need to convert the ELF file produced by the IDE (LEDBlink_21569.dxe) into a form suitable for loading into the chip. I’m running the elfloader (part of the CCES installation) from WSL2 as follows:

/mnt/c/analog/cces/3.0.2/elfloader.exe \
   -proc ADSP-21569 -b UARTHOST -f ASCII -Width 8 -verbose \
   LEDBlink_21569.dxe -o blink.ldr

Step 3. Back in Python, we can take the file and shove it into the DSP one byte at a time:

with open('blink.ldr', 'r') as f:
    for line in f:
        d = int(line.strip(), 16)
        s.write(bytes([d]))

After a few seconds of blinking on the RX and TX LEDs on the SOM, indicating the UART0 activity, we see the familiar blinking of the yellow LED4, LED6, and LED7, as before.

That’s great news, as it provides a way to program the part without any specialized hardware tools. For example, in an embedded application an application processor could send the DSP its boot code via UART. It’s a bit slow, but presumably the same process could work over a faster interface like SPI2.

Bad news?

If we unplug the USB DA connection to the SOMCRR board, we find that the Python code example above does not communicate with the board anymore (no response to the Autobaud command). But if we plug the USB cable back into the debug agent, then it all works fine. It works even with the CCES program closed.

Silly mistake! The USB connector was the only shared ground between the computer and the DSP board. Attaching a ground wire between the USB to serial adapter and P14, and all is well again.

Preload executable

Let’s take a closer look at how the IDE runs the code. Go into Run -> Debug Configurations, and add a new “Application with CrossCore Debugger”. Click through the wizard and notice that it adds a “preload” ELF file such as the following one, to be run before the blink code runs:

C:\analog\cces\3.0.2\SHARC\ldr\ezkit21569_preload.dxe

What does this do? Let’s navigate to the following location in the CCES documentation:

CrossCore® Embedded Studio 3.0.2 >
  Integrated Development Environment >
    Debugging Targets >
      Debugging ADSP-SC5xx SHARC+ and ARM Projects

The first sentence says that “preload files are used for the ADSP-SC5xx EZ-KITs and processors only”. These files are “equivalent to initcodes, but used during the debugging phase of development”. From my reading of this section, it appears that the preload files configure external memory, if using it, and only for the multi-core parts. Thus, on ADSP-21569 we should have no use for it.

Nevertheless, the project comes with pre-built init code executables even on ADSP-21569. In fact, the source code is provided for two

C:\analog\cces\3.0.2\SHARC\ldr\21569_init
C:\analog\cces\3.0.2\SHARC\ldr\21569_preload

One of these is an “initialization code project” and the other is “CCES preload code project”. Clear as mud! I think the init is used for production applications, while the preload version is used only when running the code directly from the CCES, but they do more or less the same thing: configure clocks, and the DDR.

Clock configuration is the reason why these files are provided not just for ADSP-SC5xx, but also for the 2156x. However, in this basic tutorial we will not modify the clocks, so the init code is not, in fact, needed after all.

Preload exe vs init code

LDR files produced by the elfloader utility support an -init switch, as does the processor bootstream itself. The hardware reference manual explains:

An initialization block instructs the boot kernel to perform a function call to the target address after the entire block has loaded. The function called is referred to as the initialization code (Initcode) routine.

Traditionally, an Initcode routine is used to set up the system PLL, bit rates, wait states, and the external memory controllers. Boot time can be significantly reduced when an init block is executed early in the boot process.

We read in the CCES documentation that the init code can also be packaged into a separate project/program, instead of being a part of the full application’s boot stream:

The preload executables are simple programs that invoke the init code functionality to configure the processor prior to loading the main application.

But the CCES documentation warns:

Do not use the preload executables when building bootable LDR files with the -init switch. The preload executables are not configured for use for LDR initialization blocks.

In other words, to use the -init switch, we should compile the 21569_init version of the initialization project.

However, no init code is needed for the blink project, since we do not need to adjust any of the clock parameters—the default values work.

CCES scatters various related files all over the file system, making it seem more complicated to build a project than is necessary. If we collect all the files together, it’s not so bad. Here’s what the IDE-provided Blink example needs to put together:

OBJ = \
  ConfigSoftSwitches_EV_SOMCRR_EZLITE_LED_OFF.doj \
  ConfigSoftSwitches_EV_SOMCRR_EZLITE_LED_ON.doj \
  SoftConfig_EV_21569_SOM_Blink1.doj \
  SoftConfig_EV_21569_SOM_Blink2.doj \
  adi_gpio.doj \
  adi_initialize.doj \
  app_IVT.doj \
  app_heaptab.doj \
  app_startup.doj \
  main.doj \
  pinmux_config.doj \
  sru_config.doj \

These files are either assembly files, like app_IVT.s and app_startup.s, or plain C files, like all the rest of them. Some are auto-generated, like adi_initialize.c (initializes SRU and pin-mux), app_heaptab.c, pinmux_config.c, and sru_config.c. The “Soft Config” files are essentially copies of each other, two files to turn the LED on, and two files to turn it off (seriously—the only difference in these files is the “data” written to the LED). This leaves just main.c, and the GPIO driver. By the standards of modern SoCs with tens of peripherals, all of this is almost trivial.

The compilation rules are as simple as can be. For the sake of explicitness, here they are:

blink.ldr: blink.dxe
	$(ELFL) $(ELFFLAGS) $< -o $@

blink.dxe: $(OBJ)
	$(CC) $(LDFLAGS) -o $@ $^

%.doj: %.s
	$(ASM) $(ASFLAGS) -o $@ $<

%.doj: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

We have met the ELFL, or elfloader, rule above already: it creates the bootstream from an ELF input. The rest are standard linking, assembly, and compilation steps. The toolchain gets installed with CCES and is unfortunately entirely closed-source:

CC   = /mnt/c/analog/cces/3.0.2/cc21k.exe
ASM  = /mnt/c/analog/cces/3.0.2/easm21k.exe
ELFL = /mnt/c/analog/cces/3.0.2/elfloader.exe

The remaining piece of the Makefile are the flags. I’ll give CFLAGS, the other two are very similar:

CFLAGS = \
  -proc ADSP-21569 -si-revision any -flags-compiler \
  --no_wrap_diagnostics -g -DCORE0 -D_DEBUG -DADI_DEBUG \
  -structs-do-not-overlap -no-const-strings -no-multiline -warn-protos \
  -double-size-32 -char-size-8 -swc -gnu-style-dependencies

Discussion

The Blink example is accompanied by a lengthy license agreement that imposes significant restrictions, such as prohibiting external distribution and public posting of source code. This makes it impractical to release modifications without careful review.

The process to build and run the Blink example is somewhat fragile and may break in future versions of the Eclipse-based IDE, making it difficult to fully automate.

Due to the complexity of modern IDEs, it is not always clear which source files beyond main.c are included in the build. While this is not critical for a simple example like Blink, developers should be aware of the build inputs and dependencies when working on more complex projects to ensure proper provenance and supply chain transparency.

Notably, ADI appears to rely on a “security through obscurity” approach, reflecting a limited transparency regarding security mechanisms. This approach limits developers’ ability to audit or verify the security of the system:

The sources for ROM code are not available in CCES to protect the ADSP-SC5xx/ADSP-215xx secure booting and encryption details.[2]

It is noteworthy that the SHARC+ processor family currently lacks open-source toolchain components—such as an assembler, linker, compiler, loader, and debugging tools—which may limit accessibility for experimentation and early evaluation, potentially affecting broader adoption among engineers.


  1. Analog Devices: ADSP-21562/3/5/6/7/9 SHARC+ Processor Hardware Reference. Revision 1.1, October 2022. Part Number 82-100137-01.
  2. Analog Devices: CrossCore Embedded Studio 3.0.2 > SHARC-FX Development Tools Documentation > Loader and Utilities Manual > Loader.
Unix

Weinberger on Coding

Published 6 Feb 2026. By Jakob Kastelic (ed.).

All of the quotes below are taken from the interview with Peter J. Weinberger (Murray Hill, 8 September 1989), with Michael S. Mahoney interviewing. This expands on the Unix values captured in the previous article.

Programming as Changing Reality

What you tell the machine to do, it’s not doing it on the model, it’s doing it on the mathematical reality. There’s no gap. Which, I think, makes it very appealing. There’s a difference between a computer program and a theorem, which is that when you prove a theorem you now sort of know something you didn’t know before. But when you write a program, the universe has changed. You could do something you couldn’t do before.

The Ideal of Permanent, Correct Design

I also have this feeling that you never want to have to touch the program. So it’s important to do it right early and that it always be okay. It’s not just a problem of the minute, although one writes a lot of code that’s got to do the problem of the minute, it’s got to fill the niche permanently—which is completely unrealistic but it’s certainly an attitude. And I think that matches this other. If it’s just going to be a slipshod temporary hacked up way of doing it it’s just not going to work long enough. And you’re going to just have to come back and do it again and it’s just too much like work. Not that reality actually matches this in any way but I think that’s the attitude.

Theory-Driven Code vs. Hack-Driven Code

If you have a theory based program you can believe you got the last bug out. If you have a hacked up program, all it is, is bugs. Surrounded by, you know, something that does something. You never get the last bug out. So we’ve got both kinds. And it’s partly the style of the programmer. Both kinds are useful. But one kind’s a lot easier to explain and understand even if it’s not more useful.

Usefulness Ultimately Beats Elegance

A program that’s sufficiently useful can have any number of bad effects or properties. But people prefer small, clean, easy-to-understand programs. But they will use big, horrible, grotesque, disgusting, buggy programs if they’re sufficiently useful. And some will complain louder than others, but it’s a rare few who will say “this is just so awful I won’t use it.”

Learnability Over Perfection

My guess is that there is a modest amount to learn and you can use it. And the truth is our secretaries use it. We don’t have a special system for secretaries. They just use it. Now, when you watch them use it you say “oh, but there’s so many easier ways of doing it, there this and this”, but it doesn’t really matter. They don’t have to use it perfectly.

Documentation as a Design Litmus Test

The story is if you write the documentation early, it’s likely it’ll be possible to explain what your program does, whereas if you wait until your program is completely finished, you may discover that however coherent it looked while you writing the various pieces, it’s impossible to explain it.