Skip to content
academia.sh

Lesson 08 / 10

The Pillars of the Design Framework

Scoring a decision set across five pillars and counting the tension between pillars: how many decisions raise one pillar while lowering another, which pillar pair is most tense, deriving the pillar no decision improves, and comparing the pillar profile of handed-over decisions against what remains and the share that does not carry at exit.

Published Updated

In this lesson

  • Defines the design framework's five pillars as types and scores a decision set against them
  • Counts the tension between pillars with a matrix: how many decisions lower which pillar while raising another
  • Derives the pillar no decision improves and the most tense pillar pair
  • Compares the pillar profile of handed-over decisions against what remains and counts the share that cannot carry at exit
Contents

The first two lessons measured geography on two separate axes, and both ended in the same shape: a decision raised one number and lowered another. Spreading across three zones raised the item left standing and hurt the rent and the synchronous write time. Three edge locations lowered latency and produced more than ten thousand stale responses a day. Each of these two observations is, on its own, an anecdote; to turn into a decision ledger they have to be written down with the same measures and in the same form.

The design frameworks used to evaluate rented infrastructure do exactly this. They score a decision not as good or bad but separately, pillar by pillar. This lesson uses five pillars — operations, security, reliability, performance, and cost — because those five are the common core of the widely used frameworks; the number of pillars varies from framework to framework, and some add a sixth such as sustainability. The framework’s name does not appear in this lesson; what is measured is not a name but how the pillars constrain one another. A tension matrix built the same way was set up for quality attributes in a previous course; the method is the same, but the numbers there do not carry over here.

Five Pillars and the Decision Set

  • CG13. The pillars are called by their type name: operations, security, reliability, performance, cost.
  • CG14. The decision set consists of 12 decisions made in this course and the previous two lessons; all are geography, resilience, or leasing decisions.
  • CG15. The scores are an assumption between -2 and +2, not a measured value; what is meaningful is the ranking and the sign, not the absolute size.
  • CG16. A decision counts as a handoff if implementing it requires renting a service the provider operates.
  • CG17. The exit class has three values: definition (can be written down and built again unchanged in another lease), partial, none.
  • CG18. Decisions are scored independently of one another. In real sets they affect each other; this is a known limit of the model.
// framework/model.mjs — decision set and pillar scores is a MODEL. Pillars are called by their type
// name; scores are this lesson's assumption, not measured values. There is no real product or catalog.
export const PILLAR = ["operations", "security", "reliability", "performance", "cost"];

// name, [operations, security, reliability, performance, cost] (-2..+2), handoff?, exit class
export const DECISION = [
  ["three-zone-spread", [-1, 0, 2, 0, -1], false, "definition"],
  ["full-copy-in-second-region", [-2, 0, 2, 1, -2], false, "definition"],
  ["cross-region-synchronous-write", [-1, 0, 2, -2, -1], false, "definition"],
  ["three-edge-locations", [-1, 1, 1, 2, -1], true, "partial"],
  ["subscriber-summary-cache", [0, 0, 1, 2, 0], true, "definition"],
  ["managed-relational-database", [2, 1, 1, -1, -2], true, "partial"],
  ["function-runtime", [2, 0, 1, -1, -1], true, "none"],
  ["provider-key-management", [1, 2, 0, -1, -1], true, "none"],
  ["per-environment-separate-lease", [-2, 2, 1, 0, -1], true, "definition"],
  ["automatic-patching-handoff", [2, 2, -1, 0, 0], true, "none"],
  ["single-write-endpoint", [1, 0, -2, 1, 0], false, "definition"],
  ["raw-read-in-object-store", [1, 0, 1, -1, -1], true, "partial"],
];

export const score = (k, s) => k[1][PILLAR.indexOf(s)];
export const raising = (s) => DECISION.filter((k) => score(k, s) > 0);
export const lowering = (s) => DECISION.filter((k) => score(k, s) < 0);
export const total = (s) => DECISION.reduce((a, k) => a + score(k, s), 0);

// tension: if decision i raises pillar i while lowering pillar j, the cell increases by one
export function tension(i, j) {
  return DECISION.filter((k) => score(k, i) > 0 && score(k, j) < 0).length;
}
export const tense = (k) => k[1].some((p) => p > 0) && k[1].some((p) => p < 0);

Tension Between Pillars

// framework/score.mjs — pillar totals and the tension matrix between pillars
import { PILLAR, DECISION, score, raising, lowering, total, tension, tense } from "./model.mjs";

const s = (x, n) => String(x).padStart(n);
console.log(`${DECISION.length} decisions, ${PILLAR.length} pillars; scores are in -2..+2 (assumption).`);
console.log();
console.log("decision                          | ops   | sec   | rel   | prf   | cst   | net   | tense  ");
console.log("----------------------------------|-------|-------|-------|-------|-------|-------|--------");
for (const k of DECISION) {
  const net = k[1].reduce((a, b) => a + b, 0);
  console.log(`${k[0].padEnd(33)} |${k[1].map((p) => s(p, 5)).join(" |")} | ${s(net, 5)} | ` +
    `${tense(k) ? "yes" : "no"}`);
}

console.log();
console.log("pillar        | total   | raising decisions   | lowering decisions   | highest   | lowest  ");
console.log("--------------|---------|---------------------|----------------------|-----------|---------");
for (const t of PILLAR) {
  const p = DECISION.map((k) => score(k, t));
  console.log(`${t.padEnd(13)} | ${s(total(t), 7)} | ${s(raising(t).length, 19)} | ` +
    `${s(lowering(t).length, 20)} | ${s(Math.max(...p), 9)} | ${s(Math.min(...p), 8)}`);
}

console.log();
console.log("raising \\ lowering   | operations   | security   | reliability   | performance   | cost    | row  ");
console.log("---------------------|--------------|------------|---------------|---------------|---------|------");
for (const i of PILLAR) {
  const h = PILLAR.map((j) => (i === j ? "-" : String(tension(i, j))));
  const row = PILLAR.filter((j) => j !== i).reduce((a, j) => a + tension(i, j), 0);
  console.log(`${i.padEnd(20)} | ${s(h[0], 12)} | ${s(h[1], 10)} | ${s(h[2], 13)} | ` +
    `${s(h[3], 13)} | ${s(h[4], 7)} | ${s(row, 5)}`);
}

const neverRaised = PILLAR.filter((t) => raising(t).length === 0);
const pair = [];
for (const i of PILLAR) for (const j of PILLAR) if (i !== j) pair.push([i, j, tension(i, j)]);
pair.sort((a, b) => b[2] - a[2]);
console.log();
console.log(`tense decisions ${DECISION.filter(tense).length}/${DECISION.length}; ` +
  `decisions that lower no pillar ${DECISION.filter((k) => !k[1].some((p) => p < 0)).length}.`);
console.log(`pillar no decision improves: ${neverRaised.join(", ") || "none"}.`);
console.log(`most tense pair: ${pair[0][0]} rises while ${pair[0][1]} falls — ${pair[0][2]} decisions.`);
12 decisions, 5 pillars; scores are in -2..+2 (assumption).

decision                          | ops   | sec   | rel   | prf   | cst   | net   | tense  
----------------------------------|-------|-------|-------|-------|-------|-------|--------
three-zone-spread                 |   -1 |    0 |    2 |    0 |   -1 |     0 | yes
full-copy-in-second-region        |   -2 |    0 |    2 |    1 |   -2 |    -1 | yes
cross-region-synchronous-write    |   -1 |    0 |    2 |   -2 |   -1 |    -2 | yes
three-edge-locations              |   -1 |    1 |    1 |    2 |   -1 |     2 | yes
subscriber-summary-cache          |    0 |    0 |    1 |    2 |    0 |     3 | no
managed-relational-database       |    2 |    1 |    1 |   -1 |   -2 |     1 | yes
function-runtime                  |    2 |    0 |    1 |   -1 |   -1 |     1 | yes
provider-key-management           |    1 |    2 |    0 |   -1 |   -1 |     1 | yes
per-environment-separate-lease    |   -2 |    2 |    1 |    0 |   -1 |     0 | yes
automatic-patching-handoff        |    2 |    2 |   -1 |    0 |    0 |     3 | yes
single-write-endpoint             |    1 |    0 |   -2 |    1 |    0 |     0 | yes
raw-read-in-object-store          |    1 |    0 |    1 |   -1 |   -1 |     0 | yes

pillar        | total   | raising decisions   | lowering decisions   | highest   | lowest  
--------------|---------|---------------------|----------------------|-----------|---------
operations    |       2 |                   6 |                    5 |         2 |       -2
security      |       8 |                   5 |                    0 |         2 |        0
reliability   |       9 |                   9 |                    2 |         2 |       -2
performance   |       0 |                   4 |                    5 |         2 |       -2
cost          |     -11 |                   0 |                    9 |         0 |       -2

raising \ lowering   | operations   | security   | reliability   | performance   | cost    | row  
---------------------|--------------|------------|---------------|---------------|---------|------
operations           |            - |          0 |             2 |             4 |       4 |    10
security             |            2 |          - |             1 |             2 |       4 |     9
reliability          |            5 |          0 |             - |             4 |       8 |    17
performance          |            2 |          0 |             1 |             - |       2 |     5
cost                 |            0 |          0 |             0 |             0 |       - |     0

tense decisions 11/12; decisions that lower no pillar 1.
pillar no decision improves: cost.
most tense pair: reliability rises while cost falls — 8 decisions.

The last column of the first table is this lesson’s first number: 11 of the 12 decisions are tense, meaning each raises at least one pillar while lowering at least one other. The only decision that lowers no pillar is the subscriber summary cache, and even that one came with a cost in the second lesson — stale responses. If the pillar scoring did not see the cost, that does not mean there is no cost; it means it fell outside the measure set.

The second table summarizes the pillars on their own. Reliability is raised by 9 decisions and, at +9, is the highest-total pillar. Cost, in contrast, is raised by 0 decisions, lowered by 9, and totals -11. This does not show the decisions were badly chosen; it is a consequence of what the set is made of. This lesson gathered geography and resilience decisions, and none of them raise cost — the decisions that raise cost come from a different kind and have not been measured yet. Security showing 0 lowering decisions reads the same way: this set contains no decision that breaks security, not that security cannot be broken.

The third table is the real measure. The row counts the decisions that raise one pillar; the column counts the measure they lower. The reliability row, at 17, is by far the most tense row: the decisions that raise reliability lower cost in 8 decisions, operations in 5, and performance in 4. The most tense pair is reliability and cost, and 8 decisions carry this pair. The cost row being entirely zero is the matrix form of the same finding as the second table: since no decision raises cost, there is no cell where cost lowers another pillar either.

Where the matrix earns its keep is not a ranking but a list of which pair needs to be settled explicitly. Because the reliability–cost cell is filled with 8, if that pair is not discussed once and pinned to an upper bound, eight separate decisions each make their own trade-off, unaware of the other seven. The reliability–security cell, by contrast, is 0: none of the nine decisions that raise reliability lower security, so there is no need to write a separate trade-off rule for that pair. Empty cells carry as much information as full ones.

The Pillar Profile of the Handed-Over Decision

The tension matrix split decisions across pillars; the second cut splits them by who owns them. A decision is handed over if implementing it requires renting a service the provider operates.

// framework/handoff.mjs — splitting the pillar profile between handed-over and retained-own decisions
import { PILLAR, DECISION, score } from "./model.mjs";

const s = (x, n) => String(x).padStart(n);
const y = (x) => x.toFixed(2);
const EXIT = { definition: 2, partial: 1, none: 0 };
const group = { handoff: DECISION.filter((k) => k[2]), own: DECISION.filter((k) => !k[2]) };

console.log("group     | decisions   | operations   | security   | reliability   | performance   | cost    | exit score  ");
console.log("----------|-------------|--------------|------------|---------------|---------------|---------|-------------");
for (const [name, g] of Object.entries(group)) {
  const avg = PILLAR.map((t) => g.reduce((a, k) => a + score(k, t), 0) / g.length);
  const exitScore = g.reduce((a, k) => a + EXIT[k[3]], 0) / (2 * g.length);
  console.log(`${name.padEnd(9)} | ${s(g.length, 11)} | ${s(y(avg[0]), 12)} | ${s(y(avg[1]), 10)} | ` +
    `${s(y(avg[2]), 13)} | ${s(y(avg[3]), 13)} | ${s(y(avg[4]), 7)} | ${s(`${y(100 * exitScore)}%`, 12)}`);
}

console.log();
console.log("group     | definition at exit   | partial   | none   | decisions not carried  ");
console.log("----------|----------------------|-----------|--------|------------------------");
for (const [name, g] of Object.entries(group)) {
  const count = (t) => g.filter((k) => k[3] === t).length;
  console.log(`${name.padEnd(9)} | ${s(count("definition"), 20)} | ${s(count("partial"), 9)} | ` +
    `${s(count("none"), 6)} | ${s(`${count("partial") + count("none")}/${g.length}`, 23)}`);
}

const handoffOpsSec = group.handoff.reduce((a, k) => a + score(k, "security") + score(k, "operations"), 0);
const ownOpsSec = group.own.reduce((a, k) => a + score(k, "security") + score(k, "operations"), 0);
console.log();
console.log(`handed-over ${group.handoff.length} decisions total ${handoffOpsSec} in operations + security, ` +
  `retained-own ${group.own.length} decisions ${ownOpsSec}.`);
console.log(`of the handed-over ${group.handoff.length} decisions, those that cannot carry at exit as a ` +
  `definition: ${group.handoff.filter((k) => k[3] !== "definition").length}; of the retained-own ` +
  `${group.own.length}, ${group.own.filter((k) => k[3] !== "definition").length}.`);
group     | decisions   | operations   | security   | reliability   | performance   | cost    | exit score  
----------|-------------|--------------|------------|---------------|---------------|---------|-------------
handoff   |           8 |         0.63 |       1.00 |          0.63 |          0.00 |   -0.88 |       43.75%
own       |           4 |        -0.75 |       0.00 |          1.00 |          0.00 |   -1.00 |      100.00%

group     | definition at exit   | partial   | none   | decisions not carried  
----------|----------------------|-----------|--------|------------------------
handoff   |                    2 |         3 |      3 |                     6/8
own       |                    4 |         0 |      0 |                     0/4

handed-over 8 decisions total 13 in operations + security, retained-own 4 decisions -3.
of the handed-over 8 decisions, those that cannot carry at exit as a definition: 6; of the retained-own 4, 0.

The difference between handed-over and retained-own decisions concentrates in two pillars. In operations, handed-over decisions average +0.63, retained-own decisions -0.75; in security, +1.00 versus 0.00. Their sums, 13 against -3, mean the payoff of the lease is paid in these two pillars. In reliability, the ranking reverses — retained-own decisions average +1.00, handed-over decisions +0.63. The decisions that raise reliability the most are decisions that cannot be handed to anyone: spreading across zones, keeping a copy in a second region, making the write synchronous.

The last table gives the exit column, and this lesson’s sharpest contrast is here. All 4 of the 4 retained-own decisions carry at exit as a definition: how many zones, which region, which write pattern — every one is a sentence that can be written down and built again unchanged in another lease. Of the 8 handed-over decisions, only 2 carry; 6 carry partially or not at all. The exit score is 100 percent against 43.75 percent. Leasing raises the operations and security pillars while pulling the exit column below half, and this column appears in none of the five above.

Summary

  • The framework scores a decision not as good or bad but pillar by pillar; the five used in this lesson are operations, security, reliability, performance, and cost. What is measured is not a name but how the pillars constrain one another.
  • 11 of the 12 decisions are tense; there is exactly one decision that lowers no pillar, and even its cost fell outside the pillars.
  • 0 decisions raise cost, 9 lower it; the pillar no decision improves is cost. This is a consequence of the set being made of geography and resilience decisions.
  • The most tense pair is reliability and cost: 8 decisions raise reliability while lowering cost. The reliability row, at 17, is the most tense row.
  • The 8 handed-over decisions average +0.63 and +1.00 in operations and security, the 4 retained-own decisions -0.75 and 0.00; in reliability the ranking reverses (+1.00 against +0.63).
  • At exit, 4/4 of the retained-own decisions carry as a definition, only 2/8 of the handed-over ones: the exit score is 100 percent against 43.75 percent.

Next Step

This lesson counted cost as a pillar, and that pillar turned up a strange property: none of the twelve decisions raise it. The reason is not that the decisions were poor, but the composition of the set — geography and resilience decisions are the side that pays cost, not the side that earns it. The decisions that raise the cost pillar come from elsewhere, and not one of them has been measured yet: under which terms the same capacity is rented. The same machine, the same hour, the same work; but capacity carries one price if it can be requested and released instantly, another if it is tied to a long-term commitment, and a third if the provider can reclaim it. The next lesson runs three models against the same load profile and counts, together, the total cost, the work re-run because reclaimed capacity was lost, and the unused commitment; it shows that as the load profile changes, the winning model changes too.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close