Skip to content
C.W.K.
Stream
Lesson 01 of 05 · published

Decide the Policy Once

~12 min · policy, consistency, judgment, governance

Level 0Loose Parts
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Two Apps, Two Postures, Zero Decisions

Encryption at rest existed in two of the three original siblings, and they disagreed. One refused to start if its key was missing — a fail-fast gate, on the reasoning that silently writing plaintext is worse than not running. The other degraded quietly: no key, write plaintext, log a warning. The third had no encryption at all, and got it from the shared layer when it adopted.

Neither posture is wrong. What is wrong is that the difference had never been decided. Two authors made a local call on a question that was actually one question, and the resulting divergence looked like architecture from a distance and was two unexamined opinions up close.

The unification required somebody to choose, and choosing meant answering something concrete: what should happen to a person whose machine is not fully provisioned? The answer taken was graceful everywhere. A machine without the key writes plaintext and logs one warning; encrypted lines from another machine pass through opaque. The fail-fast gate was retired.

Why That Answer, and Why It Is a Judgment

The reasoning is about people, not about cryptography. It also had to cover the app that was getting encryption for the first time, which is the one most likely to meet a machine that has never been provisioned. These apps run across several machines, and provisioning is uneven — a fresh machine, a restored one, a peer that has not had a key installed yet. A fail-fast gate turns each of those into a stopped application, and the person in front of it usually wanted to read yesterday's notes rather than to write a new secret.

You could reasonably decide the other way. What you cannot reasonably do is decide it twice, differently, without noticing that it is the same decision — nor leave the third app with no answer at all because nobody owned the question.

Unifying an implementation forces a decision that was previously being made by default, in every place that had one — and forces an answer for the place that had none. That is one of consolidation's genuine benefits and it is rarely stated: the extraction does not merely remove duplicated code, it surfaces every question the duplicates had been answering independently. Budget for that. The technical work is usually smaller than the set of decisions it exposes.

The Other Policies, and the Shape They Share

Three more arrived together three weeks later, and they rhyme. One vocabulary for visibility, everywhere — one app had used a different word for the same state, and both continued to be readable because the fold-time normalizer keeps projecting old records. One media policy: existing attachments first, new ones after, with caps, and a missing file warns and is skipped rather than failing the whole request. One emotion list, fetched from its owner with a shared fallback, so nobody hand-mirrors a different list.

Each replaced a place where apps had diverged without deciding to. And each has the same structure: a question that looks technical, an answer that is actually about what should happen to a person, and a written record so the next app inherits the answer instead of the question.

Code

The gate that was retired, and the posture that replaced it·python
# BEFORE - one app, fail-fast. Defensible reasoning: silently
# writing plaintext is worse than not running at all.
def require_enabled() -> None:
    if not _key_available():
        raise RuntimeError(
            "encryption key missing; refusing to start"
        )

# ...called at startup. A machine without the key has a stopped app.


# AFTER - the family posture, chosen deliberately: graceful
# everywhere. The question this answers is not cryptographic, it is
# "what should happen to somebody whose machine is not fully
# provisioned yet" - and the answer is that they can still read
# yesterday's notes.
class Veil:
    def __init__(self, service: str, account: str, env_var: str):
        self._key = _load_key(service, account, env_var)
        if self._key is None:
            log.warning(
                "no encryption key for %s/%s - writing plaintext. "
                "Lines encrypted on another machine will pass "
                "through unreadable.", service, account,
            )

    def encrypt_line(self, text: str) -> str:
        return _encrypt(text, self._key) if self._key else text

    def maybe_decrypt_line(self, line: str) -> str:
        """Opaque pass-through, never an exception: a line this
        machine cannot read still belongs in the file, and a fold
        that raises on it would take the whole record down."""
        if not self._key or not _looks_encrypted(line):
            return line
        try:
            return _decrypt(line, self._key)
        except Exception:
            return line


# What the unification COST, and the part worth budgeting for: it
# forced a decision that two authors had each been making by default
# - and produced one for the third app, which had never had to make
# it. The code change was small. Choosing the family's posture toward
# a half-provisioned machine was the actual work.

External links

Exercise

Find one behavior implemented in two or more of your services that differs in its error posture — one retries and one fails, one defaults and one raises. Write down what question that difference is answering, and whether anybody ever decided it. Then propose one answer for both, and note who would have to agree.
Hint
Error posture is the richest place to look, because it is rarely specified and almost always decided implicitly by whoever wrote the first version. The differences are real decisions that nobody remembers making, which is exactly what makes them worth surfacing.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.