Tech articles and blog posts

A tech article tells other developers what you tried, or what you fixed. chaff finds by machine the openings that keep the reader waiting, and the shapes that generated text leaves behind. The genre is blog/tech. It is also the genre chaff uses when none is set.

What usually goes wrong

More and more tech articles start as an AI draft. Published as they are, they keep the draft’s shapes.

What goes wrongExample
An opening that fits any article“In today’s fast-paced world, containers have become an essential part of modern development”
Sentences that only announce“The key point is”, “What’s important is”, “Honestly,”
Knocking down what nobody said“This isn’t just waiting time”
A count that does not match“We made the following two changes” above a list of three
A link that goes nowhereA link to a heading the article does not have
A stock closing“I hope this helps!”, “Happy coding!”

What chaff checks, and what it does not

These are the main rules for this genre.

RuleWhat it findsWhen it runs
padded-introAn opening that fits any articleAlways
closing-clicheA stock closingAlways
ai-tellPhrases common in generated text (“delve into”) piling upAlways (info)
announcing-openerSentences opening with “The key point is” and the like, piling upAlways (info)
ai-generated-compositeThree or more signals of generated text in one articleAlways
contraction-consistency“isn’t” and “is not” mixed in one articleAlways (info)
title-case-consistencyHeadings that mix Title Case and sentence caseAlways (info)
announced-count-mismatchA count announced that differs from the list below itAlways
broken-linkA link to a heading the article does not haveAlways
quote-without-sourceWords given to someone (“X said …”) with no source in the paragraphAlways (info)
empty-conclusionA conclusion that says nothing newWhen an AI reads it, with npx chaffjs test

What it does not do is decided too.

  • It does not check that the code works. The writer runs the commands and checks the settings.
  • It does not check that the numbers are true. Whether it really was “9 minutes to 2 minutes” is for the writer to check.
  • It does not open links to other sites. It only checks links to headings inside the article.
  • It never says “an AI wrote this”. It marks where the shapes of generated text pile up, as places to reread.

Try it

If you have not set up yet, do Getting ready first. The article below was saved as article.md. It was written for this page.

# How We Made Our Docker Builds Faster — The Overlooked Key of Caching

In today's fast-paced world, containers have become an essential part of modern development. In this article, we will delve into how our team cut our Docker build time.

## What Was the Problem?

Every CI build took **about 9 minutes**.

This isn't just waiting time. It's **a problem that eats away at our development speed**.

Honestly, we ignored this problem for half a year. Before we knew it, the waiting was quietly eating hours of our week.

## The Root Cause

The key point is the order of the Dockerfile.

Here's what we found:

- We wrote `COPY . .` before `RUN yarn install`
- So changing one line of source re-installed every dependency
- CI did not reuse the cache from the previous build

What's important is that the cache was being thrown away silently. The build never failed; it is just slower now.

## What we fixed

We made the following two changes:

- Copy only `package.json` and `yarn.lock` first, install, then copy the source
- Pass `--cache-from` to `docker build` so the previous image is used as a cache
- Add `node_modules` to `.dockerignore`

Moreover, we recorded the results in `docs/build-time.md`. For details, see [the build time log](#build-time-log).

## Results

Builds went from 9 minutes to 2 minutes. It's not just faster builds, it's a dramatic boost to our team's productivity.

I hope this helps! Happy coding!

Run it with the genre. --compact prints each finding on two lines.

$ npx chaffjs article.md --genre blog/tech --compact

article.md   blog/tech · English   genre from --genre

  1:71    info    Section length varies by only 25% (want at least 35%)
                  section-length-uniformity
  3:1     info    "delve into, in today's fast-paced world, it's not just" appear together (score 25, limit 18)
                  ai-tell
  3:1     warning "in today's fast-paced world" is an opening that fits any article
                  padded-intro
  3:1     warning "ai-tell, section-length-uniformity, padded-intro, closing-cliche, announcing-opener" occur together in this document (5 signals, 3 needed)
                  ai-generated-composite
  11:1    info    "Honestly," and other announcing openers start 3 sentences (3 needed)
                  announcing-opener
  15:1    info    "The key point is" and other announcing openers start 3 sentences (3 needed)
                  announcing-opener
  23:1    info    "What's important is" and other announcing openers start 3 sentences (3 needed)
                  announcing-opener
  23:68   info    "it is" is written "it's" elsewhere in the document
                  contraction-consistency
  25:17   info    This heading's capitalisation differs from the rest (1 in this document)
                  title-case-consistency
  27:23   warning "two changes" is announced, but the number of items in the list below is 3
                  announced-count-mismatch
  33:77   warning The link "[the build time log](#build-time-log)" points to "#build-time-log", which is not a heading on this page
                  broken-link
  39:1    warning Closes with "hope this helps"
                  closing-cliche
  39:20   warning Closes with "happy coding"
                  closing-cliche

13 findings, 70 rules not run

3:1 at the start of a line means line 3, character 1. warning is worth fixing; info marks a place to reread, and never fails a run.

The guide that comes first

With the genre set, chaff opens the report with the genre’s guide: what a good tech article does, as questions to check the draft against. Under them is a line naming the rules that matter most for this genre. Most of the questions are ones no rule can answer, so read them before the findings. An AI that rewrites the text works to the same lines (Using the guide with an AI).

$ npx chaffjs article.md --genre blog/tech

Guide: Tech blog (blog/tech)

  Before the findings below, check the draft against these.

  - Does the opening make one claim, plainly (what the reader will learn or be able to do)?
  - Do the steps and code run as written and give the same result?
  - Are the versions used (language, libraries, OS) and the date they were tried stated?
  - Does it say what was tried and failed, the limits, and the assumptions?
  - Does the ending point to what to read or try next, rather than restate the body?

  Rules that matter most for this genre: closing-cliche, empty-conclusion, padded-intro, paragraph-length-variance, quote-without-source, rule-of-three, section-length-uniformity, sentence-rhythm
  Change it with guide: in chaff.yaml; leave it out with --no-guide.

════════════════════════════════════════════════════════════

article.md   blog/tech · English   genre from --genre
…

A genre that was guessed from the path or the content, or left at the default, prints no guide. A guide for the wrong kind of document would mislead. --compact and --no-guide leave it out. A team changes the lines with guide: in chaff.yaml (Changing the genre’s guide).

What each finding means

LineFindingWhat it meansHow to fix it
3padded-intro“In today’s fast-paced world” fits any articleStart with the problem: “Every CI build took 9 minutes.”
3ai-tell“delve into”, “in today’s fast-paced world” and “it’s not just” are common in generated text, and they pile up hereSay what the article does in plain words: “This article shows how we cut our build time.”
11, 15, 23announcing-opener“Honestly,”, “The key point is” and “What’s important is” announce instead of sayingDelete the announcement and start with the point
23contraction-consistencyThe article writes “it’s” three times, but “it is” hereWrite the same word the same way: “it’s just slower now”
25title-case-consistency“What we fixed” is in sentence case; the other headings are in Title CaseMake the headings agree
27announced-count-mismatchIt says “two changes”, but the list has threeFix the number or the list, whichever is wrong
33broken-linkThere is no heading “build time log” in the articleAdd the heading, or remove the link
39closing-cliche“I hope this helps!” and “Happy coding!” are stock closingsDelete them; end on what the reader can do next
1section-length-uniformityEvery section is about the same lengthGive more room to the sections with more to say; do not pad to match
3ai-generated-compositeSeveral of these shapes occur together in one articleIf a light pass is not enough, rewrite it (see below)

“This isn’t just waiting time” is a contrast frame, and “quietly eating hours” is a metaphor common in generated text. chaff does not flag either in this article. A machine does not find every habit.

When the shapes pile up, rewrite

Fixing the findings one by one can still leave an article of bold phrases and bullets that reads as generated. Then rewrite it, section by section. The steps are in Making AI-sounding text sound human, under “The bold rewrite”.

Way to fixWhat it changesWhen to use it
Light passOnly the places chaff flaggedThe content and structure are good; only the wording grates
Bold rewriteEach section, and the shape of the whole articleAfter a light pass, it still reads as generated

After a rewrite, run npx chaffjs compare article.md rewritten.md to check that no number or command was lost. The same page has a worked rewrite and the compare screen.

Checks that read meaning go to an AI

Whether a conclusion says anything new cannot be decided without reading it. npx chaffjs test sends only the passages the machine narrowed down to an AI. With --dry-run it shows them without sending anything.

npx chaffjs test article.md --dry-run    show what would go to an AI (sends nothing)

For this article, the machine narrowed down no passage, so nothing would be sent:

article.md   sends 0 passages out of 24 sentences (the API was not called)

      0 to send  Empty conclusion  (narrowed by machine)


────────────────────────────────────────────────────────────

  0 passages in all would be sent. With --dry-run the API was not called.
  Sent to: anthropic / claude-opus-5 (no credentials)

To have an AI read the passages, you need an API key. How to set it is in the README.

A starter chaff.yaml

Put this chaff.yaml in the folder that holds your articles.

genre: blog/tech
language: en

With this file in place, npx chaffjs article.md --compact alone shows the findings above. A rule whose info you do not want is turned off under rules: (section-length-uniformity: off, for example). When your team settles how it writes, add it as described in Define your team’s writing rules.