---
title: 'Agile Frameworks'
source: 'https://academia.sh/en/courses/process-and-team/agile-frameworks'
course: 'Process, Team and Delivery'
language: en
updated: '2026-08-23T07:01:07+00:00'
license: 'CC BY-SA 4.0'
---

# Agile Frameworks

The common structure of iterative delivery frameworks and a scan of iteration length: at every length from one week to six weeks, flow time, feedback delay, rework, time spent in ceremony, and half-done work items are counted.

The previous lesson showed how powerful a variable batch size is: between one batch of eighteen items
and six batches of three items, waiting changed by a factor of thirteen and feedback delay by a
factor of four. But those batches were chosen by hand. Iterative delivery frameworks institutionalize
exactly this choice — they tie batch size to a fixed **duration** and turn that duration into a
rhythm.

This lesson takes up frameworks by their **common structure**, because structure is what is
measurable: a **backlog**, a fixed-length **iteration**, a planning session at the start of the
iteration and a **review ceremony** at the end, and a **role separation** that keeps the person doing
the work apart from the person accepting it. Once these four elements are chosen, the rest of the
numbers fall out on their own. The through-line is the regional library network, and it is fiction; in
this lesson, change requests coming in for the network's loan, fee, membership, and branch services
are pooled into a single backlog.

## The Common Skeleton

The block below is a **model**, not a measurement. It runs a framework's skeleton, not its name.

**PM7 — thirty work items are derived from a generator; the generator is self-written and the seed is
visible.** The same item set is used at every iteration length; the difference between lengths comes
only from the rhythm. Every item has a size (2–8 person-days), an arrival day, and earlier items it
touches the same data as.

**PM8 — the team is four people and produces 4 person-days of work per day.** The ceremony cost per
iteration is `4 + L` person-days: a fixed planning-and-evaluation overhead plus a review share that
grows with iteration length. This is the single number that carries the cost of a short iteration.

**PM9 — acceptance is granted only at the review ceremony.** This is the measurable consequence of
role separation: the person doing the work and the person accepting it are different people, and the
accepter is only at the table during the ceremony. Even if an item finishes on the iteration's first
day, it receives acceptance only on the iteration's last day.

**PM10 — the defect surfaces at the moment of acceptance.** Rework size is one-third of the item plus
1 person-day for every item that touches it and was completed in the same iteration after the
defective item finished. In a long iteration, more items finish after the defective one, so this
surcharge grows.

**PM11 — an item that does not fit in the iteration becomes a carry-over item, left half-done,** and
pays a 1-person-day carryover cost in the next iteration. This is the second price of a short
iteration.

```js
// agile.mjs — the common skeleton of iterative delivery (model); detailed run at one length
import { writeFileSync } from "node:fs";

const PEOPLE = 4;      // team; PEOPLE person-days of work come out per day
const CARRYOVER = 1;   // return cost a carried-over item pays in the next iteration
const ceremony = (L) => 4 + L;              // planning + review + evaluation (person-days)

// PM7: the generator is self-written, the seed is visible; the item set is the same across every iteration length.
let seed = 20250401;
const rand = () => (seed = (seed * 1103515245 + 12345) % 2147483648) / 2147483648;
const ITEM = [...Array(30)].map((_, id) => ({
  id, size: 2 + Math.floor(rand() * 7), arrival: Math.floor(rand() * 40),
  defect: rand() < 0.35, linked: [],
}));
for (const k of ITEM)                        // earlier items touching the same data
  for (const o of ITEM) if (o.id < k.id && rand() < 0.18) k.linked.push(o.id);

function run(L) {
  const day = 5 * L, net = PEOPLE * day - ceremony(L);
  const pool = ITEM.map((k) => ({ ...k, remaining: k.size, done: -1, started: false, reworked: false }));
  const log = [], delays = [];
  let n = 0, halfDone = 0, carryover = 0, ceremonyTotal = 0, rework = 0;

  while (pool.some((k) => k.done < 0)) {
    const start = n * day, end = start + day - 1;          // the review ceremony sits on the last day
    ceremonyTotal += ceremony(L);
    let cap = net, spent = 0;
    const candidates = pool.filter((k) => k.done < 0 && k.arrival <= start)
      .sort((a, b) => (b.started - a.started) || (a.arrival - b.arrival) || (a.id - b.id));
    const finished = [];
    let worked = 0, halfDoneThis = 0;
    for (const k of candidates) {                          // capacity's worth is pulled in at planning
      const extra = k.started ? CARRYOVER : 0;
      if (cap <= extra) break;
      if (extra) { cap -= extra; spent += extra; carryover += extra; }
      k.started = true; worked += 1;
      const taken = Math.min(cap, k.remaining);
      k.remaining -= taken; cap -= taken; spent += taken;
      if (k.remaining === 0) { k.done = start + Math.min(day - 1, Math.floor(spent / PEOPLE)); finished.push(k); }
      else { halfDone += 1; halfDoneThis += 1; }
    }
    // PM10: acceptance is granted only at the review ceremony; the defect surfaces that day.
    let found = 0;
    for (const k of finished) {
      if (!k.defect || k.reworked) continue;
      const touching = finished.filter((x) => x.linked.includes(k.id) && x.done >= k.done).length;
      const size = Math.ceil(k.size / 3) + touching;
      pool.push({ id: 100 + k.id, size, remaining: size, arrival: end + 1, done: -1,
                   started: false, defect: false, reworked: true, linked: [] });
      rework += size; found += 1;
      delays.push(end - k.done);
    }
    log.push({ n: n + 1, start, end, pulled: worked, finished: finished.length,
                 halfDone: halfDoneThis, backlog: candidates.length - worked, found });
    n += 1;
  }
  const original = pool.filter((k) => !k.reworked);
  const avg = (a) => a.reduce((x, y) => x + y, 0) / a.length;
  const base = ITEM.reduce((a, k) => a + k.size, 0);
  return { L, iterations: n, day: Math.max(...pool.map((k) => k.done)) + 1,
    flow: avg(original.map((k) => k.done - k.arrival + 1)), delay: avg(delays),
    defective: delays.length, rework, halfDone, carryover, ceremony: ceremonyTotal,
    base, total: base + rework + carryover + ceremonyTotal, log };
}

const SCAN = [1, 2, 3, 4, 6].map(run);
writeFileSync("scan.json", JSON.stringify(SCAN.map(({ log, ...r }) => r)));

console.log(`work items: ${ITEM.length}, base work: ${ITEM.reduce((a, k) => a + k.size, 0)} person-days, ` +
  `defective items: ${ITEM.filter((k) => k.defect).length}, ` +
  `linked pairs: ${ITEM.reduce((a, k) => a + k.linked.length, 0)} (seed ${20250401})`);

const twoWeek = SCAN[1];
console.log(`\ntwo-week iteration, detailed run (ceremony ${ceremony(2)} person-days, ` +
  `net capacity ${PEOPLE * 10 - ceremony(2)} person-days):`);
console.log(`${"iteration".padStart(9)}${"work day".padStart(9)}${"pulled".padStart(9)}` +
  `${"finished".padStart(10)}${"half-done".padStart(13)}${"backlog left".padStart(15)}` +
  `${"defects found".padStart(15)}`);
for (const r of twoWeek.log)
  console.log(`${String(r.n).padStart(9)}${`${r.start}-${r.end}`.padStart(9)}` +
    `${String(r.pulled).padStart(9)}${String(r.finished).padStart(10)}` +
    `${String(r.halfDone).padStart(13)}${String(r.backlog).padStart(15)}` +
    `${String(r.found).padStart(15)}`);
console.log(`total ${twoWeek.day} work days, ${twoWeek.iterations} iterations; ` +
  `${twoWeek.defective} defects found with an average delay of ${twoWeek.delay.toFixed(1)} work days`);
```

```
work items: 30, base work: 152 person-days, defective items: 8, linked pairs: 75 (seed 20250401)

two-week iteration, detailed run (ceremony 6 person-days, net capacity 34 person-days):
iteration work day   pulled  finished    half-done   backlog left  defects found
        1      0-9        1         1            0              0              0
        2    10-19        8         8            0              5              1
        3    20-29        8         7            1              3              2
        4    30-39        7         6            1              3              1
        5    40-49        8         8            0              4              2
        6    50-59        6         6            0              0              2
        7    60-69        2         2            0              0              0
total 62 work days, 7 iterations; 8 defects found with an average delay of 5.5 work days
```

In the two-week rhythm, the first iteration is nearly empty: on day zero the backlog holds only one
item, and everything that arrives afterward waits for the next planning session. From the third
iteration on, a queue of three to five items forms in the backlog, and one item is left half-done
every iteration. All eight defects are found at review ceremonies, and they surface only after
waiting an average of 5.5 work days.

## Scanning Iteration Length

Rhythm by itself is not a value, it is a setting. The block below runs the same thirty items at one-,
two-, three-, four-, and six-week iterations.

**PM12 — two costs grow in opposite directions.** A short iteration lowers delay and the duration a
half-done item sits idle, but it raises the ceremony share; a long iteration lowers the ceremony share
but makes an item wait both to start and for acceptance.

```js
// scan.mjs — reads the scan results agile.mjs wrote; iteration length is scanned
import { readFileSync } from "node:fs";

const T = JSON.parse(readFileSync("scan.json", "utf8"));
const table = (header, row, caption) => {
  const write = (h) => console.log(h.map((c, i) => String(c).padStart(header[i][1])).join(""));
  console.log(); console.log(caption); write(header.map((b) => b[0])); row.forEach(write);
};

table([["length", 8], ["iterations", 12], ["work day", 10], ["avg. flow time", 18],
       ["avg. delay", 13], ["rework", 11], ["half-done", 12]],
  T.map((r) => [`${r.L} week`, r.iterations, r.day, r.flow.toFixed(1), r.delay.toFixed(1),
                r.rework, r.halfDone]),
  "iteration length scan (flow time and delay in work days):");

table([["length", 8], ["base work", 11], ["ceremony", 10], ["carryover", 11], ["rework", 10],
       ["total", 9], ["ceremony share", 16]],
  T.map((r) => [`${r.L} week`, r.base, r.ceremony, r.carryover, r.rework, r.total,
                `${((100 * r.ceremony) / r.total).toFixed(1)}%`]),
  "distribution of work effort (person-days):");

// PM12: two costs grow in opposite directions; the net price of stretching one step is below.
const signed = (x, d = 0) => (x > 0 ? "+" : "") + x.toFixed(d);
console.log(`\nthe cost of stretching by one step:`);
for (let i = 1; i < T.length; i++) {
  const a = T[i - 1], b = T[i];
  console.log(`  ${a.L} -> ${b.L} week: ceremony ${signed(b.ceremony - a.ceremony)}, ` +
    `carryover ${signed(b.carryover - a.carryover)}, rework ${signed(b.rework - a.rework)}, ` +
    `net ${signed(b.total - a.total)} person-days; flow time ${signed(b.flow - a.flow, 1)}, ` +
    `delay ${signed(b.delay - a.delay, 1)} work days`);
}
const least = T.reduce((a, b) => (b.total < a.total ? b : a));
const fastest = T.reduce((a, b) => (b.flow < a.flow ? b : a));
console.log(`\nleast work effort at ${least.L} week (${least.total} person-days), ` +
  `shortest flow time at ${fastest.L} week (${fastest.flow.toFixed(1)} work days)`);
```

```
iteration length scan (flow time and delay in work days):
  length  iterations  work day    avg. flow time   avg. delay     rework   half-done
  1 week          13        63              12.5          2.3         19           8
  2 week           7        62              13.8          5.5         21           2
  3 week           5        63              17.6          4.9         20           1
  4 week           5        81              21.6         10.6         22           1
  6 week           4        93              31.2         17.0         23           0

distribution of work effort (person-days):
  length  base work  ceremony  carryover    rework    total  ceremony share
  1 week        152        65          8        19      244           26.6%
  2 week        152        42          2        21      217           19.4%
  3 week        152        35          1        20      208           16.8%
  4 week        152        40          1        22      215           18.6%
  6 week        152        40          0        23      215           18.6%

the cost of stretching by one step:
  1 -> 2 week: ceremony -23, carryover -6, rework +2, net -27 person-days; flow time +1.3, delay +3.3 work days
  2 -> 3 week: ceremony -7, carryover -1, rework -1, net -9 person-days; flow time +3.8, delay -0.6 work days
  3 -> 4 week: ceremony +5, carryover 0, rework +2, net +7 person-days; flow time +4.0, delay +5.8 work days
  4 -> 6 week: ceremony 0, carryover -1, rework +1, net 0 person-days; flow time +9.6, delay +6.4 work days

least work effort at 3 week (208 person-days), shortest flow time at 1 week (12.5 work days)
```

## Two Metrics Point to Two Different Lengths

The scan does not produce a single winner, it produces two separate winners. **Flow time is shortest at
one week** (12.5 work days) and degrades in a single direction as length grows: at six weeks it
reaches 31.2 work days, two and a half times as long. Feedback delay moves the same way — from 2.3
work days to 17.0 work days, a factor of seven. The reason is twofold: an item waits for the next
planning session to start and for the end of the iteration to be accepted, and both waits are directly
proportional to iteration length.

**Total work effort, on the other hand, is lowest at three weeks** (208 person-days). The one-week
rhythm spends 244 person-days, and 65 of those are ceremony — **26.6% of the total**. The same rhythm
produces a half-done item eight times and pays 8 person-days of carryover cost; the six-week rhythm
has no half-done items at all. Going from one week to two weeks saves 23 person-days of ceremony and 6
person-days of carryover, at the cost of adding only 1.3 work days to flow time — this step is cheap.
Going from three weeks to four weeks, on the other hand, **adds** 7 person-days and piles 4.0 work
days onto flow time; there is nothing left to gain at this step.

Rework moves less than expected: from 19 to 23 person-days, only a one-fifth change. The number of
linked items completed after a defective one grows in a long iteration, but in a set of thirty items
this surcharge stays small. **The real price of a long iteration is not the volume of rework but the
delay itself**; the one-unit drop in rework between two weeks and three weeks is part of this same
noise and should not be read as a trend.

The work-day column carries a separate warning: the one-week rhythm finishes in 63 work days, the
two-week rhythm in 62. A short iteration shortens flow time but does not finish the work as a whole
any sooner, because the capacity it loses goes to ceremony.

## Summary

- Iterative frameworks were reduced to four common elements — a backlog, a fixed-length iteration, a
  planning session and review ceremony, and a role separation that keeps acceptance authority apart —
  and this skeleton was run.
- The same 30 items were run at five lengths; flow time is 12.5 work days at one week and 31.2 at six
  weeks, and feedback delay rises from 2.3 to 17.0 work days.
- Total work effort is lowest at three weeks (208 person-days); the one-week rhythm spends 244
  person-days, of which 26.6% is ceremony, and it also leaves 8 items half-done and pays 8 person-days
  of carryover cost.
- Because two metrics point to two different lengths, the scan does not produce a single best one:
  flow time points to one week, total work effort points to three weeks.
- Rework is the column that moves the least, rising from 19 to 23 person-days; the price of a long
  iteration is the delay itself far more than the volume of rework.

## Next Step

Every measurement in this lesson rested on a fixed-rhythm assumption: a work item can only start at
the next planning session and can only be accepted at the end of the iteration. Once the rhythm is
removed, the only control lever left is **how many items are open at the same time**. The next lesson
scans that limit: as the work-in-progress limit rises from one upward, how cycle time and the share of
flow time spent waiting change, at which limit throughput stops rising, and what is lost when the
limit is held too low. Flow metrics are defined in the same run — lead time, cycle time, and
throughput — and what decision each one drives is written down separately.
