Where Requirements Get Hard: Wicked Problems, CATWOE's Slippery Names, and Assumptions Engineering

Soft Systems Methodology and CATWOE give you a genuinely useful way to surface conflicting worldviews before committing to a design. They don't make the underlying problem easy, and using them carelessly can create a false sense that it's now been solved. This page covers three things worth knowing before you rely on them: why the problems SSM was built for structurally resist ever being fully "solved," why CATWOE's own terminology sets a trap for anyone reading its letters in their everyday English sense, and why most of what gets called "requirements gathering" is really a narrower, more specific activity than the name suggests.

Wicked problems: why "the problem" won't hold still

Rittel and Webber coined the term wicked problem to describe exactly the class of situation SSM was built for: problems that resist the kind of clean formulation an engineering discipline is used to working with1. A few of their ten properties matter most for software work specifically:

  • There is no definitive formulation of a wicked problem. Every stakeholder's account of what the problem is is itself a proposed solution direction in disguise — describing the extension-request situation as "students need support" versus "the process needs consistency" isn't a neutral framing choice, it already points toward a different fix.
  • Solutions aren't true-or-false, they're better-or-worse. There's no test you can run that proves a requirements decision was correct, only ongoing judgement about whether it's working out well.
  • There's no stopping rule. A wicked problem doesn't announce when it's solved. You stop working on it because you've run out of time, budget, or patience — not because a check confirmed completion.
  • Every wicked problem is essentially unique, which is exactly why a requirements process that worked well on the last project can still surprise you on this one.
graph LR T["Tame problem"] --> T1["Agreed goal"] T --> T2["A test exists that proves
when it's solved"] W["Wicked problem"] --> W1["Contested formulation"] W --> W2["No test proves completion —
only ongoing judgement"] style W2 fill:#FFC857

SSM's response to this isn't to tame wicked problems into tame ones — it can't. Its response is to give you a disciplined, repeatable way of taking one useful, provisional step: build root definitions, compare them against reality, debate the differences, act, and go round again. Doing CATWOE analysis well doesn't mean the problem is now solved. It means you've taken one careful turn of a cycle that doesn't have a defined endpoint, on a problem that structurally can't be fully specified in advance. A project can run this cycle competently and still get blindsided by a case nobody's root definition anticipated — that's not a failure of the method, it's what "wicked" means.

CATWOE's slippery names: "Customers" isn't who you think

CATWOE reuses ordinary English words for technical purposes, and one of them causes real, recurring confusion. In everyday business language, "customer" means whoever pays. In CATWOE, Customers means whoever receives the effect of the transformation — for better or worse — regardless of any money changing hands.

Take a root definition for a company's redundancy process. The Customers, in CATWOE's strict sense, are the employees being made redundant: they are the ones the transformation is actually done to. Calling them "customers" can feel almost perverse — nobody involved would use that word in conversation — but the letter is doing real analytical work, forcing the question "who does this transformation actually land on?" rather than letting an easier, friendlier answer (perhaps "the company" or "HR") quietly stand in for it.

Actors has a related but different trap. CATWOE's Actors are specifically whoever carries out the transformation — not everyone who interacts with the system, and not the broader sense of "actor" you'll meet in a UML use-case diagram, where an actor can be any external entity, human or system, that triggers a use case. Importing that broader meaning into a CATWOE table quietly smuggles in people who touch the system without actually doing the transformation, and the resulting root definition ends up vaguer than it needs to be.

The general fix is the same for every letter: read each one as a technical term scoped to the specific root definition you're building, not as its everyday meaning, and re-derive who fills each role from that root definition's own Transformation and Weltanschauung — not from a single, fixed list of "the stakeholders" you carry across every version. Who counts as a Customer, an Actor, or even an Owner can genuinely change between two root definitions of the exact same situation, which is precisely the kind of thing a side-by-side CATWOE comparison is built to surface.

Requirements elicitation is mostly assumptions engineering

Here's a claim worth taking seriously: a large share of what gets labelled "gathering requirements" is actually the much narrower activity of surfacing assumptions nobody realised they were making — because an assumption is invisible from inside the worldview that makes it feel obvious. This is exactly the CATWOE Weltanschauung acting on the analyst as much as the stakeholder.

Meyer's classification of specification failures — the "seven sins of the specifier" — names two sins that are, structurally, unexamined assumptions wearing the clothes of a finished requirement2. Silence is a real feature of the problem that simply never gets mentioned, because it felt too obvious to state. Wishful thinking is specifying something as though it's straightforwardly achievable, without ever checking that it actually is. Neither shows up as an error when you reread the document — that's exactly the problem. A missing assumption doesn't look like a gap; it looks like nothing at all.

Naur's account of programming as theory-building pushes this further than most requirements teaching does: even a genuinely careful, complete-looking specification isn't the same thing as the working theory that lives in the heads of the people who understand why the system does what it does5. A huge share of what actually makes a system correct is tacit — the kind of knowledge Polanyi argued we can know more of than we can ever fully say6 — and no amount of documentation fully recovers it once the people who held it move on. Treating "requirements are finished" as a phase you complete and close is often the single biggest unexamined assumption on the whole project.

Design by Contract is Meyer's own answer to exactly this problem, and it's worth naming because it turns Silence from a documentation failure into a runtime one instead of leaving it undetectable3. A contract's require clause states a precondition, its ensure clause states a postcondition, and both are checked automatically, every time the routine runs — which means an assumption that would otherwise sit silently in a specifier's head (or nowhere at all) becomes an explicit, machine-verified claim that fails loudly the moment it's violated, rather than quietly producing a wrong answer nobody notices.

This is exactly how PatLang expresses contracts within its BDD scenarios rather than bolting on a separate annotation syntax: a require precondition can be written as an additional And clause under Given, and an ensure postcondition as an additional And clause under Then. What distinguishes a contract clause from an ordinary example clause isn't the syntax — both are just And lines — it's what the clause is claiming. An ordinary Given/When/Then example describes one specific, concrete case, useful as training data for the language's own inductive synthesis engine, but silent, by construction, about everything the example didn't happen to cover. A precondition added as an And under Given and a postcondition added as an And under Then state something that has to hold on every call satisfying that Given, not just the one case being illustrated — which makes it the direct equivalent of promoting a tacit assumption to Dewar's "load-bearing" category and then actually building the signpost, except here the signpost is checked automatically at runtime rather than waiting for a person to notice.

Techniques for surfacing assumptions before they surface you

Keep an explicit, living assumption log. Dewar's assumption-based planning gives this a rigorous, well-tested shape: for each significant claim underneath a requirement, write it down explicitly, then classify it — is it load-bearing (the system doesn't work if this turns out false) and vulnerable (there's a real chance it is)? For every load-bearing, vulnerable assumption, define a signpost: an observable, early sign that it's starting to fail. Then decide a shaping action (something you can do now to reduce the chance it fails) or a hedging action (something ready to go if it does anyway)7.

graph LR A["Stated requirement"] --> B["What assumption is
this quietly resting on?"] B --> C{"Load-bearing
and vulnerable?"} C -->|No| D["Log it, move on"] C -->|Yes| E["Define a signpost:
early sign it's failing"] E --> F["Shaping action
(reduce the risk now)"] E --> G["Hedging action
(ready if it fails anyway)"] style C fill:#FFC857

Build two root definitions for the same real situation, on purpose. This is the same technique from CATWOE itself, reframed explicitly as an assumption-surfacing device: build a second, genuinely different Weltanschauung's root definition for the situation you're already working on, then compare CATWOE tables side by side. Every element that stayed the same across both is a more robust requirement than it looked. Every element that changed is a live, contested assumption worth resolving deliberately, rather than by whichever worldview happened to get interviewed first.

Run a silence audit. Reread a draft specification hunting specifically for Meyer's silence — for every stated requirement, ask "what am I assuming here that I never actually wrote down?", and write that down too, however obvious it feels. The value isn't in stating the obvious; it's in finding out whether it's equally obvious, and the same obvious thing, to every stakeholder who'll read it.

Construct an adversarial stakeholder. Deliberately invent a plausible person holding a Weltanschauung genuinely different from the one the current draft was built around, and ask specifically which stated requirement they'd dispute first. If you genuinely can't construct one, that's worth noting on its own. Usually you can, and the dispute is exactly where a hidden assumption was living.

Then go a step further and adopt a saboteur's mindset. A legitimate adversarial stakeholder still holds a coherent, honest worldview — someone who disagrees with the requirement, not someone trying to defeat it. It's worth also asking the more hostile version of the same question: how would I break this, or use it for something it was never meant to permit? This site's Data Security material is largely this discipline made concrete, one attack surface at a time: a SQL Injection attack is what happens when someone actually tests the assumption that user input will always be the well-formed value the system silently expected, rather than just writing that assumption down; a Unix Permissions misconfiguration is what happens when "only authorised people will ever be in a position to try this" turns out to have been an assumption nobody actually tried to break. The Trustworthy Software Initiative's verifiability pillar makes the same point at the scale of a whole pipeline rather than one input field: an assumption you haven't actively tried to break yet isn't the same claim as an assumption that holds4.

Use a cheap prototype to provoke "that's not what I meant." That reaction, delivered early against a low-cost prototype, is a successful assumption discovery — not scope creep, and not a stakeholder moving the goalposts. Treat it as the method working as intended.

The honest limit, again

None of this converts a wicked problem into a tame one. Rittel and Webber's point about there being no stopping rule applies here too: an assumption log, once built, isn't a milestone you close off — it's a living document, revisited on the same kind of cycle SSM itself runs on, because the situation it describes keeps moving. The realistic goal isn't "we have now surfaced all the assumptions." It's "we've built a disciplined habit of noticing when we're about to act on one we never actually checked."

Where this connects

References


  1. Rittel, H. W. J., & Webber, M. M. (1973). Dilemmas in a general theory of planning. Policy Sciences, 4(2), 155–169. https://doi.org/10.1007/BF01405730

  2. Meyer, B. (1985). On formalism in specifications. IEEE Software, 2(1), 6–26. https://doi.org/10.1109/MS.1985.229776

  3. Meyer, B. (1992). Applying design by contract. Computer, 25(10), 40–51.

  4. See this site's Trustworthy Software Initiative page for the fuller account of the verifiability pillar and its "never merge what you could not explain to an auditor" standard.

  5. Naur, P. (1985). Programming as theory building. Microprocessing and Microprogramming, 15(5), 253–261. https://doi.org/10.1016/0165-6074(85)90032-8

  6. Polanyi, M. (1966). The Tacit Dimension. University of Chicago Press.

  7. Dewar, J. A. (2002). Assumption-Based Planning: A Tool for Reducing Avoidable Surprises. Cambridge University Press.