[
  {
    "title": "What survives a port is the asset",
    "description": "The same repo holds the API twice — NestJS and a Spring Boot port serving one hand-written contract. What transferred unchanged between them is where the engineering actually lives.",
    "url": "https://murn.eu/blog/port-test",
    "markdown": "https://murn.eu/blog/port-test/index.md",
    "article": 20,
    "date": "2026-07-04",
    "readingTime": "3 min read",
    "wordCount": 539,
    "tags": [
      "engineering",
      "philosophy"
    ]
  },
  {
    "title": "Staging should run NODE_ENV=production",
    "description": "NODE_ENV is a semantics switch that libraries branch on, not a deployment tier. Set it to staging and your rehearsal runs different code paths than the show.",
    "url": "https://murn.eu/blog/two-envs",
    "markdown": "https://murn.eu/blog/two-envs/index.md",
    "article": 19,
    "date": "2026-06-30",
    "readingTime": "3 min read",
    "wordCount": 478,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "Every knob is a promise",
    "description": "The retry policy is three hardcoded constants, and the decision record says why: a config option promises that every value works. Unused knobs are debt, not flexibility.",
    "url": "https://murn.eu/blog/knob-debt",
    "markdown": "https://murn.eu/blog/knob-debt/index.md",
    "article": 18,
    "date": "2026-06-27",
    "readingTime": "3 min read",
    "wordCount": 561,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "Refresh on the publisher's clock",
    "description": "The weather source publishes on a ~25-minute cycle, so the refresh cron fires at :27 — cache freshness is a property of the upstream's rhythm, not of your TTL.",
    "url": "https://murn.eu/blog/upstream-clock",
    "markdown": "https://murn.eu/blog/upstream-clock/index.md",
    "article": 17,
    "date": "2026-06-23",
    "readingTime": "2 min read",
    "wordCount": 396,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "Validate 86 fields to serve 6",
    "description": "The API returns six fields per station but validates the upstream's entire shape. Drift in the eighty fields you ignore is the earliest warning about the six you depend on.",
    "url": "https://murn.eu/blog/boundary-tripwire",
    "markdown": "https://murn.eu/blog/boundary-tripwire/index.md",
    "article": 16,
    "date": "2026-06-20",
    "readingTime": "3 min read",
    "wordCount": 538,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "Missing is not zero",
    "description": "One weather station reports no temperature at all. Encode that as 0 or drop the key, and every consumer inherits the bug — absence belongs in the contract, typed.",
    "url": "https://murn.eu/blog/typed-absence",
    "markdown": "https://murn.eu/blog/typed-absence/index.md",
    "article": 15,
    "date": "2026-06-16",
    "readingTime": "2 min read",
    "wordCount": 440,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "Generated specs drift away from review",
    "description": "Doc generation doesn't stop code and contract from disagreeing — it decides which one drifts unreviewed. The artifact a stakeholder approved should be the one the code chases.",
    "url": "https://murn.eu/blog/drift-direction",
    "markdown": "https://murn.eu/blog/drift-direction/index.md",
    "article": 14,
    "date": "2026-06-13",
    "readingTime": "4 min read",
    "wordCount": 607,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "Undocumented decisions get made twice",
    "description": "My test folder got migrated twice — same move, same commit message, days apart. An agent session starts with no memory, and an unwritten decision is just an accident it feels free to fix.",
    "url": "https://murn.eu/blog/relitigation",
    "markdown": "https://murn.eu/blog/relitigation/index.md",
    "article": 13,
    "date": "2026-06-09",
    "readingTime": "4 min read",
    "wordCount": 695,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "The AI plan is for keeping, not following",
    "description": "I had the model write its full project plan, committed it, and added a rule to never follow it. The unedited default is what makes judgment visible.",
    "url": "https://murn.eu/blog/control-plan",
    "markdown": "https://murn.eu/blog/control-plan/index.md",
    "article": 12,
    "date": "2026-06-06",
    "readingTime": "3 min read",
    "wordCount": 597,
    "tags": [
      "engineering",
      "philosophy"
    ]
  },
  {
    "title": "Disposable by default",
    "description": "Vibe coding versus agentic engineering is sold as a skill tier. It is really a per-artifact call: most agent output is disposable, and verification only pays once an output crosses the boundary where a second reader has to trust it.",
    "url": "https://murn.eu/blog/disposable",
    "markdown": "https://murn.eu/blog/disposable/index.md",
    "article": 10,
    "date": "2026-06-02",
    "readingTime": "5 min read",
    "wordCount": 1056,
    "tags": [
      "engineering",
      "llm"
    ]
  },
  {
    "title": "Ship the answer with the question",
    "description": "The brief's openapi.yaml was missing, so I committed my own. A question that ships with its answer turns authoring into a veto — and never blocks.",
    "url": "https://murn.eu/blog/executable-questions",
    "markdown": "https://murn.eu/blog/executable-questions/index.md",
    "article": 11,
    "date": "2026-06-02",
    "readingTime": "4 min read",
    "wordCount": 866,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "Pattern shopping",
    "description": "Pattern catalogs read forward — name, then move, then constraint. Engineering runs the other way. Reading them as curriculum produces moves imported from a vocabulary rather than earned from a failure.",
    "url": "https://murn.eu/blog/pattern-shopping",
    "markdown": "https://murn.eu/blog/pattern-shopping/index.md",
    "article": 9,
    "date": "2026-05-26",
    "readingTime": "8 min read",
    "wordCount": 1600,
    "tags": [
      "engineering",
      "llm"
    ]
  },
  {
    "title": "Rule drift",
    "description": "Osmani's ratchet — every mistake becomes a rule — is the accretion move. The second move is consolidation: rules that live in one canonical file cannot drift, and most harness write-ups skip it.",
    "url": "https://murn.eu/blog/rule-drift",
    "markdown": "https://murn.eu/blog/rule-drift/index.md",
    "article": 8,
    "date": "2026-05-25",
    "readingTime": "5 min read",
    "wordCount": 1090,
    "tags": [
      "engineering",
      "llm"
    ]
  },
  {
    "title": "Thinking in code",
    "description": "For ambiguous problems, writing the code is where the thinking happens — not a transcription step after the thinking has finished. The medium and the thought go together.",
    "url": "https://murn.eu/blog/thinking-in-code",
    "markdown": "https://murn.eu/blog/thinking-in-code/index.md",
    "article": 7,
    "date": "2026-05-23",
    "readingTime": "5 min read",
    "wordCount": 1240,
    "tags": [
      "engineering",
      "llm"
    ]
  },
  {
    "title": "Judgement-shaped problems",
    "description": "Most production 'agents' are integrations in costume. The test is where the if/else lives — in code, or in inference.",
    "url": "https://murn.eu/blog/judgement-shaped-problems",
    "markdown": "https://murn.eu/blog/judgement-shaped-problems/index.md",
    "article": 6,
    "date": "2026-04-30",
    "readingTime": "6 min read",
    "wordCount": 1306,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "LLM catch",
    "description": "LLMs write perfect plans, detailed specifications, and complete architectures. Nobody ever builds any of it.",
    "url": "https://murn.eu/blog/the-catch",
    "markdown": "https://murn.eu/blog/the-catch/index.md",
    "article": 5,
    "date": "2026-04-21",
    "readingTime": "4 min read",
    "wordCount": 669,
    "tags": [
      "engineering",
      "llm"
    ]
  },
  {
    "title": "Asking is not enough",
    "description": "Most LLM workflows fail not because the model gets it wrong, but because nobody defined what right looks like before calling it.",
    "url": "https://murn.eu/blog/asking-is-not-enough",
    "markdown": "https://murn.eu/blog/asking-is-not-enough/index.md",
    "article": 4,
    "date": "2026-03-18",
    "readingTime": "6 min read",
    "wordCount": 716,
    "tags": [
      "engineering",
      "llm"
    ]
  },
  {
    "title": "Prompts as pipelines",
    "description": "Prompt engineering is not the art of writing one better prompt — it is the discipline of breaking the work into phases that can fail independently.",
    "url": "https://murn.eu/blog/prompts-as-pipelines",
    "markdown": "https://murn.eu/blog/prompts-as-pipelines/index.md",
    "article": 3,
    "date": "2026-03-01",
    "readingTime": "5 min read",
    "wordCount": 985,
    "tags": [
      "engineering",
      "llm"
    ]
  },
  {
    "title": "Boring software",
    "description": "The best software is the kind where nothing happens. Everything was planned, written, tested, and deployed — and it just works.",
    "url": "https://murn.eu/blog/boring",
    "markdown": "https://murn.eu/blog/boring/index.md",
    "article": 2,
    "date": "2026-02-05",
    "readingTime": "5 min read",
    "wordCount": 849,
    "tags": [
      "engineering"
    ]
  },
  {
    "title": "On simplicity",
    "description": "Simplicity is not the absence of complexity — it is the resolution of it.",
    "url": "https://murn.eu/blog/on-simplicity",
    "markdown": "https://murn.eu/blog/on-simplicity/index.md",
    "article": 1,
    "date": "2026-01-10",
    "readingTime": "4 min read",
    "wordCount": 460,
    "tags": [
      "design"
    ]
  },
  {
    "title": "Return value",
    "description": "Most blog posts get published because the writer wrote them. This blog is built around a different test — would the reader keep it?",
    "url": "https://murn.eu/blog/return-value",
    "markdown": "https://murn.eu/blog/return-value/index.md",
    "article": 0,
    "date": "2026-01-05",
    "readingTime": "4 min read",
    "wordCount": 659,
    "tags": [
      "philosophy"
    ]
  }
]