Back to Ideas 7 min read

The Done Column Is a Syllabus

E
EkoHacks Team
·
The Done Column Is a Syllabus

A board is built for the present tense. Open ours on any given morning and it shows you the handful of cards in flight this week, which is exactly what a board is for. But by late July the Done column on the Dojo board held 187 cards, and one afternoon we wanted to read them the way you read a book: end to end, in one sitting, looking for the plot.

You cannot do that by scrolling. You can do it with two tools whose names do a lot of work in this story, so let us define both properly.

The tracker and the query language

Linear is the issue tracker where our work lives. Every piece of work is a card: an identifier such as DOJ-77, a title, a description, a column that says where it stands. When a pair finishes a story, the card moves to Done and the board forgets about it. The card is still there, though, and so is everything written on it. The board is the present; the columns are the archive.

GraphQL is a query language for APIs. The idea fits in one sentence: you write down the shape of the answer you want, and the server returns exactly that shape. Linear exposes its whole archive through one endpoint, https://api.linear.app/graphql, and you talk to it by posting a query like this:

query DoneStories($teamKey: String!, $state: String!, $after: String) {
  issues(
    filter: { team: { key: { eq: $teamKey } }, state: { name: { eq: $state } } }
    first: 100
    after: $after
  ) {
    pageInfo { hasNextPage endCursor }
    nodes {
      identifier
      title
      description
      completedAt
      labels { nodes { name } }
    }
  }
}

Read it as a form you are handing to the server. The filter block says which cards: team DOJ, column Done. The nodes block says which fields you want back for each card, and the answer contains those fields and nothing else. Add createdAt to the query and it appears in the response; delete description and it vanishes. That round trip, shape in and shape out, is the entire idea of GraphQL, and it is why the response lands ready to use instead of arriving as a haystack you filter afterwards.

The pageInfo block is the other lesson. Linear hands over at most 100 cards per request, along with a cursor marking where the page ended. You feed that cursor back as after and ask again, until hasNextPage says false. Our column came back as 100 cards, then 87. Pagination is the first thing every real API makes you learn, and the first thing every beginner script gets wrong by reading page one and declaring victory.

The whole thing is a short shell script: curl to post the query, jq to merge the pages and print one line per card. We have kept the scripts as teaching materials in the Dojo repo under docs/workshop/linear-graphql/, ready to point at any team and any column.

What 187 cards read like

Then we read the column. One line per card, in order, out loud. It takes about fifteen minutes per hundred stories, and it is the best fifteen minutes of tooling this team has spent, because the column has a plot.

The early cards are labelled like parts in a warehouse:

XP: extract a shared XP ledger store (foundation)

Server side authorisation for dashboard and admin endpoints

Accurate, tidy, and rankable by exactly the people who wrote them. A customer reading that first title learns that some machinery exists and that the developers are fond of it.

Somewhere around card 74, the voice changes:

A quiet week blanks a developer's whole profile, and their trend reads as a collapse

The XP economy is invisible until after it pays

A participant cannot find their own metrics drill-down

Each of these is a one-sentence failure told in the world of the person who suffers it. You can rank these knowing nothing about our codebase. A learner whose profile reads as a collapse is losing heart today; the invisible economy can wait a week. That ranking conversation is open to everyone in the room, which is the point of writing stories this way.

The Art of Agile Development teaches that a story is a reminder to have a conversation: a few words on a card that stand in for a decision the team talked through together. Read end to end, our column shows a team slowly learning that lesson in public. The cards start recording the conversations themselves. One description opens with "Agreed design (with Anastasis, 2026-07-23)". Another cites "per the DOJ-185 decision". Three cards begin with the word "Decide", because the work they represented was a conversation, so the card said so.

Nobody planned that arc. It only becomes visible when you read the column as one document, and the API is what makes the column readable.

XP, twice over

At EkoHacks, XP means two things, and the pun is deliberate.

XP is Extreme Programming, the discipline the dojo teaches: work planned as stories, test-driven development, a failing test committed before the code that passes it, small releases landing on the trunk several times a week.

XP is also experience points, the number the Dojo product pays when real engineering happens: a merged pull request, a green build, a streak of both. Practising the first XP is how you earn the second. That is the whole design of the platform.

The Done column is where the two meet, which is why we now use this reading as a workshop session. Every card in that column is a story that was written, talked through, sized, split, built red-then-green, and finished. The column is a syllabus assembled by accident: it contains worked examples of bug stories, security stories told as risk, clean-up stories, and decision cards, together with a visible record of the team getting better at writing them. Handing a learner 187 finished stories in chronological order teaches more about planning in small customer-valued pieces than any slide we could make, because the drift from warehouse labels to human sentences happened to us, on the record, with dates.

Run it on your own board

This transfers directly, and the exercise is short enough for one session of your own XP project.

  1. Create a personal API key in Linear under Settings, then Security & access, then Personal API keys. Prove it works with the smallest query there is: { viewer { name } }. If the API answers with your name, you are in.
  2. Pull your Done column with the paginated query above, or take our scripts from docs/workshop/linear-graphql/ and run ./fetch-done-stories.sh YOURTEAM Done.
  3. Read every title aloud, in order. For each one, ask two questions. Could the person who pays for this work rank it against the others? Does it say what someone would notice when it is done?
  4. Sort the titles into two piles: written for the customer, written for the compiler. Date the boundary between them if you can find one.
  5. Rewrite five titles from the compiler pile in the customer's words, and notice how often the rewrite teaches you what the story was actually for.

This is how we do things at EkoHacks: the tools we build with become the tools we teach with. The board keeps the receipts, the API reads them back, and the Done column, it turns out, is the course.

E

Written by

EkoHacks Team

More from Ideas

·6 min read

What the Nullable Gave Back

One file, seven behaviours held fixed, the database swapped for a Nullable: about 180 times less time inside the tests, and coverage flat to two decimals.

E
EkoHacks Team
·6 min read

Twenty Six More Tests, Four Fewer Behaviours

Removing the mocks grew the suite from 44 tests to 70 and quietly deleted four behaviours, every one of them a failure path. Test count is not coverage.

E
EkoHacks Team
·6 min read

The Best Coverage Number in the Room

Same commit, same spec, same test count. The mocked suite ran 5.6 times faster, covered 3.5 fewer points of real code, and posted the best branch coverage.

E
EkoHacks Team